Coverage Report

Created: 2018-09-25 14:53

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