Line data Source code
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/. */
6 :
7 : #include "ContentEventHandler.h"
8 : #include "mozilla/IMEStateManager.h"
9 : #include "mozilla/TextComposition.h"
10 : #include "mozilla/TextEvents.h"
11 : #include "mozilla/dom/Element.h"
12 : #include "mozilla/dom/HTMLUnknownElement.h"
13 : #include "mozilla/dom/Selection.h"
14 : #include "nsCaret.h"
15 : #include "nsCOMPtr.h"
16 : #include "nsContentUtils.h"
17 : #include "nsCopySupport.h"
18 : #include "nsFocusManager.h"
19 : #include "nsFontMetrics.h"
20 : #include "nsFrameSelection.h"
21 : #include "nsIContentIterator.h"
22 : #include "nsIPresShell.h"
23 : #include "nsISelection.h"
24 : #include "nsIFrame.h"
25 : #include "nsIObjectFrame.h"
26 : #include "nsLayoutUtils.h"
27 : #include "nsPresContext.h"
28 : #include "nsQueryObject.h"
29 : #include "nsRange.h"
30 : #include "nsTextFragment.h"
31 : #include "nsTextFrame.h"
32 : #include "nsView.h"
33 :
34 : #include <algorithm>
35 :
36 : namespace mozilla {
37 :
38 : using namespace dom;
39 : using namespace widget;
40 :
41 : /******************************************************************/
42 : /* ContentEventHandler */
43 : /******************************************************************/
44 :
45 : // NOTE
46 : //
47 : // ContentEventHandler *creates* ranges as following rules:
48 : // 1. Start of range:
49 : // 1.1. Cases: [textNode or text[Node or textNode[
50 : // When text node is start of a range, start node is the text node and
51 : // start offset is any number between 0 and the length of the text.
52 : // 1.2. Case: [<element>:
53 : // When start of an element node is start of a range, start node is
54 : // parent of the element and start offset is the element's index in the
55 : // parent.
56 : // 1.3. Case: <element/>[
57 : // When after an empty element node is start of a range, start node is
58 : // parent of the element and start offset is the element's index in the
59 : // parent + 1.
60 : // 1.4. Case: <element>[
61 : // When start of a non-empty element is start of a range, start node is
62 : // the element and start offset is 0.
63 : // 1.5. Case: <root>[
64 : // When start of a range is 0 and there are no nodes causing text,
65 : // start node is the root node and start offset is 0.
66 : // 1.6. Case: [</root>
67 : // When start of a range is out of bounds, start node is the root node
68 : // and start offset is number of the children.
69 : // 2. End of range:
70 : // 2.1. Cases: ]textNode or text]Node or textNode]
71 : // When a text node is end of a range, end node is the text node and
72 : // end offset is any number between 0 and the length of the text.
73 : // 2.2. Case: ]<element>
74 : // When before an element node (meaning before the open tag of the
75 : // element) is end of a range, end node is previous node causing text.
76 : // Note that this case shouldn't be handled directly. If rule 2.1 and
77 : // 2.3 are handled correctly, the loop with nsContentIterator shouldn't
78 : // reach the element node since the loop should've finished already at
79 : // handling the last node which caused some text.
80 : // 2.3. Case: <element>]
81 : // When a line break is caused before a non-empty element node and it's
82 : // end of a range, end node is the element and end offset is 0.
83 : // (i.e., including open tag of the element)
84 : // 2.4. Cases: <element/>]
85 : // When after an empty element node is end of a range, end node is
86 : // parent of the element node and end offset is the element's index in
87 : // the parent + 1. (i.e., including close tag of the element or empty
88 : // element)
89 : // 2.5. Case: ]</root>
90 : // When end of a range is out of bounds, end node is the root node and
91 : // end offset is number of the children.
92 : //
93 : // ContentEventHandler *treats* ranges as following additional rules:
94 : // 1. When the start node is an element node which doesn't have children,
95 : // it includes a line break caused before itself (i.e., includes its open
96 : // tag). For example, if start position is { <br>, 0 }, the line break
97 : // caused by <br> should be included into the flatten text.
98 : // 2. When the end node is an element node which doesn't have children,
99 : // it includes the end (i.e., includes its close tag except empty element).
100 : // Although, currently, any close tags don't cause line break, this also
101 : // includes its open tag. For example, if end position is { <br>, 0 }, the
102 : // line break caused by the <br> should be included into the flatten text.
103 :
104 0 : ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
105 : : mPresContext(aPresContext)
106 : , mPresShell(aPresContext->GetPresShell())
107 : , mSelection(nullptr)
108 : , mFirstSelectedRange(nullptr)
109 0 : , mRootContent(nullptr)
110 : {
111 0 : }
112 :
113 : nsresult
114 0 : ContentEventHandler::InitBasic()
115 : {
116 0 : NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
117 :
118 : // If text frame which has overflowing selection underline is dirty,
119 : // we need to flush the pending reflow here.
120 0 : mPresShell->FlushPendingNotifications(FlushType::Layout);
121 :
122 : // Flushing notifications can cause mPresShell to be destroyed (bug 577963).
123 0 : NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
124 :
125 0 : return NS_OK;
126 : }
127 :
128 : nsresult
129 0 : ContentEventHandler::InitRootContent(Selection* aNormalSelection)
130 : {
131 0 : MOZ_ASSERT(aNormalSelection);
132 :
133 : // Root content should be computed with normal selection because normal
134 : // selection is typically has at least one range but the other selections
135 : // not so. If there is a range, computing its root is easy, but if
136 : // there are no ranges, we need to use ancestor limit instead.
137 0 : MOZ_ASSERT(aNormalSelection->Type() == SelectionType::eNormal);
138 :
139 0 : if (!aNormalSelection->RangeCount()) {
140 : // If there is no selection range, we should compute the selection root
141 : // from ancestor limiter or root content of the document.
142 : nsresult rv =
143 0 : aNormalSelection->GetAncestorLimiter(getter_AddRefs(mRootContent));
144 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
145 0 : return NS_ERROR_FAILURE;
146 : }
147 0 : if (!mRootContent) {
148 0 : mRootContent = mPresShell->GetDocument()->GetRootElement();
149 0 : if (NS_WARN_IF(!mRootContent)) {
150 0 : return NS_ERROR_NOT_AVAILABLE;
151 : }
152 : }
153 0 : return NS_OK;
154 : }
155 :
156 0 : RefPtr<nsRange> range(aNormalSelection->GetRangeAt(0));
157 0 : if (NS_WARN_IF(!range)) {
158 0 : return NS_ERROR_UNEXPECTED;
159 : }
160 :
161 : // If there is a selection, we should retrieve the selection root from
162 : // the range since when the window is inactivated, the ancestor limiter
163 : // of selection was cleared by blur event handler of EditorBase but the
164 : // selection range still keeps storing the nodes. If the active element of
165 : // the deactive window is <input> or <textarea>, we can compute the
166 : // selection root from them.
167 0 : nsINode* startNode = range->GetStartContainer();
168 0 : nsINode* endNode = range->GetEndContainer();
169 0 : if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
170 0 : return NS_ERROR_FAILURE;
171 : }
172 :
173 : // See bug 537041 comment 5, the range could have removed node.
174 0 : if (NS_WARN_IF(startNode->GetUncomposedDoc() != mPresShell->GetDocument())) {
175 0 : return NS_ERROR_FAILURE;
176 : }
177 :
178 0 : NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
179 : "firstNormalSelectionRange crosses the document boundary");
180 :
181 0 : mRootContent = startNode->GetSelectionRootContent(mPresShell);
182 0 : if (NS_WARN_IF(!mRootContent)) {
183 0 : return NS_ERROR_FAILURE;
184 : }
185 :
186 0 : return NS_OK;
187 : }
188 :
189 : nsresult
190 0 : ContentEventHandler::InitCommon(SelectionType aSelectionType)
191 : {
192 0 : if (mSelection && mSelection->Type() == aSelectionType) {
193 0 : return NS_OK;
194 : }
195 :
196 0 : mSelection = nullptr;
197 0 : mFirstSelectedRange = nullptr;
198 0 : mRootContent = nullptr;
199 :
200 0 : nsresult rv = InitBasic();
201 0 : NS_ENSURE_SUCCESS(rv, rv);
202 :
203 : nsCOMPtr<nsISelectionController> selectionController =
204 0 : mPresShell->GetSelectionControllerForFocusedContent();
205 0 : if (NS_WARN_IF(!selectionController)) {
206 0 : return NS_ERROR_NOT_AVAILABLE;
207 : }
208 0 : nsCOMPtr<nsISelection> selection;
209 0 : rv = selectionController->GetSelection(ToRawSelectionType(aSelectionType),
210 0 : getter_AddRefs(selection));
211 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
212 0 : return NS_ERROR_UNEXPECTED;
213 : }
214 :
215 0 : mSelection = static_cast<Selection*>(selection.get());
216 0 : if (NS_WARN_IF(!mSelection)) {
217 0 : return NS_ERROR_NOT_AVAILABLE;
218 : }
219 :
220 0 : RefPtr<Selection> normalSelection;
221 0 : if (mSelection->Type() == SelectionType::eNormal) {
222 0 : normalSelection = mSelection;
223 : } else {
224 0 : nsCOMPtr<nsISelection> domSelection;
225 : nsresult rv =
226 0 : selectionController->GetSelection(
227 : nsISelectionController::SELECTION_NORMAL,
228 0 : getter_AddRefs(domSelection));
229 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
230 0 : return NS_ERROR_UNEXPECTED;
231 : }
232 0 : if (NS_WARN_IF(!domSelection)) {
233 0 : return NS_ERROR_NOT_AVAILABLE;
234 : }
235 0 : normalSelection = domSelection->AsSelection();
236 0 : if (NS_WARN_IF(!normalSelection)) {
237 0 : return NS_ERROR_NOT_AVAILABLE;
238 : }
239 : }
240 :
241 0 : rv = InitRootContent(normalSelection);
242 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
243 0 : return rv;
244 : }
245 :
246 0 : if (mSelection->RangeCount()) {
247 0 : mFirstSelectedRange = mSelection->GetRangeAt(0);
248 0 : if (NS_WARN_IF(!mFirstSelectedRange)) {
249 0 : return NS_ERROR_UNEXPECTED;
250 : }
251 0 : return NS_OK;
252 : }
253 :
254 : // Even if there are no selection ranges, it's usual case if aSelectionType
255 : // is a special selection.
256 0 : if (aSelectionType != SelectionType::eNormal) {
257 0 : MOZ_ASSERT(!mFirstSelectedRange);
258 0 : return NS_OK;
259 : }
260 :
261 : // But otherwise, we need to assume that there is a selection range at the
262 : // beginning of the root content if aSelectionType is eNormal.
263 0 : rv = nsRange::CreateRange(mRootContent, 0, mRootContent, 0,
264 0 : getter_AddRefs(mFirstSelectedRange));
265 0 : if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!mFirstSelectedRange)) {
266 0 : return NS_ERROR_UNEXPECTED;
267 : }
268 0 : return NS_OK;
269 : }
270 :
271 : nsresult
272 0 : ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
273 : {
274 0 : NS_ASSERTION(aEvent, "aEvent must not be null");
275 0 : MOZ_ASSERT(aEvent->mMessage == eQuerySelectedText ||
276 : aEvent->mInput.mSelectionType == SelectionType::eNormal);
277 :
278 0 : if (NS_WARN_IF(!aEvent->mInput.IsValidOffset()) ||
279 0 : NS_WARN_IF(!aEvent->mInput.IsValidEventMessage(aEvent->mMessage))) {
280 0 : return NS_ERROR_FAILURE;
281 : }
282 :
283 : // Note that we should ignore WidgetQueryContentEvent::Input::mSelectionType
284 : // if the event isn't eQuerySelectedText.
285 : SelectionType selectionType =
286 0 : aEvent->mMessage == eQuerySelectedText ? aEvent->mInput.mSelectionType :
287 0 : SelectionType::eNormal;
288 0 : if (NS_WARN_IF(selectionType == SelectionType::eNone)) {
289 0 : return NS_ERROR_FAILURE;
290 : }
291 :
292 0 : nsresult rv = InitCommon(selectionType);
293 0 : NS_ENSURE_SUCCESS(rv, rv);
294 :
295 : // Be aware, WidgetQueryContentEvent::mInput::mOffset should be made absolute
296 : // offset before sending it to ContentEventHandler because querying selection
297 : // every time may be expensive. So, if the caller caches selection, it
298 : // should initialize the event with the cached value.
299 0 : if (aEvent->mInput.mRelativeToInsertionPoint) {
300 0 : MOZ_ASSERT(selectionType == SelectionType::eNormal);
301 : RefPtr<TextComposition> composition =
302 0 : IMEStateManager::GetTextCompositionFor(aEvent->mWidget);
303 0 : if (composition) {
304 0 : uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
305 0 : if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
306 0 : return NS_ERROR_FAILURE;
307 : }
308 : } else {
309 0 : LineBreakType lineBreakType = GetLineBreakType(aEvent);
310 0 : uint32_t selectionStart = 0;
311 0 : rv = GetStartOffset(mFirstSelectedRange, &selectionStart, lineBreakType);
312 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
313 0 : return NS_ERROR_FAILURE;
314 : }
315 0 : if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
316 0 : return NS_ERROR_FAILURE;
317 : }
318 : }
319 : }
320 :
321 0 : aEvent->mSucceeded = false;
322 :
323 0 : aEvent->mReply.mContentsRoot = mRootContent.get();
324 :
325 0 : aEvent->mReply.mHasSelection = !mSelection->IsCollapsed();
326 :
327 0 : nsRect r;
328 0 : nsIFrame* frame = nsCaret::GetGeometry(mSelection, &r);
329 0 : if (!frame) {
330 0 : frame = mRootContent->GetPrimaryFrame();
331 0 : if (NS_WARN_IF(!frame)) {
332 0 : return NS_ERROR_FAILURE;
333 : }
334 : }
335 0 : aEvent->mReply.mFocusedWidget = frame->GetNearestWidget();
336 :
337 0 : return NS_OK;
338 : }
339 :
340 : nsresult
341 0 : ContentEventHandler::Init(WidgetSelectionEvent* aEvent)
342 : {
343 0 : NS_ASSERTION(aEvent, "aEvent must not be null");
344 :
345 0 : nsresult rv = InitCommon();
346 0 : NS_ENSURE_SUCCESS(rv, rv);
347 :
348 0 : aEvent->mSucceeded = false;
349 :
350 0 : return NS_OK;
351 : }
352 :
353 : nsIContent*
354 0 : ContentEventHandler::GetFocusedContent()
355 : {
356 0 : nsIDocument* doc = mPresShell->GetDocument();
357 0 : if (!doc) {
358 0 : return nullptr;
359 : }
360 0 : nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow();
361 0 : nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
362 0 : return nsFocusManager::GetFocusedDescendant(window, true,
363 0 : getter_AddRefs(focusedWindow));
364 : }
365 :
366 : bool
367 0 : ContentEventHandler::IsPlugin(nsIContent* aContent)
368 : {
369 0 : return aContent &&
370 0 : aContent->GetDesiredIMEState().mEnabled == IMEState::PLUGIN;
371 : }
372 :
373 : nsresult
374 0 : ContentEventHandler::QueryContentRect(nsIContent* aContent,
375 : WidgetQueryContentEvent* aEvent)
376 : {
377 0 : NS_PRECONDITION(aContent, "aContent must not be null");
378 :
379 0 : nsIFrame* frame = aContent->GetPrimaryFrame();
380 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
381 :
382 : // get rect for first frame
383 0 : nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
384 0 : nsresult rv = ConvertToRootRelativeOffset(frame, resultRect);
385 0 : NS_ENSURE_SUCCESS(rv, rv);
386 :
387 : // account for any additional frames
388 0 : while ((frame = frame->GetNextContinuation()) != nullptr) {
389 0 : nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
390 0 : rv = ConvertToRootRelativeOffset(frame, frameRect);
391 0 : NS_ENSURE_SUCCESS(rv, rv);
392 0 : resultRect.UnionRect(resultRect, frameRect);
393 : }
394 :
395 : aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
396 0 : resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
397 : // Returning empty rect may cause native IME confused, let's make sure to
398 : // return non-empty rect.
399 0 : EnsureNonEmptyRect(aEvent->mReply.mRect);
400 0 : aEvent->mSucceeded = true;
401 :
402 0 : return NS_OK;
403 : }
404 :
405 : // Editor places a bogus BR node under its root content if the editor doesn't
406 : // have any text. This happens even for single line editors.
407 : // When we get text content and when we change the selection,
408 : // we don't want to include the bogus BRs at the end.
409 0 : static bool IsContentBR(nsIContent* aContent)
410 : {
411 0 : return aContent->IsHTMLElement(nsGkAtoms::br) &&
412 0 : !aContent->AttrValueIs(kNameSpaceID_None,
413 : nsGkAtoms::type,
414 : nsGkAtoms::moz,
415 0 : eIgnoreCase) &&
416 0 : !aContent->AttrValueIs(kNameSpaceID_None,
417 : nsGkAtoms::mozeditorbogusnode,
418 : nsGkAtoms::_true,
419 0 : eIgnoreCase);
420 : }
421 :
422 0 : static bool IsMozBR(nsIContent* aContent)
423 : {
424 0 : return aContent->IsHTMLElement(nsGkAtoms::br) && !IsContentBR(aContent);
425 : }
426 :
427 0 : static void ConvertToNativeNewlines(nsString& aString)
428 : {
429 : #if defined(XP_WIN)
430 : aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
431 : #endif
432 0 : }
433 :
434 0 : static void AppendString(nsAString& aString, nsIContent* aContent)
435 : {
436 0 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
437 : "aContent is not a text node!");
438 0 : const nsTextFragment* text = aContent->GetText();
439 0 : if (!text) {
440 0 : return;
441 : }
442 0 : text->AppendTo(aString);
443 : }
444 :
445 0 : static void AppendSubString(nsAString& aString, nsIContent* aContent,
446 : uint32_t aXPOffset, uint32_t aXPLength)
447 : {
448 0 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
449 : "aContent is not a text node!");
450 0 : const nsTextFragment* text = aContent->GetText();
451 0 : if (!text) {
452 0 : return;
453 : }
454 0 : text->AppendTo(aString, int32_t(aXPOffset), int32_t(aXPLength));
455 : }
456 :
457 : #if defined(XP_WIN)
458 : static uint32_t CountNewlinesInXPLength(nsIContent* aContent,
459 : uint32_t aXPLength)
460 : {
461 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
462 : "aContent is not a text node!");
463 : const nsTextFragment* text = aContent->GetText();
464 : if (!text) {
465 : return 0;
466 : }
467 : // For automated tests, we should abort on debug build.
468 : MOZ_ASSERT(aXPLength == UINT32_MAX || aXPLength <= text->GetLength(),
469 : "aXPLength is out-of-bounds");
470 : const uint32_t length = std::min(aXPLength, text->GetLength());
471 : uint32_t newlines = 0;
472 : for (uint32_t i = 0; i < length; ++i) {
473 : if (text->CharAt(i) == '\n') {
474 : ++newlines;
475 : }
476 : }
477 : return newlines;
478 : }
479 :
480 : static uint32_t CountNewlinesInNativeLength(nsIContent* aContent,
481 : uint32_t aNativeLength)
482 : {
483 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
484 : "aContent is not a text node!");
485 : const nsTextFragment* text = aContent->GetText();
486 : if (!text) {
487 : return 0;
488 : }
489 : // For automated tests, we should abort on debug build.
490 : MOZ_ASSERT(
491 : (aNativeLength == UINT32_MAX || aNativeLength <= text->GetLength() * 2),
492 : "aNativeLength is unexpected value");
493 : const uint32_t xpLength = text->GetLength();
494 : uint32_t newlines = 0;
495 : for (uint32_t i = 0, nativeOffset = 0;
496 : i < xpLength && nativeOffset < aNativeLength;
497 : ++i, ++nativeOffset) {
498 : // For automated tests, we should abort on debug build.
499 : MOZ_ASSERT(i < text->GetLength(), "i is out-of-bounds");
500 : if (text->CharAt(i) == '\n') {
501 : ++newlines;
502 : ++nativeOffset;
503 : }
504 : }
505 : return newlines;
506 : }
507 : #endif
508 :
509 : /* static */ uint32_t
510 0 : ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
511 : uint32_t aStartOffset,
512 : uint32_t aEndOffset)
513 : {
514 0 : MOZ_ASSERT(aEndOffset >= aStartOffset,
515 : "aEndOffset must be equals or larger than aStartOffset");
516 0 : if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
517 0 : return 0;
518 : }
519 0 : if (aStartOffset == aEndOffset) {
520 0 : return 0;
521 : }
522 0 : return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aEndOffset) -
523 0 : GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aStartOffset);
524 : }
525 :
526 : /* static */ uint32_t
527 0 : ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
528 : uint32_t aMaxLength)
529 : {
530 0 : if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
531 0 : return 0;
532 : }
533 0 : return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
534 : }
535 :
536 : /* static */ uint32_t
537 0 : ContentEventHandler::GetNativeTextLengthBefore(nsIContent* aContent,
538 : nsINode* aRootNode)
539 : {
540 0 : if (NS_WARN_IF(aContent->IsNodeOfType(nsINode::eTEXT))) {
541 0 : return 0;
542 : }
543 0 : return ShouldBreakLineBefore(aContent, aRootNode) ?
544 0 : GetBRLength(LINE_BREAK_TYPE_NATIVE) : 0;
545 : }
546 :
547 : /* static inline */ uint32_t
548 0 : ContentEventHandler::GetBRLength(LineBreakType aLineBreakType)
549 : {
550 : #if defined(XP_WIN)
551 : // Length of \r\n
552 : return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
553 : #else
554 0 : return 1;
555 : #endif
556 : }
557 :
558 : /* static */ uint32_t
559 0 : ContentEventHandler::GetTextLength(nsIContent* aContent,
560 : LineBreakType aLineBreakType,
561 : uint32_t aMaxLength)
562 : {
563 0 : MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
564 :
565 : uint32_t textLengthDifference =
566 : #if defined(XP_WIN)
567 : // On Windows, the length of a native newline ("\r\n") is twice the length
568 : // of the XP newline ("\n"), so XP length is equal to the length of the
569 : // native offset plus the number of newlines encountered in the string.
570 : (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
571 : CountNewlinesInXPLength(aContent, aMaxLength) : 0;
572 : #else
573 : // On other platforms, the native and XP newlines are the same.
574 0 : 0;
575 : #endif
576 :
577 0 : const nsTextFragment* text = aContent->GetText();
578 0 : if (!text) {
579 0 : return 0;
580 : }
581 0 : uint32_t length = std::min(text->GetLength(), aMaxLength);
582 0 : return length + textLengthDifference;
583 : }
584 :
585 0 : static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
586 : {
587 : #if defined(XP_WIN)
588 : // On Windows, the length of a native newline ("\r\n") is twice the length of
589 : // the XP newline ("\n"), so XP offset is equal to the length of the native
590 : // offset minus the number of newlines encountered in the string.
591 : return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
592 : #else
593 : // On other platforms, the native and XP newlines are the same.
594 0 : return aNativeOffset;
595 : #endif
596 : }
597 :
598 : /* static */ bool
599 0 : ContentEventHandler::ShouldBreakLineBefore(nsIContent* aContent,
600 : nsINode* aRootNode)
601 : {
602 : // We don't need to append linebreak at the start of the root element.
603 0 : if (aContent == aRootNode) {
604 0 : return false;
605 : }
606 :
607 : // If it's not an HTML element (including other markup language's elements),
608 : // we shouldn't insert like break before that for now. Becoming this is a
609 : // problem must be edge case. E.g., when ContentEventHandler is used with
610 : // MathML or SVG elements.
611 0 : if (!aContent->IsHTMLElement()) {
612 0 : return false;
613 : }
614 :
615 : // If the element is <br>, we need to check if the <br> is caused by web
616 : // content. Otherwise, i.e., it's caused by internal reason of Gecko,
617 : // it shouldn't be exposed as a line break to flatten text.
618 0 : if (aContent->IsHTMLElement(nsGkAtoms::br)) {
619 0 : return IsContentBR(aContent);
620 : }
621 :
622 : // Note that ideally, we should refer the style of the primary frame of
623 : // aContent for deciding if it's an inline. However, it's difficult
624 : // IMEContentObserver to notify IME of text change caused by style change.
625 : // Therefore, currently, we should check only from the tag for now.
626 0 : if (aContent->IsAnyOfHTMLElements(nsGkAtoms::a,
627 : nsGkAtoms::abbr,
628 : nsGkAtoms::acronym,
629 : nsGkAtoms::b,
630 : nsGkAtoms::bdi,
631 : nsGkAtoms::bdo,
632 : nsGkAtoms::big,
633 : nsGkAtoms::cite,
634 : nsGkAtoms::code,
635 : nsGkAtoms::data,
636 : nsGkAtoms::del,
637 : nsGkAtoms::dfn,
638 : nsGkAtoms::em,
639 : nsGkAtoms::font,
640 : nsGkAtoms::i,
641 : nsGkAtoms::ins,
642 : nsGkAtoms::kbd,
643 : nsGkAtoms::mark,
644 : nsGkAtoms::s,
645 : nsGkAtoms::samp,
646 : nsGkAtoms::small,
647 : nsGkAtoms::span,
648 : nsGkAtoms::strike,
649 : nsGkAtoms::strong,
650 : nsGkAtoms::sub,
651 : nsGkAtoms::sup,
652 : nsGkAtoms::time,
653 : nsGkAtoms::tt,
654 : nsGkAtoms::u,
655 : nsGkAtoms::var)) {
656 0 : return false;
657 : }
658 :
659 : // If the element is unknown element, we shouldn't insert line breaks before
660 : // it since unknown elements should be ignored.
661 0 : RefPtr<HTMLUnknownElement> unknownHTMLElement = do_QueryObject(aContent);
662 0 : return !unknownHTMLElement;
663 : }
664 :
665 : nsresult
666 0 : ContentEventHandler::GenerateFlatTextContent(nsIContent* aContent,
667 : nsString& aString,
668 : LineBreakType aLineBreakType)
669 : {
670 0 : MOZ_ASSERT(aString.IsEmpty());
671 :
672 0 : RefPtr<nsRange> range = new nsRange(mRootContent);
673 0 : ErrorResult rv;
674 0 : range->SelectNodeContents(*aContent, rv);
675 0 : if (NS_WARN_IF(rv.Failed())) {
676 0 : return rv.StealNSResult();
677 : }
678 0 : return GenerateFlatTextContent(range, aString, aLineBreakType);
679 : }
680 :
681 : nsresult
682 0 : ContentEventHandler::GenerateFlatTextContent(nsRange* aRange,
683 : nsString& aString,
684 : LineBreakType aLineBreakType)
685 : {
686 0 : MOZ_ASSERT(aString.IsEmpty());
687 :
688 0 : if (aRange->Collapsed()) {
689 0 : return NS_OK;
690 : }
691 :
692 0 : nsINode* startNode = aRange->GetStartContainer();
693 0 : nsINode* endNode = aRange->GetEndContainer();
694 0 : if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
695 0 : return NS_ERROR_FAILURE;
696 : }
697 :
698 0 : if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
699 0 : nsIContent* content = startNode->AsContent();
700 0 : AppendSubString(aString, content, aRange->StartOffset(),
701 0 : aRange->EndOffset() - aRange->StartOffset());
702 0 : ConvertToNativeNewlines(aString);
703 0 : return NS_OK;
704 : }
705 :
706 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
707 0 : nsresult rv = iter->Init(aRange);
708 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
709 0 : return rv;
710 : }
711 0 : for (; !iter->IsDone(); iter->Next()) {
712 0 : nsINode* node = iter->GetCurrentNode();
713 0 : if (NS_WARN_IF(!node)) {
714 0 : break;
715 : }
716 0 : if (!node->IsContent()) {
717 0 : continue;
718 : }
719 0 : nsIContent* content = node->AsContent();
720 :
721 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
722 0 : if (content == startNode) {
723 0 : AppendSubString(aString, content, aRange->StartOffset(),
724 0 : content->TextLength() - aRange->StartOffset());
725 0 : } else if (content == endNode) {
726 0 : AppendSubString(aString, content, 0, aRange->EndOffset());
727 : } else {
728 0 : AppendString(aString, content);
729 : }
730 0 : } else if (ShouldBreakLineBefore(content, mRootContent)) {
731 0 : aString.Append(char16_t('\n'));
732 : }
733 : }
734 0 : if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
735 0 : ConvertToNativeNewlines(aString);
736 : }
737 0 : return NS_OK;
738 : }
739 :
740 : static FontRange*
741 0 : AppendFontRange(nsTArray<FontRange>& aFontRanges, uint32_t aBaseOffset)
742 : {
743 0 : FontRange* fontRange = aFontRanges.AppendElement();
744 0 : fontRange->mStartOffset = aBaseOffset;
745 0 : return fontRange;
746 : }
747 :
748 : /* static */ uint32_t
749 0 : ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
750 : uint32_t aXPStartOffset,
751 : uint32_t aXPEndOffset,
752 : LineBreakType aLineBreakType)
753 : {
754 0 : MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
755 :
756 0 : return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
757 : GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
758 0 : aXPEndOffset - aXPStartOffset;
759 : }
760 :
761 : /* static */ void
762 0 : ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
763 : nsIContent* aContent,
764 : int32_t aBaseOffset,
765 : int32_t aXPStartOffset,
766 : int32_t aXPEndOffset,
767 : LineBreakType aLineBreakType)
768 : {
769 0 : MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
770 :
771 0 : nsIFrame* frame = aContent->GetPrimaryFrame();
772 0 : if (!frame) {
773 : // It is a non-rendered content, create an empty range for it.
774 0 : AppendFontRange(aFontRanges, aBaseOffset);
775 0 : return;
776 : }
777 :
778 0 : int32_t baseOffset = aBaseOffset;
779 : #ifdef DEBUG
780 : {
781 0 : nsTextFrame* text = do_QueryFrame(frame);
782 0 : MOZ_ASSERT(text, "Not a text frame");
783 : }
784 : #endif
785 0 : auto* curr = static_cast<nsTextFrame*>(frame);
786 0 : while (curr) {
787 0 : int32_t frameXPStart = std::max(curr->GetContentOffset(), aXPStartOffset);
788 0 : int32_t frameXPEnd = std::min(curr->GetContentEnd(), aXPEndOffset);
789 0 : if (frameXPStart >= frameXPEnd) {
790 0 : curr = curr->GetNextContinuation();
791 0 : continue;
792 : }
793 :
794 0 : gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
795 0 : gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
796 :
797 0 : nsTextFrame* next = nullptr;
798 0 : if (frameXPEnd < aXPEndOffset) {
799 0 : next = curr->GetNextContinuation();
800 0 : while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
801 0 : frameXPEnd = std::min(next->GetContentEnd(), aXPEndOffset);
802 0 : next = frameXPEnd < aXPEndOffset ?
803 : next->GetNextContinuation() : nullptr;
804 : }
805 : }
806 :
807 : gfxTextRun::Range skipRange(iter.ConvertOriginalToSkipped(frameXPStart),
808 0 : iter.ConvertOriginalToSkipped(frameXPEnd));
809 0 : gfxTextRun::GlyphRunIterator runIter(textRun, skipRange);
810 0 : int32_t lastXPEndOffset = frameXPStart;
811 0 : while (runIter.NextRun()) {
812 0 : gfxFont* font = runIter.GetGlyphRun()->mFont.get();
813 : int32_t startXPOffset =
814 0 : iter.ConvertSkippedToOriginal(runIter.GetStringStart());
815 : // It is possible that the first glyph run has exceeded the frame,
816 : // because the whole frame is filled by skipped chars.
817 0 : if (startXPOffset >= frameXPEnd) {
818 0 : break;
819 : }
820 :
821 0 : if (startXPOffset > lastXPEndOffset) {
822 : // Create range for skipped leading chars.
823 0 : AppendFontRange(aFontRanges, baseOffset);
824 0 : baseOffset += GetTextLengthInRange(
825 0 : aContent, lastXPEndOffset, startXPOffset, aLineBreakType);
826 0 : lastXPEndOffset = startXPOffset;
827 : }
828 :
829 0 : FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
830 0 : fontRange->mFontName = font->GetName();
831 0 : fontRange->mFontSize = font->GetAdjustedSize();
832 :
833 : // The converted original offset may exceed the range,
834 : // hence we need to clamp it.
835 : int32_t endXPOffset =
836 0 : iter.ConvertSkippedToOriginal(runIter.GetStringEnd());
837 0 : endXPOffset = std::min(frameXPEnd, endXPOffset);
838 0 : baseOffset += GetTextLengthInRange(aContent, startXPOffset, endXPOffset,
839 0 : aLineBreakType);
840 0 : lastXPEndOffset = endXPOffset;
841 : }
842 0 : if (lastXPEndOffset < frameXPEnd) {
843 : // Create range for skipped trailing chars. It also handles case
844 : // that the whole frame contains only skipped chars.
845 0 : AppendFontRange(aFontRanges, baseOffset);
846 0 : baseOffset += GetTextLengthInRange(
847 0 : aContent, lastXPEndOffset, frameXPEnd, aLineBreakType);
848 : }
849 :
850 0 : curr = next;
851 : }
852 : }
853 :
854 : nsresult
855 0 : ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
856 : FontRangeArray& aFontRanges,
857 : uint32_t& aLength,
858 : LineBreakType aLineBreakType)
859 : {
860 0 : MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
861 :
862 0 : if (aRange->Collapsed()) {
863 0 : return NS_OK;
864 : }
865 :
866 0 : nsINode* startNode = aRange->GetStartContainer();
867 0 : nsINode* endNode = aRange->GetEndContainer();
868 0 : if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
869 0 : return NS_ERROR_FAILURE;
870 : }
871 :
872 : // baseOffset is the flattened offset of each content node.
873 0 : int32_t baseOffset = 0;
874 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
875 0 : nsresult rv = iter->Init(aRange);
876 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
877 0 : return rv;
878 : }
879 0 : for (; !iter->IsDone(); iter->Next()) {
880 0 : nsINode* node = iter->GetCurrentNode();
881 0 : if (NS_WARN_IF(!node)) {
882 0 : break;
883 : }
884 0 : if (!node->IsContent()) {
885 0 : continue;
886 : }
887 0 : nsIContent* content = node->AsContent();
888 :
889 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
890 0 : int32_t startOffset = content != startNode ? 0 : aRange->StartOffset();
891 0 : int32_t endOffset = content != endNode ?
892 0 : content->TextLength() : aRange->EndOffset();
893 : AppendFontRanges(aFontRanges, content, baseOffset,
894 0 : startOffset, endOffset, aLineBreakType);
895 0 : baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
896 0 : aLineBreakType);
897 0 : } else if (ShouldBreakLineBefore(content, mRootContent)) {
898 0 : if (aFontRanges.IsEmpty()) {
899 0 : MOZ_ASSERT(baseOffset == 0);
900 0 : FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
901 0 : nsIFrame* frame = content->GetPrimaryFrame();
902 0 : if (frame) {
903 0 : const nsFont& font = frame->GetParent()->StyleFont()->mFont;
904 0 : const FontFamilyList& fontList = font.fontlist;
905 0 : const FontFamilyName& fontName = fontList.IsEmpty() ?
906 : FontFamilyName(fontList.GetDefaultFontType()) :
907 0 : fontList.GetFontlist()[0];
908 0 : fontName.AppendToString(fontRange->mFontName, false);
909 0 : fontRange->mFontSize =
910 0 : frame->PresContext()->AppUnitsToDevPixels(font.size);
911 : }
912 : }
913 0 : baseOffset += GetBRLength(aLineBreakType);
914 : }
915 : }
916 :
917 0 : aLength = baseOffset;
918 0 : return NS_OK;
919 : }
920 :
921 : nsresult
922 0 : ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
923 : bool aForward,
924 : uint32_t* aXPOffset)
925 : {
926 : // XXX This method assumes that the frame boundaries must be cluster
927 : // boundaries. It's false, but no problem now, maybe.
928 0 : if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
929 0 : *aXPOffset == 0 || *aXPOffset == aContent->TextLength()) {
930 0 : return NS_OK;
931 : }
932 :
933 0 : NS_ASSERTION(*aXPOffset <= aContent->TextLength(),
934 : "offset is out of range.");
935 :
936 0 : RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
937 : int32_t offsetInFrame;
938 : CaretAssociationHint hint =
939 0 : aForward ? CARET_ASSOCIATE_BEFORE : CARET_ASSOCIATE_AFTER;
940 0 : nsIFrame* frame = fs->GetFrameForNodeOffset(aContent, int32_t(*aXPOffset),
941 0 : hint, &offsetInFrame);
942 0 : if (frame) {
943 : int32_t startOffset, endOffset;
944 0 : nsresult rv = frame->GetOffsets(startOffset, endOffset);
945 0 : NS_ENSURE_SUCCESS(rv, rv);
946 0 : if (*aXPOffset == static_cast<uint32_t>(startOffset) ||
947 0 : *aXPOffset == static_cast<uint32_t>(endOffset)) {
948 0 : return NS_OK;
949 : }
950 0 : if (!frame->IsTextFrame()) {
951 0 : return NS_ERROR_FAILURE;
952 : }
953 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
954 0 : int32_t newOffsetInFrame = *aXPOffset - startOffset;
955 0 : newOffsetInFrame += aForward ? -1 : 1;
956 : // PeekOffsetCharacter() should respect cluster but ignore user-select
957 : // style. If it returns "FOUND", we should use the result. Otherwise,
958 : // we shouldn't use the result because the offset was moved to reversed
959 : // direction.
960 0 : nsTextFrame::PeekOffsetCharacterOptions options;
961 0 : options.mRespectClusters = true;
962 0 : options.mIgnoreUserStyleAll = true;
963 0 : if (textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame,
964 0 : options) == nsIFrame::FOUND) {
965 0 : *aXPOffset = startOffset + newOffsetInFrame;
966 0 : return NS_OK;
967 : }
968 : }
969 :
970 : // If the frame isn't available, we only can check surrogate pair...
971 0 : const nsTextFragment* text = aContent->GetText();
972 0 : NS_ENSURE_TRUE(text, NS_ERROR_FAILURE);
973 0 : if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
974 0 : NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1))) {
975 0 : *aXPOffset += aForward ? 1 : -1;
976 : }
977 0 : return NS_OK;
978 : }
979 :
980 : nsresult
981 0 : ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
982 : uint32_t aOffset,
983 : uint32_t aLength,
984 : LineBreakType aLineBreakType,
985 : bool aExpandToClusterBoundaries,
986 : uint32_t* aNewOffset,
987 : nsIContent** aLastTextNode)
988 : {
989 0 : if (aNewOffset) {
990 0 : *aNewOffset = aOffset;
991 : }
992 0 : if (aLastTextNode) {
993 0 : *aLastTextNode = nullptr;
994 : }
995 :
996 : // Special case like <br contenteditable>
997 0 : if (!mRootContent->HasChildren()) {
998 0 : nsresult rv = aRange->CollapseTo(mRootContent, 0);
999 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1000 0 : return rv;
1001 : }
1002 : }
1003 :
1004 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
1005 0 : nsresult rv = iter->Init(mRootContent);
1006 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1007 0 : return rv;
1008 : }
1009 :
1010 0 : uint32_t offset = 0;
1011 0 : uint32_t endOffset = aOffset + aLength;
1012 0 : bool startSet = false;
1013 0 : for (; !iter->IsDone(); iter->Next()) {
1014 0 : nsINode* node = iter->GetCurrentNode();
1015 0 : if (NS_WARN_IF(!node)) {
1016 0 : break;
1017 : }
1018 : // FYI: mRootContent shouldn't cause any text. So, we can skip it simply.
1019 0 : if (node == mRootContent || !node->IsContent()) {
1020 0 : continue;
1021 : }
1022 0 : nsIContent* content = node->AsContent();
1023 :
1024 0 : if (aLastTextNode && content->IsNodeOfType(nsINode::eTEXT)) {
1025 0 : NS_IF_RELEASE(*aLastTextNode);
1026 0 : NS_ADDREF(*aLastTextNode = content);
1027 : }
1028 :
1029 : uint32_t textLength =
1030 0 : content->IsNodeOfType(nsINode::eTEXT) ?
1031 : GetTextLength(content, aLineBreakType) :
1032 0 : (ShouldBreakLineBefore(content, mRootContent) ?
1033 0 : GetBRLength(aLineBreakType) : 0);
1034 0 : if (!textLength) {
1035 0 : continue;
1036 : }
1037 :
1038 : // When the start offset is in between accumulated offset and the last
1039 : // offset of the node, the node is the start node of the range.
1040 0 : if (!startSet && aOffset <= offset + textLength) {
1041 0 : nsINode* startNode = nullptr;
1042 0 : int32_t startNodeOffset = -1;
1043 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
1044 : // Rule #1.1: [textNode or text[Node or textNode[
1045 0 : uint32_t xpOffset = aOffset - offset;
1046 0 : if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
1047 0 : xpOffset = ConvertToXPOffset(content, xpOffset);
1048 : }
1049 :
1050 0 : if (aExpandToClusterBoundaries) {
1051 0 : uint32_t oldXPOffset = xpOffset;
1052 0 : rv = ExpandToClusterBoundary(content, false, &xpOffset);
1053 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1054 0 : return rv;
1055 : }
1056 0 : if (aNewOffset) {
1057 : // This is correct since a cluster shouldn't include line break.
1058 0 : *aNewOffset -= (oldXPOffset - xpOffset);
1059 : }
1060 : }
1061 0 : startNode = content;
1062 0 : startNodeOffset = static_cast<int32_t>(xpOffset);
1063 0 : } else if (aOffset < offset + textLength) {
1064 : // Rule #1.2 [<element>
1065 0 : startNode = content->GetParent();
1066 0 : if (NS_WARN_IF(!startNode)) {
1067 0 : return NS_ERROR_FAILURE;
1068 : }
1069 0 : startNodeOffset = startNode->IndexOf(content);
1070 0 : if (NS_WARN_IF(startNodeOffset == -1)) {
1071 : // The content is being removed from the parent!
1072 0 : return NS_ERROR_FAILURE;
1073 : }
1074 0 : } else if (!content->HasChildren()) {
1075 : // Rule #1.3: <element/>[
1076 0 : startNode = content->GetParent();
1077 0 : if (NS_WARN_IF(!startNode)) {
1078 0 : return NS_ERROR_FAILURE;
1079 : }
1080 0 : startNodeOffset = startNode->IndexOf(content) + 1;
1081 0 : if (NS_WARN_IF(startNodeOffset == 0)) {
1082 : // The content is being removed from the parent!
1083 0 : return NS_ERROR_FAILURE;
1084 : }
1085 : } else {
1086 : // Rule #1.4: <element>[
1087 0 : startNode = content;
1088 0 : startNodeOffset = 0;
1089 : }
1090 0 : NS_ASSERTION(startNode, "startNode must not be nullptr");
1091 0 : NS_ASSERTION(startNodeOffset >= 0,
1092 : "startNodeOffset must not be negative");
1093 0 : rv = aRange->SetStart(startNode, startNodeOffset);
1094 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1095 0 : return rv;
1096 : }
1097 0 : startSet = true;
1098 :
1099 0 : if (!aLength) {
1100 0 : rv = aRange->SetEnd(startNode, startNodeOffset);
1101 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1102 0 : return rv;
1103 : }
1104 0 : return NS_OK;
1105 : }
1106 : }
1107 :
1108 : // When the end offset is in the content, the node is the end node of the
1109 : // range.
1110 0 : if (endOffset <= offset + textLength) {
1111 0 : MOZ_ASSERT(startSet,
1112 : "The start of the range should've been set already");
1113 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
1114 : // Rule #2.1: ]textNode or text]Node or textNode]
1115 0 : uint32_t xpOffset = endOffset - offset;
1116 0 : if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
1117 0 : uint32_t xpOffsetCurrent = ConvertToXPOffset(content, xpOffset);
1118 0 : if (xpOffset && GetBRLength(aLineBreakType) > 1) {
1119 0 : MOZ_ASSERT(GetBRLength(aLineBreakType) == 2);
1120 0 : uint32_t xpOffsetPre = ConvertToXPOffset(content, xpOffset - 1);
1121 : // If previous character's XP offset is same as current character's,
1122 : // it means that the end offset is between \r and \n. So, the
1123 : // range end should be after the \n.
1124 0 : if (xpOffsetPre == xpOffsetCurrent) {
1125 0 : xpOffset = xpOffsetCurrent + 1;
1126 : } else {
1127 0 : xpOffset = xpOffsetCurrent;
1128 : }
1129 : }
1130 : }
1131 0 : if (aExpandToClusterBoundaries) {
1132 0 : rv = ExpandToClusterBoundary(content, true, &xpOffset);
1133 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1134 0 : return rv;
1135 : }
1136 : }
1137 0 : NS_ASSERTION(xpOffset <= INT32_MAX,
1138 : "The end node offset is too large");
1139 0 : rv = aRange->SetEnd(content, static_cast<int32_t>(xpOffset));
1140 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1141 0 : return rv;
1142 : }
1143 0 : return NS_OK;
1144 : }
1145 :
1146 0 : if (endOffset == offset) {
1147 : // Rule #2.2: ]<element>
1148 : // NOTE: Please don't crash on release builds because it must be
1149 : // overreaction but we shouldn't allow this bug when some
1150 : // automated tests find this.
1151 0 : MOZ_ASSERT(false, "This case should've already been handled at "
1152 : "the last node which caused some text");
1153 : return NS_ERROR_FAILURE;
1154 : }
1155 :
1156 0 : if (content->HasChildren() &&
1157 0 : ShouldBreakLineBefore(content, mRootContent)) {
1158 : // Rule #2.3: </element>]
1159 0 : rv = aRange->SetEnd(content, 0);
1160 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1161 0 : return rv;
1162 : }
1163 0 : return NS_OK;
1164 : }
1165 :
1166 : // Rule #2.4: <element/>]
1167 0 : nsINode* endNode = content->GetParent();
1168 0 : if (NS_WARN_IF(!endNode)) {
1169 0 : return NS_ERROR_FAILURE;
1170 : }
1171 0 : int32_t indexInParent = endNode->IndexOf(content);
1172 0 : if (NS_WARN_IF(indexInParent == -1)) {
1173 : // The content is being removed from the parent!
1174 0 : return NS_ERROR_FAILURE;
1175 : }
1176 0 : rv = aRange->SetEnd(endNode, indexInParent + 1);
1177 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1178 0 : return rv;
1179 : }
1180 0 : return NS_OK;
1181 : }
1182 :
1183 0 : offset += textLength;
1184 : }
1185 :
1186 0 : if (!startSet) {
1187 0 : MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
1188 0 : if (!offset) {
1189 : // Rule #1.5: <root>[</root>
1190 : // When there are no nodes causing text, the start of the DOM range
1191 : // should be start of the root node since clicking on such editor (e.g.,
1192 : // <div contenteditable><span></span></div>) sets caret to the start of
1193 : // the editor (i.e., before <span> in the example).
1194 0 : rv = aRange->SetStart(mRootContent, 0);
1195 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1196 0 : return rv;
1197 : }
1198 0 : if (!aLength) {
1199 0 : rv = aRange->SetEnd(mRootContent, 0);
1200 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1201 0 : return rv;
1202 : }
1203 0 : return NS_OK;
1204 : }
1205 : } else {
1206 : // Rule #1.5: [</root>
1207 0 : rv = aRange->SetStart(mRootContent,
1208 0 : static_cast<int32_t>(mRootContent->GetChildCount()));
1209 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1210 0 : return rv;
1211 : }
1212 : }
1213 0 : if (aNewOffset) {
1214 0 : *aNewOffset = offset;
1215 : }
1216 : }
1217 : // Rule #2.5: ]</root>
1218 0 : rv = aRange->SetEnd(mRootContent,
1219 0 : static_cast<int32_t>(mRootContent->GetChildCount()));
1220 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1221 0 : return rv;
1222 : }
1223 0 : return NS_OK;
1224 : }
1225 :
1226 : /* static */ LineBreakType
1227 0 : ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent* aEvent)
1228 : {
1229 0 : return GetLineBreakType(aEvent->mUseNativeLineBreak);
1230 : }
1231 :
1232 : /* static */ LineBreakType
1233 0 : ContentEventHandler::GetLineBreakType(WidgetSelectionEvent* aEvent)
1234 : {
1235 0 : return GetLineBreakType(aEvent->mUseNativeLineBreak);
1236 : }
1237 :
1238 : /* static */ LineBreakType
1239 0 : ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak)
1240 : {
1241 0 : return aUseNativeLineBreak ?
1242 0 : LINE_BREAK_TYPE_NATIVE : LINE_BREAK_TYPE_XP;
1243 : }
1244 :
1245 : nsresult
1246 0 : ContentEventHandler::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
1247 : {
1248 0 : switch (aEvent->mMessage) {
1249 : case eQuerySelectedText:
1250 0 : return OnQuerySelectedText(aEvent);
1251 : case eQueryTextContent:
1252 0 : return OnQueryTextContent(aEvent);
1253 : case eQueryCaretRect:
1254 0 : return OnQueryCaretRect(aEvent);
1255 : case eQueryTextRect:
1256 0 : return OnQueryTextRect(aEvent);
1257 : case eQueryTextRectArray:
1258 0 : return OnQueryTextRectArray(aEvent);
1259 : case eQueryEditorRect:
1260 0 : return OnQueryEditorRect(aEvent);
1261 : case eQueryContentState:
1262 0 : return OnQueryContentState(aEvent);
1263 : case eQuerySelectionAsTransferable:
1264 0 : return OnQuerySelectionAsTransferable(aEvent);
1265 : case eQueryCharacterAtPoint:
1266 0 : return OnQueryCharacterAtPoint(aEvent);
1267 : case eQueryDOMWidgetHittest:
1268 0 : return OnQueryDOMWidgetHittest(aEvent);
1269 : default:
1270 0 : return NS_ERROR_NOT_IMPLEMENTED;
1271 : }
1272 : return NS_OK;
1273 : }
1274 :
1275 : // Similar to nsFrameSelection::GetFrameForNodeOffset,
1276 : // but this is more flexible for OnQueryTextRect to use
1277 0 : static nsresult GetFrameForTextRect(nsINode* aNode,
1278 : int32_t aNodeOffset,
1279 : bool aHint,
1280 : nsIFrame** aReturnFrame)
1281 : {
1282 0 : NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT),
1283 : NS_ERROR_UNEXPECTED);
1284 0 : nsIContent* content = static_cast<nsIContent*>(aNode);
1285 0 : nsIFrame* frame = content->GetPrimaryFrame();
1286 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1287 0 : int32_t childNodeOffset = 0;
1288 0 : return frame->GetChildFrameContainingOffset(aNodeOffset, aHint,
1289 0 : &childNodeOffset, aReturnFrame);
1290 : }
1291 :
1292 : nsresult
1293 0 : ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
1294 : {
1295 0 : nsresult rv = Init(aEvent);
1296 0 : if (NS_FAILED(rv)) {
1297 0 : return rv;
1298 : }
1299 :
1300 0 : if (!mFirstSelectedRange) {
1301 0 : MOZ_ASSERT(aEvent->mInput.mSelectionType != SelectionType::eNormal);
1302 0 : MOZ_ASSERT(aEvent->mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND);
1303 0 : MOZ_ASSERT(aEvent->mReply.mString.IsEmpty());
1304 0 : MOZ_ASSERT(!aEvent->mReply.mHasSelection);
1305 0 : aEvent->mSucceeded = true;
1306 0 : return NS_OK;
1307 : }
1308 :
1309 0 : nsINode* const startNode = mFirstSelectedRange->GetStartContainer();
1310 0 : nsINode* const endNode = mFirstSelectedRange->GetEndContainer();
1311 :
1312 : // Make sure the selection is within the root content range.
1313 0 : if (!nsContentUtils::ContentIsDescendantOf(startNode, mRootContent) ||
1314 0 : !nsContentUtils::ContentIsDescendantOf(endNode, mRootContent)) {
1315 0 : return NS_ERROR_NOT_AVAILABLE;
1316 : }
1317 :
1318 0 : NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
1319 : "The reply string must be empty");
1320 :
1321 0 : LineBreakType lineBreakType = GetLineBreakType(aEvent);
1322 0 : rv = GetStartOffset(mFirstSelectedRange,
1323 0 : &aEvent->mReply.mOffset, lineBreakType);
1324 0 : NS_ENSURE_SUCCESS(rv, rv);
1325 :
1326 0 : nsCOMPtr<nsINode> anchorNode, focusNode;
1327 0 : int32_t anchorOffset = 0, focusOffset = 0;
1328 0 : if (mSelection->RangeCount()) {
1329 : // If there is only one selection range, the anchor/focus node and offset
1330 : // are the information of the range. Therefore, we have the direction
1331 : // information.
1332 0 : if (mSelection->RangeCount() == 1) {
1333 0 : anchorNode = mSelection->GetAnchorNode();
1334 0 : focusNode = mSelection->GetFocusNode();
1335 0 : if (NS_WARN_IF(!anchorNode) || NS_WARN_IF(!focusNode)) {
1336 0 : return NS_ERROR_FAILURE;
1337 : }
1338 0 : anchorOffset = static_cast<int32_t>(mSelection->AnchorOffset());
1339 0 : focusOffset = static_cast<int32_t>(mSelection->FocusOffset());
1340 0 : if (NS_WARN_IF(anchorOffset < 0) || NS_WARN_IF(focusOffset < 0)) {
1341 0 : return NS_ERROR_FAILURE;
1342 : }
1343 :
1344 0 : int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
1345 0 : focusNode, focusOffset);
1346 0 : aEvent->mReply.mReversed = compare > 0;
1347 : }
1348 : // However, if there are 2 or more selection ranges, we have no information
1349 : // of that.
1350 : else {
1351 0 : aEvent->mReply.mReversed = false;
1352 : }
1353 :
1354 0 : if (!mFirstSelectedRange->Collapsed()) {
1355 0 : rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString,
1356 0 : lineBreakType);
1357 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1358 0 : return rv;
1359 : }
1360 : } else {
1361 0 : aEvent->mReply.mString.Truncate();
1362 : }
1363 : } else {
1364 0 : NS_ASSERTION(mFirstSelectedRange->Collapsed(),
1365 : "When mSelection doesn't have selection, mFirstSelectedRange must be "
1366 : "collapsed");
1367 0 : anchorNode = focusNode = mFirstSelectedRange->GetStartContainer();
1368 0 : if (NS_WARN_IF(!anchorNode)) {
1369 0 : return NS_ERROR_FAILURE;
1370 : }
1371 0 : anchorOffset = focusOffset =
1372 0 : static_cast<int32_t>(mFirstSelectedRange->StartOffset());
1373 0 : if (NS_WARN_IF(anchorOffset < 0)) {
1374 0 : return NS_ERROR_FAILURE;
1375 : }
1376 :
1377 0 : aEvent->mReply.mReversed = false;
1378 0 : aEvent->mReply.mString.Truncate();
1379 : }
1380 :
1381 :
1382 0 : nsIFrame* frame = nullptr;
1383 0 : rv = GetFrameForTextRect(focusNode, focusOffset, true, &frame);
1384 0 : if (NS_SUCCEEDED(rv) && frame) {
1385 0 : aEvent->mReply.mWritingMode = frame->GetWritingMode();
1386 : } else {
1387 0 : aEvent->mReply.mWritingMode = WritingMode();
1388 : }
1389 :
1390 0 : aEvent->mSucceeded = true;
1391 0 : return NS_OK;
1392 : }
1393 :
1394 : nsresult
1395 0 : ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent)
1396 : {
1397 0 : nsresult rv = Init(aEvent);
1398 0 : if (NS_FAILED(rv)) {
1399 0 : return rv;
1400 : }
1401 :
1402 0 : NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
1403 : "The reply string must be empty");
1404 :
1405 0 : LineBreakType lineBreakType = GetLineBreakType(aEvent);
1406 :
1407 0 : RefPtr<nsRange> range = new nsRange(mRootContent);
1408 0 : rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
1409 : aEvent->mInput.mLength, lineBreakType, false,
1410 0 : &aEvent->mReply.mOffset);
1411 0 : NS_ENSURE_SUCCESS(rv, rv);
1412 :
1413 0 : rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
1414 0 : NS_ENSURE_SUCCESS(rv, rv);
1415 :
1416 0 : if (aEvent->mWithFontRanges) {
1417 : uint32_t fontRangeLength;
1418 0 : rv = GenerateFlatFontRanges(range, aEvent->mReply.mFontRanges,
1419 0 : fontRangeLength, lineBreakType);
1420 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1421 0 : return rv;
1422 : }
1423 :
1424 0 : MOZ_ASSERT(fontRangeLength == aEvent->mReply.mString.Length(),
1425 : "Font ranges doesn't match the string");
1426 : }
1427 :
1428 0 : aEvent->mSucceeded = true;
1429 :
1430 0 : return NS_OK;
1431 : }
1432 :
1433 : void
1434 0 : ContentEventHandler::EnsureNonEmptyRect(nsRect& aRect) const
1435 : {
1436 : // See the comment in ContentEventHandler.h why this doesn't set them to
1437 : // one device pixel.
1438 0 : aRect.height = std::max(1, aRect.height);
1439 0 : aRect.width = std::max(1, aRect.width);
1440 0 : }
1441 :
1442 : void
1443 0 : ContentEventHandler::EnsureNonEmptyRect(LayoutDeviceIntRect& aRect) const
1444 : {
1445 0 : aRect.height = std::max(1, aRect.height);
1446 0 : aRect.width = std::max(1, aRect.width);
1447 0 : }
1448 :
1449 : ContentEventHandler::NodePosition
1450 0 : ContentEventHandler::GetNodePositionHavingFlatText(
1451 : const NodePosition& aNodePosition)
1452 : {
1453 : return GetNodePositionHavingFlatText(aNodePosition.mNode,
1454 0 : aNodePosition.mOffset);
1455 : }
1456 :
1457 : ContentEventHandler::NodePosition
1458 0 : ContentEventHandler::GetNodePositionHavingFlatText(nsINode* aNode,
1459 : int32_t aNodeOffset)
1460 : {
1461 0 : if (aNode->IsNodeOfType(nsINode::eTEXT)) {
1462 0 : return NodePosition(aNode, aNodeOffset);
1463 : }
1464 :
1465 0 : int32_t childCount = static_cast<int32_t>(aNode->GetChildCount());
1466 :
1467 : // If it's a empty element node, returns itself.
1468 0 : if (!childCount) {
1469 0 : MOZ_ASSERT(!aNodeOffset || aNodeOffset == 1);
1470 0 : return NodePosition(aNode, aNodeOffset);
1471 : }
1472 :
1473 : // If there is a node at given position, return the start of it.
1474 0 : if (aNodeOffset < childCount) {
1475 0 : return NodePosition(aNode->GetChildAt(aNodeOffset), 0);
1476 : }
1477 :
1478 : // If the offset represents "after" the node, we need to return the last
1479 : // child of it. For example, if a range is |<p>[<br>]</p>|, then, the
1480 : // end point is {<p>, 1}. In such case, callers need the <br> node.
1481 0 : if (aNodeOffset == childCount) {
1482 0 : NodePosition result;
1483 0 : result.mNode = aNode->GetChildAt(childCount - 1);
1484 0 : result.mOffset = result.mNode->IsNodeOfType(nsINode::eTEXT) ?
1485 0 : static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
1486 : }
1487 :
1488 0 : NS_WARNING("aNodeOffset is invalid value");
1489 0 : return NodePosition();
1490 : }
1491 :
1492 : ContentEventHandler::FrameAndNodeOffset
1493 0 : ContentEventHandler::GetFirstFrameInRangeForTextRect(nsRange* aRange)
1494 : {
1495 0 : NodePosition nodePosition;
1496 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
1497 0 : for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
1498 0 : nsINode* node = iter->GetCurrentNode();
1499 0 : if (NS_WARN_IF(!node)) {
1500 0 : break;
1501 : }
1502 :
1503 0 : if (!node->IsContent()) {
1504 0 : continue;
1505 : }
1506 :
1507 0 : if (node->IsNodeOfType(nsINode::eTEXT)) {
1508 : // If the range starts at the end of a text node, we need to find
1509 : // next node which causes text.
1510 : int32_t offsetInNode =
1511 0 : node == aRange->GetStartContainer() ? aRange->StartOffset() : 0;
1512 0 : if (static_cast<uint32_t>(offsetInNode) < node->Length()) {
1513 0 : nodePosition.mNode = node;
1514 0 : nodePosition.mOffset = offsetInNode;
1515 0 : break;
1516 : }
1517 0 : continue;
1518 : }
1519 :
1520 : // If the element node causes a line break before it, it's the first
1521 : // node causing text.
1522 0 : if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
1523 0 : IsMozBR(node->AsContent())) {
1524 0 : nodePosition.mNode = node;
1525 0 : nodePosition.mOffset = 0;
1526 : }
1527 : }
1528 :
1529 0 : if (!nodePosition.IsValid()) {
1530 0 : return FrameAndNodeOffset();
1531 : }
1532 :
1533 0 : nsIFrame* firstFrame = nullptr;
1534 0 : GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
1535 0 : true, &firstFrame);
1536 0 : return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
1537 : }
1538 :
1539 : ContentEventHandler::FrameAndNodeOffset
1540 0 : ContentEventHandler::GetLastFrameInRangeForTextRect(nsRange* aRange)
1541 : {
1542 0 : NodePosition nodePosition;
1543 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
1544 0 : iter->Init(aRange);
1545 :
1546 0 : nsINode* endNode = aRange->GetEndContainer();
1547 0 : uint32_t endOffset = static_cast<uint32_t>(aRange->EndOffset());
1548 : // If the end point is start of a text node or specified by its parent and
1549 : // index, the node shouldn't be included into the range. For example,
1550 : // with this case, |<p>abc[<br>]def</p>|, the range ends at 3rd children of
1551 : // <p> (see the range creation rules, "2.4. Cases: <element/>]"). This causes
1552 : // following frames:
1553 : // +----+-----+
1554 : // | abc|[<br>|
1555 : // +----+-----+
1556 : // +----+
1557 : // |]def|
1558 : // +----+
1559 : // So, if this method includes the 2nd text frame's rect to its result, the
1560 : // caller will return too tall rect which includes 2 lines in this case isn't
1561 : // expected by native IME (e.g., popup of IME will be positioned at bottom
1562 : // of "d" instead of right-bottom of "c"). Therefore, this method shouldn't
1563 : // include the last frame when its content isn't really in aRange.
1564 0 : nsINode* nextNodeOfRangeEnd = nullptr;
1565 0 : if (endNode->IsNodeOfType(nsINode::eTEXT)) {
1566 : // Don't set nextNodeOfRangeEnd to the start node of aRange because if
1567 : // endNode is same as start node of the range, the text node shouldn't be
1568 : // next of range end even if the offset is 0. This could occur with empty
1569 : // text node.
1570 0 : if (!endOffset && aRange->GetStartContainer() != endNode) {
1571 0 : nextNodeOfRangeEnd = endNode;
1572 : }
1573 0 : } else if (endOffset < endNode->GetChildCount()) {
1574 0 : nextNodeOfRangeEnd = endNode->GetChildAt(endOffset);
1575 : }
1576 :
1577 0 : for (iter->Last(); !iter->IsDone(); iter->Prev()) {
1578 0 : nsINode* node = iter->GetCurrentNode();
1579 0 : if (NS_WARN_IF(!node)) {
1580 0 : break;
1581 : }
1582 :
1583 0 : if (!node->IsContent() || node == nextNodeOfRangeEnd) {
1584 0 : continue;
1585 : }
1586 :
1587 0 : if (node->IsNodeOfType(nsINode::eTEXT)) {
1588 0 : nodePosition.mNode = node;
1589 0 : if (node == aRange->GetEndContainer()) {
1590 0 : nodePosition.mOffset = aRange->EndOffset();
1591 : } else {
1592 0 : nodePosition.mOffset = node->Length();
1593 : }
1594 : // If the text node is empty or the last node of the range but the index
1595 : // is 0, we should store current position but continue looking for
1596 : // previous node (If there are no nodes before it, we should use current
1597 : // node position for returning its frame).
1598 0 : if (!nodePosition.mOffset) {
1599 0 : continue;
1600 : }
1601 0 : break;
1602 : }
1603 :
1604 0 : if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
1605 0 : IsMozBR(node->AsContent())) {
1606 0 : nodePosition.mNode = node;
1607 0 : nodePosition.mOffset = 0;
1608 0 : break;
1609 : }
1610 : }
1611 :
1612 0 : if (!nodePosition.IsValid()) {
1613 0 : return FrameAndNodeOffset();
1614 : }
1615 :
1616 0 : nsIFrame* lastFrame = nullptr;
1617 0 : GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
1618 0 : true, &lastFrame);
1619 0 : if (!lastFrame) {
1620 0 : return FrameAndNodeOffset();
1621 : }
1622 :
1623 : // If the last frame is a text frame, we need to check if the range actually
1624 : // includes at least one character in the range. Therefore, if it's not a
1625 : // text frame, we need to do nothing anymore.
1626 0 : if (!lastFrame->IsTextFrame()) {
1627 0 : return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
1628 : }
1629 :
1630 : int32_t start, end;
1631 0 : if (NS_WARN_IF(NS_FAILED(lastFrame->GetOffsets(start, end)))) {
1632 0 : return FrameAndNodeOffset();
1633 : }
1634 :
1635 : // If the start offset in the node is same as the computed offset in the
1636 : // node and it's not 0, the frame shouldn't be added to the text rect. So,
1637 : // this should return previous text frame and its last offset if there is
1638 : // at least one text frame.
1639 0 : if (nodePosition.mOffset && nodePosition.mOffset == start) {
1640 0 : GetFrameForTextRect(nodePosition.mNode, --nodePosition.mOffset,
1641 0 : true, &lastFrame);
1642 0 : if (NS_WARN_IF(!lastFrame)) {
1643 0 : return FrameAndNodeOffset();
1644 : }
1645 : }
1646 :
1647 0 : return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
1648 : }
1649 :
1650 : ContentEventHandler::FrameRelativeRect
1651 0 : ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame)
1652 : {
1653 : // Note that this method should be called only with an element's frame whose
1654 : // open tag causes a line break or moz-<br> for computing empty last line's
1655 : // rect.
1656 0 : MOZ_ASSERT(ShouldBreakLineBefore(aFrame->GetContent(), mRootContent) ||
1657 : IsMozBR(aFrame->GetContent()));
1658 :
1659 0 : nsIFrame* frameForFontMetrics = aFrame;
1660 :
1661 : // If it's not a <br> frame, this method computes the line breaker's rect
1662 : // outside the frame. Therefore, we need to compute with parent frame's
1663 : // font metrics in such case.
1664 0 : if (!aFrame->IsBrFrame() && aFrame->GetParent()) {
1665 0 : frameForFontMetrics = aFrame->GetParent();
1666 : }
1667 :
1668 : // Note that <br> element's rect is decided with line-height but we need
1669 : // a rect only with font height. Additionally, <br> frame's width and
1670 : // height are 0 in quirks mode if it's not an empty line. So, we cannot
1671 : // use frame rect information even if it's a <br> frame.
1672 :
1673 0 : FrameRelativeRect result(aFrame);
1674 :
1675 : RefPtr<nsFontMetrics> fontMetrics =
1676 0 : nsLayoutUtils::GetInflatedFontMetricsForFrame(frameForFontMetrics);
1677 0 : if (NS_WARN_IF(!fontMetrics)) {
1678 0 : return FrameRelativeRect();
1679 : }
1680 :
1681 0 : const WritingMode kWritingMode = frameForFontMetrics->GetWritingMode();
1682 0 : nscoord baseline = aFrame->GetCaretBaseline();
1683 0 : if (kWritingMode.IsVertical()) {
1684 0 : if (kWritingMode.IsLineInverted()) {
1685 0 : result.mRect.x = baseline - fontMetrics->MaxDescent();
1686 : } else {
1687 0 : result.mRect.x = baseline - fontMetrics->MaxAscent();
1688 : }
1689 0 : result.mRect.width = fontMetrics->MaxHeight();
1690 : } else {
1691 0 : result.mRect.y = baseline - fontMetrics->MaxAscent();
1692 0 : result.mRect.height = fontMetrics->MaxHeight();
1693 : }
1694 :
1695 : // If aFrame isn't a <br> frame, caret should be at outside of it because
1696 : // the line break is before its open tag. For example, case of
1697 : // |<div><p>some text</p></div>|, caret is before <p> element and in <div>
1698 : // element, the caret should be left of top-left corner of <p> element like:
1699 : //
1700 : // +-<div>------------------- <div>'s border box
1701 : // | I +-<p>----------------- <p>'s border box
1702 : // | I |
1703 : // | I |
1704 : // | |
1705 : // ^- caret
1706 : //
1707 : // However, this is a hack for unusual scenario. This hack shouldn't be
1708 : // used as far as possible.
1709 0 : if (!aFrame->IsBrFrame()) {
1710 0 : if (kWritingMode.IsVertical()) {
1711 0 : if (kWritingMode.IsLineInverted()) {
1712 : // above of top-left corner of aFrame.
1713 0 : result.mRect.x = 0;
1714 : } else {
1715 : // above of top-right corner of aFrame.
1716 0 : result.mRect.x = aFrame->GetRect().XMost() - result.mRect.width;
1717 : }
1718 0 : result.mRect.y = -mPresContext->AppUnitsPerDevPixel();
1719 : } else {
1720 : // left of top-left corner of aFrame.
1721 0 : result.mRect.x = -mPresContext->AppUnitsPerDevPixel();
1722 0 : result.mRect.y = 0;
1723 : }
1724 : }
1725 0 : return result;
1726 : }
1727 :
1728 : ContentEventHandler::FrameRelativeRect
1729 0 : ContentEventHandler::GuessLineBreakerRectAfter(nsIContent* aTextContent)
1730 : {
1731 : // aTextContent should be a text node.
1732 0 : MOZ_ASSERT(aTextContent->IsNodeOfType(nsINode::eTEXT));
1733 :
1734 0 : FrameRelativeRect result;
1735 0 : int32_t length = static_cast<int32_t>(aTextContent->Length());
1736 0 : if (NS_WARN_IF(length < 0)) {
1737 0 : return result;
1738 : }
1739 : // Get the last nsTextFrame which is caused by aTextContent. Note that
1740 : // a text node can cause multiple text frames, e.g., the text is too long
1741 : // and wrapped by its parent block or the text has line breakers and its
1742 : // white-space property respects the line breakers (e.g., |pre|).
1743 0 : nsIFrame* lastTextFrame = nullptr;
1744 0 : nsresult rv = GetFrameForTextRect(aTextContent, length, true, &lastTextFrame);
1745 0 : if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!lastTextFrame)) {
1746 0 : return result;
1747 : }
1748 0 : const nsRect kLastTextFrameRect = lastTextFrame->GetRect();
1749 0 : if (lastTextFrame->GetWritingMode().IsVertical()) {
1750 : // Below of the last text frame.
1751 0 : result.mRect.SetRect(0, kLastTextFrameRect.height,
1752 0 : kLastTextFrameRect.width, 0);
1753 : } else {
1754 : // Right of the last text frame (not bidi-aware).
1755 0 : result.mRect.SetRect(kLastTextFrameRect.width, 0,
1756 0 : 0, kLastTextFrameRect.height);
1757 : }
1758 0 : result.mBaseFrame = lastTextFrame;
1759 0 : return result;
1760 : }
1761 :
1762 : ContentEventHandler::FrameRelativeRect
1763 0 : ContentEventHandler::GuessFirstCaretRectIn(nsIFrame* aFrame)
1764 : {
1765 0 : const WritingMode kWritingMode = aFrame->GetWritingMode();
1766 :
1767 : // Computes the font height, but if it's not available, we should use
1768 : // default font size of Firefox. The default font size in default settings
1769 : // is 16px.
1770 : RefPtr<nsFontMetrics> fontMetrics =
1771 0 : nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
1772 : const nscoord kMaxHeight =
1773 0 : fontMetrics ? fontMetrics->MaxHeight() :
1774 0 : 16 * mPresContext->AppUnitsPerDevPixel();
1775 :
1776 0 : nsRect caretRect;
1777 0 : const nsRect kContentRect = aFrame->GetContentRect() - aFrame->GetPosition();
1778 0 : caretRect.y = kContentRect.y;
1779 0 : if (!kWritingMode.IsVertical()) {
1780 0 : if (kWritingMode.IsBidiLTR()) {
1781 0 : caretRect.x = kContentRect.x;
1782 : } else {
1783 : // Move 1px left for the space of caret itself.
1784 0 : const nscoord kOnePixel = mPresContext->AppUnitsPerDevPixel();
1785 0 : caretRect.x = kContentRect.XMost() - kOnePixel;
1786 : }
1787 0 : caretRect.height = kMaxHeight;
1788 : // However, don't add kOnePixel here because it may cause 2px width at
1789 : // aligning the edge to device pixels.
1790 0 : caretRect.width = 1;
1791 : } else {
1792 0 : if (kWritingMode.IsVerticalLR()) {
1793 0 : caretRect.x = kContentRect.x;
1794 : } else {
1795 0 : caretRect.x = kContentRect.XMost() - kMaxHeight;
1796 : }
1797 0 : caretRect.width = kMaxHeight;
1798 : // Don't add app units for a device pixel because it may cause 2px height
1799 : // at aligning the edge to device pixels.
1800 0 : caretRect.height = 1;
1801 : }
1802 0 : return FrameRelativeRect(caretRect, aFrame);
1803 : }
1804 :
1805 : nsresult
1806 0 : ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
1807 : {
1808 0 : nsresult rv = Init(aEvent);
1809 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1810 0 : return rv;
1811 : }
1812 :
1813 0 : LineBreakType lineBreakType = GetLineBreakType(aEvent);
1814 0 : const uint32_t kBRLength = GetBRLength(lineBreakType);
1815 :
1816 0 : RefPtr<nsRange> range = new nsRange(mRootContent);
1817 :
1818 0 : bool isVertical = false;
1819 0 : LayoutDeviceIntRect rect;
1820 0 : uint32_t offset = aEvent->mInput.mOffset;
1821 0 : const uint32_t kEndOffset = offset + aEvent->mInput.mLength;
1822 0 : bool wasLineBreaker = false;
1823 : // lastCharRect stores the last charRect value (see below for the detail of
1824 : // charRect).
1825 0 : nsRect lastCharRect;
1826 : // lastFrame is base frame of lastCharRect.
1827 0 : nsIFrame* lastFrame = nullptr;
1828 0 : while (offset < kEndOffset) {
1829 0 : nsCOMPtr<nsIContent> lastTextContent;
1830 0 : rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
1831 0 : nullptr, getter_AddRefs(lastTextContent));
1832 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1833 0 : return rv;
1834 : }
1835 :
1836 : // If the range is collapsed, offset has already reached the end of the
1837 : // contents.
1838 0 : if (range->Collapsed()) {
1839 0 : break;
1840 : }
1841 :
1842 : // Get the first frame which causes some text after the offset.
1843 0 : FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
1844 :
1845 : // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
1846 : // means that there are no visible frames having text or the offset reached
1847 : // the end of contents.
1848 0 : if (!firstFrame.IsValid()) {
1849 0 : nsAutoString allText;
1850 0 : rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
1851 : // If the offset doesn't reach the end of contents yet but there is no
1852 : // frames for the node, that means that current offset's node is hidden
1853 : // by CSS or something. Ideally, we should handle it with the last
1854 : // visible text node's last character's rect, but it's not usual cases
1855 : // in actual web services. Therefore, currently, we should make this
1856 : // case fail.
1857 0 : if (NS_WARN_IF(NS_FAILED(rv)) || offset < allText.Length()) {
1858 0 : return NS_ERROR_FAILURE;
1859 : }
1860 : // Otherwise, we should append caret rect at the end of the contents
1861 : // later.
1862 0 : break;
1863 : }
1864 :
1865 0 : nsIContent* firstContent = firstFrame.mFrame->GetContent();
1866 0 : if (NS_WARN_IF(!firstContent)) {
1867 0 : return NS_ERROR_FAILURE;
1868 : }
1869 :
1870 0 : bool startsBetweenLineBreaker = false;
1871 0 : nsAutoString chars;
1872 : // XXX not bidi-aware this class...
1873 0 : isVertical = firstFrame->GetWritingMode().IsVertical();
1874 :
1875 0 : nsIFrame* baseFrame = firstFrame;
1876 : // charRect should have each character rect or line breaker rect relative
1877 : // to the base frame.
1878 0 : AutoTArray<nsRect, 16> charRects;
1879 :
1880 : // If the first frame is a text frame, the result should be computed with
1881 : // the frame's API.
1882 0 : if (firstFrame->IsTextFrame()) {
1883 0 : rv = firstFrame->GetCharacterRectsInRange(firstFrame.mOffsetInNode,
1884 0 : kEndOffset - offset, charRects);
1885 0 : if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
1886 0 : return rv;
1887 : }
1888 : // Assign the characters whose rects are computed by the call of
1889 : // nsTextFrame::GetCharacterRectsInRange().
1890 0 : AppendSubString(chars, firstContent, firstFrame.mOffsetInNode,
1891 0 : charRects.Length());
1892 0 : if (NS_WARN_IF(chars.Length() != charRects.Length())) {
1893 0 : return NS_ERROR_UNEXPECTED;
1894 : }
1895 0 : if (kBRLength > 1 && chars[0] == '\n' &&
1896 0 : offset == aEvent->mInput.mOffset && offset) {
1897 : // If start of range starting from previous offset of query range is
1898 : // same as the start of query range, the query range starts from
1899 : // between a line breaker (i.e., the range starts between "\r" and
1900 : // "\n").
1901 0 : RefPtr<nsRange> rangeToPrevOffset = new nsRange(mRootContent);
1902 0 : rv = SetRangeFromFlatTextOffset(rangeToPrevOffset,
1903 0 : aEvent->mInput.mOffset - 1, 1,
1904 0 : lineBreakType, true, nullptr);
1905 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1906 0 : return rv;
1907 : }
1908 0 : startsBetweenLineBreaker =
1909 0 : range->GetStartContainer() ==
1910 0 : rangeToPrevOffset->GetStartContainer() &&
1911 0 : range->StartOffset() == rangeToPrevOffset->StartOffset();
1912 : }
1913 : }
1914 : // Other contents should cause a line breaker rect before it.
1915 : // Note that moz-<br> element does not cause any text, however,
1916 : // it represents empty line at the last of current block. Therefore,
1917 : // we need to compute its rect too.
1918 0 : else if (ShouldBreakLineBefore(firstContent, mRootContent) ||
1919 0 : IsMozBR(firstContent)) {
1920 0 : nsRect brRect;
1921 : // If the frame is not a <br> frame, we need to compute the caret rect
1922 : // with last character's rect before firstContent if there is.
1923 : // For example, if caret is after "c" of |<p>abc</p><p>def</p>|, IME may
1924 : // query a line breaker's rect after "c". Then, if we compute it only
1925 : // with the 2nd <p>'s block frame, the result will be:
1926 : // +-<p>--------------------------------+
1927 : // |abc |
1928 : // +------------------------------------+
1929 : //
1930 : // I+-<p>--------------------------------+
1931 : // |def |
1932 : // +------------------------------------+
1933 : // However, users expect popup windows of IME should be positioned at
1934 : // right-bottom of "c" like this:
1935 : // +-<p>--------------------------------+
1936 : // |abcI |
1937 : // +------------------------------------+
1938 : //
1939 : // +-<p>--------------------------------+
1940 : // |def |
1941 : // +------------------------------------+
1942 : // Therefore, if the first frame isn't a <br> frame and there is a text
1943 : // node before the first node in the queried range, we should compute the
1944 : // first rect with the previous character's rect.
1945 : // If we already compute a character's rect in the queried range, we can
1946 : // compute it with the cached last character's rect. (However, don't
1947 : // use this path if it's a <br> frame because trusting <br> frame's rect
1948 : // is better than guessing the rect from the previous character.)
1949 0 : if (!firstFrame->IsBrFrame() && aEvent->mInput.mOffset != offset) {
1950 0 : baseFrame = lastFrame;
1951 0 : brRect = lastCharRect;
1952 0 : if (!wasLineBreaker) {
1953 0 : if (isVertical) {
1954 : // Right of the last character.
1955 0 : brRect.y = brRect.YMost() + 1;
1956 0 : brRect.height = 1;
1957 : } else {
1958 : // Under the last character.
1959 0 : brRect.x = brRect.XMost() + 1;
1960 0 : brRect.width = 1;
1961 : }
1962 : }
1963 : }
1964 : // If it's not a <br> frame and it's the first character rect at the
1965 : // queried range, we need to the previous character of the start of
1966 : // the queried range if there is a text node.
1967 0 : else if (!firstFrame->IsBrFrame() && lastTextContent) {
1968 : FrameRelativeRect brRectRelativeToLastTextFrame =
1969 0 : GuessLineBreakerRectAfter(lastTextContent);
1970 0 : if (NS_WARN_IF(!brRectRelativeToLastTextFrame.IsValid())) {
1971 0 : return NS_ERROR_FAILURE;
1972 : }
1973 : // Look for the last text frame for lastTextContent.
1974 0 : nsIFrame* primaryFrame = lastTextContent->GetPrimaryFrame();
1975 0 : if (NS_WARN_IF(!primaryFrame)) {
1976 0 : return NS_ERROR_FAILURE;
1977 : }
1978 0 : baseFrame = primaryFrame->LastContinuation();
1979 0 : if (NS_WARN_IF(!baseFrame)) {
1980 0 : return NS_ERROR_FAILURE;
1981 : }
1982 0 : brRect = brRectRelativeToLastTextFrame.RectRelativeTo(baseFrame);
1983 : }
1984 : // Otherwise, we need to compute the line breaker's rect only with the
1985 : // first frame's rect. But this may be unexpected. For example,
1986 : // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is
1987 : // before "a", therefore, users expect the rect left of "a". However,
1988 : // we don't have enough information about the next character here and
1989 : // this isn't usual case (e.g., IME typically tries to query the rect
1990 : // of "a" or caret rect for computing its popup position). Therefore,
1991 : // we shouldn't do more complicated hack here unless we'll get some bug
1992 : // reports actually.
1993 : else {
1994 0 : FrameRelativeRect relativeBRRect = GetLineBreakerRectBefore(firstFrame);
1995 0 : brRect = relativeBRRect.RectRelativeTo(firstFrame);
1996 : }
1997 0 : charRects.AppendElement(brRect);
1998 0 : chars.AssignLiteral("\n");
1999 0 : if (kBRLength > 1 && offset == aEvent->mInput.mOffset && offset) {
2000 : // If the first frame for the previous offset of the query range and
2001 : // the first frame for the start of query range are same, that means
2002 : // the start offset is between the first line breaker (i.e., the range
2003 : // starts between "\r" and "\n").
2004 0 : rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset - 1, 1,
2005 0 : lineBreakType, true, nullptr);
2006 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2007 0 : return NS_ERROR_UNEXPECTED;
2008 : }
2009 : FrameAndNodeOffset frameForPrevious =
2010 0 : GetFirstFrameInRangeForTextRect(range);
2011 0 : startsBetweenLineBreaker = frameForPrevious.mFrame == firstFrame.mFrame;
2012 : }
2013 : } else {
2014 : NS_WARNING("The frame is neither a text frame nor a frame whose content "
2015 0 : "causes a line break");
2016 0 : return NS_ERROR_FAILURE;
2017 : }
2018 :
2019 0 : for (size_t i = 0; i < charRects.Length() && offset < kEndOffset; i++) {
2020 0 : nsRect charRect = charRects[i];
2021 : // Store lastCharRect before applying CSS transform because it may be
2022 : // used for computing a line breaker rect. Then, the computed line
2023 : // breaker rect will be applied CSS transform again. Therefore,
2024 : // the value of lastCharRect should be raw rect value relative to the
2025 : // base frame.
2026 0 : lastCharRect = charRect;
2027 0 : lastFrame = baseFrame;
2028 0 : rv = ConvertToRootRelativeOffset(baseFrame, charRect);
2029 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2030 0 : return rv;
2031 : }
2032 :
2033 : rect = LayoutDeviceIntRect::FromUnknownRect(
2034 0 : charRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
2035 : // Returning empty rect may cause native IME confused, let's make sure to
2036 : // return non-empty rect.
2037 0 : EnsureNonEmptyRect(rect);
2038 :
2039 0 : aEvent->mReply.mRectArray.AppendElement(rect);
2040 0 : offset++;
2041 :
2042 : // If it's not a line breaker or the line breaker length is same as
2043 : // XP line breaker's, we need to do nothing for current character.
2044 0 : wasLineBreaker = chars[i] == '\n';
2045 0 : if (!wasLineBreaker || kBRLength == 1) {
2046 0 : continue;
2047 : }
2048 :
2049 0 : MOZ_ASSERT(kBRLength == 2);
2050 :
2051 : // If it's already reached the end of query range, we don't need to do
2052 : // anymore.
2053 0 : if (offset == kEndOffset) {
2054 0 : break;
2055 : }
2056 :
2057 : // If the query range starts from between a line breaker, i.e., it starts
2058 : // between "\r" and "\n", the appended rect was for the "\n". Therefore,
2059 : // we don't need to append same rect anymore for current "\r\n".
2060 0 : if (startsBetweenLineBreaker) {
2061 0 : continue;
2062 : }
2063 :
2064 : // The appended rect was for "\r" of "\r\n". Therefore, we need to
2065 : // append same rect for "\n" too because querying rect of "\r" and "\n"
2066 : // should return same rect. E.g., IME may query previous character's
2067 : // rect of first character of a line.
2068 0 : aEvent->mReply.mRectArray.AppendElement(rect);
2069 0 : offset++;
2070 : }
2071 : }
2072 :
2073 : // If the query range is longer than actual content length, we should append
2074 : // caret rect at the end of the content as the last character rect because
2075 : // native IME may want to query character rect at the end of contents for
2076 : // deciding the position of a popup window (e.g., suggest window for next
2077 : // word). Note that when this method hasn't appended character rects, it
2078 : // means that the offset is too large or the query range is collapsed.
2079 0 : if (offset < kEndOffset || aEvent->mReply.mRectArray.IsEmpty()) {
2080 : // If we've already retrieved some character rects before current offset,
2081 : // we can guess the last rect from the last character's rect unless it's a
2082 : // line breaker. (If it's a line breaker, the caret rect is in next line.)
2083 0 : if (!aEvent->mReply.mRectArray.IsEmpty() && !wasLineBreaker) {
2084 0 : rect = aEvent->mReply.mRectArray.LastElement();
2085 0 : if (isVertical) {
2086 0 : rect.y = rect.YMost() + 1;
2087 0 : rect.height = 1;
2088 0 : MOZ_ASSERT(rect.width);
2089 : } else {
2090 0 : rect.x = rect.XMost() + 1;
2091 0 : rect.width = 1;
2092 0 : MOZ_ASSERT(rect.height);
2093 : }
2094 0 : aEvent->mReply.mRectArray.AppendElement(rect);
2095 : } else {
2096 : // Note that don't use eQueryCaretRect here because if caret is at the
2097 : // end of the content, it returns actual caret rect instead of computing
2098 : // the rect itself. It means that the result depends on caret position.
2099 : // So, we shouldn't use it for consistency result in automated tests.
2100 0 : WidgetQueryContentEvent queryTextRect(eQueryTextRect, *aEvent);
2101 0 : WidgetQueryContentEvent::Options options(*aEvent);
2102 0 : queryTextRect.InitForQueryTextRect(offset, 1, options);
2103 0 : rv = OnQueryTextRect(&queryTextRect);
2104 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2105 0 : return rv;
2106 : }
2107 0 : if (NS_WARN_IF(!queryTextRect.mSucceeded)) {
2108 0 : return NS_ERROR_FAILURE;
2109 : }
2110 0 : MOZ_ASSERT(!queryTextRect.mReply.mRect.IsEmpty());
2111 0 : if (queryTextRect.mReply.mWritingMode.IsVertical()) {
2112 0 : queryTextRect.mReply.mRect.height = 1;
2113 : } else {
2114 0 : queryTextRect.mReply.mRect.width = 1;
2115 : }
2116 0 : aEvent->mReply.mRectArray.AppendElement(queryTextRect.mReply.mRect);
2117 : }
2118 : }
2119 :
2120 0 : aEvent->mSucceeded = true;
2121 0 : return NS_OK;
2122 : }
2123 :
2124 : nsresult
2125 0 : ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
2126 : {
2127 0 : nsresult rv = Init(aEvent);
2128 0 : if (NS_FAILED(rv)) {
2129 0 : return rv;
2130 : }
2131 :
2132 : // If mLength is 0 (this may be caused by bug of native IME), we should
2133 : // redirect this event to OnQueryCaretRect().
2134 0 : if (!aEvent->mInput.mLength) {
2135 0 : return OnQueryCaretRect(aEvent);
2136 : }
2137 :
2138 0 : LineBreakType lineBreakType = GetLineBreakType(aEvent);
2139 0 : RefPtr<nsRange> range = new nsRange(mRootContent);
2140 0 : nsCOMPtr<nsIContent> lastTextContent;
2141 0 : rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
2142 : aEvent->mInput.mLength, lineBreakType, true,
2143 : &aEvent->mReply.mOffset,
2144 0 : getter_AddRefs(lastTextContent));
2145 0 : NS_ENSURE_SUCCESS(rv, rv);
2146 0 : rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
2147 0 : NS_ENSURE_SUCCESS(rv, rv);
2148 :
2149 : // used to iterate over all contents and their frames
2150 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
2151 0 : iter->Init(range);
2152 :
2153 : // Get the first frame which causes some text after the offset.
2154 0 : FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
2155 :
2156 : // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
2157 : // means that there are no visible frames having text or the offset reached
2158 : // the end of contents.
2159 0 : if (!firstFrame.IsValid()) {
2160 0 : nsAutoString allText;
2161 0 : rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
2162 : // If the offset doesn't reach the end of contents but there is no frames
2163 : // for the node, that means that current offset's node is hidden by CSS or
2164 : // something. Ideally, we should handle it with the last visible text
2165 : // node's last character's rect, but it's not usual cases in actual web
2166 : // services. Therefore, currently, we should make this case fail.
2167 0 : if (NS_WARN_IF(NS_FAILED(rv)) ||
2168 0 : static_cast<uint32_t>(aEvent->mInput.mOffset) < allText.Length()) {
2169 0 : return NS_ERROR_FAILURE;
2170 : }
2171 :
2172 : // Look for the last frame which should be included text rects.
2173 0 : IgnoredErrorResult erv;
2174 0 : range->SelectNodeContents(*mRootContent, erv);
2175 0 : if (NS_WARN_IF(erv.Failed())) {
2176 0 : return NS_ERROR_UNEXPECTED;
2177 : }
2178 0 : nsRect rect;
2179 0 : FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(range);
2180 : // If there is at least one frame which can be used for computing a rect
2181 : // for a character or a line breaker, we should use it for guessing the
2182 : // caret rect at the end of the contents.
2183 0 : if (lastFrame) {
2184 0 : if (NS_WARN_IF(!lastFrame->GetContent())) {
2185 0 : return NS_ERROR_FAILURE;
2186 : }
2187 0 : FrameRelativeRect relativeRect;
2188 : // If there is a <br> frame at the end, it represents an empty line at
2189 : // the end with moz-<br> or content <br> in a block level element.
2190 0 : if (lastFrame->IsBrFrame()) {
2191 0 : relativeRect = GetLineBreakerRectBefore(lastFrame);
2192 : }
2193 : // If there is a text frame at the end, use its information.
2194 0 : else if (lastFrame->IsTextFrame()) {
2195 0 : relativeRect = GuessLineBreakerRectAfter(lastFrame->GetContent());
2196 : }
2197 : // If there is an empty frame which is neither a text frame nor a <br>
2198 : // frame at the end, guess caret rect in it.
2199 : else {
2200 0 : relativeRect = GuessFirstCaretRectIn(lastFrame);
2201 : }
2202 0 : if (NS_WARN_IF(!relativeRect.IsValid())) {
2203 0 : return NS_ERROR_FAILURE;
2204 : }
2205 0 : rect = relativeRect.RectRelativeTo(lastFrame);
2206 0 : rv = ConvertToRootRelativeOffset(lastFrame, rect);
2207 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2208 0 : return rv;
2209 : }
2210 0 : aEvent->mReply.mWritingMode = lastFrame->GetWritingMode();
2211 : }
2212 : // Otherwise, if there are no contents in mRootContent, guess caret rect in
2213 : // its frame (with its font height and content box).
2214 : else {
2215 0 : nsIFrame* rootContentFrame = mRootContent->GetPrimaryFrame();
2216 0 : if (NS_WARN_IF(!rootContentFrame)) {
2217 0 : return NS_ERROR_FAILURE;
2218 : }
2219 0 : FrameRelativeRect relativeRect = GuessFirstCaretRectIn(rootContentFrame);
2220 0 : if (NS_WARN_IF(!relativeRect.IsValid())) {
2221 0 : return NS_ERROR_FAILURE;
2222 : }
2223 0 : rect = relativeRect.RectRelativeTo(rootContentFrame);
2224 0 : rv = ConvertToRootRelativeOffset(rootContentFrame, rect);
2225 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2226 0 : return rv;
2227 : }
2228 0 : aEvent->mReply.mWritingMode = rootContentFrame->GetWritingMode();
2229 : }
2230 : aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
2231 0 : rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
2232 0 : EnsureNonEmptyRect(aEvent->mReply.mRect);
2233 0 : aEvent->mSucceeded = true;
2234 0 : return NS_OK;
2235 : }
2236 :
2237 0 : nsRect rect, frameRect;
2238 0 : nsPoint ptOffset;
2239 :
2240 : // If the first frame is a text frame, the result should be computed with
2241 : // the frame's rect but not including the rect before start point of the
2242 : // queried range.
2243 0 : if (firstFrame->IsTextFrame()) {
2244 0 : rect.SetRect(nsPoint(0, 0), firstFrame->GetRect().Size());
2245 0 : rv = ConvertToRootRelativeOffset(firstFrame, rect);
2246 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2247 0 : return rv;
2248 : }
2249 0 : frameRect = rect;
2250 : // Exclude the rect before start point of the queried range.
2251 0 : firstFrame->GetPointFromOffset(firstFrame.mOffsetInNode, &ptOffset);
2252 0 : if (firstFrame->GetWritingMode().IsVertical()) {
2253 0 : rect.y += ptOffset.y;
2254 0 : rect.height -= ptOffset.y;
2255 : } else {
2256 0 : rect.x += ptOffset.x;
2257 0 : rect.width -= ptOffset.x;
2258 : }
2259 : }
2260 : // If first frame causes a line breaker but it's not a <br> frame, we cannot
2261 : // compute proper rect only with the frame because typically caret is at
2262 : // right of the last character of it. For example, if caret is after "c" of
2263 : // |<p>abc</p><p>def</p>|, IME may query a line breaker's rect after "c".
2264 : // Then, if we compute it only with the 2nd <p>'s block frame, the result
2265 : // will be:
2266 : // +-<p>--------------------------------+
2267 : // |abc |
2268 : // +------------------------------------+
2269 : //
2270 : // I+-<p>--------------------------------+
2271 : // |def |
2272 : // +------------------------------------+
2273 : // However, users expect popup windows of IME should be positioned at
2274 : // right-bottom of "c" like this:
2275 : // +-<p>--------------------------------+
2276 : // |abcI |
2277 : // +------------------------------------+
2278 : //
2279 : // +-<p>--------------------------------+
2280 : // |def |
2281 : // +------------------------------------+
2282 : // Therefore, if the first frame isn't a <br> frame and there is a text
2283 : // node before the first node in the queried range, we should compute the
2284 : // first rect with the previous character's rect.
2285 0 : else if (!firstFrame->IsBrFrame() && lastTextContent) {
2286 : FrameRelativeRect brRectAfterLastChar =
2287 0 : GuessLineBreakerRectAfter(lastTextContent);
2288 0 : if (NS_WARN_IF(!brRectAfterLastChar.IsValid())) {
2289 0 : return NS_ERROR_FAILURE;
2290 : }
2291 0 : rect = brRectAfterLastChar.mRect;
2292 0 : rv = ConvertToRootRelativeOffset(brRectAfterLastChar.mBaseFrame, rect);
2293 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2294 0 : return rv;
2295 : }
2296 0 : frameRect = rect;
2297 : }
2298 : // Otherwise, we need to compute the line breaker's rect only with the
2299 : // first frame's rect. But this may be unexpected. For example,
2300 : // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is before
2301 : // "a", therefore, users expect the rect left of "a". However, we don't
2302 : // have enough information about the next character here and this isn't
2303 : // usual case (e.g., IME typically tries to query the rect of "a" or caret
2304 : // rect for computing its popup position). Therefore, we shouldn't do
2305 : // more complicated hack here unless we'll get some bug reports actually.
2306 : else {
2307 0 : FrameRelativeRect relativeRect = GetLineBreakerRectBefore(firstFrame);
2308 0 : if (NS_WARN_IF(!relativeRect.IsValid())) {
2309 0 : return NS_ERROR_FAILURE;
2310 : }
2311 0 : rect = relativeRect.RectRelativeTo(firstFrame);
2312 0 : rv = ConvertToRootRelativeOffset(firstFrame, rect);
2313 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2314 0 : return rv;
2315 : }
2316 0 : frameRect = rect;
2317 : }
2318 : // UnionRect() requires non-empty rect. So, let's make sure to get non-emtpy
2319 : // rect from the first frame.
2320 0 : EnsureNonEmptyRect(rect);
2321 :
2322 : // Get the last frame which causes some text in the range.
2323 0 : FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(range);
2324 0 : if (NS_WARN_IF(!lastFrame.IsValid())) {
2325 0 : return NS_ERROR_FAILURE;
2326 : }
2327 :
2328 : // iterate over all covered frames
2329 0 : for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
2330 0 : frame = frame->GetNextContinuation();
2331 0 : if (!frame) {
2332 0 : do {
2333 0 : iter->Next();
2334 0 : nsINode* node = iter->GetCurrentNode();
2335 0 : if (!node) {
2336 0 : break;
2337 : }
2338 0 : if (!node->IsNodeOfType(nsINode::eCONTENT)) {
2339 0 : continue;
2340 : }
2341 0 : nsIFrame* primaryFrame = node->AsContent()->GetPrimaryFrame();
2342 : // The node may be hidden by CSS.
2343 0 : if (!primaryFrame) {
2344 0 : continue;
2345 : }
2346 : // We should take only text frame's rect and br frame's rect. We can
2347 : // always use frame rect of text frame and GetLineBreakerRectBefore()
2348 : // can return exactly correct rect only for <br> frame for now. On the
2349 : // other hand, GetLineBreakRectBefore() returns guessed caret rect for
2350 : // the other frames. We shouldn't include such odd rect to the result.
2351 0 : if (primaryFrame->IsTextFrame() || primaryFrame->IsBrFrame()) {
2352 0 : frame = primaryFrame;
2353 : }
2354 0 : } while (!frame && !iter->IsDone());
2355 0 : if (!frame) {
2356 0 : break;
2357 : }
2358 : }
2359 0 : if (frame->IsTextFrame()) {
2360 0 : frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
2361 : } else {
2362 0 : MOZ_ASSERT(frame->IsBrFrame());
2363 0 : FrameRelativeRect relativeRect = GetLineBreakerRectBefore(frame);
2364 0 : if (NS_WARN_IF(!relativeRect.IsValid())) {
2365 0 : return NS_ERROR_FAILURE;
2366 : }
2367 0 : frameRect = relativeRect.RectRelativeTo(frame);
2368 : }
2369 0 : rv = ConvertToRootRelativeOffset(frame, frameRect);
2370 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2371 0 : return rv;
2372 : }
2373 : // UnionRect() requires non-empty rect. So, let's make sure to get
2374 : // non-emtpy rect from the frame.
2375 0 : EnsureNonEmptyRect(frameRect);
2376 0 : if (frame != lastFrame) {
2377 : // not last frame, so just add rect to previous result
2378 0 : rect.UnionRect(rect, frameRect);
2379 : }
2380 : }
2381 :
2382 : // Get the ending frame rect.
2383 : // FYI: If first frame and last frame are same, frameRect is already set
2384 : // to the rect excluding the text before the query range.
2385 0 : if (firstFrame.mFrame != lastFrame.mFrame) {
2386 0 : frameRect.SetRect(nsPoint(0, 0), lastFrame->GetRect().Size());
2387 0 : rv = ConvertToRootRelativeOffset(lastFrame, frameRect);
2388 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2389 0 : return rv;
2390 : }
2391 : }
2392 :
2393 : // Shrink the last frame for cutting off the text after the query range.
2394 0 : if (lastFrame->IsTextFrame()) {
2395 0 : lastFrame->GetPointFromOffset(lastFrame.mOffsetInNode, &ptOffset);
2396 0 : if (lastFrame->GetWritingMode().IsVertical()) {
2397 0 : frameRect.height -= lastFrame->GetRect().height - ptOffset.y;
2398 : } else {
2399 0 : frameRect.width -= lastFrame->GetRect().width - ptOffset.x;
2400 : }
2401 : // UnionRect() requires non-empty rect. So, let's make sure to get
2402 : // non-empty rect from the last frame.
2403 0 : EnsureNonEmptyRect(frameRect);
2404 :
2405 0 : if (firstFrame.mFrame == lastFrame.mFrame) {
2406 0 : rect.IntersectRect(rect, frameRect);
2407 : } else {
2408 0 : rect.UnionRect(rect, frameRect);
2409 : }
2410 : }
2411 :
2412 : aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
2413 0 : rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
2414 : // Returning empty rect may cause native IME confused, let's make sure to
2415 : // return non-empty rect.
2416 0 : EnsureNonEmptyRect(aEvent->mReply.mRect);
2417 0 : aEvent->mReply.mWritingMode = lastFrame->GetWritingMode();
2418 0 : aEvent->mSucceeded = true;
2419 0 : return NS_OK;
2420 : }
2421 :
2422 : nsresult
2423 0 : ContentEventHandler::OnQueryEditorRect(WidgetQueryContentEvent* aEvent)
2424 : {
2425 0 : nsresult rv = Init(aEvent);
2426 0 : if (NS_FAILED(rv)) {
2427 0 : return rv;
2428 : }
2429 :
2430 0 : nsIContent* focusedContent = GetFocusedContent();
2431 0 : rv = QueryContentRect(IsPlugin(focusedContent) ?
2432 0 : focusedContent : mRootContent.get(), aEvent);
2433 0 : NS_ENSURE_SUCCESS(rv, rv);
2434 0 : return NS_OK;
2435 : }
2436 :
2437 : nsresult
2438 0 : ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
2439 : {
2440 0 : nsresult rv = Init(aEvent);
2441 0 : if (NS_FAILED(rv)) {
2442 0 : return rv;
2443 : }
2444 :
2445 : // When the selection is collapsed and the queried offset is current caret
2446 : // position, we should return the "real" caret rect.
2447 0 : if (mSelection->IsCollapsed()) {
2448 0 : nsRect caretRect;
2449 0 : nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
2450 0 : if (caretFrame) {
2451 : uint32_t offset;
2452 0 : rv = GetStartOffset(mFirstSelectedRange,
2453 0 : &offset, GetLineBreakType(aEvent));
2454 0 : NS_ENSURE_SUCCESS(rv, rv);
2455 0 : if (offset == aEvent->mInput.mOffset) {
2456 0 : rv = ConvertToRootRelativeOffset(caretFrame, caretRect);
2457 0 : NS_ENSURE_SUCCESS(rv, rv);
2458 : nscoord appUnitsPerDevPixel =
2459 0 : caretFrame->PresContext()->AppUnitsPerDevPixel();
2460 : aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
2461 0 : caretRect.ToOutsidePixels(appUnitsPerDevPixel));
2462 : // Returning empty rect may cause native IME confused, let's make sure
2463 : // to return non-empty rect.
2464 0 : EnsureNonEmptyRect(aEvent->mReply.mRect);
2465 0 : aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
2466 0 : aEvent->mReply.mOffset = aEvent->mInput.mOffset;
2467 0 : aEvent->mSucceeded = true;
2468 0 : return NS_OK;
2469 : }
2470 : }
2471 : }
2472 :
2473 : // Otherwise, we should guess the caret rect from the character's rect.
2474 0 : WidgetQueryContentEvent queryTextRectEvent(eQueryTextRect, *aEvent);
2475 0 : WidgetQueryContentEvent::Options options(*aEvent);
2476 0 : queryTextRectEvent.InitForQueryTextRect(aEvent->mInput.mOffset, 1, options);
2477 0 : rv = OnQueryTextRect(&queryTextRectEvent);
2478 0 : if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!queryTextRectEvent.mSucceeded)) {
2479 0 : return NS_ERROR_FAILURE;
2480 : }
2481 0 : queryTextRectEvent.mReply.mString.Truncate();
2482 0 : aEvent->mReply = queryTextRectEvent.mReply;
2483 0 : if (aEvent->GetWritingMode().IsVertical()) {
2484 0 : aEvent->mReply.mRect.height = 1;
2485 : } else {
2486 0 : aEvent->mReply.mRect.width = 1;
2487 : }
2488 : // Returning empty rect may cause native IME confused, let's make sure to
2489 : // return non-empty rect.
2490 0 : aEvent->mSucceeded = true;
2491 0 : return NS_OK;
2492 : }
2493 :
2494 : nsresult
2495 0 : ContentEventHandler::OnQueryContentState(WidgetQueryContentEvent* aEvent)
2496 : {
2497 0 : nsresult rv = Init(aEvent);
2498 0 : if (NS_FAILED(rv)) {
2499 0 : return rv;
2500 : }
2501 0 : aEvent->mSucceeded = true;
2502 0 : return NS_OK;
2503 : }
2504 :
2505 : nsresult
2506 0 : ContentEventHandler::OnQuerySelectionAsTransferable(
2507 : WidgetQueryContentEvent* aEvent)
2508 : {
2509 0 : nsresult rv = Init(aEvent);
2510 0 : if (NS_FAILED(rv)) {
2511 0 : return rv;
2512 : }
2513 :
2514 0 : if (!aEvent->mReply.mHasSelection) {
2515 0 : aEvent->mSucceeded = true;
2516 0 : aEvent->mReply.mTransferable = nullptr;
2517 0 : return NS_OK;
2518 : }
2519 :
2520 0 : nsCOMPtr<nsIDocument> doc = mPresShell->GetDocument();
2521 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
2522 :
2523 0 : rv = nsCopySupport::GetTransferableForSelection(
2524 0 : mSelection, doc, getter_AddRefs(aEvent->mReply.mTransferable));
2525 0 : NS_ENSURE_SUCCESS(rv, rv);
2526 :
2527 0 : aEvent->mSucceeded = true;
2528 0 : return NS_OK;
2529 : }
2530 :
2531 : nsresult
2532 0 : ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
2533 : {
2534 0 : nsresult rv = Init(aEvent);
2535 0 : if (NS_FAILED(rv)) {
2536 0 : return rv;
2537 : }
2538 :
2539 0 : aEvent->mReply.mOffset = aEvent->mReply.mTentativeCaretOffset =
2540 : WidgetQueryContentEvent::NOT_FOUND;
2541 :
2542 0 : nsIFrame* rootFrame = mPresShell->GetRootFrame();
2543 0 : NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
2544 0 : nsIWidget* rootWidget = rootFrame->GetNearestWidget();
2545 0 : NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
2546 :
2547 : // The root frame's widget might be different, e.g., the event was fired on
2548 : // a popup but the rootFrame is the document root.
2549 0 : if (rootWidget != aEvent->mWidget) {
2550 0 : NS_PRECONDITION(aEvent->mWidget, "The event must have the widget");
2551 0 : nsView* view = nsView::GetViewFor(aEvent->mWidget);
2552 0 : NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
2553 0 : rootFrame = view->GetFrame();
2554 0 : NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
2555 0 : rootWidget = rootFrame->GetNearestWidget();
2556 0 : NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
2557 : }
2558 :
2559 : WidgetQueryContentEvent eventOnRoot(true, eQueryCharacterAtPoint,
2560 0 : rootWidget);
2561 0 : eventOnRoot.mUseNativeLineBreak = aEvent->mUseNativeLineBreak;
2562 0 : eventOnRoot.mRefPoint = aEvent->mRefPoint;
2563 0 : if (rootWidget != aEvent->mWidget) {
2564 0 : eventOnRoot.mRefPoint += aEvent->mWidget->WidgetToScreenOffset() -
2565 0 : rootWidget->WidgetToScreenOffset();
2566 : }
2567 : nsPoint ptInRoot =
2568 0 : nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame);
2569 :
2570 0 : nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
2571 0 : if (!targetFrame || !targetFrame->GetContent() ||
2572 0 : !nsContentUtils::ContentIsDescendantOf(targetFrame->GetContent(),
2573 0 : mRootContent)) {
2574 : // There is no character at the point.
2575 0 : aEvent->mSucceeded = true;
2576 0 : return NS_OK;
2577 : }
2578 0 : nsPoint ptInTarget = ptInRoot + rootFrame->GetOffsetToCrossDoc(targetFrame);
2579 0 : int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
2580 0 : int32_t targetAPD = targetFrame->PresContext()->AppUnitsPerDevPixel();
2581 0 : ptInTarget = ptInTarget.ScaleToOtherAppUnits(rootAPD, targetAPD);
2582 :
2583 : nsIFrame::ContentOffsets tentativeCaretOffsets =
2584 0 : targetFrame->GetContentOffsetsFromPoint(ptInTarget);
2585 0 : if (!tentativeCaretOffsets.content ||
2586 0 : !nsContentUtils::ContentIsDescendantOf(tentativeCaretOffsets.content,
2587 0 : mRootContent)) {
2588 : // There is no character nor tentative caret point at the point.
2589 0 : aEvent->mSucceeded = true;
2590 0 : return NS_OK;
2591 : }
2592 :
2593 0 : rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
2594 0 : NodePosition(tentativeCaretOffsets),
2595 : mRootContent,
2596 : &aEvent->mReply.mTentativeCaretOffset,
2597 0 : GetLineBreakType(aEvent));
2598 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2599 0 : return rv;
2600 : }
2601 :
2602 0 : if (!targetFrame->IsTextFrame()) {
2603 : // There is no character at the point but there is tentative caret point.
2604 0 : aEvent->mSucceeded = true;
2605 0 : return NS_OK;
2606 : }
2607 :
2608 0 : MOZ_ASSERT(
2609 : aEvent->mReply.mTentativeCaretOffset != WidgetQueryContentEvent::NOT_FOUND,
2610 : "The point is inside a character bounding box. Why tentative caret point "
2611 : "hasn't been found?");
2612 :
2613 0 : nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame);
2614 : nsIFrame::ContentOffsets contentOffsets =
2615 0 : textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
2616 0 : NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE);
2617 : uint32_t offset;
2618 0 : rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
2619 0 : NodePosition(contentOffsets),
2620 : mRootContent, &offset,
2621 0 : GetLineBreakType(aEvent));
2622 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2623 0 : return rv;
2624 : }
2625 :
2626 0 : WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->mWidget);
2627 0 : WidgetQueryContentEvent::Options options(*aEvent);
2628 0 : textRect.InitForQueryTextRect(offset, 1, options);
2629 0 : rv = OnQueryTextRect(&textRect);
2630 0 : NS_ENSURE_SUCCESS(rv, rv);
2631 0 : NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
2632 :
2633 : // currently, we don't need to get the actual text.
2634 0 : aEvent->mReply.mOffset = offset;
2635 0 : aEvent->mReply.mRect = textRect.mReply.mRect;
2636 0 : aEvent->mSucceeded = true;
2637 0 : return NS_OK;
2638 : }
2639 :
2640 : nsresult
2641 0 : ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
2642 : {
2643 0 : NS_ASSERTION(aEvent, "aEvent must not be null");
2644 :
2645 0 : nsresult rv = InitBasic();
2646 0 : if (NS_FAILED(rv)) {
2647 0 : return rv;
2648 : }
2649 :
2650 0 : aEvent->mSucceeded = false;
2651 0 : aEvent->mReply.mWidgetIsHit = false;
2652 :
2653 0 : NS_ENSURE_TRUE(aEvent->mWidget, NS_ERROR_FAILURE);
2654 :
2655 0 : nsIDocument* doc = mPresShell->GetDocument();
2656 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
2657 0 : nsIFrame* docFrame = mPresShell->GetRootFrame();
2658 0 : NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE);
2659 :
2660 : LayoutDeviceIntPoint eventLoc =
2661 0 : aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset();
2662 0 : CSSIntRect docFrameRect = docFrame->GetScreenRect();
2663 : CSSIntPoint eventLocCSS(
2664 0 : mPresContext->DevPixelsToIntCSSPixels(eventLoc.x) - docFrameRect.x,
2665 0 : mPresContext->DevPixelsToIntCSSPixels(eventLoc.y) - docFrameRect.y);
2666 :
2667 : Element* contentUnderMouse =
2668 0 : doc->ElementFromPointHelper(eventLocCSS.x, eventLocCSS.y, false, false);
2669 0 : if (contentUnderMouse) {
2670 0 : nsIWidget* targetWidget = nullptr;
2671 0 : nsIFrame* targetFrame = contentUnderMouse->GetPrimaryFrame();
2672 0 : nsIObjectFrame* pluginFrame = do_QueryFrame(targetFrame);
2673 0 : if (pluginFrame) {
2674 0 : targetWidget = pluginFrame->GetWidget();
2675 0 : } else if (targetFrame) {
2676 0 : targetWidget = targetFrame->GetNearestWidget();
2677 : }
2678 0 : if (aEvent->mWidget == targetWidget) {
2679 0 : aEvent->mReply.mWidgetIsHit = true;
2680 : }
2681 : }
2682 :
2683 0 : aEvent->mSucceeded = true;
2684 0 : return NS_OK;
2685 : }
2686 :
2687 : /* static */ nsresult
2688 0 : ContentEventHandler::GetFlatTextLengthInRange(
2689 : const NodePosition& aStartPosition,
2690 : const NodePosition& aEndPosition,
2691 : nsIContent* aRootContent,
2692 : uint32_t* aLength,
2693 : LineBreakType aLineBreakType,
2694 : bool aIsRemovingNode /* = false */)
2695 : {
2696 0 : if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) ||
2697 0 : NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) {
2698 0 : return NS_ERROR_INVALID_ARG;
2699 : }
2700 :
2701 0 : if (aStartPosition == aEndPosition) {
2702 0 : *aLength = 0;
2703 0 : return NS_OK;
2704 : }
2705 :
2706 : // Don't create nsContentIterator instance until it's really necessary since
2707 : // destroying without initializing causes unexpected NS_ASSERTION() call.
2708 0 : nsCOMPtr<nsIContentIterator> iter;
2709 :
2710 : // Working with ContentIterator, we may need to adjust the end position for
2711 : // including it forcibly.
2712 0 : NodePosition endPosition(aEndPosition);
2713 :
2714 : // This may be called for retrieving the text of removed nodes. Even in this
2715 : // case, the node thinks it's still in the tree because UnbindFromTree() will
2716 : // be called after here. However, the node was already removed from the
2717 : // array of children of its parent. So, be careful to handle this case.
2718 0 : if (aIsRemovingNode) {
2719 0 : DebugOnly<nsIContent*> parent = aStartPosition.mNode->GetParent();
2720 0 : MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.mNode) == -1,
2721 : "At removing the node, the node shouldn't be in the array of children "
2722 : "of its parent");
2723 0 : MOZ_ASSERT(aStartPosition.mNode == endPosition.mNode,
2724 : "At removing the node, start and end node should be same");
2725 0 : MOZ_ASSERT(aStartPosition.mOffset == 0,
2726 : "When the node is being removed, the start offset should be 0");
2727 0 : MOZ_ASSERT(static_cast<uint32_t>(endPosition.mOffset) ==
2728 : endPosition.mNode->GetChildCount(),
2729 : "When the node is being removed, the end offset should be child count");
2730 0 : iter = NS_NewPreContentIterator();
2731 0 : nsresult rv = iter->Init(aStartPosition.mNode);
2732 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2733 0 : return rv;
2734 : }
2735 : } else {
2736 0 : RefPtr<nsRange> prev = new nsRange(aRootContent);
2737 0 : nsresult rv = aStartPosition.SetToRangeStart(prev);
2738 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2739 0 : return rv;
2740 : }
2741 :
2742 : // When the end position is immediately after non-root element's open tag,
2743 : // we need to include a line break caused by the open tag.
2744 0 : if (endPosition.mNode != aRootContent &&
2745 0 : endPosition.IsImmediatelyAfterOpenTag()) {
2746 0 : if (endPosition.mNode->HasChildren()) {
2747 : // When the end node has some children, move the end position to before
2748 : // the open tag of its first child.
2749 0 : nsINode* firstChild = endPosition.mNode->GetFirstChild();
2750 0 : if (NS_WARN_IF(!firstChild)) {
2751 0 : return NS_ERROR_FAILURE;
2752 : }
2753 0 : endPosition = NodePositionBefore(firstChild, 0);
2754 : } else {
2755 : // When the end node is empty, move the end position after the node.
2756 0 : nsIContent* parentContent = endPosition.mNode->GetParent();
2757 0 : if (NS_WARN_IF(!parentContent)) {
2758 0 : return NS_ERROR_FAILURE;
2759 : }
2760 0 : int32_t indexInParent = parentContent->IndexOf(endPosition.mNode);
2761 0 : if (NS_WARN_IF(indexInParent < 0)) {
2762 0 : return NS_ERROR_FAILURE;
2763 : }
2764 0 : endPosition = NodePositionBefore(parentContent, indexInParent + 1);
2765 : }
2766 : }
2767 :
2768 0 : if (endPosition.OffsetIsValid()) {
2769 : // Offset is within node's length; set end of range to that offset
2770 0 : rv = endPosition.SetToRangeEnd(prev);
2771 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2772 0 : return rv;
2773 : }
2774 0 : iter = NS_NewPreContentIterator();
2775 0 : rv = iter->Init(prev);
2776 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2777 0 : return rv;
2778 : }
2779 0 : } else if (endPosition.mNode != aRootContent) {
2780 : // Offset is past node's length; set end of range to end of node
2781 0 : rv = endPosition.SetToRangeEndAfter(prev);
2782 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2783 0 : return rv;
2784 : }
2785 0 : iter = NS_NewPreContentIterator();
2786 0 : rv = iter->Init(prev);
2787 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2788 0 : return rv;
2789 : }
2790 : } else {
2791 : // Offset is past the root node; set end of range to end of root node
2792 0 : iter = NS_NewPreContentIterator();
2793 0 : rv = iter->Init(aRootContent);
2794 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2795 0 : return rv;
2796 : }
2797 : }
2798 : }
2799 :
2800 0 : *aLength = 0;
2801 0 : for (; !iter->IsDone(); iter->Next()) {
2802 0 : nsINode* node = iter->GetCurrentNode();
2803 0 : if (NS_WARN_IF(!node)) {
2804 0 : break;
2805 : }
2806 0 : if (!node->IsContent()) {
2807 0 : continue;
2808 : }
2809 0 : nsIContent* content = node->AsContent();
2810 :
2811 0 : if (node->IsNodeOfType(nsINode::eTEXT)) {
2812 : // Note: our range always starts from offset 0
2813 0 : if (node == endPosition.mNode) {
2814 0 : *aLength += GetTextLength(content, aLineBreakType,
2815 0 : endPosition.mOffset);
2816 : } else {
2817 0 : *aLength += GetTextLength(content, aLineBreakType);
2818 : }
2819 0 : } else if (ShouldBreakLineBefore(content, aRootContent)) {
2820 : // If the start position is start of this node but doesn't include the
2821 : // open tag, don't append the line break length.
2822 0 : if (node == aStartPosition.mNode && !aStartPosition.IsBeforeOpenTag()) {
2823 0 : continue;
2824 : }
2825 : // If the end position is before the open tag, don't append the line
2826 : // break length.
2827 0 : if (node == endPosition.mNode && endPosition.IsBeforeOpenTag()) {
2828 0 : continue;
2829 : }
2830 0 : *aLength += GetBRLength(aLineBreakType);
2831 : }
2832 : }
2833 0 : return NS_OK;
2834 : }
2835 :
2836 : nsresult
2837 0 : ContentEventHandler::GetStartOffset(nsRange* aRange,
2838 : uint32_t* aOffset,
2839 : LineBreakType aLineBreakType)
2840 : {
2841 0 : MOZ_ASSERT(aRange);
2842 0 : return GetFlatTextLengthInRange(
2843 0 : NodePosition(mRootContent, 0),
2844 0 : NodePosition(aRange->GetStartContainer(), aRange->StartOffset()),
2845 0 : mRootContent, aOffset, aLineBreakType);
2846 : }
2847 :
2848 : nsresult
2849 0 : ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
2850 : {
2851 0 : MOZ_ASSERT(aRange);
2852 0 : MOZ_ASSERT(aRange->Collapsed());
2853 :
2854 0 : if (!aRange || !aRange->Collapsed()) {
2855 0 : return NS_ERROR_INVALID_ARG;
2856 : }
2857 :
2858 0 : nsCOMPtr<nsINode> container = aRange->GetStartContainer();
2859 0 : int32_t offsetInParentNode = aRange->StartOffset();
2860 0 : if (NS_WARN_IF(!container) || NS_WARN_IF(offsetInParentNode < 0)) {
2861 0 : return NS_ERROR_INVALID_ARG;
2862 : }
2863 :
2864 : // If the node is text node, we don't need to modify aRange.
2865 0 : if (container->IsNodeOfType(nsINode::eTEXT)) {
2866 0 : return NS_OK;
2867 : }
2868 :
2869 : // If the container is not a text node but it has a text node at the offset,
2870 : // we should adjust the range into the text node.
2871 : // NOTE: This is emulating similar situation of EditorBase.
2872 0 : nsINode* childNode = nullptr;
2873 0 : int32_t offsetInChildNode = -1;
2874 0 : if (!offsetInParentNode && container->HasChildren()) {
2875 : // If the range is the start of the container, adjusted the range to the
2876 : // start of the first child.
2877 0 : childNode = container->GetFirstChild();
2878 0 : offsetInChildNode = 0;
2879 0 : } else if (static_cast<uint32_t>(offsetInParentNode) <
2880 0 : container->GetChildCount()) {
2881 : // If the range is next to a child node, adjust the range to the end of
2882 : // the previous child.
2883 0 : childNode = container->GetChildAt(offsetInParentNode - 1);
2884 0 : offsetInChildNode = childNode->Length();
2885 : }
2886 :
2887 : // But if the found node isn't a text node, we cannot modify the range.
2888 0 : if (!childNode || !childNode->IsNodeOfType(nsINode::eTEXT) ||
2889 0 : NS_WARN_IF(offsetInChildNode < 0)) {
2890 0 : return NS_OK;
2891 : }
2892 :
2893 0 : nsresult rv = aRange->CollapseTo(childNode, offsetInChildNode);
2894 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2895 0 : return rv;
2896 : }
2897 0 : return NS_OK;
2898 : }
2899 :
2900 : nsresult
2901 0 : ContentEventHandler::GetStartFrameAndOffset(const nsRange* aRange,
2902 : nsIFrame*& aFrame,
2903 : int32_t& aOffsetInFrame)
2904 : {
2905 0 : MOZ_ASSERT(aRange);
2906 :
2907 0 : aFrame = nullptr;
2908 0 : aOffsetInFrame = -1;
2909 :
2910 0 : nsINode* node = aRange->GetStartContainer();
2911 0 : if (NS_WARN_IF(!node) ||
2912 0 : NS_WARN_IF(!node->IsNodeOfType(nsINode::eCONTENT))) {
2913 0 : return NS_ERROR_FAILURE;
2914 : }
2915 0 : nsIContent* content = static_cast<nsIContent*>(node);
2916 0 : RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
2917 0 : aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
2918 : fs->GetHint(), &aOffsetInFrame);
2919 0 : if (NS_WARN_IF(!aFrame)) {
2920 0 : return NS_ERROR_FAILURE;
2921 : }
2922 0 : return NS_OK;
2923 : }
2924 :
2925 : nsresult
2926 0 : ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
2927 : nsRect& aRect)
2928 : {
2929 0 : NS_ASSERTION(aFrame, "aFrame must not be null");
2930 :
2931 0 : nsPresContext* thisPC = aFrame->PresContext();
2932 0 : nsPresContext* rootPC = thisPC->GetRootPresContext();
2933 0 : if (NS_WARN_IF(!rootPC)) {
2934 0 : return NS_ERROR_FAILURE;
2935 : }
2936 0 : nsIFrame* rootFrame = rootPC->PresShell()->GetRootFrame();
2937 0 : if (NS_WARN_IF(!rootFrame)) {
2938 0 : return NS_ERROR_FAILURE;
2939 : }
2940 :
2941 0 : aRect = nsLayoutUtils::TransformFrameRectToAncestor(aFrame, aRect, rootFrame);
2942 :
2943 : // TransformFrameRectToAncestor returned the rect in the ancestor's appUnits,
2944 : // but we want it in aFrame's units (in case of different full-zoom factors),
2945 : // so convert back.
2946 0 : aRect = aRect.ScaleToOtherAppUnitsRoundOut(rootPC->AppUnitsPerDevPixel(),
2947 : thisPC->AppUnitsPerDevPixel());
2948 :
2949 0 : return NS_OK;
2950 : }
2951 :
2952 0 : static void AdjustRangeForSelection(nsIContent* aRoot,
2953 : nsINode** aNode,
2954 : int32_t* aNodeOffset)
2955 : {
2956 0 : nsINode* node = *aNode;
2957 0 : int32_t nodeOffset = *aNodeOffset;
2958 0 : if (aRoot == node || NS_WARN_IF(!node->GetParent()) ||
2959 0 : !node->IsNodeOfType(nsINode::eTEXT)) {
2960 0 : return;
2961 : }
2962 :
2963 : // When the offset is at the end of the text node, set it to after the
2964 : // text node, to make sure the caret is drawn on a new line when the last
2965 : // character of the text node is '\n' in <textarea>.
2966 : int32_t textLength =
2967 0 : static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength());
2968 0 : MOZ_ASSERT(nodeOffset <= textLength, "Offset is past length of text node");
2969 0 : if (nodeOffset != textLength) {
2970 0 : return;
2971 : }
2972 :
2973 0 : nsIContent* aRootParent = aRoot->GetParent();
2974 0 : if (NS_WARN_IF(!aRootParent)) {
2975 0 : return;
2976 : }
2977 : // If the root node is not an anonymous div of <textarea>, we don't need to
2978 : // do this hack. If you did this, ContentEventHandler couldn't distinguish
2979 : // if the range includes open tag of the next node in some cases, e.g.,
2980 : // textNode]<p></p> vs. textNode<p>]</p>
2981 0 : if (!aRootParent->IsHTMLElement(nsGkAtoms::textarea)) {
2982 0 : return;
2983 : }
2984 :
2985 0 : *aNode = node->GetParent();
2986 0 : MOZ_ASSERT((*aNode)->IndexOf(node) != -1);
2987 0 : *aNodeOffset = (*aNode)->IndexOf(node) + 1;
2988 : }
2989 :
2990 : nsresult
2991 0 : ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
2992 : {
2993 0 : aEvent->mSucceeded = false;
2994 :
2995 : // Get selection to manipulate
2996 : // XXX why do we need to get them from ISM? This method should work fine
2997 : // without ISM.
2998 0 : nsCOMPtr<nsISelection> sel;
2999 : nsresult rv =
3000 0 : IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(sel),
3001 0 : getter_AddRefs(mRootContent));
3002 0 : mSelection = sel ? sel->AsSelection() : nullptr;
3003 0 : if (rv != NS_ERROR_NOT_AVAILABLE) {
3004 0 : NS_ENSURE_SUCCESS(rv, rv);
3005 : } else {
3006 0 : rv = Init(aEvent);
3007 0 : NS_ENSURE_SUCCESS(rv, rv);
3008 : }
3009 :
3010 : // Get range from offset and length
3011 0 : RefPtr<nsRange> range = new nsRange(mRootContent);
3012 0 : rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength,
3013 : GetLineBreakType(aEvent),
3014 0 : aEvent->mExpandToClusterBoundary);
3015 0 : NS_ENSURE_SUCCESS(rv, rv);
3016 :
3017 0 : nsINode* startNode = range->GetStartContainer();
3018 0 : nsINode* endNode = range->GetEndContainer();
3019 0 : int32_t startNodeOffset = range->StartOffset();
3020 0 : int32_t endNodeOffset = range->EndOffset();
3021 0 : AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
3022 0 : AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
3023 0 : if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) ||
3024 0 : NS_WARN_IF(startNodeOffset < 0) || NS_WARN_IF(endNodeOffset < 0)) {
3025 0 : return NS_ERROR_UNEXPECTED;
3026 : }
3027 :
3028 0 : mSelection->StartBatchChanges();
3029 :
3030 : // Clear selection first before setting
3031 0 : rv = mSelection->RemoveAllRanges();
3032 : // Need to call EndBatchChanges at the end even if call failed
3033 0 : if (NS_SUCCEEDED(rv)) {
3034 0 : if (aEvent->mReversed) {
3035 0 : rv = mSelection->Collapse(endNode, endNodeOffset);
3036 : } else {
3037 0 : rv = mSelection->Collapse(startNode, startNodeOffset);
3038 : }
3039 0 : if (NS_SUCCEEDED(rv) &&
3040 0 : (startNode != endNode || startNodeOffset != endNodeOffset)) {
3041 0 : if (aEvent->mReversed) {
3042 0 : rv = mSelection->Extend(startNode, startNodeOffset);
3043 : } else {
3044 0 : rv = mSelection->Extend(endNode, endNodeOffset);
3045 : }
3046 : }
3047 : }
3048 :
3049 : // Pass the eSetSelection events reason along with the BatchChange-end
3050 : // selection change notifications.
3051 0 : mSelection->EndBatchChangesInternal(aEvent->mReason);
3052 0 : NS_ENSURE_SUCCESS(rv, rv);
3053 :
3054 0 : mSelection->ScrollIntoViewInternal(
3055 : nsISelectionController::SELECTION_FOCUS_REGION,
3056 0 : false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis());
3057 0 : aEvent->mSucceeded = true;
3058 0 : return NS_OK;
3059 : }
3060 :
3061 : nsRect
3062 0 : ContentEventHandler::FrameRelativeRect::RectRelativeTo(
3063 : nsIFrame* aDestFrame) const
3064 : {
3065 0 : if (!mBaseFrame || NS_WARN_IF(!aDestFrame)) {
3066 0 : return nsRect();
3067 : }
3068 :
3069 0 : if (NS_WARN_IF(aDestFrame->PresContext() != mBaseFrame->PresContext())) {
3070 0 : return nsRect();
3071 : }
3072 :
3073 0 : if (aDestFrame == mBaseFrame) {
3074 0 : return mRect;
3075 : }
3076 :
3077 0 : nsIFrame* rootFrame = mBaseFrame->PresContext()->PresShell()->GetRootFrame();
3078 : nsRect baseFrameRectInRootFrame =
3079 0 : nsLayoutUtils::TransformFrameRectToAncestor(mBaseFrame, nsRect(),
3080 0 : rootFrame);
3081 : nsRect destFrameRectInRootFrame =
3082 0 : nsLayoutUtils::TransformFrameRectToAncestor(aDestFrame, nsRect(),
3083 0 : rootFrame);
3084 : nsPoint difference =
3085 0 : destFrameRectInRootFrame.TopLeft() - baseFrameRectInRootFrame.TopLeft();
3086 0 : return mRect - difference;
3087 : }
3088 :
3089 : } // namespace mozilla
|