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/. */
7 #include "ContentEventHandler.h"
8 #include "mozilla/IMEStateManager.h"
9 #include "mozilla/TextEvents.h"
10 #include "mozilla/dom/Element.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"
24 #include "nsIObjectFrame.h"
25 #include "nsLayoutUtils.h"
26 #include "nsPresContext.h"
28 #include "nsTextFragment.h"
29 #include "nsTextFrame.h"
37 using namespace widget
;
39 /******************************************************************/
40 /* ContentEventHandler */
41 /******************************************************************/
43 ContentEventHandler::ContentEventHandler(nsPresContext
* aPresContext
)
44 : mPresContext(aPresContext
)
45 , mPresShell(aPresContext
->GetPresShell())
47 , mFirstSelectedRange(nullptr)
48 , mRootContent(nullptr)
53 ContentEventHandler::InitBasic()
55 NS_ENSURE_TRUE(mPresShell
, NS_ERROR_NOT_AVAILABLE
);
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
);
61 // Flushing notifications can cause mPresShell to be destroyed (bug 577963).
62 NS_ENSURE_TRUE(!mPresShell
->IsDestroying(), NS_ERROR_FAILURE
);
68 ContentEventHandler::InitCommon()
74 nsresult rv
= InitBasic();
75 NS_ENSURE_SUCCESS(rv
, rv
);
77 nsCopySupport::GetSelectionForCopy(mPresShell
->GetDocument(),
78 getter_AddRefs(mSelection
));
80 nsCOMPtr
<nsIDOMRange
> firstRange
;
81 rv
= mSelection
->GetRangeAt(0, getter_AddRefs(firstRange
));
82 // This shell doesn't support selection.
84 return NS_ERROR_NOT_AVAILABLE
;
86 mFirstSelectedRange
= static_cast<nsRange
*>(firstRange
.get());
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
);
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");
99 mRootContent
= startNode
->GetSelectionRootContent(mPresShell
);
100 NS_ENSURE_TRUE(mRootContent
, NS_ERROR_FAILURE
);
105 ContentEventHandler::Init(WidgetQueryContentEvent
* aEvent
)
107 NS_ASSERTION(aEvent
, "aEvent must not be null");
109 nsresult rv
= InitCommon();
110 NS_ENSURE_SUCCESS(rv
, rv
);
112 aEvent
->mSucceeded
= false;
114 aEvent
->mReply
.mContentsRoot
= mRootContent
.get();
117 rv
= mSelection
->GetIsCollapsed(&isCollapsed
);
118 NS_ENSURE_SUCCESS(rv
, NS_ERROR_NOT_AVAILABLE
);
119 aEvent
->mReply
.mHasSelection
= !isCollapsed
;
122 nsIFrame
* frame
= nsCaret::GetGeometry(mSelection
, &r
);
123 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
125 aEvent
->mReply
.mFocusedWidget
= frame
->GetNearestWidget();
131 ContentEventHandler::Init(WidgetSelectionEvent
* aEvent
)
133 NS_ASSERTION(aEvent
, "aEvent must not be null");
135 nsresult rv
= InitCommon();
136 NS_ENSURE_SUCCESS(rv
, rv
);
138 aEvent
->mSucceeded
= false;
144 ContentEventHandler::GetFocusedContent()
146 nsIDocument
* doc
= mPresShell
->GetDocument();
150 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(doc
->GetWindow());
151 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
;
152 return nsFocusManager::GetFocusedDescendant(window
, true,
153 getter_AddRefs(focusedWindow
));
157 ContentEventHandler::IsPlugin(nsIContent
* aContent
)
160 aContent
->GetDesiredIMEState().mEnabled
== IMEState::PLUGIN
;
164 ContentEventHandler::QueryContentRect(nsIContent
* aContent
,
165 WidgetQueryContentEvent
* aEvent
)
167 NS_PRECONDITION(aContent
, "aContent must not be null");
169 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
170 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
172 // get rect for first frame
173 nsRect
resultRect(nsPoint(0, 0), frame
->GetRect().Size());
174 nsresult rv
= ConvertToRootViewRelativeOffset(frame
, resultRect
);
175 NS_ENSURE_SUCCESS(rv
, rv
);
177 // account for any additional frames
178 while ((frame
= frame
->GetNextContinuation()) != nullptr) {
179 nsRect
frameRect(nsPoint(0, 0), frame
->GetRect().Size());
180 rv
= ConvertToRootViewRelativeOffset(frame
, frameRect
);
181 NS_ENSURE_SUCCESS(rv
, rv
);
182 resultRect
.UnionRect(resultRect
, frameRect
);
185 aEvent
->mReply
.mRect
=
186 resultRect
.ToOutsidePixels(mPresContext
->AppUnitsPerDevPixel());
187 aEvent
->mSucceeded
= true;
192 // Editor places a bogus BR node under its root content if the editor doesn't
193 // have any text. This happens even for single line editors.
194 // When we get text content and when we change the selection,
195 // we don't want to include the bogus BRs at the end.
196 static bool IsContentBR(nsIContent
* aContent
)
198 return aContent
->IsHTML() &&
199 aContent
->Tag() == nsGkAtoms::br
&&
200 !aContent
->AttrValueIs(kNameSpaceID_None
,
204 !aContent
->AttrValueIs(kNameSpaceID_None
,
205 nsGkAtoms::mozeditorbogusnode
,
210 static void ConvertToNativeNewlines(nsAFlatString
& aString
)
212 #if defined(XP_MACOSX)
213 // XXX Mac OS X doesn't use "\r".
214 aString
.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r"));
215 #elif defined(XP_WIN)
216 aString
.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
220 static void AppendString(nsAString
& aString
, nsIContent
* aContent
)
222 NS_ASSERTION(aContent
->IsNodeOfType(nsINode::eTEXT
),
223 "aContent is not a text node!");
224 const nsTextFragment
* text
= aContent
->GetText();
228 text
->AppendTo(aString
);
231 static void AppendSubString(nsAString
& aString
, nsIContent
* aContent
,
232 uint32_t aXPOffset
, uint32_t aXPLength
)
234 NS_ASSERTION(aContent
->IsNodeOfType(nsINode::eTEXT
),
235 "aContent is not a text node!");
236 const nsTextFragment
* text
= aContent
->GetText();
240 text
->AppendTo(aString
, int32_t(aXPOffset
), int32_t(aXPLength
));
244 static uint32_t CountNewlinesInXPLength(nsIContent
* aContent
,
247 NS_ASSERTION(aContent
->IsNodeOfType(nsINode::eTEXT
),
248 "aContent is not a text node!");
249 const nsTextFragment
* text
= aContent
->GetText();
253 // For automated tests, we should abort on debug build.
255 (aXPLength
== UINT32_MAX
|| aXPLength
<= text
->GetLength()),
256 "aXPLength is out-of-bounds");
257 const uint32_t length
= std::min(aXPLength
, text
->GetLength());
258 uint32_t newlines
= 0;
259 for (uint32_t i
= 0; i
< length
; ++i
) {
260 if (text
->CharAt(i
) == '\n') {
267 static uint32_t CountNewlinesInNativeLength(nsIContent
* aContent
,
268 uint32_t aNativeLength
)
270 NS_ASSERTION(aContent
->IsNodeOfType(nsINode::eTEXT
),
271 "aContent is not a text node!");
272 const nsTextFragment
* text
= aContent
->GetText();
276 // For automated tests, we should abort on debug build.
278 (aNativeLength
== UINT32_MAX
|| aNativeLength
<= text
->GetLength() * 2),
279 "aNativeLength is unexpected value");
280 const uint32_t xpLength
= text
->GetLength();
281 uint32_t newlines
= 0;
282 for (uint32_t i
= 0, nativeOffset
= 0;
283 i
< xpLength
&& nativeOffset
< aNativeLength
;
284 ++i
, ++nativeOffset
) {
285 // For automated tests, we should abort on debug build.
286 NS_ABORT_IF_FALSE(i
< text
->GetLength(), "i is out-of-bounds");
287 if (text
->CharAt(i
) == '\n') {
296 /* static */ uint32_t
297 ContentEventHandler::GetNativeTextLength(nsIContent
* aContent
,
298 uint32_t aStartOffset
,
301 MOZ_ASSERT(aEndOffset
>= aStartOffset
,
302 "aEndOffset must be equals or larger than aStartOffset");
303 if (aStartOffset
== aEndOffset
) {
306 return GetTextLength(aContent
, LINE_BREAK_TYPE_NATIVE
, aEndOffset
) -
307 GetTextLength(aContent
, LINE_BREAK_TYPE_NATIVE
, aStartOffset
);
310 /* static */ uint32_t
311 ContentEventHandler::GetNativeTextLength(nsIContent
* aContent
,
314 return GetTextLength(aContent
, LINE_BREAK_TYPE_NATIVE
, aMaxLength
);
317 /* static */ uint32_t
318 ContentEventHandler::GetTextLength(nsIContent
* aContent
,
319 LineBreakType aLineBreakType
,
322 if (aContent
->IsNodeOfType(nsINode::eTEXT
)) {
323 uint32_t textLengthDifference
=
324 #if defined(XP_MACOSX)
325 // On Mac, the length of a native newline ("\r") is equal to the length of
326 // the XP newline ("\n"), so the native length is the same as the XP
329 #elif defined(XP_WIN)
330 // On Windows, the length of a native newline ("\r\n") is twice the length
331 // of the XP newline ("\n"), so XP length is equal to the length of the
332 // native offset plus the number of newlines encountered in the string.
333 (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) ?
334 CountNewlinesInXPLength(aContent
, aMaxLength
) : 0;
336 // On other platforms, the native and XP newlines are the same.
340 const nsTextFragment
* text
= aContent
->GetText();
344 uint32_t length
= std::min(text
->GetLength(), aMaxLength
);
345 return length
+ textLengthDifference
;
346 } else if (IsContentBR(aContent
)) {
349 return (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) ? 2 : 1;
357 static uint32_t ConvertToXPOffset(nsIContent
* aContent
, uint32_t aNativeOffset
)
359 #if defined(XP_MACOSX)
360 // On Mac, the length of a native newline ("\r") is equal to the length of
361 // the XP newline ("\n"), so the native offset is the same as the XP offset.
362 return aNativeOffset
;
363 #elif defined(XP_WIN)
364 // On Windows, the length of a native newline ("\r\n") is twice the length of
365 // the XP newline ("\n"), so XP offset is equal to the length of the native
366 // offset minus the number of newlines encountered in the string.
367 return aNativeOffset
- CountNewlinesInNativeLength(aContent
, aNativeOffset
);
369 // On other platforms, the native and XP newlines are the same.
370 return aNativeOffset
;
374 static nsresult
GenerateFlatTextContent(nsRange
* aRange
,
375 nsAFlatString
& aString
,
376 LineBreakType aLineBreakType
)
378 nsCOMPtr
<nsIContentIterator
> iter
= NS_NewContentIterator();
381 NS_ASSERTION(aString
.IsEmpty(), "aString must be empty string");
383 nsINode
* startNode
= aRange
->GetStartParent();
384 NS_ENSURE_TRUE(startNode
, NS_ERROR_FAILURE
);
385 nsINode
* endNode
= aRange
->GetEndParent();
386 NS_ENSURE_TRUE(endNode
, NS_ERROR_FAILURE
);
388 if (startNode
== endNode
&& startNode
->IsNodeOfType(nsINode::eTEXT
)) {
389 nsIContent
* content
= static_cast<nsIContent
*>(startNode
);
390 AppendSubString(aString
, content
, aRange
->StartOffset(),
391 aRange
->EndOffset() - aRange
->StartOffset());
392 ConvertToNativeNewlines(aString
);
397 for (; !iter
->IsDone(); iter
->Next()) {
398 nsINode
* node
= iter
->GetCurrentNode();
402 if (!node
->IsNodeOfType(nsINode::eCONTENT
)) {
405 nsIContent
* content
= static_cast<nsIContent
*>(node
);
407 if (content
->IsNodeOfType(nsINode::eTEXT
)) {
408 if (content
== startNode
) {
409 AppendSubString(aString
, content
, aRange
->StartOffset(),
410 content
->TextLength() - aRange
->StartOffset());
411 } else if (content
== endNode
) {
412 AppendSubString(aString
, content
, 0, aRange
->EndOffset());
414 AppendString(aString
, content
);
416 } else if (IsContentBR(content
)) {
417 aString
.Append(char16_t('\n'));
420 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
421 ConvertToNativeNewlines(aString
);
427 ContentEventHandler::ExpandToClusterBoundary(nsIContent
* aContent
,
431 // XXX This method assumes that the frame boundaries must be cluster
432 // boundaries. It's false, but no problem now, maybe.
433 if (!aContent
->IsNodeOfType(nsINode::eTEXT
) ||
434 *aXPOffset
== 0 || *aXPOffset
== aContent
->TextLength()) {
438 NS_ASSERTION(*aXPOffset
<= aContent
->TextLength(),
439 "offset is out of range.");
441 nsRefPtr
<nsFrameSelection
> fs
= mPresShell
->FrameSelection();
442 int32_t offsetInFrame
;
443 CaretAssociationHint hint
=
444 aForward
? CARET_ASSOCIATE_BEFORE
: CARET_ASSOCIATE_AFTER
;
445 nsIFrame
* frame
= fs
->GetFrameForNodeOffset(aContent
, int32_t(*aXPOffset
),
446 hint
, &offsetInFrame
);
448 // This content doesn't have any frames, we only can check surrogate pair...
449 const nsTextFragment
* text
= aContent
->GetText();
450 NS_ENSURE_TRUE(text
, NS_ERROR_FAILURE
);
451 if (NS_IS_LOW_SURROGATE(text
->CharAt(*aXPOffset
)) &&
452 NS_IS_HIGH_SURROGATE(text
->CharAt(*aXPOffset
- 1))) {
453 *aXPOffset
+= aForward
? 1 : -1;
457 int32_t startOffset
, endOffset
;
458 nsresult rv
= frame
->GetOffsets(startOffset
, endOffset
);
459 NS_ENSURE_SUCCESS(rv
, rv
);
460 if (*aXPOffset
== static_cast<uint32_t>(startOffset
) ||
461 *aXPOffset
== static_cast<uint32_t>(endOffset
)) {
464 if (frame
->GetType() != nsGkAtoms::textFrame
) {
465 return NS_ERROR_FAILURE
;
467 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(frame
);
468 int32_t newOffsetInFrame
= *aXPOffset
- startOffset
;
469 newOffsetInFrame
+= aForward
? -1 : 1;
470 textFrame
->PeekOffsetCharacter(aForward
, &newOffsetInFrame
);
471 *aXPOffset
= startOffset
+ newOffsetInFrame
;
476 ContentEventHandler::SetRangeFromFlatTextOffset(nsRange
* aRange
,
479 LineBreakType aLineBreakType
,
480 bool aExpandToClusterBoundaries
,
481 uint32_t* aNewOffset
)
484 *aNewOffset
= aOffset
;
487 nsCOMPtr
<nsIContentIterator
> iter
= NS_NewPreContentIterator();
488 nsresult rv
= iter
->Init(mRootContent
);
489 NS_ENSURE_SUCCESS(rv
, rv
);
492 uint32_t endOffset
= aOffset
+ aLength
;
493 bool startSet
= false;
494 for (; !iter
->IsDone(); iter
->Next()) {
495 nsINode
* node
= iter
->GetCurrentNode();
499 if (!node
->IsNodeOfType(nsINode::eCONTENT
)) {
502 nsIContent
* content
= static_cast<nsIContent
*>(node
);
504 uint32_t textLength
= GetTextLength(content
, aLineBreakType
);
509 if (offset
<= aOffset
&& aOffset
< offset
+ textLength
) {
511 if (!content
->IsNodeOfType(nsINode::eTEXT
)) {
514 xpOffset
= aOffset
- offset
;
515 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
516 xpOffset
= ConvertToXPOffset(content
, xpOffset
);
520 if (aExpandToClusterBoundaries
) {
521 uint32_t oldXPOffset
= xpOffset
;
522 rv
= ExpandToClusterBoundary(content
, false, &xpOffset
);
523 NS_ENSURE_SUCCESS(rv
, rv
);
525 // This is correct since a cluster shouldn't include line break.
526 *aNewOffset
-= (oldXPOffset
- xpOffset
);
530 rv
= aRange
->SetStart(content
, int32_t(xpOffset
));
531 NS_ENSURE_SUCCESS(rv
, rv
);
534 // Ensure that the end offset and the start offset are same.
535 rv
= aRange
->SetEnd(content
, int32_t(xpOffset
));
536 NS_ENSURE_SUCCESS(rv
, rv
);
540 if (endOffset
<= offset
+ textLength
) {
541 nsINode
* endNode
= content
;
543 if (content
->IsNodeOfType(nsINode::eTEXT
)) {
544 xpOffset
= endOffset
- offset
;
545 if (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) {
546 xpOffset
= ConvertToXPOffset(content
, xpOffset
);
548 if (aExpandToClusterBoundaries
) {
549 rv
= ExpandToClusterBoundary(content
, true, &xpOffset
);
550 NS_ENSURE_SUCCESS(rv
, rv
);
553 // Use first position of next node, because the end node is ignored
554 // by ContentIterator when the offset is zero.
557 if (iter
->IsDone()) {
560 endNode
= iter
->GetCurrentNode();
563 rv
= aRange
->SetEnd(endNode
, int32_t(xpOffset
));
564 NS_ENSURE_SUCCESS(rv
, rv
);
568 offset
+= textLength
;
571 if (offset
< aOffset
) {
572 return NS_ERROR_FAILURE
;
576 MOZ_ASSERT(!mRootContent
->IsNodeOfType(nsINode::eTEXT
));
577 rv
= aRange
->SetStart(mRootContent
, int32_t(mRootContent
->GetChildCount()));
578 NS_ENSURE_SUCCESS(rv
, rv
);
580 *aNewOffset
= offset
;
583 rv
= aRange
->SetEnd(mRootContent
, int32_t(mRootContent
->GetChildCount()));
584 NS_ASSERTION(NS_SUCCEEDED(rv
), "nsIDOMRange::SetEnd failed");
588 /* static */ LineBreakType
589 ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent
* aEvent
)
591 return GetLineBreakType(aEvent
->mUseNativeLineBreak
);
594 /* static */ LineBreakType
595 ContentEventHandler::GetLineBreakType(WidgetSelectionEvent
* aEvent
)
597 return GetLineBreakType(aEvent
->mUseNativeLineBreak
);
600 /* static */ LineBreakType
601 ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak
)
603 return aUseNativeLineBreak
?
604 LINE_BREAK_TYPE_NATIVE
: LINE_BREAK_TYPE_XP
;
607 // Similar to nsFrameSelection::GetFrameForNodeOffset,
608 // but this is more flexible for OnQueryTextRect to use
609 static nsresult
GetFrameForTextRect(nsINode
* aNode
,
612 nsIFrame
** aReturnFrame
)
614 NS_ENSURE_TRUE(aNode
&& aNode
->IsNodeOfType(nsINode::eCONTENT
),
615 NS_ERROR_UNEXPECTED
);
616 nsIContent
* content
= static_cast<nsIContent
*>(aNode
);
617 nsIFrame
* frame
= content
->GetPrimaryFrame();
618 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
619 int32_t childNodeOffset
= 0;
620 return frame
->GetChildFrameContainingOffset(aNodeOffset
, aHint
,
621 &childNodeOffset
, aReturnFrame
);
625 ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent
* aEvent
)
627 nsresult rv
= Init(aEvent
);
632 NS_ASSERTION(aEvent
->mReply
.mString
.IsEmpty(),
633 "The reply string must be empty");
635 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
636 rv
= GetFlatTextOffsetOfRange(mRootContent
, mFirstSelectedRange
,
637 &aEvent
->mReply
.mOffset
, lineBreakType
);
638 NS_ENSURE_SUCCESS(rv
, rv
);
640 nsCOMPtr
<nsIDOMNode
> anchorDomNode
, focusDomNode
;
641 rv
= mSelection
->GetAnchorNode(getter_AddRefs(anchorDomNode
));
642 NS_ENSURE_TRUE(anchorDomNode
, NS_ERROR_FAILURE
);
643 rv
= mSelection
->GetFocusNode(getter_AddRefs(focusDomNode
));
644 NS_ENSURE_TRUE(focusDomNode
, NS_ERROR_FAILURE
);
646 int32_t anchorOffset
, focusOffset
;
647 rv
= mSelection
->GetAnchorOffset(&anchorOffset
);
648 NS_ENSURE_SUCCESS(rv
, rv
);
649 rv
= mSelection
->GetFocusOffset(&focusOffset
);
650 NS_ENSURE_SUCCESS(rv
, rv
);
652 nsCOMPtr
<nsINode
> anchorNode(do_QueryInterface(anchorDomNode
));
653 nsCOMPtr
<nsINode
> focusNode(do_QueryInterface(focusDomNode
));
654 NS_ENSURE_TRUE(anchorNode
&& focusNode
, NS_ERROR_UNEXPECTED
);
656 int16_t compare
= nsContentUtils::ComparePoints(anchorNode
, anchorOffset
,
657 focusNode
, focusOffset
);
658 aEvent
->mReply
.mReversed
= compare
> 0;
661 rv
= GenerateFlatTextContent(mFirstSelectedRange
, aEvent
->mReply
.mString
,
663 NS_ENSURE_SUCCESS(rv
, rv
);
666 nsIFrame
* frame
= nullptr;
667 rv
= GetFrameForTextRect(focusNode
, focusOffset
, true, &frame
);
668 if (NS_SUCCEEDED(rv
) && frame
) {
669 aEvent
->mReply
.mWritingMode
= frame
->GetWritingMode();
671 aEvent
->mReply
.mWritingMode
= WritingMode();
674 aEvent
->mSucceeded
= true;
679 ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent
* aEvent
)
681 nsresult rv
= Init(aEvent
);
686 NS_ASSERTION(aEvent
->mReply
.mString
.IsEmpty(),
687 "The reply string must be empty");
689 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
691 nsRefPtr
<nsRange
> range
= new nsRange(mRootContent
);
692 rv
= SetRangeFromFlatTextOffset(range
, aEvent
->mInput
.mOffset
,
693 aEvent
->mInput
.mLength
, lineBreakType
, false,
694 &aEvent
->mReply
.mOffset
);
695 NS_ENSURE_SUCCESS(rv
, rv
);
697 rv
= GenerateFlatTextContent(range
, aEvent
->mReply
.mString
, lineBreakType
);
698 NS_ENSURE_SUCCESS(rv
, rv
);
700 aEvent
->mSucceeded
= true;
705 // Adjust to use a child node if possible
706 // to make the returned rect more accurate
707 static nsINode
* AdjustTextRectNode(nsINode
* aNode
,
708 int32_t& aNodeOffset
)
710 int32_t childCount
= int32_t(aNode
->GetChildCount());
711 nsINode
* node
= aNode
;
713 if (aNodeOffset
< childCount
) {
714 node
= aNode
->GetChildAt(aNodeOffset
);
716 } else if (aNodeOffset
== childCount
) {
717 node
= aNode
->GetChildAt(childCount
- 1);
718 aNodeOffset
= node
->IsNodeOfType(nsINode::eTEXT
) ?
719 static_cast<int32_t>(static_cast<nsIContent
*>(node
)->TextLength()) : 1;
726 ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent
* aEvent
)
728 nsresult rv
= Init(aEvent
);
733 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
734 nsRefPtr
<nsRange
> range
= new nsRange(mRootContent
);
735 rv
= SetRangeFromFlatTextOffset(range
, aEvent
->mInput
.mOffset
,
736 aEvent
->mInput
.mLength
, lineBreakType
, true,
737 &aEvent
->mReply
.mOffset
);
738 NS_ENSURE_SUCCESS(rv
, rv
);
739 rv
= GenerateFlatTextContent(range
, aEvent
->mReply
.mString
, lineBreakType
);
740 NS_ENSURE_SUCCESS(rv
, rv
);
742 // used to iterate over all contents and their frames
743 nsCOMPtr
<nsIContentIterator
> iter
= NS_NewContentIterator();
746 // get the starting frame
747 int32_t nodeOffset
= range
->StartOffset();
748 nsINode
* node
= iter
->GetCurrentNode();
750 node
= AdjustTextRectNode(range
->GetStartParent(), nodeOffset
);
752 nsIFrame
* firstFrame
= nullptr;
753 rv
= GetFrameForTextRect(node
, nodeOffset
, true, &firstFrame
);
754 NS_ENSURE_SUCCESS(rv
, rv
);
756 // get the starting frame rect
757 nsRect
rect(nsPoint(0, 0), firstFrame
->GetRect().Size());
758 rv
= ConvertToRootViewRelativeOffset(firstFrame
, rect
);
759 NS_ENSURE_SUCCESS(rv
, rv
);
760 nsRect frameRect
= rect
;
762 firstFrame
->GetPointFromOffset(nodeOffset
, &ptOffset
);
763 // minus 1 to avoid creating an empty rect
764 rect
.x
+= ptOffset
.x
- 1;
765 rect
.width
-= ptOffset
.x
- 1;
767 // get the ending frame
768 nodeOffset
= range
->EndOffset();
769 node
= AdjustTextRectNode(range
->GetEndParent(), nodeOffset
);
770 nsIFrame
* lastFrame
= nullptr;
771 rv
= GetFrameForTextRect(node
, nodeOffset
, range
->Collapsed(), &lastFrame
);
772 NS_ENSURE_SUCCESS(rv
, rv
);
774 // iterate over all covered frames
775 for (nsIFrame
* frame
= firstFrame
; frame
!= lastFrame
;) {
776 frame
= frame
->GetNextContinuation();
780 node
= iter
->GetCurrentNode();
784 if (!node
->IsNodeOfType(nsINode::eCONTENT
)) {
787 frame
= static_cast<nsIContent
*>(node
)->GetPrimaryFrame();
788 } while (!frame
&& !iter
->IsDone());
790 // this can happen when the end offset of the range is 0.
794 frameRect
.SetRect(nsPoint(0, 0), frame
->GetRect().Size());
795 rv
= ConvertToRootViewRelativeOffset(frame
, frameRect
);
796 NS_ENSURE_SUCCESS(rv
, rv
);
797 if (frame
!= lastFrame
) {
798 // not last frame, so just add rect to previous result
799 rect
.UnionRect(rect
, frameRect
);
803 // get the ending frame rect
804 lastFrame
->GetPointFromOffset(nodeOffset
, &ptOffset
);
805 // minus 1 to avoid creating an empty rect
806 frameRect
.width
-= lastFrame
->GetRect().width
- ptOffset
.x
- 1;
808 if (firstFrame
== lastFrame
) {
809 rect
.IntersectRect(rect
, frameRect
);
811 rect
.UnionRect(rect
, frameRect
);
813 aEvent
->mReply
.mRect
=
814 rect
.ToOutsidePixels(mPresContext
->AppUnitsPerDevPixel());
815 aEvent
->mSucceeded
= true;
820 ContentEventHandler::OnQueryEditorRect(WidgetQueryContentEvent
* aEvent
)
822 nsresult rv
= Init(aEvent
);
827 nsIContent
* focusedContent
= GetFocusedContent();
828 rv
= QueryContentRect(IsPlugin(focusedContent
) ?
829 focusedContent
: mRootContent
.get(), aEvent
);
830 NS_ENSURE_SUCCESS(rv
, rv
);
835 ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent
* aEvent
)
837 nsresult rv
= Init(aEvent
);
842 LineBreakType lineBreakType
= GetLineBreakType(aEvent
);
844 // When the selection is collapsed and the queried offset is current caret
845 // position, we should return the "real" caret rect.
846 bool selectionIsCollapsed
;
847 rv
= mSelection
->GetIsCollapsed(&selectionIsCollapsed
);
848 NS_ENSURE_SUCCESS(rv
, rv
);
851 nsIFrame
* caretFrame
= nsCaret::GetGeometry(mSelection
, &caretRect
);
853 if (selectionIsCollapsed
) {
855 rv
= GetFlatTextOffsetOfRange(mRootContent
, mFirstSelectedRange
, &offset
,
857 NS_ENSURE_SUCCESS(rv
, rv
);
858 if (offset
== aEvent
->mInput
.mOffset
) {
860 return NS_ERROR_FAILURE
;
862 rv
= ConvertToRootViewRelativeOffset(caretFrame
, caretRect
);
863 NS_ENSURE_SUCCESS(rv
, rv
);
864 aEvent
->mReply
.mRect
=
865 caretRect
.ToOutsidePixels(caretFrame
->PresContext()->AppUnitsPerDevPixel());
866 aEvent
->mReply
.mOffset
= aEvent
->mInput
.mOffset
;
867 aEvent
->mSucceeded
= true;
872 // Otherwise, we should set the guessed caret rect.
873 nsRefPtr
<nsRange
> range
= new nsRange(mRootContent
);
874 rv
= SetRangeFromFlatTextOffset(range
, aEvent
->mInput
.mOffset
, 0,
876 &aEvent
->mReply
.mOffset
);
877 NS_ENSURE_SUCCESS(rv
, rv
);
879 int32_t xpOffsetInFrame
;
881 rv
= GetStartFrameAndOffset(range
, &frame
, &xpOffsetInFrame
);
882 NS_ENSURE_SUCCESS(rv
, rv
);
885 rv
= frame
->GetPointFromOffset(range
->StartOffset(), &posInFrame
);
886 NS_ENSURE_SUCCESS(rv
, rv
);
889 rect
.x
= posInFrame
.x
;
890 rect
.y
= posInFrame
.y
;
891 rect
.width
= caretRect
.width
;
892 rect
.height
= frame
->GetSize().height
;
894 rv
= ConvertToRootViewRelativeOffset(frame
, rect
);
895 NS_ENSURE_SUCCESS(rv
, rv
);
897 aEvent
->mReply
.mRect
=
898 rect
.ToOutsidePixels(mPresContext
->AppUnitsPerDevPixel());
899 aEvent
->mSucceeded
= true;
904 ContentEventHandler::OnQueryContentState(WidgetQueryContentEvent
* aEvent
)
906 nsresult rv
= Init(aEvent
);
910 aEvent
->mSucceeded
= true;
915 ContentEventHandler::OnQuerySelectionAsTransferable(
916 WidgetQueryContentEvent
* aEvent
)
918 nsresult rv
= Init(aEvent
);
923 if (!aEvent
->mReply
.mHasSelection
) {
924 aEvent
->mSucceeded
= true;
925 aEvent
->mReply
.mTransferable
= nullptr;
929 nsCOMPtr
<nsIDocument
> doc
= mPresShell
->GetDocument();
930 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
932 rv
= nsCopySupport::GetTransferableForSelection(
933 mSelection
, doc
, getter_AddRefs(aEvent
->mReply
.mTransferable
));
934 NS_ENSURE_SUCCESS(rv
, rv
);
936 aEvent
->mSucceeded
= true;
941 ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent
* aEvent
)
943 nsresult rv
= Init(aEvent
);
948 nsIFrame
* rootFrame
= mPresShell
->GetRootFrame();
949 NS_ENSURE_TRUE(rootFrame
, NS_ERROR_FAILURE
);
950 nsIWidget
* rootWidget
= rootFrame
->GetNearestWidget();
951 NS_ENSURE_TRUE(rootWidget
, NS_ERROR_FAILURE
);
953 // The root frame's widget might be different, e.g., the event was fired on
954 // a popup but the rootFrame is the document root.
955 if (rootWidget
!= aEvent
->widget
) {
956 NS_PRECONDITION(aEvent
->widget
, "The event must have the widget");
957 nsView
* view
= nsView::GetViewFor(aEvent
->widget
);
958 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
959 rootFrame
= view
->GetFrame();
960 NS_ENSURE_TRUE(rootFrame
, NS_ERROR_FAILURE
);
961 rootWidget
= rootFrame
->GetNearestWidget();
962 NS_ENSURE_TRUE(rootWidget
, NS_ERROR_FAILURE
);
965 WidgetQueryContentEvent
eventOnRoot(true, NS_QUERY_CHARACTER_AT_POINT
,
967 eventOnRoot
.mUseNativeLineBreak
= aEvent
->mUseNativeLineBreak
;
968 eventOnRoot
.refPoint
= aEvent
->refPoint
;
969 if (rootWidget
!= aEvent
->widget
) {
970 eventOnRoot
.refPoint
+= LayoutDeviceIntPoint::FromUntyped(
971 aEvent
->widget
->WidgetToScreenOffset() -
972 rootWidget
->WidgetToScreenOffset());
975 nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot
, rootFrame
);
977 nsIFrame
* targetFrame
= nsLayoutUtils::GetFrameForPoint(rootFrame
, ptInRoot
);
978 if (!targetFrame
|| targetFrame
->GetType() != nsGkAtoms::textFrame
||
979 !targetFrame
->GetContent() ||
980 !nsContentUtils::ContentIsDescendantOf(targetFrame
->GetContent(),
982 // there is no character at the point.
983 aEvent
->mReply
.mOffset
= WidgetQueryContentEvent::NOT_FOUND
;
984 aEvent
->mSucceeded
= true;
987 nsPoint ptInTarget
= ptInRoot
+ rootFrame
->GetOffsetToCrossDoc(targetFrame
);
988 int32_t rootAPD
= rootFrame
->PresContext()->AppUnitsPerDevPixel();
989 int32_t targetAPD
= targetFrame
->PresContext()->AppUnitsPerDevPixel();
990 ptInTarget
= ptInTarget
.ConvertAppUnits(rootAPD
, targetAPD
);
992 nsTextFrame
* textframe
= static_cast<nsTextFrame
*>(targetFrame
);
993 nsIFrame::ContentOffsets contentOffsets
=
994 textframe
->GetCharacterOffsetAtFramePoint(ptInTarget
);
995 NS_ENSURE_TRUE(contentOffsets
.content
, NS_ERROR_FAILURE
);
997 rv
= GetFlatTextOffsetOfRange(mRootContent
, contentOffsets
.content
,
998 contentOffsets
.offset
, &offset
,
999 GetLineBreakType(aEvent
));
1000 NS_ENSURE_SUCCESS(rv
, rv
);
1002 WidgetQueryContentEvent
textRect(true, NS_QUERY_TEXT_RECT
, aEvent
->widget
);
1003 textRect
.InitForQueryTextRect(offset
, 1, aEvent
->mUseNativeLineBreak
);
1004 rv
= OnQueryTextRect(&textRect
);
1005 NS_ENSURE_SUCCESS(rv
, rv
);
1006 NS_ENSURE_TRUE(textRect
.mSucceeded
, NS_ERROR_FAILURE
);
1008 // currently, we don't need to get the actual text.
1009 aEvent
->mReply
.mOffset
= offset
;
1010 aEvent
->mReply
.mRect
= textRect
.mReply
.mRect
;
1011 aEvent
->mSucceeded
= true;
1016 ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent
* aEvent
)
1018 NS_ASSERTION(aEvent
, "aEvent must not be null");
1020 nsresult rv
= InitBasic();
1021 if (NS_FAILED(rv
)) {
1025 aEvent
->mSucceeded
= false;
1026 aEvent
->mReply
.mWidgetIsHit
= false;
1028 NS_ENSURE_TRUE(aEvent
->widget
, NS_ERROR_FAILURE
);
1030 nsIDocument
* doc
= mPresShell
->GetDocument();
1031 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
1032 nsIFrame
* docFrame
= mPresShell
->GetRootFrame();
1033 NS_ENSURE_TRUE(docFrame
, NS_ERROR_FAILURE
);
1035 LayoutDeviceIntPoint eventLoc
= aEvent
->refPoint
+
1036 LayoutDeviceIntPoint::FromUntyped(aEvent
->widget
->WidgetToScreenOffset());
1037 nsIntRect docFrameRect
= docFrame
->GetScreenRect(); // Returns CSS pixels
1038 CSSIntPoint
eventLocCSS(
1039 mPresContext
->DevPixelsToIntCSSPixels(eventLoc
.x
) - docFrameRect
.x
,
1040 mPresContext
->DevPixelsToIntCSSPixels(eventLoc
.y
) - docFrameRect
.y
);
1042 Element
* contentUnderMouse
=
1043 doc
->ElementFromPointHelper(eventLocCSS
.x
, eventLocCSS
.y
, false, false);
1044 if (contentUnderMouse
) {
1045 nsIWidget
* targetWidget
= nullptr;
1046 nsIFrame
* targetFrame
= contentUnderMouse
->GetPrimaryFrame();
1047 nsIObjectFrame
* pluginFrame
= do_QueryFrame(targetFrame
);
1049 targetWidget
= pluginFrame
->GetWidget();
1050 } else if (targetFrame
) {
1051 targetWidget
= targetFrame
->GetNearestWidget();
1053 if (aEvent
->widget
== targetWidget
) {
1054 aEvent
->mReply
.mWidgetIsHit
= true;
1058 aEvent
->mSucceeded
= true;
1062 /* static */ nsresult
1063 ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent
* aRootContent
,
1065 int32_t aNodeOffset
,
1067 LineBreakType aLineBreakType
)
1069 NS_ENSURE_STATE(aRootContent
);
1070 NS_ASSERTION(aOffset
, "param is invalid");
1072 nsRefPtr
<nsRange
> prev
= new nsRange(aRootContent
);
1073 nsCOMPtr
<nsIDOMNode
> rootDOMNode(do_QueryInterface(aRootContent
));
1074 prev
->SetStart(rootDOMNode
, 0);
1076 nsCOMPtr
<nsIDOMNode
> startDOMNode(do_QueryInterface(aNode
));
1077 NS_ASSERTION(startDOMNode
, "startNode doesn't have nsIDOMNode");
1079 nsCOMPtr
<nsIContentIterator
> iter
= NS_NewContentIterator();
1081 if (aNode
->Length() >= static_cast<uint32_t>(aNodeOffset
)) {
1082 // Offset is within node's length; set end of range to that offset
1083 prev
->SetEnd(startDOMNode
, aNodeOffset
);
1085 } else if (aNode
!= static_cast<nsINode
*>(aRootContent
)) {
1086 // Offset is past node's length; set end of range to end of node
1087 prev
->SetEndAfter(startDOMNode
);
1090 // Offset is past the root node; set end of range to end of root node
1091 iter
->Init(aRootContent
);
1094 nsCOMPtr
<nsINode
> startNode
= do_QueryInterface(startDOMNode
);
1095 nsINode
* endNode
= aNode
;
1098 for (; !iter
->IsDone(); iter
->Next()) {
1099 nsINode
* node
= iter
->GetCurrentNode();
1103 if (!node
->IsNodeOfType(nsINode::eCONTENT
)) {
1106 nsIContent
* content
= static_cast<nsIContent
*>(node
);
1108 if (node
->IsNodeOfType(nsINode::eTEXT
)) {
1109 // Note: our range always starts from offset 0
1110 if (node
== endNode
) {
1111 *aOffset
+= GetTextLength(content
, aLineBreakType
, aNodeOffset
);
1113 *aOffset
+= GetTextLength(content
, aLineBreakType
);
1115 } else if (IsContentBR(content
)) {
1117 // On Windows, the length of the newline is 2.
1118 *aOffset
+= (aLineBreakType
== LINE_BREAK_TYPE_NATIVE
) ? 2 : 1;
1120 // On other platforms, the length of the newline is 1.
1128 /* static */ nsresult
1129 ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent
* aRootContent
,
1132 LineBreakType aLineBreakType
)
1134 nsINode
* startNode
= aRange
->GetStartParent();
1135 NS_ENSURE_TRUE(startNode
, NS_ERROR_FAILURE
);
1136 int32_t startOffset
= aRange
->StartOffset();
1137 return GetFlatTextOffsetOfRange(aRootContent
, startNode
, startOffset
,
1138 aOffset
, aLineBreakType
);
1142 ContentEventHandler::GetStartFrameAndOffset(nsRange
* aRange
,
1144 int32_t* aOffsetInFrame
)
1146 NS_ASSERTION(aRange
&& aFrame
&& aOffsetInFrame
, "params are invalid");
1148 nsIContent
* content
= nullptr;
1149 nsINode
* node
= aRange
->GetStartParent();
1150 if (node
&& node
->IsNodeOfType(nsINode::eCONTENT
)) {
1151 content
= static_cast<nsIContent
*>(node
);
1153 NS_ASSERTION(content
, "the start node doesn't have nsIContent!");
1155 nsRefPtr
<nsFrameSelection
> fs
= mPresShell
->FrameSelection();
1156 *aFrame
= fs
->GetFrameForNodeOffset(content
, aRange
->StartOffset(),
1157 fs
->GetHint(), aOffsetInFrame
);
1158 NS_ENSURE_TRUE((*aFrame
), NS_ERROR_FAILURE
);
1159 NS_ASSERTION((*aFrame
)->GetType() == nsGkAtoms::textFrame
,
1160 "The frame is not textframe");
1165 ContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame
* aFrame
,
1168 NS_ASSERTION(aFrame
, "aFrame must not be null");
1170 nsView
* view
= nullptr;
1172 aFrame
->GetOffsetFromView(posInView
, &view
);
1174 return NS_ERROR_FAILURE
;
1176 aRect
+= posInView
+ view
->GetOffsetTo(nullptr);
1180 static void AdjustRangeForSelection(nsIContent
* aRoot
,
1182 int32_t* aNodeOffset
)
1184 nsINode
* node
= *aNode
;
1185 int32_t nodeOffset
= *aNodeOffset
;
1186 if (aRoot
!= node
&& node
->GetParent()) {
1187 if (node
->IsNodeOfType(nsINode::eTEXT
)) {
1188 // When the offset is at the end of the text node, set it to after the
1189 // text node, to make sure the caret is drawn on a new line when the last
1190 // character of the text node is '\n'
1191 int32_t nodeLength
=
1192 static_cast<int32_t>(static_cast<nsIContent
*>(node
)->TextLength());
1193 MOZ_ASSERT(nodeOffset
<= nodeLength
, "Offset is past length of text node");
1194 if (nodeOffset
== nodeLength
) {
1195 node
= node
->GetParent();
1196 nodeOffset
= node
->IndexOf(*aNode
) + 1;
1199 node
= node
->GetParent();
1200 nodeOffset
= node
->IndexOf(*aNode
) + (nodeOffset
? 1 : 0);
1204 nsIContent
* brContent
= node
->GetChildAt(nodeOffset
- 1);
1205 while (brContent
&& brContent
->IsHTML()) {
1206 if (brContent
->Tag() != nsGkAtoms::br
|| IsContentBR(brContent
)) {
1209 brContent
= node
->GetChildAt(--nodeOffset
- 1);
1212 *aNodeOffset
= std::max(nodeOffset
, 0);
1216 ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent
* aEvent
)
1218 aEvent
->mSucceeded
= false;
1220 // Get selection to manipulate
1221 // XXX why do we need to get them from ISM? This method should work fine
1224 IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(mSelection
),
1225 getter_AddRefs(mRootContent
));
1226 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
1227 NS_ENSURE_SUCCESS(rv
, rv
);
1230 NS_ENSURE_SUCCESS(rv
, rv
);
1233 // Get range from offset and length
1234 nsRefPtr
<nsRange
> range
= new nsRange(mRootContent
);
1235 rv
= SetRangeFromFlatTextOffset(range
, aEvent
->mOffset
, aEvent
->mLength
,
1236 GetLineBreakType(aEvent
),
1237 aEvent
->mExpandToClusterBoundary
);
1238 NS_ENSURE_SUCCESS(rv
, rv
);
1240 nsINode
* startNode
= range
->GetStartParent();
1241 nsINode
* endNode
= range
->GetEndParent();
1242 int32_t startNodeOffset
= range
->StartOffset();
1243 int32_t endNodeOffset
= range
->EndOffset();
1244 AdjustRangeForSelection(mRootContent
, &startNode
, &startNodeOffset
);
1245 AdjustRangeForSelection(mRootContent
, &endNode
, &endNodeOffset
);
1247 nsCOMPtr
<nsIDOMNode
> startDomNode(do_QueryInterface(startNode
));
1248 nsCOMPtr
<nsIDOMNode
> endDomNode(do_QueryInterface(endNode
));
1249 NS_ENSURE_TRUE(startDomNode
&& endDomNode
, NS_ERROR_UNEXPECTED
);
1251 nsCOMPtr
<nsISelectionPrivate
> selPrivate(do_QueryInterface(mSelection
));
1252 selPrivate
->StartBatchChanges();
1254 // Clear selection first before setting
1255 rv
= mSelection
->RemoveAllRanges();
1256 // Need to call EndBatchChanges at the end even if call failed
1257 if (NS_SUCCEEDED(rv
)) {
1258 if (aEvent
->mReversed
) {
1259 rv
= mSelection
->Collapse(endDomNode
, endNodeOffset
);
1261 rv
= mSelection
->Collapse(startDomNode
, startNodeOffset
);
1263 if (NS_SUCCEEDED(rv
) &&
1264 (startDomNode
!= endDomNode
|| startNodeOffset
!= endNodeOffset
)) {
1265 if (aEvent
->mReversed
) {
1266 rv
= mSelection
->Extend(startDomNode
, startNodeOffset
);
1268 rv
= mSelection
->Extend(endDomNode
, endNodeOffset
);
1272 selPrivate
->EndBatchChanges();
1273 NS_ENSURE_SUCCESS(rv
, rv
);
1275 selPrivate
->ScrollIntoViewInternal(
1276 nsISelectionController::SELECTION_FOCUS_REGION
,
1277 false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis());
1278 aEvent
->mSucceeded
= true;
1282 } // namespace mozilla