Bug 1133492 - Extract some of nsPresShell into a separate TouchManager class. r=smaug
This commit is contained in:
211
layout/base/TouchManager.cpp
Normal file
211
layout/base/TouchManager.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/* -*- 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 "nsPresShell.h"
|
||||
|
||||
void
|
||||
TouchManager::Init(PresShell* aPresShell, nsIDocument* aDocument)
|
||||
{
|
||||
mPresShell = aPresShell;
|
||||
mDocument = aDocument;
|
||||
}
|
||||
|
||||
void
|
||||
TouchManager::Destroy()
|
||||
{
|
||||
EvictTouches();
|
||||
mDocument = nullptr;
|
||||
mPresShell = nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
EvictTouchPoint(nsRefPtr<dom::Touch>& aTouch,
|
||||
nsIDocument* aLimitToDocument = nullptr)
|
||||
{
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aTouch->mTarget));
|
||||
if (node) {
|
||||
nsIDocument* doc = node->GetCurrentDoc();
|
||||
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, NS_TOUCH_END, widget);
|
||||
event.widget = widget;
|
||||
event.time = PR_IntervalNow();
|
||||
event.touches.AppendElement(aTouch);
|
||||
nsEventStatus status;
|
||||
widget->DispatchEvent(&event, status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) {
|
||||
// We couldn't dispatch touchend. Remove the touch from gCaptureTouchList
|
||||
// explicitly.
|
||||
nsIPresShell::gCaptureTouchList->Remove(aTouch->Identifier());
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
AppendToTouchList(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData, void *aTouchList)
|
||||
{
|
||||
WidgetTouchEvent::TouchArray* touches =
|
||||
static_cast<WidgetTouchEvent::TouchArray*>(aTouchList);
|
||||
aData->mChanged = false;
|
||||
touches->AppendElement(aData);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
TouchManager::EvictTouches()
|
||||
{
|
||||
WidgetTouchEvent::AutoTouchArray touches;
|
||||
PresShell::gCaptureTouchList->Enumerate(&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->message) {
|
||||
case NS_TOUCH_START: {
|
||||
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->touches.Length() == 1) {
|
||||
WidgetTouchEvent::AutoTouchArray touches;
|
||||
PresShell::gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&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->touches.Length(); ++i) {
|
||||
dom::Touch* touch = touchEvent->touches[i];
|
||||
int32_t id = touch->Identifier();
|
||||
if (!PresShell::gCaptureTouchList->Get(id, nullptr)) {
|
||||
// If it is not already in the queue, it is a new touch
|
||||
touch->mChanged = true;
|
||||
}
|
||||
touch->mMessage = aEvent->message;
|
||||
PresShell::gCaptureTouchList->Put(id, touch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NS_TOUCH_MOVE: {
|
||||
// Check for touches that changed. Mark them add to queue
|
||||
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
||||
WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
|
||||
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->message;
|
||||
|
||||
nsRefPtr<dom::Touch> oldTouch = PresShell::gCaptureTouchList->GetWeak(id);
|
||||
if (!oldTouch) {
|
||||
touches.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
if (!touch->Equals(oldTouch)) {
|
||||
touch->mChanged = true;
|
||||
haveChanged = true;
|
||||
}
|
||||
|
||||
nsCOMPtr<dom::EventTarget> targetPtr = oldTouch->mTarget;
|
||||
if (!targetPtr) {
|
||||
touches.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
touch->SetTarget(targetPtr);
|
||||
|
||||
PresShell::gCaptureTouchList->Put(id, touch);
|
||||
// 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->touches.Length(); ++i) {
|
||||
if (touchEvent->touches[i]) {
|
||||
touchEvent->touches[i]->mChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (PresShell::gPreventMouseEvents) {
|
||||
*aStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NS_TOUCH_END:
|
||||
aIsHandlingUserInput = true;
|
||||
// Fall through to touchcancel code
|
||||
case NS_TOUCH_CANCEL: {
|
||||
// Remove the changed touches
|
||||
// need to make sure we only remove touches that are ending here
|
||||
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
||||
WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
|
||||
for (uint32_t i = 0; i < touches.Length(); ++i) {
|
||||
dom::Touch* touch = touches[i];
|
||||
if (!touch) {
|
||||
continue;
|
||||
}
|
||||
touch->mMessage = aEvent->message;
|
||||
touch->mChanged = true;
|
||||
|
||||
int32_t id = touch->Identifier();
|
||||
nsRefPtr<dom::Touch> oldTouch = PresShell::gCaptureTouchList->GetWeak(id);
|
||||
if (!oldTouch) {
|
||||
continue;
|
||||
}
|
||||
nsCOMPtr<EventTarget> targetPtr = oldTouch->mTarget;
|
||||
|
||||
aCurrentEventContent = do_QueryInterface(targetPtr);
|
||||
touch->SetTarget(targetPtr);
|
||||
PresShell::gCaptureTouchList->Remove(id);
|
||||
}
|
||||
// add any touches left in the touch list, but ensure changed=false
|
||||
PresShell::gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
36
layout/base/TouchManager.h
Normal file
36
layout/base/TouchManager.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* -*- 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/.
|
||||
*/
|
||||
|
||||
/* Description of TouchManager class.
|
||||
* Incapsulate code related with work of touch events.
|
||||
*/
|
||||
|
||||
#ifndef TouchManager_h_
|
||||
#define TouchManager_h_
|
||||
|
||||
class PresShell;
|
||||
class nsIDocument;
|
||||
|
||||
class TouchManager {
|
||||
public:
|
||||
void Init(PresShell* aPresShell, nsIDocument* aDocument);
|
||||
void Destroy();
|
||||
|
||||
bool PreHandleEvent(mozilla::WidgetEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
bool& aTouchIsNew,
|
||||
bool& aIsHandlingUserInput,
|
||||
nsCOMPtr<nsIContent>& aCurrentEventContent);
|
||||
|
||||
private:
|
||||
void EvictTouches();
|
||||
|
||||
nsRefPtr<PresShell> mPresShell;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
};
|
||||
|
||||
#endif /* !defined(TouchManager_h_) */
|
||||
@@ -99,6 +99,7 @@ UNIFIED_SOURCES += [
|
||||
'SelectionCarets.cpp',
|
||||
'StackArena.cpp',
|
||||
'TouchCaret.cpp',
|
||||
'TouchManager.cpp',
|
||||
]
|
||||
|
||||
# nsPresArena.cpp needs to be built separately because it uses plarena.h.
|
||||
|
||||
@@ -975,6 +975,8 @@ PresShell::Init(nsIDocument* aDocument,
|
||||
|
||||
// Setup our font inflation preferences.
|
||||
SetupFontInflation();
|
||||
|
||||
mTouchManager.Init(this, mDocument);
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
@@ -1277,7 +1279,7 @@ PresShell::Destroy()
|
||||
|
||||
mHaveShutDown = true;
|
||||
|
||||
EvictTouches();
|
||||
mTouchManager.Destroy();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -6804,60 +6806,6 @@ PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
EvictTouchPoint(nsRefPtr<dom::Touch>& aTouch,
|
||||
nsIDocument* aLimitToDocument = nullptr)
|
||||
{
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aTouch->mTarget));
|
||||
if (node) {
|
||||
nsIDocument* doc = node->GetCurrentDoc();
|
||||
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, NS_TOUCH_END, widget);
|
||||
event.widget = widget;
|
||||
event.time = PR_IntervalNow();
|
||||
event.touches.AppendElement(aTouch);
|
||||
nsEventStatus status;
|
||||
widget->DispatchEvent(&event, status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) {
|
||||
// We couldn't dispatch touchend. Remove the touch from gCaptureTouchList
|
||||
// explicitly.
|
||||
nsIPresShell::gCaptureTouchList->Remove(aTouch->Identifier());
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
AppendToTouchList(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData, void *aTouchList)
|
||||
{
|
||||
WidgetTouchEvent::TouchArray* touches =
|
||||
static_cast<WidgetTouchEvent::TouchArray*>(aTouchList);
|
||||
aData->mChanged = false;
|
||||
touches->AppendElement(aData);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::EvictTouches()
|
||||
{
|
||||
WidgetTouchEvent::AutoTouchArray touches;
|
||||
gCaptureTouchList->Enumerate(&AppendToTouchList, &touches);
|
||||
for (uint32_t i = 0; i < touches.Length(); ++i) {
|
||||
EvictTouchPoint(touches[i], mDocument);
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
FindAnyTarget(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData,
|
||||
void* aAnyTarget)
|
||||
@@ -8068,125 +8016,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
|
||||
case NS_MOUSE_BUTTON_UP:
|
||||
isHandlingUserInput = true;
|
||||
break;
|
||||
case NS_TOUCH_START: {
|
||||
isHandlingUserInput = 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->touches.Length() == 1) {
|
||||
WidgetTouchEvent::AutoTouchArray touches;
|
||||
gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&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->touches.Length(); ++i) {
|
||||
dom::Touch* touch = touchEvent->touches[i];
|
||||
int32_t id = touch->Identifier();
|
||||
if (!gCaptureTouchList->Get(id, nullptr)) {
|
||||
// If it is not already in the queue, it is a new touch
|
||||
touch->mChanged = true;
|
||||
}
|
||||
touch->mMessage = aEvent->message;
|
||||
gCaptureTouchList->Put(id, touch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NS_TOUCH_END:
|
||||
isHandlingUserInput = true;
|
||||
// Fall through to touchcancel code
|
||||
case NS_TOUCH_CANCEL: {
|
||||
// Remove the changed touches
|
||||
// need to make sure we only remove touches that are ending here
|
||||
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
||||
WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
|
||||
for (uint32_t i = 0; i < touches.Length(); ++i) {
|
||||
dom::Touch* touch = touches[i];
|
||||
if (!touch) {
|
||||
continue;
|
||||
}
|
||||
touch->mMessage = aEvent->message;
|
||||
touch->mChanged = true;
|
||||
|
||||
int32_t id = touch->Identifier();
|
||||
nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
|
||||
if (!oldTouch) {
|
||||
continue;
|
||||
}
|
||||
nsCOMPtr<EventTarget> targetPtr = oldTouch->mTarget;
|
||||
|
||||
mCurrentEventContent = do_QueryInterface(targetPtr);
|
||||
touch->SetTarget(targetPtr);
|
||||
gCaptureTouchList->Remove(id);
|
||||
}
|
||||
// add any touches left in the touch list, but ensure changed=false
|
||||
gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches);
|
||||
break;
|
||||
}
|
||||
case NS_TOUCH_MOVE: {
|
||||
// Check for touches that changed. Mark them add to queue
|
||||
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
||||
WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
|
||||
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->message;
|
||||
|
||||
nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
|
||||
if (!oldTouch) {
|
||||
touches.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
if (!touch->Equals(oldTouch)) {
|
||||
touch->mChanged = true;
|
||||
haveChanged = true;
|
||||
}
|
||||
|
||||
nsCOMPtr<dom::EventTarget> targetPtr = oldTouch->mTarget;
|
||||
if (!targetPtr) {
|
||||
touches.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
touch->SetTarget(targetPtr);
|
||||
|
||||
gCaptureTouchList->Put(id, touch);
|
||||
// if we're moving from touchstart to touchmove for this touch
|
||||
// we allow preventDefault to prevent mouse events
|
||||
if (oldTouch->mMessage != touch->mMessage) {
|
||||
touchIsNew = true;
|
||||
}
|
||||
}
|
||||
// is nothing has changed, we should just return
|
||||
if (!haveChanged) {
|
||||
if (touchIsNew) {
|
||||
// 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->touches.Length(); ++i) {
|
||||
if (touchEvent->touches[i]) {
|
||||
touchEvent->touches[i]->mChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (gPreventMouseEvents) {
|
||||
*aStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NS_DRAGDROP_DROP:
|
||||
nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
|
||||
if (session) {
|
||||
@@ -8198,6 +8028,12 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mTouchManager.PreHandleEvent(aEvent, aStatus,
|
||||
touchIsNew, isHandlingUserInput,
|
||||
mCurrentEventContent)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (aEvent->message == NS_CONTEXTMENU) {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "nsStyleSet.h"
|
||||
#include "nsContentUtils.h" // For AddScriptBlocker().
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "TouchManager.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
@@ -740,8 +741,6 @@ protected:
|
||||
static void MarkImagesInListVisible(const nsDisplayList& aList);
|
||||
void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect);
|
||||
|
||||
void EvictTouches();
|
||||
|
||||
// Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
|
||||
void HandleKeyboardEvent(nsINode* aTarget,
|
||||
mozilla::WidgetKeyboardEvent& aEvent,
|
||||
@@ -812,6 +811,9 @@ protected:
|
||||
nsCallbackEventRequest* mFirstCallbackEventRequest;
|
||||
nsCallbackEventRequest* mLastCallbackEventRequest;
|
||||
|
||||
// TouchManager
|
||||
TouchManager mTouchManager;
|
||||
|
||||
// TouchCaret
|
||||
nsRefPtr<mozilla::TouchCaret> mTouchCaret;
|
||||
nsRefPtr<mozilla::SelectionCarets> mSelectionCarets;
|
||||
|
||||
Reference in New Issue
Block a user