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/. */
8 #include "TouchCaret.h"
12 #include "nsBlockFrame.h"
13 #include "nsCanvasFrame.h"
16 #include "nsContentUtils.h"
17 #include "nsDOMTokenList.h"
18 #include "nsFrameSelection.h"
19 #include "nsIContent.h"
20 #include "nsIDOMNode.h"
21 #include "nsIDOMWindow.h"
23 #include "nsIInterfaceRequestorUtils.h"
24 #include "nsIPresShell.h"
25 #include "nsIScrollableFrame.h"
26 #include "nsISelection.h"
27 #include "nsISelectionController.h"
28 #include "nsISelectionPrivate.h"
29 #include "nsPresContext.h"
30 #include "nsQueryContentEventResult.h"
32 #include "mozilla/dom/SelectionStateChangedEvent.h"
33 #include "mozilla/dom/CustomEvent.h"
34 #include "mozilla/BasicEvents.h"
35 #include "mozilla/Preferences.h"
37 using namespace mozilla
;
40 static PRLogModuleInfo
* gTouchCaretLog
;
41 static const char* kTouchCaretLogModuleName
= "TouchCaret";
43 // To enable all the TOUCHCARET_LOG print statements, set the environment
44 // variable NSPR_LOG_MODULES=TouchCaret:5
45 #define TOUCHCARET_LOG(message, ...) \
46 PR_LOG(gTouchCaretLog, PR_LOG_DEBUG, \
47 ("TouchCaret (%p): %s:%d : " message "\n", this, __FUNCTION__, \
48 __LINE__, ##__VA_ARGS__));
50 #define TOUCHCARET_LOG_STATIC(message, ...) \
51 PR_LOG(gTouchCaretLog, PR_LOG_DEBUG, \
52 ("TouchCaret: %s:%d : " message "\n", __FUNCTION__, __LINE__, \
55 #define TOUCHCARET_LOG(message, ...)
56 #define TOUCHCARET_LOG_STATIC(message, ...)
57 #endif // #ifdef PR_LOGGING
59 // Click on the boundary of input/textarea will place the caret at the
60 // front/end of the content. To advoid this, we need to deflate the content
61 // boundary by 61 app units (1 pixel + 1 app unit).
62 static const int32_t kBoundaryAppUnits
= 61;
64 NS_IMPL_ISUPPORTS(TouchCaret
, nsISelectionListener
)
66 /*static*/ int32_t TouchCaret::sTouchCaretInflateSize
= 0;
67 /*static*/ int32_t TouchCaret::sTouchCaretExpirationTime
= 0;
69 TouchCaret::TouchCaret(nsIPresShell
* aPresShell
)
70 : mState(TOUCHCARET_NONE
),
72 mCaretCenterToDownPointOffsetY(0),
76 MOZ_ASSERT(NS_IsMainThread());
79 if (!gTouchCaretLog
) {
80 gTouchCaretLog
= PR_NewLogModule(kTouchCaretLogModuleName
);
84 TOUCHCARET_LOG("Constructor, PresShell=%p", aPresShell
);
86 static bool addedTouchCaretPref
= false;
87 if (!addedTouchCaretPref
) {
88 Preferences::AddIntVarCache(&sTouchCaretInflateSize
,
89 "touchcaret.inflatesize.threshold");
90 Preferences::AddIntVarCache(&sTouchCaretExpirationTime
,
91 "touchcaret.expiration.time");
92 addedTouchCaretPref
= true;
95 // The presshell owns us, so no addref.
96 mPresShell
= do_GetWeakReference(aPresShell
);
97 MOZ_ASSERT(mPresShell
, "Hey, pres shell should support weak refs");
100 TouchCaret::~TouchCaret()
102 TOUCHCARET_LOG("Destructor");
103 MOZ_ASSERT(NS_IsMainThread());
105 if (mTouchCaretExpirationTimer
) {
106 mTouchCaretExpirationTimer
->Cancel();
107 mTouchCaretExpirationTimer
= nullptr;
112 TouchCaret::GetCaretFocusFrame(nsRect
* aOutRect
)
114 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
119 nsRefPtr
<nsCaret
> caret
= presShell
->GetCaret();
125 nsIFrame
* frame
= caret
->GetGeometry(&rect
);
135 TouchCaret::GetCanvasFrame()
137 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
141 return presShell
->GetCanvasFrame();
145 TouchCaret::GetRootFrame()
147 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
151 return presShell
->GetRootFrame();
155 TouchCaret::SetVisibility(bool aVisible
)
157 if (mVisible
== aVisible
) {
158 TOUCHCARET_LOG("Set visibility %s, same as the old one",
159 (aVisible
? "shown" : "hidden"));
163 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
168 mozilla::dom::Element
* touchCaretElement
= presShell
->GetTouchCaretElement();
169 if (!touchCaretElement
) {
175 // Set touch caret visibility.
177 touchCaretElement
->ClassList()->Toggle(NS_LITERAL_STRING("hidden"),
178 dom::Optional
<bool>(!mVisible
),
180 TOUCHCARET_LOG("Set visibility %s", (mVisible
? "shown" : "hidden"));
182 // Set touch caret expiration time.
183 mVisible
? LaunchExpirationTimer() : CancelExpirationTimer();
185 // We must call SetMayHaveTouchCaret() in order to get APZC to wait until the
186 // event has been round-tripped and check whether it has been handled,
187 // otherwise B2G will end up panning the document when the user tries to drag
189 presShell
->SetMayHaveTouchCaret(mVisible
);
193 TouchCaret::GetTouchFrameRect()
195 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
200 dom::Element
* touchCaretElement
= presShell
->GetTouchCaretElement();
201 nsIFrame
* canvasFrame
= GetCanvasFrame();
202 return nsLayoutUtils::GetRectRelativeToFrame(touchCaretElement
, canvasFrame
);
206 TouchCaret::GetContentBoundary()
208 nsIFrame
* focusFrame
= GetCaretFocusFrame();
209 nsIFrame
* canvasFrame
= GetCanvasFrame();
210 if (!focusFrame
|| !canvasFrame
) {
214 // Get the editing host to determine the touch caret dragable boundary.
215 dom::Element
* editingHost
= focusFrame
->GetContent()->GetEditingHost();
221 for (nsIFrame
* frame
= editingHost
->GetPrimaryFrame(); frame
;
222 frame
= frame
->GetNextContinuation()) {
223 nsRect rect
= frame
->GetContentRectRelativeToSelf();
224 nsLayoutUtils::TransformRect(frame
, canvasFrame
, rect
);
225 resultRect
= resultRect
.Union(rect
);
227 mozilla::layout::FrameChildListIterator
lists(frame
);
228 for (; !lists
.IsDone(); lists
.Next()) {
229 // Loop over all children to take the overflow rect in to consideration.
230 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
231 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
232 nsIFrame
* kid
= childFrames
.get();
233 nsRect overflowRect
= kid
->GetScrollableOverflowRect();
234 nsLayoutUtils::TransformRect(kid
, canvasFrame
, overflowRect
);
235 resultRect
= resultRect
.Union(overflowRect
);
239 // Shrink rect to make sure we never hit the boundary.
240 resultRect
.Deflate(kBoundaryAppUnits
);
246 TouchCaret::GetCaretYCenterPosition()
249 nsIFrame
* focusFrame
= GetCaretFocusFrame(&caretRect
);
250 nsIFrame
* canvasFrame
= GetCanvasFrame();
252 nsLayoutUtils::TransformRect(focusFrame
, canvasFrame
, caretRect
);
254 return (caretRect
.y
+ caretRect
.height
/ 2);
258 TouchCaret::SetTouchFramePos(const nsRect
& aCaretRect
)
260 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
265 mozilla::dom::Element
* touchCaretElement
= presShell
->GetTouchCaretElement();
266 if (!touchCaretElement
) {
270 // Convert aOrigin to CSS pixels.
271 nsRefPtr
<nsPresContext
> presContext
= presShell
->GetPresContext();
272 int32_t x
= presContext
->AppUnitsToIntCSSPixels(aCaretRect
.Center().x
);
273 int32_t y
= presContext
->AppUnitsToIntCSSPixels(aCaretRect
.y
);
274 int32_t padding
= presContext
->AppUnitsToIntCSSPixels(aCaretRect
.height
);
276 nsAutoString styleStr
;
277 styleStr
.AppendLiteral("left: ");
278 styleStr
.AppendInt(x
);
279 styleStr
.AppendLiteral("px; top: ");
280 styleStr
.AppendInt(y
);
281 styleStr
.AppendLiteral("px; padding-top: ");
282 styleStr
.AppendInt(padding
);
283 styleStr
.AppendLiteral("px;");
285 TOUCHCARET_LOG("Set style: %s", NS_ConvertUTF16toUTF8(styleStr
).get());
287 touchCaretElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::style
,
292 TouchCaret::MoveCaret(const nsPoint
& movePoint
)
294 nsIFrame
* focusFrame
= GetCaretFocusFrame();
295 nsIFrame
* canvasFrame
= GetCanvasFrame();
296 if (!focusFrame
&& !canvasFrame
) {
299 nsIFrame
* scrollable
=
300 nsLayoutUtils::GetClosestFrameOfType(focusFrame
, nsGkAtoms::scrollFrame
);
302 // Convert touch/mouse position to frame coordinates.
303 nsPoint offsetToCanvasFrame
= nsPoint(0,0);
304 nsLayoutUtils::TransformPoint(scrollable
, canvasFrame
, offsetToCanvasFrame
);
305 nsPoint pt
= movePoint
- offsetToCanvasFrame
;
308 nsIFrame::ContentOffsets offsets
=
309 scrollable
->GetContentOffsetsFromPoint(pt
, nsIFrame::SKIP_HIDDEN
);
311 // Move caret position.
312 nsWeakFrame weakScrollable
= scrollable
;
313 nsRefPtr
<nsFrameSelection
> fs
= scrollable
->GetFrameSelection();
314 fs
->HandleClick(offsets
.content
, offsets
.StartOffset(),
320 if (!weakScrollable
.IsAlive()) {
324 // Scroll scrolled frame.
325 nsIScrollableFrame
* saf
= do_QueryFrame(scrollable
);
326 nsIFrame
* capturingFrame
= saf
->GetScrolledFrame();
327 offsetToCanvasFrame
= nsPoint(0,0);
328 nsLayoutUtils::TransformPoint(capturingFrame
, canvasFrame
, offsetToCanvasFrame
);
329 pt
= movePoint
- offsetToCanvasFrame
;
330 fs
->StartAutoScrollTimer(capturingFrame
, pt
, sAutoScrollTimerDelay
);
334 TouchCaret::IsOnTouchCaret(const nsPoint
& aPoint
)
336 return mVisible
&& nsLayoutUtils::ContainsPoint(GetTouchFrameRect(), aPoint
,
337 TouchCaretInflateSize());
341 TouchCaret::NotifySelectionChanged(nsIDOMDocument
* aDoc
, nsISelection
* aSel
,
344 TOUCHCARET_LOG("aSel (%p), Reason=%d", aSel
, aReason
);
346 // Hide touch caret while no caret exists.
347 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
352 nsRefPtr
<nsCaret
> caret
= presShell
->GetCaret();
354 SetVisibility(false);
358 // The same touch caret is shared amongst the document and any text widgets it
359 // may contain. This means that the touch caret could get notifications from
360 // multiple selections.
361 // If this notification is for a selection that is not the one the
362 // the caret is currently interested in , then there is nothing to do!
363 if (aSel
!= caret
->GetSelection()) {
364 TOUCHCARET_LOG("Return for selection mismatch!");
368 // Update touch caret position and visibility.
369 // Hide touch caret while key event causes selection change.
370 // Also hide touch caret when gecko or javascript collapse the selection.
371 if (aReason
& nsISelectionListener::KEYPRESS_REASON
||
372 aReason
& nsISelectionListener::COLLAPSETOSTART_REASON
||
373 aReason
& nsISelectionListener::COLLAPSETOEND_REASON
) {
374 TOUCHCARET_LOG("KEYPRESS_REASON");
375 SetVisibility(false);
377 SyncVisibilityWithCaret();
384 TouchCaret::SyncVisibilityWithCaret()
386 TOUCHCARET_LOG("SyncVisibilityWithCaret");
388 if (!IsDisplayable()) {
389 SetVisibility(false);
400 TouchCaret::UpdatePositionIfNeeded()
402 TOUCHCARET_LOG("UpdatePositionIfNeeded");
404 if (!IsDisplayable()) {
405 SetVisibility(false);
415 TouchCaret::IsDisplayable()
417 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
419 TOUCHCARET_LOG("PresShell is nullptr!");
423 nsRefPtr
<nsCaret
> caret
= presShell
->GetCaret();
425 TOUCHCARET_LOG("Caret is nullptr!");
429 nsIFrame
* canvasFrame
= GetCanvasFrame();
431 TOUCHCARET_LOG("No canvas frame!");
435 nsIFrame
* rootFrame
= GetRootFrame();
437 TOUCHCARET_LOG("No root frame!");
441 dom::Element
* touchCaretElement
= presShell
->GetTouchCaretElement();
442 if (!touchCaretElement
) {
443 TOUCHCARET_LOG("No touch caret frame element!");
447 if (presShell
->IsPaintingSuppressed()) {
448 TOUCHCARET_LOG("PresShell is suppressing painting!");
452 if (!caret
->IsVisible()) {
453 TOUCHCARET_LOG("Caret is not visible!");
458 nsIFrame
* focusFrame
= caret
->GetGeometry(&focusRect
);
460 TOUCHCARET_LOG("Focus frame is not valid!");
463 if (focusRect
.IsEmpty()) {
464 TOUCHCARET_LOG("Focus rect is empty!");
468 dom::Element
* editingHost
= focusFrame
->GetContent()->GetEditingHost();
470 TOUCHCARET_LOG("Cannot get editing host!");
474 if (!nsContentUtils::HasNonEmptyTextContent(
475 editingHost
, nsContentUtils::eRecurseIntoChildren
)) {
476 TOUCHCARET_LOG("The content is empty!");
480 if (mState
!= TOUCHCARET_TOUCHDRAG_ACTIVE
&&
481 !nsLayoutUtils::IsRectVisibleInScrollFrames(focusFrame
, focusRect
)) {
482 TOUCHCARET_LOG("Caret does not show in the scrollable frame!");
486 TOUCHCARET_LOG("Touch caret is displayable!");
491 TouchCaret::UpdatePosition()
493 MOZ_ASSERT(mVisible
);
495 nsRect rect
= GetTouchCaretRect();
496 rect
= ClampRectToScrollFrame(rect
);
497 SetTouchFramePos(rect
);
501 TouchCaret::GetTouchCaretRect()
504 nsIFrame
* focusFrame
= GetCaretFocusFrame(&focusRect
);
505 nsIFrame
* rootFrame
= GetRootFrame();
506 // Transform the position to make it relative to root frame.
507 nsLayoutUtils::TransformRect(focusFrame
, rootFrame
, focusRect
);
513 TouchCaret::ClampRectToScrollFrame(const nsRect
& aRect
)
516 nsIFrame
* focusFrame
= GetCaretFocusFrame();
517 nsIFrame
* rootFrame
= GetRootFrame();
519 // Clamp the touch caret position to the scrollframe boundary.
520 nsIFrame
* closestScrollFrame
=
521 nsLayoutUtils::GetClosestFrameOfType(focusFrame
, nsGkAtoms::scrollFrame
);
523 while (closestScrollFrame
) {
524 nsIScrollableFrame
* sf
= do_QueryFrame(closestScrollFrame
);
525 nsRect visualRect
= sf
->GetScrollPortRect();
527 // Clamp the touch caret in the scroll port.
528 nsLayoutUtils::TransformRect(closestScrollFrame
, rootFrame
, visualRect
);
529 rect
= rect
.Intersect(visualRect
);
531 // Get next ancestor scroll frame.
533 nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame
->GetParent(),
534 nsGkAtoms::scrollFrame
);
541 TouchCaret::DisableTouchCaretCallback(nsITimer
* aTimer
, void* aTouchCaret
)
543 nsRefPtr
<TouchCaret
> self
= static_cast<TouchCaret
*>(aTouchCaret
);
544 NS_PRECONDITION(aTimer
== self
->mTouchCaretExpirationTimer
,
547 self
->SetVisibility(false);
551 TouchCaret::LaunchExpirationTimer()
553 if (TouchCaretExpirationTime() > 0) {
554 if (!mTouchCaretExpirationTimer
) {
555 mTouchCaretExpirationTimer
= do_CreateInstance("@mozilla.org/timer;1");
558 if (mTouchCaretExpirationTimer
) {
559 mTouchCaretExpirationTimer
->Cancel();
560 mTouchCaretExpirationTimer
->InitWithFuncCallback(DisableTouchCaretCallback
,
562 TouchCaretExpirationTime(),
563 nsITimer::TYPE_ONE_SHOT
);
569 TouchCaret::CancelExpirationTimer()
571 if (mTouchCaretExpirationTimer
) {
572 mTouchCaretExpirationTimer
->Cancel();
577 TouchCaret::SetSelectionDragState(bool aState
)
579 nsIFrame
* caretFocusFrame
= GetCaretFocusFrame();
580 if (!caretFocusFrame
) {
584 nsRefPtr
<nsFrameSelection
> fs
= caretFocusFrame
->GetFrameSelection();
585 fs
->SetDragState(aState
);
589 TouchCaret::HandleEvent(WidgetEvent
* aEvent
)
591 MOZ_ASSERT(NS_IsMainThread());
592 if (!IsDisplayable()) {
593 return nsEventStatus_eIgnore
;
596 nsEventStatus status
= nsEventStatus_eIgnore
;
598 switch (aEvent
->message
) {
600 status
= HandleTouchDownEvent(aEvent
->AsTouchEvent());
602 case NS_MOUSE_BUTTON_DOWN
:
603 status
= HandleMouseDownEvent(aEvent
->AsMouseEvent());
606 status
= HandleTouchUpEvent(aEvent
->AsTouchEvent());
608 case NS_MOUSE_BUTTON_UP
:
609 status
= HandleMouseUpEvent(aEvent
->AsMouseEvent());
612 status
= HandleTouchMoveEvent(aEvent
->AsTouchEvent());
615 status
= HandleMouseMoveEvent(aEvent
->AsMouseEvent());
617 case NS_TOUCH_CANCEL
:
619 SetState(TOUCHCARET_NONE
);
620 LaunchExpirationTimer();
628 // Disable touch caret while key/wheel event is received.
629 TOUCHCARET_LOG("Receive key/wheel event %d", aEvent
->message
);
630 SetVisibility(false);
632 case NS_MOUSE_MOZLONGTAP
:
633 if (mState
== TOUCHCARET_TOUCHDRAG_ACTIVE
) {
634 // Disable long tap event from APZ while dragging the touch caret.
635 status
= nsEventStatus_eConsumeNoDefault
;
646 TouchCaret::GetEventPosition(WidgetTouchEvent
* aEvent
, int32_t aIdentifier
)
648 for (size_t i
= 0; i
< aEvent
->touches
.Length(); i
++) {
649 if (aEvent
->touches
[i
]->mIdentifier
== aIdentifier
) {
650 // Get event coordinate relative to canvas frame.
651 nsIFrame
* canvasFrame
= GetCanvasFrame();
652 nsIntPoint touchIntPoint
= aEvent
->touches
[i
]->mRefPoint
;
653 return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
,
658 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
662 TouchCaret::GetEventPosition(WidgetMouseEvent
* aEvent
)
664 // Get event coordinate relative to canvas frame.
665 nsIFrame
* canvasFrame
= GetCanvasFrame();
666 nsIntPoint mouseIntPoint
=
667 LayoutDeviceIntPoint::ToUntyped(aEvent
->AsGUIEvent()->refPoint
);
668 return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
,
674 TouchCaret::HandleMouseMoveEvent(WidgetMouseEvent
* aEvent
)
676 TOUCHCARET_LOG("Got a mouse-move in state %d", mState
);
677 nsEventStatus status
= nsEventStatus_eIgnore
;
680 case TOUCHCARET_NONE
:
683 case TOUCHCARET_MOUSEDRAG_ACTIVE
:
685 nsPoint movePoint
= GetEventPosition(aEvent
);
686 movePoint
.y
+= mCaretCenterToDownPointOffsetY
;
687 nsRect contentBoundary
= GetContentBoundary();
688 movePoint
= contentBoundary
.ClampPoint(movePoint
);
690 MoveCaret(movePoint
);
692 status
= nsEventStatus_eConsumeNoDefault
;
696 case TOUCHCARET_TOUCHDRAG_ACTIVE
:
697 case TOUCHCARET_TOUCHDRAG_INACTIVE
:
698 // Consume mouse event in touch sequence.
699 status
= nsEventStatus_eConsumeNoDefault
;
707 TouchCaret::HandleTouchMoveEvent(WidgetTouchEvent
* aEvent
)
709 TOUCHCARET_LOG("Got a touch-move in state %d", mState
);
710 nsEventStatus status
= nsEventStatus_eIgnore
;
713 case TOUCHCARET_NONE
:
716 case TOUCHCARET_MOUSEDRAG_ACTIVE
:
717 // Consume touch event in mouse sequence.
718 status
= nsEventStatus_eConsumeNoDefault
;
721 case TOUCHCARET_TOUCHDRAG_ACTIVE
:
723 nsPoint movePoint
= GetEventPosition(aEvent
, mActiveTouchId
);
724 movePoint
.y
+= mCaretCenterToDownPointOffsetY
;
725 nsRect contentBoundary
= GetContentBoundary();
726 movePoint
= contentBoundary
.ClampPoint(movePoint
);
728 MoveCaret(movePoint
);
730 status
= nsEventStatus_eConsumeNoDefault
;
734 case TOUCHCARET_TOUCHDRAG_INACTIVE
:
735 // Consume NS_TOUCH_MOVE event in TOUCHCARET_TOUCHDRAG_INACTIVE state.
736 status
= nsEventStatus_eConsumeNoDefault
;
744 TouchCaret::HandleMouseUpEvent(WidgetMouseEvent
* aEvent
)
746 TOUCHCARET_LOG("Got a mouse-up in state %d", mState
);
747 nsEventStatus status
= nsEventStatus_eIgnore
;
750 case TOUCHCARET_NONE
:
753 case TOUCHCARET_MOUSEDRAG_ACTIVE
:
754 if (aEvent
->button
== WidgetMouseEvent::eLeftButton
) {
755 SetSelectionDragState(false);
756 LaunchExpirationTimer();
757 SetState(TOUCHCARET_NONE
);
758 status
= nsEventStatus_eConsumeNoDefault
;
762 case TOUCHCARET_TOUCHDRAG_ACTIVE
:
763 case TOUCHCARET_TOUCHDRAG_INACTIVE
:
764 // Consume mouse event in touch sequence.
765 status
= nsEventStatus_eConsumeNoDefault
;
773 TouchCaret::HandleTouchUpEvent(WidgetTouchEvent
* aEvent
)
775 TOUCHCARET_LOG("Got a touch-end in state %d", mState
);
776 // Remove touches from cache if the stroke is gone in TOUCHDRAG states.
777 if (mState
== TOUCHCARET_TOUCHDRAG_ACTIVE
||
778 mState
== TOUCHCARET_TOUCHDRAG_INACTIVE
) {
779 for (size_t i
= 0; i
< aEvent
->touches
.Length(); i
++) {
780 nsTArray
<int32_t>::index_type index
=
781 mTouchesId
.IndexOf(aEvent
->touches
[i
]->mIdentifier
);
782 MOZ_ASSERT(index
!= nsTArray
<int32_t>::NoIndex
);
783 mTouchesId
.RemoveElementAt(index
);
787 nsEventStatus status
= nsEventStatus_eIgnore
;
790 case TOUCHCARET_NONE
:
793 case TOUCHCARET_MOUSEDRAG_ACTIVE
:
794 // Consume touch event in mouse sequence.
795 status
= nsEventStatus_eConsumeNoDefault
;
798 case TOUCHCARET_TOUCHDRAG_ACTIVE
:
799 if (mTouchesId
.Length() == 0) {
800 SetSelectionDragState(false);
801 // No more finger on the screen.
802 SetState(TOUCHCARET_NONE
);
803 LaunchExpirationTimer();
805 // Still has finger touching on the screen.
806 if (aEvent
->touches
[0]->mIdentifier
== mActiveTouchId
) {
807 // Remove finger from the touch caret.
808 SetState(TOUCHCARET_TOUCHDRAG_INACTIVE
);
809 LaunchExpirationTimer();
811 // If the finger removed is not the finger on touch caret, remain in
812 // TOUCHCARET_DRAG_ACTIVE state.
815 status
= nsEventStatus_eConsumeNoDefault
;
818 case TOUCHCARET_TOUCHDRAG_INACTIVE
:
819 if (mTouchesId
.Length() == 0) {
820 // No more finger on the screen.
821 SetState(TOUCHCARET_NONE
);
823 status
= nsEventStatus_eConsumeNoDefault
;
831 TouchCaret::HandleMouseDownEvent(WidgetMouseEvent
* aEvent
)
833 TOUCHCARET_LOG("Got a mouse-down in state %d", mState
);
834 if (!GetVisibility()) {
835 // If touch caret is invisible, bypass event.
836 return nsEventStatus_eIgnore
;
839 nsEventStatus status
= nsEventStatus_eIgnore
;
842 case TOUCHCARET_NONE
:
843 if (aEvent
->button
== WidgetMouseEvent::eLeftButton
) {
844 nsPoint point
= GetEventPosition(aEvent
);
845 if (IsOnTouchCaret(point
)) {
846 SetSelectionDragState(true);
847 // Cache distence of the event point to the center of touch caret.
848 mCaretCenterToDownPointOffsetY
= GetCaretYCenterPosition() - point
.y
;
849 // Enter TOUCHCARET_MOUSEDRAG_ACTIVE state and cancel the timer.
850 SetState(TOUCHCARET_MOUSEDRAG_ACTIVE
);
851 CancelExpirationTimer();
852 status
= nsEventStatus_eConsumeNoDefault
;
854 // Set touch caret invisible if HisTest fails. Bypass event.
855 SetVisibility(false);
856 status
= nsEventStatus_eIgnore
;
859 // Set touch caret invisible if not left button down event.
860 SetVisibility(false);
861 status
= nsEventStatus_eIgnore
;
865 case TOUCHCARET_MOUSEDRAG_ACTIVE
:
866 SetVisibility(false);
867 SetState(TOUCHCARET_NONE
);
870 case TOUCHCARET_TOUCHDRAG_ACTIVE
:
871 case TOUCHCARET_TOUCHDRAG_INACTIVE
:
872 // Consume mouse event in touch sequence.
873 status
= nsEventStatus_eConsumeNoDefault
;
881 TouchCaret::HandleTouchDownEvent(WidgetTouchEvent
* aEvent
)
883 TOUCHCARET_LOG("Got a touch-start in state %d", mState
);
885 nsEventStatus status
= nsEventStatus_eIgnore
;
888 case TOUCHCARET_NONE
:
889 if (!GetVisibility()) {
890 // If touch caret is invisible, bypass event.
891 status
= nsEventStatus_eIgnore
;
893 // Loop over all the touches and see if any of them is on the touch
895 for (size_t i
= 0; i
< aEvent
->touches
.Length(); ++i
) {
896 int32_t touchId
= aEvent
->touches
[i
]->Identifier();
897 nsPoint point
= GetEventPosition(aEvent
, touchId
);
898 if (IsOnTouchCaret(point
)) {
899 SetSelectionDragState(true);
900 // Touch start position is contained in touch caret.
901 mActiveTouchId
= touchId
;
902 // Cache distance of the event point to the center of touch caret.
903 mCaretCenterToDownPointOffsetY
= GetCaretYCenterPosition() - point
.y
;
904 // Enter TOUCHCARET_TOUCHDRAG_ACTIVE state and cancel the timer.
905 SetState(TOUCHCARET_TOUCHDRAG_ACTIVE
);
906 CancelExpirationTimer();
907 status
= nsEventStatus_eConsumeNoDefault
;
911 // No touch is on the touch caret. Set touch caret invisible, and bypass
913 if (mActiveTouchId
== -1) {
914 SetVisibility(false);
915 status
= nsEventStatus_eIgnore
;
920 case TOUCHCARET_MOUSEDRAG_ACTIVE
:
921 case TOUCHCARET_TOUCHDRAG_ACTIVE
:
922 case TOUCHCARET_TOUCHDRAG_INACTIVE
:
923 // Consume NS_TOUCH_START event.
924 status
= nsEventStatus_eConsumeNoDefault
;
928 // Cache active touch IDs in TOUCHDRAG states.
929 if (mState
== TOUCHCARET_TOUCHDRAG_ACTIVE
||
930 mState
== TOUCHCARET_TOUCHDRAG_INACTIVE
) {
932 for (size_t i
= 0; i
< aEvent
->touches
.Length(); i
++) {
933 mTouchesId
.AppendElement(aEvent
->touches
[i
]->mIdentifier
);
941 TouchCaret::DispatchTapEvent()
943 nsCOMPtr
<nsIPresShell
> presShell
= do_QueryReferent(mPresShell
);
948 nsRefPtr
<nsCaret
> caret
= presShell
->GetCaret();
953 dom::Selection
* sel
= static_cast<dom::Selection
*>(caret
->GetSelection());
958 nsIDocument
* doc
= presShell
->GetDocument();
962 dom::SelectionStateChangedEventInit init
;
963 init
.mBubbles
= true;
965 // XXX: Do we need to flush layout?
966 presShell
->FlushPendingNotifications(Flush_Layout
);
967 nsRect rect
= nsContentUtils::GetSelectionBoundingRect(sel
);
968 nsRefPtr
<dom::DOMRect
>domRect
= new dom::DOMRect(ToSupports(doc
));
970 domRect
->SetLayoutRect(rect
);
971 init
.mBoundingClientRect
= domRect
;
972 init
.mVisible
= false;
974 sel
->Stringify(init
.mSelectedText
);
976 dom::Sequence
<dom::SelectionState
> state
;
977 state
.AppendElement(dom::SelectionState::Taponcaret
);
978 init
.mStates
= state
;
980 nsRefPtr
<dom::SelectionStateChangedEvent
> event
=
981 dom::SelectionStateChangedEvent::Constructor(doc
, NS_LITERAL_STRING("mozselectionstatechanged"), init
);
983 event
->SetTrusted(true);
984 event
->GetInternalNSEvent()->mFlags
.mOnlyChromeDispatch
= true;
986 doc
->DispatchEvent(event
, &ret
);
990 TouchCaret::SetState(TouchCaretState aState
)
992 TOUCHCARET_LOG("state changed from %d to %d", mState
, aState
);
993 if (mState
== TOUCHCARET_NONE
) {
994 MOZ_ASSERT(aState
!= TOUCHCARET_TOUCHDRAG_INACTIVE
,
995 "mState: NONE => TOUCHDRAG_INACTIVE isn't allowed!");
998 if (mState
== TOUCHCARET_TOUCHDRAG_ACTIVE
) {
999 MOZ_ASSERT(aState
!= TOUCHCARET_MOUSEDRAG_ACTIVE
,
1000 "mState: TOUCHDRAG_ACTIVE => MOUSEDRAG_ACTIVE isn't allowed!");
1003 if (mState
== TOUCHCARET_MOUSEDRAG_ACTIVE
) {
1004 MOZ_ASSERT(aState
== TOUCHCARET_MOUSEDRAG_ACTIVE
||
1005 aState
== TOUCHCARET_NONE
,
1006 "MOUSEDRAG_ACTIVE allowed next state: NONE!");
1009 if (mState
== TOUCHCARET_TOUCHDRAG_INACTIVE
) {
1010 MOZ_ASSERT(aState
== TOUCHCARET_TOUCHDRAG_INACTIVE
||
1011 aState
== TOUCHCARET_NONE
,
1012 "TOUCHDRAG_INACTIVE allowed next state: NONE!");
1017 if (mState
== TOUCHCARET_NONE
) {
1018 mActiveTouchId
= -1;
1019 mCaretCenterToDownPointOffsetY
= 0;
1022 mIsValidTap
= false;
1024 } else if (mState
== TOUCHCARET_TOUCHDRAG_ACTIVE
||
1025 mState
== TOUCHCARET_MOUSEDRAG_ACTIVE
) {