Bug 1133492 - Extract some of nsPresShell into a separate TouchManager class. r=smaug

This commit is contained in:
Maksim Lebedev
2015-02-20 02:12:00 -05:00
parent ab1e214700
commit ce6cdec278
5 changed files with 261 additions and 175 deletions

View 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;
}

View 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_) */

View File

@@ -99,6 +99,7 @@ UNIFIED_SOURCES += [
'SelectionCarets.cpp', 'SelectionCarets.cpp',
'StackArena.cpp', 'StackArena.cpp',
'TouchCaret.cpp', 'TouchCaret.cpp',
'TouchManager.cpp',
] ]
# nsPresArena.cpp needs to be built separately because it uses plarena.h. # nsPresArena.cpp needs to be built separately because it uses plarena.h.

View File

@@ -975,6 +975,8 @@ PresShell::Init(nsIDocument* aDocument,
// Setup our font inflation preferences. // Setup our font inflation preferences.
SetupFontInflation(); SetupFontInflation();
mTouchManager.Init(this, mDocument);
} }
#ifdef PR_LOGGING #ifdef PR_LOGGING
@@ -1277,7 +1279,7 @@ PresShell::Destroy()
mHaveShutDown = true; mHaveShutDown = true;
EvictTouches(); mTouchManager.Destroy();
} }
void 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 static PLDHashOperator
FindAnyTarget(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData, FindAnyTarget(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData,
void* aAnyTarget) void* aAnyTarget)
@@ -8068,125 +8016,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
case NS_MOUSE_BUTTON_UP: case NS_MOUSE_BUTTON_UP:
isHandlingUserInput = true; isHandlingUserInput = true;
break; 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: case NS_DRAGDROP_DROP:
nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession(); nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
if (session) { if (session) {
@@ -8198,6 +8028,12 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
} }
break; break;
} }
if (!mTouchManager.PreHandleEvent(aEvent, aStatus,
touchIsNew, isHandlingUserInput,
mCurrentEventContent)) {
return NS_OK;
}
} }
if (aEvent->message == NS_CONTEXTMENU) { if (aEvent->message == NS_CONTEXTMENU) {

View File

@@ -32,6 +32,7 @@
#include "nsStyleSet.h" #include "nsStyleSet.h"
#include "nsContentUtils.h" // For AddScriptBlocker(). #include "nsContentUtils.h" // For AddScriptBlocker().
#include "nsRefreshDriver.h" #include "nsRefreshDriver.h"
#include "TouchManager.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h" #include "mozilla/EventForwards.h"
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
@@ -740,8 +741,6 @@ protected:
static void MarkImagesInListVisible(const nsDisplayList& aList); static void MarkImagesInListVisible(const nsDisplayList& aList);
void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect); void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect);
void EvictTouches();
// Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent. // Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
void HandleKeyboardEvent(nsINode* aTarget, void HandleKeyboardEvent(nsINode* aTarget,
mozilla::WidgetKeyboardEvent& aEvent, mozilla::WidgetKeyboardEvent& aEvent,
@@ -812,6 +811,9 @@ protected:
nsCallbackEventRequest* mFirstCallbackEventRequest; nsCallbackEventRequest* mFirstCallbackEventRequest;
nsCallbackEventRequest* mLastCallbackEventRequest; nsCallbackEventRequest* mLastCallbackEventRequest;
// TouchManager
TouchManager mTouchManager;
// TouchCaret // TouchCaret
nsRefPtr<mozilla::TouchCaret> mTouchCaret; nsRefPtr<mozilla::TouchCaret> mTouchCaret;
nsRefPtr<mozilla::SelectionCarets> mSelectionCarets; nsRefPtr<mozilla::SelectionCarets> mSelectionCarets;