LCOV - code coverage report
Current view: top level - layout/base - TouchManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 26 176 14.8 %
Date: 2017-07-14 16:53:18 Functions: 6 13 46.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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/.
       6             :  */
       7             : 
       8             : #include "TouchManager.h"
       9             : 
      10             : #include "mozilla/dom/EventTarget.h"
      11             : #include "mozilla/PresShell.h"
      12             : #include "nsIFrame.h"
      13             : #include "nsView.h"
      14             : 
      15             : using namespace mozilla::dom;
      16             : 
      17             : namespace mozilla {
      18             : 
      19             : nsDataHashtable<nsUint32HashKey, TouchManager::TouchInfo>* TouchManager::sCaptureTouchList;
      20             : 
      21             : /*static*/ void
      22           3 : TouchManager::InitializeStatics()
      23             : {
      24           3 :   NS_ASSERTION(!sCaptureTouchList, "InitializeStatics called multiple times!");
      25           3 :   sCaptureTouchList = new nsDataHashtable<nsUint32HashKey, TouchManager::TouchInfo>;
      26           3 : }
      27             : 
      28             : /*static*/ void
      29           0 : TouchManager::ReleaseStatics()
      30             : {
      31           0 :   NS_ASSERTION(sCaptureTouchList, "ReleaseStatics called without Initialize!");
      32           0 :   delete sCaptureTouchList;
      33           0 :   sCaptureTouchList = nullptr;
      34           0 : }
      35             : 
      36             : void
      37          28 : TouchManager::Init(PresShell* aPresShell, nsIDocument* aDocument)
      38             : {
      39          28 :   mPresShell = aPresShell;
      40          28 :   mDocument = aDocument;
      41          28 : }
      42             : 
      43             : void
      44           4 : TouchManager::Destroy()
      45             : {
      46           4 :   EvictTouches();
      47           4 :   mDocument = nullptr;
      48           4 :   mPresShell = nullptr;
      49           4 : }
      50             : 
      51             : static nsIContent*
      52           0 : GetNonAnonymousAncestor(EventTarget* aTarget)
      53             : {
      54           0 :   nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget));
      55           0 :   if (content && content->IsInNativeAnonymousSubtree()) {
      56           0 :     content = content->FindFirstNonChromeOnlyAccessContent();
      57             :   }
      58           0 :   return content;
      59             : }
      60             : 
      61             : /*static*/ void
      62           0 : TouchManager::EvictTouchPoint(RefPtr<Touch>& aTouch,
      63             :                               nsIDocument* aLimitToDocument)
      64             : {
      65           0 :   nsCOMPtr<nsINode> node(do_QueryInterface(aTouch->mTarget));
      66           0 :   if (node) {
      67           0 :     nsIDocument* doc = node->GetUncomposedDoc();
      68           0 :     if (doc && (!aLimitToDocument || aLimitToDocument == doc)) {
      69           0 :       nsIPresShell* presShell = doc->GetShell();
      70           0 :       if (presShell) {
      71           0 :         nsIFrame* frame = presShell->GetRootFrame();
      72           0 :         if (frame) {
      73           0 :           nsPoint pt(aTouch->mRefPoint.x, aTouch->mRefPoint.y);
      74           0 :           nsCOMPtr<nsIWidget> widget = frame->GetView()->GetNearestWidget(&pt);
      75           0 :           if (widget) {
      76           0 :             WidgetTouchEvent event(true, eTouchEnd, widget);
      77           0 :             event.mTime = PR_IntervalNow();
      78           0 :             event.mTouches.AppendElement(aTouch);
      79             :             nsEventStatus status;
      80           0 :             widget->DispatchEvent(&event, status);
      81             :           }
      82             :         }
      83             :       }
      84             :     }
      85             :   }
      86           0 :   if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) {
      87           0 :     sCaptureTouchList->Remove(aTouch->Identifier());
      88             :   }
      89           0 : }
      90             : 
      91             : /*static*/ void
      92           4 : TouchManager::AppendToTouchList(WidgetTouchEvent::TouchArray* aTouchList)
      93             : {
      94           8 :   for (auto iter = sCaptureTouchList->Iter();
      95           4 :        !iter.Done();
      96           0 :        iter.Next()) {
      97           0 :     RefPtr<Touch>& touch = iter.Data().mTouch;
      98           0 :     touch->mChanged = false;
      99           0 :     aTouchList->AppendElement(touch);
     100             :   }
     101           4 : }
     102             : 
     103             : void
     104           4 : TouchManager::EvictTouches()
     105             : {
     106           8 :   WidgetTouchEvent::AutoTouchArray touches;
     107           4 :   AppendToTouchList(&touches);
     108           4 :   for (uint32_t i = 0; i < touches.Length(); ++i) {
     109           0 :     EvictTouchPoint(touches[i], mDocument);
     110             :   }
     111           4 : }
     112             : 
     113             : bool
     114          10 : TouchManager::PreHandleEvent(WidgetEvent* aEvent,
     115             :                              nsEventStatus* aStatus,
     116             :                              bool& aTouchIsNew,
     117             :                              bool& aIsHandlingUserInput,
     118             :                              nsCOMPtr<nsIContent>& aCurrentEventContent)
     119             : {
     120          10 :   switch (aEvent->mMessage) {
     121             :     case eTouchStart: {
     122           0 :       aIsHandlingUserInput = true;
     123           0 :       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
     124             :       // if there is only one touch in this touchstart event, assume that it is
     125             :       // the start of a new touch session and evict any old touches in the
     126             :       // queue
     127           0 :       if (touchEvent->mTouches.Length() == 1) {
     128           0 :         WidgetTouchEvent::AutoTouchArray touches;
     129           0 :         AppendToTouchList(&touches);
     130           0 :         for (uint32_t i = 0; i < touches.Length(); ++i) {
     131           0 :           EvictTouchPoint(touches[i]);
     132             :         }
     133             :       }
     134             :       // Add any new touches to the queue
     135           0 :       for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
     136           0 :         Touch* touch = touchEvent->mTouches[i];
     137           0 :         int32_t id = touch->Identifier();
     138           0 :         if (!sCaptureTouchList->Get(id, nullptr)) {
     139             :           // If it is not already in the queue, it is a new touch
     140           0 :           touch->mChanged = true;
     141             :         }
     142           0 :         touch->mMessage = aEvent->mMessage;
     143             :         TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget),
     144           0 :                            true };
     145           0 :         sCaptureTouchList->Put(id, info);
     146             :       }
     147           0 :       break;
     148             :     }
     149             :     case eTouchMove: {
     150             :       // Check for touches that changed. Mark them add to queue
     151           0 :       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
     152           0 :       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
     153           0 :       bool haveChanged = false;
     154           0 :       for (int32_t i = touches.Length(); i; ) {
     155           0 :         --i;
     156           0 :         Touch* touch = touches[i];
     157           0 :         if (!touch) {
     158           0 :           continue;
     159             :         }
     160           0 :         int32_t id = touch->Identifier();
     161           0 :         touch->mMessage = aEvent->mMessage;
     162             : 
     163           0 :         TouchInfo info;
     164           0 :         if (!sCaptureTouchList->Get(id, &info)) {
     165           0 :           touches.RemoveElementAt(i);
     166           0 :           continue;
     167             :         }
     168           0 :         RefPtr<Touch> oldTouch = info.mTouch;
     169           0 :         if (!touch->Equals(oldTouch)) {
     170           0 :           touch->mChanged = true;
     171           0 :           haveChanged = true;
     172             :         }
     173             : 
     174           0 :         nsCOMPtr<EventTarget> targetPtr = oldTouch->mTarget;
     175           0 :         if (!targetPtr) {
     176           0 :           touches.RemoveElementAt(i);
     177           0 :           continue;
     178             :         }
     179           0 :         nsCOMPtr<nsINode> targetNode(do_QueryInterface(targetPtr));
     180           0 :         if (!targetNode->IsInComposedDoc()) {
     181           0 :           targetPtr = do_QueryInterface(info.mNonAnonymousTarget);
     182             :         }
     183           0 :         touch->SetTarget(targetPtr);
     184             : 
     185           0 :         info.mTouch = touch;
     186             :         // info.mNonAnonymousTarget is still valid from above
     187           0 :         sCaptureTouchList->Put(id, info);
     188             :         // if we're moving from touchstart to touchmove for this touch
     189             :         // we allow preventDefault to prevent mouse events
     190           0 :         if (oldTouch->mMessage != touch->mMessage) {
     191           0 :           aTouchIsNew = true;
     192             :         }
     193             :       }
     194             :       // is nothing has changed, we should just return
     195           0 :       if (!haveChanged) {
     196           0 :         if (aTouchIsNew) {
     197             :           // however, if this is the first touchmove after a touchstart,
     198             :           // it is special in that preventDefault is allowed on it, so
     199             :           // we must dispatch it to content even if nothing changed. we
     200             :           // arbitrarily pick the first touch point to be the "changed"
     201             :           // touch because firing an event with no changed events doesn't
     202             :           // work.
     203           0 :           for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
     204           0 :             if (touchEvent->mTouches[i]) {
     205           0 :               touchEvent->mTouches[i]->mChanged = true;
     206           0 :               break;
     207             :             }
     208             :           }
     209             :         } else {
     210           0 :           return false;
     211             :         }
     212             :       }
     213           0 :       break;
     214             :     }
     215             :     case eTouchEnd:
     216           0 :       aIsHandlingUserInput = true;
     217             :       // Fall through to touchcancel code
     218             :       MOZ_FALLTHROUGH;
     219             :     case eTouchCancel: {
     220             :       // Remove the changed touches
     221             :       // need to make sure we only remove touches that are ending here
     222           0 :       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
     223           0 :       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
     224           0 :       for (uint32_t i = 0; i < touches.Length(); ++i) {
     225           0 :         Touch* touch = touches[i];
     226           0 :         if (!touch) {
     227           0 :           continue;
     228             :         }
     229           0 :         touch->mMessage = aEvent->mMessage;
     230           0 :         touch->mChanged = true;
     231             : 
     232           0 :         int32_t id = touch->Identifier();
     233           0 :         TouchInfo info;
     234           0 :         if (!sCaptureTouchList->Get(id, &info)) {
     235           0 :           continue;
     236             :         }
     237           0 :         nsCOMPtr<EventTarget> targetPtr = info.mTouch->mTarget;
     238           0 :         nsCOMPtr<nsINode> targetNode(do_QueryInterface(targetPtr));
     239           0 :         if (targetNode && !targetNode->IsInComposedDoc()) {
     240           0 :           targetPtr = do_QueryInterface(info.mNonAnonymousTarget);
     241             :         }
     242             : 
     243           0 :         aCurrentEventContent = do_QueryInterface(targetPtr);
     244           0 :         touch->SetTarget(targetPtr);
     245           0 :         sCaptureTouchList->Remove(id);
     246             :       }
     247             :       // add any touches left in the touch list, but ensure changed=false
     248           0 :       AppendToTouchList(&touches);
     249           0 :       break;
     250             :     }
     251             :     case eTouchPointerCancel: {
     252             :       // Don't generate pointer events by touch events after eTouchPointerCancel
     253             :       // is received.
     254           0 :       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
     255           0 :       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
     256           0 :       for (uint32_t i = 0; i < touches.Length(); ++i) {
     257           0 :         Touch* touch = touches[i];
     258           0 :         if (!touch) {
     259           0 :           continue;
     260             :         }
     261           0 :         int32_t id = touch->Identifier();
     262           0 :         TouchInfo info;
     263           0 :         if (!sCaptureTouchList->Get(id, &info)) {
     264           0 :           continue;
     265             :         }
     266           0 :         info.mConvertToPointer = false;
     267           0 :         sCaptureTouchList->Put(id, info);
     268             :       }
     269           0 :       break;
     270             :     }
     271             :     default:
     272          10 :       break;
     273             :   }
     274          10 :   return true;
     275             : }
     276             : 
     277             : /*static*/ already_AddRefed<nsIContent>
     278           0 : TouchManager::GetAnyCapturedTouchTarget()
     279             : {
     280           0 :   nsCOMPtr<nsIContent> result = nullptr;
     281           0 :   if (sCaptureTouchList->Count() == 0) {
     282           0 :     return result.forget();
     283             :   }
     284           0 :   for (auto iter = sCaptureTouchList->Iter(); !iter.Done(); iter.Next()) {
     285           0 :     RefPtr<Touch>& touch = iter.Data().mTouch;
     286           0 :     if (touch) {
     287           0 :       EventTarget* target = touch->GetTarget();
     288           0 :       if (target) {
     289           0 :         result = do_QueryInterface(target);
     290           0 :         break;
     291             :       }
     292             :     }
     293             :   }
     294           0 :   return result.forget();
     295             : }
     296             : 
     297             : /*static*/ bool
     298           0 : TouchManager::HasCapturedTouch(int32_t aId)
     299             : {
     300           0 :   return sCaptureTouchList->Contains(aId);
     301             : }
     302             : 
     303             : /*static*/ already_AddRefed<Touch>
     304           0 : TouchManager::GetCapturedTouch(int32_t aId)
     305             : {
     306           0 :   RefPtr<Touch> touch;
     307           0 :   TouchInfo info;
     308           0 :   if (sCaptureTouchList->Get(aId, &info)) {
     309           0 :     touch = info.mTouch;
     310             :   }
     311           0 :   return touch.forget();
     312             : }
     313             : 
     314             : /*static*/ bool
     315           0 : TouchManager::ShouldConvertTouchToPointer(const Touch* aTouch,
     316             :                                           const WidgetTouchEvent* aEvent)
     317             : {
     318           0 :   if (!aTouch || !aTouch->convertToPointer) {
     319           0 :     return false;
     320             :   }
     321           0 :   TouchInfo info;
     322           0 :   if (!sCaptureTouchList->Get(aTouch->Identifier(), &info)) {
     323             :     // This check runs before the TouchManager has the touch registered in its
     324             :     // touch list. It's because we dispatching pointer events before handling
     325             :     // touch events. So we convert eTouchStart to pointerdown even it's not
     326             :     // registered.
     327             :     // Check WidgetTouchEvent::mMessage because Touch::mMessage is assigned when
     328             :     // pre-handling touch events.
     329           0 :     return aEvent->mMessage == eTouchStart;
     330             :   }
     331           0 :   return info.mConvertToPointer;
     332             : }
     333             : 
     334             : } // namespace mozilla

Generated by: LCOV version 1.13