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/TextComposition.h"
19 #include "mozilla/TextEditor.h"
20 #include "mozilla/TextEvents.h"
21 #include "mozilla/dom/Element.h"
22 #include "mozilla/dom/HTMLBRElement.h"
23 #include "mozilla/dom/HTMLUnknownElement.h"
24 #include "mozilla/dom/Selection.h"
25 #include "mozilla/dom/Text.h"
28 #include "nsContentUtils.h"
29 #include "nsCopySupport.h"
30 #include "nsElementTable.h"
31 #include "nsFocusManager.h"
32 #include "nsFontMetrics.h"
33 #include "nsFrameSelection.h"
35 #include "nsLayoutUtils.h"
36 #include "nsPresContext.h"
37 #include "nsQueryObject.h"
39 #include "nsTextFragment.h"
40 #include "nsTextFrame.h"
42 #include "mozilla/ViewportUtils.h"
46 // Work around conflicting define in rpcndr.h
49 #endif // defined(small)
54 using namespace widget
;
56 /******************************************************************/
57 /* ContentEventHandler::RawRange */
58 /******************************************************************/
60 void ContentEventHandler::RawRange::AssertStartIsBeforeOrEqualToEnd() {
62 *nsContentUtils::ComparePoints(
64 *mStart
.Offset(NodePosition::OffsetFilter::kValidOrInvalidOffsets
),
66 *mEnd
.Offset(NodePosition::OffsetFilter::kValidOrInvalidOffsets
)) <=
70 nsresult
ContentEventHandler::RawRange::SetStart(
71 const RawRangeBoundary
& aStart
) {
72 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aStart
.Container());
74 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
77 if (!aStart
.IsSetAndValid()) {
78 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
81 // Collapse if not positioned yet, or if positioned in another document.
82 if (!IsPositioned() || newRoot
!= mRoot
) {
84 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
85 mEnd
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
89 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
90 AssertStartIsBeforeOrEqualToEnd();
94 nsresult
ContentEventHandler::RawRange::SetEnd(const RawRangeBoundary
& aEnd
) {
95 nsINode
* newRoot
= RangeUtils::ComputeRootNode(aEnd
.Container());
97 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
100 if (!aEnd
.IsSetAndValid()) {
101 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
104 // Collapse if not positioned yet, or if positioned in another document.
105 if (!IsPositioned() || newRoot
!= mRoot
) {
107 mStart
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
108 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
112 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
113 AssertStartIsBeforeOrEqualToEnd();
117 nsresult
ContentEventHandler::RawRange::SetEndAfter(nsINode
* aEndContainer
) {
118 return SetEnd(RangeUtils::GetRawRangeBoundaryAfter(aEndContainer
));
121 void ContentEventHandler::RawRange::SetStartAndEnd(const nsRange
* aRange
) {
122 DebugOnly
<nsresult
> rv
=
123 SetStartAndEnd(aRange
->StartRef().AsRaw(), aRange
->EndRef().AsRaw());
124 MOZ_ASSERT(!aRange
->IsPositioned() || NS_SUCCEEDED(rv
));
127 nsresult
ContentEventHandler::RawRange::SetStartAndEnd(
128 const RawRangeBoundary
& aStart
, const RawRangeBoundary
& aEnd
) {
129 nsINode
* newStartRoot
= RangeUtils::ComputeRootNode(aStart
.Container());
131 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
133 if (!aStart
.IsSetAndValid()) {
134 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
137 if (aStart
.Container() == aEnd
.Container()) {
138 if (!aEnd
.IsSetAndValid()) {
139 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
141 MOZ_ASSERT(*aStart
.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets
) <=
142 *aEnd
.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets
));
143 mRoot
= newStartRoot
;
144 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
145 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
149 nsINode
* newEndRoot
= RangeUtils::ComputeRootNode(aEnd
.Container());
151 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
153 if (!aEnd
.IsSetAndValid()) {
154 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
157 // If they have different root, this should be collapsed at the end point.
158 if (newStartRoot
!= newEndRoot
) {
160 mStart
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
161 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
165 // Otherwise, set the range as specified.
166 mRoot
= newStartRoot
;
167 mStart
.CopyFrom(aStart
, RangeBoundaryIsMutationObserved::Yes
);
168 mEnd
.CopyFrom(aEnd
, RangeBoundaryIsMutationObserved::Yes
);
169 AssertStartIsBeforeOrEqualToEnd();
173 nsresult
ContentEventHandler::RawRange::SelectNodeContents(
174 const nsINode
* aNodeToSelectContents
) {
175 nsINode
* const newRoot
=
176 RangeUtils::ComputeRootNode(const_cast<nsINode
*>(aNodeToSelectContents
));
178 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR
;
181 mStart
= RangeBoundary(const_cast<nsINode
*>(aNodeToSelectContents
), nullptr);
182 mEnd
= RangeBoundary(const_cast<nsINode
*>(aNodeToSelectContents
),
183 aNodeToSelectContents
->GetLastChild());
187 /******************************************************************/
188 /* ContentEventHandler */
189 /******************************************************************/
193 // ContentEventHandler *creates* ranges as following rules:
194 // 1. Start of range:
195 // 1.1. Cases: [textNode or text[Node or textNode[
196 // When text node is start of a range, start node is the text node and
197 // start offset is any number between 0 and the length of the text.
198 // 1.2. Case: [<element>:
199 // When start of an element node is start of a range, start node is
200 // parent of the element and start offset is the element's index in the
202 // 1.3. Case: <element/>[
203 // When after an empty element node is start of a range, start node is
204 // parent of the element and start offset is the element's index in the
206 // 1.4. Case: <element>[
207 // When start of a non-empty element is start of a range, start node is
208 // the element and start offset is 0.
209 // 1.5. Case: <root>[
210 // When start of a range is 0 and there are no nodes causing text,
211 // start node is the root node and start offset is 0.
212 // 1.6. Case: [</root>
213 // When start of a range is out of bounds, start node is the root node
214 // and start offset is number of the children.
216 // 2.1. Cases: ]textNode or text]Node or textNode]
217 // When a text node is end of a range, end node is the text node and
218 // end offset is any number between 0 and the length of the text.
219 // 2.2. Case: ]<element>
220 // When before an element node (meaning before the open tag of the
221 // element) is end of a range, end node is previous node causing text.
222 // Note that this case shouldn't be handled directly. If rule 2.1 and
223 // 2.3 are handled correctly, the loop with ContentIterator shouldn't
224 // reach the element node since the loop should've finished already at
225 // handling the last node which caused some text.
226 // 2.3. Case: <element>]
227 // When a line break is caused before a non-empty element node and it's
228 // end of a range, end node is the element and end offset is 0.
229 // (i.e., including open tag of the element)
230 // 2.4. Cases: <element/>]
231 // When after an empty element node is end of a range, end node is
232 // parent of the element node and end offset is the element's index in
233 // the parent + 1. (i.e., including close tag of the element or empty
235 // 2.5. Case: ]</root>
236 // When end of a range is out of bounds, end node is the root node and
237 // end offset is number of the children.
239 // ContentEventHandler *treats* ranges as following additional rules:
240 // 1. When the start node is an element node which doesn't have children,
241 // it includes a line break caused before itself (i.e., includes its open
242 // tag). For example, if start position is { <br>, 0 }, the line break
243 // caused by <br> should be included into the flatten text.
244 // 2. When the end node is an element node which doesn't have children,
245 // it includes the end (i.e., includes its close tag except empty element).
246 // Although, currently, any close tags don't cause line break, this also
247 // includes its open tag. For example, if end position is { <br>, 0 }, the
248 // line break caused by the <br> should be included into the flatten text.
250 ContentEventHandler::ContentEventHandler(nsPresContext
* aPresContext
)
251 : mDocument(aPresContext
->Document()) {}
253 nsresult
ContentEventHandler::InitBasic(bool aRequireFlush
) {
254 NS_ENSURE_TRUE(mDocument
, NS_ERROR_NOT_AVAILABLE
);
256 // If text frame which has overflowing selection underline is dirty,
257 // we need to flush the pending reflow here.
258 mDocument
->FlushPendingNotifications(FlushType::Layout
);
263 nsresult
ContentEventHandler::InitRootContent(
264 const Selection
& aNormalSelection
) {
265 // Root content should be computed with normal selection because normal
266 // selection is typically has at least one range but the other selections
267 // not so. If there is a range, computing its root is easy, but if
268 // there are no ranges, we need to use ancestor limit instead.
269 MOZ_ASSERT(aNormalSelection
.Type() == SelectionType::eNormal
);
271 if (!aNormalSelection
.RangeCount()) {
272 // If there is no selection range, we should compute the selection root
273 // from ancestor limiter or root content of the document.
275 Element::FromNodeOrNull(aNormalSelection
.GetAncestorLimiter());
277 mRootElement
= mDocument
->GetRootElement();
278 if (NS_WARN_IF(!mRootElement
)) {
279 return NS_ERROR_NOT_AVAILABLE
;
285 RefPtr
<const nsRange
> range(aNormalSelection
.GetRangeAt(0));
286 if (NS_WARN_IF(!range
)) {
287 return NS_ERROR_UNEXPECTED
;
290 // If there is a selection, we should retrieve the selection root from
291 // the range since when the window is inactivated, the ancestor limiter
292 // of selection was cleared by blur event handler of EditorBase but the
293 // selection range still keeps storing the nodes. If the active element of
294 // the deactive window is <input> or <textarea>, we can compute the
295 // selection root from them.
296 nsCOMPtr
<nsINode
> startNode
= range
->GetStartContainer();
297 nsINode
* endNode
= range
->GetEndContainer();
298 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
)) {
299 return NS_ERROR_FAILURE
;
302 // See bug 537041 comment 5, the range could have removed node.
303 if (NS_WARN_IF(startNode
->GetComposedDoc() != mDocument
)) {
304 return NS_ERROR_FAILURE
;
307 NS_ASSERTION(startNode
->GetComposedDoc() == endNode
->GetComposedDoc(),
308 "firstNormalSelectionRange crosses the document boundary");
310 RefPtr
<PresShell
> presShell
= mDocument
->GetPresShell();
312 Element::FromNodeOrNull(startNode
->GetSelectionRootContent(presShell
));
313 if (NS_WARN_IF(!mRootElement
)) {
314 return NS_ERROR_FAILURE
;
320 nsresult
ContentEventHandler::InitCommon(EventMessage aEventMessage
,
321 SelectionType aSelectionType
,
322 bool aRequireFlush
) {
323 if (mSelection
&& mSelection
->Type() == aSelectionType
) {
327 mSelection
= nullptr;
328 mRootElement
= nullptr;
329 mFirstSelectedRawRange
.Clear();
331 nsresult rv
= InitBasic(aRequireFlush
);
332 NS_ENSURE_SUCCESS(rv
, rv
);
334 RefPtr
<nsFrameSelection
> frameSel
;
335 if (PresShell
* presShell
= mDocument
->GetPresShell()) {
336 frameSel
= presShell
->GetLastFocusedFrameSelection();
338 if (NS_WARN_IF(!frameSel
)) {
339 return NS_ERROR_NOT_AVAILABLE
;
342 mSelection
= frameSel
->GetSelection(aSelectionType
);
343 if (NS_WARN_IF(!mSelection
)) {
344 return NS_ERROR_NOT_AVAILABLE
;
347 RefPtr
<Selection
> normalSelection
;
348 if (mSelection
->Type() == SelectionType::eNormal
) {
349 normalSelection
= mSelection
;
351 normalSelection
= frameSel
->GetSelection(SelectionType::eNormal
);
352 if (NS_WARN_IF(!normalSelection
)) {
353 return NS_ERROR_NOT_AVAILABLE
;
357 rv
= InitRootContent(*normalSelection
);
358 if (NS_WARN_IF(NS_FAILED(rv
))) {
362 if (mSelection
->RangeCount()) {
363 mFirstSelectedRawRange
.SetStartAndEnd(mSelection
->GetRangeAt(0));
367 // Even if there are no selection ranges, it's usual case if aSelectionType
368 // is a special selection or we're handling eQuerySelectedText.
369 if (aSelectionType
!= SelectionType::eNormal
||
370 aEventMessage
== eQuerySelectedText
) {
371 MOZ_ASSERT(!mFirstSelectedRawRange
.IsPositioned());
375 // But otherwise, we need to assume that there is a selection range at the
376 // beginning of the root content if aSelectionType is eNormal.
377 rv
= mFirstSelectedRawRange
.CollapseTo(RawRangeBoundary(mRootElement
, 0u));
378 if (NS_WARN_IF(NS_FAILED(rv
))) {
379 return NS_ERROR_UNEXPECTED
;
384 nsresult
ContentEventHandler::Init(WidgetQueryContentEvent
* aEvent
) {
385 NS_ASSERTION(aEvent
, "aEvent must not be null");
386 MOZ_ASSERT(aEvent
->mMessage
== eQuerySelectedText
||
387 aEvent
->mInput
.mSelectionType
== SelectionType::eNormal
);
389 if (NS_WARN_IF(!aEvent
->mInput
.IsValidOffset()) ||
390 NS_WARN_IF(!aEvent
->mInput
.IsValidEventMessage(aEvent
->mMessage
))) {
391 return NS_ERROR_FAILURE
;
394 // Note that we should ignore WidgetQueryContentEvent::Input::mSelectionType
395 // if the event isn't eQuerySelectedText.
396 SelectionType selectionType
= aEvent
->mMessage
== eQuerySelectedText
397 ? aEvent
->mInput
.mSelectionType
398 : SelectionType::eNormal
;
399 if (NS_WARN_IF(selectionType
== SelectionType::eNone
)) {
400 return NS_ERROR_FAILURE
;
404 InitCommon(aEvent
->mMessage
, selectionType
, aEvent
->NeedsToFlushLayout());
405 NS_ENSURE_SUCCESS(rv
, rv
);
407 // Be aware, WidgetQueryContentEvent::mInput::mOffset should be made absolute
408 // offset before sending it to ContentEventHandler because querying selection
409 // every time may be expensive. So, if the caller caches selection, it
410 // should initialize the event with the cached value.
411 if (aEvent
->mInput
.mRelativeToInsertionPoint
) {
412 MOZ_ASSERT(selectionType
== SelectionType::eNormal
);
413 RefPtr
<TextComposition
> composition
=
414 IMEStateManager::GetTextCompositionFor(aEvent
->mWidget
);
416 uint32_t compositionStart
= composition
->NativeOffsetOfStartComposition();
417 if (NS_WARN_IF(!aEvent
->mInput
.MakeOffsetAbsolute(compositionStart
))) {
418 return NS_ERROR_FAILURE
;
421 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
422 uint32_t selectionStart
= 0;
423 rv
= GetStartOffset(mFirstSelectedRawRange
, &selectionStart
,
425 if (NS_WARN_IF(NS_FAILED(rv
))) {
426 return NS_ERROR_FAILURE
;
428 if (NS_WARN_IF(!aEvent
->mInput
.MakeOffsetAbsolute(selectionStart
))) {
429 return NS_ERROR_FAILURE
;
434 // Ideally, we should emplace only when we return succeeded event.
435 // However, we need to emplace here since it's hard to store the various
436 // result. Intead, `HandleQueryContentEvent()` will reset `mReply` if
437 // corresponding handler returns error.
438 aEvent
->EmplaceReply();
440 aEvent
->mReply
->mContentsRoot
= mRootElement
.get();
441 aEvent
->mReply
->mIsEditableContent
=
442 mRootElement
&& mRootElement
->IsEditable();
445 nsIFrame
* frame
= nsCaret::GetGeometry(mSelection
, &r
);
447 frame
= mRootElement
->GetPrimaryFrame();
448 if (NS_WARN_IF(!frame
)) {
449 return NS_ERROR_FAILURE
;
452 aEvent
->mReply
->mFocusedWidget
= frame
->GetNearestWidget();
457 nsresult
ContentEventHandler::Init(WidgetSelectionEvent
* aEvent
) {
458 NS_ASSERTION(aEvent
, "aEvent must not be null");
460 nsresult rv
= InitCommon(aEvent
->mMessage
);
461 NS_ENSURE_SUCCESS(rv
, rv
);
463 aEvent
->mSucceeded
= false;
468 nsIContent
* ContentEventHandler::GetFocusedContent() {
469 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
470 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
471 return nsFocusManager::GetFocusedDescendant(
472 window
, nsFocusManager::eIncludeAllDescendants
,
473 getter_AddRefs(focusedWindow
));
476 nsresult
ContentEventHandler::QueryContentRect(
477 nsIContent
* aContent
, WidgetQueryContentEvent
* aEvent
) {
478 MOZ_ASSERT(aContent
, "aContent must not be null");
480 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
481 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
483 // get rect for first frame
484 nsRect
resultRect(nsPoint(0, 0), frame
->GetRect().Size());
485 nsresult rv
= ConvertToRootRelativeOffset(frame
, resultRect
);
486 NS_ENSURE_SUCCESS(rv
, rv
);
488 nsPresContext
* presContext
= frame
->PresContext();
490 // account for any additional frames
491 while ((frame
= frame
->GetNextContinuation())) {
492 nsRect
frameRect(nsPoint(0, 0), frame
->GetRect().Size());
493 rv
= ConvertToRootRelativeOffset(frame
, frameRect
);
494 NS_ENSURE_SUCCESS(rv
, rv
);
495 resultRect
.UnionRect(resultRect
, frameRect
);
498 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
499 resultRect
, presContext
->AppUnitsPerDevPixel());
500 // Returning empty rect may cause native IME confused, let's make sure to
501 // return non-empty rect.
502 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
507 // Editor places a padding <br> element under its root content if the editor
508 // doesn't have any text. This happens even for single line editors.
509 // When we get text content and when we change the selection,
510 // we don't want to include the padding <br> elements at the end.
511 static bool IsContentBR(const nsIContent
& aContent
) {
512 const HTMLBRElement
* brElement
= HTMLBRElement::FromNode(aContent
);
513 return brElement
&& !brElement
->IsPaddingForEmptyLastLine() &&
514 !brElement
->IsPaddingForEmptyEditor();
517 static bool IsPaddingBR(const nsIContent
& aContent
) {
518 return aContent
.IsHTMLElement(nsGkAtoms::br
) && !IsContentBR(aContent
);
521 static void ConvertToNativeNewlines(nsString
& aString
) {
523 aString
.ReplaceSubstring(u
"\n"_ns
, u
"\r\n"_ns
);
527 static void AppendString(nsString
& aString
, const Text
& aTextNode
) {
528 const uint32_t oldXPLength
= aString
.Length();
529 aTextNode
.TextFragment().AppendTo(aString
);
530 if (aTextNode
.HasFlag(NS_MAYBE_MASKED
)) {
531 TextEditor::MaskString(aString
, aTextNode
, oldXPLength
, 0);
535 static void AppendSubString(nsString
& aString
, const Text
& aTextNode
,
536 uint32_t aXPOffset
, uint32_t aXPLength
) {
537 const uint32_t oldXPLength
= aString
.Length();
538 aTextNode
.TextFragment().AppendTo(aString
, aXPOffset
, aXPLength
);
539 if (aTextNode
.HasFlag(NS_MAYBE_MASKED
)) {
540 TextEditor::MaskString(aString
, aTextNode
, oldXPLength
, aXPOffset
);
545 template <typename StringType
>
546 static uint32_t CountNewlinesInXPLength(const StringType
& aString
) {
548 const auto* end
= aString
.EndReading();
549 for (const auto* iter
= aString
.BeginReading(); iter
< end
; ++iter
) {
557 static uint32_t CountNewlinesInXPLength(const Text
& aTextNode
,
558 uint32_t aXPLength
) {
559 const nsTextFragment
& textFragment
= aTextNode
.TextFragment();
560 // For automated tests, we should abort on debug build.
561 MOZ_ASSERT(aXPLength
== UINT32_MAX
|| aXPLength
<= textFragment
.GetLength(),
562 "aXPLength is out-of-bounds");
563 const uint32_t length
= std::min(aXPLength
, textFragment
.GetLength());
567 if (textFragment
.Is2b()) {
568 nsDependentSubstring
str(textFragment
.Get2b(), length
);
569 return CountNewlinesInXPLength(str
);
571 nsDependentCSubstring
str(textFragment
.Get1b(), length
);
572 return CountNewlinesInXPLength(str
);
575 template <typename StringType
>
576 static uint32_t CountNewlinesInNativeLength(const StringType
& aString
,
577 uint32_t aNativeLength
) {
579 (aNativeLength
== UINT32_MAX
|| aNativeLength
<= aString
.Length() * 2),
580 "aNativeLength is unexpected value");
582 uint32_t nativeOffset
= 0;
583 const auto* end
= aString
.EndReading();
584 for (const auto* iter
= aString
.BeginReading();
585 iter
< end
&& nativeOffset
< aNativeLength
; ++iter
, ++nativeOffset
) {
594 static uint32_t CountNewlinesInNativeLength(const Text
& aTextNode
,
595 uint32_t aNativeLength
) {
596 const nsTextFragment
& textFragment
= aTextNode
.TextFragment();
597 const uint32_t xpLength
= textFragment
.GetLength();
601 if (textFragment
.Is2b()) {
602 nsDependentSubstring
str(textFragment
.Get2b(), xpLength
);
603 return CountNewlinesInNativeLength(str
, aNativeLength
);
605 nsDependentCSubstring
str(textFragment
.Get1b(), xpLength
);
606 return CountNewlinesInNativeLength(str
, aNativeLength
);
611 uint32_t ContentEventHandler::GetNativeTextLength(const Text
& aTextNode
,
612 uint32_t aStartOffset
,
613 uint32_t aEndOffset
) {
614 MOZ_ASSERT(aEndOffset
>= aStartOffset
,
615 "aEndOffset must be equals or larger than aStartOffset");
616 if (aStartOffset
== aEndOffset
) {
619 return GetTextLength(aTextNode
, LINE_BREAK_TYPE_NATIVE
, aEndOffset
) -
620 GetTextLength(aTextNode
, LINE_BREAK_TYPE_NATIVE
, aStartOffset
);
624 uint32_t ContentEventHandler::GetNativeTextLength(const Text
& aTextNode
,
625 uint32_t aMaxLength
) {
626 return GetTextLength(aTextNode
, LINE_BREAK_TYPE_NATIVE
, aMaxLength
);
630 uint32_t ContentEventHandler::GetBRLength(LineBreakType aLineBreakType
) {
633 return (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) ? 2 : 1;
640 uint32_t ContentEventHandler::GetTextLength(const Text
& aTextNode
,
641 LineBreakType aLineBreakType
,
642 uint32_t aMaxLength
) {
643 const uint32_t textLengthDifference
=
645 // On Windows, the length of a native newline ("\r\n") is twice the length
646 // of the XP newline ("\n"), so XP length is equal to the length of the
647 // native offset plus the number of newlines encountered in the string.
648 (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
)
649 ? CountNewlinesInXPLength(aTextNode
, aMaxLength
)
652 // On other platforms, the native and XP newlines are the same.
656 const uint32_t length
=
657 std::min(aTextNode
.TextFragment().GetLength(), aMaxLength
);
658 return length
+ textLengthDifference
;
661 static uint32_t ConvertToXPOffset(const Text
& aTextNode
,
662 uint32_t aNativeOffset
) {
664 // On Windows, the length of a native newline ("\r\n") is twice the length of
665 // the XP newline ("\n"), so XP offset is equal to the length of the native
666 // offset minus the number of newlines encountered in the string.
667 return aNativeOffset
- CountNewlinesInNativeLength(aTextNode
, aNativeOffset
);
669 // On other platforms, the native and XP newlines are the same.
670 return aNativeOffset
;
675 uint32_t ContentEventHandler::GetNativeTextLength(const nsAString
& aText
) {
676 const uint32_t textLengthDifference
=
678 // On Windows, the length of a native newline ("\r\n") is twice the length
679 // of the XP newline ("\n"), so XP length is equal to the length of the
680 // native offset plus the number of newlines encountered in the string.
681 CountNewlinesInXPLength(aText
);
683 // On other platforms, the native and XP newlines are the same.
686 return aText
.Length() + textLengthDifference
;
690 bool ContentEventHandler::ShouldBreakLineBefore(const nsIContent
& aContent
,
691 const Element
* aRootElement
) {
692 // We don't need to append linebreak at the start of the root element.
693 if (&aContent
== aRootElement
) {
697 // If it's not an HTML element (including other markup language's elements),
698 // we shouldn't insert like break before that for now. Becoming this is a
699 // problem must be edge case. E.g., when ContentEventHandler is used with
700 // MathML or SVG elements.
701 if (!aContent
.IsHTMLElement()) {
705 // If the element is <br>, we need to check if the <br> is caused by web
706 // content. Otherwise, i.e., it's caused by internal reason of Gecko,
707 // it shouldn't be exposed as a line break to flatten text.
708 if (aContent
.IsHTMLElement(nsGkAtoms::br
)) {
709 return IsContentBR(aContent
);
712 // Note that ideally, we should refer the style of the primary frame of
713 // aContent for deciding if it's an inline. However, it's difficult
714 // IMEContentObserver to notify IME of text change caused by style change.
715 // Therefore, currently, we should check only from the tag for now.
716 if (aContent
.IsAnyOfHTMLElements(
717 nsGkAtoms::a
, nsGkAtoms::abbr
, nsGkAtoms::acronym
, nsGkAtoms::b
,
718 nsGkAtoms::bdi
, nsGkAtoms::bdo
, nsGkAtoms::big
, nsGkAtoms::cite
,
719 nsGkAtoms::code
, nsGkAtoms::data
, nsGkAtoms::del
, nsGkAtoms::dfn
,
720 nsGkAtoms::em
, nsGkAtoms::font
, nsGkAtoms::i
, nsGkAtoms::ins
,
721 nsGkAtoms::kbd
, nsGkAtoms::mark
, nsGkAtoms::s
, nsGkAtoms::samp
,
722 nsGkAtoms::small
, nsGkAtoms::span
, nsGkAtoms::strike
,
723 nsGkAtoms::strong
, nsGkAtoms::sub
, nsGkAtoms::sup
, nsGkAtoms::time
,
724 nsGkAtoms::tt
, nsGkAtoms::u
, nsGkAtoms::var
)) {
728 // If the element is unknown element, we shouldn't insert line breaks before
729 // it since unknown elements should be ignored.
730 RefPtr
<HTMLUnknownElement
> unknownHTMLElement
=
731 do_QueryObject(const_cast<nsIContent
*>(&aContent
));
732 return !unknownHTMLElement
;
735 nsresult
ContentEventHandler::GenerateFlatTextContent(
736 const Element
* aElement
, nsString
& aString
, LineBreakType aLineBreakType
) {
737 MOZ_ASSERT(aString
.IsEmpty());
740 nsresult rv
= rawRange
.SelectNodeContents(aElement
);
741 if (NS_WARN_IF(NS_FAILED(rv
))) {
744 return GenerateFlatTextContent(rawRange
, aString
, aLineBreakType
);
747 nsresult
ContentEventHandler::GenerateFlatTextContent(
748 const RawRange
& aRawRange
, nsString
& aString
,
749 LineBreakType aLineBreakType
) {
750 MOZ_ASSERT(aString
.IsEmpty());
752 if (aRawRange
.Collapsed()) {
756 nsINode
* startNode
= aRawRange
.GetStartContainer();
757 nsINode
* endNode
= aRawRange
.GetEndContainer();
758 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
)) {
759 return NS_ERROR_FAILURE
;
762 if (startNode
== endNode
&& startNode
->IsText()) {
763 AppendSubString(aString
, *startNode
->AsText(), aRawRange
.StartOffset(),
764 aRawRange
.EndOffset() - aRawRange
.StartOffset());
765 ConvertToNativeNewlines(aString
);
769 PreContentIterator preOrderIter
;
771 preOrderIter
.Init(aRawRange
.Start().AsRaw(), aRawRange
.End().AsRaw());
772 if (NS_WARN_IF(NS_FAILED(rv
))) {
775 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
776 nsINode
* node
= preOrderIter
.GetCurrentNode();
777 if (NS_WARN_IF(!node
)) {
780 if (!node
->IsContent()) {
784 if (const Text
* textNode
= Text::FromNode(node
)) {
785 if (textNode
== startNode
) {
786 AppendSubString(aString
, *textNode
, aRawRange
.StartOffset(),
787 textNode
->TextLength() - aRawRange
.StartOffset());
788 } else if (textNode
== endNode
) {
789 AppendSubString(aString
, *textNode
, 0, aRawRange
.EndOffset());
791 AppendString(aString
, *textNode
);
793 } else if (ShouldBreakLineBefore(*node
->AsContent(), mRootElement
)) {
794 aString
.Append(char16_t('\n'));
797 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
798 ConvertToNativeNewlines(aString
);
803 static FontRange
* AppendFontRange(nsTArray
<FontRange
>& aFontRanges
,
804 uint32_t aBaseOffset
) {
805 FontRange
* fontRange
= aFontRanges
.AppendElement();
806 fontRange
->mStartOffset
= aBaseOffset
;
811 uint32_t ContentEventHandler::GetTextLengthInRange(
812 const Text
& aTextNode
, uint32_t aXPStartOffset
, uint32_t aXPEndOffset
,
813 LineBreakType aLineBreakType
) {
814 return aLineBreakType
== LINE_BREAK_TYPE_NATIVE
815 ? GetNativeTextLength(aTextNode
, aXPStartOffset
, aXPEndOffset
)
816 : aXPEndOffset
- aXPStartOffset
;
820 void ContentEventHandler::AppendFontRanges(FontRangeArray
& aFontRanges
,
821 const Text
& aTextNode
,
822 uint32_t aBaseOffset
,
823 uint32_t aXPStartOffset
,
824 uint32_t aXPEndOffset
,
825 LineBreakType aLineBreakType
) {
826 nsIFrame
* frame
= aTextNode
.GetPrimaryFrame();
828 // It is a non-rendered content, create an empty range for it.
829 AppendFontRange(aFontRanges
, aBaseOffset
);
833 uint32_t baseOffset
= aBaseOffset
;
836 nsTextFrame
* text
= do_QueryFrame(frame
);
837 MOZ_ASSERT(text
, "Not a text frame");
840 auto* curr
= static_cast<nsTextFrame
*>(frame
);
842 uint32_t frameXPStart
= std::max(
843 static_cast<uint32_t>(curr
->GetContentOffset()), aXPStartOffset
);
844 uint32_t frameXPEnd
=
845 std::min(static_cast<uint32_t>(curr
->GetContentEnd()), aXPEndOffset
);
846 if (frameXPStart
>= frameXPEnd
) {
847 curr
= curr
->GetNextContinuation();
851 gfxSkipCharsIterator iter
= curr
->EnsureTextRun(nsTextFrame::eInflated
);
852 gfxTextRun
* textRun
= curr
->GetTextRun(nsTextFrame::eInflated
);
854 nsTextFrame
* next
= nullptr;
855 if (frameXPEnd
< aXPEndOffset
) {
856 next
= curr
->GetNextContinuation();
857 while (next
&& next
->GetTextRun(nsTextFrame::eInflated
) == textRun
) {
858 frameXPEnd
= std::min(static_cast<uint32_t>(next
->GetContentEnd()),
861 frameXPEnd
< aXPEndOffset
? next
->GetNextContinuation() : nullptr;
865 gfxTextRun::Range
skipRange(iter
.ConvertOriginalToSkipped(frameXPStart
),
866 iter
.ConvertOriginalToSkipped(frameXPEnd
));
867 uint32_t lastXPEndOffset
= frameXPStart
;
868 for (gfxTextRun::GlyphRunIterator
runIter(textRun
, skipRange
);
869 !runIter
.AtEnd(); runIter
.NextRun()) {
870 gfxFont
* font
= runIter
.GlyphRun()->mFont
.get();
871 uint32_t startXPOffset
=
872 iter
.ConvertSkippedToOriginal(runIter
.StringStart());
873 // It is possible that the first glyph run has exceeded the frame,
874 // because the whole frame is filled by skipped chars.
875 if (startXPOffset
>= frameXPEnd
) {
879 if (startXPOffset
> lastXPEndOffset
) {
880 // Create range for skipped leading chars.
881 AppendFontRange(aFontRanges
, baseOffset
);
882 baseOffset
+= GetTextLengthInRange(aTextNode
, lastXPEndOffset
,
883 startXPOffset
, aLineBreakType
);
886 FontRange
* fontRange
= AppendFontRange(aFontRanges
, baseOffset
);
887 fontRange
->mFontName
.Append(NS_ConvertUTF8toUTF16(font
->GetName()));
889 ParentLayerToScreenScale2D cumulativeResolution
=
890 ParentLayerToParentLayerScale(
891 frame
->PresShell()->GetCumulativeResolution()) *
892 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
895 std::max(cumulativeResolution
.xScale
, cumulativeResolution
.yScale
);
897 fontRange
->mFontSize
= font
->GetAdjustedSize() * scale
;
899 // The converted original offset may exceed the range,
900 // hence we need to clamp it.
901 uint32_t endXPOffset
= iter
.ConvertSkippedToOriginal(runIter
.StringEnd());
902 endXPOffset
= std::min(frameXPEnd
, endXPOffset
);
903 baseOffset
+= GetTextLengthInRange(aTextNode
, startXPOffset
, endXPOffset
,
905 lastXPEndOffset
= endXPOffset
;
907 if (lastXPEndOffset
< frameXPEnd
) {
908 // Create range for skipped trailing chars. It also handles case
909 // that the whole frame contains only skipped chars.
910 AppendFontRange(aFontRanges
, baseOffset
);
911 baseOffset
+= GetTextLengthInRange(aTextNode
, lastXPEndOffset
, frameXPEnd
,
919 nsresult
ContentEventHandler::GenerateFlatFontRanges(
920 const RawRange
& aRawRange
, FontRangeArray
& aFontRanges
, uint32_t& aLength
,
921 LineBreakType aLineBreakType
) {
922 MOZ_ASSERT(aFontRanges
.IsEmpty(), "aRanges must be empty array");
924 if (aRawRange
.Collapsed()) {
928 nsINode
* startNode
= aRawRange
.GetStartContainer();
929 nsINode
* endNode
= aRawRange
.GetEndContainer();
930 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
)) {
931 return NS_ERROR_FAILURE
;
934 // baseOffset is the flattened offset of each content node.
935 uint32_t baseOffset
= 0;
936 PreContentIterator preOrderIter
;
938 preOrderIter
.Init(aRawRange
.Start().AsRaw(), aRawRange
.End().AsRaw());
939 if (NS_WARN_IF(NS_FAILED(rv
))) {
942 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
943 nsINode
* node
= preOrderIter
.GetCurrentNode();
944 if (NS_WARN_IF(!node
)) {
947 if (!node
->IsContent()) {
950 nsIContent
* content
= node
->AsContent();
952 if (const Text
* textNode
= Text::FromNode(content
)) {
953 const uint32_t startOffset
=
954 textNode
!= startNode
? 0 : aRawRange
.StartOffset();
955 const uint32_t endOffset
=
956 textNode
!= endNode
? textNode
->TextLength() : aRawRange
.EndOffset();
957 AppendFontRanges(aFontRanges
, *textNode
, baseOffset
, startOffset
,
958 endOffset
, aLineBreakType
);
959 baseOffset
+= GetTextLengthInRange(*textNode
, startOffset
, endOffset
,
961 } else if (ShouldBreakLineBefore(*content
, mRootElement
)) {
962 if (aFontRanges
.IsEmpty()) {
963 MOZ_ASSERT(baseOffset
== 0);
964 FontRange
* fontRange
= AppendFontRange(aFontRanges
, baseOffset
);
965 if (nsIFrame
* frame
= content
->GetPrimaryFrame()) {
966 const nsFont
& font
= frame
->GetParent()->StyleFont()->mFont
;
967 const StyleFontFamilyList
& fontList
= font
.family
.families
;
968 MOZ_ASSERT(!fontList
.list
.IsEmpty(), "Empty font family?");
969 const StyleSingleFontFamily
* fontName
=
970 fontList
.list
.IsEmpty() ? nullptr : &fontList
.list
.AsSpan()[0];
973 fontName
->AppendToString(name
, false);
975 AppendUTF8toUTF16(name
, fontRange
->mFontName
);
977 ParentLayerToScreenScale2D cumulativeResolution
=
978 ParentLayerToParentLayerScale(
979 frame
->PresShell()->GetCumulativeResolution()) *
981 GetTransformToAncestorScaleCrossProcessForFrameMetrics(frame
);
983 float scale
= std::max(cumulativeResolution
.xScale
,
984 cumulativeResolution
.yScale
);
986 fontRange
->mFontSize
= frame
->PresContext()->CSSPixelsToDevPixels(
987 font
.size
.ToCSSPixels() * scale
);
990 baseOffset
+= GetBRLength(aLineBreakType
);
994 aLength
= baseOffset
;
998 nsresult
ContentEventHandler::ExpandToClusterBoundary(
999 Text
& aTextNode
, bool aForward
, uint32_t* aXPOffset
) const {
1000 // XXX This method assumes that the frame boundaries must be cluster
1001 // boundaries. It's false, but no problem now, maybe.
1002 if (*aXPOffset
== 0 || *aXPOffset
== aTextNode
.TextLength()) {
1006 NS_ASSERTION(*aXPOffset
<= aTextNode
.TextLength(), "offset is out of range.");
1008 MOZ_DIAGNOSTIC_ASSERT(mDocument
->GetPresShell());
1009 int32_t offsetInFrame
;
1010 CaretAssociationHint hint
=
1011 aForward
? CARET_ASSOCIATE_BEFORE
: CARET_ASSOCIATE_AFTER
;
1012 nsIFrame
* frame
= nsFrameSelection::GetFrameForNodeOffset(
1013 &aTextNode
, int32_t(*aXPOffset
), hint
, &offsetInFrame
);
1015 auto [startOffset
, endOffset
] = frame
->GetOffsets();
1016 if (*aXPOffset
== static_cast<uint32_t>(startOffset
) ||
1017 *aXPOffset
== static_cast<uint32_t>(endOffset
)) {
1020 if (!frame
->IsTextFrame()) {
1021 return NS_ERROR_FAILURE
;
1023 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(frame
);
1024 int32_t newOffsetInFrame
= *aXPOffset
- startOffset
;
1025 newOffsetInFrame
+= aForward
? -1 : 1;
1026 // PeekOffsetCharacter() should respect cluster but ignore user-select
1027 // style. If it returns "FOUND", we should use the result. Otherwise,
1028 // we shouldn't use the result because the offset was moved to reversed
1030 nsTextFrame::PeekOffsetCharacterOptions options
;
1031 options
.mRespectClusters
= true;
1032 options
.mIgnoreUserStyleAll
= true;
1033 if (textFrame
->PeekOffsetCharacter(aForward
, &newOffsetInFrame
, options
) ==
1035 *aXPOffset
= startOffset
+ newOffsetInFrame
;
1040 // If the frame isn't available, we only can check surrogate pair...
1041 if (aTextNode
.TextFragment().IsLowSurrogateFollowingHighSurrogateAt(
1043 *aXPOffset
+= aForward
? 1 : -1;
1048 nsresult
ContentEventHandler::SetRawRangeFromFlatTextOffset(
1049 RawRange
* aRawRange
, uint32_t aOffset
, uint32_t aLength
,
1050 LineBreakType aLineBreakType
, bool aExpandToClusterBoundaries
,
1051 uint32_t* aNewOffset
, Text
** aLastTextNode
) {
1053 *aNewOffset
= aOffset
;
1055 if (aLastTextNode
) {
1056 *aLastTextNode
= nullptr;
1059 // Special case like <br contenteditable>
1060 if (!mRootElement
->HasChildren()) {
1061 nsresult rv
= aRawRange
->CollapseTo(RawRangeBoundary(mRootElement
, 0u));
1062 if (NS_WARN_IF(NS_FAILED(rv
))) {
1067 PreContentIterator preOrderIter
;
1068 nsresult rv
= preOrderIter
.Init(mRootElement
);
1069 if (NS_WARN_IF(NS_FAILED(rv
))) {
1073 uint32_t offset
= 0;
1074 uint32_t endOffset
= aOffset
+ aLength
;
1075 bool startSet
= false;
1076 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
1077 nsINode
* node
= preOrderIter
.GetCurrentNode();
1078 if (NS_WARN_IF(!node
)) {
1081 // FYI: mRootElement shouldn't cause any text. So, we can skip it simply.
1082 if (node
== mRootElement
|| !node
->IsContent()) {
1085 nsIContent
* const content
= node
->AsContent();
1086 Text
* const contentAsText
= Text::FromNode(content
);
1088 if (aLastTextNode
&& contentAsText
) {
1089 NS_IF_RELEASE(*aLastTextNode
);
1090 NS_ADDREF(*aLastTextNode
= contentAsText
);
1093 uint32_t textLength
= contentAsText
1094 ? GetTextLength(*contentAsText
, aLineBreakType
)
1095 : (ShouldBreakLineBefore(*content
, mRootElement
)
1096 ? GetBRLength(aLineBreakType
)
1102 // When the start offset is in between accumulated offset and the last
1103 // offset of the node, the node is the start node of the range.
1104 if (!startSet
&& aOffset
<= offset
+ textLength
) {
1105 nsINode
* startNode
= nullptr;
1106 Maybe
<uint32_t> startNodeOffset
;
1107 if (contentAsText
) {
1108 // Rule #1.1: [textNode or text[Node or textNode[
1109 uint32_t xpOffset
= aOffset
- offset
;
1110 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
1111 xpOffset
= ConvertToXPOffset(*contentAsText
, xpOffset
);
1114 if (aExpandToClusterBoundaries
) {
1115 const uint32_t oldXPOffset
= xpOffset
;
1117 ExpandToClusterBoundary(*contentAsText
, false, &xpOffset
);
1118 if (NS_WARN_IF(NS_FAILED(rv
))) {
1122 // This is correct since a cluster shouldn't include line break.
1123 *aNewOffset
-= (oldXPOffset
- xpOffset
);
1126 startNode
= contentAsText
;
1127 startNodeOffset
= Some(xpOffset
);
1128 } else if (aOffset
< offset
+ textLength
) {
1129 // Rule #1.2 [<element>
1130 startNode
= content
->GetParent();
1131 if (NS_WARN_IF(!startNode
)) {
1132 return NS_ERROR_FAILURE
;
1134 startNodeOffset
= startNode
->ComputeIndexOf(content
);
1135 if (MOZ_UNLIKELY(NS_WARN_IF(startNodeOffset
.isNothing()))) {
1136 // The content is being removed from the parent!
1137 return NS_ERROR_FAILURE
;
1139 } else if (!content
->HasChildren()) {
1140 // Rule #1.3: <element/>[
1141 startNode
= content
->GetParent();
1142 if (NS_WARN_IF(!startNode
)) {
1143 return NS_ERROR_FAILURE
;
1145 startNodeOffset
= startNode
->ComputeIndexOf(content
);
1146 if (MOZ_UNLIKELY(NS_WARN_IF(startNodeOffset
.isNothing()))) {
1147 // The content is being removed from the parent!
1148 return NS_ERROR_FAILURE
;
1150 MOZ_ASSERT(*startNodeOffset
!= UINT32_MAX
);
1151 ++(*startNodeOffset
);
1153 // Rule #1.4: <element>[
1154 startNode
= content
;
1155 startNodeOffset
= Some(0);
1157 NS_ASSERTION(startNode
, "startNode must not be nullptr");
1158 MOZ_ASSERT(startNodeOffset
.isSome(),
1159 "startNodeOffset must not be Nothing");
1160 rv
= aRawRange
->SetStart(startNode
, *startNodeOffset
);
1161 if (NS_WARN_IF(NS_FAILED(rv
))) {
1167 rv
= aRawRange
->SetEnd(startNode
, *startNodeOffset
);
1168 if (NS_WARN_IF(NS_FAILED(rv
))) {
1175 // When the end offset is in the content, the node is the end node of the
1177 if (endOffset
<= offset
+ textLength
) {
1178 MOZ_ASSERT(startSet
, "The start of the range should've been set already");
1179 if (contentAsText
) {
1180 // Rule #2.1: ]textNode or text]Node or textNode]
1181 uint32_t xpOffset
= endOffset
- offset
;
1182 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
1183 const uint32_t xpOffsetCurrent
=
1184 ConvertToXPOffset(*contentAsText
, xpOffset
);
1185 if (xpOffset
&& GetBRLength(aLineBreakType
) > 1) {
1186 MOZ_ASSERT(GetBRLength(aLineBreakType
) == 2);
1187 const uint32_t xpOffsetPre
=
1188 ConvertToXPOffset(*contentAsText
, xpOffset
- 1);
1189 // If previous character's XP offset is same as current character's,
1190 // it means that the end offset is between \r and \n. So, the
1191 // range end should be after the \n.
1192 if (xpOffsetPre
== xpOffsetCurrent
) {
1193 xpOffset
= xpOffsetCurrent
+ 1;
1195 xpOffset
= xpOffsetCurrent
;
1199 if (aExpandToClusterBoundaries
) {
1201 ExpandToClusterBoundary(*contentAsText
, true, &xpOffset
);
1202 if (NS_WARN_IF(NS_FAILED(rv
))) {
1206 NS_ASSERTION(xpOffset
<= INT32_MAX
, "The end node offset is too large");
1207 nsresult rv
= aRawRange
->SetEnd(contentAsText
, xpOffset
);
1208 if (NS_WARN_IF(NS_FAILED(rv
))) {
1214 if (endOffset
== offset
) {
1215 // Rule #2.2: ]<element>
1216 // NOTE: Please don't crash on release builds because it must be
1217 // overreaction but we shouldn't allow this bug when some
1218 // automated tests find this.
1220 "This case should've already been handled at "
1221 "the last node which caused some text");
1222 return NS_ERROR_FAILURE
;
1225 if (content
->HasChildren() &&
1226 ShouldBreakLineBefore(*content
, mRootElement
)) {
1227 // Rule #2.3: </element>]
1228 rv
= aRawRange
->SetEnd(content
, 0);
1229 if (NS_WARN_IF(NS_FAILED(rv
))) {
1235 // Rule #2.4: <element/>]
1236 nsINode
* endNode
= content
->GetParent();
1237 if (NS_WARN_IF(!endNode
)) {
1238 return NS_ERROR_FAILURE
;
1240 const Maybe
<uint32_t> indexInParent
= endNode
->ComputeIndexOf(content
);
1241 if (MOZ_UNLIKELY(NS_WARN_IF(indexInParent
.isNothing()))) {
1242 // The content is being removed from the parent!
1243 return NS_ERROR_FAILURE
;
1245 MOZ_ASSERT(*indexInParent
!= UINT32_MAX
);
1246 rv
= aRawRange
->SetEnd(endNode
, *indexInParent
+ 1);
1247 if (NS_WARN_IF(NS_FAILED(rv
))) {
1253 offset
+= textLength
;
1258 // Rule #1.5: <root>[</root>
1259 // When there are no nodes causing text, the start of the DOM range
1260 // should be start of the root node since clicking on such editor (e.g.,
1261 // <div contenteditable><span></span></div>) sets caret to the start of
1262 // the editor (i.e., before <span> in the example).
1263 rv
= aRawRange
->SetStart(mRootElement
, 0);
1264 if (NS_WARN_IF(NS_FAILED(rv
))) {
1268 rv
= aRawRange
->SetEnd(mRootElement
, 0);
1269 if (NS_WARN_IF(NS_FAILED(rv
))) {
1275 // Rule #1.5: [</root>
1276 rv
= aRawRange
->SetStart(mRootElement
, mRootElement
->GetChildCount());
1277 if (NS_WARN_IF(NS_FAILED(rv
))) {
1282 *aNewOffset
= offset
;
1285 // Rule #2.5: ]</root>
1286 rv
= aRawRange
->SetEnd(mRootElement
, mRootElement
->GetChildCount());
1287 if (NS_WARN_IF(NS_FAILED(rv
))) {
1294 LineBreakType
ContentEventHandler::GetLineBreakType(
1295 WidgetQueryContentEvent
* aEvent
) {
1296 return GetLineBreakType(aEvent
->mUseNativeLineBreak
);
1300 LineBreakType
ContentEventHandler::GetLineBreakType(
1301 WidgetSelectionEvent
* aEvent
) {
1302 return GetLineBreakType(aEvent
->mUseNativeLineBreak
);
1306 LineBreakType
ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak
) {
1307 return aUseNativeLineBreak
? LINE_BREAK_TYPE_NATIVE
: LINE_BREAK_TYPE_XP
;
1310 nsresult
ContentEventHandler::HandleQueryContentEvent(
1311 WidgetQueryContentEvent
* aEvent
) {
1312 nsresult rv
= NS_ERROR_NOT_IMPLEMENTED
;
1313 switch (aEvent
->mMessage
) {
1314 case eQuerySelectedText
:
1315 rv
= OnQuerySelectedText(aEvent
);
1317 case eQueryTextContent
:
1318 rv
= OnQueryTextContent(aEvent
);
1320 case eQueryCaretRect
:
1321 rv
= OnQueryCaretRect(aEvent
);
1323 case eQueryTextRect
:
1324 rv
= OnQueryTextRect(aEvent
);
1326 case eQueryTextRectArray
:
1327 rv
= OnQueryTextRectArray(aEvent
);
1329 case eQueryEditorRect
:
1330 rv
= OnQueryEditorRect(aEvent
);
1332 case eQueryContentState
:
1333 rv
= OnQueryContentState(aEvent
);
1335 case eQuerySelectionAsTransferable
:
1336 rv
= OnQuerySelectionAsTransferable(aEvent
);
1338 case eQueryCharacterAtPoint
:
1339 rv
= OnQueryCharacterAtPoint(aEvent
);
1341 case eQueryDOMWidgetHittest
:
1342 rv
= OnQueryDOMWidgetHittest(aEvent
);
1347 if (NS_FAILED(rv
)) {
1348 aEvent
->mReply
.reset(); // Mark the query failed.
1352 MOZ_ASSERT(aEvent
->Succeeded());
1356 // Similar to nsFrameSelection::GetFrameForNodeOffset,
1357 // but this is more flexible for OnQueryTextRect to use
1358 static Result
<nsIFrame
*, nsresult
> GetFrameForTextRect(const nsINode
* aNode
,
1359 int32_t aNodeOffset
,
1361 const nsIContent
* content
= nsIContent::FromNodeOrNull(aNode
);
1362 if (NS_WARN_IF(!content
)) {
1363 return Err(NS_ERROR_UNEXPECTED
);
1365 nsIFrame
* frame
= content
->GetPrimaryFrame();
1366 // The node may be invisible, e.g., `display: none`, invisible text node
1367 // around block elements, etc. Therefore, don't warn when we don't find
1372 int32_t childNodeOffset
= 0;
1373 nsIFrame
* returnFrame
= nullptr;
1374 nsresult rv
= frame
->GetChildFrameContainingOffset(
1375 aNodeOffset
, aHint
, &childNodeOffset
, &returnFrame
);
1376 if (NS_FAILED(rv
)) {
1382 nsresult
ContentEventHandler::OnQuerySelectedText(
1383 WidgetQueryContentEvent
* aEvent
) {
1384 nsresult rv
= Init(aEvent
);
1385 if (NS_FAILED(rv
)) {
1389 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
1391 if (!mFirstSelectedRawRange
.IsPositioned()) {
1392 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
1393 MOZ_ASSERT_IF(mSelection
, !mSelection
->RangeCount());
1394 // This is special case that `mReply` is emplaced, but mOffsetAndData is
1395 // not emplaced but treated as succeeded because of no selection ranges
1400 nsINode
* const startNode
= mFirstSelectedRawRange
.GetStartContainer();
1401 nsINode
* const endNode
= mFirstSelectedRawRange
.GetEndContainer();
1403 // Make sure the selection is within the root content range.
1404 if (!startNode
->IsInclusiveDescendantOf(mRootElement
) ||
1405 !endNode
->IsInclusiveDescendantOf(mRootElement
)) {
1406 return NS_ERROR_NOT_AVAILABLE
;
1409 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
1410 uint32_t startOffset
= 0;
1411 if (NS_WARN_IF(NS_FAILED(GetStartOffset(mFirstSelectedRawRange
, &startOffset
,
1413 return NS_ERROR_FAILURE
;
1416 const RangeBoundary
& anchorRef
= mSelection
->RangeCount() > 0
1417 ? mSelection
->AnchorRef()
1418 : mFirstSelectedRawRange
.Start();
1419 const RangeBoundary
& focusRef
= mSelection
->RangeCount() > 0
1420 ? mSelection
->FocusRef()
1421 : mFirstSelectedRawRange
.End();
1422 if (NS_WARN_IF(!anchorRef
.IsSet()) || NS_WARN_IF(!focusRef
.IsSet())) {
1423 return NS_ERROR_FAILURE
;
1426 if (mSelection
->RangeCount()) {
1427 // If there is only one selection range, the anchor/focus node and offset
1428 // are the information of the range. Therefore, we have the direction
1430 if (mSelection
->RangeCount() == 1) {
1431 // The selection's points should always be comparable, independent of the
1432 // selection (see nsISelectionController.idl).
1433 Maybe
<int32_t> compare
=
1434 nsContentUtils::ComparePoints(anchorRef
, focusRef
);
1435 if (compare
.isNothing()) {
1436 return NS_ERROR_FAILURE
;
1439 aEvent
->mReply
->mReversed
= compare
.value() > 0;
1441 // However, if there are 2 or more selection ranges, we have no information
1444 aEvent
->mReply
->mReversed
= false;
1447 nsString selectedString
;
1448 if (!mFirstSelectedRawRange
.Collapsed() &&
1449 NS_WARN_IF(NS_FAILED(GenerateFlatTextContent(
1450 mFirstSelectedRawRange
, selectedString
, lineBreakType
)))) {
1451 return NS_ERROR_FAILURE
;
1453 aEvent
->mReply
->mOffsetAndData
.emplace(startOffset
, selectedString
,
1454 OffsetAndDataFor::SelectedString
);
1456 NS_ASSERTION(anchorRef
== focusRef
,
1457 "When mSelection doesn't have selection, "
1458 "mFirstSelectedRawRange must be collapsed");
1460 aEvent
->mReply
->mReversed
= false;
1461 aEvent
->mReply
->mOffsetAndData
.emplace(startOffset
, EmptyString(),
1462 OffsetAndDataFor::SelectedString
);
1465 Result
<nsIFrame
*, nsresult
> frameForTextRectOrError
= GetFrameForTextRect(
1466 focusRef
.Container(),
1467 focusRef
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
).valueOr(0),
1469 if (NS_WARN_IF(frameForTextRectOrError
.isErr()) ||
1470 !frameForTextRectOrError
.inspect()) {
1471 aEvent
->mReply
->mWritingMode
= WritingMode();
1473 aEvent
->mReply
->mWritingMode
=
1474 frameForTextRectOrError
.inspect()->GetWritingMode();
1477 MOZ_ASSERT(aEvent
->Succeeded());
1481 nsresult
ContentEventHandler::OnQueryTextContent(
1482 WidgetQueryContentEvent
* aEvent
) {
1483 nsresult rv
= Init(aEvent
);
1484 if (NS_FAILED(rv
)) {
1488 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
1490 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
1493 uint32_t startOffset
= 0;
1494 if (NS_WARN_IF(NS_FAILED(SetRawRangeFromFlatTextOffset(
1495 &rawRange
, aEvent
->mInput
.mOffset
, aEvent
->mInput
.mLength
,
1496 lineBreakType
, false, &startOffset
)))) {
1497 return NS_ERROR_FAILURE
;
1500 nsString textInRange
;
1501 if (NS_WARN_IF(NS_FAILED(
1502 GenerateFlatTextContent(rawRange
, textInRange
, lineBreakType
)))) {
1503 return NS_ERROR_FAILURE
;
1506 aEvent
->mReply
->mOffsetAndData
.emplace(startOffset
, textInRange
,
1507 OffsetAndDataFor::EditorString
);
1509 if (aEvent
->mWithFontRanges
) {
1510 uint32_t fontRangeLength
;
1511 if (NS_WARN_IF(NS_FAILED(
1512 GenerateFlatFontRanges(rawRange
, aEvent
->mReply
->mFontRanges
,
1513 fontRangeLength
, lineBreakType
)))) {
1514 return NS_ERROR_FAILURE
;
1517 MOZ_ASSERT(fontRangeLength
== aEvent
->mReply
->DataLength(),
1518 "Font ranges doesn't match the string");
1521 MOZ_ASSERT(aEvent
->Succeeded());
1525 void ContentEventHandler::EnsureNonEmptyRect(nsRect
& aRect
) const {
1526 // See the comment in ContentEventHandler.h why this doesn't set them to
1527 // one device pixel.
1528 aRect
.height
= std::max(1, aRect
.height
);
1529 aRect
.width
= std::max(1, aRect
.width
);
1532 void ContentEventHandler::EnsureNonEmptyRect(LayoutDeviceIntRect
& aRect
) const {
1533 aRect
.height
= std::max(1, aRect
.height
);
1534 aRect
.width
= std::max(1, aRect
.width
);
1537 ContentEventHandler::FrameAndNodeOffset
1538 ContentEventHandler::GetFirstFrameInRangeForTextRect(
1539 const RawRange
& aRawRange
) {
1540 NodePosition nodePosition
;
1541 PreContentIterator preOrderIter
;
1543 preOrderIter
.Init(aRawRange
.Start().AsRaw(), aRawRange
.End().AsRaw());
1544 if (NS_WARN_IF(NS_FAILED(rv
))) {
1545 return FrameAndNodeOffset();
1547 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
1548 nsINode
* node
= preOrderIter
.GetCurrentNode();
1549 if (NS_WARN_IF(!node
)) {
1553 auto* content
= nsIContent::FromNode(node
);
1554 if (MOZ_UNLIKELY(!content
)) {
1558 // If the node is invisible (e.g., the node is or is in an invisible node or
1559 // it's a white-space only text node around a block boundary), we should
1561 if (!content
->GetPrimaryFrame()) {
1565 if (auto* textNode
= Text::FromNode(content
)) {
1566 // If the range starts at the end of a text node, we need to find
1567 // next node which causes text.
1568 const uint32_t offsetInNode
= textNode
== aRawRange
.GetStartContainer()
1569 ? aRawRange
.StartOffset()
1571 if (offsetInNode
< textNode
->TextDataLength()) {
1572 nodePosition
= {textNode
, offsetInNode
};
1578 // If the element node causes a line break before it, it's the first
1579 // node causing text.
1580 if (ShouldBreakLineBefore(*content
, mRootElement
) ||
1581 IsPaddingBR(*content
)) {
1582 nodePosition
= {content
, 0u};
1586 if (!nodePosition
.IsSetAndValid()) {
1587 return FrameAndNodeOffset();
1590 Result
<nsIFrame
*, nsresult
> firstFrameOrError
= GetFrameForTextRect(
1591 nodePosition
.Container(),
1592 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
), true);
1593 if (NS_WARN_IF(firstFrameOrError
.isErr()) || !firstFrameOrError
.inspect()) {
1594 return FrameAndNodeOffset();
1596 return FrameAndNodeOffset(
1597 firstFrameOrError
.inspect(),
1598 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
));
1601 ContentEventHandler::FrameAndNodeOffset
1602 ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange
& aRawRange
) {
1603 NodePosition nodePosition
;
1604 PreContentIterator preOrderIter
;
1606 preOrderIter
.Init(aRawRange
.Start().AsRaw(), aRawRange
.End().AsRaw());
1607 if (NS_WARN_IF(NS_FAILED(rv
))) {
1608 return FrameAndNodeOffset();
1611 const RangeBoundary
& endPoint
= aRawRange
.End();
1612 MOZ_ASSERT(endPoint
.IsSetAndValid());
1613 // If the end point is start of a text node or specified by its parent and
1614 // index, the node shouldn't be included into the range. For example,
1615 // with this case, |<p>abc[<br>]def</p>|, the range ends at 3rd children of
1616 // <p> (see the range creation rules, "2.4. Cases: <element/>]"). This causes
1617 // following frames:
1624 // So, if this method includes the 2nd text frame's rect to its result, the
1625 // caller will return too tall rect which includes 2 lines in this case isn't
1626 // expected by native IME (e.g., popup of IME will be positioned at bottom
1627 // of "d" instead of right-bottom of "c"). Therefore, this method shouldn't
1628 // include the last frame when its content isn't really in aRawRange.
1629 nsINode
* nextNodeOfRangeEnd
= nullptr;
1630 if (endPoint
.Container()->IsText()) {
1631 // Don't set nextNodeOfRangeEnd to the start node of aRawRange because if
1632 // the container of the end is same as start node of the range, the text
1633 // node shouldn't be next of range end even if the offset is 0. This
1634 // could occur with empty text node.
1635 if (endPoint
.IsStartOfContainer() &&
1636 aRawRange
.GetStartContainer() != endPoint
.Container()) {
1637 nextNodeOfRangeEnd
= endPoint
.Container();
1639 } else if (endPoint
.IsSetAndValid()) {
1640 nextNodeOfRangeEnd
= endPoint
.GetChildAtOffset();
1643 for (preOrderIter
.Last(); !preOrderIter
.IsDone(); preOrderIter
.Prev()) {
1644 nsINode
* node
= preOrderIter
.GetCurrentNode();
1645 if (NS_WARN_IF(!node
)) {
1649 if (node
== nextNodeOfRangeEnd
) {
1653 auto* content
= nsIContent::FromNode(node
);
1654 if (MOZ_UNLIKELY(!content
)) {
1658 // If the node is invisible (e.g., the node is or is in an invisible node or
1659 // it's a white-space only text node around a block boundary), we should
1661 if (!content
->GetPrimaryFrame()) {
1665 if (auto* textNode
= Text::FromNode(node
)) {
1666 nodePosition
= {textNode
, textNode
== aRawRange
.GetEndContainer()
1667 ? aRawRange
.EndOffset()
1668 : textNode
->TextDataLength()};
1670 // If the text node is empty or the last node of the range but the index
1671 // is 0, we should store current position but continue looking for
1672 // previous node (If there are no nodes before it, we should use current
1673 // node position for returning its frame).
1674 if (*nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
) ==
1681 if (ShouldBreakLineBefore(*content
, mRootElement
) ||
1682 IsPaddingBR(*content
)) {
1683 nodePosition
= {content
, 0u};
1688 if (!nodePosition
.IsSet()) {
1689 return FrameAndNodeOffset();
1692 Result
<nsIFrame
*, nsresult
> lastFrameOrError
= GetFrameForTextRect(
1693 nodePosition
.Container(),
1694 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
), true);
1695 if (NS_WARN_IF(lastFrameOrError
.isErr()) || !lastFrameOrError
.inspect()) {
1696 return FrameAndNodeOffset();
1699 // If the last frame is a text frame, we need to check if the range actually
1700 // includes at least one character in the range. Therefore, if it's not a
1701 // text frame, we need to do nothing anymore.
1702 if (!lastFrameOrError
.inspect()->IsTextFrame()) {
1703 return FrameAndNodeOffset(
1704 lastFrameOrError
.inspect(),
1705 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
));
1708 int32_t start
= lastFrameOrError
.inspect()->GetOffsets().first
;
1710 // If the start offset in the node is same as the computed offset in the
1711 // node and it's not 0, the frame shouldn't be added to the text rect. So,
1712 // this should return previous text frame and its last offset if there is
1713 // at least one text frame.
1714 if (*nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
) &&
1715 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
) ==
1716 static_cast<uint32_t>(start
)) {
1717 const uint32_t newNodePositionOffset
=
1718 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
);
1719 MOZ_ASSERT(newNodePositionOffset
!= 0);
1720 nodePosition
= {nodePosition
.Container(), newNodePositionOffset
- 1u};
1721 lastFrameOrError
= GetFrameForTextRect(
1722 nodePosition
.Container(),
1723 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
), true);
1724 if (NS_WARN_IF(lastFrameOrError
.isErr()) || !lastFrameOrError
.inspect()) {
1725 return FrameAndNodeOffset();
1729 return FrameAndNodeOffset(
1730 lastFrameOrError
.inspect(),
1731 *nodePosition
.Offset(NodePosition::OffsetFilter::kValidOffsets
));
1734 ContentEventHandler::FrameRelativeRect
1735 ContentEventHandler::GetLineBreakerRectBefore(nsIFrame
* aFrame
) {
1736 // Note that this method should be called only with an element's frame whose
1737 // open tag causes a line break or moz-<br> for computing empty last line's
1739 MOZ_ASSERT(aFrame
->GetContent());
1740 MOZ_ASSERT(ShouldBreakLineBefore(*aFrame
->GetContent(), mRootElement
) ||
1741 IsPaddingBR(*aFrame
->GetContent()));
1743 nsIFrame
* frameForFontMetrics
= aFrame
;
1745 // If it's not a <br> frame, this method computes the line breaker's rect
1746 // outside the frame. Therefore, we need to compute with parent frame's
1747 // font metrics in such case.
1748 if (!aFrame
->IsBrFrame() && aFrame
->GetParent()) {
1749 frameForFontMetrics
= aFrame
->GetParent();
1752 // Note that <br> element's rect is decided with line-height but we need
1753 // a rect only with font height. Additionally, <br> frame's width and
1754 // height are 0 in quirks mode if it's not an empty line. So, we cannot
1755 // use frame rect information even if it's a <br> frame.
1757 RefPtr
<nsFontMetrics
> fontMetrics
=
1758 nsLayoutUtils::GetInflatedFontMetricsForFrame(frameForFontMetrics
);
1759 if (NS_WARN_IF(!fontMetrics
)) {
1760 return FrameRelativeRect();
1763 const WritingMode kWritingMode
= frameForFontMetrics
->GetWritingMode();
1765 auto caretBlockAxisMetrics
=
1766 aFrame
->GetCaretBlockAxisMetrics(kWritingMode
, *fontMetrics
);
1767 nscoord inlineOffset
= 0;
1769 // If aFrame isn't a <br> frame, caret should be at outside of it because
1770 // the line break is before its open tag. For example, case of
1771 // |<div><p>some text</p></div>|, caret is before <p> element and in <div>
1772 // element, the caret should be left of top-left corner of <p> element like:
1774 // +-<div>------------------- <div>'s border box
1775 // | I +-<p>----------------- <p>'s border box
1781 // However, this is a hack for unusual scenario. This hack shouldn't be
1782 // used as far as possible.
1783 if (!aFrame
->IsBrFrame()) {
1784 if (kWritingMode
.IsVertical() && !kWritingMode
.IsLineInverted()) {
1785 // above of top-right corner of aFrame.
1786 caretBlockAxisMetrics
.mOffset
=
1787 aFrame
->GetRect().XMost() - caretBlockAxisMetrics
.mExtent
;
1789 // above (For vertical) or left (For horizontal) of top-left corner of
1791 caretBlockAxisMetrics
.mOffset
= 0;
1793 inlineOffset
= -aFrame
->PresContext()->AppUnitsPerDevPixel();
1795 FrameRelativeRect
result(aFrame
);
1796 if (kWritingMode
.IsVertical()) {
1797 result
.mRect
.x
= caretBlockAxisMetrics
.mOffset
;
1798 result
.mRect
.y
= inlineOffset
;
1799 result
.mRect
.width
= caretBlockAxisMetrics
.mExtent
;
1801 result
.mRect
.x
= inlineOffset
;
1802 result
.mRect
.y
= caretBlockAxisMetrics
.mOffset
;
1803 result
.mRect
.height
= caretBlockAxisMetrics
.mExtent
;
1808 ContentEventHandler::FrameRelativeRect
1809 ContentEventHandler::GuessLineBreakerRectAfter(const Text
& aTextNode
) {
1810 FrameRelativeRect result
;
1811 const int32_t length
= static_cast<int32_t>(aTextNode
.TextLength());
1812 if (NS_WARN_IF(length
< 0)) {
1815 // Get the last nsTextFrame which is caused by aTextNode. Note that
1816 // a text node can cause multiple text frames, e.g., the text is too long
1817 // and wrapped by its parent block or the text has line breakers and its
1818 // white-space property respects the line breakers (e.g., |pre|).
1819 Result
<nsIFrame
*, nsresult
> lastTextFrameOrError
=
1820 GetFrameForTextRect(&aTextNode
, length
, true);
1821 if (NS_WARN_IF(lastTextFrameOrError
.isErr()) ||
1822 !lastTextFrameOrError
.inspect()) {
1825 const nsRect kLastTextFrameRect
= lastTextFrameOrError
.inspect()->GetRect();
1826 if (lastTextFrameOrError
.inspect()->GetWritingMode().IsVertical()) {
1827 // Below of the last text frame.
1828 result
.mRect
.SetRect(0, kLastTextFrameRect
.height
, kLastTextFrameRect
.width
,
1831 // Right of the last text frame (not bidi-aware).
1832 result
.mRect
.SetRect(kLastTextFrameRect
.width
, 0, 0,
1833 kLastTextFrameRect
.height
);
1835 result
.mBaseFrame
= lastTextFrameOrError
.unwrap();
1839 ContentEventHandler::FrameRelativeRect
1840 ContentEventHandler::GuessFirstCaretRectIn(nsIFrame
* aFrame
) {
1841 const WritingMode kWritingMode
= aFrame
->GetWritingMode();
1842 nsPresContext
* presContext
= aFrame
->PresContext();
1844 // Computes the font height, but if it's not available, we should use
1845 // default font size of Firefox. The default font size in default settings
1847 RefPtr
<nsFontMetrics
> fontMetrics
=
1848 nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame
);
1849 const nscoord kMaxHeight
= fontMetrics
1850 ? fontMetrics
->MaxHeight()
1851 : 16 * presContext
->AppUnitsPerDevPixel();
1854 const nsRect kContentRect
= aFrame
->GetContentRect() - aFrame
->GetPosition();
1855 caretRect
.y
= kContentRect
.y
;
1856 if (!kWritingMode
.IsVertical()) {
1857 if (kWritingMode
.IsBidiLTR()) {
1858 caretRect
.x
= kContentRect
.x
;
1860 // Move 1px left for the space of caret itself.
1861 const nscoord kOnePixel
= presContext
->AppUnitsPerDevPixel();
1862 caretRect
.x
= kContentRect
.XMost() - kOnePixel
;
1864 caretRect
.height
= kMaxHeight
;
1865 // However, don't add kOnePixel here because it may cause 2px width at
1866 // aligning the edge to device pixels.
1867 caretRect
.width
= 1;
1869 if (kWritingMode
.IsVerticalLR()) {
1870 caretRect
.x
= kContentRect
.x
;
1872 caretRect
.x
= kContentRect
.XMost() - kMaxHeight
;
1874 caretRect
.width
= kMaxHeight
;
1875 // Don't add app units for a device pixel because it may cause 2px height
1876 // at aligning the edge to device pixels.
1877 caretRect
.height
= 1;
1879 return FrameRelativeRect(caretRect
, aFrame
);
1883 LayoutDeviceIntRect
ContentEventHandler::GetCaretRectBefore(
1884 const LayoutDeviceIntRect
& aCharRect
, const WritingMode
& aWritingMode
) {
1885 LayoutDeviceIntRect
caretRectBefore(aCharRect
);
1886 if (aWritingMode
.IsVertical()) {
1887 caretRectBefore
.height
= 1;
1889 // TODO: Make here bidi-aware.
1890 caretRectBefore
.width
= 1;
1892 return caretRectBefore
;
1896 nsRect
ContentEventHandler::GetCaretRectBefore(
1897 const nsRect
& aCharRect
, const WritingMode
& aWritingMode
) {
1898 nsRect
caretRectBefore(aCharRect
);
1899 if (aWritingMode
.IsVertical()) {
1900 // For making the height 1 device pixel after aligning the rect edges to
1901 // device pixels, don't add one device pixel in app units here.
1902 caretRectBefore
.height
= 1;
1904 // TODO: Make here bidi-aware.
1905 // For making the width 1 device pixel after aligning the rect edges to
1906 // device pixels, don't add one device pixel in app units here.
1907 caretRectBefore
.width
= 1;
1909 return caretRectBefore
;
1913 LayoutDeviceIntRect
ContentEventHandler::GetCaretRectAfter(
1914 const LayoutDeviceIntRect
& aCharRect
, const WritingMode
& aWritingMode
) {
1915 LayoutDeviceIntRect
caretRectAfter(aCharRect
);
1916 if (aWritingMode
.IsVertical()) {
1917 caretRectAfter
.y
= aCharRect
.YMost() + 1;
1918 caretRectAfter
.height
= 1;
1920 // TODO: Make here bidi-aware.
1921 caretRectAfter
.x
= aCharRect
.XMost() + 1;
1922 caretRectAfter
.width
= 1;
1924 return caretRectAfter
;
1928 nsRect
ContentEventHandler::GetCaretRectAfter(nsPresContext
& aPresContext
,
1929 const nsRect
& aCharRect
,
1930 const WritingMode
& aWritingMode
) {
1931 nsRect
caretRectAfter(aCharRect
);
1932 const nscoord onePixel
= aPresContext
.AppUnitsPerDevPixel();
1933 if (aWritingMode
.IsVertical()) {
1934 caretRectAfter
.y
= aCharRect
.YMost() + onePixel
;
1935 // For making the height 1 device pixel after aligning the rect edges to
1936 // device pixels, don't add one device pixel in app units here.
1937 caretRectAfter
.height
= 1;
1939 // TODO: Make here bidi-aware.
1940 caretRectAfter
.x
= aCharRect
.XMost() + onePixel
;
1941 // For making the width 1 device pixel after aligning the rect edges to
1942 // device pixels, don't add one device pixel in app units here.
1943 caretRectAfter
.width
= 1;
1945 return caretRectAfter
;
1948 nsresult
ContentEventHandler::OnQueryTextRectArray(
1949 WidgetQueryContentEvent
* aEvent
) {
1950 nsresult rv
= Init(aEvent
);
1951 if (NS_WARN_IF(NS_FAILED(rv
))) {
1955 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
1957 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
1958 const uint32_t kBRLength
= GetBRLength(lineBreakType
);
1960 WritingMode lastVisibleFrameWritingMode
;
1961 LayoutDeviceIntRect rect
;
1962 uint32_t offset
= aEvent
->mInput
.mOffset
;
1963 const uint32_t kEndOffset
= aEvent
->mInput
.EndOffset();
1964 bool wasLineBreaker
= false;
1965 // lastCharRect stores the last charRect value (see below for the detail of
1967 nsRect lastCharRect
;
1968 // lastFrame is base frame of lastCharRect.
1969 // TODO: We should look for this if the first text is not visible. However,
1970 // users cannot put caret invisible text and users cannot type in it
1971 // at least only with user's operations. Therefore, we don't need to
1972 // fix this immediately.
1973 nsIFrame
* lastFrame
= nullptr;
1974 nsAutoString flattenedAllText
;
1975 flattenedAllText
.SetIsVoid(true);
1976 while (offset
< kEndOffset
) {
1977 RefPtr
<Text
> lastTextNode
;
1980 SetRawRangeFromFlatTextOffset(&rawRange
, offset
, 1, lineBreakType
, true,
1981 nullptr, getter_AddRefs(lastTextNode
));
1982 if (NS_WARN_IF(NS_FAILED(rv
))) {
1986 // TODO: When we crossed parent block boundary now, we should fill pending
1987 // character rects with caret rect after the last visible character
1990 // If the range is collapsed, offset has already reached the end of the
1992 if (rawRange
.Collapsed()) {
1996 // Get the first frame which causes some text after the offset.
1997 FrameAndNodeOffset firstFrame
= GetFirstFrameInRangeForTextRect(rawRange
);
1999 // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
2000 // means that the offset reached the end of contents or there is no visible
2001 // frame in the range generating flattened text.
2002 if (!firstFrame
.IsValid()) {
2003 if (flattenedAllText
.IsVoid()) {
2004 flattenedAllText
.SetIsVoid(false);
2005 if (NS_WARN_IF(NS_FAILED(GenerateFlatTextContent(
2006 mRootElement
, flattenedAllText
, lineBreakType
)))) {
2007 NS_WARNING("ContentEventHandler::GenerateFlatTextContent() failed");
2008 return NS_ERROR_FAILURE
;
2011 // If we've reached end of the root, append caret rect at the end of
2013 if (offset
>= flattenedAllText
.Length()) {
2016 // Otherwise, we're in an invisible node. If the node is followed by a
2017 // block boundary causing a line break, we can use the boundary.
2018 // Otherwise, if the node follows a block boundary of a parent block, we
2019 // can use caret rect at previous visible frame causing flattened text.
2020 const uint32_t remainingLengthInCurrentRange
= [&]() {
2022 if (rawRange
.GetStartContainer() == lastTextNode
) {
2023 if (rawRange
.StartOffset() < lastTextNode
->TextDataLength()) {
2024 return lastTextNode
->TextDataLength() - rawRange
.StartOffset();
2028 // Must be there are not nodes which may cause generating text.
2029 // Therefore, we can skip all nodes before the last found text node
2030 // and all text in the last text node.
2031 return lastTextNode
->TextDataLength();
2033 if (rawRange
.GetStartContainer() &&
2034 rawRange
.GetStartContainer()->IsContent() &&
2035 ShouldBreakLineBefore(*rawRange
.GetStartContainer()->AsContent(),
2037 if (kBRLength
!= 1u && offset
- aEvent
->mInput
.mOffset
< kBRLength
) {
2038 // Don't return kBRLength if start position is less than the length
2039 // of a line-break because the offset may be between CRLF on
2040 // Windows. In the case, we will be again here and gets same
2041 // result and we need to pay the penalty only once. Therefore, we
2042 // can keep going without complicated check.
2049 offset
+= std::max(1u, remainingLengthInCurrentRange
);
2053 nsIContent
* firstContent
= firstFrame
.mFrame
->GetContent();
2054 if (NS_WARN_IF(!firstContent
)) {
2055 return NS_ERROR_FAILURE
;
2058 bool startsBetweenLineBreaker
= false;
2060 lastVisibleFrameWritingMode
= firstFrame
->GetWritingMode();
2062 nsIFrame
* baseFrame
= firstFrame
;
2063 // charRect should have each character rect or line breaker rect relative
2064 // to the base frame.
2065 AutoTArray
<nsRect
, 16> charRects
;
2067 // If the first frame is a text frame, the result should be computed with
2069 if (firstFrame
->IsTextFrame()) {
2070 rv
= firstFrame
->GetCharacterRectsInRange(firstFrame
.mOffsetInNode
,
2071 kEndOffset
- offset
, charRects
);
2072 if (NS_WARN_IF(NS_FAILED(rv
)) || NS_WARN_IF(charRects
.IsEmpty())) {
2075 // Assign the characters whose rects are computed by the call of
2076 // nsTextFrame::GetCharacterRectsInRange().
2077 AppendSubString(chars
, *firstContent
->AsText(), firstFrame
.mOffsetInNode
,
2078 charRects
.Length());
2079 if (NS_WARN_IF(chars
.Length() != charRects
.Length())) {
2080 return NS_ERROR_UNEXPECTED
;
2082 if (kBRLength
> 1 && chars
[0] == '\n' &&
2083 offset
== aEvent
->mInput
.mOffset
&& offset
) {
2084 // If start of range starting from previous offset of query range is
2085 // same as the start of query range, the query range starts from
2086 // between a line breaker (i.e., the range starts between "\r" and
2088 RawRange rawRangeToPrevOffset
;
2089 nsresult rv
= SetRawRangeFromFlatTextOffset(&rawRangeToPrevOffset
,
2090 aEvent
->mInput
.mOffset
- 1,
2091 1, lineBreakType
, true);
2092 if (NS_WARN_IF(NS_FAILED(rv
))) {
2095 startsBetweenLineBreaker
=
2096 rawRange
.GetStartContainer() ==
2097 rawRangeToPrevOffset
.GetStartContainer() &&
2098 rawRange
.StartOffset() == rawRangeToPrevOffset
.StartOffset();
2101 // Other contents should cause a line breaker rect before it.
2102 // Note that moz-<br> element does not cause any text, however,
2103 // it represents empty line at the last of current block. Therefore,
2104 // we need to compute its rect too.
2105 else if (ShouldBreakLineBefore(*firstContent
, mRootElement
) ||
2106 IsPaddingBR(*firstContent
)) {
2108 // If the frame is not a <br> frame, we need to compute the caret rect
2109 // with last character's rect before firstContent if there is.
2110 // For example, if caret is after "c" of |<p>abc</p><p>def</p>|, IME may
2111 // query a line breaker's rect after "c". Then, if we compute it only
2112 // with the 2nd <p>'s block frame, the result will be:
2113 // +-<p>--------------------------------+
2115 // +------------------------------------+
2117 // I+-<p>--------------------------------+
2119 // +------------------------------------+
2120 // However, users expect popup windows of IME should be positioned at
2121 // right-bottom of "c" like this:
2122 // +-<p>--------------------------------+
2124 // +------------------------------------+
2126 // +-<p>--------------------------------+
2128 // +------------------------------------+
2129 // Therefore, if the first frame isn't a <br> frame and there is a text
2130 // node before the first node in the queried range, we should compute the
2131 // first rect with the previous character's rect.
2132 // If we already compute a character's rect in the queried range, we can
2133 // compute it with the cached last character's rect. (However, don't
2134 // use this path if it's a <br> frame because trusting <br> frame's rect
2135 // is better than guessing the rect from the previous character.)
2136 if (!firstFrame
->IsBrFrame() && !aEvent
->mReply
->mRectArray
.IsEmpty()) {
2137 baseFrame
= lastFrame
;
2138 brRect
= lastCharRect
;
2139 if (!wasLineBreaker
) {
2140 brRect
= GetCaretRectAfter(*baseFrame
->PresContext(), brRect
,
2141 lastVisibleFrameWritingMode
);
2144 // If it's not a <br> frame and it's the first character rect at the
2145 // queried range, we need the previous character rect of the start of
2146 // the queried range if there is a visible text node.
2147 else if (!firstFrame
->IsBrFrame() && lastTextNode
&&
2148 lastTextNode
->GetPrimaryFrame()) {
2149 FrameRelativeRect brRectRelativeToLastTextFrame
=
2150 GuessLineBreakerRectAfter(*lastTextNode
);
2151 if (NS_WARN_IF(!brRectRelativeToLastTextFrame
.IsValid())) {
2152 return NS_ERROR_FAILURE
;
2154 // Look for the last text frame for lastTextNode.
2155 nsIFrame
* primaryFrame
= lastTextNode
->GetPrimaryFrame();
2156 if (NS_WARN_IF(!primaryFrame
)) {
2157 return NS_ERROR_FAILURE
;
2159 baseFrame
= primaryFrame
->LastContinuation();
2160 if (NS_WARN_IF(!baseFrame
)) {
2161 return NS_ERROR_FAILURE
;
2163 brRect
= brRectRelativeToLastTextFrame
.RectRelativeTo(baseFrame
);
2165 // Otherwise, we need to compute the line breaker's rect only with the
2166 // first frame's rect. But this may be unexpected. For example,
2167 // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is
2168 // before "a", therefore, users expect the rect left of "a". However,
2169 // we don't have enough information about the next character here and
2170 // this isn't usual case (e.g., IME typically tries to query the rect
2171 // of "a" or caret rect for computing its popup position). Therefore,
2172 // we shouldn't do more complicated hack here unless we'll get some bug
2173 // reports actually.
2175 FrameRelativeRect relativeBRRect
= GetLineBreakerRectBefore(firstFrame
);
2176 brRect
= relativeBRRect
.RectRelativeTo(firstFrame
);
2178 charRects
.AppendElement(brRect
);
2179 chars
.AssignLiteral("\n");
2180 if (kBRLength
> 1 && offset
== aEvent
->mInput
.mOffset
&& offset
) {
2181 // If the first frame for the previous offset of the query range and
2182 // the first frame for the start of query range are same, that means
2183 // the start offset is between the first line breaker (i.e., the range
2184 // starts between "\r" and "\n").
2185 nsresult rv
= SetRawRangeFromFlatTextOffset(
2186 &rawRange
, aEvent
->mInput
.mOffset
- 1, 1, lineBreakType
, true);
2187 if (NS_WARN_IF(NS_FAILED(rv
))) {
2188 return NS_ERROR_UNEXPECTED
;
2190 FrameAndNodeOffset frameForPrevious
=
2191 GetFirstFrameInRangeForTextRect(rawRange
);
2192 startsBetweenLineBreaker
= frameForPrevious
.mFrame
== firstFrame
.mFrame
;
2196 "The frame is neither a text frame nor a frame whose content "
2197 "causes a line break");
2198 return NS_ERROR_FAILURE
;
2201 for (size_t i
= 0; i
< charRects
.Length() && offset
< kEndOffset
; i
++) {
2202 nsRect charRect
= charRects
[i
];
2203 // Store lastCharRect before applying CSS transform because it may be
2204 // used for computing a line breaker rect. Then, the computed line
2205 // breaker rect will be applied CSS transform again. Therefore,
2206 // the value of lastCharRect should be raw rect value relative to the
2208 lastCharRect
= charRect
;
2209 lastFrame
= baseFrame
;
2210 rv
= ConvertToRootRelativeOffset(baseFrame
, charRect
);
2211 if (NS_WARN_IF(NS_FAILED(rv
))) {
2215 nsPresContext
* presContext
= baseFrame
->PresContext();
2216 rect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2217 charRect
, presContext
->AppUnitsPerDevPixel());
2218 if (nsPresContext
* rootContext
=
2219 presContext
->GetInProcessRootContentDocumentPresContext()) {
2220 rect
= RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2221 rect
, rootContext
->PresShell()));
2223 // Returning empty rect may cause native IME confused, let's make sure to
2224 // return non-empty rect.
2225 EnsureNonEmptyRect(rect
);
2227 // If we found some invisible characters followed by current visible
2228 // character, make their rects same as caret rect before the first visible
2229 // character because IME may want to put their UI next to the rect of the
2230 // invisible character for next input.
2231 // Note that chars do not contain the invisible characters.
2232 if (i
== 0u && MOZ_LIKELY(offset
> aEvent
->mInput
.mOffset
)) {
2233 const uint32_t offsetInRange
=
2234 offset
- CheckedInt
<uint32_t>(aEvent
->mInput
.mOffset
).value();
2235 if (offsetInRange
> aEvent
->mReply
->mRectArray
.Length()) {
2236 LayoutDeviceIntRect caretRectBefore
=
2237 GetCaretRectBefore(rect
, lastVisibleFrameWritingMode
);
2238 for ([[maybe_unused
]] uint32_t index
: IntegerRange
<uint32_t>(
2239 offsetInRange
- aEvent
->mReply
->mRectArray
.Length())) {
2240 aEvent
->mReply
->mRectArray
.AppendElement(caretRectBefore
);
2242 MOZ_ASSERT(aEvent
->mReply
->mRectArray
.Length() == offsetInRange
);
2246 aEvent
->mReply
->mRectArray
.AppendElement(rect
);
2249 // If it's not a line breaker or the line breaker length is same as
2250 // XP line breaker's, we need to do nothing for current character.
2251 wasLineBreaker
= chars
[i
] == '\n';
2252 if (!wasLineBreaker
|| kBRLength
== 1) {
2256 MOZ_ASSERT(kBRLength
== 2);
2258 // If it's already reached the end of query range, we don't need to do
2260 if (offset
== kEndOffset
) {
2264 // If the query range starts from between a line breaker, i.e., it starts
2265 // between "\r" and "\n", the appended rect was for the "\n". Therefore,
2266 // we don't need to append same rect anymore for current "\r\n".
2267 if (startsBetweenLineBreaker
) {
2271 // The appended rect was for "\r" of "\r\n". Therefore, we need to
2272 // append same rect for "\n" too because querying rect of "\r" and "\n"
2273 // should return same rect. E.g., IME may query previous character's
2274 // rect of first character of a line.
2275 aEvent
->mReply
->mRectArray
.AppendElement(rect
);
2280 // If we've not handled some invisible character rects, fill them as caret
2281 // rect after the last visible character.
2282 if (!aEvent
->mReply
->mRectArray
.IsEmpty()) {
2283 const uint32_t offsetInRange
=
2284 offset
- CheckedInt
<uint32_t>(aEvent
->mInput
.mOffset
).value();
2285 if (offsetInRange
> aEvent
->mReply
->mRectArray
.Length()) {
2286 LayoutDeviceIntRect caretRectAfter
=
2287 GetCaretRectAfter(aEvent
->mReply
->mRectArray
.LastElement(),
2288 lastVisibleFrameWritingMode
);
2289 for ([[maybe_unused
]] uint32_t index
: IntegerRange
<uint32_t>(
2290 offsetInRange
- aEvent
->mReply
->mRectArray
.Length())) {
2291 aEvent
->mReply
->mRectArray
.AppendElement(caretRectAfter
);
2293 MOZ_ASSERT(aEvent
->mReply
->mRectArray
.Length() == offsetInRange
);
2297 // If the query range is longer than actual content length, we should append
2298 // caret rect at the end of the content as the last character rect because
2299 // native IME may want to query character rect at the end of contents for
2300 // deciding the position of a popup window (e.g., suggest window for next
2301 // word). Note that when this method hasn't appended character rects, it
2302 // means that the offset is too large or the query range is collapsed.
2303 if (offset
< kEndOffset
|| aEvent
->mReply
->mRectArray
.IsEmpty()) {
2304 // If we've already retrieved some character rects before current offset,
2305 // we can guess the last rect from the last character's rect unless it's a
2306 // line breaker. (If it's a line breaker, the caret rect is in next line.)
2307 if (!aEvent
->mReply
->mRectArray
.IsEmpty() && !wasLineBreaker
) {
2308 rect
= GetCaretRectAfter(aEvent
->mReply
->mRectArray
.LastElement(),
2309 lastVisibleFrameWritingMode
);
2310 aEvent
->mReply
->mRectArray
.AppendElement(rect
);
2312 // Note that don't use eQueryCaretRect here because if caret is at the
2313 // end of the content, it returns actual caret rect instead of computing
2314 // the rect itself. It means that the result depends on caret position.
2315 // So, we shouldn't use it for consistency result in automated tests.
2316 WidgetQueryContentEvent
queryTextRectEvent(eQueryTextRect
, *aEvent
);
2317 WidgetQueryContentEvent::Options
options(*aEvent
);
2318 queryTextRectEvent
.InitForQueryTextRect(offset
, 1, options
);
2319 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent
))) ||
2320 NS_WARN_IF(queryTextRectEvent
.Failed())) {
2321 return NS_ERROR_FAILURE
;
2323 if (queryTextRectEvent
.mReply
->mWritingMode
.IsVertical()) {
2324 queryTextRectEvent
.mReply
->mRect
.height
= 1;
2326 queryTextRectEvent
.mReply
->mRect
.width
= 1;
2328 aEvent
->mReply
->mRectArray
.AppendElement(
2329 queryTextRectEvent
.mReply
->mRect
);
2333 MOZ_ASSERT(aEvent
->Succeeded());
2337 nsresult
ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent
* aEvent
) {
2338 // If mLength is 0 (this may be caused by bug of native IME), we should
2339 // redirect this event to OnQueryCaretRect().
2340 if (!aEvent
->mInput
.mLength
) {
2341 return OnQueryCaretRect(aEvent
);
2344 nsresult rv
= Init(aEvent
);
2345 if (NS_FAILED(rv
)) {
2349 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
2351 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
2353 RefPtr
<Text
> lastTextNode
;
2354 uint32_t startOffset
= 0;
2355 if (NS_WARN_IF(NS_FAILED(SetRawRangeFromFlatTextOffset(
2356 &rawRange
, aEvent
->mInput
.mOffset
, aEvent
->mInput
.mLength
,
2357 lineBreakType
, true, &startOffset
, getter_AddRefs(lastTextNode
))))) {
2358 return NS_ERROR_FAILURE
;
2361 if (NS_WARN_IF(NS_FAILED(
2362 GenerateFlatTextContent(rawRange
, string
, lineBreakType
)))) {
2363 return NS_ERROR_FAILURE
;
2365 aEvent
->mReply
->mOffsetAndData
.emplace(startOffset
, string
,
2366 OffsetAndDataFor::EditorString
);
2368 // used to iterate over all contents and their frames
2369 PostContentIterator postOrderIter
;
2370 rv
= postOrderIter
.Init(rawRange
.Start().AsRaw(), rawRange
.End().AsRaw());
2371 if (NS_WARN_IF(NS_FAILED(rv
))) {
2372 return NS_ERROR_FAILURE
;
2375 // Get the first frame which causes some text after the offset.
2376 FrameAndNodeOffset firstFrame
= GetFirstFrameInRangeForTextRect(rawRange
);
2378 // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
2379 // means that there are no visible frames having text or the offset reached
2380 // the end of contents.
2381 if (!firstFrame
.IsValid()) {
2382 nsAutoString allText
;
2383 rv
= GenerateFlatTextContent(mRootElement
, allText
, lineBreakType
);
2384 // If the offset doesn't reach the end of contents but there is no frames
2385 // for the node, that means that current offset's node is hidden by CSS or
2386 // something. Ideally, we should handle it with the last visible text
2387 // node's last character's rect, but it's not usual cases in actual web
2388 // services. Therefore, currently, we should make this case fail.
2389 if (NS_WARN_IF(NS_FAILED(rv
)) ||
2390 static_cast<uint32_t>(aEvent
->mInput
.mOffset
) < allText
.Length()) {
2391 return NS_ERROR_FAILURE
;
2394 // Look for the last frame which should be included text rects.
2395 rv
= rawRange
.SelectNodeContents(mRootElement
);
2396 if (NS_WARN_IF(NS_FAILED(rv
))) {
2397 return NS_ERROR_UNEXPECTED
;
2400 FrameAndNodeOffset lastFrame
= GetLastFrameInRangeForTextRect(rawRange
);
2401 // If there is at least one frame which can be used for computing a rect
2402 // for a character or a line breaker, we should use it for guessing the
2403 // caret rect at the end of the contents.
2404 nsPresContext
* presContext
;
2406 presContext
= lastFrame
->PresContext();
2407 if (NS_WARN_IF(!lastFrame
->GetContent())) {
2408 return NS_ERROR_FAILURE
;
2410 FrameRelativeRect relativeRect
;
2411 // If there is a <br> frame at the end, it represents an empty line at
2412 // the end with moz-<br> or content <br> in a block level element.
2413 if (lastFrame
->IsBrFrame()) {
2414 relativeRect
= GetLineBreakerRectBefore(lastFrame
);
2416 // If there is a text frame at the end, use its information.
2417 else if (lastFrame
->IsTextFrame()) {
2418 const Text
* textNode
= Text::FromNode(lastFrame
->GetContent());
2419 MOZ_ASSERT(textNode
);
2421 relativeRect
= GuessLineBreakerRectAfter(*textNode
);
2424 // If there is an empty frame which is neither a text frame nor a <br>
2425 // frame at the end, guess caret rect in it.
2427 relativeRect
= GuessFirstCaretRectIn(lastFrame
);
2429 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2430 return NS_ERROR_FAILURE
;
2432 rect
= relativeRect
.RectRelativeTo(lastFrame
);
2433 rv
= ConvertToRootRelativeOffset(lastFrame
, rect
);
2434 if (NS_WARN_IF(NS_FAILED(rv
))) {
2437 aEvent
->mReply
->mWritingMode
= lastFrame
->GetWritingMode();
2439 // Otherwise, if there are no contents in mRootElement, guess caret rect in
2440 // its frame (with its font height and content box).
2442 nsIFrame
* rootContentFrame
= mRootElement
->GetPrimaryFrame();
2443 if (NS_WARN_IF(!rootContentFrame
)) {
2444 return NS_ERROR_FAILURE
;
2446 presContext
= rootContentFrame
->PresContext();
2447 FrameRelativeRect relativeRect
= GuessFirstCaretRectIn(rootContentFrame
);
2448 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2449 return NS_ERROR_FAILURE
;
2451 rect
= relativeRect
.RectRelativeTo(rootContentFrame
);
2452 rv
= ConvertToRootRelativeOffset(rootContentFrame
, rect
);
2453 if (NS_WARN_IF(NS_FAILED(rv
))) {
2456 aEvent
->mReply
->mWritingMode
= rootContentFrame
->GetWritingMode();
2458 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2459 rect
, presContext
->AppUnitsPerDevPixel());
2460 if (nsPresContext
* rootContext
=
2461 presContext
->GetInProcessRootContentDocumentPresContext()) {
2462 aEvent
->mReply
->mRect
=
2463 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2464 aEvent
->mReply
->mRect
, rootContext
->PresShell()));
2466 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
2468 MOZ_ASSERT(aEvent
->Succeeded());
2472 nsRect rect
, frameRect
;
2475 // If the first frame is a text frame, the result should be computed with
2476 // the frame's rect but not including the rect before start point of the
2478 if (firstFrame
->IsTextFrame()) {
2479 rect
.SetRect(nsPoint(0, 0), firstFrame
->GetRect().Size());
2480 rv
= ConvertToRootRelativeOffset(firstFrame
, rect
);
2481 if (NS_WARN_IF(NS_FAILED(rv
))) {
2485 // Exclude the rect before start point of the queried range.
2486 firstFrame
->GetPointFromOffset(firstFrame
.mOffsetInNode
, &ptOffset
);
2487 if (firstFrame
->GetWritingMode().IsVertical()) {
2488 rect
.y
+= ptOffset
.y
;
2489 rect
.height
-= ptOffset
.y
;
2491 rect
.x
+= ptOffset
.x
;
2492 rect
.width
-= ptOffset
.x
;
2495 // If first frame causes a line breaker but it's not a <br> frame, we cannot
2496 // compute proper rect only with the frame because typically caret is at
2497 // right of the last character of it. For example, if caret is after "c" of
2498 // |<p>abc</p><p>def</p>|, IME may query a line breaker's rect after "c".
2499 // Then, if we compute it only with the 2nd <p>'s block frame, the result
2501 // +-<p>--------------------------------+
2503 // +------------------------------------+
2505 // I+-<p>--------------------------------+
2507 // +------------------------------------+
2508 // However, users expect popup windows of IME should be positioned at
2509 // right-bottom of "c" like this:
2510 // +-<p>--------------------------------+
2512 // +------------------------------------+
2514 // +-<p>--------------------------------+
2516 // +------------------------------------+
2517 // Therefore, if the first frame isn't a <br> frame and there is a visible
2518 // text node before the first node in the queried range, we should compute the
2519 // first rect with the previous character's rect.
2520 else if (!firstFrame
->IsBrFrame() && lastTextNode
&&
2521 lastTextNode
->GetPrimaryFrame()) {
2522 FrameRelativeRect brRectAfterLastChar
=
2523 GuessLineBreakerRectAfter(*lastTextNode
);
2524 if (NS_WARN_IF(!brRectAfterLastChar
.IsValid())) {
2525 return NS_ERROR_FAILURE
;
2527 rect
= brRectAfterLastChar
.mRect
;
2528 rv
= ConvertToRootRelativeOffset(brRectAfterLastChar
.mBaseFrame
, rect
);
2529 if (NS_WARN_IF(NS_FAILED(rv
))) {
2534 // Otherwise, we need to compute the line breaker's rect only with the
2535 // first frame's rect. But this may be unexpected. For example,
2536 // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is before
2537 // "a", therefore, users expect the rect left of "a". However, we don't
2538 // have enough information about the next character here and this isn't
2539 // usual case (e.g., IME typically tries to query the rect of "a" or caret
2540 // rect for computing its popup position). Therefore, we shouldn't do
2541 // more complicated hack here unless we'll get some bug reports actually.
2543 FrameRelativeRect relativeRect
= GetLineBreakerRectBefore(firstFrame
);
2544 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2545 return NS_ERROR_FAILURE
;
2547 rect
= relativeRect
.RectRelativeTo(firstFrame
);
2548 rv
= ConvertToRootRelativeOffset(firstFrame
, rect
);
2549 if (NS_WARN_IF(NS_FAILED(rv
))) {
2554 // UnionRect() requires non-empty rect. So, let's make sure to get non-emtpy
2555 // rect from the first frame.
2556 EnsureNonEmptyRect(rect
);
2558 // Get the last frame which causes some text in the range.
2559 FrameAndNodeOffset lastFrame
= GetLastFrameInRangeForTextRect(rawRange
);
2560 if (NS_WARN_IF(!lastFrame
.IsValid())) {
2561 return NS_ERROR_FAILURE
;
2564 // iterate over all covered frames
2565 for (nsIFrame
* frame
= firstFrame
; frame
!= lastFrame
;) {
2566 frame
= frame
->GetNextContinuation();
2569 postOrderIter
.Next();
2570 nsINode
* node
= postOrderIter
.GetCurrentNode();
2574 if (!node
->IsContent()) {
2577 nsIFrame
* primaryFrame
= node
->AsContent()->GetPrimaryFrame();
2578 // The node may be hidden by CSS.
2579 if (!primaryFrame
) {
2582 // We should take only text frame's rect and br frame's rect. We can
2583 // always use frame rect of text frame and GetLineBreakerRectBefore()
2584 // can return exactly correct rect only for <br> frame for now. On the
2585 // other hand, GetLineBreakRectBefore() returns guessed caret rect for
2586 // the other frames. We shouldn't include such odd rect to the result.
2587 if (primaryFrame
->IsTextFrame() || primaryFrame
->IsBrFrame()) {
2588 frame
= primaryFrame
;
2590 } while (!frame
&& !postOrderIter
.IsDone());
2595 if (frame
->IsTextFrame()) {
2596 frameRect
.SetRect(nsPoint(0, 0), frame
->GetRect().Size());
2598 MOZ_ASSERT(frame
->IsBrFrame());
2599 FrameRelativeRect relativeRect
= GetLineBreakerRectBefore(frame
);
2600 if (NS_WARN_IF(!relativeRect
.IsValid())) {
2601 return NS_ERROR_FAILURE
;
2603 frameRect
= relativeRect
.RectRelativeTo(frame
);
2605 rv
= ConvertToRootRelativeOffset(frame
, frameRect
);
2606 if (NS_WARN_IF(NS_FAILED(rv
))) {
2609 // UnionRect() requires non-empty rect. So, let's make sure to get
2610 // non-emtpy rect from the frame.
2611 EnsureNonEmptyRect(frameRect
);
2612 if (frame
!= lastFrame
) {
2613 // not last frame, so just add rect to previous result
2614 rect
.UnionRect(rect
, frameRect
);
2618 // Get the ending frame rect.
2619 // FYI: If first frame and last frame are same, frameRect is already set
2620 // to the rect excluding the text before the query range.
2621 if (firstFrame
.mFrame
!= lastFrame
.mFrame
) {
2622 frameRect
.SetRect(nsPoint(0, 0), lastFrame
->GetRect().Size());
2623 rv
= ConvertToRootRelativeOffset(lastFrame
, frameRect
);
2624 if (NS_WARN_IF(NS_FAILED(rv
))) {
2629 // Shrink the last frame for cutting off the text after the query range.
2630 if (lastFrame
->IsTextFrame()) {
2631 lastFrame
->GetPointFromOffset(lastFrame
.mOffsetInNode
, &ptOffset
);
2632 if (lastFrame
->GetWritingMode().IsVertical()) {
2633 frameRect
.height
-= lastFrame
->GetRect().height
- ptOffset
.y
;
2635 frameRect
.width
-= lastFrame
->GetRect().width
- ptOffset
.x
;
2637 // UnionRect() requires non-empty rect. So, let's make sure to get
2638 // non-empty rect from the last frame.
2639 EnsureNonEmptyRect(frameRect
);
2641 if (firstFrame
.mFrame
== lastFrame
.mFrame
) {
2642 rect
.IntersectRect(rect
, frameRect
);
2644 rect
.UnionRect(rect
, frameRect
);
2648 nsPresContext
* presContext
= lastFrame
->PresContext();
2649 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2650 rect
, presContext
->AppUnitsPerDevPixel());
2651 if (nsPresContext
* rootContext
=
2652 presContext
->GetInProcessRootContentDocumentPresContext()) {
2653 aEvent
->mReply
->mRect
=
2654 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2655 aEvent
->mReply
->mRect
, rootContext
->PresShell()));
2657 // Returning empty rect may cause native IME confused, let's make sure to
2658 // return non-empty rect.
2659 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
2660 aEvent
->mReply
->mWritingMode
= lastFrame
->GetWritingMode();
2662 MOZ_ASSERT(aEvent
->Succeeded());
2666 nsresult
ContentEventHandler::OnQueryEditorRect(
2667 WidgetQueryContentEvent
* aEvent
) {
2668 nsresult rv
= Init(aEvent
);
2669 if (NS_FAILED(rv
)) {
2673 if (NS_WARN_IF(NS_FAILED(QueryContentRect(mRootElement
, aEvent
)))) {
2674 return NS_ERROR_FAILURE
;
2677 MOZ_ASSERT(aEvent
->Succeeded());
2681 nsresult
ContentEventHandler::OnQueryCaretRect(
2682 WidgetQueryContentEvent
* aEvent
) {
2683 nsresult rv
= Init(aEvent
);
2684 if (NS_FAILED(rv
)) {
2688 // When the selection is collapsed and the queried offset is current caret
2689 // position, we should return the "real" caret rect.
2690 if (mSelection
->IsCollapsed()) {
2692 nsIFrame
* caretFrame
= nsCaret::GetGeometry(mSelection
, &caretRect
);
2695 rv
= GetStartOffset(mFirstSelectedRawRange
, &offset
,
2696 GetLineBreakType(aEvent
));
2697 NS_ENSURE_SUCCESS(rv
, rv
);
2698 if (offset
== aEvent
->mInput
.mOffset
) {
2699 rv
= ConvertToRootRelativeOffset(caretFrame
, caretRect
);
2700 NS_ENSURE_SUCCESS(rv
, rv
);
2701 nsPresContext
* presContext
= caretFrame
->PresContext();
2702 aEvent
->mReply
->mRect
= LayoutDeviceIntRect::FromAppUnitsToOutside(
2703 caretRect
, presContext
->AppUnitsPerDevPixel());
2704 if (nsPresContext
* rootContext
=
2705 presContext
->GetInProcessRootContentDocumentPresContext()) {
2706 aEvent
->mReply
->mRect
=
2707 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual(
2708 aEvent
->mReply
->mRect
, rootContext
->PresShell()));
2710 // Returning empty rect may cause native IME confused, let's make sure
2711 // to return non-empty rect.
2712 EnsureNonEmptyRect(aEvent
->mReply
->mRect
);
2713 aEvent
->mReply
->mWritingMode
= caretFrame
->GetWritingMode();
2714 aEvent
->mReply
->mOffsetAndData
.emplace(
2715 aEvent
->mInput
.mOffset
, EmptyString(),
2716 OffsetAndDataFor::SelectedString
);
2718 MOZ_ASSERT(aEvent
->Succeeded());
2724 // Otherwise, we should guess the caret rect from the character's rect.
2725 WidgetQueryContentEvent
queryTextRectEvent(eQueryTextRect
, *aEvent
);
2726 WidgetQueryContentEvent::Options
options(*aEvent
);
2727 queryTextRectEvent
.InitForQueryTextRect(aEvent
->mInput
.mOffset
, 1, options
);
2728 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent
))) ||
2729 NS_WARN_IF(queryTextRectEvent
.Failed())) {
2730 return NS_ERROR_FAILURE
;
2732 queryTextRectEvent
.mReply
->TruncateData();
2733 aEvent
->mReply
->mOffsetAndData
=
2734 std::move(queryTextRectEvent
.mReply
->mOffsetAndData
);
2735 aEvent
->mReply
->mWritingMode
=
2736 std::move(queryTextRectEvent
.mReply
->mWritingMode
);
2737 aEvent
->mReply
->mRect
= GetCaretRectBefore(queryTextRectEvent
.mReply
->mRect
,
2738 aEvent
->mReply
->mWritingMode
);
2740 MOZ_ASSERT(aEvent
->Succeeded());
2744 nsresult
ContentEventHandler::OnQueryContentState(
2745 WidgetQueryContentEvent
* aEvent
) {
2746 if (NS_FAILED(Init(aEvent
))) {
2747 return NS_ERROR_FAILURE
;
2749 MOZ_ASSERT(aEvent
->mReply
.isSome());
2750 MOZ_ASSERT(aEvent
->Succeeded());
2754 nsresult
ContentEventHandler::OnQuerySelectionAsTransferable(
2755 WidgetQueryContentEvent
* aEvent
) {
2756 nsresult rv
= Init(aEvent
);
2757 if (NS_FAILED(rv
)) {
2761 MOZ_ASSERT(aEvent
->mReply
.isSome());
2763 if (mSelection
->IsCollapsed()) {
2764 MOZ_ASSERT(!aEvent
->mReply
->mTransferable
);
2768 if (NS_WARN_IF(NS_FAILED(nsCopySupport::GetTransferableForSelection(
2769 mSelection
, mDocument
,
2770 getter_AddRefs(aEvent
->mReply
->mTransferable
))))) {
2771 return NS_ERROR_FAILURE
;
2774 MOZ_ASSERT(aEvent
->Succeeded());
2778 nsresult
ContentEventHandler::OnQueryCharacterAtPoint(
2779 WidgetQueryContentEvent
* aEvent
) {
2780 nsresult rv
= Init(aEvent
);
2781 if (NS_FAILED(rv
)) {
2785 MOZ_ASSERT(aEvent
->mReply
->mOffsetAndData
.isNothing());
2786 MOZ_ASSERT(aEvent
->mReply
->mTentativeCaretOffset
.isNothing());
2788 PresShell
* presShell
= mDocument
->GetPresShell();
2789 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
2790 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
2791 NS_ENSURE_TRUE(rootFrame
, NS_ERROR_FAILURE
);
2792 nsIWidget
* rootWidget
= rootFrame
->GetNearestWidget();
2793 NS_ENSURE_TRUE(rootWidget
, NS_ERROR_FAILURE
);
2795 // The root frame's widget might be different, e.g., the event was fired on
2796 // a popup but the rootFrame is the document root.
2797 if (rootWidget
!= aEvent
->mWidget
) {
2798 MOZ_ASSERT(aEvent
->mWidget
, "The event must have the widget");
2799 nsView
* view
= nsView::GetViewFor(aEvent
->mWidget
);
2800 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
2801 rootFrame
= view
->GetFrame();
2802 NS_ENSURE_TRUE(rootFrame
, NS_ERROR_FAILURE
);
2803 rootWidget
= rootFrame
->GetNearestWidget();
2804 NS_ENSURE_TRUE(rootWidget
, NS_ERROR_FAILURE
);
2807 WidgetQueryContentEvent
queryCharAtPointOnRootWidgetEvent(
2808 true, eQueryCharacterAtPoint
, rootWidget
);
2809 queryCharAtPointOnRootWidgetEvent
.mUseNativeLineBreak
=
2810 aEvent
->mUseNativeLineBreak
;
2811 queryCharAtPointOnRootWidgetEvent
.mRefPoint
= aEvent
->mRefPoint
;
2812 if (rootWidget
!= aEvent
->mWidget
) {
2813 queryCharAtPointOnRootWidgetEvent
.mRefPoint
+=
2814 aEvent
->mWidget
->WidgetToScreenOffset() -
2815 rootWidget
->WidgetToScreenOffset();
2817 nsPoint ptInRoot
= nsLayoutUtils::GetEventCoordinatesRelativeTo(
2818 &queryCharAtPointOnRootWidgetEvent
, RelativeTo
{rootFrame
});
2820 nsIFrame
* targetFrame
=
2821 nsLayoutUtils::GetFrameForPoint(RelativeTo
{rootFrame
}, ptInRoot
);
2822 if (!targetFrame
|| !targetFrame
->GetContent() ||
2823 !targetFrame
->GetContent()->IsInclusiveDescendantOf(mRootElement
)) {
2824 // There is no character at the point.
2825 MOZ_ASSERT(aEvent
->Succeeded());
2828 nsPoint ptInTarget
= ptInRoot
+ rootFrame
->GetOffsetToCrossDoc(targetFrame
);
2829 int32_t rootAPD
= rootFrame
->PresContext()->AppUnitsPerDevPixel();
2830 int32_t targetAPD
= targetFrame
->PresContext()->AppUnitsPerDevPixel();
2831 ptInTarget
= ptInTarget
.ScaleToOtherAppUnits(rootAPD
, targetAPD
);
2833 nsIFrame::ContentOffsets tentativeCaretOffsets
=
2834 targetFrame
->GetContentOffsetsFromPoint(ptInTarget
);
2835 if (!tentativeCaretOffsets
.content
||
2836 !tentativeCaretOffsets
.content
->IsInclusiveDescendantOf(mRootElement
)) {
2837 // There is no character nor tentative caret point at the point.
2838 MOZ_ASSERT(aEvent
->Succeeded());
2842 uint32_t tentativeCaretOffset
= 0;
2843 if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange(
2844 NodePosition(mRootElement
, 0u), NodePosition(tentativeCaretOffsets
),
2845 mRootElement
, &tentativeCaretOffset
, GetLineBreakType(aEvent
))))) {
2846 return NS_ERROR_FAILURE
;
2849 aEvent
->mReply
->mTentativeCaretOffset
.emplace(tentativeCaretOffset
);
2850 if (!targetFrame
->IsTextFrame()) {
2851 // There is no character at the point but there is tentative caret point.
2852 MOZ_ASSERT(aEvent
->Succeeded());
2856 nsTextFrame
* textframe
= static_cast<nsTextFrame
*>(targetFrame
);
2857 nsIFrame::ContentOffsets contentOffsets
=
2858 textframe
->GetCharacterOffsetAtFramePoint(ptInTarget
);
2859 NS_ENSURE_TRUE(contentOffsets
.content
, NS_ERROR_FAILURE
);
2860 uint32_t offset
= 0;
2861 if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange(
2862 NodePosition(mRootElement
, 0u), NodePosition(contentOffsets
),
2863 mRootElement
, &offset
, GetLineBreakType(aEvent
))))) {
2864 return NS_ERROR_FAILURE
;
2867 WidgetQueryContentEvent
queryTextRectEvent(true, eQueryTextRect
,
2869 WidgetQueryContentEvent::Options
options(*aEvent
);
2870 queryTextRectEvent
.InitForQueryTextRect(offset
, 1, options
);
2871 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent
))) ||
2872 NS_WARN_IF(queryTextRectEvent
.Failed())) {
2873 return NS_ERROR_FAILURE
;
2876 aEvent
->mReply
->mOffsetAndData
=
2877 std::move(queryTextRectEvent
.mReply
->mOffsetAndData
);
2878 aEvent
->mReply
->mRect
= queryTextRectEvent
.mReply
->mRect
;
2880 MOZ_ASSERT(aEvent
->Succeeded());
2884 nsresult
ContentEventHandler::OnQueryDOMWidgetHittest(
2885 WidgetQueryContentEvent
* aEvent
) {
2886 NS_ASSERTION(aEvent
, "aEvent must not be null");
2888 nsresult rv
= InitBasic();
2889 if (NS_FAILED(rv
)) {
2893 aEvent
->mReply
->mWidgetIsHit
= false;
2895 NS_ENSURE_TRUE(aEvent
->mWidget
, NS_ERROR_FAILURE
);
2897 PresShell
* presShell
= mDocument
->GetPresShell();
2898 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
2899 nsIFrame
* docFrame
= presShell
->GetRootFrame();
2900 NS_ENSURE_TRUE(docFrame
, NS_ERROR_FAILURE
);
2902 LayoutDeviceIntPoint eventLoc
=
2903 aEvent
->mRefPoint
+ aEvent
->mWidget
->WidgetToScreenOffset();
2904 CSSIntRect docFrameRect
= docFrame
->GetScreenRect();
2905 CSSIntPoint
eventLocCSS(
2906 docFrame
->PresContext()->DevPixelsToIntCSSPixels(eventLoc
.x
) -
2908 docFrame
->PresContext()->DevPixelsToIntCSSPixels(eventLoc
.y
) -
2911 if (Element
* contentUnderMouse
= mDocument
->ElementFromPointHelper(
2912 eventLocCSS
.x
, eventLocCSS
.y
, false, false, ViewportType::Visual
)) {
2913 if (nsIFrame
* targetFrame
= contentUnderMouse
->GetPrimaryFrame()) {
2914 if (aEvent
->mWidget
== targetFrame
->GetNearestWidget()) {
2915 aEvent
->mReply
->mWidgetIsHit
= true;
2920 MOZ_ASSERT(aEvent
->Succeeded());
2925 nsresult
ContentEventHandler::GetFlatTextLengthInRange(
2926 const NodePosition
& aStartPosition
, const NodePosition
& aEndPosition
,
2927 const Element
* aRootElement
, uint32_t* aLength
,
2928 LineBreakType aLineBreakType
, bool aIsRemovingNode
/* = false */) {
2929 if (NS_WARN_IF(!aRootElement
) || NS_WARN_IF(!aStartPosition
.IsSet()) ||
2930 NS_WARN_IF(!aEndPosition
.IsSet()) || NS_WARN_IF(!aLength
)) {
2931 return NS_ERROR_INVALID_ARG
;
2934 if (aStartPosition
== aEndPosition
) {
2939 PreContentIterator preOrderIter
;
2941 // Working with ContentIterator, we may need to adjust the end position for
2942 // including it forcibly.
2943 NodePosition
endPosition(aEndPosition
);
2945 // This may be called for retrieving the text of removed nodes. Even in this
2946 // case, the node thinks it's still in the tree because UnbindFromTree() will
2947 // be called after here. However, the node was already removed from the
2948 // array of children of its parent. So, be careful to handle this case.
2949 if (aIsRemovingNode
) {
2950 DebugOnly
<nsIContent
*> parent
= aStartPosition
.Container()->GetParent();
2953 parent
->ComputeIndexOf(aStartPosition
.Container()).isNothing(),
2954 "At removing the node, the node shouldn't be in the array of children "
2956 MOZ_ASSERT(aStartPosition
.Container() == endPosition
.Container(),
2957 "At removing the node, start and end node should be same");
2958 MOZ_ASSERT(*aStartPosition
.Offset(
2959 NodePosition::OffsetFilter::kValidOrInvalidOffsets
) == 0,
2960 "When the node is being removed, the start offset should be 0");
2962 static_cast<uint32_t>(*endPosition
.Offset(
2963 NodePosition::OffsetFilter::kValidOrInvalidOffsets
)) ==
2964 endPosition
.Container()->GetChildCount(),
2965 "When the node is being removed, the end offset should be child count");
2966 nsresult rv
= preOrderIter
.Init(aStartPosition
.Container());
2967 if (NS_WARN_IF(NS_FAILED(rv
))) {
2971 RawRange prevRawRange
;
2972 nsresult rv
= prevRawRange
.SetStart(aStartPosition
.AsRaw());
2973 if (NS_WARN_IF(NS_FAILED(rv
))) {
2977 // When the end position is immediately after non-root element's open tag,
2978 // we need to include a line break caused by the open tag.
2979 if (endPosition
.Container() != aRootElement
&&
2980 endPosition
.IsImmediatelyAfterOpenTag()) {
2981 if (endPosition
.Container()->HasChildren()) {
2982 // When the end node has some children, move the end position to before
2983 // the open tag of its first child.
2984 nsINode
* firstChild
= endPosition
.Container()->GetFirstChild();
2985 if (NS_WARN_IF(!firstChild
)) {
2986 return NS_ERROR_FAILURE
;
2988 endPosition
= NodePositionBefore(firstChild
, 0u);
2990 // When the end node is empty, move the end position after the node.
2991 nsIContent
* parentContent
= endPosition
.Container()->GetParent();
2992 if (NS_WARN_IF(!parentContent
)) {
2993 return NS_ERROR_FAILURE
;
2995 Maybe
<uint32_t> indexInParent
=
2996 parentContent
->ComputeIndexOf(endPosition
.Container());
2997 if (MOZ_UNLIKELY(NS_WARN_IF(indexInParent
.isNothing()))) {
2998 return NS_ERROR_FAILURE
;
3000 MOZ_ASSERT(*indexInParent
!= UINT32_MAX
);
3001 endPosition
= NodePositionBefore(parentContent
, *indexInParent
+ 1u);
3005 if (endPosition
.IsSetAndValid()) {
3006 // Offset is within node's length; set end of range to that offset
3007 rv
= prevRawRange
.SetEnd(endPosition
.AsRaw());
3008 if (NS_WARN_IF(NS_FAILED(rv
))) {
3011 rv
= preOrderIter
.Init(prevRawRange
.Start().AsRaw(),
3012 prevRawRange
.End().AsRaw());
3013 if (NS_WARN_IF(NS_FAILED(rv
))) {
3016 } else if (endPosition
.Container() != aRootElement
) {
3017 // Offset is past node's length; set end of range to end of node
3018 rv
= prevRawRange
.SetEndAfter(endPosition
.Container());
3019 if (NS_WARN_IF(NS_FAILED(rv
))) {
3022 rv
= preOrderIter
.Init(prevRawRange
.Start().AsRaw(),
3023 prevRawRange
.End().AsRaw());
3024 if (NS_WARN_IF(NS_FAILED(rv
))) {
3028 // Offset is past the root node; set end of range to end of root node
3029 rv
= preOrderIter
.Init(const_cast<Element
*>(aRootElement
));
3030 if (NS_WARN_IF(NS_FAILED(rv
))) {
3037 for (; !preOrderIter
.IsDone(); preOrderIter
.Next()) {
3038 nsINode
* node
= preOrderIter
.GetCurrentNode();
3039 if (NS_WARN_IF(!node
)) {
3042 if (!node
->IsContent()) {
3045 nsIContent
* content
= node
->AsContent();
3047 if (const Text
* textNode
= Text::FromNode(content
)) {
3048 // Note: our range always starts from offset 0
3049 if (node
== endPosition
.Container()) {
3050 // NOTE: We should have an offset here, as endPosition.Container() is a
3051 // nsINode::eTEXT, which always has an offset.
3052 *aLength
+= GetTextLength(
3053 *textNode
, aLineBreakType
,
3054 *endPosition
.Offset(
3055 NodePosition::OffsetFilter::kValidOrInvalidOffsets
));
3057 *aLength
+= GetTextLength(*textNode
, aLineBreakType
);
3059 } else if (ShouldBreakLineBefore(*content
, aRootElement
)) {
3060 // If the start position is start of this node but doesn't include the
3061 // open tag, don't append the line break length.
3062 if (node
== aStartPosition
.Container() &&
3063 !aStartPosition
.IsBeforeOpenTag()) {
3066 // If the end position is before the open tag, don't append the line
3068 if (node
== endPosition
.Container() && endPosition
.IsBeforeOpenTag()) {
3071 *aLength
+= GetBRLength(aLineBreakType
);
3077 nsresult
ContentEventHandler::GetStartOffset(const RawRange
& aRawRange
,
3079 LineBreakType aLineBreakType
) {
3080 // To match the "no skip start" hack in ContentIterator::Init, when range
3081 // offset is 0 and the range node is not a container, we have to assume the
3082 // range _includes_ the node, which means the start offset should _not_
3083 // include the node.
3085 // For example, for this content: <br>abc, and range (<br>, 0)-("abc", 1), the
3086 // range includes the linebreak from <br>, so the start offset should _not_
3087 // include <br>, and the start offset should be 0.
3089 // However, for this content: <p/>abc, and range (<p>, 0)-("abc", 1), the
3090 // range does _not_ include the linebreak from <p> because <p> is a container,
3091 // so the start offset _should_ include <p>, and the start offset should be 1.
3093 nsINode
* startNode
= aRawRange
.GetStartContainer();
3094 bool startIsContainer
= true;
3095 if (startNode
->IsHTMLElement()) {
3096 nsAtom
* name
= startNode
->NodeInfo()->NameAtom();
3098 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
3100 const NodePosition
& startPos
=
3101 startIsContainer
? NodePosition(startNode
, aRawRange
.StartOffset())
3102 : NodePositionBefore(startNode
, aRawRange
.StartOffset());
3103 return GetFlatTextLengthInRange(NodePosition(mRootElement
, 0u), startPos
,
3104 mRootElement
, aOffset
, aLineBreakType
);
3107 nsresult
ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(
3108 RawRange
& aRawRange
) {
3109 MOZ_ASSERT(aRawRange
.Collapsed());
3111 if (!aRawRange
.Collapsed()) {
3112 return NS_ERROR_INVALID_ARG
;
3115 const RangeBoundary
& startPoint
= aRawRange
.Start();
3116 if (NS_WARN_IF(!startPoint
.IsSet())) {
3117 return NS_ERROR_INVALID_ARG
;
3120 // If the node does not have children like a text node, we don't need to
3121 // modify aRawRange.
3122 if (!startPoint
.Container()->HasChildren()) {
3126 // If the container is not a text node but it has a text node at the offset,
3127 // we should adjust the range into the text node.
3128 // NOTE: This is emulating similar situation of EditorBase.
3129 if (startPoint
.IsStartOfContainer()) {
3130 // If the range is the start of the container, adjusted the range to the
3131 // start of the first child.
3132 if (!startPoint
.Container()->GetFirstChild()->IsText()) {
3135 nsresult rv
= aRawRange
.CollapseTo(
3136 RawRangeBoundary(startPoint
.Container()->GetFirstChild(), 0u));
3137 if (NS_WARN_IF(NS_FAILED(rv
))) {
3143 if (!startPoint
.IsSetAndValid()) {
3147 // If start of the range is next to a child node, adjust the range to the
3148 // end of the previous child (i.e., startPoint.Ref()).
3149 if (!startPoint
.Ref()->IsText()) {
3152 nsresult rv
= aRawRange
.CollapseTo(
3153 RawRangeBoundary(startPoint
.Ref(), startPoint
.Ref()->Length()));
3154 if (NS_WARN_IF(NS_FAILED(rv
))) {
3160 nsresult
ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame
* aFrame
,
3162 NS_ASSERTION(aFrame
, "aFrame must not be null");
3164 nsPresContext
* thisPC
= aFrame
->PresContext();
3165 nsPresContext
* rootPC
= thisPC
->GetRootPresContext();
3166 if (NS_WARN_IF(!rootPC
)) {
3167 return NS_ERROR_FAILURE
;
3169 nsIFrame
* rootFrame
= rootPC
->PresShell()->GetRootFrame();
3170 if (NS_WARN_IF(!rootFrame
)) {
3171 return NS_ERROR_FAILURE
;
3174 aRect
= nsLayoutUtils::TransformFrameRectToAncestor(aFrame
, aRect
, rootFrame
);
3176 // TransformFrameRectToAncestor returned the rect in the ancestor's appUnits,
3177 // but we want it in aFrame's units (in case of different full-zoom factors),
3179 aRect
= aRect
.ScaleToOtherAppUnitsRoundOut(rootPC
->AppUnitsPerDevPixel(),
3180 thisPC
->AppUnitsPerDevPixel());
3185 static void AdjustRangeForSelection(const Element
* aRootElement
,
3187 Maybe
<uint32_t>* aNodeOffset
) {
3188 nsINode
* node
= *aNode
;
3189 Maybe
<uint32_t> nodeOffset
= *aNodeOffset
;
3190 if (aRootElement
== node
|| NS_WARN_IF(!node
->GetParent()) ||
3195 // When the offset is at the end of the text node, set it to after the
3196 // text node, to make sure the caret is drawn on a new line when the last
3197 // character of the text node is '\n' in <textarea>.
3198 const uint32_t textLength
= node
->AsContent()->TextLength();
3199 MOZ_ASSERT(nodeOffset
.isNothing() || *nodeOffset
<= textLength
,
3200 "Offset is past length of text node");
3201 if (nodeOffset
.isNothing() || *nodeOffset
!= textLength
) {
3205 Element
* rootParentElement
= aRootElement
->GetParentElement();
3206 if (NS_WARN_IF(!rootParentElement
)) {
3209 // If the root node is not an anonymous div of <textarea>, we don't need to
3210 // do this hack. If you did this, ContentEventHandler couldn't distinguish
3211 // if the range includes open tag of the next node in some cases, e.g.,
3212 // textNode]<p></p> vs. textNode<p>]</p>
3213 if (!rootParentElement
->IsHTMLElement(nsGkAtoms::textarea
)) {
3217 // If the node is being removed from its parent, it holds the ex-parent,
3218 // but the parent have already removed the child from its child chain.
3219 // Therefore `ComputeIndexOf` may fail, but I don't want to make Beta/Nightly
3220 // crash at accessing `Maybe::operator*` so that here checks `isSome`, but
3221 // crashing only in debug builds may help to debug something complicated
3222 // situation, therefore, `MOZ_ASSERT` is put here.
3223 *aNode
= node
->GetParent();
3224 Maybe
<uint32_t> index
= (*aNode
)->ComputeIndexOf(node
);
3225 MOZ_ASSERT(index
.isSome());
3226 if (index
.isSome()) {
3227 MOZ_ASSERT(*index
!= UINT32_MAX
);
3228 *aNodeOffset
= Some(*index
+ 1u);
3230 *aNodeOffset
= Some(0u);
3234 nsresult
ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent
* aEvent
) {
3235 aEvent
->mSucceeded
= false;
3237 // Get selection to manipulate
3238 // XXX why do we need to get them from ISM? This method should work fine
3240 nsresult rv
= IMEStateManager::GetFocusSelectionAndRootElement(
3241 getter_AddRefs(mSelection
), getter_AddRefs(mRootElement
));
3242 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
3243 NS_ENSURE_SUCCESS(rv
, rv
);
3246 NS_ENSURE_SUCCESS(rv
, rv
);
3249 // Get range from offset and length
3251 rv
= SetRawRangeFromFlatTextOffset(&rawRange
, aEvent
->mOffset
,
3252 aEvent
->mLength
, GetLineBreakType(aEvent
),
3253 aEvent
->mExpandToClusterBoundary
);
3254 NS_ENSURE_SUCCESS(rv
, rv
);
3256 nsINode
* startNode
= rawRange
.GetStartContainer();
3257 nsINode
* endNode
= rawRange
.GetEndContainer();
3258 Maybe
<uint32_t> startNodeOffset
= Some(rawRange
.StartOffset());
3259 Maybe
<uint32_t> endNodeOffset
= Some(rawRange
.EndOffset());
3260 AdjustRangeForSelection(mRootElement
, &startNode
, &startNodeOffset
);
3261 AdjustRangeForSelection(mRootElement
, &endNode
, &endNodeOffset
);
3262 if (NS_WARN_IF(!startNode
) || NS_WARN_IF(!endNode
) ||
3263 NS_WARN_IF(startNodeOffset
.isNothing()) ||
3264 NS_WARN_IF(endNodeOffset
.isNothing())) {
3265 return NS_ERROR_UNEXPECTED
;
3268 if (aEvent
->mReversed
) {
3269 nsCOMPtr
<nsINode
> startNodeStrong(startNode
);
3270 nsCOMPtr
<nsINode
> endNodeStrong(endNode
);
3272 MOZ_KnownLive(mSelection
)
3273 ->SetBaseAndExtentInLimiter(*endNodeStrong
, *endNodeOffset
,
3274 *startNodeStrong
, *startNodeOffset
, error
);
3275 if (NS_WARN_IF(error
.Failed())) {
3276 return error
.StealNSResult();
3279 nsCOMPtr
<nsINode
> startNodeStrong(startNode
);
3280 nsCOMPtr
<nsINode
> endNodeStrong(endNode
);
3282 MOZ_KnownLive(mSelection
)
3283 ->SetBaseAndExtentInLimiter(*startNodeStrong
, *startNodeOffset
,
3284 *endNodeStrong
, *endNodeOffset
, error
);
3285 if (NS_WARN_IF(error
.Failed())) {
3286 return error
.StealNSResult();
3290 // `ContentEventHandler` is a `MOZ_STACK_CLASS`, so `mSelection` is known to
3292 MOZ_KnownLive(mSelection
)
3293 ->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION
,
3294 ScrollAxis(), ScrollAxis(), 0);
3295 aEvent
->mSucceeded
= true;
3299 nsRect
ContentEventHandler::FrameRelativeRect::RectRelativeTo(
3300 nsIFrame
* aDestFrame
) const {
3301 if (!mBaseFrame
|| NS_WARN_IF(!aDestFrame
)) {
3305 if (NS_WARN_IF(aDestFrame
->PresContext() != mBaseFrame
->PresContext())) {
3309 if (aDestFrame
== mBaseFrame
) {
3313 nsIFrame
* rootFrame
= mBaseFrame
->PresShell()->GetRootFrame();
3314 nsRect baseFrameRectInRootFrame
= nsLayoutUtils::TransformFrameRectToAncestor(
3315 mBaseFrame
, nsRect(), rootFrame
);
3316 nsRect destFrameRectInRootFrame
= nsLayoutUtils::TransformFrameRectToAncestor(
3317 aDestFrame
, nsRect(), rootFrame
);
3318 nsPoint difference
=
3319 destFrameRectInRootFrame
.TopLeft() - baseFrameRectInRootFrame
.TopLeft();
3320 return mRect
- difference
;
3323 } // namespace mozilla