dom/events/ContentEventHandler.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:681c54503f1a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et 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/TextEvents.h"
10 #include "mozilla/dom/Element.h"
11 #include "nsCaret.h"
12 #include "nsCOMPtr.h"
13 #include "nsContentUtils.h"
14 #include "nsCopySupport.h"
15 #include "nsFocusManager.h"
16 #include "nsFrameSelection.h"
17 #include "nsIContentIterator.h"
18 #include "nsIPresShell.h"
19 #include "nsISelection.h"
20 #include "nsISelectionController.h"
21 #include "nsISelectionPrivate.h"
22 #include "nsIDOMRange.h"
23 #include "nsIFrame.h"
24 #include "nsIObjectFrame.h"
25 #include "nsLayoutUtils.h"
26 #include "nsPresContext.h"
27 #include "nsRange.h"
28 #include "nsTextFragment.h"
29 #include "nsTextFrame.h"
30 #include "nsView.h"
31
32 #include <algorithm>
33
34 namespace mozilla {
35
36 using namespace dom;
37 using namespace widget;
38
39 /******************************************************************/
40 /* ContentEventHandler */
41 /******************************************************************/
42
43 ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
44 : mPresContext(aPresContext)
45 , mPresShell(aPresContext->GetPresShell())
46 , mSelection(nullptr)
47 , mFirstSelectedRange(nullptr)
48 , mRootContent(nullptr)
49 {
50 }
51
52 nsresult
53 ContentEventHandler::InitBasic()
54 {
55 NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
56
57 // If text frame which has overflowing selection underline is dirty,
58 // we need to flush the pending reflow here.
59 mPresShell->FlushPendingNotifications(Flush_Layout);
60
61 // Flushing notifications can cause mPresShell to be destroyed (bug 577963).
62 NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
63
64 return NS_OK;
65 }
66
67 nsresult
68 ContentEventHandler::InitCommon()
69 {
70 if (mSelection) {
71 return NS_OK;
72 }
73
74 nsresult rv = InitBasic();
75 NS_ENSURE_SUCCESS(rv, rv);
76
77 nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(),
78 getter_AddRefs(mSelection));
79
80 nsCOMPtr<nsIDOMRange> firstRange;
81 rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
82 // This shell doesn't support selection.
83 if (NS_FAILED(rv)) {
84 return NS_ERROR_NOT_AVAILABLE;
85 }
86 mFirstSelectedRange = static_cast<nsRange*>(firstRange.get());
87
88 nsINode* startNode = mFirstSelectedRange->GetStartParent();
89 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
90 nsINode* endNode = mFirstSelectedRange->GetEndParent();
91 NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
92
93 // See bug 537041 comment 5, the range could have removed node.
94 NS_ENSURE_TRUE(startNode->GetCurrentDoc() == mPresShell->GetDocument(),
95 NS_ERROR_NOT_AVAILABLE);
96 NS_ASSERTION(startNode->GetCurrentDoc() == endNode->GetCurrentDoc(),
97 "mFirstSelectedRange crosses the document boundary");
98
99 mRootContent = startNode->GetSelectionRootContent(mPresShell);
100 NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
101 return NS_OK;
102 }
103
104 nsresult
105 ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
106 {
107 NS_ASSERTION(aEvent, "aEvent must not be null");
108
109 nsresult rv = InitCommon();
110 NS_ENSURE_SUCCESS(rv, rv);
111
112 aEvent->mSucceeded = false;
113
114 aEvent->mReply.mContentsRoot = mRootContent.get();
115
116 bool isCollapsed;
117 rv = mSelection->GetIsCollapsed(&isCollapsed);
118 NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
119 aEvent->mReply.mHasSelection = !isCollapsed;
120
121 nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
122 NS_ASSERTION(caret, "GetCaret returned null");
123
124 nsRect r;
125 nsIFrame* frame = caret->GetGeometry(mSelection, &r);
126 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
127
128 aEvent->mReply.mFocusedWidget = frame->GetNearestWidget();
129
130 return NS_OK;
131 }
132
133 nsresult
134 ContentEventHandler::Init(WidgetSelectionEvent* aEvent)
135 {
136 NS_ASSERTION(aEvent, "aEvent must not be null");
137
138 nsresult rv = InitCommon();
139 NS_ENSURE_SUCCESS(rv, rv);
140
141 aEvent->mSucceeded = false;
142
143 return NS_OK;
144 }
145
146 nsIContent*
147 ContentEventHandler::GetFocusedContent()
148 {
149 nsIDocument* doc = mPresShell->GetDocument();
150 if (!doc) {
151 return nullptr;
152 }
153 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(doc->GetWindow());
154 nsCOMPtr<nsPIDOMWindow> focusedWindow;
155 return nsFocusManager::GetFocusedDescendant(window, true,
156 getter_AddRefs(focusedWindow));
157 }
158
159 bool
160 ContentEventHandler::IsPlugin(nsIContent* aContent)
161 {
162 return aContent &&
163 aContent->GetDesiredIMEState().mEnabled == IMEState::PLUGIN;
164 }
165
166 nsresult
167 ContentEventHandler::QueryContentRect(nsIContent* aContent,
168 WidgetQueryContentEvent* aEvent)
169 {
170 NS_PRECONDITION(aContent, "aContent must not be null");
171
172 nsIFrame* frame = aContent->GetPrimaryFrame();
173 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
174
175 // get rect for first frame
176 nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
177 nsresult rv = ConvertToRootViewRelativeOffset(frame, resultRect);
178 NS_ENSURE_SUCCESS(rv, rv);
179
180 // account for any additional frames
181 while ((frame = frame->GetNextContinuation()) != nullptr) {
182 nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
183 rv = ConvertToRootViewRelativeOffset(frame, frameRect);
184 NS_ENSURE_SUCCESS(rv, rv);
185 resultRect.UnionRect(resultRect, frameRect);
186 }
187
188 aEvent->mReply.mRect =
189 resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
190 aEvent->mSucceeded = true;
191
192 return NS_OK;
193 }
194
195 // Editor places a bogus BR node under its root content if the editor doesn't
196 // have any text. This happens even for single line editors.
197 // When we get text content and when we change the selection,
198 // we don't want to include the bogus BRs at the end.
199 static bool IsContentBR(nsIContent* aContent)
200 {
201 return aContent->IsHTML() &&
202 aContent->Tag() == nsGkAtoms::br &&
203 !aContent->AttrValueIs(kNameSpaceID_None,
204 nsGkAtoms::type,
205 nsGkAtoms::moz,
206 eIgnoreCase) &&
207 !aContent->AttrValueIs(kNameSpaceID_None,
208 nsGkAtoms::mozeditorbogusnode,
209 nsGkAtoms::_true,
210 eIgnoreCase);
211 }
212
213 static void ConvertToNativeNewlines(nsAFlatString& aString)
214 {
215 #if defined(XP_MACOSX)
216 // XXX Mac OS X doesn't use "\r".
217 aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r"));
218 #elif defined(XP_WIN)
219 aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
220 #endif
221 }
222
223 static void AppendString(nsAString& aString, nsIContent* aContent)
224 {
225 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
226 "aContent is not a text node!");
227 const nsTextFragment* text = aContent->GetText();
228 if (!text) {
229 return;
230 }
231 text->AppendTo(aString);
232 }
233
234 static void AppendSubString(nsAString& aString, nsIContent* aContent,
235 uint32_t aXPOffset, uint32_t aXPLength)
236 {
237 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
238 "aContent is not a text node!");
239 const nsTextFragment* text = aContent->GetText();
240 if (!text) {
241 return;
242 }
243 text->AppendTo(aString, int32_t(aXPOffset), int32_t(aXPLength));
244 }
245
246 #if defined(XP_WIN)
247 static uint32_t CountNewlinesInXPLength(nsIContent* aContent,
248 uint32_t aXPLength)
249 {
250 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
251 "aContent is not a text node!");
252 const nsTextFragment* text = aContent->GetText();
253 if (!text) {
254 return 0;
255 }
256 // For automated tests, we should abort on debug build.
257 NS_ABORT_IF_FALSE(
258 (aXPLength == UINT32_MAX || aXPLength <= text->GetLength()),
259 "aXPLength is out-of-bounds");
260 const uint32_t length = std::min(aXPLength, text->GetLength());
261 uint32_t newlines = 0;
262 for (uint32_t i = 0; i < length; ++i) {
263 if (text->CharAt(i) == '\n') {
264 ++newlines;
265 }
266 }
267 return newlines;
268 }
269
270 static uint32_t CountNewlinesInNativeLength(nsIContent* aContent,
271 uint32_t aNativeLength)
272 {
273 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
274 "aContent is not a text node!");
275 const nsTextFragment* text = aContent->GetText();
276 if (!text) {
277 return 0;
278 }
279 // For automated tests, we should abort on debug build.
280 MOZ_ASSERT(
281 (aNativeLength == UINT32_MAX || aNativeLength <= text->GetLength() * 2),
282 "aNativeLength is unexpected value");
283 const uint32_t xpLength = text->GetLength();
284 uint32_t newlines = 0;
285 for (uint32_t i = 0, nativeOffset = 0;
286 i < xpLength && nativeOffset < aNativeLength;
287 ++i, ++nativeOffset) {
288 // For automated tests, we should abort on debug build.
289 NS_ABORT_IF_FALSE(i < text->GetLength(), "i is out-of-bounds");
290 if (text->CharAt(i) == '\n') {
291 ++newlines;
292 ++nativeOffset;
293 }
294 }
295 return newlines;
296 }
297 #endif
298
299 /* static */ uint32_t
300 ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
301 uint32_t aMaxLength)
302 {
303 return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
304 }
305
306 /* static */ uint32_t
307 ContentEventHandler::GetTextLength(nsIContent* aContent,
308 LineBreakType aLineBreakType,
309 uint32_t aMaxLength)
310 {
311 if (aContent->IsNodeOfType(nsINode::eTEXT)) {
312 uint32_t textLengthDifference =
313 #if defined(XP_MACOSX)
314 // On Mac, the length of a native newline ("\r") is equal to the length of
315 // the XP newline ("\n"), so the native length is the same as the XP
316 // length.
317 0;
318 #elif defined(XP_WIN)
319 // On Windows, the length of a native newline ("\r\n") is twice the length
320 // of the XP newline ("\n"), so XP length is equal to the length of the
321 // native offset plus the number of newlines encountered in the string.
322 (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
323 CountNewlinesInXPLength(aContent, aMaxLength) : 0;
324 #else
325 // On other platforms, the native and XP newlines are the same.
326 0;
327 #endif
328
329 const nsTextFragment* text = aContent->GetText();
330 if (!text) {
331 return 0;
332 }
333 uint32_t length = std::min(text->GetLength(), aMaxLength);
334 return length + textLengthDifference;
335 } else if (IsContentBR(aContent)) {
336 #if defined(XP_WIN)
337 // Length of \r\n
338 return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
339 #else
340 return 1;
341 #endif
342 }
343 return 0;
344 }
345
346 static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
347 {
348 #if defined(XP_MACOSX)
349 // On Mac, the length of a native newline ("\r") is equal to the length of
350 // the XP newline ("\n"), so the native offset is the same as the XP offset.
351 return aNativeOffset;
352 #elif defined(XP_WIN)
353 // On Windows, the length of a native newline ("\r\n") is twice the length of
354 // the XP newline ("\n"), so XP offset is equal to the length of the native
355 // offset minus the number of newlines encountered in the string.
356 return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
357 #else
358 // On other platforms, the native and XP newlines are the same.
359 return aNativeOffset;
360 #endif
361 }
362
363 static nsresult GenerateFlatTextContent(nsRange* aRange,
364 nsAFlatString& aString,
365 LineBreakType aLineBreakType)
366 {
367 nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
368 iter->Init(aRange);
369
370 NS_ASSERTION(aString.IsEmpty(), "aString must be empty string");
371
372 nsINode* startNode = aRange->GetStartParent();
373 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
374 nsINode* endNode = aRange->GetEndParent();
375 NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
376
377 if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
378 nsIContent* content = static_cast<nsIContent*>(startNode);
379 AppendSubString(aString, content, aRange->StartOffset(),
380 aRange->EndOffset() - aRange->StartOffset());
381 ConvertToNativeNewlines(aString);
382 return NS_OK;
383 }
384
385 nsAutoString tmpStr;
386 for (; !iter->IsDone(); iter->Next()) {
387 nsINode* node = iter->GetCurrentNode();
388 if (!node) {
389 break;
390 }
391 if (!node->IsNodeOfType(nsINode::eCONTENT)) {
392 continue;
393 }
394 nsIContent* content = static_cast<nsIContent*>(node);
395
396 if (content->IsNodeOfType(nsINode::eTEXT)) {
397 if (content == startNode) {
398 AppendSubString(aString, content, aRange->StartOffset(),
399 content->TextLength() - aRange->StartOffset());
400 } else if (content == endNode) {
401 AppendSubString(aString, content, 0, aRange->EndOffset());
402 } else {
403 AppendString(aString, content);
404 }
405 } else if (IsContentBR(content)) {
406 aString.Append(char16_t('\n'));
407 }
408 }
409 if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
410 ConvertToNativeNewlines(aString);
411 }
412 return NS_OK;
413 }
414
415 nsresult
416 ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
417 bool aForward,
418 uint32_t* aXPOffset)
419 {
420 // XXX This method assumes that the frame boundaries must be cluster
421 // boundaries. It's false, but no problem now, maybe.
422 if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
423 *aXPOffset == 0 || *aXPOffset == aContent->TextLength()) {
424 return NS_OK;
425 }
426
427 NS_ASSERTION(*aXPOffset <= aContent->TextLength(),
428 "offset is out of range.");
429
430 nsRefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
431 int32_t offsetInFrame;
432 nsFrameSelection::HINT hint =
433 aForward ? nsFrameSelection::HINTLEFT : nsFrameSelection::HINTRIGHT;
434 nsIFrame* frame = fs->GetFrameForNodeOffset(aContent, int32_t(*aXPOffset),
435 hint, &offsetInFrame);
436 if (!frame) {
437 // This content doesn't have any frames, we only can check surrogate pair...
438 const nsTextFragment* text = aContent->GetText();
439 NS_ENSURE_TRUE(text, NS_ERROR_FAILURE);
440 if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
441 NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1))) {
442 *aXPOffset += aForward ? 1 : -1;
443 }
444 return NS_OK;
445 }
446 int32_t startOffset, endOffset;
447 nsresult rv = frame->GetOffsets(startOffset, endOffset);
448 NS_ENSURE_SUCCESS(rv, rv);
449 if (*aXPOffset == static_cast<uint32_t>(startOffset) ||
450 *aXPOffset == static_cast<uint32_t>(endOffset)) {
451 return NS_OK;
452 }
453 if (frame->GetType() != nsGkAtoms::textFrame) {
454 return NS_ERROR_FAILURE;
455 }
456 nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
457 int32_t newOffsetInFrame = *aXPOffset - startOffset;
458 newOffsetInFrame += aForward ? -1 : 1;
459 textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame);
460 *aXPOffset = startOffset + newOffsetInFrame;
461 return NS_OK;
462 }
463
464 nsresult
465 ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
466 uint32_t aOffset,
467 uint32_t aLength,
468 LineBreakType aLineBreakType,
469 bool aExpandToClusterBoundaries,
470 uint32_t* aNewOffset)
471 {
472 if (aNewOffset) {
473 *aNewOffset = aOffset;
474 }
475
476 nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
477 nsresult rv = iter->Init(mRootContent);
478 NS_ENSURE_SUCCESS(rv, rv);
479
480 uint32_t offset = 0;
481 uint32_t endOffset = aOffset + aLength;
482 bool startSet = false;
483 for (; !iter->IsDone(); iter->Next()) {
484 nsINode* node = iter->GetCurrentNode();
485 if (!node) {
486 break;
487 }
488 if (!node->IsNodeOfType(nsINode::eCONTENT)) {
489 continue;
490 }
491 nsIContent* content = static_cast<nsIContent*>(node);
492
493 uint32_t textLength = GetTextLength(content, aLineBreakType);
494 if (!textLength) {
495 continue;
496 }
497
498 if (offset <= aOffset && aOffset < offset + textLength) {
499 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
500 NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
501
502 uint32_t xpOffset;
503 if (!content->IsNodeOfType(nsINode::eTEXT)) {
504 xpOffset = 0;
505 } else {
506 xpOffset = aOffset - offset;
507 if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
508 xpOffset = ConvertToXPOffset(content, xpOffset);
509 }
510 }
511
512 if (aExpandToClusterBoundaries) {
513 uint32_t oldXPOffset = xpOffset;
514 rv = ExpandToClusterBoundary(content, false, &xpOffset);
515 NS_ENSURE_SUCCESS(rv, rv);
516 if (aNewOffset) {
517 // This is correct since a cluster shouldn't include line break.
518 *aNewOffset -= (oldXPOffset - xpOffset);
519 }
520 }
521
522 rv = aRange->SetStart(domNode, int32_t(xpOffset));
523 NS_ENSURE_SUCCESS(rv, rv);
524 startSet = true;
525 if (aLength == 0) {
526 // Ensure that the end offset and the start offset are same.
527 rv = aRange->SetEnd(domNode, int32_t(xpOffset));
528 NS_ENSURE_SUCCESS(rv, rv);
529 return NS_OK;
530 }
531 }
532 if (endOffset <= offset + textLength) {
533 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
534 NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
535
536 uint32_t xpOffset;
537 if (content->IsNodeOfType(nsINode::eTEXT)) {
538 xpOffset = endOffset - offset;
539 if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
540 xpOffset = ConvertToXPOffset(content, xpOffset);
541 }
542 if (aExpandToClusterBoundaries) {
543 rv = ExpandToClusterBoundary(content, true, &xpOffset);
544 NS_ENSURE_SUCCESS(rv, rv);
545 }
546 } else {
547 // Use first position of next node, because the end node is ignored
548 // by ContentIterator when the offset is zero.
549 xpOffset = 0;
550 iter->Next();
551 if (iter->IsDone()) {
552 break;
553 }
554 domNode = do_QueryInterface(iter->GetCurrentNode());
555 }
556
557 rv = aRange->SetEnd(domNode, int32_t(xpOffset));
558 NS_ENSURE_SUCCESS(rv, rv);
559 return NS_OK;
560 }
561
562 offset += textLength;
563 }
564
565 if (offset < aOffset) {
566 return NS_ERROR_FAILURE;
567 }
568
569 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mRootContent));
570 NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!");
571 if (!startSet) {
572 MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
573 rv = aRange->SetStart(domNode, int32_t(mRootContent->GetChildCount()));
574 NS_ENSURE_SUCCESS(rv, rv);
575 if (aNewOffset) {
576 *aNewOffset = offset;
577 }
578 }
579 rv = aRange->SetEnd(domNode, int32_t(mRootContent->GetChildCount()));
580 NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
581 return rv;
582 }
583
584 /* static */ LineBreakType
585 ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent* aEvent)
586 {
587 return GetLineBreakType(aEvent->mUseNativeLineBreak);
588 }
589
590 /* static */ LineBreakType
591 ContentEventHandler::GetLineBreakType(WidgetSelectionEvent* aEvent)
592 {
593 return GetLineBreakType(aEvent->mUseNativeLineBreak);
594 }
595
596 /* static */ LineBreakType
597 ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak)
598 {
599 return aUseNativeLineBreak ?
600 LINE_BREAK_TYPE_NATIVE : LINE_BREAK_TYPE_XP;
601 }
602
603 nsresult
604 ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
605 {
606 nsresult rv = Init(aEvent);
607 if (NS_FAILED(rv)) {
608 return rv;
609 }
610
611 NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
612 "The reply string must be empty");
613
614 LineBreakType lineBreakType = GetLineBreakType(aEvent);
615 rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange,
616 &aEvent->mReply.mOffset, lineBreakType);
617 NS_ENSURE_SUCCESS(rv, rv);
618
619 nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode;
620 rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode));
621 NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE);
622 rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode));
623 NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE);
624
625 int32_t anchorOffset, focusOffset;
626 rv = mSelection->GetAnchorOffset(&anchorOffset);
627 NS_ENSURE_SUCCESS(rv, rv);
628 rv = mSelection->GetFocusOffset(&focusOffset);
629 NS_ENSURE_SUCCESS(rv, rv);
630
631 nsCOMPtr<nsINode> anchorNode(do_QueryInterface(anchorDomNode));
632 nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode));
633 NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED);
634
635 int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
636 focusNode, focusOffset);
637 aEvent->mReply.mReversed = compare > 0;
638
639 if (compare) {
640 rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString,
641 lineBreakType);
642 NS_ENSURE_SUCCESS(rv, rv);
643 }
644
645 aEvent->mSucceeded = true;
646 return NS_OK;
647 }
648
649 nsresult
650 ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent)
651 {
652 nsresult rv = Init(aEvent);
653 if (NS_FAILED(rv)) {
654 return rv;
655 }
656
657 NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
658 "The reply string must be empty");
659
660 LineBreakType lineBreakType = GetLineBreakType(aEvent);
661
662 nsRefPtr<nsRange> range = new nsRange(mRootContent);
663 rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
664 aEvent->mInput.mLength, lineBreakType, false,
665 &aEvent->mReply.mOffset);
666 NS_ENSURE_SUCCESS(rv, rv);
667
668 rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
669 NS_ENSURE_SUCCESS(rv, rv);
670
671 aEvent->mSucceeded = true;
672
673 return NS_OK;
674 }
675
676 // Adjust to use a child node if possible
677 // to make the returned rect more accurate
678 static nsINode* AdjustTextRectNode(nsINode* aNode,
679 int32_t& aNodeOffset)
680 {
681 int32_t childCount = int32_t(aNode->GetChildCount());
682 nsINode* node = aNode;
683 if (childCount) {
684 if (aNodeOffset < childCount) {
685 node = aNode->GetChildAt(aNodeOffset);
686 aNodeOffset = 0;
687 } else if (aNodeOffset == childCount) {
688 node = aNode->GetChildAt(childCount - 1);
689 aNodeOffset = node->IsNodeOfType(nsINode::eTEXT) ?
690 static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength()) : 1;
691 }
692 }
693 return node;
694 }
695
696 // Similar to nsFrameSelection::GetFrameForNodeOffset,
697 // but this is more flexible for OnQueryTextRect to use
698 static nsresult GetFrameForTextRect(nsINode* aNode,
699 int32_t aNodeOffset,
700 bool aHint,
701 nsIFrame** aReturnFrame)
702 {
703 NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT),
704 NS_ERROR_UNEXPECTED);
705 nsIContent* content = static_cast<nsIContent*>(aNode);
706 nsIFrame* frame = content->GetPrimaryFrame();
707 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
708 int32_t childNodeOffset = 0;
709 return frame->GetChildFrameContainingOffset(aNodeOffset, aHint,
710 &childNodeOffset, aReturnFrame);
711 }
712
713 nsresult
714 ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
715 {
716 nsresult rv = Init(aEvent);
717 if (NS_FAILED(rv)) {
718 return rv;
719 }
720
721 LineBreakType lineBreakType = GetLineBreakType(aEvent);
722 nsRefPtr<nsRange> range = new nsRange(mRootContent);
723 rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
724 aEvent->mInput.mLength, lineBreakType, true,
725 &aEvent->mReply.mOffset);
726 NS_ENSURE_SUCCESS(rv, rv);
727 rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
728 NS_ENSURE_SUCCESS(rv, rv);
729
730 // used to iterate over all contents and their frames
731 nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
732 iter->Init(range);
733
734 // get the starting frame
735 int32_t nodeOffset = range->StartOffset();
736 nsINode* node = iter->GetCurrentNode();
737 if (!node) {
738 node = AdjustTextRectNode(range->GetStartParent(), nodeOffset);
739 }
740 nsIFrame* firstFrame = nullptr;
741 rv = GetFrameForTextRect(node, nodeOffset, true, &firstFrame);
742 NS_ENSURE_SUCCESS(rv, rv);
743
744 // get the starting frame rect
745 nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size());
746 rv = ConvertToRootViewRelativeOffset(firstFrame, rect);
747 NS_ENSURE_SUCCESS(rv, rv);
748 nsRect frameRect = rect;
749 nsPoint ptOffset;
750 firstFrame->GetPointFromOffset(nodeOffset, &ptOffset);
751 // minus 1 to avoid creating an empty rect
752 rect.x += ptOffset.x - 1;
753 rect.width -= ptOffset.x - 1;
754
755 // get the ending frame
756 nodeOffset = range->EndOffset();
757 node = AdjustTextRectNode(range->GetEndParent(), nodeOffset);
758 nsIFrame* lastFrame = nullptr;
759 rv = GetFrameForTextRect(node, nodeOffset, range->Collapsed(), &lastFrame);
760 NS_ENSURE_SUCCESS(rv, rv);
761
762 // iterate over all covered frames
763 for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
764 frame = frame->GetNextContinuation();
765 if (!frame) {
766 do {
767 iter->Next();
768 node = iter->GetCurrentNode();
769 if (!node) {
770 break;
771 }
772 if (!node->IsNodeOfType(nsINode::eCONTENT)) {
773 continue;
774 }
775 frame = static_cast<nsIContent*>(node)->GetPrimaryFrame();
776 } while (!frame && !iter->IsDone());
777 if (!frame) {
778 // this can happen when the end offset of the range is 0.
779 frame = lastFrame;
780 }
781 }
782 frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
783 rv = ConvertToRootViewRelativeOffset(frame, frameRect);
784 NS_ENSURE_SUCCESS(rv, rv);
785 if (frame != lastFrame) {
786 // not last frame, so just add rect to previous result
787 rect.UnionRect(rect, frameRect);
788 }
789 }
790
791 // get the ending frame rect
792 lastFrame->GetPointFromOffset(nodeOffset, &ptOffset);
793 // minus 1 to avoid creating an empty rect
794 frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1;
795
796 if (firstFrame == lastFrame) {
797 rect.IntersectRect(rect, frameRect);
798 } else {
799 rect.UnionRect(rect, frameRect);
800 }
801 aEvent->mReply.mRect =
802 rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
803 aEvent->mSucceeded = true;
804 return NS_OK;
805 }
806
807 nsresult
808 ContentEventHandler::OnQueryEditorRect(WidgetQueryContentEvent* aEvent)
809 {
810 nsresult rv = Init(aEvent);
811 if (NS_FAILED(rv)) {
812 return rv;
813 }
814
815 nsIContent* focusedContent = GetFocusedContent();
816 rv = QueryContentRect(IsPlugin(focusedContent) ?
817 focusedContent : mRootContent.get(), aEvent);
818 NS_ENSURE_SUCCESS(rv, rv);
819 return NS_OK;
820 }
821
822 nsresult
823 ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
824 {
825 nsresult rv = Init(aEvent);
826 if (NS_FAILED(rv)) {
827 return rv;
828 }
829
830 LineBreakType lineBreakType = GetLineBreakType(aEvent);
831
832 nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
833 NS_ASSERTION(caret, "GetCaret returned null");
834
835 // When the selection is collapsed and the queried offset is current caret
836 // position, we should return the "real" caret rect.
837 bool selectionIsCollapsed;
838 rv = mSelection->GetIsCollapsed(&selectionIsCollapsed);
839 NS_ENSURE_SUCCESS(rv, rv);
840
841 if (selectionIsCollapsed) {
842 uint32_t offset;
843 rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset,
844 lineBreakType);
845 NS_ENSURE_SUCCESS(rv, rv);
846 if (offset == aEvent->mInput.mOffset) {
847 nsRect rect;
848 nsIFrame* caretFrame = caret->GetGeometry(mSelection, &rect);
849 if (!caretFrame) {
850 return NS_ERROR_FAILURE;
851 }
852 rv = ConvertToRootViewRelativeOffset(caretFrame, rect);
853 NS_ENSURE_SUCCESS(rv, rv);
854 aEvent->mReply.mRect =
855 rect.ToOutsidePixels(caretFrame->PresContext()->AppUnitsPerDevPixel());
856 aEvent->mReply.mOffset = aEvent->mInput.mOffset;
857 aEvent->mSucceeded = true;
858 return NS_OK;
859 }
860 }
861
862 // Otherwise, we should set the guessed caret rect.
863 nsRefPtr<nsRange> range = new nsRange(mRootContent);
864 rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0,
865 lineBreakType, true,
866 &aEvent->mReply.mOffset);
867 NS_ENSURE_SUCCESS(rv, rv);
868
869 int32_t xpOffsetInFrame;
870 nsIFrame* frame;
871 rv = GetStartFrameAndOffset(range, &frame, &xpOffsetInFrame);
872 NS_ENSURE_SUCCESS(rv, rv);
873
874 nsPoint posInFrame;
875 rv = frame->GetPointFromOffset(range->StartOffset(), &posInFrame);
876 NS_ENSURE_SUCCESS(rv, rv);
877
878 nsRect rect;
879 rect.x = posInFrame.x;
880 rect.y = posInFrame.y;
881 rect.width = caret->GetCaretRect().width;
882 rect.height = frame->GetSize().height;
883
884 rv = ConvertToRootViewRelativeOffset(frame, rect);
885 NS_ENSURE_SUCCESS(rv, rv);
886
887 aEvent->mReply.mRect =
888 rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
889 aEvent->mSucceeded = true;
890 return NS_OK;
891 }
892
893 nsresult
894 ContentEventHandler::OnQueryContentState(WidgetQueryContentEvent* aEvent)
895 {
896 nsresult rv = Init(aEvent);
897 if (NS_FAILED(rv)) {
898 return rv;
899 }
900 aEvent->mSucceeded = true;
901 return NS_OK;
902 }
903
904 nsresult
905 ContentEventHandler::OnQuerySelectionAsTransferable(
906 WidgetQueryContentEvent* aEvent)
907 {
908 nsresult rv = Init(aEvent);
909 if (NS_FAILED(rv)) {
910 return rv;
911 }
912
913 if (!aEvent->mReply.mHasSelection) {
914 aEvent->mSucceeded = true;
915 aEvent->mReply.mTransferable = nullptr;
916 return NS_OK;
917 }
918
919 nsCOMPtr<nsIDocument> doc = mPresShell->GetDocument();
920 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
921
922 rv = nsCopySupport::GetTransferableForSelection(
923 mSelection, doc, getter_AddRefs(aEvent->mReply.mTransferable));
924 NS_ENSURE_SUCCESS(rv, rv);
925
926 aEvent->mSucceeded = true;
927 return NS_OK;
928 }
929
930 nsresult
931 ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
932 {
933 nsresult rv = Init(aEvent);
934 if (NS_FAILED(rv)) {
935 return rv;
936 }
937
938 nsIFrame* rootFrame = mPresShell->GetRootFrame();
939 NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
940 nsIWidget* rootWidget = rootFrame->GetNearestWidget();
941 NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
942
943 // The root frame's widget might be different, e.g., the event was fired on
944 // a popup but the rootFrame is the document root.
945 if (rootWidget != aEvent->widget) {
946 NS_PRECONDITION(aEvent->widget, "The event must have the widget");
947 nsView* view = nsView::GetViewFor(aEvent->widget);
948 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
949 rootFrame = view->GetFrame();
950 NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
951 rootWidget = rootFrame->GetNearestWidget();
952 NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
953 }
954
955 WidgetQueryContentEvent eventOnRoot(true, NS_QUERY_CHARACTER_AT_POINT,
956 rootWidget);
957 eventOnRoot.mUseNativeLineBreak = aEvent->mUseNativeLineBreak;
958 eventOnRoot.refPoint = aEvent->refPoint;
959 if (rootWidget != aEvent->widget) {
960 eventOnRoot.refPoint += LayoutDeviceIntPoint::FromUntyped(
961 aEvent->widget->WidgetToScreenOffset() -
962 rootWidget->WidgetToScreenOffset());
963 }
964 nsPoint ptInRoot =
965 nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame);
966
967 nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
968 if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame ||
969 !targetFrame->GetContent() ||
970 !nsContentUtils::ContentIsDescendantOf(targetFrame->GetContent(),
971 mRootContent)) {
972 // there is no character at the point.
973 aEvent->mReply.mOffset = WidgetQueryContentEvent::NOT_FOUND;
974 aEvent->mSucceeded = true;
975 return NS_OK;
976 }
977 nsPoint ptInTarget = ptInRoot + rootFrame->GetOffsetToCrossDoc(targetFrame);
978 int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
979 int32_t targetAPD = targetFrame->PresContext()->AppUnitsPerDevPixel();
980 ptInTarget = ptInTarget.ConvertAppUnits(rootAPD, targetAPD);
981
982 nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame);
983 nsIFrame::ContentOffsets contentOffsets =
984 textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
985 NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE);
986 uint32_t offset;
987 rv = GetFlatTextOffsetOfRange(mRootContent, contentOffsets.content,
988 contentOffsets.offset, &offset,
989 GetLineBreakType(aEvent));
990 NS_ENSURE_SUCCESS(rv, rv);
991
992 WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, aEvent->widget);
993 textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak);
994 rv = OnQueryTextRect(&textRect);
995 NS_ENSURE_SUCCESS(rv, rv);
996 NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
997
998 // currently, we don't need to get the actual text.
999 aEvent->mReply.mOffset = offset;
1000 aEvent->mReply.mRect = textRect.mReply.mRect;
1001 aEvent->mSucceeded = true;
1002 return NS_OK;
1003 }
1004
1005 nsresult
1006 ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
1007 {
1008 NS_ASSERTION(aEvent, "aEvent must not be null");
1009
1010 nsresult rv = InitBasic();
1011 if (NS_FAILED(rv)) {
1012 return rv;
1013 }
1014
1015 aEvent->mSucceeded = false;
1016 aEvent->mReply.mWidgetIsHit = false;
1017
1018 NS_ENSURE_TRUE(aEvent->widget, NS_ERROR_FAILURE);
1019
1020 nsIDocument* doc = mPresShell->GetDocument();
1021 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1022 nsIFrame* docFrame = mPresShell->GetRootFrame();
1023 NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE);
1024
1025 LayoutDeviceIntPoint eventLoc = aEvent->refPoint +
1026 LayoutDeviceIntPoint::FromUntyped(aEvent->widget->WidgetToScreenOffset());
1027 nsIntRect docFrameRect = docFrame->GetScreenRect(); // Returns CSS pixels
1028 CSSIntPoint eventLocCSS(
1029 mPresContext->DevPixelsToIntCSSPixels(eventLoc.x) - docFrameRect.x,
1030 mPresContext->DevPixelsToIntCSSPixels(eventLoc.y) - docFrameRect.y);
1031
1032 Element* contentUnderMouse =
1033 doc->ElementFromPointHelper(eventLocCSS.x, eventLocCSS.y, false, false);
1034 if (contentUnderMouse) {
1035 nsIWidget* targetWidget = nullptr;
1036 nsIFrame* targetFrame = contentUnderMouse->GetPrimaryFrame();
1037 nsIObjectFrame* pluginFrame = do_QueryFrame(targetFrame);
1038 if (pluginFrame) {
1039 targetWidget = pluginFrame->GetWidget();
1040 } else if (targetFrame) {
1041 targetWidget = targetFrame->GetNearestWidget();
1042 }
1043 if (aEvent->widget == targetWidget) {
1044 aEvent->mReply.mWidgetIsHit = true;
1045 }
1046 }
1047
1048 aEvent->mSucceeded = true;
1049 return NS_OK;
1050 }
1051
1052 /* static */ nsresult
1053 ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
1054 nsINode* aNode,
1055 int32_t aNodeOffset,
1056 uint32_t* aOffset,
1057 LineBreakType aLineBreakType)
1058 {
1059 NS_ENSURE_STATE(aRootContent);
1060 NS_ASSERTION(aOffset, "param is invalid");
1061
1062 nsRefPtr<nsRange> prev = new nsRange(aRootContent);
1063 nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
1064 prev->SetStart(rootDOMNode, 0);
1065
1066 nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode));
1067 NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
1068
1069 nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
1070
1071 if (aNode->Length() >= static_cast<uint32_t>(aNodeOffset)) {
1072 // Offset is within node's length; set end of range to that offset
1073 prev->SetEnd(startDOMNode, aNodeOffset);
1074 iter->Init(prev);
1075 } else if (aNode != static_cast<nsINode*>(aRootContent)) {
1076 // Offset is past node's length; set end of range to end of node
1077 prev->SetEndAfter(startDOMNode);
1078 iter->Init(prev);
1079 } else {
1080 // Offset is past the root node; set end of range to end of root node
1081 iter->Init(aRootContent);
1082 }
1083
1084 nsCOMPtr<nsINode> startNode = do_QueryInterface(startDOMNode);
1085 nsINode* endNode = aNode;
1086
1087 *aOffset = 0;
1088 for (; !iter->IsDone(); iter->Next()) {
1089 nsINode* node = iter->GetCurrentNode();
1090 if (!node) {
1091 break;
1092 }
1093 if (!node->IsNodeOfType(nsINode::eCONTENT)) {
1094 continue;
1095 }
1096 nsIContent* content = static_cast<nsIContent*>(node);
1097
1098 if (node->IsNodeOfType(nsINode::eTEXT)) {
1099 // Note: our range always starts from offset 0
1100 if (node == endNode) {
1101 *aOffset += GetTextLength(content, aLineBreakType, aNodeOffset);
1102 } else {
1103 *aOffset += GetTextLength(content, aLineBreakType);
1104 }
1105 } else if (IsContentBR(content)) {
1106 #if defined(XP_WIN)
1107 // On Windows, the length of the newline is 2.
1108 *aOffset += (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
1109 #else
1110 // On other platforms, the length of the newline is 1.
1111 *aOffset += 1;
1112 #endif
1113 }
1114 }
1115 return NS_OK;
1116 }
1117
1118 /* static */ nsresult
1119 ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
1120 nsRange* aRange,
1121 uint32_t* aOffset,
1122 LineBreakType aLineBreakType)
1123 {
1124 nsINode* startNode = aRange->GetStartParent();
1125 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
1126 int32_t startOffset = aRange->StartOffset();
1127 return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset,
1128 aOffset, aLineBreakType);
1129 }
1130
1131 nsresult
1132 ContentEventHandler::GetStartFrameAndOffset(nsRange* aRange,
1133 nsIFrame** aFrame,
1134 int32_t* aOffsetInFrame)
1135 {
1136 NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid");
1137
1138 nsIContent* content = nullptr;
1139 nsINode* node = aRange->GetStartParent();
1140 if (node && node->IsNodeOfType(nsINode::eCONTENT)) {
1141 content = static_cast<nsIContent*>(node);
1142 }
1143 NS_ASSERTION(content, "the start node doesn't have nsIContent!");
1144
1145 nsRefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
1146 *aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
1147 fs->GetHint(), aOffsetInFrame);
1148 NS_ENSURE_TRUE((*aFrame), NS_ERROR_FAILURE);
1149 NS_ASSERTION((*aFrame)->GetType() == nsGkAtoms::textFrame,
1150 "The frame is not textframe");
1151 return NS_OK;
1152 }
1153
1154 nsresult
1155 ContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
1156 nsRect& aRect)
1157 {
1158 NS_ASSERTION(aFrame, "aFrame must not be null");
1159
1160 nsView* view = nullptr;
1161 nsPoint posInView;
1162 aFrame->GetOffsetFromView(posInView, &view);
1163 if (!view) {
1164 return NS_ERROR_FAILURE;
1165 }
1166 aRect += posInView + view->GetOffsetTo(nullptr);
1167 return NS_OK;
1168 }
1169
1170 static void AdjustRangeForSelection(nsIContent* aRoot,
1171 nsINode** aNode,
1172 int32_t* aNodeOffset)
1173 {
1174 nsINode* node = *aNode;
1175 int32_t nodeOffset = *aNodeOffset;
1176 if (aRoot != node && node->GetParent()) {
1177 if (node->IsNodeOfType(nsINode::eTEXT)) {
1178 // When the offset is at the end of the text node, set it to after the
1179 // text node, to make sure the caret is drawn on a new line when the last
1180 // character of the text node is '\n'
1181 int32_t nodeLength =
1182 static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength());
1183 MOZ_ASSERT(nodeOffset <= nodeLength, "Offset is past length of text node");
1184 if (nodeOffset == nodeLength) {
1185 node = node->GetParent();
1186 nodeOffset = node->IndexOf(*aNode) + 1;
1187 }
1188 } else {
1189 node = node->GetParent();
1190 nodeOffset = node->IndexOf(*aNode) + (nodeOffset ? 1 : 0);
1191 }
1192 }
1193
1194 nsIContent* brContent = node->GetChildAt(nodeOffset - 1);
1195 while (brContent && brContent->IsHTML()) {
1196 if (brContent->Tag() != nsGkAtoms::br || IsContentBR(brContent)) {
1197 break;
1198 }
1199 brContent = node->GetChildAt(--nodeOffset - 1);
1200 }
1201 *aNode = node;
1202 *aNodeOffset = std::max(nodeOffset, 0);
1203 }
1204
1205 nsresult
1206 ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
1207 {
1208 aEvent->mSucceeded = false;
1209
1210 // Get selection to manipulate
1211 // XXX why do we need to get them from ISM? This method should work fine
1212 // without ISM.
1213 nsresult rv =
1214 IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(mSelection),
1215 getter_AddRefs(mRootContent));
1216 if (rv != NS_ERROR_NOT_AVAILABLE) {
1217 NS_ENSURE_SUCCESS(rv, rv);
1218 } else {
1219 rv = Init(aEvent);
1220 NS_ENSURE_SUCCESS(rv, rv);
1221 }
1222
1223 // Get range from offset and length
1224 nsRefPtr<nsRange> range = new nsRange(mRootContent);
1225 rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength,
1226 GetLineBreakType(aEvent),
1227 aEvent->mExpandToClusterBoundary);
1228 NS_ENSURE_SUCCESS(rv, rv);
1229
1230 nsINode* startNode = range->GetStartParent();
1231 nsINode* endNode = range->GetEndParent();
1232 int32_t startNodeOffset = range->StartOffset();
1233 int32_t endNodeOffset = range->EndOffset();
1234 AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
1235 AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
1236
1237 nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
1238 nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
1239 NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
1240
1241 nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
1242 selPrivate->StartBatchChanges();
1243
1244 // Clear selection first before setting
1245 rv = mSelection->RemoveAllRanges();
1246 // Need to call EndBatchChanges at the end even if call failed
1247 if (NS_SUCCEEDED(rv)) {
1248 if (aEvent->mReversed) {
1249 rv = mSelection->Collapse(endDomNode, endNodeOffset);
1250 } else {
1251 rv = mSelection->Collapse(startDomNode, startNodeOffset);
1252 }
1253 if (NS_SUCCEEDED(rv) &&
1254 (startDomNode != endDomNode || startNodeOffset != endNodeOffset)) {
1255 if (aEvent->mReversed) {
1256 rv = mSelection->Extend(startDomNode, startNodeOffset);
1257 } else {
1258 rv = mSelection->Extend(endDomNode, endNodeOffset);
1259 }
1260 }
1261 }
1262 selPrivate->EndBatchChanges();
1263 NS_ENSURE_SUCCESS(rv, rv);
1264
1265 selPrivate->ScrollIntoViewInternal(
1266 nsISelectionController::SELECTION_FOCUS_REGION,
1267 false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis());
1268 aEvent->mSucceeded = true;
1269 return NS_OK;
1270 }
1271
1272 } // namespace mozilla

mercurial