1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ContentEventHandler.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/ContentIterator.h"
12 #include "mozilla/IMEStateManager.h"
13 #include "mozilla/IntegerRange.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/PresShell.h"
16 #include "mozilla/RangeBoundary.h"
17 #include "mozilla/RangeUtils.h"
18 #include "mozilla/SelectionMovementUtils.h"
19 #include "mozilla/TextComposition.h"
20 #include "mozilla/TextEditor.h"
21 #include "mozilla/TextEvents.h"
22 #include "mozilla/dom/Element.h"
23 #include "mozilla/dom/HTMLBRElement.h"
24 #include "mozilla/dom/HTMLUnknownElement.h"
25 #include "mozilla/dom/Selection.h"
26 #include "mozilla/dom/Text.h"
29 #include "nsContentUtils.h"
30 #include "nsCopySupport.h"
31 #include "nsElementTable.h"
32 #include "nsFocusManager.h"
33 #include "nsFontMetrics.h"
34 #include "nsFrameSelection.h"
35 #include "nsHTMLTags.h"
37 #include "nsLayoutUtils.h"
38 #include "nsPresContext.h"
39 #include "nsQueryObject.h"
41 #include "nsTextFragment.h"
42 #include "nsTextFrame.h"
44 #include "mozilla/ViewportUtils.h"
48 // Work around conflicting define in rpcndr.h
51 #endif // defined(small)
53 #if defined(XP_WIN) && 0
54 # define TRANSLATE_NEW_LINES
60 using namespace widget
;
62 /******************************************************************/
63 /* ContentEventHandler::SimpleRangeBase */
64 /******************************************************************/
66 ContentEventHandler::SimpleRangeBase
<
67 RefPtr
<nsINode
>, RangeBoundary
>::SimpleRangeBase() = default;
70 ContentEventHandler::SimpleRangeBase
<nsINode
*,
71 RawRangeBoundary
>::SimpleRangeBase()
73 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
74 mAssertNoGC
.emplace();
75 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
79 template <typename OtherNodeType
, typename OtherRangeBoundaryType
>
80 ContentEventHandler::SimpleRangeBase
<RefPtr
<nsINode
>, RangeBoundary
>::
82 const SimpleRangeBase
<OtherNodeType
, OtherRangeBoundaryType
>& aOther
)
83 : mRoot(aOther
.GetRoot()),
84 mStart
{aOther
.Start().AsRaw()},
85 mEnd
{aOther
.End().AsRaw()}
86 // Don't use the copy constructor of mAssertNoGC
90 template <typename OtherNodeType
, typename OtherRangeBoundaryType
>
91 ContentEventHandler::SimpleRangeBase
<nsINode
*, RawRangeBoundary
>::
93 const SimpleRangeBase
<OtherNodeType
, OtherRangeBoundaryType
>& aOther
)
94 : mRoot(aOther
.GetRoot()),
95 mStart
{aOther
.Start().AsRaw()},
96 mEnd
{aOther
.End().AsRaw()} {
97 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
98 mAssertNoGC
.emplace();
99 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
103 ContentEventHandler::SimpleRangeBase
<RefPtr
<nsINode
>, RangeBoundary
>::
105 SimpleRangeBase
<RefPtr
<nsINode
>, RangeBoundary
>&& aOther
) noexcept
106 : mRoot(std::move(aOther
.GetRoot())),
107 mStart(std::move(aOther
.mStart
)),
108 mEnd(std::move(aOther
.mEnd
)) {}
111 ContentEventHandler::SimpleRangeBase
<nsINode
*, RawRangeBoundary
>::
113 SimpleRangeBase
<nsINode
*, RawRangeBoundary
>&& aOther
) noexcept
114 : mRoot(std::move(aOther
.GetRoot())),
115 mStart(std::move(aOther
.mStart
)),
116 mEnd(std::move(aOther
.mEnd
)) {
117 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
118 mAssertNoGC
.emplace();
119 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
122 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
124 ContentEventHandler::SimpleRangeBase
<
125 RefPtr
<nsINode
>, RangeBoundary
>::~SimpleRangeBase() = default;
128 ContentEventHandler::SimpleRangeBase
<nsINode
*,
129 RawRangeBoundary
>::~SimpleRangeBase() {
130 MOZ_DIAGNOSTIC_ASSERT(!mMutationGuard
.Mutated(0));
132 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
134 template <typename NodeType
, typename RangeBoundaryType
>
135 void ContentEventHandler::SimpleRangeBase
<
136 NodeType
, RangeBoundaryType
>::AssertStartIsBeforeOrEqualToEnd() {
138 *nsContentUtils::ComparePoints(
141 RangeBoundaryType::OffsetFilter::kValidOrInvalidOffsets
),
144 RangeBoundaryType::OffsetFilter::kValidOrInvalidOffsets
)) <= 0);
147 template <typename NodeType
, typename RangeBoundaryType
>
149 ContentEventHandler::SimpleRangeBase
<NodeType
, RangeBoundaryType
>::SetStart(
150 const RawRangeBoundary
& aStart
) {
151 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aStart
.Container());
153 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
156 if (!aStart
.IsSetAndValid()) {
157 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
160 // Collapse if not positioned yet, or if positioned in another document.
161 if (!IsPositioned() || newRoot
!= mRoot
) {
163 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
164 mEnd
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
168 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
169 AssertStartIsBeforeOrEqualToEnd();
173 template <typename NodeType
, typename RangeBoundaryType
>
175 ContentEventHandler::SimpleRangeBase
<NodeType
, RangeBoundaryType
>::SetEnd(
176 const RawRangeBoundary
& aEnd
) {
177 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aEnd
.Container());
179 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
182 if (!aEnd
.IsSetAndValid()) {
183 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
186 // Collapse if not positioned yet, or if positioned in another document.
187 if (!IsPositioned() || newRoot
!= mRoot
) {
189 mStart
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
190 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
194 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
195 AssertStartIsBeforeOrEqualToEnd();
199 template <typename NodeType
, typename RangeBoundaryType
>
201 ContentEventHandler::SimpleRangeBase
<NodeType
, RangeBoundaryType
>::SetEndAfter(
202 nsINode
* aEndContainer
) {
203 return SetEnd(RangeUtils::GetRawRangeBoundaryAfter(aEndContainer
));
206 template <typename NodeType
, typename RangeBoundaryType
>
207 void ContentEventHandler::SimpleRangeBase
<
208 NodeType
, RangeBoundaryType
>::SetStartAndEnd(const nsRange
* aRange
) {
209 DebugOnly
<nsresult
> rv
=
210 SetStartAndEnd(aRange
->StartRef().AsRaw(), aRange
->EndRef().AsRaw());
211 MOZ_ASSERT(!aRange
->IsPositioned() || NS_SUCCEEDED(rv
));
214 template <typename NodeType
, typename RangeBoundaryType
>
215 nsresult
ContentEventHandler::SimpleRangeBase
<
216 NodeType
, RangeBoundaryType
>::SetStartAndEnd(const RawRangeBoundary
& aStart
,
217 const RawRangeBoundary
& aEnd
) {
218 nsINode
* newStartRoot
= RangeUtils::ComputeRootNode(aStart
.Container());
220 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
222 if (!aStart
.IsSetAndValid()) {
223 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
226 if (aStart
.Container() == aEnd
.Container()) {
227 if (!aEnd
.IsSetAndValid()) {
228 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
230 MOZ_ASSERT(*aStart
.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets
) <=
231 *aEnd
.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets
));
232 mRoot
= newStartRoot
;
233 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
234 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
238 nsINode
* newEndRoot
= RangeUtils::ComputeRootNode(aEnd
.Container());
240 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
242 if (!aEnd
.IsSetAndValid()) {
243 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
246 // If they have different root, this should be collapsed at the end point.
247 if (newStartRoot
!= newEndRoot
) {
249 mStart
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
250 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
254 // Otherwise, set the range as specified.
255 mRoot
= newStartRoot
;
256 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
257 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
258 AssertStartIsBeforeOrEqualToEnd();
262 template <typename NodeType
, typename RangeBoundaryType
>
263 nsresult
ContentEventHandler::SimpleRangeBase
<NodeType
, RangeBoundaryType
>::
264 SelectNodeContents(const nsINode
* aNodeToSelectContents
) {
265 nsINode
* const newRoot
=
266 RangeUtils::ComputeRootNode(const_cast<nsINode
*>(aNodeToSelectContents
));
268 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
272 RangeBoundaryType(const_cast<nsINode
*>(aNodeToSelectContents
), nullptr);
273 mEnd
= RangeBoundaryType(const_cast<nsINode
*>(aNodeToSelectContents
),
274 aNodeToSelectContents
->GetLastChild());
278 /******************************************************************/
279 /* ContentEventHandler */
280 /******************************************************************/
284 // ContentEventHandler *creates* ranges as following rules:
285 // 1. Start of range:
286 // 1.1. Cases: [textNode or text[Node or textNode[
287 // When text node is start of a range, start node is the text node and
288 // start offset is any number between 0 and the length of the text.
289 // 1.2. Case: [<element>:
290 // When start of an element node is start of a range, start node is
291 // parent of the element and start offset is the element's index in the
293 // 1.3. Case: <element/>[
294 // When after an empty element node is start of a range, start node is
295 // parent of the element and start offset is the element's index in the
297 // 1.4. Case: <element>[
298 // When start of a non-empty element is start of a range, start node is
299 // the element and start offset is 0.
300 // 1.5. Case: <root>[
301 // When start of a range is 0 and there are no nodes causing text,
302 // start node is the root node and start offset is 0.
303 // 1.6. Case: [</root>
304 // When start of a range is out of bounds, start node is the root node
305 // and start offset is number of the children.
307 // 2.1. Cases: ]textNode or text]Node or textNode]
308 // When a text node is end of a range, end node is the text node and
309 // end offset is any number between 0 and the length of the text.
310 // 2.2. Case: ]<element>
311 // When before an element node (meaning before the open tag of the
312 // element) is end of a range, end node is previous node causing text.
313 // Note that this case shouldn't be handled directly. If rule 2.1 and
314 // 2.3 are handled correctly, the loop with ContentIterator shouldn't
315 // reach the element node since the loop should've finished already at
316 // handling the last node which caused some text.
317 // 2.3. Case: <element>]
318 // When a line break is caused before a non-empty element node and it's
319 // end of a range, end node is the element and end offset is 0.
320 // (i.e., including open tag of the element)
321 // 2.4. Cases: <element/>]
322 // When after an empty element node is end of a range, end node is
323 // parent of the element node and end offset is the element's index in
324 // the parent + 1. (i.e., including close tag of the element or empty
326 // 2.5. Case: ]</root>
327 // When end of a range is out of bounds, end node is the root node and
328 // end offset is number of the children.
330 // ContentEventHandler *treats* ranges as following additional rules:
331 // 1. When the start node is an element node which doesn't have children,
332 // it includes a line break caused before itself (i.e., includes its open
333 // tag). For example, if start position is { <br>, 0 }, the line break
334 // caused by <br> should be included into the flatten text.
335 // 2. When the end node is an element node which doesn't have children,
336 // it includes the end (i.e., includes its close tag except empty element).
337 // Although, currently, any close tags don't cause line break, this also
338 // includes its open tag. For example, if end position is { <br>, 0 }, the
339 // line break caused by the <br> should be included into the flatten text.
341 ContentEventHandler::ContentEventHandler(nsPresContext
* aPresContext
)
342 : mDocument(aPresContext
->Document()) {}
344 nsresult
ContentEventHandler::InitBasic(bool aRequireFlush
) {
345 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
347 // If text frame which has overflowing selection underline is dirty,
348 // we need to flush the pending reflow here.
349 mDocument
->FlushPendingNotifications(FlushType::Layout
);
354 nsresult
ContentEventHandler::InitRootContent(
355 const Selection
& aNormalSelection
) {
356 // Root content should be computed with normal selection because normal
357 // selection is typically has at least one range but the other selections
358 // not so. If there is a range, computing its root is easy, but if
359 // there are no ranges, we need to use ancestor limit instead.
360 MOZ_ASSERT(aNormalSelection
.Type() == SelectionType::eNormal
);
362 if (!aNormalSelection
.RangeCount()) {
363 // If there is no selection range, we should compute the selection root
364 // from ancestor limiter or root content of the document.
366 Element::FromNodeOrNull(aNormalSelection
.GetAncestorLimiter());
368 mRootElement
= mDocument
->GetRootElement();
369 if (NS_WARN_IF(!mRootElement
)) {
370 return NS_ERROR_NOT_AVAILABLE
;
376 RefPtr
<const nsRange
> range(aNormalSelection
.GetRangeAt(0));
377 if (NS_WARN_IF(!range
)) {
378 return NS_ERROR_UNEXPECTED
;
381 // If there is a selection, we should retrieve the selection root from
382 // the range since when the window is inactivated, the ancestor limiter
383 // of selection was cleared by blur event handler of EditorBase but the
384 // selection range still keeps storing the nodes. If the active element of
385 // the deactive window is <input> or <textarea>, we can compute the
386 // selection root from them.
387 nsCOMPtr
<nsINode
> startNode
= range
->GetStartContainer();
388 nsINode
* endNode
= range
->GetEndContainer();
389 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
)) {
390 return NS_ERROR_FAILURE
;
393 // See bug 537041 comment 5, the range could have removed node.
394 if (NS_WARN_IF(startNode
->GetComposedDoc() != mDocument
)) {
395 return NS_ERROR_FAILURE
;
398 NS_ASSERTION(startNode
->GetComposedDoc() == endNode
->GetComposedDoc(),
399 "firstNormalSelectionRange crosses the document boundary");
401 RefPtr
<PresShell
> presShell
= mDocument
->GetPresShell();
403 Element::FromNodeOrNull(startNode
->GetSelectionRootContent(presShell
));
404 if (NS_WARN_IF(!mRootElement
)) {
405 return NS_ERROR_FAILURE
;
411 nsresult
ContentEventHandler::InitCommon(EventMessage aEventMessage
,
412 SelectionType aSelectionType
,
413 bool aRequireFlush
) {
414 if (mSelection
&& mSelection
->Type() == aSelectionType
) {
418 mSelection
= nullptr;
419 mRootElement
= nullptr;
420 mFirstSelectedSimpleRange
.Clear();
422 nsresult rv
= InitBasic(aRequireFlush
);
423 NS_ENSURE_SUCCESS(rv
, rv
);
425 RefPtr
<nsFrameSelection
> frameSel
;
426 if (PresShell
* presShell
= mDocument
->GetPresShell()) {
427 frameSel
= presShell
->GetLastFocusedFrameSelection();
429 if (NS_WARN_IF(!frameSel
)) {
430 return NS_ERROR_NOT_AVAILABLE
;
433 mSelection
= frameSel
->GetSelection(aSelectionType
);
434 if (NS_WARN_IF(!mSelection
)) {
435 return NS_ERROR_NOT_AVAILABLE
;
438 RefPtr
<Selection
> normalSelection
;
439 if (mSelection
->Type() == SelectionType::eNormal
) {
440 normalSelection
= mSelection
;
442 normalSelection
= frameSel
->GetSelection(SelectionType::eNormal
);
443 if (NS_WARN_IF(!normalSelection
)) {
444 return NS_ERROR_NOT_AVAILABLE
;
448 rv
= InitRootContent(*normalSelection
);
449 if (NS_WARN_IF(NS_FAILED(rv
))) {
453 if (mSelection
->RangeCount()) {
454 mFirstSelectedSimpleRange
.SetStartAndEnd(mSelection
->GetRangeAt(0));
458 // Even if there are no selection ranges, it's usual case if aSelectionType
459 // is a special selection or we're handling eQuerySelectedText.
460 if (aSelectionType
!= SelectionType::eNormal
||
461 aEventMessage
== eQuerySelectedText
) {
462 MOZ_ASSERT(!mFirstSelectedSimpleRange
.IsPositioned());
466 // But otherwise, we need to assume that there is a selection range at the
467 // beginning of the root content if aSelectionType is eNormal.
468 rv
= mFirstSelectedSimpleRange
.CollapseTo(RawRangeBoundary(mRootElement
, 0u));
469 if (NS_WARN_IF(NS_FAILED(rv
))) {
470 return NS_ERROR_UNEXPECTED
;
475 nsresult
ContentEventHandler::Init(WidgetQueryContentEvent
* aEvent
) {
476 NS_ASSERTION(aEvent
, "aEvent must not be null");
477 MOZ_ASSERT(aEvent
->mMessage
== eQuerySelectedText
||
478 aEvent
->mInput
.mSelectionType
== SelectionType::eNormal
);
480 if (NS_WARN_IF(!aEvent
->mInput
.IsValidOffset()) ||
481 NS_WARN_IF(!aEvent
->mInput
.IsValidEventMessage(aEvent
->mMessage
))) {
482 return NS_ERROR_FAILURE
;
485 // Note that we should ignore WidgetQueryContentEvent::Input::mSelectionType
486 // if the event isn't eQuerySelectedText.
487 SelectionType selectionType
= aEvent
->mMessage
== eQuerySelectedText
488 ? aEvent
->mInput
.mSelectionType
489 : SelectionType::eNormal
;
490 if (NS_WARN_IF(selectionType
== SelectionType::eNone
)) {
491 return NS_ERROR_FAILURE
;
494 nsresult rv
= InitCommon(aEvent
->mMessage
, selectionType
,
495 aEvent
->AllowFlushingPendingNotifications());
496 NS_ENSURE_SUCCESS(rv
, rv
);
498 // Be aware, WidgetQueryContentEvent::mInput::mOffset should be made absolute
499 // offset before sending it to ContentEventHandler because querying selection
500 // every time may be expensive. So, if the caller caches selection, it
501 // should initialize the event with the cached value.
502 if (aEvent
->mInput
.mRelativeToInsertionPoint
) {
503 MOZ_ASSERT(selectionType
== SelectionType::eNormal
);
504 TextComposition
* composition
=
505 IMEStateManager::GetTextCompositionFor(aEvent
->mWidget
);
507 uint32_t compositionStart
= composition
->NativeOffsetOfStartComposition();
508 if (NS_WARN_IF(!aEvent
->mInput
.MakeOffsetAbsolute(compositionStart
))) {
509 return NS_ERROR_FAILURE
;
512 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
513 uint32_t selectionStart
= 0;
514 rv
= GetStartOffset(mFirstSelectedSimpleRange
, &selectionStart
,
516 if (NS_WARN_IF(NS_FAILED(rv
))) {
517 return NS_ERROR_FAILURE
;
519 if (NS_WARN_IF(!aEvent
->mInput
.MakeOffsetAbsolute(selectionStart
))) {
520 return NS_ERROR_FAILURE
;
525 // Ideally, we should emplace only when we return succeeded event.
526 // However, we need to emplace here since it's hard to store the various
527 // result. Intead, `HandleQueryContentEvent()` will reset `mReply` if
528 // corresponding handler returns error.
529 aEvent
->EmplaceReply();
531 aEvent
->mReply
->mContentsRoot
= mRootElement
.get();
532 aEvent
->mReply
->mIsEditableContent
=
533 mRootElement
&& mRootElement
->IsEditable();
536 nsIFrame
* frame
= nsCaret::GetGeometry(mSelection
, &r
);
538 frame
= mRootElement
->GetPrimaryFrame();
539 if (NS_WARN_IF(!frame
)) {
540 return NS_ERROR_FAILURE
;
543 aEvent
->mReply
->mFocusedWidget
= frame
->GetNearestWidget();
548 nsresult
ContentEventHandler::Init(WidgetSelectionEvent
* aEvent
) {
549 NS_ASSERTION(aEvent
, "aEvent must not be null");
551 nsresult rv
= InitCommon(aEvent
->mMessage
);
552 NS_ENSURE_SUCCESS(rv
, rv
);
554 aEvent
->mSucceeded
= false;
559 nsIContent
* ContentEventHandler::GetFocusedContent() {
560 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
561 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
562 return nsFocusManager::GetFocusedDescendant(
563 window
, nsFocusManager::eIncludeAllDescendants
,
564 getter_AddRefs(focusedWindow
));
567 nsresult
ContentEventHandler::QueryContentRect(
568 nsIContent
* aContent
, WidgetQueryContentEvent
* aEvent
) {
569 MOZ_ASSERT(aContent
, "aContent must not be null");
571 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
572 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
574 // get rect for first frame
575 nsRect
resultRect(nsPoint(0, 0), frame
->GetRect().Size());
576 nsresult rv
= ConvertToRootRelativeOffset(frame
, resultRect
);
577 NS_ENSURE_SUCCESS(rv
, rv
);
579 nsPresContext
* presContext
= frame
->PresContext();
581 // account for any additional frames
582 while ((frame
= frame
->GetNextContinuation())) {
583 nsRect
frameRect(nsPoint(0, 0), frame
->GetRect().Size());
584 rv
= ConvertToRootRelativeOffset(frame
, frameRect
);
585 NS_ENSURE_SUCCESS(rv
, rv
);
586 resultRect
.UnionRect(resultRect
, frameRect
);
589 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
590 resultRect
, presContext
->AppUnitsPerDevPixel());
591 // Returning empty rect may cause native IME confused, let's make sure to
592 // return non-empty rect.
593 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
598 // Editor places a padding <br> element under its root content if the editor
599 // doesn't have any text. This happens even for single line editors.
600 // When we get text content and when we change the selection,
601 // we don't want to include the padding <br> elements at the end.
602 static bool IsContentBR(const nsIContent
& aContent
) {
603 const HTMLBRElement
* brElement
= HTMLBRElement::FromNode(aContent
);
604 return brElement
&& !brElement
->IsPaddingForEmptyLastLine() &&
605 !brElement
->IsPaddingForEmptyEditor();
608 static bool IsPaddingBR(const nsIContent
& aContent
) {
609 return aContent
.IsHTMLElement(nsGkAtoms::br
) && !IsContentBR(aContent
);
612 static void ConvertToNativeNewlines(nsString
& aString
) {
613 #if defined(TRANSLATE_NEW_LINES)
614 aString
.ReplaceSubstring(u
"\n"_ns
, u
"\r\n"_ns
);
618 static void AppendString(nsString
& aString
, const Text
& aTextNode
) {
619 const uint32_t oldXPLength
= aString
.Length();
620 aTextNode
.TextFragment().AppendTo(aString
);
621 if (aTextNode
.HasFlag(NS_MAYBE_MASKED
)) {
622 TextEditor::MaskString(aString
, aTextNode
, oldXPLength
, 0);
626 static void AppendSubString(nsString
& aString
, const Text
& aTextNode
,
627 uint32_t aXPOffset
, uint32_t aXPLength
) {
628 const uint32_t oldXPLength
= aString
.Length();
629 aTextNode
.TextFragment().AppendTo(aString
, aXPOffset
, aXPLength
);
630 if (aTextNode
.HasFlag(NS_MAYBE_MASKED
)) {
631 TextEditor::MaskString(aString
, aTextNode
, oldXPLength
, aXPOffset
);
635 #if defined(TRANSLATE_NEW_LINES)
636 template <typename StringType
>
637 static uint32_t CountNewlinesInXPLength(const StringType
& aString
) {
639 const auto* end
= aString
.EndReading();
640 for (const auto* iter
= aString
.BeginReading(); iter
< end
; ++iter
) {
648 static uint32_t CountNewlinesInXPLength(const Text
& aTextNode
,
649 uint32_t aXPLength
) {
650 const nsTextFragment
& textFragment
= aTextNode
.TextFragment();
651 // For automated tests, we should abort on debug build.
652 MOZ_ASSERT(aXPLength
== UINT32_MAX
|| aXPLength
<= textFragment
.GetLength(),
653 "aXPLength is out-of-bounds");
654 const uint32_t length
= std::min(aXPLength
, textFragment
.GetLength());
658 if (textFragment
.Is2b()) {
659 nsDependentSubstring
str(textFragment
.Get2b(), length
);
660 return CountNewlinesInXPLength(str
);
662 nsDependentCSubstring
str(textFragment
.Get1b(), length
);
663 return CountNewlinesInXPLength(str
);
666 template <typename StringType
>
667 static uint32_t CountNewlinesInNativeLength(const StringType
& aString
,
668 uint32_t aNativeLength
) {
670 (aNativeLength
== UINT32_MAX
|| aNativeLength
<= aString
.Length() * 2),
671 "aNativeLength is unexpected value");
673 uint32_t nativeOffset
= 0;
674 const auto* end
= aString
.EndReading();
675 for (const auto* iter
= aString
.BeginReading();
676 iter
< end
&& nativeOffset
< aNativeLength
; ++iter
, ++nativeOffset
) {
685 static uint32_t CountNewlinesInNativeLength(const Text
& aTextNode
,
686 uint32_t aNativeLength
) {
687 const nsTextFragment
& textFragment
= aTextNode
.TextFragment();
688 const uint32_t xpLength
= textFragment
.GetLength();
692 if (textFragment
.Is2b()) {
693 nsDependentSubstring
str(textFragment
.Get2b(), xpLength
);
694 return CountNewlinesInNativeLength(str
, aNativeLength
);
696 nsDependentCSubstring
str(textFragment
.Get1b(), xpLength
);
697 return CountNewlinesInNativeLength(str
, aNativeLength
);
702 uint32_t ContentEventHandler::GetNativeTextLength(const Text
& aTextNode
,
703 uint32_t aStartOffset
,
704 uint32_t aEndOffset
) {
705 MOZ_ASSERT(aEndOffset
>= aStartOffset
,
706 "aEndOffset must be equals or larger than aStartOffset");
707 if (aStartOffset
== aEndOffset
) {
710 return GetTextLength(aTextNode
, LINE_BREAK_TYPE_NATIVE
, aEndOffset
) -
711 GetTextLength(aTextNode
, LINE_BREAK_TYPE_NATIVE
, aStartOffset
);
715 uint32_t ContentEventHandler::GetNativeTextLength(const Text
& aTextNode
,
716 uint32_t aMaxLength
) {
717 return GetTextLength(aTextNode
, LINE_BREAK_TYPE_NATIVE
, aMaxLength
);
721 uint32_t ContentEventHandler::GetBRLength(LineBreakType aLineBreakType
) {
722 #if defined(TRANSLATE_NEW_LINES)
724 return (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) ? 2 : 1;
731 uint32_t ContentEventHandler::GetTextLength(const Text
& aTextNode
,
732 LineBreakType aLineBreakType
,
733 uint32_t aMaxLength
) {
734 const uint32_t textLengthDifference
=
735 #if defined(TRANSLATE_NEW_LINES)
736 // On Windows, the length of a native newline ("\r\n") is twice the length
737 // of the XP newline ("\n"), so XP length is equal to the length of the
738 // native offset plus the number of newlines encountered in the string.
739 (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
)
740 ? CountNewlinesInXPLength(aTextNode
, aMaxLength
)
743 // On other platforms, the native and XP newlines are the same.
747 const uint32_t length
=
748 std::min(aTextNode
.TextFragment().GetLength(), aMaxLength
);
749 return length
+ textLengthDifference
;
752 static uint32_t ConvertToXPOffset(const Text
& aTextNode
,
753 uint32_t aNativeOffset
) {
754 #if defined(TRANSLATE_NEW_LINES)
755 // On Windows, the length of a native newline ("\r\n") is twice the length of
756 // the XP newline ("\n"), so XP offset is equal to the length of the native
757 // offset minus the number of newlines encountered in the string.
758 return aNativeOffset
- CountNewlinesInNativeLength(aTextNode
, aNativeOffset
);
760 // On other platforms, the native and XP newlines are the same.
761 return aNativeOffset
;
766 uint32_t ContentEventHandler::GetNativeTextLength(const nsAString
& aText
) {
767 const uint32_t textLengthDifference
=
768 #if defined(TRANSLATE_NEW_LINES)
769 // On Windows, the length of a native newline ("\r\n") is twice the length
770 // of the XP newline ("\n"), so XP length is equal to the length of the
771 // native offset plus the number of newlines encountered in the string.
772 CountNewlinesInXPLength(aText
);
774 // On other platforms, the native and XP newlines are the same.
777 return aText
.Length() + textLengthDifference
;
781 bool ContentEventHandler::ShouldBreakLineBefore(const nsIContent
& aContent
,
782 const Element
* aRootElement
) {
783 // We don't need to append linebreak at the start of the root element.
784 if (&aContent
== aRootElement
) {
788 // If it's not an HTML element (including other markup language's elements),
789 // we shouldn't insert like break before that for now. Becoming this is a
790 // problem must be edge case. E.g., when ContentEventHandler is used with
791 // MathML or SVG elements.
792 if (!aContent
.IsHTMLElement()) {
797 nsHTMLTags::CaseSensitiveAtomTagToId(aContent
.NodeInfo()->NameAtom())) {
799 // If the element is <br>, we need to check if the <br> is caused by web
800 // content. Otherwise, i.e., it's caused by internal reason of Gecko,
801 // it shouldn't be exposed as a line break to flatten text.
802 return IsContentBR(aContent
);
805 case eHTMLTag_acronym
:
825 case eHTMLTag_strike
:
826 case eHTMLTag_strong
:
833 // Note that ideally, we should refer the style of the primary frame of
834 // aContent for deciding if it's an inline. However, it's difficult
835 // IMEContentObserver to notify IME of text change caused by style change.
836 // Therefore, currently, we should check only from the tag for now.
838 case eHTMLTag_userdefined
:
839 case eHTMLTag_unknown
:
840 // If the element is unknown element, we shouldn't insert line breaks
841 // before it since unknown elements should be ignored.
848 nsresult
ContentEventHandler::GenerateFlatTextContent(
849 const Element
* aElement
, nsString
& aString
, LineBreakType aLineBreakType
) {
850 MOZ_ASSERT(aString
.IsEmpty());
852 UnsafeSimpleRange rawRange
;
853 nsresult rv
= rawRange
.SelectNodeContents(aElement
);
854 if (NS_WARN_IF(NS_FAILED(rv
))) {
857 return GenerateFlatTextContent(rawRange
, aString
, aLineBreakType
);
860 template <typename NodeType
, typename RangeBoundaryType
>
861 nsresult
ContentEventHandler::GenerateFlatTextContent(
862 const SimpleRangeBase
<NodeType
, RangeBoundaryType
>& aSimpleRange
,
863 nsString
& aString
, LineBreakType aLineBreakType
) {
864 MOZ_ASSERT(aString
.IsEmpty());
866 if (aSimpleRange
.Collapsed()) {
870 nsINode
* startNode
= aSimpleRange
.GetStartContainer();
871 nsINode
* endNode
= aSimpleRange
.GetEndContainer();
872 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
)) {
873 return NS_ERROR_FAILURE
;
876 if (startNode
== endNode
&& startNode
->IsText()) {
877 AppendSubString(aString
, *startNode
->AsText(), aSimpleRange
.StartOffset(),
878 aSimpleRange
.EndOffset() - aSimpleRange
.StartOffset());
879 ConvertToNativeNewlines(aString
);
883 UnsafePreContentIterator preOrderIter
;
884 nsresult rv
= preOrderIter
.Init(aSimpleRange
.Start().AsRaw(),
885 aSimpleRange
.End().AsRaw());
886 if (NS_WARN_IF(NS_FAILED(rv
))) {
889 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
890 nsINode
* node
= preOrderIter
.GetCurrentNode();
891 if (NS_WARN_IF(!node
)) {
894 if (!node
->IsContent()) {
898 if (const Text
* textNode
= Text::FromNode(node
)) {
899 if (textNode
== startNode
) {
900 AppendSubString(aString
, *textNode
, aSimpleRange
.StartOffset(),
901 textNode
->TextLength() - aSimpleRange
.StartOffset());
902 } else if (textNode
== endNode
) {
903 AppendSubString(aString
, *textNode
, 0, aSimpleRange
.EndOffset());
905 AppendString(aString
, *textNode
);
907 } else if (ShouldBreakLineBefore(*node
->AsContent(), mRootElement
)) {
908 aString
.Append(char16_t('\n'));
911 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
912 ConvertToNativeNewlines(aString
);
917 static FontRange
* AppendFontRange(nsTArray
<FontRange
>& aFontRanges
,
918 uint32_t aBaseOffset
) {
919 FontRange
* fontRange
= aFontRanges
.AppendElement();
920 fontRange
->mStartOffset
= aBaseOffset
;
925 uint32_t ContentEventHandler::GetTextLengthInRange(
926 const Text
& aTextNode
, uint32_t aXPStartOffset
, uint32_t aXPEndOffset
,
927 LineBreakType aLineBreakType
) {
928 return aLineBreakType
== LINE_BREAK_TYPE_NATIVE
929 ? GetNativeTextLength(aTextNode
, aXPStartOffset
, aXPEndOffset
)
930 : aXPEndOffset
- aXPStartOffset
;
934 void ContentEventHandler::AppendFontRanges(FontRangeArray
& aFontRanges
,
935 const Text
& aTextNode
,
936 uint32_t aBaseOffset
,
937 uint32_t aXPStartOffset
,
938 uint32_t aXPEndOffset
,
939 LineBreakType aLineBreakType
) {
940 nsIFrame
* frame
= aTextNode
.GetPrimaryFrame();
942 // It is a non-rendered content, create an empty range for it.
943 AppendFontRange(aFontRanges
, aBaseOffset
);
947 uint32_t baseOffset
= aBaseOffset
;
950 nsTextFrame
* text
= do_QueryFrame(frame
);
951 MOZ_ASSERT(text
, "Not a text frame");
954 auto* curr
= static_cast<nsTextFrame
*>(frame
);
956 uint32_t frameXPStart
= std::max(
957 static_cast<uint32_t>(curr
->GetContentOffset()), aXPStartOffset
);
958 uint32_t frameXPEnd
=
959 std::min(static_cast<uint32_t>(curr
->GetContentEnd()), aXPEndOffset
);
960 if (frameXPStart
>= frameXPEnd
) {
961 curr
= curr
->GetNextContinuation();
965 gfxSkipCharsIterator iter
= curr
->EnsureTextRun(nsTextFrame::eInflated
);
966 gfxTextRun
* textRun
= curr
->GetTextRun(nsTextFrame::eInflated
);
968 nsTextFrame
* next
= nullptr;
969 if (frameXPEnd
< aXPEndOffset
) {
970 next
= curr
->GetNextContinuation();
971 while (next
&& next
->GetTextRun(nsTextFrame::eInflated
) == textRun
) {
972 frameXPEnd
= std::min(static_cast<uint32_t>(next
->GetContentEnd()),
975 frameXPEnd
< aXPEndOffset
? next
->GetNextContinuation() : nullptr;
979 gfxTextRun::Range
skipRange(iter
.ConvertOriginalToSkipped(frameXPStart
),
980 iter
.ConvertOriginalToSkipped(frameXPEnd
));
981 uint32_t lastXPEndOffset
= frameXPStart
;
982 for (gfxTextRun::GlyphRunIterator
runIter(textRun
, skipRange
);
983 !runIter
.AtEnd(); runIter
.NextRun()) {
984 gfxFont
* font
= runIter
.GlyphRun()->mFont
.get();
985 uint32_t startXPOffset
=
986 iter
.ConvertSkippedToOriginal(runIter
.StringStart());
987 // It is possible that the first glyph run has exceeded the frame,
988 // because the whole frame is filled by skipped chars.
989 if (startXPOffset
>= frameXPEnd
) {
993 if (startXPOffset
> lastXPEndOffset
) {
994 // Create range for skipped leading chars.
995 AppendFontRange(aFontRanges
, baseOffset
);
996 baseOffset
+= GetTextLengthInRange(aTextNode
, lastXPEndOffset
,
997 startXPOffset
, aLineBreakType
);
1000 FontRange
* fontRange
= AppendFontRange(aFontRanges
, baseOffset
);
1001 fontRange
->mFontName
.Append(NS_ConvertUTF8toUTF16(font
->GetName()));
1003 ParentLayerToScreenScale2D cumulativeResolution
=
1004 ParentLayerToParentLayerScale(
1005 frame
->PresShell()->GetCumulativeResolution()) *
1006 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
1009 std::max(cumulativeResolution
.xScale
, cumulativeResolution
.yScale
);
1011 fontRange
->mFontSize
= font
->GetAdjustedSize() * scale
;
1013 // The converted original offset may exceed the range,
1014 // hence we need to clamp it.
1015 uint32_t endXPOffset
= iter
.ConvertSkippedToOriginal(runIter
.StringEnd());
1016 endXPOffset
= std::min(frameXPEnd
, endXPOffset
);
1017 baseOffset
+= GetTextLengthInRange(aTextNode
, startXPOffset
, endXPOffset
,
1019 lastXPEndOffset
= endXPOffset
;
1021 if (lastXPEndOffset
< frameXPEnd
) {
1022 // Create range for skipped trailing chars. It also handles case
1023 // that the whole frame contains only skipped chars.
1024 AppendFontRange(aFontRanges
, baseOffset
);
1025 baseOffset
+= GetTextLengthInRange(aTextNode
, lastXPEndOffset
, frameXPEnd
,
1033 nsresult
ContentEventHandler::GenerateFlatFontRanges(
1034 const UnsafeSimpleRange
& aSimpleRange
, FontRangeArray
& aFontRanges
,
1035 uint32_t& aLength
, LineBreakType aLineBreakType
) {
1036 MOZ_ASSERT(aFontRanges
.IsEmpty(), "aRanges must be empty array");
1038 if (aSimpleRange
.Collapsed()) {
1042 nsINode
* startNode
= aSimpleRange
.GetStartContainer();
1043 nsINode
* endNode
= aSimpleRange
.GetEndContainer();
1044 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
)) {
1045 return NS_ERROR_FAILURE
;
1048 // baseOffset is the flattened offset of each content node.
1049 uint32_t baseOffset
= 0;
1050 UnsafePreContentIterator preOrderIter
;
1051 nsresult rv
= preOrderIter
.Init(aSimpleRange
.Start().AsRaw(),
1052 aSimpleRange
.End().AsRaw());
1053 if (NS_WARN_IF(NS_FAILED(rv
))) {
1056 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
1057 nsINode
* node
= preOrderIter
.GetCurrentNode();
1058 if (NS_WARN_IF(!node
)) {
1061 if (!node
->IsContent()) {
1064 nsIContent
* content
= node
->AsContent();
1066 if (const Text
* textNode
= Text::FromNode(content
)) {
1067 const uint32_t startOffset
=
1068 textNode
!= startNode
? 0 : aSimpleRange
.StartOffset();
1069 const uint32_t endOffset
= textNode
!= endNode
? textNode
->TextLength()
1070 : aSimpleRange
.EndOffset();
1071 AppendFontRanges(aFontRanges
, *textNode
, baseOffset
, startOffset
,
1072 endOffset
, aLineBreakType
);
1073 baseOffset
+= GetTextLengthInRange(*textNode
, startOffset
, endOffset
,
1075 } else if (ShouldBreakLineBefore(*content
, mRootElement
)) {
1076 if (aFontRanges
.IsEmpty()) {
1077 MOZ_ASSERT(baseOffset
== 0);
1078 FontRange
* fontRange
= AppendFontRange(aFontRanges
, baseOffset
);
1079 if (nsIFrame
* frame
= content
->GetPrimaryFrame()) {
1080 const nsFont
& font
= frame
->GetParent()->StyleFont()->mFont
;
1081 const StyleFontFamilyList
& fontList
= font
.family
.families
;
1082 MOZ_ASSERT(!fontList
.list
.IsEmpty(), "Empty font family?");
1083 const StyleSingleFontFamily
* fontName
=
1084 fontList
.list
.IsEmpty() ? nullptr : &fontList
.list
.AsSpan()[0];
1087 fontName
->AppendToString(name
, false);
1089 AppendUTF8toUTF16(name
, fontRange
->mFontName
);
1091 ParentLayerToScreenScale2D cumulativeResolution
=
1092 ParentLayerToParentLayerScale(
1093 frame
->PresShell()->GetCumulativeResolution()) *
1095 GetTransformToAncestorScaleCrossProcessForFrameMetrics(frame
);
1097 float scale
= std::max(cumulativeResolution
.xScale
,
1098 cumulativeResolution
.yScale
);
1100 fontRange
->mFontSize
= frame
->PresContext()->CSSPixelsToDevPixels(
1101 font
.size
.ToCSSPixels() * scale
);
1104 baseOffset
+= GetBRLength(aLineBreakType
);
1108 aLength
= baseOffset
;
1112 nsresult
ContentEventHandler::ExpandToClusterBoundary(
1113 Text
& aTextNode
, bool aForward
, uint32_t* aXPOffset
) const {
1114 // XXX This method assumes that the frame boundaries must be cluster
1115 // boundaries. It's false, but no problem now, maybe.
1116 if (*aXPOffset
== 0 || *aXPOffset
== aTextNode
.TextLength()) {
1120 NS_ASSERTION(*aXPOffset
<= aTextNode
.TextLength(), "offset is out of range.");
1122 MOZ_DIAGNOSTIC_ASSERT(mDocument
->GetPresShell());
1123 CaretAssociationHint hint
=
1124 aForward
? CaretAssociationHint::Before
: CaretAssociationHint::After
;
1125 nsIFrame
* frame
= SelectionMovementUtils::GetFrameForNodeOffset(
1126 &aTextNode
, int32_t(*aXPOffset
), hint
);
1128 auto [startOffset
, endOffset
] = frame
->GetOffsets();
1129 if (*aXPOffset
== static_cast<uint32_t>(startOffset
) ||
1130 *aXPOffset
== static_cast<uint32_t>(endOffset
)) {
1133 if (!frame
->IsTextFrame()) {
1134 return NS_ERROR_FAILURE
;
1136 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(frame
);
1137 int32_t newOffsetInFrame
= *aXPOffset
- startOffset
;
1138 newOffsetInFrame
+= aForward
? -1 : 1;
1139 // PeekOffsetCharacter() should respect cluster but ignore user-select
1140 // style. If it returns "FOUND", we should use the result. Otherwise,
1141 // we shouldn't use the result because the offset was moved to reversed
1143 nsTextFrame::PeekOffsetCharacterOptions options
;
1144 options
.mRespectClusters
= true;
1145 options
.mIgnoreUserStyleAll
= true;
1146 if (textFrame
->PeekOffsetCharacter(aForward
, &newOffsetInFrame
, options
) ==
1148 *aXPOffset
= startOffset
+ newOffsetInFrame
;
1153 // If the frame isn't available, we only can check surrogate pair...
1154 if (aTextNode
.TextFragment().IsLowSurrogateFollowingHighSurrogateAt(
1156 *aXPOffset
+= aForward
? 1 : -1;
1161 template <typename RangeType
, typename TextNodeType
>
1162 Result
<ContentEventHandler::DOMRangeAndAdjustedOffsetInFlattenedTextBase
<
1163 RangeType
, TextNodeType
>,
1165 ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase(
1166 uint32_t aOffset
, uint32_t aLength
, LineBreakType aLineBreakType
,
1167 bool aExpandToClusterBoundaries
) {
1168 DOMRangeAndAdjustedOffsetInFlattenedTextBase
<RangeType
, TextNodeType
> result
;
1169 result
.mAdjustedOffset
= aOffset
;
1171 // Special case like <br contenteditable>
1172 if (!mRootElement
->HasChildren()) {
1173 nsresult rv
= result
.mRange
.CollapseTo(RawRangeBoundary(mRootElement
, 0u));
1174 if (NS_WARN_IF(NS_FAILED(rv
))) {
1179 UnsafePreContentIterator preOrderIter
;
1180 nsresult rv
= preOrderIter
.Init(mRootElement
);
1181 if (NS_WARN_IF(NS_FAILED(rv
))) {
1185 uint32_t offset
= 0;
1186 uint32_t endOffset
= aOffset
+ aLength
;
1187 bool startSet
= false;
1188 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
1189 nsINode
* node
= preOrderIter
.GetCurrentNode();
1190 if (NS_WARN_IF(!node
)) {
1193 // FYI: mRootElement shouldn't cause any text. So, we can skip it simply.
1194 if (node
== mRootElement
|| !node
->IsContent()) {
1197 nsIContent
* const content
= node
->AsContent();
1198 Text
* const contentAsText
= Text::FromNode(content
);
1200 if (contentAsText
) {
1201 result
.mLastTextNode
= contentAsText
;
1204 uint32_t textLength
= contentAsText
1205 ? GetTextLength(*contentAsText
, aLineBreakType
)
1206 : (ShouldBreakLineBefore(*content
, mRootElement
)
1207 ? GetBRLength(aLineBreakType
)
1213 // When the start offset is in between accumulated offset and the last
1214 // offset of the node, the node is the start node of the range.
1215 if (!startSet
&& aOffset
<= offset
+ textLength
) {
1216 nsINode
* startNode
= nullptr;
1217 Maybe
<uint32_t> startNodeOffset
;
1218 if (contentAsText
) {
1219 // Rule #1.1: [textNode or text[Node or textNode[
1220 uint32_t xpOffset
= aOffset
- offset
;
1221 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
1222 xpOffset
= ConvertToXPOffset(*contentAsText
, xpOffset
);
1225 if (aExpandToClusterBoundaries
) {
1226 const uint32_t oldXPOffset
= xpOffset
;
1228 ExpandToClusterBoundary(*contentAsText
, false, &xpOffset
);
1229 if (NS_WARN_IF(NS_FAILED(rv
))) {
1232 // This is correct since a cluster shouldn't include line break.
1233 result
.mAdjustedOffset
-= (oldXPOffset
- xpOffset
);
1235 startNode
= contentAsText
;
1236 startNodeOffset
= Some(xpOffset
);
1237 } else if (aOffset
< offset
+ textLength
) {
1238 // Rule #1.2 [<element>
1239 startNode
= content
->GetParent();
1240 if (NS_WARN_IF(!startNode
)) {
1241 return Err(NS_ERROR_FAILURE
);
1243 startNodeOffset
= startNode
->ComputeIndexOf(content
);
1244 if (NS_WARN_IF(startNodeOffset
.isNothing())) {
1245 // The content is being removed from the parent!
1246 return Err(NS_ERROR_FAILURE
);
1248 } else if (!content
->HasChildren()) {
1249 // Rule #1.3: <element/>[
1250 startNode
= content
->GetParent();
1251 if (NS_WARN_IF(!startNode
)) {
1252 return Err(NS_ERROR_FAILURE
);
1254 startNodeOffset
= startNode
->ComputeIndexOf(content
);
1255 if (NS_WARN_IF(startNodeOffset
.isNothing())) {
1256 // The content is being removed from the parent!
1257 return Err(NS_ERROR_FAILURE
);
1259 MOZ_ASSERT(*startNodeOffset
!= UINT32_MAX
);
1260 ++(*startNodeOffset
);
1262 // Rule #1.4: <element>[
1263 startNode
= content
;
1264 startNodeOffset
= Some(0);
1266 NS_ASSERTION(startNode
, "startNode must not be nullptr");
1267 MOZ_ASSERT(startNodeOffset
.isSome(),
1268 "startNodeOffset must not be Nothing");
1269 rv
= result
.mRange
.SetStart(startNode
, *startNodeOffset
);
1270 if (NS_WARN_IF(NS_FAILED(rv
))) {
1276 rv
= result
.mRange
.SetEnd(startNode
, *startNodeOffset
);
1277 if (NS_WARN_IF(NS_FAILED(rv
))) {
1284 // When the end offset is in the content, the node is the end node of the
1286 if (endOffset
<= offset
+ textLength
) {
1287 MOZ_ASSERT(startSet
, "The start of the range should've been set already");
1288 if (contentAsText
) {
1289 // Rule #2.1: ]textNode or text]Node or textNode]
1290 uint32_t xpOffset
= endOffset
- offset
;
1291 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
1292 const uint32_t xpOffsetCurrent
=
1293 ConvertToXPOffset(*contentAsText
, xpOffset
);
1294 if (xpOffset
&& GetBRLength(aLineBreakType
) > 1) {
1295 MOZ_ASSERT(GetBRLength(aLineBreakType
) == 2);
1296 const uint32_t xpOffsetPre
=
1297 ConvertToXPOffset(*contentAsText
, xpOffset
- 1);
1298 // If previous character's XP offset is same as current character's,
1299 // it means that the end offset is between \r and \n. So, the
1300 // range end should be after the \n.
1301 if (xpOffsetPre
== xpOffsetCurrent
) {
1302 xpOffset
= xpOffsetCurrent
+ 1;
1304 xpOffset
= xpOffsetCurrent
;
1308 if (aExpandToClusterBoundaries
) {
1310 ExpandToClusterBoundary(*contentAsText
, true, &xpOffset
);
1311 if (NS_WARN_IF(NS_FAILED(rv
))) {
1315 NS_ASSERTION(xpOffset
<= INT32_MAX
, "The end node offset is too large");
1316 nsresult rv
= result
.mRange
.SetEnd(contentAsText
, xpOffset
);
1317 if (NS_WARN_IF(NS_FAILED(rv
))) {
1323 if (endOffset
== offset
) {
1324 // Rule #2.2: ]<element>
1325 // NOTE: Please don't crash on release builds because it must be
1326 // overreaction but we shouldn't allow this bug when some
1327 // automated tests find this.
1329 "This case should've already been handled at "
1330 "the last node which caused some text");
1331 return Err(NS_ERROR_FAILURE
);
1334 if (content
->HasChildren() &&
1335 ShouldBreakLineBefore(*content
, mRootElement
)) {
1336 // Rule #2.3: </element>]
1337 rv
= result
.mRange
.SetEnd(content
, 0);
1338 if (NS_WARN_IF(NS_FAILED(rv
))) {
1344 // Rule #2.4: <element/>]
1345 nsINode
* endNode
= content
->GetParent();
1346 if (NS_WARN_IF(!endNode
)) {
1347 return Err(NS_ERROR_FAILURE
);
1349 const Maybe
<uint32_t> indexInParent
= endNode
->ComputeIndexOf(content
);
1350 if (NS_WARN_IF(indexInParent
.isNothing())) {
1351 // The content is being removed from the parent!
1352 return Err(NS_ERROR_FAILURE
);
1354 MOZ_ASSERT(*indexInParent
!= UINT32_MAX
);
1355 rv
= result
.mRange
.SetEnd(endNode
, *indexInParent
+ 1);
1356 if (NS_WARN_IF(NS_FAILED(rv
))) {
1362 offset
+= textLength
;
1367 // Rule #1.5: <root>[</root>
1368 // When there are no nodes causing text, the start of the DOM range
1369 // should be start of the root node since clicking on such editor (e.g.,
1370 // <div contenteditable><span></span></div>) sets caret to the start of
1371 // the editor (i.e., before <span> in the example).
1372 rv
= result
.mRange
.SetStart(mRootElement
, 0);
1373 if (NS_WARN_IF(NS_FAILED(rv
))) {
1377 rv
= result
.mRange
.SetEnd(mRootElement
, 0);
1378 if (NS_WARN_IF(NS_FAILED(rv
))) {
1384 // Rule #1.5: [</root>
1385 rv
= result
.mRange
.SetStart(mRootElement
, mRootElement
->GetChildCount());
1386 if (NS_WARN_IF(NS_FAILED(rv
))) {
1390 result
.mAdjustedOffset
= offset
;
1392 // Rule #2.5: ]</root>
1393 rv
= result
.mRange
.SetEnd(mRootElement
, mRootElement
->GetChildCount());
1394 if (NS_WARN_IF(NS_FAILED(rv
))) {
1401 LineBreakType
ContentEventHandler::GetLineBreakType(
1402 WidgetQueryContentEvent
* aEvent
) {
1403 return GetLineBreakType(aEvent
->mUseNativeLineBreak
);
1407 LineBreakType
ContentEventHandler::GetLineBreakType(
1408 WidgetSelectionEvent
* aEvent
) {
1409 return GetLineBreakType(aEvent
->mUseNativeLineBreak
);
1413 LineBreakType
ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak
) {
1414 return aUseNativeLineBreak
? LINE_BREAK_TYPE_NATIVE
: LINE_BREAK_TYPE_XP
;
1417 nsresult
ContentEventHandler::HandleQueryContentEvent(
1418 WidgetQueryContentEvent
* aEvent
) {
1419 nsresult rv
= NS_ERROR_NOT_IMPLEMENTED
;
1420 switch (aEvent
->mMessage
) {
1421 case eQuerySelectedText
:
1422 rv
= OnQuerySelectedText(aEvent
);
1424 case eQueryTextContent
:
1425 rv
= OnQueryTextContent(aEvent
);
1427 case eQueryCaretRect
:
1428 rv
= OnQueryCaretRect(aEvent
);
1430 case eQueryTextRect
:
1431 rv
= OnQueryTextRect(aEvent
);
1433 case eQueryTextRectArray
:
1434 rv
= OnQueryTextRectArray(aEvent
);
1436 case eQueryEditorRect
:
1437 rv
= OnQueryEditorRect(aEvent
);
1439 case eQueryContentState
:
1440 rv
= OnQueryContentState(aEvent
);
1442 case eQuerySelectionAsTransferable
:
1443 rv
= OnQuerySelectionAsTransferable(aEvent
);
1445 case eQueryCharacterAtPoint
:
1446 rv
= OnQueryCharacterAtPoint(aEvent
);
1448 case eQueryDOMWidgetHittest
:
1449 rv
= OnQueryDOMWidgetHittest(aEvent
);
1454 if (NS_FAILED(rv
)) {
1455 aEvent
->mReply
.reset(); // Mark the query failed.
1459 MOZ_ASSERT(aEvent
->Succeeded());
1463 // Similar to nsFrameSelection::GetFrameForNodeOffset,
1464 // but this is more flexible for OnQueryTextRect to use
1465 static Result
<nsIFrame
*, nsresult
> GetFrameForTextRect(const nsINode
* aNode
,
1466 int32_t aNodeOffset
,
1468 const nsIContent
* content
= nsIContent::FromNodeOrNull(aNode
);
1469 if (NS_WARN_IF(!content
)) {
1470 return Err(NS_ERROR_UNEXPECTED
);
1472 nsIFrame
* frame
= content
->GetPrimaryFrame();
1473 // The node may be invisible, e.g., `display: none`, invisible text node
1474 // around block elements, etc. Therefore, don't warn when we don't find
1479 int32_t childNodeOffset
= 0;
1480 nsIFrame
* returnFrame
= nullptr;
1481 nsresult rv
= frame
->GetChildFrameContainingOffset(
1482 aNodeOffset
, aHint
, &childNodeOffset
, &returnFrame
);
1483 if (NS_FAILED(rv
)) {
1489 nsresult
ContentEventHandler::OnQuerySelectedText(
1490 WidgetQueryContentEvent
* aEvent
) {
1491 nsresult rv
= Init(aEvent
);
1492 if (NS_FAILED(rv
)) {
1496 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
1498 if (!mFirstSelectedSimpleRange
.IsPositioned()) {
1499 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
1500 MOZ_ASSERT_IF(mSelection
, !mSelection
->RangeCount());
1501 // This is special case that `mReply` is emplaced, but mOffsetAndData is
1502 // not emplaced but treated as succeeded because of no selection ranges
1507 const UnsafeSimpleRange
firstSelectedSimpleRange(mFirstSelectedSimpleRange
);
1508 nsINode
* const startNode
= firstSelectedSimpleRange
.GetStartContainer();
1509 nsINode
* const endNode
= firstSelectedSimpleRange
.GetEndContainer();
1511 // Make sure the selection is within the root content range.
1512 if (!startNode
->IsInclusiveDescendantOf(mRootElement
) ||
1513 !endNode
->IsInclusiveDescendantOf(mRootElement
)) {
1514 return NS_ERROR_NOT_AVAILABLE
;
1517 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
1518 uint32_t startOffset
= 0;
1519 if (NS_WARN_IF(NS_FAILED(GetStartOffset(firstSelectedSimpleRange
,
1520 &startOffset
, lineBreakType
)))) {
1521 return NS_ERROR_FAILURE
;
1524 const RawRangeBoundary anchorRef
= mSelection
->RangeCount() > 0
1525 ? mSelection
->AnchorRef().AsRaw()
1526 : firstSelectedSimpleRange
.Start();
1527 const RawRangeBoundary focusRef
= mSelection
->RangeCount() > 0
1528 ? mSelection
->FocusRef().AsRaw()
1529 : firstSelectedSimpleRange
.End();
1530 if (NS_WARN_IF(!anchorRef
.IsSet()) || NS_WARN_IF(!focusRef
.IsSet())) {
1531 return NS_ERROR_FAILURE
;
1534 if (mSelection
->RangeCount()) {
1535 // If there is only one selection range, the anchor/focus node and offset
1536 // are the information of the range. Therefore, we have the direction
1538 if (mSelection
->RangeCount() == 1) {
1539 // The selection's points should always be comparable, independent of the
1540 // selection (see nsISelectionController.idl).
1541 Maybe
<int32_t> compare
=
1542 nsContentUtils::ComparePoints(anchorRef
, focusRef
);
1543 if (compare
.isNothing()) {
1544 return NS_ERROR_FAILURE
;
1547 aEvent
->mReply
->mReversed
= compare
.value() > 0;
1549 // However, if there are 2 or more selection ranges, we have no information
1552 aEvent
->mReply
->mReversed
= false;
1555 nsString selectedString
;
1556 if (!firstSelectedSimpleRange
.Collapsed() &&
1557 NS_WARN_IF(NS_FAILED(GenerateFlatTextContent(
1558 firstSelectedSimpleRange
, selectedString
, lineBreakType
)))) {
1559 return NS_ERROR_FAILURE
;
1561 aEvent
->mReply
->mOffsetAndData
.emplace(startOffset
, selectedString
,
1562 OffsetAndDataFor::SelectedString
);
1564 NS_ASSERTION(anchorRef
== focusRef
,
1565 "When mSelection doesn't have selection, "
1566 "mFirstSelectedRawRange must be collapsed");
1568 aEvent
->mReply
->mReversed
= false;
1569 aEvent
->mReply
->mOffsetAndData
.emplace(startOffset
, EmptyString(),
1570 OffsetAndDataFor::SelectedString
);
1573 Result
<nsIFrame
*, nsresult
> frameForTextRectOrError
= GetFrameForTextRect(
1574 focusRef
.Container(),
1575 focusRef
.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets
).valueOr(0),
1577 if (NS_WARN_IF(frameForTextRectOrError
.isErr()) ||
1578 !frameForTextRectOrError
.inspect()) {
1579 aEvent
->mReply
->mWritingMode
= WritingMode();
1581 aEvent
->mReply
->mWritingMode
=
1582 frameForTextRectOrError
.inspect()->GetWritingMode();
1585 MOZ_ASSERT(aEvent
->Succeeded());
1589 nsresult
ContentEventHandler::OnQueryTextContent(
1590 WidgetQueryContentEvent
* aEvent
) {
1591 nsresult rv
= Init(aEvent
);
1592 if (NS_FAILED(rv
)) {
1596 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
1598 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
1600 Result
<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
1601 domRangeAndAdjustedOffsetOrError
= ConvertFlatTextOffsetToUnsafeDOMRange(
1602 aEvent
->mInput
.mOffset
, aEvent
->mInput
.mLength
, lineBreakType
, false);
1603 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError
.isErr())) {
1605 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() failed");
1606 return NS_ERROR_FAILURE
;
1608 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
1609 domRangeAndAdjustedOffset
= domRangeAndAdjustedOffsetOrError
.unwrap();
1611 nsString textInRange
;
1612 if (NS_WARN_IF(NS_FAILED(GenerateFlatTextContent(
1613 domRangeAndAdjustedOffset
.mRange
, textInRange
, lineBreakType
)))) {
1614 return NS_ERROR_FAILURE
;
1617 aEvent
->mReply
->mOffsetAndData
.emplace(
1618 domRangeAndAdjustedOffset
.mAdjustedOffset
, textInRange
,
1619 OffsetAndDataFor::EditorString
);
1621 if (aEvent
->mWithFontRanges
) {
1622 uint32_t fontRangeLength
;
1623 if (NS_WARN_IF(NS_FAILED(GenerateFlatFontRanges(
1624 domRangeAndAdjustedOffset
.mRange
, aEvent
->mReply
->mFontRanges
,
1625 fontRangeLength
, lineBreakType
)))) {
1626 return NS_ERROR_FAILURE
;
1629 MOZ_ASSERT(fontRangeLength
== aEvent
->mReply
->DataLength(),
1630 "Font ranges doesn't match the string");
1633 MOZ_ASSERT(aEvent
->Succeeded());
1637 void ContentEventHandler::EnsureNonEmptyRect(nsRect
& aRect
) const {
1638 // See the comment in ContentEventHandler.h why this doesn't set them to
1639 // one device pixel.
1640 aRect
.height
= std::max(1, aRect
.height
);
1641 aRect
.width
= std::max(1, aRect
.width
);
1644 void ContentEventHandler::EnsureNonEmptyRect(LayoutDeviceIntRect
& aRect
) const {
1645 aRect
.height
= std::max(1, aRect
.height
);
1646 aRect
.width
= std::max(1, aRect
.width
);
1649 template <typename NodeType
, typename RangeBoundaryType
>
1650 ContentEventHandler::FrameAndNodeOffset
1651 ContentEventHandler::GetFirstFrameInRangeForTextRect(
1652 const SimpleRangeBase
<NodeType
, RangeBoundaryType
>& aSimpleRange
) {
1653 RawNodePosition nodePosition
;
1654 UnsafePreContentIterator preOrderIter
;
1655 nsresult rv
= preOrderIter
.Init(aSimpleRange
.Start().AsRaw(),
1656 aSimpleRange
.End().AsRaw());
1657 if (NS_WARN_IF(NS_FAILED(rv
))) {
1658 return FrameAndNodeOffset();
1660 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
1661 nsINode
* node
= preOrderIter
.GetCurrentNode();
1662 if (NS_WARN_IF(!node
)) {
1666 auto* content
= nsIContent::FromNode(node
);
1667 if (MOZ_UNLIKELY(!content
)) {
1671 // If the node is invisible (e.g., the node is or is in an invisible node or
1672 // it's a white-space only text node around a block boundary), we should
1674 if (!content
->GetPrimaryFrame()) {
1678 if (auto* textNode
= Text::FromNode(content
)) {
1679 // If the range starts at the end of a text node, we need to find
1680 // next node which causes text.
1681 const uint32_t offsetInNode
= textNode
== aSimpleRange
.GetStartContainer()
1682 ? aSimpleRange
.StartOffset()
1684 if (offsetInNode
< textNode
->TextDataLength()) {
1685 nodePosition
= {textNode
, offsetInNode
};
1691 // If the element node causes a line break before it, it's the first
1692 // node causing text.
1693 if (ShouldBreakLineBefore(*content
, mRootElement
) ||
1694 IsPaddingBR(*content
)) {
1695 nodePosition
= {content
, 0u};
1699 if (!nodePosition
.IsSetAndValid()) {
1700 return FrameAndNodeOffset();
1703 Result
<nsIFrame
*, nsresult
> firstFrameOrError
= GetFrameForTextRect(
1704 nodePosition
.Container(),
1705 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
), true);
1706 if (NS_WARN_IF(firstFrameOrError
.isErr()) || !firstFrameOrError
.inspect()) {
1707 return FrameAndNodeOffset();
1709 return FrameAndNodeOffset(
1710 firstFrameOrError
.inspect(),
1711 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
));
1714 template <typename NodeType
, typename RangeBoundaryType
>
1715 ContentEventHandler::FrameAndNodeOffset
1716 ContentEventHandler::GetLastFrameInRangeForTextRect(
1717 const SimpleRangeBase
<NodeType
, RangeBoundaryType
>& aSimpleRange
) {
1718 RawNodePosition nodePosition
;
1719 UnsafePreContentIterator preOrderIter
;
1720 nsresult rv
= preOrderIter
.Init(aSimpleRange
.Start().AsRaw(),
1721 aSimpleRange
.End().AsRaw());
1722 if (NS_WARN_IF(NS_FAILED(rv
))) {
1723 return FrameAndNodeOffset();
1726 const RangeBoundaryType
& endPoint
= aSimpleRange
.End();
1727 MOZ_ASSERT(endPoint
.IsSetAndValid());
1728 // If the end point is start of a text node or specified by its parent and
1729 // index, the node shouldn't be included into the range. For example,
1730 // with this case, |<p>abc[<br>]def</p>|, the range ends at 3rd children of
1731 // <p> (see the range creation rules, "2.4. Cases: <element/>]"). This causes
1732 // following frames:
1739 // So, if this method includes the 2nd text frame's rect to its result, the
1740 // caller will return too tall rect which includes 2 lines in this case isn't
1741 // expected by native IME (e.g., popup of IME will be positioned at bottom
1742 // of "d" instead of right-bottom of "c"). Therefore, this method shouldn't
1743 // include the last frame when its content isn't really in aSimpleRange.
1744 nsINode
* nextNodeOfRangeEnd
= nullptr;
1745 if (endPoint
.Container()->IsText()) {
1746 // Don't set nextNodeOfRangeEnd to the start node of aSimpleRange because if
1747 // the container of the end is same as start node of the range, the text
1748 // node shouldn't be next of range end even if the offset is 0. This
1749 // could occur with empty text node.
1750 if (endPoint
.IsStartOfContainer() &&
1751 aSimpleRange
.GetStartContainer() != endPoint
.Container()) {
1752 nextNodeOfRangeEnd
= endPoint
.Container();
1754 } else if (endPoint
.IsSetAndValid()) {
1755 nextNodeOfRangeEnd
= endPoint
.GetChildAtOffset();
1758 for (preOrderIter
.Last(); !preOrderIter
.IsDone(); preOrderIter
.Prev()) {
1759 nsINode
* node
= preOrderIter
.GetCurrentNode();
1760 if (NS_WARN_IF(!node
)) {
1764 if (node
== nextNodeOfRangeEnd
) {
1768 auto* content
= nsIContent::FromNode(node
);
1769 if (MOZ_UNLIKELY(!content
)) {
1773 // If the node is invisible (e.g., the node is or is in an invisible node or
1774 // it's a white-space only text node around a block boundary), we should
1776 if (!content
->GetPrimaryFrame()) {
1780 if (auto* textNode
= Text::FromNode(node
)) {
1781 nodePosition
= {textNode
, textNode
== aSimpleRange
.GetEndContainer()
1782 ? aSimpleRange
.EndOffset()
1783 : textNode
->TextDataLength()};
1785 // If the text node is empty or the last node of the range but the index
1786 // is 0, we should store current position but continue looking for
1787 // previous node (If there are no nodes before it, we should use current
1788 // node position for returning its frame).
1789 if (*nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
) ==
1796 if (ShouldBreakLineBefore(*content
, mRootElement
) ||
1797 IsPaddingBR(*content
)) {
1798 nodePosition
= {content
, 0u};
1803 if (!nodePosition
.IsSet()) {
1804 return FrameAndNodeOffset();
1807 Result
<nsIFrame
*, nsresult
> lastFrameOrError
= GetFrameForTextRect(
1808 nodePosition
.Container(),
1809 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
), true);
1810 if (NS_WARN_IF(lastFrameOrError
.isErr()) || !lastFrameOrError
.inspect()) {
1811 return FrameAndNodeOffset();
1814 // If the last frame is a text frame, we need to check if the range actually
1815 // includes at least one character in the range. Therefore, if it's not a
1816 // text frame, we need to do nothing anymore.
1817 if (!lastFrameOrError
.inspect()->IsTextFrame()) {
1818 return FrameAndNodeOffset(
1819 lastFrameOrError
.inspect(),
1820 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
));
1823 int32_t start
= lastFrameOrError
.inspect()->GetOffsets().first
;
1825 // If the start offset in the node is same as the computed offset in the
1826 // node and it's not 0, the frame shouldn't be added to the text rect. So,
1827 // this should return previous text frame and its last offset if there is
1828 // at least one text frame.
1829 if (*nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
) &&
1830 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
) ==
1831 static_cast<uint32_t>(start
)) {
1832 const uint32_t newNodePositionOffset
=
1833 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
);
1834 MOZ_ASSERT(newNodePositionOffset
!= 0);
1835 nodePosition
= {nodePosition
.Container(), newNodePositionOffset
- 1u};
1836 lastFrameOrError
= GetFrameForTextRect(
1837 nodePosition
.Container(),
1838 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
),
1840 if (NS_WARN_IF(lastFrameOrError
.isErr()) || !lastFrameOrError
.inspect()) {
1841 return FrameAndNodeOffset();
1845 return FrameAndNodeOffset(
1846 lastFrameOrError
.inspect(),
1847 *nodePosition
.Offset(RawNodePosition::OffsetFilter::kValidOffsets
));
1850 ContentEventHandler::FrameRelativeRect
1851 ContentEventHandler::GetLineBreakerRectBefore(nsIFrame
* aFrame
) {
1852 // Note that this method should be called only with an element's frame whose
1853 // open tag causes a line break or moz-<br> for computing empty last line's
1855 MOZ_ASSERT(aFrame
->GetContent());
1856 MOZ_ASSERT(ShouldBreakLineBefore(*aFrame
->GetContent(), mRootElement
) ||
1857 IsPaddingBR(*aFrame
->GetContent()));
1859 nsIFrame
* frameForFontMetrics
= aFrame
;
1861 // If it's not a <br> frame, this method computes the line breaker's rect
1862 // outside the frame. Therefore, we need to compute with parent frame's
1863 // font metrics in such case.
1864 if (!aFrame
->IsBrFrame() && aFrame
->GetParent()) {
1865 frameForFontMetrics
= aFrame
->GetParent();
1868 // Note that <br> element's rect is decided with line-height but we need
1869 // a rect only with font height. Additionally, <br> frame's width and
1870 // height are 0 in quirks mode if it's not an empty line. So, we cannot
1871 // use frame rect information even if it's a <br> frame.
1873 RefPtr
<nsFontMetrics
> fontMetrics
=
1874 nsLayoutUtils::GetInflatedFontMetricsForFrame(frameForFontMetrics
);
1875 if (NS_WARN_IF(!fontMetrics
)) {
1876 return FrameRelativeRect();
1879 const WritingMode kWritingMode
= frameForFontMetrics
->GetWritingMode();
1881 auto caretBlockAxisMetrics
=
1882 aFrame
->GetCaretBlockAxisMetrics(kWritingMode
, *fontMetrics
);
1883 nscoord inlineOffset
= 0;
1885 // If aFrame isn't a <br> frame, caret should be at outside of it because
1886 // the line break is before its open tag. For example, case of
1887 // |<div><p>some text</p></div>|, caret is before <p> element and in <div>
1888 // element, the caret should be left of top-left corner of <p> element like:
1890 // +-<div>------------------- <div>'s border box
1891 // | I +-<p>----------------- <p>'s border box
1897 // However, this is a hack for unusual scenario. This hack shouldn't be
1898 // used as far as possible.
1899 if (!aFrame
->IsBrFrame()) {
1900 if (kWritingMode
.IsVertical() && !kWritingMode
.IsLineInverted()) {
1901 // above of top-right corner of aFrame.
1902 caretBlockAxisMetrics
.mOffset
=
1903 aFrame
->GetRect().XMost() - caretBlockAxisMetrics
.mExtent
;
1905 // above (For vertical) or left (For horizontal) of top-left corner of
1907 caretBlockAxisMetrics
.mOffset
= 0;
1909 inlineOffset
= -aFrame
->PresContext()->AppUnitsPerDevPixel();
1911 FrameRelativeRect
result(aFrame
);
1912 if (kWritingMode
.IsVertical()) {
1913 result
.mRect
.x
= caretBlockAxisMetrics
.mOffset
;
1914 result
.mRect
.y
= inlineOffset
;
1915 result
.mRect
.width
= caretBlockAxisMetrics
.mExtent
;
1917 result
.mRect
.x
= inlineOffset
;
1918 result
.mRect
.y
= caretBlockAxisMetrics
.mOffset
;
1919 result
.mRect
.height
= caretBlockAxisMetrics
.mExtent
;
1924 ContentEventHandler::FrameRelativeRect
1925 ContentEventHandler::GuessLineBreakerRectAfter(const Text
& aTextNode
) {
1926 FrameRelativeRect result
;
1927 const int32_t length
= static_cast<int32_t>(aTextNode
.TextLength());
1928 if (NS_WARN_IF(length
< 0)) {
1931 // Get the last nsTextFrame which is caused by aTextNode. Note that
1932 // a text node can cause multiple text frames, e.g., the text is too long
1933 // and wrapped by its parent block or the text has line breakers and its
1934 // white-space property respects the line breakers (e.g., |pre|).
1935 Result
<nsIFrame
*, nsresult
> lastTextFrameOrError
=
1936 GetFrameForTextRect(&aTextNode
, length
, true);
1937 if (NS_WARN_IF(lastTextFrameOrError
.isErr()) ||
1938 !lastTextFrameOrError
.inspect()) {
1941 const nsRect kLastTextFrameRect
= lastTextFrameOrError
.inspect()->GetRect();
1942 if (lastTextFrameOrError
.inspect()->GetWritingMode().IsVertical()) {
1943 // Below of the last text frame.
1944 result
.mRect
.SetRect(0, kLastTextFrameRect
.height
, kLastTextFrameRect
.width
,
1947 // Right of the last text frame (not bidi-aware).
1948 result
.mRect
.SetRect(kLastTextFrameRect
.width
, 0, 0,
1949 kLastTextFrameRect
.height
);
1951 result
.mBaseFrame
= lastTextFrameOrError
.unwrap();
1955 ContentEventHandler::FrameRelativeRect
1956 ContentEventHandler::GuessFirstCaretRectIn(nsIFrame
* aFrame
) {
1957 const WritingMode kWritingMode
= aFrame
->GetWritingMode();
1958 nsPresContext
* presContext
= aFrame
->PresContext();
1960 // Computes the font height, but if it's not available, we should use
1961 // default font size of Firefox. The default font size in default settings
1963 RefPtr
<nsFontMetrics
> fontMetrics
=
1964 nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame
);
1965 const nscoord kMaxHeight
= fontMetrics
1966 ? fontMetrics
->MaxHeight()
1967 : 16 * presContext
->AppUnitsPerDevPixel();
1970 const nsRect kContentRect
= aFrame
->GetContentRect() - aFrame
->GetPosition();
1971 caretRect
.y
= kContentRect
.y
;
1972 if (!kWritingMode
.IsVertical()) {
1973 if (kWritingMode
.IsBidiLTR()) {
1974 caretRect
.x
= kContentRect
.x
;
1976 // Move 1px left for the space of caret itself.
1977 const nscoord kOnePixel
= presContext
->AppUnitsPerDevPixel();
1978 caretRect
.x
= kContentRect
.XMost() - kOnePixel
;
1980 caretRect
.height
= kMaxHeight
;
1981 // However, don't add kOnePixel here because it may cause 2px width at
1982 // aligning the edge to device pixels.
1983 caretRect
.width
= 1;
1985 if (kWritingMode
.IsVerticalLR()) {
1986 caretRect
.x
= kContentRect
.x
;
1988 caretRect
.x
= kContentRect
.XMost() - kMaxHeight
;
1990 caretRect
.width
= kMaxHeight
;
1991 // Don't add app units for a device pixel because it may cause 2px height
1992 // at aligning the edge to device pixels.
1993 caretRect
.height
= 1;
1995 return FrameRelativeRect(caretRect
, aFrame
);
1999 LayoutDeviceIntRect
ContentEventHandler::GetCaretRectBefore(
2000 const LayoutDeviceIntRect
& aCharRect
, const WritingMode
& aWritingMode
) {
2001 LayoutDeviceIntRect
caretRectBefore(aCharRect
);
2002 if (aWritingMode
.IsVertical()) {
2003 caretRectBefore
.height
= 1;
2005 // TODO: Make here bidi-aware.
2006 caretRectBefore
.width
= 1;
2008 return caretRectBefore
;
2012 nsRect
ContentEventHandler::GetCaretRectBefore(
2013 const nsRect
& aCharRect
, const WritingMode
& aWritingMode
) {
2014 nsRect
caretRectBefore(aCharRect
);
2015 if (aWritingMode
.IsVertical()) {
2016 // For making the height 1 device pixel after aligning the rect edges to
2017 // device pixels, don't add one device pixel in app units here.
2018 caretRectBefore
.height
= 1;
2020 // TODO: Make here bidi-aware.
2021 // For making the width 1 device pixel after aligning the rect edges to
2022 // device pixels, don't add one device pixel in app units here.
2023 caretRectBefore
.width
= 1;
2025 return caretRectBefore
;
2029 LayoutDeviceIntRect
ContentEventHandler::GetCaretRectAfter(
2030 const LayoutDeviceIntRect
& aCharRect
, const WritingMode
& aWritingMode
) {
2031 LayoutDeviceIntRect
caretRectAfter(aCharRect
);
2032 if (aWritingMode
.IsVertical()) {
2033 caretRectAfter
.y
= aCharRect
.YMost() + 1;
2034 caretRectAfter
.height
= 1;
2036 // TODO: Make here bidi-aware.
2037 caretRectAfter
.x
= aCharRect
.XMost() + 1;
2038 caretRectAfter
.width
= 1;
2040 return caretRectAfter
;
2044 nsRect
ContentEventHandler::GetCaretRectAfter(nsPresContext
& aPresContext
,
2045 const nsRect
& aCharRect
,
2046 const WritingMode
& aWritingMode
) {
2047 nsRect
caretRectAfter(aCharRect
);
2048 const nscoord onePixel
= aPresContext
.AppUnitsPerDevPixel();
2049 if (aWritingMode
.IsVertical()) {
2050 caretRectAfter
.y
= aCharRect
.YMost() + onePixel
;
2051 // For making the height 1 device pixel after aligning the rect edges to
2052 // device pixels, don't add one device pixel in app units here.
2053 caretRectAfter
.height
= 1;
2055 // TODO: Make here bidi-aware.
2056 caretRectAfter
.x
= aCharRect
.XMost() + onePixel
;
2057 // For making the width 1 device pixel after aligning the rect edges to
2058 // device pixels, don't add one device pixel in app units here.
2059 caretRectAfter
.width
= 1;
2061 return caretRectAfter
;
2064 nsresult
ContentEventHandler::OnQueryTextRectArray(
2065 WidgetQueryContentEvent
* aEvent
) {
2066 nsresult rv
= Init(aEvent
);
2067 if (NS_WARN_IF(NS_FAILED(rv
))) {
2071 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
2073 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
2074 const uint32_t kBRLength
= GetBRLength(lineBreakType
);
2076 WritingMode lastVisibleFrameWritingMode
;
2077 LayoutDeviceIntRect rect
;
2078 uint32_t offset
= aEvent
->mInput
.mOffset
;
2079 const uint32_t kEndOffset
= aEvent
->mInput
.EndOffset();
2080 bool wasLineBreaker
= false;
2081 // lastCharRect stores the last charRect value (see below for the detail of
2083 nsRect lastCharRect
;
2084 // lastFrame is base frame of lastCharRect.
2085 // TODO: We should look for this if the first text is not visible. However,
2086 // users cannot put caret invisible text and users cannot type in it
2087 // at least only with user's operations. Therefore, we don't need to
2088 // fix this immediately.
2089 nsIFrame
* lastFrame
= nullptr;
2090 nsAutoString flattenedAllText
;
2091 flattenedAllText
.SetIsVoid(true);
2092 while (offset
< kEndOffset
) {
2093 Result
<DOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
2094 domRangeAndAdjustedOffsetOrError
=
2095 ConvertFlatTextOffsetToDOMRange(offset
, 1, lineBreakType
, true);
2096 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError
.isErr())) {
2098 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() failed");
2099 return domRangeAndAdjustedOffsetOrError
.unwrapErr();
2101 const DOMRangeAndAdjustedOffsetInFlattenedText domRangeAndAdjustedOffset
=
2102 domRangeAndAdjustedOffsetOrError
.unwrap();
2104 // TODO: When we crossed parent block boundary now, we should fill pending
2105 // character rects with caret rect after the last visible character
2108 // If the range is collapsed, offset has already reached the end of the
2110 if (domRangeAndAdjustedOffset
.mRange
.Collapsed()) {
2114 // Get the first frame which causes some text after the offset.
2115 FrameAndNodeOffset firstFrame
=
2116 GetFirstFrameInRangeForTextRect(domRangeAndAdjustedOffset
.mRange
);
2118 // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
2119 // means that the offset reached the end of contents or there is no visible
2120 // frame in the range generating flattened text.
2121 if (!firstFrame
.IsValid()) {
2122 if (flattenedAllText
.IsVoid()) {
2123 flattenedAllText
.SetIsVoid(false);
2124 if (NS_WARN_IF(NS_FAILED(GenerateFlatTextContent(
2125 mRootElement
, flattenedAllText
, lineBreakType
)))) {
2126 NS_WARNING("ContentEventHandler::GenerateFlatTextContent() failed");
2127 return NS_ERROR_FAILURE
;
2130 // If we've reached end of the root, append caret rect at the end of
2132 if (offset
>= flattenedAllText
.Length()) {
2135 // Otherwise, we're in an invisible node. If the node is followed by a
2136 // block boundary causing a line break, we can use the boundary.
2137 // Otherwise, if the node follows a block boundary of a parent block, we
2138 // can use caret rect at previous visible frame causing flattened text.
2139 const uint32_t remainingLengthInCurrentRange
= [&]() {
2140 if (domRangeAndAdjustedOffset
.mLastTextNode
) {
2141 if (domRangeAndAdjustedOffset
.RangeStartsFromLastTextNode()) {
2142 if (!domRangeAndAdjustedOffset
.RangeStartsFromEndOfContainer()) {
2143 return domRangeAndAdjustedOffset
.mLastTextNode
->TextDataLength() -
2144 domRangeAndAdjustedOffset
.mRange
.StartOffset();
2148 // Must be there are not nodes which may cause generating text.
2149 // Therefore, we can skip all nodes before the last found text node
2150 // and all text in the last text node.
2151 return domRangeAndAdjustedOffset
.mLastTextNode
->TextDataLength();
2153 if (domRangeAndAdjustedOffset
.RangeStartsFromContent() &&
2154 ShouldBreakLineBefore(
2155 *domRangeAndAdjustedOffset
.mRange
.GetStartContainer()
2158 if (kBRLength
!= 1u && offset
- aEvent
->mInput
.mOffset
< kBRLength
) {
2159 // Don't return kBRLength if start position is less than the length
2160 // of a line-break because the offset may be between CRLF on
2161 // Windows. In the case, we will be again here and gets same
2162 // result and we need to pay the penalty only once. Therefore, we
2163 // can keep going without complicated check.
2170 offset
+= std::max(1u, remainingLengthInCurrentRange
);
2174 nsIContent
* firstContent
= firstFrame
.mFrame
->GetContent();
2175 if (NS_WARN_IF(!firstContent
)) {
2176 return NS_ERROR_FAILURE
;
2179 bool startsBetweenLineBreaker
= false;
2181 lastVisibleFrameWritingMode
= firstFrame
->GetWritingMode();
2183 nsIFrame
* baseFrame
= firstFrame
;
2184 // charRect should have each character rect or line breaker rect relative
2185 // to the base frame.
2186 AutoTArray
<nsRect
, 16> charRects
;
2188 // If the first frame is a text frame, the result should be computed with
2190 if (firstFrame
->IsTextFrame()) {
2191 rv
= firstFrame
->GetCharacterRectsInRange(firstFrame
.mOffsetInNode
,
2192 kEndOffset
- offset
, charRects
);
2193 if (NS_WARN_IF(NS_FAILED(rv
)) || NS_WARN_IF(charRects
.IsEmpty())) {
2196 // Assign the characters whose rects are computed by the call of
2197 // nsTextFrame::GetCharacterRectsInRange().
2198 AppendSubString(chars
, *firstContent
->AsText(), firstFrame
.mOffsetInNode
,
2199 charRects
.Length());
2200 if (NS_WARN_IF(chars
.Length() != charRects
.Length())) {
2201 return NS_ERROR_UNEXPECTED
;
2203 if (kBRLength
> 1 && chars
[0] == '\n' &&
2204 offset
== aEvent
->mInput
.mOffset
&& offset
) {
2205 // If start of range starting from previous offset of query range is
2206 // same as the start of query range, the query range starts from
2207 // between a line breaker (i.e., the range starts between "\r" and
2209 Result
<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
2210 domRangeAndAdjustedOffsetOrError
=
2211 ConvertFlatTextOffsetToUnsafeDOMRange(
2212 aEvent
->mInput
.mOffset
- 1, 1, lineBreakType
, true);
2213 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError
.isErr())) {
2215 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() "
2217 return domRangeAndAdjustedOffsetOrError
.unwrapErr();
2219 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
2220 domRangeAndAdjustedOffsetOfPreviousChar
=
2221 domRangeAndAdjustedOffsetOrError
.unwrap();
2222 startsBetweenLineBreaker
=
2223 domRangeAndAdjustedOffset
.mRange
.GetStartContainer() ==
2224 domRangeAndAdjustedOffsetOfPreviousChar
.mRange
2225 .GetStartContainer() &&
2226 domRangeAndAdjustedOffset
.mRange
.StartOffset() ==
2227 domRangeAndAdjustedOffsetOfPreviousChar
.mRange
.StartOffset();
2230 // Other contents should cause a line breaker rect before it.
2231 // Note that moz-<br> element does not cause any text, however,
2232 // it represents empty line at the last of current block. Therefore,
2233 // we need to compute its rect too.
2234 else if (ShouldBreakLineBefore(*firstContent
, mRootElement
) ||
2235 IsPaddingBR(*firstContent
)) {
2237 // If the frame is not a <br> frame, we need to compute the caret rect
2238 // with last character's rect before firstContent if there is.
2239 // For example, if caret is after "c" of |<p>abc</p><p>def</p>|, IME may
2240 // query a line breaker's rect after "c". Then, if we compute it only
2241 // with the 2nd <p>'s block frame, the result will be:
2242 // +-<p>--------------------------------+
2244 // +------------------------------------+
2246 // I+-<p>--------------------------------+
2248 // +------------------------------------+
2249 // However, users expect popup windows of IME should be positioned at
2250 // right-bottom of "c" like this:
2251 // +-<p>--------------------------------+
2253 // +------------------------------------+
2255 // +-<p>--------------------------------+
2257 // +------------------------------------+
2258 // Therefore, if the first frame isn't a <br> frame and there is a text
2259 // node before the first node in the queried range, we should compute the
2260 // first rect with the previous character's rect.
2261 // If we already compute a character's rect in the queried range, we can
2262 // compute it with the cached last character's rect. (However, don't
2263 // use this path if it's a <br> frame because trusting <br> frame's rect
2264 // is better than guessing the rect from the previous character.)
2265 if (!firstFrame
->IsBrFrame() && !aEvent
->mReply
->mRectArray
.IsEmpty()) {
2266 baseFrame
= lastFrame
;
2267 brRect
= lastCharRect
;
2268 if (!wasLineBreaker
) {
2269 brRect
= GetCaretRectAfter(*baseFrame
->PresContext(), brRect
,
2270 lastVisibleFrameWritingMode
);
2273 // If it's not a <br> frame and it's the first character rect at the
2274 // queried range, we need the previous character rect of the start of
2275 // the queried range if there is a visible text node.
2276 else if (!firstFrame
->IsBrFrame() &&
2277 domRangeAndAdjustedOffset
.mLastTextNode
&&
2278 domRangeAndAdjustedOffset
.mLastTextNode
->GetPrimaryFrame()) {
2279 FrameRelativeRect brRectRelativeToLastTextFrame
=
2280 GuessLineBreakerRectAfter(*domRangeAndAdjustedOffset
.mLastTextNode
);
2281 if (NS_WARN_IF(!brRectRelativeToLastTextFrame
.IsValid())) {
2282 return NS_ERROR_FAILURE
;
2284 // Look for the last text frame for the last text node.
2285 nsIFrame
* primaryFrame
=
2286 domRangeAndAdjustedOffset
.mLastTextNode
->GetPrimaryFrame();
2287 if (NS_WARN_IF(!primaryFrame
)) {
2288 return NS_ERROR_FAILURE
;
2290 baseFrame
= primaryFrame
->LastContinuation();
2291 if (NS_WARN_IF(!baseFrame
)) {
2292 return NS_ERROR_FAILURE
;
2294 brRect
= brRectRelativeToLastTextFrame
.RectRelativeTo(baseFrame
);
2296 // Otherwise, we need to compute the line breaker's rect only with the
2297 // first frame's rect. But this may be unexpected. For example,
2298 // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is
2299 // before "a", therefore, users expect the rect left of "a". However,
2300 // we don't have enough information about the next character here and
2301 // this isn't usual case (e.g., IME typically tries to query the rect
2302 // of "a" or caret rect for computing its popup position). Therefore,
2303 // we shouldn't do more complicated hack here unless we'll get some bug
2304 // reports actually.
2306 FrameRelativeRect relativeBRRect
= GetLineBreakerRectBefore(firstFrame
);
2307 brRect
= relativeBRRect
.RectRelativeTo(firstFrame
);
2309 charRects
.AppendElement(brRect
);
2310 chars
.AssignLiteral("\n");
2311 if (kBRLength
> 1 && offset
== aEvent
->mInput
.mOffset
&& offset
) {
2312 // If the first frame for the previous offset of the query range and
2313 // the first frame for the start of query range are same, that means
2314 // the start offset is between the first line breaker (i.e., the range
2315 // starts between "\r" and "\n").
2316 Result
<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
2317 domRangeAndAdjustedOffsetOrError
=
2318 ConvertFlatTextOffsetToUnsafeDOMRange(
2319 aEvent
->mInput
.mOffset
- 1, 1, lineBreakType
, true);
2320 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError
.isErr())) {
2322 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() "
2324 return NS_ERROR_UNEXPECTED
;
2326 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
2327 domRangeAndAdjustedOffset
=
2328 domRangeAndAdjustedOffsetOrError
.unwrap();
2329 FrameAndNodeOffset frameForPrevious
=
2330 GetFirstFrameInRangeForTextRect(domRangeAndAdjustedOffset
.mRange
);
2331 startsBetweenLineBreaker
= frameForPrevious
.mFrame
== firstFrame
.mFrame
;
2335 "The frame is neither a text frame nor a frame whose content "
2336 "causes a line break");
2337 return NS_ERROR_FAILURE
;
2340 for (size_t i
= 0; i
< charRects
.Length() && offset
< kEndOffset
; i
++) {
2341 nsRect charRect
= charRects
[i
];
2342 // Store lastCharRect before applying CSS transform because it may be
2343 // used for computing a line breaker rect. Then, the computed line
2344 // breaker rect will be applied CSS transform again. Therefore,
2345 // the value of lastCharRect should be raw rect value relative to the
2347 lastCharRect
= charRect
;
2348 lastFrame
= baseFrame
;
2349 rv
= ConvertToRootRelativeOffset(baseFrame
, charRect
);
2350 if (NS_WARN_IF(NS_FAILED(rv
))) {
2354 nsPresContext
* presContext
= baseFrame
->PresContext();
2355 rect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2356 charRect
, presContext
->AppUnitsPerDevPixel());
2357 if (nsPresContext
* rootContext
=
2358 presContext
->GetInProcessRootContentDocumentPresContext()) {
2359 rect
= RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2360 rect
, rootContext
->PresShell()));
2362 // Returning empty rect may cause native IME confused, let's make sure to
2363 // return non-empty rect.
2364 EnsureNonEmptyRect(rect
);
2366 // If we found some invisible characters followed by current visible
2367 // character, make their rects same as caret rect before the first visible
2368 // character because IME may want to put their UI next to the rect of the
2369 // invisible character for next input.
2370 // Note that chars do not contain the invisible characters.
2371 if (i
== 0u && MOZ_LIKELY(offset
> aEvent
->mInput
.mOffset
)) {
2372 const uint32_t offsetInRange
=
2373 offset
- CheckedInt
<uint32_t>(aEvent
->mInput
.mOffset
).value();
2374 if (offsetInRange
> aEvent
->mReply
->mRectArray
.Length()) {
2375 LayoutDeviceIntRect caretRectBefore
=
2376 GetCaretRectBefore(rect
, lastVisibleFrameWritingMode
);
2377 for ([[maybe_unused
]] uint32_t index
: IntegerRange
<uint32_t>(
2378 offsetInRange
- aEvent
->mReply
->mRectArray
.Length())) {
2379 aEvent
->mReply
->mRectArray
.AppendElement(caretRectBefore
);
2381 MOZ_ASSERT(aEvent
->mReply
->mRectArray
.Length() == offsetInRange
);
2385 aEvent
->mReply
->mRectArray
.AppendElement(rect
);
2388 // If it's not a line breaker or the line breaker length is same as
2389 // XP line breaker's, we need to do nothing for current character.
2390 wasLineBreaker
= chars
[i
] == '\n';
2391 if (!wasLineBreaker
|| kBRLength
== 1) {
2395 MOZ_ASSERT(kBRLength
== 2);
2397 // If it's already reached the end of query range, we don't need to do
2399 if (offset
== kEndOffset
) {
2403 // If the query range starts from between a line breaker, i.e., it starts
2404 // between "\r" and "\n", the appended rect was for the "\n". Therefore,
2405 // we don't need to append same rect anymore for current "\r\n".
2406 if (startsBetweenLineBreaker
) {
2410 // The appended rect was for "\r" of "\r\n". Therefore, we need to
2411 // append same rect for "\n" too because querying rect of "\r" and "\n"
2412 // should return same rect. E.g., IME may query previous character's
2413 // rect of first character of a line.
2414 aEvent
->mReply
->mRectArray
.AppendElement(rect
);
2419 // If we've not handled some invisible character rects, fill them as caret
2420 // rect after the last visible character.
2421 if (!aEvent
->mReply
->mRectArray
.IsEmpty()) {
2422 const uint32_t offsetInRange
=
2423 offset
- CheckedInt
<uint32_t>(aEvent
->mInput
.mOffset
).value();
2424 if (offsetInRange
> aEvent
->mReply
->mRectArray
.Length()) {
2425 LayoutDeviceIntRect caretRectAfter
=
2426 GetCaretRectAfter(aEvent
->mReply
->mRectArray
.LastElement(),
2427 lastVisibleFrameWritingMode
);
2428 for ([[maybe_unused
]] uint32_t index
: IntegerRange
<uint32_t>(
2429 offsetInRange
- aEvent
->mReply
->mRectArray
.Length())) {
2430 aEvent
->mReply
->mRectArray
.AppendElement(caretRectAfter
);
2432 MOZ_ASSERT(aEvent
->mReply
->mRectArray
.Length() == offsetInRange
);
2436 // If the query range is longer than actual content length, we should append
2437 // caret rect at the end of the content as the last character rect because
2438 // native IME may want to query character rect at the end of contents for
2439 // deciding the position of a popup window (e.g., suggest window for next
2440 // word). Note that when this method hasn't appended character rects, it
2441 // means that the offset is too large or the query range is collapsed.
2442 if (offset
< kEndOffset
|| aEvent
->mReply
->mRectArray
.IsEmpty()) {
2443 // If we've already retrieved some character rects before current offset,
2444 // we can guess the last rect from the last character's rect unless it's a
2445 // line breaker. (If it's a line breaker, the caret rect is in next line.)
2446 if (!aEvent
->mReply
->mRectArray
.IsEmpty() && !wasLineBreaker
) {
2447 rect
= GetCaretRectAfter(aEvent
->mReply
->mRectArray
.LastElement(),
2448 lastVisibleFrameWritingMode
);
2449 aEvent
->mReply
->mRectArray
.AppendElement(rect
);
2451 // Note that don't use eQueryCaretRect here because if caret is at the
2452 // end of the content, it returns actual caret rect instead of computing
2453 // the rect itself. It means that the result depends on caret position.
2454 // So, we shouldn't use it for consistency result in automated tests.
2455 WidgetQueryContentEvent
queryTextRectEvent(eQueryTextRect
, *aEvent
);
2456 WidgetQueryContentEvent::Options
options(*aEvent
);
2457 queryTextRectEvent
.InitForQueryTextRect(offset
, 1, options
);
2458 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent
))) ||
2459 NS_WARN_IF(queryTextRectEvent
.Failed())) {
2460 return NS_ERROR_FAILURE
;
2462 if (queryTextRectEvent
.mReply
->mWritingMode
.IsVertical()) {
2463 queryTextRectEvent
.mReply
->mRect
.height
= 1;
2465 queryTextRectEvent
.mReply
->mRect
.width
= 1;
2467 aEvent
->mReply
->mRectArray
.AppendElement(
2468 queryTextRectEvent
.mReply
->mRect
);
2472 MOZ_ASSERT(aEvent
->Succeeded());
2476 nsresult
ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent
* aEvent
) {
2477 // If mLength is 0 (this may be caused by bug of native IME), we should
2478 // redirect this event to OnQueryCaretRect().
2479 if (!aEvent
->mInput
.mLength
) {
2480 return OnQueryCaretRect(aEvent
);
2483 nsresult rv
= Init(aEvent
);
2484 if (NS_FAILED(rv
)) {
2488 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
2490 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
2491 Result
<DOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
2492 domRangeAndAdjustedOffsetOrError
= ConvertFlatTextOffsetToDOMRange(
2493 aEvent
->mInput
.mOffset
, aEvent
->mInput
.mLength
, lineBreakType
, true);
2494 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError
.isErr())) {
2495 NS_WARNING("ContentEventHandler::ConvertFlatTextOffsetToDOMRange() failed");
2496 return NS_ERROR_FAILURE
;
2498 DOMRangeAndAdjustedOffsetInFlattenedText domRangeAndAdjustedOffset
=
2499 domRangeAndAdjustedOffsetOrError
.unwrap();
2501 if (NS_WARN_IF(NS_FAILED(GenerateFlatTextContent(
2502 domRangeAndAdjustedOffset
.mRange
, string
, lineBreakType
)))) {
2503 return NS_ERROR_FAILURE
;
2505 aEvent
->mReply
->mOffsetAndData
.emplace(
2506 domRangeAndAdjustedOffset
.mAdjustedOffset
, string
,
2507 OffsetAndDataFor::EditorString
);
2509 // used to iterate over all contents and their frames
2510 PostContentIterator postOrderIter
;
2511 rv
= postOrderIter
.Init(domRangeAndAdjustedOffset
.mRange
.Start().AsRaw(),
2512 domRangeAndAdjustedOffset
.mRange
.End().AsRaw());
2513 if (NS_WARN_IF(NS_FAILED(rv
))) {
2514 return NS_ERROR_FAILURE
;
2517 // Get the first frame which causes some text after the offset.
2518 FrameAndNodeOffset firstFrame
=
2519 GetFirstFrameInRangeForTextRect(domRangeAndAdjustedOffset
.mRange
);
2521 // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
2522 // means that there are no visible frames having text or the offset reached
2523 // the end of contents.
2524 if (!firstFrame
.IsValid()) {
2525 nsAutoString allText
;
2526 rv
= GenerateFlatTextContent(mRootElement
, allText
, lineBreakType
);
2527 // If the offset doesn't reach the end of contents but there is no frames
2528 // for the node, that means that current offset's node is hidden by CSS or
2529 // something. Ideally, we should handle it with the last visible text
2530 // node's last character's rect, but it's not usual cases in actual web
2531 // services. Therefore, currently, we should make this case fail.
2532 if (NS_WARN_IF(NS_FAILED(rv
)) ||
2533 static_cast<uint32_t>(aEvent
->mInput
.mOffset
) < allText
.Length()) {
2534 return NS_ERROR_FAILURE
;
2537 // Look for the last frame which should be included text rects.
2538 rv
= domRangeAndAdjustedOffset
.mRange
.SelectNodeContents(mRootElement
);
2539 if (NS_WARN_IF(NS_FAILED(rv
))) {
2540 return NS_ERROR_UNEXPECTED
;
2543 FrameAndNodeOffset lastFrame
=
2544 GetLastFrameInRangeForTextRect(domRangeAndAdjustedOffset
.mRange
);
2545 // If there is at least one frame which can be used for computing a rect
2546 // for a character or a line breaker, we should use it for guessing the
2547 // caret rect at the end of the contents.
2548 nsPresContext
* presContext
;
2550 presContext
= lastFrame
->PresContext();
2551 if (NS_WARN_IF(!lastFrame
->GetContent())) {
2552 return NS_ERROR_FAILURE
;
2554 FrameRelativeRect relativeRect
;
2555 // If there is a <br> frame at the end, it represents an empty line at
2556 // the end with moz-<br> or content <br> in a block level element.
2557 if (lastFrame
->IsBrFrame()) {
2558 relativeRect
= GetLineBreakerRectBefore(lastFrame
);
2560 // If there is a text frame at the end, use its information.
2561 else if (lastFrame
->IsTextFrame()) {
2562 const Text
* textNode
= Text::FromNode(lastFrame
->GetContent());
2563 MOZ_ASSERT(textNode
);
2565 relativeRect
= GuessLineBreakerRectAfter(*textNode
);
2568 // If there is an empty frame which is neither a text frame nor a <br>
2569 // frame at the end, guess caret rect in it.
2571 relativeRect
= GuessFirstCaretRectIn(lastFrame
);
2573 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2574 return NS_ERROR_FAILURE
;
2576 rect
= relativeRect
.RectRelativeTo(lastFrame
);
2577 rv
= ConvertToRootRelativeOffset(lastFrame
, rect
);
2578 if (NS_WARN_IF(NS_FAILED(rv
))) {
2581 aEvent
->mReply
->mWritingMode
= lastFrame
->GetWritingMode();
2583 // Otherwise, if there are no contents in mRootElement, guess caret rect in
2584 // its frame (with its font height and content box).
2586 nsIFrame
* rootContentFrame
= mRootElement
->GetPrimaryFrame();
2587 if (NS_WARN_IF(!rootContentFrame
)) {
2588 return NS_ERROR_FAILURE
;
2590 presContext
= rootContentFrame
->PresContext();
2591 FrameRelativeRect relativeRect
= GuessFirstCaretRectIn(rootContentFrame
);
2592 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2593 return NS_ERROR_FAILURE
;
2595 rect
= relativeRect
.RectRelativeTo(rootContentFrame
);
2596 rv
= ConvertToRootRelativeOffset(rootContentFrame
, rect
);
2597 if (NS_WARN_IF(NS_FAILED(rv
))) {
2600 aEvent
->mReply
->mWritingMode
= rootContentFrame
->GetWritingMode();
2602 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2603 rect
, presContext
->AppUnitsPerDevPixel());
2604 if (nsPresContext
* rootContext
=
2605 presContext
->GetInProcessRootContentDocumentPresContext()) {
2606 aEvent
->mReply
->mRect
=
2607 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2608 aEvent
->mReply
->mRect
, rootContext
->PresShell()));
2610 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
2612 MOZ_ASSERT(aEvent
->Succeeded());
2616 nsRect rect
, frameRect
;
2619 // If the first frame is a text frame, the result should be computed with
2620 // the frame's rect but not including the rect before start point of the
2622 if (firstFrame
->IsTextFrame()) {
2623 rect
.SetRect(nsPoint(0, 0), firstFrame
->GetRect().Size());
2624 rv
= ConvertToRootRelativeOffset(firstFrame
, rect
);
2625 if (NS_WARN_IF(NS_FAILED(rv
))) {
2629 // Exclude the rect before start point of the queried range.
2630 firstFrame
->GetPointFromOffset(firstFrame
.mOffsetInNode
, &ptOffset
);
2631 if (firstFrame
->GetWritingMode().IsVertical()) {
2632 rect
.y
+= ptOffset
.y
;
2633 rect
.height
-= ptOffset
.y
;
2635 rect
.x
+= ptOffset
.x
;
2636 rect
.width
-= ptOffset
.x
;
2639 // If first frame causes a line breaker but it's not a <br> frame, we cannot
2640 // compute proper rect only with the frame because typically caret is at
2641 // right of the last character of it. For example, if caret is after "c" of
2642 // |<p>abc</p><p>def</p>|, IME may query a line breaker's rect after "c".
2643 // Then, if we compute it only with the 2nd <p>'s block frame, the result
2645 // +-<p>--------------------------------+
2647 // +------------------------------------+
2649 // I+-<p>--------------------------------+
2651 // +------------------------------------+
2652 // However, users expect popup windows of IME should be positioned at
2653 // right-bottom of "c" like this:
2654 // +-<p>--------------------------------+
2656 // +------------------------------------+
2658 // +-<p>--------------------------------+
2660 // +------------------------------------+
2661 // Therefore, if the first frame isn't a <br> frame and there is a visible
2662 // text node before the first node in the queried range, we should compute the
2663 // first rect with the previous character's rect.
2664 else if (!firstFrame
->IsBrFrame() &&
2665 domRangeAndAdjustedOffset
.mLastTextNode
&&
2666 domRangeAndAdjustedOffset
.mLastTextNode
->GetPrimaryFrame()) {
2667 FrameRelativeRect brRectAfterLastChar
=
2668 GuessLineBreakerRectAfter(*domRangeAndAdjustedOffset
.mLastTextNode
);
2669 if (NS_WARN_IF(!brRectAfterLastChar
.IsValid())) {
2670 return NS_ERROR_FAILURE
;
2672 rect
= brRectAfterLastChar
.mRect
;
2673 rv
= ConvertToRootRelativeOffset(brRectAfterLastChar
.mBaseFrame
, rect
);
2674 if (NS_WARN_IF(NS_FAILED(rv
))) {
2679 // Otherwise, we need to compute the line breaker's rect only with the
2680 // first frame's rect. But this may be unexpected. For example,
2681 // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is before
2682 // "a", therefore, users expect the rect left of "a". However, we don't
2683 // have enough information about the next character here and this isn't
2684 // usual case (e.g., IME typically tries to query the rect of "a" or caret
2685 // rect for computing its popup position). Therefore, we shouldn't do
2686 // more complicated hack here unless we'll get some bug reports actually.
2688 FrameRelativeRect relativeRect
= GetLineBreakerRectBefore(firstFrame
);
2689 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2690 return NS_ERROR_FAILURE
;
2692 rect
= relativeRect
.RectRelativeTo(firstFrame
);
2693 rv
= ConvertToRootRelativeOffset(firstFrame
, rect
);
2694 if (NS_WARN_IF(NS_FAILED(rv
))) {
2699 // UnionRect() requires non-empty rect. So, let's make sure to get non-emtpy
2700 // rect from the first frame.
2701 EnsureNonEmptyRect(rect
);
2703 // Get the last frame which causes some text in the range.
2704 FrameAndNodeOffset lastFrame
=
2705 GetLastFrameInRangeForTextRect(domRangeAndAdjustedOffset
.mRange
);
2706 if (NS_WARN_IF(!lastFrame
.IsValid())) {
2707 return NS_ERROR_FAILURE
;
2710 // iterate over all covered frames
2711 for (nsIFrame
* frame
= firstFrame
; frame
!= lastFrame
;) {
2712 frame
= frame
->GetNextContinuation();
2715 postOrderIter
.Next();
2716 nsINode
* node
= postOrderIter
.GetCurrentNode();
2720 if (!node
->IsContent()) {
2723 nsIFrame
* primaryFrame
= node
->AsContent()->GetPrimaryFrame();
2724 // The node may be hidden by CSS.
2725 if (!primaryFrame
) {
2728 // We should take only text frame's rect and br frame's rect. We can
2729 // always use frame rect of text frame and GetLineBreakerRectBefore()
2730 // can return exactly correct rect only for <br> frame for now. On the
2731 // other hand, GetLineBreakRectBefore() returns guessed caret rect for
2732 // the other frames. We shouldn't include such odd rect to the result.
2733 if (primaryFrame
->IsTextFrame() || primaryFrame
->IsBrFrame()) {
2734 frame
= primaryFrame
;
2736 } while (!frame
&& !postOrderIter
.IsDone());
2741 if (frame
->IsTextFrame()) {
2742 frameRect
.SetRect(nsPoint(0, 0), frame
->GetRect().Size());
2744 MOZ_ASSERT(frame
->IsBrFrame());
2745 FrameRelativeRect relativeRect
= GetLineBreakerRectBefore(frame
);
2746 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2747 return NS_ERROR_FAILURE
;
2749 frameRect
= relativeRect
.RectRelativeTo(frame
);
2751 rv
= ConvertToRootRelativeOffset(frame
, frameRect
);
2752 if (NS_WARN_IF(NS_FAILED(rv
))) {
2755 // UnionRect() requires non-empty rect. So, let's make sure to get
2756 // non-emtpy rect from the frame.
2757 EnsureNonEmptyRect(frameRect
);
2758 if (frame
!= lastFrame
) {
2759 // not last frame, so just add rect to previous result
2760 rect
.UnionRect(rect
, frameRect
);
2764 // Get the ending frame rect.
2765 // FYI: If first frame and last frame are same, frameRect is already set
2766 // to the rect excluding the text before the query range.
2767 if (firstFrame
.mFrame
!= lastFrame
.mFrame
) {
2768 frameRect
.SetRect(nsPoint(0, 0), lastFrame
->GetRect().Size());
2769 rv
= ConvertToRootRelativeOffset(lastFrame
, frameRect
);
2770 if (NS_WARN_IF(NS_FAILED(rv
))) {
2775 // Shrink the last frame for cutting off the text after the query range.
2776 if (lastFrame
->IsTextFrame()) {
2777 lastFrame
->GetPointFromOffset(lastFrame
.mOffsetInNode
, &ptOffset
);
2778 if (lastFrame
->GetWritingMode().IsVertical()) {
2779 frameRect
.height
-= lastFrame
->GetRect().height
- ptOffset
.y
;
2781 frameRect
.width
-= lastFrame
->GetRect().width
- ptOffset
.x
;
2783 // UnionRect() requires non-empty rect. So, let's make sure to get
2784 // non-empty rect from the last frame.
2785 EnsureNonEmptyRect(frameRect
);
2787 if (firstFrame
.mFrame
== lastFrame
.mFrame
) {
2788 rect
.IntersectRect(rect
, frameRect
);
2790 rect
.UnionRect(rect
, frameRect
);
2794 nsPresContext
* presContext
= lastFrame
->PresContext();
2795 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2796 rect
, presContext
->AppUnitsPerDevPixel());
2797 if (nsPresContext
* rootContext
=
2798 presContext
->GetInProcessRootContentDocumentPresContext()) {
2799 aEvent
->mReply
->mRect
=
2800 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2801 aEvent
->mReply
->mRect
, rootContext
->PresShell()));
2803 // Returning empty rect may cause native IME confused, let's make sure to
2804 // return non-empty rect.
2805 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
2806 aEvent
->mReply
->mWritingMode
= lastFrame
->GetWritingMode();
2808 MOZ_ASSERT(aEvent
->Succeeded());
2812 nsresult
ContentEventHandler::OnQueryEditorRect(
2813 WidgetQueryContentEvent
* aEvent
) {
2814 nsresult rv
= Init(aEvent
);
2815 if (NS_FAILED(rv
)) {
2819 if (NS_WARN_IF(NS_FAILED(QueryContentRect(mRootElement
, aEvent
)))) {
2820 return NS_ERROR_FAILURE
;
2823 MOZ_ASSERT(aEvent
->Succeeded());
2827 nsresult
ContentEventHandler::OnQueryCaretRect(
2828 WidgetQueryContentEvent
* aEvent
) {
2829 nsresult rv
= Init(aEvent
);
2830 if (NS_FAILED(rv
)) {
2834 // When the selection is collapsed and the queried offset is current caret
2835 // position, we should return the "real" caret rect.
2836 if (mSelection
->IsCollapsed()) {
2838 nsIFrame
* caretFrame
= nsCaret::GetGeometry(mSelection
, &caretRect
);
2841 rv
= GetStartOffset(mFirstSelectedSimpleRange
, &offset
,
2842 GetLineBreakType(aEvent
));
2843 NS_ENSURE_SUCCESS(rv
, rv
);
2844 if (offset
== aEvent
->mInput
.mOffset
) {
2845 rv
= ConvertToRootRelativeOffset(caretFrame
, caretRect
);
2846 NS_ENSURE_SUCCESS(rv
, rv
);
2847 nsPresContext
* presContext
= caretFrame
->PresContext();
2848 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2849 caretRect
, presContext
->AppUnitsPerDevPixel());
2850 if (nsPresContext
* rootContext
=
2851 presContext
->GetInProcessRootContentDocumentPresContext()) {
2852 aEvent
->mReply
->mRect
=
2853 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2854 aEvent
->mReply
->mRect
, rootContext
->PresShell()));
2856 // Returning empty rect may cause native IME confused, let's make sure
2857 // to return non-empty rect.
2858 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
2859 aEvent
->mReply
->mWritingMode
= caretFrame
->GetWritingMode();
2860 aEvent
->mReply
->mOffsetAndData
.emplace(
2861 aEvent
->mInput
.mOffset
, EmptyString(),
2862 OffsetAndDataFor::SelectedString
);
2864 MOZ_ASSERT(aEvent
->Succeeded());
2870 // Otherwise, we should guess the caret rect from the character's rect.
2871 WidgetQueryContentEvent
queryTextRectEvent(eQueryTextRect
, *aEvent
);
2872 WidgetQueryContentEvent::Options
options(*aEvent
);
2873 queryTextRectEvent
.InitForQueryTextRect(aEvent
->mInput
.mOffset
, 1, options
);
2874 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent
))) ||
2875 NS_WARN_IF(queryTextRectEvent
.Failed())) {
2876 return NS_ERROR_FAILURE
;
2878 queryTextRectEvent
.mReply
->TruncateData();
2879 aEvent
->mReply
->mOffsetAndData
=
2880 std::move(queryTextRectEvent
.mReply
->mOffsetAndData
);
2881 aEvent
->mReply
->mWritingMode
=
2882 std::move(queryTextRectEvent
.mReply
->mWritingMode
);
2883 aEvent
->mReply
->mRect
= GetCaretRectBefore(queryTextRectEvent
.mReply
->mRect
,
2884 aEvent
->mReply
->mWritingMode
);
2886 MOZ_ASSERT(aEvent
->Succeeded());
2890 nsresult
ContentEventHandler::OnQueryContentState(
2891 WidgetQueryContentEvent
* aEvent
) {
2892 if (NS_FAILED(Init(aEvent
))) {
2893 return NS_ERROR_FAILURE
;
2895 MOZ_ASSERT(aEvent
->mReply
.isSome());
2896 MOZ_ASSERT(aEvent
->Succeeded());
2900 nsresult
ContentEventHandler::OnQuerySelectionAsTransferable(
2901 WidgetQueryContentEvent
* aEvent
) {
2902 nsresult rv
= Init(aEvent
);
2903 if (NS_FAILED(rv
)) {
2907 MOZ_ASSERT(aEvent
->mReply
.isSome());
2909 if (mSelection
->IsCollapsed()) {
2910 MOZ_ASSERT(!aEvent
->mReply
->mTransferable
);
2914 if (NS_WARN_IF(NS_FAILED(nsCopySupport::GetTransferableForSelection(
2915 mSelection
, mDocument
,
2916 getter_AddRefs(aEvent
->mReply
->mTransferable
))))) {
2917 return NS_ERROR_FAILURE
;
2920 MOZ_ASSERT(aEvent
->Succeeded());
2924 nsresult
ContentEventHandler::OnQueryCharacterAtPoint(
2925 WidgetQueryContentEvent
* aEvent
) {
2926 nsresult rv
= Init(aEvent
);
2927 if (NS_FAILED(rv
)) {
2931 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
2932 MOZ_ASSERT(aEvent
->mReply
->mTentativeCaretOffset
.isNothing());
2934 PresShell
* presShell
= mDocument
->GetPresShell();
2935 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
2936 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
2937 NS_ENSURE_TRUE(rootFrame
, NS_ERROR_FAILURE
);
2938 nsIWidget
* rootWidget
= rootFrame
->GetNearestWidget();
2939 NS_ENSURE_TRUE(rootWidget
, NS_ERROR_FAILURE
);
2941 // The root frame's widget might be different, e.g., the event was fired on
2942 // a popup but the rootFrame is the document root.
2943 if (rootWidget
!= aEvent
->mWidget
) {
2944 MOZ_ASSERT(aEvent
->mWidget
, "The event must have the widget");
2945 nsView
* view
= nsView::GetViewFor(aEvent
->mWidget
);
2946 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
2947 rootFrame
= view
->GetFrame();
2948 NS_ENSURE_TRUE(rootFrame
, NS_ERROR_FAILURE
);
2949 rootWidget
= rootFrame
->GetNearestWidget();
2950 NS_ENSURE_TRUE(rootWidget
, NS_ERROR_FAILURE
);
2953 WidgetQueryContentEvent
queryCharAtPointOnRootWidgetEvent(
2954 true, eQueryCharacterAtPoint
, rootWidget
);
2955 queryCharAtPointOnRootWidgetEvent
.mUseNativeLineBreak
=
2956 aEvent
->mUseNativeLineBreak
;
2957 queryCharAtPointOnRootWidgetEvent
.mRefPoint
= aEvent
->mRefPoint
;
2958 if (rootWidget
!= aEvent
->mWidget
) {
2959 queryCharAtPointOnRootWidgetEvent
.mRefPoint
+=
2960 aEvent
->mWidget
->WidgetToScreenOffset() -
2961 rootWidget
->WidgetToScreenOffset();
2963 nsPoint ptInRoot
= nsLayoutUtils::GetEventCoordinatesRelativeTo(
2964 &queryCharAtPointOnRootWidgetEvent
, RelativeTo
{rootFrame
});
2966 nsIFrame
* targetFrame
=
2967 nsLayoutUtils::GetFrameForPoint(RelativeTo
{rootFrame
}, ptInRoot
);
2968 if (!targetFrame
|| !targetFrame
->GetContent() ||
2969 !targetFrame
->GetContent()->IsInclusiveDescendantOf(mRootElement
)) {
2970 // There is no character at the point.
2971 MOZ_ASSERT(aEvent
->Succeeded());
2974 nsPoint ptInTarget
= ptInRoot
+ rootFrame
->GetOffsetToCrossDoc(targetFrame
);
2975 int32_t rootAPD
= rootFrame
->PresContext()->AppUnitsPerDevPixel();
2976 int32_t targetAPD
= targetFrame
->PresContext()->AppUnitsPerDevPixel();
2977 ptInTarget
= ptInTarget
.ScaleToOtherAppUnits(rootAPD
, targetAPD
);
2979 nsIFrame::ContentOffsets tentativeCaretOffsets
=
2980 targetFrame
->GetContentOffsetsFromPoint(ptInTarget
);
2981 if (!tentativeCaretOffsets
.content
||
2982 !tentativeCaretOffsets
.content
->IsInclusiveDescendantOf(mRootElement
)) {
2983 // There is no character nor tentative caret point at the point.
2984 MOZ_ASSERT(aEvent
->Succeeded());
2988 uint32_t tentativeCaretOffset
= 0;
2989 if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange(
2990 RawNodePosition(mRootElement
, 0u),
2991 RawNodePosition(tentativeCaretOffsets
), mRootElement
,
2992 &tentativeCaretOffset
, GetLineBreakType(aEvent
))))) {
2993 return NS_ERROR_FAILURE
;
2996 aEvent
->mReply
->mTentativeCaretOffset
.emplace(tentativeCaretOffset
);
2997 if (!targetFrame
->IsTextFrame()) {
2998 // There is no character at the point but there is tentative caret point.
2999 MOZ_ASSERT(aEvent
->Succeeded());
3003 nsTextFrame
* textframe
= static_cast<nsTextFrame
*>(targetFrame
);
3004 nsIFrame::ContentOffsets contentOffsets
=
3005 textframe
->GetCharacterOffsetAtFramePoint(ptInTarget
);
3006 NS_ENSURE_TRUE(contentOffsets
.content
, NS_ERROR_FAILURE
);
3007 uint32_t offset
= 0;
3008 if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange(
3009 RawNodePosition(mRootElement
, 0u), RawNodePosition(contentOffsets
),
3010 mRootElement
, &offset
, GetLineBreakType(aEvent
))))) {
3011 return NS_ERROR_FAILURE
;
3014 WidgetQueryContentEvent
queryTextRectEvent(true, eQueryTextRect
,
3016 WidgetQueryContentEvent::Options
options(*aEvent
);
3017 queryTextRectEvent
.InitForQueryTextRect(offset
, 1, options
);
3018 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent
))) ||
3019 NS_WARN_IF(queryTextRectEvent
.Failed())) {
3020 return NS_ERROR_FAILURE
;
3023 aEvent
->mReply
->mOffsetAndData
=
3024 std::move(queryTextRectEvent
.mReply
->mOffsetAndData
);
3025 aEvent
->mReply
->mRect
= queryTextRectEvent
.mReply
->mRect
;
3027 MOZ_ASSERT(aEvent
->Succeeded());
3031 nsresult
ContentEventHandler::OnQueryDOMWidgetHittest(
3032 WidgetQueryContentEvent
* aEvent
) {
3033 NS_ASSERTION(aEvent
, "aEvent must not be null");
3035 nsresult rv
= InitBasic();
3036 if (NS_FAILED(rv
)) {
3040 aEvent
->mReply
->mWidgetIsHit
= false;
3042 NS_ENSURE_TRUE(aEvent
->mWidget
, NS_ERROR_FAILURE
);
3044 PresShell
* presShell
= mDocument
->GetPresShell();
3045 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
3046 nsIFrame
* docFrame
= presShell
->GetRootFrame();
3047 NS_ENSURE_TRUE(docFrame
, NS_ERROR_FAILURE
);
3049 LayoutDeviceIntPoint eventLoc
=
3050 aEvent
->mRefPoint
+ aEvent
->mWidget
->WidgetToScreenOffset();
3051 CSSIntRect docFrameRect
= docFrame
->GetScreenRect();
3052 CSSIntPoint
eventLocCSS(
3053 docFrame
->PresContext()->DevPixelsToIntCSSPixels(eventLoc
.x
) -
3055 docFrame
->PresContext()->DevPixelsToIntCSSPixels(eventLoc
.y
) -
3058 if (Element
* contentUnderMouse
= mDocument
->ElementFromPointHelper(
3059 eventLocCSS
.x
, eventLocCSS
.y
, false, false, ViewportType::Visual
)) {
3060 if (nsIFrame
* targetFrame
= contentUnderMouse
->GetPrimaryFrame()) {
3061 if (aEvent
->mWidget
== targetFrame
->GetNearestWidget()) {
3062 aEvent
->mReply
->mWidgetIsHit
= true;
3067 MOZ_ASSERT(aEvent
->Succeeded());
3072 nsresult
ContentEventHandler::GetFlatTextLengthInRange(
3073 const RawNodePosition
& aStartPosition
, const RawNodePosition
& aEndPosition
,
3074 const Element
* aRootElement
, uint32_t* aLength
,
3075 LineBreakType aLineBreakType
, bool aIsRemovingNode
/* = false */) {
3076 if (NS_WARN_IF(!aRootElement
) || NS_WARN_IF(!aStartPosition
.IsSet()) ||
3077 NS_WARN_IF(!aEndPosition
.IsSet()) || NS_WARN_IF(!aLength
)) {
3078 return NS_ERROR_INVALID_ARG
;
3081 if (aStartPosition
== aEndPosition
) {
3086 UnsafePreContentIterator preOrderIter
;
3088 // Working with ContentIterator, we may need to adjust the end position for
3089 // including it forcibly.
3090 RawNodePosition
endPosition(aEndPosition
);
3092 // This may be called for retrieving the text of removed nodes. Even in this
3093 // case, the node thinks it's still in the tree because UnbindFromTree() will
3094 // be called after here. However, the node was already removed from the
3095 // array of children of its parent. So, be careful to handle this case.
3096 if (aIsRemovingNode
) {
3097 DebugOnly
<nsIContent
*> parent
= aStartPosition
.Container()->GetParent();
3100 parent
->ComputeIndexOf(aStartPosition
.Container()).isNothing(),
3101 "At removing the node, the node shouldn't be in the array of children "
3103 MOZ_ASSERT(aStartPosition
.Container() == endPosition
.Container(),
3104 "At removing the node, start and end node should be same");
3105 MOZ_ASSERT(*aStartPosition
.Offset(
3106 RawNodePosition::OffsetFilter::kValidOrInvalidOffsets
) == 0,
3107 "When the node is being removed, the start offset should be 0");
3109 static_cast<uint32_t>(*endPosition
.Offset(
3110 RawNodePosition::OffsetFilter::kValidOrInvalidOffsets
)) ==
3111 endPosition
.Container()->GetChildCount(),
3112 "When the node is being removed, the end offset should be child count");
3113 nsresult rv
= preOrderIter
.Init(aStartPosition
.Container());
3114 if (NS_WARN_IF(NS_FAILED(rv
))) {
3118 SimpleRange prevSimpleRange
;
3119 nsresult rv
= prevSimpleRange
.SetStart(aStartPosition
.AsRaw());
3120 if (NS_WARN_IF(NS_FAILED(rv
))) {
3124 // When the end position is immediately after non-root element's open tag,
3125 // we need to include a line break caused by the open tag.
3126 if (endPosition
.Container() != aRootElement
&&
3127 endPosition
.IsImmediatelyAfterOpenTag()) {
3128 if (endPosition
.Container()->HasChildren()) {
3129 // When the end node has some children, move the end position to before
3130 // the open tag of its first child.
3131 nsINode
* firstChild
= endPosition
.Container()->GetFirstChild();
3132 if (NS_WARN_IF(!firstChild
)) {
3133 return NS_ERROR_FAILURE
;
3135 endPosition
= RawNodePositionBefore(firstChild
, 0u);
3137 // When the end node is empty, move the end position after the node.
3138 nsIContent
* parentContent
= endPosition
.Container()->GetParent();
3139 if (NS_WARN_IF(!parentContent
)) {
3140 return NS_ERROR_FAILURE
;
3142 Maybe
<uint32_t> indexInParent
=
3143 parentContent
->ComputeIndexOf(endPosition
.Container());
3144 if (MOZ_UNLIKELY(NS_WARN_IF(indexInParent
.isNothing()))) {
3145 return NS_ERROR_FAILURE
;
3147 MOZ_ASSERT(*indexInParent
!= UINT32_MAX
);
3148 endPosition
= RawNodePositionBefore(parentContent
, *indexInParent
+ 1u);
3152 if (endPosition
.IsSetAndValid()) {
3153 // Offset is within node's length; set end of range to that offset
3154 rv
= prevSimpleRange
.SetEnd(endPosition
.AsRaw());
3155 if (NS_WARN_IF(NS_FAILED(rv
))) {
3158 rv
= preOrderIter
.Init(prevSimpleRange
.Start().AsRaw(),
3159 prevSimpleRange
.End().AsRaw());
3160 if (NS_WARN_IF(NS_FAILED(rv
))) {
3163 } else if (endPosition
.Container() != aRootElement
) {
3164 // Offset is past node's length; set end of range to end of node
3165 rv
= prevSimpleRange
.SetEndAfter(endPosition
.Container());
3166 if (NS_WARN_IF(NS_FAILED(rv
))) {
3169 rv
= preOrderIter
.Init(prevSimpleRange
.Start().AsRaw(),
3170 prevSimpleRange
.End().AsRaw());
3171 if (NS_WARN_IF(NS_FAILED(rv
))) {
3175 // Offset is past the root node; set end of range to end of root node
3176 rv
= preOrderIter
.Init(const_cast<Element
*>(aRootElement
));
3177 if (NS_WARN_IF(NS_FAILED(rv
))) {
3184 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
3185 nsINode
* node
= preOrderIter
.GetCurrentNode();
3186 if (NS_WARN_IF(!node
)) {
3189 if (!node
->IsContent()) {
3192 nsIContent
* content
= node
->AsContent();
3194 if (const Text
* textNode
= Text::FromNode(content
)) {
3195 // Note: our range always starts from offset 0
3196 if (node
== endPosition
.Container()) {
3197 // NOTE: We should have an offset here, as endPosition.Container() is a
3198 // nsINode::eTEXT, which always has an offset.
3199 *aLength
+= GetTextLength(
3200 *textNode
, aLineBreakType
,
3201 *endPosition
.Offset(
3202 RawNodePosition::OffsetFilter::kValidOrInvalidOffsets
));
3204 *aLength
+= GetTextLength(*textNode
, aLineBreakType
);
3206 } else if (ShouldBreakLineBefore(*content
, aRootElement
)) {
3207 // If the start position is start of this node but doesn't include the
3208 // open tag, don't append the line break length.
3209 if (node
== aStartPosition
.Container() &&
3210 !aStartPosition
.IsBeforeOpenTag()) {
3213 // If the end position is before the open tag, don't append the line
3215 if (node
== endPosition
.Container() && endPosition
.IsBeforeOpenTag()) {
3218 *aLength
+= GetBRLength(aLineBreakType
);
3224 template <typename SimpleRangeType
>
3225 nsresult
ContentEventHandler::GetStartOffset(
3226 const SimpleRangeType
& aSimpleRange
, uint32_t* aOffset
,
3227 LineBreakType aLineBreakType
) {
3228 // To match the "no skip start" hack in ContentIterator::Init, when range
3229 // offset is 0 and the range node is not a container, we have to assume the
3230 // range _includes_ the node, which means the start offset should _not_
3231 // include the node.
3233 // For example, for this content: <br>abc, and range (<br>, 0)-("abc", 1), the
3234 // range includes the linebreak from <br>, so the start offset should _not_
3235 // include <br>, and the start offset should be 0.
3237 // However, for this content: <p/>abc, and range (<p>, 0)-("abc", 1), the
3238 // range does _not_ include the linebreak from <p> because <p> is a container,
3239 // so the start offset _should_ include <p>, and the start offset should be 1.
3241 nsINode
* startNode
= aSimpleRange
.GetStartContainer();
3242 bool startIsContainer
= true;
3243 if (startNode
->IsHTMLElement()) {
3244 nsAtom
* name
= startNode
->NodeInfo()->NameAtom();
3246 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
3248 RawNodePosition
startPos(startNode
, aSimpleRange
.StartOffset());
3249 startPos
.mAfterOpenTag
= startIsContainer
;
3250 return GetFlatTextLengthInRange(RawNodePosition(mRootElement
, 0u), startPos
,
3251 mRootElement
, aOffset
, aLineBreakType
);
3254 nsresult
ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(
3255 SimpleRange
& aSimpleRange
) {
3256 MOZ_ASSERT(aSimpleRange
.Collapsed());
3258 if (!aSimpleRange
.Collapsed()) {
3259 return NS_ERROR_INVALID_ARG
;
3262 const RangeBoundary
& startPoint
= aSimpleRange
.Start();
3263 if (NS_WARN_IF(!startPoint
.IsSet())) {
3264 return NS_ERROR_INVALID_ARG
;
3267 // If the node does not have children like a text node, we don't need to
3268 // modify aSimpleRange.
3269 if (!startPoint
.Container()->HasChildren()) {
3273 // If the container is not a text node but it has a text node at the offset,
3274 // we should adjust the range into the text node.
3275 // NOTE: This is emulating similar situation of EditorBase.
3276 if (startPoint
.IsStartOfContainer()) {
3277 // If the range is the start of the container, adjusted the range to the
3278 // start of the first child.
3279 if (!startPoint
.Container()->GetFirstChild()->IsText()) {
3282 nsresult rv
= aSimpleRange
.CollapseTo(
3283 RawRangeBoundary(startPoint
.Container()->GetFirstChild(), 0u));
3284 if (NS_WARN_IF(NS_FAILED(rv
))) {
3290 if (!startPoint
.IsSetAndValid()) {
3294 // If start of the range is next to a child node, adjust the range to the
3295 // end of the previous child (i.e., startPoint.Ref()).
3296 if (!startPoint
.Ref()->IsText()) {
3299 nsresult rv
= aSimpleRange
.CollapseTo(
3300 RawRangeBoundary(startPoint
.Ref(), startPoint
.Ref()->Length()));
3301 if (NS_WARN_IF(NS_FAILED(rv
))) {
3307 nsresult
ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame
* aFrame
,
3309 NS_ASSERTION(aFrame
, "aFrame must not be null");
3311 nsPresContext
* thisPC
= aFrame
->PresContext();
3312 nsPresContext
* rootPC
= thisPC
->GetRootPresContext();
3313 if (NS_WARN_IF(!rootPC
)) {
3314 return NS_ERROR_FAILURE
;
3316 nsIFrame
* rootFrame
= rootPC
->PresShell()->GetRootFrame();
3317 if (NS_WARN_IF(!rootFrame
)) {
3318 return NS_ERROR_FAILURE
;
3321 aRect
= nsLayoutUtils::TransformFrameRectToAncestor(aFrame
, aRect
, rootFrame
);
3323 // TransformFrameRectToAncestor returned the rect in the ancestor's appUnits,
3324 // but we want it in aFrame's units (in case of different full-zoom factors),
3326 aRect
= aRect
.ScaleToOtherAppUnitsRoundOut(rootPC
->AppUnitsPerDevPixel(),
3327 thisPC
->AppUnitsPerDevPixel());
3332 static void AdjustRangeForSelection(const Element
* aRootElement
,
3334 Maybe
<uint32_t>* aNodeOffset
) {
3335 nsINode
* node
= *aNode
;
3336 Maybe
<uint32_t> nodeOffset
= *aNodeOffset
;
3337 if (aRootElement
== node
|| NS_WARN_IF(!node
->GetParent()) ||
3342 // When the offset is at the end of the text node, set it to after the
3343 // text node, to make sure the caret is drawn on a new line when the last
3344 // character of the text node is '\n' in <textarea>.
3345 const uint32_t textLength
= node
->AsContent()->TextLength();
3346 MOZ_ASSERT(nodeOffset
.isNothing() || *nodeOffset
<= textLength
,
3347 "Offset is past length of text node");
3348 if (nodeOffset
.isNothing() || *nodeOffset
!= textLength
) {
3352 Element
* rootParentElement
= aRootElement
->GetParentElement();
3353 if (NS_WARN_IF(!rootParentElement
)) {
3356 // If the root node is not an anonymous div of <textarea>, we don't need to
3357 // do this hack. If you did this, ContentEventHandler couldn't distinguish
3358 // if the range includes open tag of the next node in some cases, e.g.,
3359 // textNode]<p></p> vs. textNode<p>]</p>
3360 if (!rootParentElement
->IsHTMLElement(nsGkAtoms::textarea
)) {
3364 // If the node is being removed from its parent, it holds the ex-parent,
3365 // but the parent have already removed the child from its child chain.
3366 // Therefore `ComputeIndexOf` may fail, but I don't want to make Beta/Nightly
3367 // crash at accessing `Maybe::operator*` so that here checks `isSome`, but
3368 // crashing only in debug builds may help to debug something complicated
3369 // situation, therefore, `MOZ_ASSERT` is put here.
3370 *aNode
= node
->GetParent();
3371 Maybe
<uint32_t> index
= (*aNode
)->ComputeIndexOf(node
);
3372 MOZ_ASSERT(index
.isSome());
3373 if (index
.isSome()) {
3374 MOZ_ASSERT(*index
!= UINT32_MAX
);
3375 *aNodeOffset
= Some(*index
+ 1u);
3377 *aNodeOffset
= Some(0u);
3381 nsresult
ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent
* aEvent
) {
3382 aEvent
->mSucceeded
= false;
3384 // Get selection to manipulate
3385 // XXX why do we need to get them from ISM? This method should work fine
3387 nsresult rv
= IMEStateManager::GetFocusSelectionAndRootElement(
3388 getter_AddRefs(mSelection
), getter_AddRefs(mRootElement
));
3389 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
3390 NS_ENSURE_SUCCESS(rv
, rv
);
3393 NS_ENSURE_SUCCESS(rv
, rv
);
3396 // Get range from offset and length
3397 nsINode
* startNode
= nullptr;
3398 nsINode
* endNode
= nullptr;
3399 Maybe
<uint32_t> startNodeOffset
;
3400 Maybe
<uint32_t> endNodeOffset
;
3402 Result
<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
3403 domRangeAndAdjustedOffsetOrError
=
3404 ConvertFlatTextOffsetToUnsafeDOMRange(
3405 aEvent
->mOffset
, aEvent
->mLength
, GetLineBreakType(aEvent
),
3406 aEvent
->mExpandToClusterBoundary
);
3407 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError
.isErr())) {
3409 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() failed");
3410 return domRangeAndAdjustedOffsetOrError
.unwrapErr();
3412 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
3413 domRangeAndAdjustedOffset
= domRangeAndAdjustedOffsetOrError
.unwrap();
3414 startNode
= domRangeAndAdjustedOffset
.mRange
.GetStartContainer();
3415 endNode
= domRangeAndAdjustedOffset
.mRange
.GetEndContainer();
3416 startNodeOffset
= Some(domRangeAndAdjustedOffset
.mRange
.StartOffset());
3417 endNodeOffset
= Some(domRangeAndAdjustedOffset
.mRange
.EndOffset());
3418 AdjustRangeForSelection(mRootElement
, &startNode
, &startNodeOffset
);
3419 AdjustRangeForSelection(mRootElement
, &endNode
, &endNodeOffset
);
3420 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
) ||
3421 NS_WARN_IF(startNodeOffset
.isNothing()) ||
3422 NS_WARN_IF(endNodeOffset
.isNothing())) {
3423 return NS_ERROR_UNEXPECTED
;
3427 if (aEvent
->mReversed
) {
3428 nsCOMPtr
<nsINode
> startNodeStrong(startNode
);
3429 nsCOMPtr
<nsINode
> endNodeStrong(endNode
);
3431 MOZ_KnownLive(mSelection
)
3432 ->SetBaseAndExtentInLimiter(*endNodeStrong
, *endNodeOffset
,
3433 *startNodeStrong
, *startNodeOffset
, error
);
3434 if (NS_WARN_IF(error
.Failed())) {
3435 return error
.StealNSResult();
3438 nsCOMPtr
<nsINode
> startNodeStrong(startNode
);
3439 nsCOMPtr
<nsINode
> endNodeStrong(endNode
);
3441 MOZ_KnownLive(mSelection
)
3442 ->SetBaseAndExtentInLimiter(*startNodeStrong
, *startNodeOffset
,
3443 *endNodeStrong
, *endNodeOffset
, error
);
3444 if (NS_WARN_IF(error
.Failed())) {
3445 return error
.StealNSResult();
3449 // `ContentEventHandler` is a `MOZ_STACK_CLASS`, so `mSelection` is known to
3451 MOZ_KnownLive(mSelection
)
3452 ->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION
,
3453 ScrollAxis(), ScrollAxis(), 0);
3454 aEvent
->mSucceeded
= true;
3458 nsRect
ContentEventHandler::FrameRelativeRect::RectRelativeTo(
3459 nsIFrame
* aDestFrame
) const {
3460 if (!mBaseFrame
|| NS_WARN_IF(!aDestFrame
)) {
3464 if (NS_WARN_IF(aDestFrame
->PresContext() != mBaseFrame
->PresContext())) {
3468 if (aDestFrame
== mBaseFrame
) {
3472 nsIFrame
* rootFrame
= mBaseFrame
->PresShell()->GetRootFrame();
3473 nsRect baseFrameRectInRootFrame
= nsLayoutUtils::TransformFrameRectToAncestor(
3474 mBaseFrame
, nsRect(), rootFrame
);
3475 nsRect destFrameRectInRootFrame
= nsLayoutUtils::TransformFrameRectToAncestor(
3476 aDestFrame
, nsRect(), rootFrame
);
3477 nsPoint difference
=
3478 destFrameRectInRootFrame
.TopLeft() - baseFrameRectInRootFrame
.TopLeft();
3479 return mRect
- difference
;
3482 } // namespace mozilla