Bug 1943226 - Make nsFrameSelection treat limiters are elements r=jjaschke,dom-core

`nsFrameSelection::GetLimiter()` is not `nullptr` only when it's an instance for
an independent selection of a text control.  In the case, it's set to the editor
root anonymous `<div>` of the text control.  Despite the name, this is already
optimized only for this purpose in `nsFrameSelection::NodeIsInLimiters()`.
Thus, we don't have any problems to make this clearer for the other developers
with renaming some parameter names.

`nsFrameSelection::GetAncestorLimiter()` is also always an `Element`.  So,
we can change this to `Element` too.

Differential Revision: https://phabricator.services.mozilla.com/D235254
This commit is contained in:
Masayuki Nakano
2025-01-24 02:53:07 +00:00
parent 5e9b04cd87
commit 2b503ca56d
12 changed files with 114 additions and 71 deletions

View File

@@ -2179,7 +2179,7 @@ nsresult Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
return rv; return rv;
} }
nsIContent* Selection::GetAncestorLimiter() const { Element* Selection::GetAncestorLimiter() const {
MOZ_ASSERT(mSelectionType == SelectionType::eNormal); MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
if (mFrameSelection) { if (mFrameSelection) {
@@ -2188,7 +2188,7 @@ nsIContent* Selection::GetAncestorLimiter() const {
return nullptr; return nullptr;
} }
void Selection::SetAncestorLimiter(nsIContent* aLimiter) { void Selection::SetAncestorLimiter(Element* aLimiter) {
if (NeedsToLogSelectionAPI(*this)) { if (NeedsToLogSelectionAPI(*this)) {
LogSelectionAPI(this, __FUNCTION__, "aLimiter", aLimiter); LogSelectionAPI(this, __FUNCTION__, "aLimiter", aLimiter);
LogStackForSelectionAPI(); LogStackForSelectionAPI();

View File

@@ -783,8 +783,8 @@ class Selection final : public nsSupportsWeakReference,
const TextRangeStyle& aTextRangeStyle); const TextRangeStyle& aTextRangeStyle);
// Methods to manipulate our mFrameSelection's ancestor limiter. // Methods to manipulate our mFrameSelection's ancestor limiter.
nsIContent* GetAncestorLimiter() const; Element* GetAncestorLimiter() const;
void SetAncestorLimiter(nsIContent* aLimiter); void SetAncestorLimiter(Element* aLimiter);
/* /*
* Frame Offset cache can be used just during calling * Frame Offset cache can be used just during calling

View File

@@ -364,8 +364,7 @@ nsresult ContentEventHandler::InitRootContent(
if (!aNormalSelection.RangeCount()) { if (!aNormalSelection.RangeCount()) {
// If there is no selection range, we should compute the selection root // If there is no selection range, we should compute the selection root
// from ancestor limiter or root content of the document. // from ancestor limiter or root content of the document.
mRootElement = mRootElement = aNormalSelection.GetAncestorLimiter();
Element::FromNodeOrNull(aNormalSelection.GetAncestorLimiter());
if (!mRootElement) { if (!mRootElement) {
mRootElement = mDocument->GetRootElement(); mRootElement = mDocument->GetRootElement();
if (NS_WARN_IF(!mRootElement)) { if (NS_WARN_IF(!mRootElement)) {

View File

@@ -339,7 +339,8 @@ class TextInputSelectionController final : public nsSupportsWeakReference,
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TextInputSelectionController, NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TextInputSelectionController,
nsISelectionController) nsISelectionController)
TextInputSelectionController(PresShell* aPresShell, nsIContent* aLimiter); TextInputSelectionController(PresShell* aPresShell,
Element& aEditorRootAnonymousDiv);
void SetScrollContainerFrame(ScrollContainerFrame* aScrollContainerFrame); void SetScrollContainerFrame(ScrollContainerFrame* aScrollContainerFrame);
nsFrameSelection* GetConstFrameSelection() { return mFrameSelection; } nsFrameSelection* GetConstFrameSelection() { return mFrameSelection; }
@@ -401,12 +402,12 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_WEAK(TextInputSelectionController, mFrameSelection) NS_IMPL_CYCLE_COLLECTION_WEAK(TextInputSelectionController, mFrameSelection)
TextInputSelectionController::TextInputSelectionController( TextInputSelectionController::TextInputSelectionController(
PresShell* aPresShell, nsIContent* aLimiter) { PresShell* aPresShell, Element& aEditorRootAnonymousDiv) {
if (aPresShell) { if (aPresShell) {
bool accessibleCaretEnabled = const bool accessibleCaretEnabled = PresShell::AccessibleCaretEnabled(
PresShell::AccessibleCaretEnabled(aLimiter->OwnerDoc()->GetDocShell()); aEditorRootAnonymousDiv.OwnerDoc()->GetDocShell());
mFrameSelection = mFrameSelection = new nsFrameSelection(aPresShell, accessibleCaretEnabled,
new nsFrameSelection(aPresShell, aLimiter, accessibleCaretEnabled); &aEditorRootAnonymousDiv);
mPresShellWeak = do_GetWeakReference(aPresShell); mPresShellWeak = do_GetWeakReference(aPresShell);
} }
} }
@@ -1608,14 +1609,14 @@ nsresult TextControlState::BindToFrame(nsTextControlFrame* aFrame) {
mBoundFrame = aFrame; mBoundFrame = aFrame;
Element* rootNode = aFrame->GetRootNode(); MOZ_ASSERT(aFrame->GetRootNode());
MOZ_ASSERT(rootNode); Element& editorRootAnonymousDiv = *aFrame->GetRootNode();
PresShell* presShell = aFrame->PresContext()->GetPresShell(); PresShell* presShell = aFrame->PresContext()->GetPresShell();
MOZ_ASSERT(presShell); MOZ_ASSERT(presShell);
// Create a SelectionController // Create a SelectionController
mSelCon = new TextInputSelectionController(presShell, rootNode); mSelCon = new TextInputSelectionController(presShell, editorRootAnonymousDiv);
MOZ_ASSERT(!mTextListener, "Should not overwrite the object"); MOZ_ASSERT(!mTextListener, "Should not overwrite the object");
mTextListener = new TextInputListener(mTextCtrlElement); mTextListener = new TextInputListener(mTextCtrlElement);
@@ -1645,9 +1646,11 @@ nsresult TextControlState::BindToFrame(nsTextControlFrame* aFrame) {
// Set the correct direction on the newly created root node // Set the correct direction on the newly created root node
if (mTextEditor->IsRightToLeft()) { if (mTextEditor->IsRightToLeft()) {
rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, u"rtl"_ns, false); editorRootAnonymousDiv.SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
u"rtl"_ns, false);
} else if (mTextEditor->IsLeftToRight()) { } else if (mTextEditor->IsLeftToRight()) {
rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, u"ltr"_ns, false); editorRootAnonymousDiv.SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
u"ltr"_ns, false);
} else { } else {
// otherwise, inherit the content node's direction // otherwise, inherit the content node's direction
} }

View File

@@ -524,14 +524,14 @@ class MOZ_STACK_CLASS AutoClonedSelectionRangeArray final
* Equivalent to nsFrameSelection::GetLimiter(). * Equivalent to nsFrameSelection::GetLimiter().
* NOTE: This should be called only when IsForSelection() returns true. * NOTE: This should be called only when IsForSelection() returns true.
*/ */
[[nodiscard]] nsIContent* GetLimiter() const { [[nodiscard]] dom::Element* GetLimiter() const {
return mLimitersAndCaretData.mLimiter; return mLimitersAndCaretData.mLimiter;
} }
/** /**
* Equivalent to nsFrameSelection::GetAncestorLimiter() * Equivalent to nsFrameSelection::GetAncestorLimiter()
* NOTE: This should be called only when IsForSelection() returns true. * NOTE: This should be called only when IsForSelection() returns true.
*/ */
[[nodiscard]] nsIContent* GetAncestorLimiter() const { [[nodiscard]] dom::Element* GetAncestorLimiter() const {
return mLimitersAndCaretData.mAncestorLimiter; return mLimitersAndCaretData.mAncestorLimiter;
} }
/** /**
@@ -553,12 +553,12 @@ class MOZ_STACK_CLASS AutoClonedSelectionRangeArray final
return mLimitersAndCaretData.mCaretBidiLevel; return mLimitersAndCaretData.mCaretBidiLevel;
} }
void SetAncestorLimiter(const nsIContent* aSelectionAncestorLimiter) { void SetAncestorLimiter(const dom::Element* aSelectionAncestorLimiter) {
if (mLimitersAndCaretData.mAncestorLimiter == aSelectionAncestorLimiter) { if (mLimitersAndCaretData.mAncestorLimiter == aSelectionAncestorLimiter) {
return; return;
} }
mLimitersAndCaretData.mAncestorLimiter = mLimitersAndCaretData.mAncestorLimiter =
const_cast<nsIContent*>(aSelectionAncestorLimiter); const_cast<dom::Element*>(aSelectionAncestorLimiter);
if (NodeIsInLimiters(GetFocusNode())) { if (NodeIsInLimiters(GetFocusNode())) {
return; return;
} }

View File

@@ -5702,7 +5702,7 @@ Element* EditorBase::FindSelectionRoot(const nsINode& aNode) const {
} }
void EditorBase::InitializeSelectionAncestorLimit( void EditorBase::InitializeSelectionAncestorLimit(
nsIContent& aAncestorLimit) const { Element& aAncestorLimit) const {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
SelectionRef().SetAncestorLimiter(&aAncestorLimit); SelectionRef().SetAncestorLimiter(&aAncestorLimit);
@@ -5712,7 +5712,7 @@ nsresult EditorBase::InitializeSelection(
const nsINode& aOriginalEventTargetNode) { const nsINode& aOriginalEventTargetNode) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
nsCOMPtr<nsIContent> selectionRootContent = const RefPtr<Element> selectionRootContent =
FindSelectionRoot(aOriginalEventTargetNode); FindSelectionRoot(aOriginalEventTargetNode);
if (!selectionRootContent) { if (!selectionRootContent) {
return NS_OK; return NS_OK;

View File

@@ -2517,8 +2517,7 @@ class EditorBase : public nsIEditor,
* has parent node. So, it's always safe to * has parent node. So, it's always safe to
* call SetAncestorLimit() with this node. * call SetAncestorLimit() with this node.
*/ */
virtual void InitializeSelectionAncestorLimit( virtual void InitializeSelectionAncestorLimit(Element& aAncestorLimit) const;
nsIContent& aAncestorLimit) const;
/** /**
* Initializes selection and caret for the editor at getting focus. If * Initializes selection and caret for the editor at getting focus. If

View File

@@ -1063,7 +1063,7 @@ nsresult HTMLEditor::CollapseSelectionToEndOfLastLeafNodeOfDocument() const {
} }
void HTMLEditor::InitializeSelectionAncestorLimit( void HTMLEditor::InitializeSelectionAncestorLimit(
nsIContent& aAncestorLimit) const { Element& aAncestorLimit) const {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
// Hack for initializing selection. // Hack for initializing selection.
@@ -7450,7 +7450,7 @@ void HTMLEditor::NotifyEditingHostMaybeChanged() {
} }
// Compute current editing host. // Compute current editing host.
nsIContent* editingHost = ComputeEditingHost(); Element* const editingHost = ComputeEditingHost();
if (NS_WARN_IF(!editingHost)) { if (NS_WARN_IF(!editingHost)) {
return; return;
} }

View File

@@ -3505,7 +3505,7 @@ class HTMLEditor final : public EditorBase,
SetHTMLBackgroundColorWithTransaction(const nsAString& aColor); SetHTMLBackgroundColorWithTransaction(const nsAString& aColor);
MOZ_CAN_RUN_SCRIPT_BOUNDARY void InitializeSelectionAncestorLimit( MOZ_CAN_RUN_SCRIPT_BOUNDARY void InitializeSelectionAncestorLimit(
nsIContent& aAncestorLimit) const final; Element& aAncestorLimit) const final;
/** /**
* Make the given selection span the entire document. * Make the given selection span the entire document.

View File

@@ -939,7 +939,7 @@ void PresShell::Init(nsPresContext* aPresContext, nsViewManager* aViewManager) {
mAccessibleCaretEventHub->Init(); mAccessibleCaretEventHub->Init();
} }
mSelection = new nsFrameSelection(this, nullptr, accessibleCaretEnabled); mSelection = new nsFrameSelection(this, accessibleCaretEnabled);
// Important: this has to happen after the selection has been set up // Important: this has to happen after the selection has been set up
#ifdef SHOW_CARET #ifdef SHOW_CARET
@@ -2462,7 +2462,7 @@ PresShell::CompleteMove(bool aForward, bool aExtend) {
// Beware! This may flush notifications via synchronous // Beware! This may flush notifications via synchronous
// ScrollSelectionIntoView. // ScrollSelectionIntoView.
RefPtr<nsFrameSelection> frameSelection = mSelection; RefPtr<nsFrameSelection> frameSelection = mSelection;
nsIContent* limiter = frameSelection->GetAncestorLimiter(); Element* const limiter = frameSelection->GetAncestorLimiter();
nsIFrame* frame = limiter ? limiter->GetPrimaryFrame() nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
: FrameConstructor()->GetRootElementFrame(); : FrameConstructor()->GetRootElementFrame();
if (!frame) { if (!frame) {

View File

@@ -19,6 +19,7 @@
#include "mozilla/IntegerRange.h" #include "mozilla/IntegerRange.h"
#include "mozilla/Logging.h" #include "mozilla/Logging.h"
#include "mozilla/PresShell.h" #include "mozilla/PresShell.h"
#include "mozilla/PseudoStyleType.h"
#include "mozilla/ScrollContainerFrame.h" #include "mozilla/ScrollContainerFrame.h"
#include "mozilla/ScrollTypes.h" #include "mozilla/ScrollTypes.h"
#include "mozilla/StaticAnalysisFunctions.h" #include "mozilla/StaticAnalysisFunctions.h"
@@ -218,19 +219,35 @@ bool nsFrameSelection::NodeIsInLimiters(const nsINode* aContainerNode) const {
// static // static
bool nsFrameSelection::NodeIsInLimiters( bool nsFrameSelection::NodeIsInLimiters(
const nsINode* aContainerNode, const nsIContent* aSelectionLimiter, const nsINode* aContainerNode, const Element* aSelectionLimiter,
const nsIContent* aSelectionAncestorLimiter) { const Element* aSelectionAncestorLimiter) {
if (!aContainerNode) { if (!aContainerNode) {
return false; return false;
} }
if (aSelectionLimiter && aSelectionLimiter != aContainerNode && // If there is a selection limiter, it must be the anonymous <div> of a text
aSelectionLimiter != aContainerNode->GetParent()) { // control. The <div> should have only one Text and/or a <br>. Therefore,
// if newfocus == the limiter. that's ok. but if not there and not parent // when it's non-nullptr, selection range containers must be the container or
// bad // the Text in it.
return false; // not in the right content. tLimiter said so if (aSelectionLimiter) {
MOZ_ASSERT(aSelectionLimiter->GetPseudoElementType() ==
PseudoStyleType::mozTextControlEditingRoot);
MOZ_ASSERT(aSelectionLimiter->IsHTMLElement(nsGkAtoms::div));
if (aSelectionLimiter == aContainerNode) {
return true;
}
if (aSelectionLimiter == aContainerNode->GetParent()) {
NS_WARNING_ASSERTION(aContainerNode->IsText(),
ToString(*aContainerNode).c_str());
MOZ_ASSERT(aContainerNode->IsText());
return true;
}
return false;
} }
// XXX We might need to return `false` if aContainerNode is in a native
// anonymous subtree, but doing it will make it impossible to select the
// anonymous subtree text in <details>.
return !aSelectionAncestorLimiter || return !aSelectionAncestorLimiter ||
aContainerNode->IsInclusiveDescendantOf(aSelectionAncestorLimiter); aContainerNode->IsInclusiveDescendantOf(aSelectionAncestorLimiter);
} }
@@ -376,8 +393,9 @@ nsFrameSelection::CreateRangeExtendedToSomewhere(
nsDirection aExtendDirection, nsSelectionAmount aAmount, nsDirection aExtendDirection, nsSelectionAmount aAmount,
CaretMovementStyle aMovementStyle); CaretMovementStyle aMovementStyle);
nsFrameSelection::nsFrameSelection(PresShell* aPresShell, nsIContent* aLimiter, nsFrameSelection::nsFrameSelection(
const bool aAccessibleCaretEnabled) { PresShell* aPresShell, const bool aAccessibleCaretEnabled,
Element* aEditorRootAnonymousDiv /* = nullptr */) {
for (size_t i = 0; i < std::size(mDomSelections); i++) { for (size_t i = 0; i < std::size(mDomSelections); i++) {
mDomSelections[i] = new Selection(kPresentSelectionTypes[i], this); mDomSelections[i] = new Selection(kPresentSelectionTypes[i], this);
} }
@@ -389,7 +407,13 @@ nsFrameSelection::nsFrameSelection(PresShell* aPresShell, nsIContent* aLimiter,
mPresShell = aPresShell; mPresShell = aPresShell;
mDragState = false; mDragState = false;
mLimiters.mLimiter = aLimiter;
MOZ_ASSERT_IF(aEditorRootAnonymousDiv,
aEditorRootAnonymousDiv->GetPseudoElementType() ==
PseudoStyleType::mozTextControlEditingRoot);
MOZ_ASSERT_IF(aEditorRootAnonymousDiv,
aEditorRootAnonymousDiv->IsHTMLElement(nsGkAtoms::div));
mLimiters.mLimiter = aEditorRootAnonymousDiv;
// This should only ever be initialized on the main thread, so we are OK here. // This should only ever be initialized on the main thread, so we are OK here.
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@@ -940,9 +964,8 @@ nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
// static // static
Result<PeekOffsetOptions, nsresult> Result<PeekOffsetOptions, nsresult>
nsFrameSelection::CreatePeekOffsetOptionsForCaretMove( nsFrameSelection::CreatePeekOffsetOptionsForCaretMove(
const nsIContent* aSelectionLimiter, const Element* aSelectionLimiter, ForceEditableRegion aForceEditableRegion,
ForceEditableRegion aForceEditableRegion, ExtendSelection aExtendSelection, ExtendSelection aExtendSelection, CaretMovementStyle aMovementStyle) {
CaretMovementStyle aMovementStyle) {
PeekOffsetOptions options; PeekOffsetOptions options;
// set data using aSelectionLimiter to stop on scroll views. If we have a // set data using aSelectionLimiter to stop on scroll views. If we have a
// limiter then we stop peeking when we hit scrollable views. If no limiter // limiter then we stop peeking when we hit scrollable views. If no limiter
@@ -978,7 +1001,7 @@ Result<Element*, nsresult> nsFrameSelection::GetAncestorLimiterForCaretMove(
MOZ_ASSERT(mPresShell->GetDocument() == content->GetComposedDoc()); MOZ_ASSERT(mPresShell->GetDocument() == content->GetComposedDoc());
Element* ancestorLimiter = Element::FromNodeOrNull(GetAncestorLimiter()); Element* ancestorLimiter = GetAncestorLimiter();
if (aSelection->IsEditorSelection()) { if (aSelection->IsEditorSelection()) {
// If the editor has not receive `focus` event, it may have not set ancestor // If the editor has not receive `focus` event, it may have not set ancestor
// limiter. Then, we need to compute it here for the caret move. // limiter. Then, we need to compute it here for the caret move.
@@ -2051,9 +2074,7 @@ nsFrameSelection::CreateRangeExtendedToSomewhere(
: aRange.EndRef().AsRaw(), : aRange.EndRef().AsRaw(),
aExtendDirection, aLimitersAndCaretData.mCaretAssociationHint, aExtendDirection, aLimitersAndCaretData.mCaretAssociationHint,
aLimitersAndCaretData.mCaretBidiLevel, aAmount, options.unwrap(), aLimitersAndCaretData.mCaretBidiLevel, aAmount, options.unwrap(),
// FIXME: mAncestorLimiter should always be an Element, but it's not aLimitersAndCaretData.mAncestorLimiter);
// guaranteed at build time for now.
Element::FromNodeOrNull(aLimitersAndCaretData.mAncestorLimiter));
if (result.isErr()) { if (result.isErr()) {
return result.propagateErr(); return result.propagateErr();
} }
@@ -3022,7 +3043,7 @@ nsresult CreateAndAddRange(nsINode* aContainer, int32_t aOffset,
// End of Table Selection // End of Table Selection
void nsFrameSelection::SetAncestorLimiter(nsIContent* aLimiter) { void nsFrameSelection::SetAncestorLimiter(Element* aLimiter) {
if (mLimiters.mAncestorLimiter != aLimiter) { if (mLimiters.mAncestorLimiter != aLimiter) {
mLimiters.mAncestorLimiter = aLimiter; mLimiters.mAncestorLimiter = aLimiter;
const Selection& sel = NormalSelection(); const Selection& sel = NormalSelection();

View File

@@ -15,6 +15,7 @@
#include "mozilla/CompactPair.h" #include "mozilla/CompactPair.h"
#include "mozilla/EnumSet.h" #include "mozilla/EnumSet.h"
#include "mozilla/EventForwards.h" #include "mozilla/EventForwards.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Highlight.h" #include "mozilla/dom/Highlight.h"
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
#include "mozilla/Result.h" #include "mozilla/Result.h"
@@ -238,6 +239,7 @@ enum class TableSelectionMode : uint32_t {
class nsFrameSelection final { class nsFrameSelection final {
public: public:
using CaretAssociationHint = mozilla::CaretAssociationHint; using CaretAssociationHint = mozilla::CaretAssociationHint;
using Element = mozilla::dom::Element;
/*interfaces for addref and release and queryinterface*/ /*interfaces for addref and release and queryinterface*/
@@ -520,8 +522,8 @@ class nsFrameSelection final {
[[nodiscard]] bool NodeIsInLimiters(const nsINode* aContainerNode) const; [[nodiscard]] bool NodeIsInLimiters(const nsINode* aContainerNode) const;
[[nodiscard]] static bool NodeIsInLimiters( [[nodiscard]] static bool NodeIsInLimiters(
const nsINode* aContainerNode, const nsIContent* aSelectionLimiter, const nsINode* aContainerNode, const Element* aSelectionLimiter,
const nsIContent* aSelectionAncestorLimiter); const Element* aSelectionAncestorLimiter);
/** /**
* GetFrameToPageSelect() returns a frame which is ancestor limit of * GetFrameToPageSelect() returns a frame which is ancestor limit of
@@ -802,16 +804,26 @@ class nsFrameSelection final {
} }
/** /**
* Get the content node that limits the selection * GetLimiter() returns the selection limiter element which is currently
* * non-nullptr only when this instance is for an independent selection of a
* When searching up a nodes for parents, as in a text edit field * text control. Then, this returns the editor root anonymous <div> in the
* in an browser page, we must stop at this node else we reach into the * text control element.
* parent page, which is very bad!
*/ */
nsIContent* GetLimiter() const { return mLimiters.mLimiter; } Element* GetLimiter() const { return mLimiters.mLimiter; }
nsIContent* GetAncestorLimiter() const { return mLimiters.mAncestorLimiter; } /**
MOZ_CAN_RUN_SCRIPT_BOUNDARY void SetAncestorLimiter(nsIContent* aLimiter); * GetAncestorLimiter() returns the root of current selection ranges. This is
* typically the focused editing host unless it's the root element of the
* document.
*/
Element* GetAncestorLimiter() const { return mLimiters.mAncestorLimiter; }
/**
* Set ancestor limiter. If aLimiter is not nullptr, this adjusts all
* selection ranges into the limiter element. Thus, calling this may run
* the selection listeners.
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY void SetAncestorLimiter(Element* aLimiter);
/** /**
* GetPrevNextBidiLevels will return the frames and associated Bidi levels of * GetPrevNextBidiLevels will return the frames and associated Bidi levels of
@@ -855,13 +867,15 @@ class nsFrameSelection final {
* @param aPresShell is the parameter to be used for most of the other calls * @param aPresShell is the parameter to be used for most of the other calls
* for callbacks etc * for callbacks etc
* *
* @param aLimiter limits the selection to nodes with aLimiter parents
*
* @param aAccessibleCaretEnabled true if we should enable the accessible * @param aAccessibleCaretEnabled true if we should enable the accessible
* caret. * caret.
*
* @param aEditorRootAnonymousDiv if this instance is for an independent
* selection for a text control, specify this to the anonymous <div> element
* of the text control which contains only an editable Text and/or a <br>.
*/ */
nsFrameSelection(mozilla::PresShell* aPresShell, nsIContent* aLimiter, nsFrameSelection(mozilla::PresShell* aPresShell, bool aAccessibleCaretEnabled,
bool aAccessibleCaretEnabled); Element* aEditorRootAnonymousDiv = nullptr);
/** /**
* @param aRequesterFuncName function name which wants to start the batch. * @param aRequesterFuncName function name which wants to start the batch.
@@ -1005,7 +1019,7 @@ class nsFrameSelection final {
enum class ForceEditableRegion : bool { No, Yes }; enum class ForceEditableRegion : bool { No, Yes };
static mozilla::Result<mozilla::PeekOffsetOptions, nsresult> static mozilla::Result<mozilla::PeekOffsetOptions, nsresult>
CreatePeekOffsetOptionsForCaretMove(const nsIContent* aSelectionLimiter, CreatePeekOffsetOptionsForCaretMove(const Element* aSelectionLimiter,
ForceEditableRegion aForceEditableRegion, ForceEditableRegion aForceEditableRegion,
ExtendSelection aExtendSelection, ExtendSelection aExtendSelection,
CaretMovementStyle aMovementStyle); CaretMovementStyle aMovementStyle);
@@ -1019,8 +1033,8 @@ class nsFrameSelection final {
* @param aSelection The selection object. Must be non-null * @param aSelection The selection object. Must be non-null
* @return The ancestor limiter, or nullptr. * @return The ancestor limiter, or nullptr.
*/ */
mozilla::Result<mozilla::dom::Element*, nsresult> mozilla::Result<Element*, nsresult> GetAncestorLimiterForCaretMove(
GetAncestorLimiterForCaretMove(mozilla::dom::Selection* aSelection) const; mozilla::dom::Selection* aSelection) const;
/** /**
* CreateRangeExtendedToSomewhere() is common method to implement * CreateRangeExtendedToSomewhere() is common method to implement
@@ -1190,10 +1204,15 @@ class nsFrameSelection final {
Batching mBatching; Batching mBatching;
struct Limiters { struct Limiters {
// Limit selection navigation to a child of this node. // Limit selection navigation to a child of this element.
nsCOMPtr<nsIContent> mLimiter; // This is set only when the nsFrameSelection instance is for the
// Limit selection navigation to a descendant of this node. // independent selection for a text control. If this is set, it's always
nsCOMPtr<nsIContent> mAncestorLimiter; // the anonymous <div> of the text control element.
RefPtr<Element> mLimiter;
// Limit selection navigation to a descendant of this element.
// This is typically the focused editing host if set unless it's the root
// element of the document.
RefPtr<Element> mAncestorLimiter;
}; };
Limiters mLimiters; Limiters mLimiters;
@@ -1294,6 +1313,8 @@ namespace mozilla {
* A struct for sharing nsFrameSelection outside of its instance. * A struct for sharing nsFrameSelection outside of its instance.
*/ */
struct LimitersAndCaretData { struct LimitersAndCaretData {
using Element = dom::Element;
LimitersAndCaretData() = default; LimitersAndCaretData() = default;
explicit LimitersAndCaretData(const nsFrameSelection& aFrameSelection) explicit LimitersAndCaretData(const nsFrameSelection& aFrameSelection)
: mLimiter(aFrameSelection.GetLimiter()), : mLimiter(aFrameSelection.GetLimiter()),
@@ -1312,9 +1333,9 @@ struct LimitersAndCaretData {
} }
// nsFrameSelection::GetLimiter // nsFrameSelection::GetLimiter
nsCOMPtr<nsIContent> mLimiter; RefPtr<Element> mLimiter;
// nsFrameSelection::GetAncestorLimiter // nsFrameSelection::GetAncestorLimiter
nsCOMPtr<nsIContent> mAncestorLimiter; RefPtr<Element> mAncestorLimiter;
// nsFrameSelection::GetHint // nsFrameSelection::GetHint
CaretAssociationHint mCaretAssociationHint = CaretAssociationHint::Before; CaretAssociationHint mCaretAssociationHint = CaretAssociationHint::Before;
// nsFrameSelection::GetCaretBidiLevel // nsFrameSelection::GetCaretBidiLevel