summaryrefslogtreecommitdiffstats
path: root/layout/base/TouchManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/TouchManager.cpp')
-rw-r--r--layout/base/TouchManager.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp
new file mode 100644
index 000000000..5167ca588
--- /dev/null
+++ b/layout/base/TouchManager.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=78:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "TouchManager.h"
+
+#include "mozilla/dom/EventTarget.h"
+#include "nsIFrame.h"
+#include "nsPresShell.h"
+#include "nsView.h"
+
+namespace mozilla {
+
+nsDataHashtable<nsUint32HashKey, TouchManager::TouchInfo>* TouchManager::sCaptureTouchList;
+
+/*static*/ void
+TouchManager::InitializeStatics()
+{
+ NS_ASSERTION(!sCaptureTouchList, "InitializeStatics called multiple times!");
+ sCaptureTouchList = new nsDataHashtable<nsUint32HashKey, TouchManager::TouchInfo>;
+}
+
+/*static*/ void
+TouchManager::ReleaseStatics()
+{
+ NS_ASSERTION(sCaptureTouchList, "ReleaseStatics called without Initialize!");
+ delete sCaptureTouchList;
+ sCaptureTouchList = nullptr;
+}
+
+void
+TouchManager::Init(PresShell* aPresShell, nsIDocument* aDocument)
+{
+ mPresShell = aPresShell;
+ mDocument = aDocument;
+}
+
+void
+TouchManager::Destroy()
+{
+ EvictTouches();
+ mDocument = nullptr;
+ mPresShell = nullptr;
+}
+
+static nsIContent*
+GetNonAnonymousAncestor(dom::EventTarget* aTarget)
+{
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget));
+ if (content && content->IsInNativeAnonymousSubtree()) {
+ content = content->FindFirstNonChromeOnlyAccessContent();
+ }
+ return content;
+}
+
+/*static*/ void
+TouchManager::EvictTouchPoint(RefPtr<dom::Touch>& aTouch,
+ nsIDocument* aLimitToDocument)
+{
+ nsCOMPtr<nsINode> node(do_QueryInterface(aTouch->mTarget));
+ if (node) {
+ nsIDocument* doc = node->GetUncomposedDoc();
+ if (doc && (!aLimitToDocument || aLimitToDocument == doc)) {
+ nsIPresShell* presShell = doc->GetShell();
+ if (presShell) {
+ nsIFrame* frame = presShell->GetRootFrame();
+ if (frame) {
+ nsPoint pt(aTouch->mRefPoint.x, aTouch->mRefPoint.y);
+ nsCOMPtr<nsIWidget> widget = frame->GetView()->GetNearestWidget(&pt);
+ if (widget) {
+ WidgetTouchEvent event(true, eTouchEnd, widget);
+ event.mTime = PR_IntervalNow();
+ event.mTouches.AppendElement(aTouch);
+ nsEventStatus status;
+ widget->DispatchEvent(&event, status);
+ }
+ }
+ }
+ }
+ }
+ if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) {
+ sCaptureTouchList->Remove(aTouch->Identifier());
+ }
+}
+
+/*static*/ void
+TouchManager::AppendToTouchList(WidgetTouchEvent::TouchArray* aTouchList)
+{
+ for (auto iter = sCaptureTouchList->Iter();
+ !iter.Done();
+ iter.Next()) {
+ RefPtr<dom::Touch>& touch = iter.Data().mTouch;
+ touch->mChanged = false;
+ aTouchList->AppendElement(touch);
+ }
+}
+
+void
+TouchManager::EvictTouches()
+{
+ WidgetTouchEvent::AutoTouchArray touches;
+ AppendToTouchList(&touches);
+ for (uint32_t i = 0; i < touches.Length(); ++i) {
+ EvictTouchPoint(touches[i], mDocument);
+ }
+}
+
+bool
+TouchManager::PreHandleEvent(WidgetEvent* aEvent,
+ nsEventStatus* aStatus,
+ bool& aTouchIsNew,
+ bool& aIsHandlingUserInput,
+ nsCOMPtr<nsIContent>& aCurrentEventContent)
+{
+ switch (aEvent->mMessage) {
+ case eTouchStart: {
+ aIsHandlingUserInput = true;
+ WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+ // if there is only one touch in this touchstart event, assume that it is
+ // the start of a new touch session and evict any old touches in the
+ // queue
+ if (touchEvent->mTouches.Length() == 1) {
+ WidgetTouchEvent::AutoTouchArray touches;
+ AppendToTouchList(&touches);
+ for (uint32_t i = 0; i < touches.Length(); ++i) {
+ EvictTouchPoint(touches[i]);
+ }
+ }
+ // Add any new touches to the queue
+ for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
+ dom::Touch* touch = touchEvent->mTouches[i];
+ int32_t id = touch->Identifier();
+ if (!sCaptureTouchList->Get(id, nullptr)) {
+ // If it is not already in the queue, it is a new touch
+ touch->mChanged = true;
+ }
+ touch->mMessage = aEvent->mMessage;
+ TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget) };
+ sCaptureTouchList->Put(id, info);
+ }
+ break;
+ }
+ case eTouchMove: {
+ // Check for touches that changed. Mark them add to queue
+ WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+ WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
+ bool haveChanged = false;
+ for (int32_t i = touches.Length(); i; ) {
+ --i;
+ dom::Touch* touch = touches[i];
+ if (!touch) {
+ continue;
+ }
+ int32_t id = touch->Identifier();
+ touch->mMessage = aEvent->mMessage;
+
+ TouchInfo info;
+ if (!sCaptureTouchList->Get(id, &info)) {
+ touches.RemoveElementAt(i);
+ continue;
+ }
+ RefPtr<dom::Touch> oldTouch = info.mTouch;
+ if (!touch->Equals(oldTouch)) {
+ touch->mChanged = true;
+ haveChanged = true;
+ }
+
+ nsCOMPtr<dom::EventTarget> targetPtr = oldTouch->mTarget;
+ if (!targetPtr) {
+ touches.RemoveElementAt(i);
+ continue;
+ }
+ nsCOMPtr<nsINode> targetNode(do_QueryInterface(targetPtr));
+ if (!targetNode->IsInComposedDoc()) {
+ targetPtr = do_QueryInterface(info.mNonAnonymousTarget);
+ }
+ touch->SetTarget(targetPtr);
+
+ info.mTouch = touch;
+ // info.mNonAnonymousTarget is still valid from above
+ sCaptureTouchList->Put(id, info);
+ // if we're moving from touchstart to touchmove for this touch
+ // we allow preventDefault to prevent mouse events
+ if (oldTouch->mMessage != touch->mMessage) {
+ aTouchIsNew = true;
+ }
+ }
+ // is nothing has changed, we should just return
+ if (!haveChanged) {
+ if (aTouchIsNew) {
+ // however, if this is the first touchmove after a touchstart,
+ // it is special in that preventDefault is allowed on it, so
+ // we must dispatch it to content even if nothing changed. we
+ // arbitrarily pick the first touch point to be the "changed"
+ // touch because firing an event with no changed events doesn't
+ // work.
+ for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
+ if (touchEvent->mTouches[i]) {
+ touchEvent->mTouches[i]->mChanged = true;
+ break;
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+ break;
+ }
+ case eTouchEnd:
+ aIsHandlingUserInput = true;
+ // Fall through to touchcancel code
+ MOZ_FALLTHROUGH;
+ case eTouchCancel: {
+ // Remove the changed touches
+ // need to make sure we only remove touches that are ending here
+ WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+ WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
+ for (uint32_t i = 0; i < touches.Length(); ++i) {
+ dom::Touch* touch = touches[i];
+ if (!touch) {
+ continue;
+ }
+ touch->mMessage = aEvent->mMessage;
+ touch->mChanged = true;
+
+ int32_t id = touch->Identifier();
+ TouchInfo info;
+ if (!sCaptureTouchList->Get(id, &info)) {
+ continue;
+ }
+ nsCOMPtr<EventTarget> targetPtr = info.mTouch->mTarget;
+ nsCOMPtr<nsINode> targetNode(do_QueryInterface(targetPtr));
+ if (targetNode && !targetNode->IsInComposedDoc()) {
+ targetPtr = do_QueryInterface(info.mNonAnonymousTarget);
+ }
+
+ aCurrentEventContent = do_QueryInterface(targetPtr);
+ touch->SetTarget(targetPtr);
+ sCaptureTouchList->Remove(id);
+ }
+ // add any touches left in the touch list, but ensure changed=false
+ AppendToTouchList(&touches);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+}
+
+/*static*/ already_AddRefed<nsIContent>
+TouchManager::GetAnyCapturedTouchTarget()
+{
+ nsCOMPtr<nsIContent> result = nullptr;
+ if (sCaptureTouchList->Count() == 0) {
+ return result.forget();
+ }
+ for (auto iter = sCaptureTouchList->Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<dom::Touch>& touch = iter.Data().mTouch;
+ if (touch) {
+ dom::EventTarget* target = touch->GetTarget();
+ if (target) {
+ result = do_QueryInterface(target);
+ break;
+ }
+ }
+ }
+ return result.forget();
+}
+
+/*static*/ bool
+TouchManager::HasCapturedTouch(int32_t aId)
+{
+ return sCaptureTouchList->Contains(aId);
+}
+
+/*static*/ already_AddRefed<dom::Touch>
+TouchManager::GetCapturedTouch(int32_t aId)
+{
+ RefPtr<dom::Touch> touch;
+ TouchInfo info;
+ if (sCaptureTouchList->Get(aId, &info)) {
+ touch = info.mTouch;
+ }
+ return touch.forget();
+}
+
+} // namespace mozilla