1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
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 "SelectionCarets.h"
10 #include "nsBidiPresUtils.h"
11 #include "nsCanvasFrame.h"
13 #include "nsContentUtils.h"
15 #include "nsDOMTokenList.h"
17 #include "nsIDocument.h"
18 #include "nsIDocShell.h"
19 #include "nsIDOMDocument.h"
20 #include "nsIDOMNodeFilter.h"
21 #include "nsIPresShell.h"
22 #include "nsPresContext.h"
25 #include "mozilla/dom/DOMRect.h"
26 #include "mozilla/dom/Element.h"
27 #include "mozilla/dom/Selection.h"
28 #include "mozilla/dom/TreeWalker.h"
29 #include "mozilla/Preferences.h"
30 #include "mozilla/TouchEvents.h"
31 #include "TouchCaret.h"
32 #include "nsFrameSelection.h"
34 using namespace mozilla
;
36 // We treat mouse/touch move as "REAL" move event once its move distance
37 // exceed this value, in CSS pixel.
38 static const int32_t kMoveStartTolerancePx
= 5;
39 // Time for trigger scroll end event, in miliseconds.
40 static const int32_t kScrollEndTimerDelay
= 300;
42 NS_IMPL_ISUPPORTS(SelectionCarets
,
45 nsISupportsWeakReference
)
47 /*static*/ int32_t SelectionCarets::sSelectionCaretsInflateSize
= 0;
49 SelectionCarets::SelectionCarets(nsIPresShell
*aPresShell
)
51 , mCaretCenterToDownPointOffsetY(0)
54 , mStartCaretVisible(false)
55 , mEndCaretVisible(false)
57 MOZ_ASSERT(NS_IsMainThread());
59 static bool addedPref
= false;
61 Preferences::AddIntVarCache(&sSelectionCaretsInflateSize
,
62 "selectioncaret.inflatesize.threshold");
66 mPresShell
= aPresShell
;
69 SelectionCarets::~SelectionCarets()
71 MOZ_ASSERT(NS_IsMainThread());
73 if (mLongTapDetectorTimer
) {
74 mLongTapDetectorTimer
->Cancel();
75 mLongTapDetectorTimer
= nullptr;
78 if (mScrollEndDetectorTimer
) {
79 mScrollEndDetectorTimer
->Cancel();
80 mScrollEndDetectorTimer
= nullptr;
87 IsOnRect(const nsRect
& aRect
,
88 const nsPoint
& aPoint
,
91 // Check if the click was in the bounding box of the selection caret
93 rect
.Inflate(aInflateSize
);
94 return rect
.Contains(aPoint
);
98 SelectionCarets::HandleEvent(WidgetEvent
* aEvent
)
100 WidgetMouseEvent
*mouseEvent
= aEvent
->AsMouseEvent();
101 if (mouseEvent
&& mouseEvent
->reason
== WidgetMouseEvent::eSynthesized
) {
102 return nsEventStatus_eIgnore
;
105 WidgetTouchEvent
*touchEvent
= aEvent
->AsTouchEvent();
106 nsIntPoint movePoint
;
107 int32_t nowTouchId
= -1;
108 if (touchEvent
&& !touchEvent
->touches
.IsEmpty()) {
109 // If touch happened, just grab event with same identifier
110 if (mActiveTouchId
>= 0) {
111 for (uint32_t i
= 0; i
< touchEvent
->touches
.Length(); ++i
) {
112 if (touchEvent
->touches
[i
]->Identifier() == mActiveTouchId
) {
113 movePoint
= touchEvent
->touches
[i
]->mRefPoint
;
114 nowTouchId
= touchEvent
->touches
[i
]->Identifier();
119 // not found, consume it
120 if (nowTouchId
== -1) {
121 return nsEventStatus_eConsumeNoDefault
;
124 movePoint
= touchEvent
->touches
[0]->mRefPoint
;
125 nowTouchId
= touchEvent
->touches
[0]->Identifier();
127 } else if (mouseEvent
) {
128 movePoint
= LayoutDeviceIntPoint::ToUntyped(mouseEvent
->AsGUIEvent()->refPoint
);
131 // Get event coordinate relative to canvas frame
132 nsIFrame
* canvasFrame
= mPresShell
->GetCanvasFrame();
134 return nsEventStatus_eIgnore
;
137 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, movePoint
, canvasFrame
);
139 if (aEvent
->message
== NS_TOUCH_START
||
140 (aEvent
->message
== NS_MOUSE_BUTTON_DOWN
&&
141 mouseEvent
->button
== WidgetMouseEvent::eLeftButton
)) {
142 // If having a active touch, ignore other touch down event
143 if (aEvent
->message
== NS_TOUCH_START
&& mActiveTouchId
>= 0) {
144 return nsEventStatus_eConsumeNoDefault
;
147 mActiveTouchId
= nowTouchId
;
148 mDownPoint
= ptInCanvas
;
149 int32_t inflateSize
= SelectionCaretsInflateSize();
150 if (mVisible
&& IsOnRect(GetStartFrameRect(), ptInCanvas
, inflateSize
)) {
151 mDragMode
= START_FRAME
;
152 mCaretCenterToDownPointOffsetY
= GetCaretYCenterPosition() - ptInCanvas
.y
;
153 SetSelectionDirection(false);
154 SetSelectionDragState(true);
155 return nsEventStatus_eConsumeNoDefault
;
156 } else if (mVisible
&& IsOnRect(GetEndFrameRect(), ptInCanvas
, inflateSize
)) {
157 mDragMode
= END_FRAME
;
158 mCaretCenterToDownPointOffsetY
= GetCaretYCenterPosition() - ptInCanvas
.y
;
159 SetSelectionDirection(true);
160 SetSelectionDragState(true);
161 return nsEventStatus_eConsumeNoDefault
;
165 SetVisibility(false);
166 LaunchLongTapDetector();
168 } else if (aEvent
->message
== NS_TOUCH_END
||
169 aEvent
->message
== NS_TOUCH_CANCEL
||
170 aEvent
->message
== NS_MOUSE_BUTTON_UP
) {
171 CancelLongTapDetector();
172 if (mDragMode
!= NONE
) {
173 // Only care about same id
174 if (mActiveTouchId
== nowTouchId
) {
175 SetSelectionDragState(false);
179 return nsEventStatus_eConsumeNoDefault
;
181 } else if (aEvent
->message
== NS_TOUCH_MOVE
||
182 aEvent
->message
== NS_MOUSE_MOVE
) {
183 if (mDragMode
== START_FRAME
|| mDragMode
== END_FRAME
) {
184 if (mActiveTouchId
== nowTouchId
) {
185 ptInCanvas
.y
+= mCaretCenterToDownPointOffsetY
;
186 return DragSelection(ptInCanvas
);
189 return nsEventStatus_eConsumeNoDefault
;
192 nsPoint delta
= mDownPoint
- ptInCanvas
;
193 if (NS_hypot(delta
.x
, delta
.y
) >
194 nsPresContext::AppUnitsPerCSSPixel() * kMoveStartTolerancePx
) {
195 CancelLongTapDetector();
197 } else if (aEvent
->message
== NS_MOUSE_MOZLONGTAP
) {
200 return nsEventStatus_eConsumeNoDefault
;
203 return nsEventStatus_eIgnore
;
207 SetElementVisibility(dom::Element
* aElement
, bool aVisible
)
214 aElement
->ClassList()->Toggle(NS_LITERAL_STRING("hidden"),
215 dom::Optional
<bool>(!aVisible
), err
);
219 SelectionCarets::SetVisibility(bool aVisible
)
225 if (mVisible
== aVisible
) {
230 dom::Element
* startElement
= mPresShell
->GetSelectionCaretsStartElement();
231 SetElementVisibility(startElement
, mVisible
&& mStartCaretVisible
);
233 dom::Element
* endElement
= mPresShell
->GetSelectionCaretsEndElement();
234 SetElementVisibility(endElement
, mVisible
&& mEndCaretVisible
);
236 // We must call SetHasTouchCaret() in order to get APZC to wait until the
237 // event has been round-tripped and check whether it has been handled,
238 // otherwise B2G will end up panning the document when the user tries to drag
240 mPresShell
->SetMayHaveTouchCaret(mVisible
);
244 SelectionCarets::SetStartFrameVisibility(bool aVisible
)
246 mStartCaretVisible
= aVisible
;
247 dom::Element
* element
= mPresShell
->GetSelectionCaretsStartElement();
248 SetElementVisibility(element
, mVisible
&& mStartCaretVisible
);
252 SelectionCarets::SetEndFrameVisibility(bool aVisible
)
254 mEndCaretVisible
= aVisible
;
255 dom::Element
* element
= mPresShell
->GetSelectionCaretsEndElement();
256 SetElementVisibility(element
, mVisible
&& mEndCaretVisible
);
260 SelectionCarets::SetTilted(bool aIsTilt
)
262 dom::Element
* startElement
= mPresShell
->GetSelectionCaretsStartElement();
263 dom::Element
* endElement
= mPresShell
->GetSelectionCaretsEndElement();
265 if (!startElement
|| !endElement
) {
270 startElement
->ClassList()->Toggle(NS_LITERAL_STRING("tilt"),
271 dom::Optional
<bool>(aIsTilt
), err
);
273 endElement
->ClassList()->Toggle(NS_LITERAL_STRING("tilt"),
274 dom::Optional
<bool>(aIsTilt
), err
);
278 SetCaretDirection(dom::Element
* aElement
, bool aIsRight
)
280 MOZ_ASSERT(aElement
);
284 aElement
->ClassList()->Add(NS_LITERAL_STRING("moz-selectioncaret-right"), err
);
285 aElement
->ClassList()->Remove(NS_LITERAL_STRING("moz-selectioncaret-left"), err
);
287 aElement
->ClassList()->Add(NS_LITERAL_STRING("moz-selectioncaret-left"), err
);
288 aElement
->ClassList()->Remove(NS_LITERAL_STRING("moz-selectioncaret-right"), err
);
293 IsRightToLeft(nsIFrame
* aFrame
)
297 return aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
) ?
298 (nsBidiPresUtils::GetFrameEmbeddingLevel(aFrame
) & 1) :
299 aFrame
->StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
;
303 * Reduce rect to 1 app unit width along either left or right edge base on
304 * aToRightEdge parameter.
307 ReduceRectToVerticalEdge(nsRect
& aRect
, bool aToRightEdge
)
310 aRect
.x
= aRect
.XMost() - 1;
316 FindFirstNodeWithFrame(nsIDocument
* aDocument
,
318 nsFrameSelection
* aFrameSelection
,
322 if (!aDocument
|| !aRange
|| !aFrameSelection
) {
326 nsCOMPtr
<nsINode
> startNode
=
327 do_QueryInterface(aBackward
? aRange
->GetEndParent() : aRange
->GetStartParent());
328 nsCOMPtr
<nsINode
> endNode
=
329 do_QueryInterface(aBackward
? aRange
->GetStartParent() : aRange
->GetEndParent());
330 int32_t offset
= aBackward
? aRange
->EndOffset() : aRange
->StartOffset();
332 nsCOMPtr
<nsIContent
> startContent
= do_QueryInterface(startNode
);
333 nsCOMPtr
<nsIContent
> endContent
= do_QueryInterface(endNode
);
334 CaretAssociationHint hintStart
=
335 nsFrameSelection::GetHintForPosition(startContent
, offset
);
336 nsIFrame
* startFrame
= aFrameSelection
->GetFrameForNodeOffset(startContent
,
346 nsRefPtr
<dom::TreeWalker
> walker
=
347 aDocument
->CreateTreeWalker(*startNode
,
348 nsIDOMNodeFilter::SHOW_ALL
,
356 startFrame
= startContent
? startContent
->GetPrimaryFrame() : nullptr;
357 while (!startFrame
&& startNode
!= endNode
) {
359 startNode
= walker
->PreviousNode(err
);
361 startNode
= walker
->NextNode(err
);
363 startContent
= do_QueryInterface(startNode
);
364 startFrame
= startContent
? startContent
->GetPrimaryFrame() : nullptr;
370 SelectionCarets::UpdateSelectionCarets()
376 nsISelection
* caretSelection
= GetSelection();
377 if (!caretSelection
) {
378 SetVisibility(false);
382 nsRefPtr
<dom::Selection
> selection
= static_cast<dom::Selection
*>(caretSelection
);
383 if (selection
->GetRangeCount() <= 0) {
384 SetVisibility(false);
388 nsRefPtr
<nsRange
> range
= selection
->GetRangeAt(0);
389 if (range
->Collapsed()) {
390 SetVisibility(false);
394 nsLayoutUtils::FirstAndLastRectCollector collector
;
395 nsRange::CollectClientRects(&collector
, range
,
396 range
->GetStartParent(), range
->StartOffset(),
397 range
->GetEndParent(), range
->EndOffset(), true, true);
399 nsIFrame
* canvasFrame
= mPresShell
->GetCanvasFrame();
400 nsIFrame
* rootFrame
= mPresShell
->GetRootFrame();
402 if (!canvasFrame
|| !rootFrame
) {
403 SetVisibility(false);
407 // Check if caret inside the scroll frame's boundary
408 nsIFrame
* caretFocusFrame
= GetCaretFocusFrame();
409 if (!caretFocusFrame
) {
410 SetVisibility(false);
413 nsIContent
*editableAncestor
= caretFocusFrame
->GetContent()->GetEditingHost();
415 if (!editableAncestor
) {
416 SetVisibility(false);
421 for (nsIFrame
* frame
= editableAncestor
->GetPrimaryFrame();
423 frame
= frame
->GetNextContinuation()) {
424 nsRect rect
= frame
->GetRectRelativeToSelf();
425 nsLayoutUtils::TransformRect(frame
, rootFrame
, rect
);
426 resultRect
= resultRect
.Union(rect
);
429 // Check start and end frame is rtl or ltr text
430 nsRefPtr
<nsFrameSelection
> fs
= caretFocusFrame
->GetFrameSelection();
432 nsIFrame
* startFrame
= FindFirstNodeWithFrame(mPresShell
->GetDocument(),
433 range
, fs
, false, startOffset
);
436 nsIFrame
* endFrame
= FindFirstNodeWithFrame(mPresShell
->GetDocument(),
437 range
, fs
, true, endOffset
);
439 if (!startFrame
|| !endFrame
) {
440 SetVisibility(false);
444 // Check if startFrame is after endFrame.
445 if (nsLayoutUtils::CompareTreePosition(startFrame
, endFrame
) > 0) {
446 SetVisibility(false);
450 bool startFrameIsRTL
= IsRightToLeft(startFrame
);
451 bool endFrameIsRTL
= IsRightToLeft(endFrame
);
453 // If start frame is LTR, then place start caret in first rect's leftmost
454 // otherwise put it to first rect's rightmost.
455 ReduceRectToVerticalEdge(collector
.mFirstRect
, startFrameIsRTL
);
457 // Contrary to start frame, if end frame is LTR, put end caret to last
458 // rect's rightmost position, otherwise, put it to last rect's leftmost.
459 ReduceRectToVerticalEdge(collector
.mLastRect
, !endFrameIsRTL
);
461 SetStartFrameVisibility(resultRect
.Intersects(collector
.mFirstRect
));
462 SetEndFrameVisibility(resultRect
.Intersects(collector
.mLastRect
));
464 nsLayoutUtils::TransformRect(rootFrame
, canvasFrame
, collector
.mFirstRect
);
465 nsLayoutUtils::TransformRect(rootFrame
, canvasFrame
, collector
.mLastRect
);
467 SetStartFramePos(collector
.mFirstRect
.BottomLeft());
468 SetEndFramePos(collector
.mLastRect
.BottomRight());
471 // If range select only one character, append tilt class name to it.
473 if (startFrame
&& endFrame
) {
474 // In this case <textarea>abc</textarea> and we select 'c' character,
475 // EndContent would be HTMLDivElement and mResultContent which get by
476 // calling startFrame->PeekOffset() with selecting next cluster would be
477 // TextNode. Although the position is same, nsContentUtils::ComparePoints
478 // still shows HTMLDivElement is after TextNode. So that we cannot use
479 // EndContent or StartContent to compare with result of PeekOffset().
480 // So we compare between next charater of startFrame and previous character
482 nsPeekOffsetStruct
posNext(eSelectCluster
,
487 true, //limit on scrolled views
491 nsPeekOffsetStruct
posPrev(eSelectCluster
,
496 true, //limit on scrolled views
499 startFrame
->PeekOffset(&posNext
);
500 endFrame
->PeekOffset(&posPrev
);
502 if (posNext
.mResultContent
&& posPrev
.mResultContent
&&
503 nsContentUtils::ComparePoints(posNext
.mResultContent
, posNext
.mContentOffset
,
504 posPrev
.mResultContent
, posPrev
.mContentOffset
) > 0) {
509 SetCaretDirection(mPresShell
->GetSelectionCaretsStartElement(), startFrameIsRTL
);
510 SetCaretDirection(mPresShell
->GetSelectionCaretsEndElement(), !endFrameIsRTL
);
515 SelectionCarets::SelectWord()
517 // If caret isn't visible, the word is not selectable
518 if (!GetCaretVisible()) {
526 nsIFrame
* canvasFrame
= mPresShell
->GetCanvasFrame();
531 // Find content offsets for mouse down point
532 nsIFrame
*ptFrame
= nsLayoutUtils::GetFrameForPoint(canvasFrame
, mDownPoint
,
533 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION
| nsLayoutUtils::IGNORE_CROSS_DOC
);
538 nsPoint ptInFrame
= mDownPoint
;
539 nsLayoutUtils::TransformPoint(canvasFrame
, ptFrame
, ptInFrame
);
541 nsIFrame
* caretFocusFrame
= GetCaretFocusFrame();
542 if (!caretFocusFrame
) {
546 SetSelectionDragState(true);
547 nsFrame
* frame
= static_cast<nsFrame
*>(ptFrame
);
548 nsresult rs
= frame
->SelectByTypeAtPoint(mPresShell
->GetPresContext(), ptInFrame
,
549 eSelectWord
, eSelectWord
, 0);
550 SetSelectionDragState(false);
552 // Clear maintain selection otherwise we cannot select less than a word
553 nsRefPtr
<nsFrameSelection
> fs
= caretFocusFrame
->GetFrameSelection();
554 fs
->MaintainSelection();
559 * If we're dragging start caret, we do not want to drag over previous
560 * character of end caret. Same as end caret. So we check if content offset
561 * exceed previous/next character of end/start caret base on aDragMode.
564 CompareRangeWithContentOffset(nsRange
* aRange
,
565 nsFrameSelection
* aSelection
,
566 nsIFrame::ContentOffsets
& aOffsets
,
567 SelectionCarets::DragMode aDragMode
)
569 MOZ_ASSERT(aDragMode
!= SelectionCarets::NONE
);
570 nsINode
* node
= nullptr;
571 int32_t nodeOffset
= 0;
572 CaretAssociationHint hint
;
575 if (aDragMode
== SelectionCarets::START_FRAME
) {
576 // Check previous character of end node offset
577 node
= aRange
->GetEndParent();
578 nodeOffset
= aRange
->EndOffset();
579 hint
= CARET_ASSOCIATE_BEFORE
;
582 // Check next character of start node offset
583 node
= aRange
->GetStartParent();
584 nodeOffset
= aRange
->StartOffset();
585 hint
= CARET_ASSOCIATE_AFTER
;
588 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(node
);
592 aSelection
->GetFrameForNodeOffset(content
, nodeOffset
, hint
, &offset
);
598 // Move one character forward/backward from point and get offset
599 nsPeekOffsetStruct
pos(eSelectCluster
,
604 true, //limit on scrolled views
607 nsresult rv
= theFrame
->PeekOffset(&pos
);
609 pos
.mResultContent
= content
;
610 pos
.mContentOffset
= nodeOffset
;
613 // Compare with current point
614 int32_t result
= nsContentUtils::ComparePoints(aOffsets
.content
,
615 aOffsets
.StartOffset(),
618 if ((aDragMode
== SelectionCarets::START_FRAME
&& result
== 1) ||
619 (aDragMode
== SelectionCarets::END_FRAME
&& result
== -1)) {
620 aOffsets
.content
= pos
.mResultContent
;
621 aOffsets
.offset
= pos
.mContentOffset
;
622 aOffsets
.secondaryOffset
= pos
.mContentOffset
;
629 SelectionCarets::DragSelection(const nsPoint
&movePoint
)
631 nsIFrame
* canvasFrame
= mPresShell
->GetCanvasFrame();
633 return nsEventStatus_eConsumeNoDefault
;
636 // Find out which content we point to
637 nsIFrame
*ptFrame
= nsLayoutUtils::GetFrameForPoint(canvasFrame
, movePoint
,
638 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION
| nsLayoutUtils::IGNORE_CROSS_DOC
);
640 return nsEventStatus_eConsumeNoDefault
;
643 nsIFrame
* caretFocusFrame
= GetCaretFocusFrame();
644 if (!caretFocusFrame
) {
645 return nsEventStatus_eConsumeNoDefault
;
648 nsRefPtr
<nsFrameSelection
> fs
= caretFocusFrame
->GetFrameSelection();
651 nsIFrame
*newFrame
= nullptr;
653 nsPoint ptInFrame
= movePoint
;
654 nsLayoutUtils::TransformPoint(canvasFrame
, ptFrame
, ptInFrame
);
655 result
= fs
->ConstrainFrameAndPointToAnchorSubtree(ptFrame
, ptInFrame
, &newFrame
, newPoint
);
656 if (NS_FAILED(result
) || !newFrame
) {
657 return nsEventStatus_eConsumeNoDefault
;
660 nsFrame::ContentOffsets offsets
=
661 newFrame
->GetContentOffsetsFromPoint(newPoint
);
662 if (!offsets
.content
) {
663 return nsEventStatus_eConsumeNoDefault
;
666 nsISelection
* caretSelection
= GetSelection();
667 nsRefPtr
<dom::Selection
> selection
= static_cast<dom::Selection
*>(caretSelection
);
668 if (selection
->GetRangeCount() <= 0) {
669 return nsEventStatus_eConsumeNoDefault
;
672 nsRefPtr
<nsRange
> range
= selection
->GetRangeAt(0);
673 if (!CompareRangeWithContentOffset(range
, fs
, offsets
, mDragMode
)) {
674 return nsEventStatus_eConsumeNoDefault
;
677 // Move caret postion.
678 nsIFrame
*scrollable
=
679 nsLayoutUtils::GetClosestFrameOfType(caretFocusFrame
, nsGkAtoms::scrollFrame
);
680 nsWeakFrame weakScrollable
= scrollable
;
681 fs
->HandleClick(offsets
.content
, offsets
.StartOffset(),
686 if (!weakScrollable
.IsAlive()) {
687 return nsEventStatus_eConsumeNoDefault
;
690 // Scroll scrolled frame.
691 nsIScrollableFrame
*saf
= do_QueryFrame(scrollable
);
692 nsIFrame
*capturingFrame
= saf
->GetScrolledFrame();
693 nsPoint ptInScrolled
= movePoint
;
694 nsLayoutUtils::TransformPoint(canvasFrame
, capturingFrame
, ptInScrolled
);
695 fs
->StartAutoScrollTimer(capturingFrame
, ptInScrolled
, TouchCaret::sAutoScrollTimerDelay
);
696 UpdateSelectionCarets();
697 return nsEventStatus_eConsumeNoDefault
;
701 SelectionCarets::GetCaretYCenterPosition()
703 nsIFrame
* canvasFrame
= mPresShell
->GetCanvasFrame();
704 nsIFrame
* caretFocusFrame
= GetCaretFocusFrame();
706 if (!canvasFrame
|| !caretFocusFrame
) {
709 nsISelection
* caretSelection
= GetSelection();
710 nsRefPtr
<dom::Selection
> selection
= static_cast<dom::Selection
*>(caretSelection
);
711 if (selection
->GetRangeCount() <= 0) {
714 nsRefPtr
<nsRange
> range
= selection
->GetRangeAt(0);
715 nsRefPtr
<nsFrameSelection
> fs
= caretFocusFrame
->GetFrameSelection();
717 MOZ_ASSERT(mDragMode
!= NONE
);
718 nsCOMPtr
<nsIContent
> node
;
720 if (mDragMode
== START_FRAME
) {
721 node
= do_QueryInterface(range
->GetStartParent());
722 nodeOffset
= range
->StartOffset();
724 node
= do_QueryInterface(range
->GetEndParent());
725 nodeOffset
= range
->EndOffset();
729 CaretAssociationHint hint
=
730 nsFrameSelection::GetHintForPosition(node
, nodeOffset
);
732 fs
->GetFrameForNodeOffset(node
, nodeOffset
, hint
, &offset
);
737 nsRect frameRect
= theFrame
->GetRectRelativeToSelf();
738 nsLayoutUtils::TransformRect(theFrame
, canvasFrame
, frameRect
);
739 return frameRect
.Center().y
;
743 SelectionCarets::SetSelectionDragState(bool aState
)
745 nsIFrame
* caretFocusFrame
= GetCaretFocusFrame();
746 if (!caretFocusFrame
) {
750 nsRefPtr
<nsFrameSelection
> fs
= caretFocusFrame
->GetFrameSelection();
751 fs
->SetDragState(aState
);
755 SelectionCarets::SetSelectionDirection(bool aForward
)
757 nsISelection
* caretSelection
= GetSelection();
758 nsRefPtr
<dom::Selection
> selection
= static_cast<dom::Selection
*>(caretSelection
);
759 selection
->SetDirection(aForward
? eDirNext
: eDirPrevious
);
763 SetFramePos(dom::Element
* aElement
, const nsPoint
& aPosition
)
769 nsAutoString styleStr
;
770 styleStr
.AppendLiteral("left:");
771 styleStr
.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(aPosition
.x
));
772 styleStr
.AppendLiteral("px;top:");
773 styleStr
.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(aPosition
.y
));
774 styleStr
.AppendLiteral("px;");
776 aElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::style
, styleStr
, true);
780 SelectionCarets::SetStartFramePos(const nsPoint
& aPosition
)
782 SetFramePos(mPresShell
->GetSelectionCaretsStartElement(), aPosition
);
786 SelectionCarets::SetEndFramePos(const nsPoint
& aPosition
)
788 SetFramePos(mPresShell
->GetSelectionCaretsEndElement(), aPosition
);
792 SelectionCarets::GetStartFrameRect()
794 nsIFrame
* canvasFrame
= mPresShell
->GetCanvasFrame();
795 dom::Element
* element
= mPresShell
->GetSelectionCaretsStartElement();
800 nsIFrame
* frame
= element
->GetPrimaryFrame();
805 nsRect frameRect
= frame
->GetRectRelativeToSelf();
806 nsLayoutUtils::TransformRect(frame
, canvasFrame
, frameRect
);
811 SelectionCarets::GetEndFrameRect()
813 nsIFrame
* canvasFrame
= mPresShell
->GetCanvasFrame();
814 dom::Element
* element
= mPresShell
->GetSelectionCaretsEndElement();
819 nsIFrame
* frame
= element
->GetPrimaryFrame();
824 nsRect frameRect
= frame
->GetRectRelativeToSelf();
825 nsLayoutUtils::TransformRect(frame
, canvasFrame
, frameRect
);
830 SelectionCarets::GetCaretFocusFrame()
832 nsRefPtr
<nsCaret
> caret
= mPresShell
->GetCaret();
838 return caret
->GetGeometry(&focusRect
);
842 SelectionCarets::GetCaretVisible()
848 nsRefPtr
<nsCaret
> caret
= mPresShell
->GetCaret();
853 return caret
->IsVisible();
857 SelectionCarets::GetSelection()
859 nsRefPtr
<nsCaret
> caret
= mPresShell
->GetCaret();
860 return caret
->GetSelection();
864 SelectionCarets::NotifySelectionChanged(nsIDOMDocument
* aDoc
,
869 aSel
->GetIsCollapsed(&isCollapsed
);
871 SetVisibility(false);
874 if (aReason
& nsISelectionListener::KEYPRESS_REASON
) {
875 SetVisibility(false);
877 UpdateSelectionCarets();
883 SelectionCarets::ScrollPositionChanged()
885 SetVisibility(false);
886 LaunchScrollEndDetector();
890 SelectionCarets::LaunchLongTapDetector()
892 if (XRE_GetProcessType() != GeckoProcessType_Default
) {
896 if (!mLongTapDetectorTimer
) {
897 mLongTapDetectorTimer
= do_CreateInstance("@mozilla.org/timer;1");
900 MOZ_ASSERT(mLongTapDetectorTimer
);
901 CancelLongTapDetector();
902 int32_t longTapDelay
= gfxPrefs::UiClickHoldContextMenusDelay();
903 mLongTapDetectorTimer
->InitWithFuncCallback(FireLongTap
,
906 nsITimer::TYPE_ONE_SHOT
);
910 SelectionCarets::CancelLongTapDetector()
912 if (XRE_GetProcessType() != GeckoProcessType_Default
) {
916 if (!mLongTapDetectorTimer
) {
920 mLongTapDetectorTimer
->Cancel();
924 SelectionCarets::FireLongTap(nsITimer
* aTimer
, void* aSelectionCarets
)
926 nsRefPtr
<SelectionCarets
> self
= static_cast<SelectionCarets
*>(aSelectionCarets
);
927 NS_PRECONDITION(aTimer
== self
->mLongTapDetectorTimer
,
934 SelectionCarets::LaunchScrollEndDetector()
936 if (!mScrollEndDetectorTimer
) {
937 mScrollEndDetectorTimer
= do_CreateInstance("@mozilla.org/timer;1");
940 MOZ_ASSERT(mScrollEndDetectorTimer
);
941 mScrollEndDetectorTimer
->InitWithFuncCallback(FireScrollEnd
,
943 kScrollEndTimerDelay
,
944 nsITimer::TYPE_ONE_SHOT
);
948 SelectionCarets::FireScrollEnd(nsITimer
* aTimer
, void* aSelectionCarets
)
950 nsRefPtr
<SelectionCarets
> self
= static_cast<SelectionCarets
*>(aSelectionCarets
);
951 NS_PRECONDITION(aTimer
== self
->mScrollEndDetectorTimer
,
953 self
->SetVisibility(true);
954 self
->UpdateSelectionCarets();