Chromium網頁輸入事件捕捉和手勢檢測過程分析
連續的輸入事件可能會產生一定的手勢操作,例如滑動手勢和捏合手勢。在Chromium中,網頁的輸入事件是在Browser程序中捕捉的。Browser程序捕獲輸入事件之後,會進行手勢操作檢測。檢測出來的手勢操作將會發送給Render程序處理,因為它們需要應用在網頁之上。與此同時,Browser程序也會將原始的輸入事件傳送給Render程序處理。本文接下來就分析Browser程序處理網頁輸入事件的過程。
《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!
接下來我們將以Chromium自帶的Content Shell APK為例,說明Chromium的Browser程序捕獲網頁輸入事件以及檢測手勢操作的過程,如圖1所示:
圖1 Browser程序處理網頁輸入事件的過程
從前面Chromium網頁輸入事件處理機制簡要介紹和學習計劃一文可以知道,Content Shell APK將網頁渲染在一個SurfaceView控制元件上。這個SurfaceView又是嵌入在一個ContentView控制元件裡面的。當用戶在網頁上觸發了一個輸入事件時,例如觸發一個Touch事件時,這個Touch事件就會被系統分發給上述ContentView控制元件處理,表現為該ContentView控制元件的成員函式onTouchEvent被呼叫。
ContentView控制元件得到Touch事件之後,會將它傳遞到Chromium的C++層去處理。Java層的每一個ContentView控制元件在C++層都對應一個ContentViewCore物件。C++層的ContentViewCore物件得到Touch事件之後,就會通過一個Gesture Dector和一個Scale Gesture Detector進行滑動(Scroll)和捏合(Pinch)手勢檢測。檢測出來的滑動和捏合手勢將會統一儲存在一個Gestrue Packet中。這個Gestrue Packet接下來會被一個Input Router封裝在一個型別為InputMsg_HandleInputEvent的IPC訊息中,傳送給Render程序處理。
注意,Touch事件經過手勢檢測之後,它本身也會被上述Input Router通過另外一個InputMsg_HandleInputEvent訊息傳送給Render程序處理。這意味著在這種情況下,Render程序將收到兩個InputMsg_HandleInputEvent訊息。
接下來,我們就從ContentView類的成員函式onTouchEvent開始,分析Browser程序處理網頁輸入事件的過程,如下所示:
public class ContentView extends FrameLayout implements ContentViewCore.InternalAccessDelegate, SmartClipProvider { ...... @Override public boolean onTouchEvent(MotionEvent event) { return mContentViewCore.onTouchEvent(event); } ...... }
這個函式定義在檔案external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentView.java中。
引數event指向的MotionEvent物件描述的就是當前發生的Touch事件。ContentView類的成員變數mContentViewCore指向的是一個ContentViewCore物件,ContentView類的成員函式onTouchEvent呼叫這個ContentViewCore物件的成員函式onTouchEvent處理引數event所描述的Touch事件。
ContentViewCore類的成員函式onTouchEvent的實現如下所示:
public class ContentViewCore
implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
......
public boolean onTouchEvent(MotionEvent event) {
TraceEvent.begin("onTouchEvent");
try {
......
final int pointerCount = event.getPointerCount();
final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
event.getEventTime(), eventAction,
pointerCount, event.getHistorySize(), event.getActionIndex(),
event.getX(), event.getY(),
pointerCount > 1 ? event.getX(1) : 0,
pointerCount > 1 ? event.getY(1) : 0,
event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
event.getRawX(), event.getRawY(),
event.getToolType(0),
pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
event.getButtonState());
......
return consumed;
} finally {
TraceEvent.end("onTouchEvent");
}
}
......
}
這個函式定義在檔案external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java中。
ContentViewCore類的成員函式onTouchEvent主要是呼叫另外一個成員函式nativeOnTouchEvent處理引數event描述的Touch事件。
ContentViewCore類的成員函式nativeOnTouchEvent是一個JNI函式,它由C++層的函式Java_com_android_org_chromium_content_browser_ContentViewCore_nativeOnTouchEvent實現,如下所示:
__attribute__((visibility("default")))
jboolean
Java_com_android_org_chromium_content_browser_ContentViewCore_nativeOnTouchEvent(JNIEnv*
env,
jobject jcaller,
jlong nativeContentViewCoreImpl,
jobject event,
jlong timeMs,
jint action,
jint pointerCount,
jint historySize,
jint actionIndex,
jfloat x0,
jfloat y0,
jfloat x1,
jfloat y1,
jint pointerId0,
jint pointerId1,
jfloat touchMajor0,
jfloat touchMajor1,
jfloat rawX,
jfloat rawY,
jint androidToolType0,
jint androidToolType1,
jint androidButtonState) {
ContentViewCoreImpl* native =
reinterpret_cast<ContentViewCoreImpl*>(nativeContentViewCoreImpl);
CHECK_NATIVE_PTR(env, jcaller, native, "OnTouchEvent", false);
return native->OnTouchEvent(env, jcaller, event, timeMs, action, pointerCount,
historySize, actionIndex, x0, y0, x1, y1, pointerId0, pointerId1,
touchMajor0, touchMajor1, rawX, rawY, androidToolType0, androidToolType1,
androidButtonState);
}
這個函式定義在檔案out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewCore_jni.h中。引數nativeContentViewCoreImpl描述的是C++層的一個ContentViewCoreImpl物件,函式Java_com_android_org_chromium_content_browser_ContentViewCore_nativeOnTouchEvent呼叫這個ContentViewCoreImpl物件的成員函式OnTouchEvent處理其它引數所描述的Touch事件。
ContentViewCoreImpl類的成員函式OnTouchEvent的實現如下所示:
jboolean ContentViewCoreImpl::OnTouchEvent(JNIEnv* env,
jobject obj,
jobject motion_event,
jlong time_ms,
jint android_action,
jint pointer_count,
jint history_size,
jint action_index,
jfloat pos_x_0,
jfloat pos_y_0,
jfloat pos_x_1,
jfloat pos_y_1,
jint pointer_id_0,
jint pointer_id_1,
jfloat touch_major_0,
jfloat touch_major_1,
jfloat raw_pos_x,
jfloat raw_pos_y,
jint android_tool_type_0,
jint android_tool_type_1,
jint android_button_state) {
RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
......
MotionEventAndroid event(1.f / dpi_scale(),
env,
motion_event,
time_ms,
android_action,
pointer_count,
history_size,
action_index,
pos_x_0,
pos_y_0,
pos_x_1,
pos_y_1,
pointer_id_0,
pointer_id_1,
touch_major_0,
touch_major_1,
raw_pos_x,
raw_pos_y,
android_tool_type_0,
android_tool_type_1,
android_button_state);
return rwhv->OnTouchEvent(event);
}
這個函式定義在檔案external/chromium_org/content/browser/android/content_view_core_impl.cc中。ContentViewCoreImpl類的成員函式OnTouchEvent首先呼叫成員函式GetRenderWidgetHostViewAndroid獲得一個RenderWidgetHostViewAndroid物件。這個RenderWidgetHostViewAndroid物件用來在C++層描述載入網頁的控制元件,它的建立過程可以參考前面Chromium硬體加速渲染的OpenGL上下文繪圖表面建立過程分析一文。
ContentViewCoreImpl類的成員函式OnTouchEvent接下來又將引數描述的Touch事件封裝在一個MotionEventAndroid物件中,然後將該MotionEventAndroid物件傳遞給前面獲得的RenderWidgetHostViewAndroid物件的成員函式OnTouchEvent處理。
RenderWidgetHostViewAndroid物件的成員函式OnTouchEvent的實現如下所示:
bool RenderWidgetHostViewAndroid::OnTouchEvent(
const ui::MotionEvent& event) {
......
if (!gesture_provider_.OnTouchEvent(event))
return false;
......
// Short-circuit touch forwarding if no touch handlers exist.
if (!host_->ShouldForwardTouchEvent()) {
const bool event_consumed = false;
gesture_provider_.OnTouchEventAck(event_consumed);
return true;
}
SendTouchEvent(CreateWebTouchEventFromMotionEvent(event));
return true;
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc中。RenderWidgetHostViewAndroid類的成員變數gesture_provider_描述的是一個FilteredGestureProvider物件。RenderWidgetHostViewAndroid類的成員函式OnTouchEvent首先呼叫這個FilteredGestureProvider物件的成員函式OnTouchEvent檢測引數event描述的Touch事件是否產生了手勢操作。如果有發生,那麼就會將它們傳送給Render程序處理。
RenderWidgetHostViewAndroid類的成員變數host_指向的是一個RenderWidgetHostImpl物件。這個RenderWidgetHostImpl物件也是用來在C++層描述載入網頁的控制元件的,它的建立過程可以參考前面Chromium硬體加速渲染的OpenGL上下文繪圖表面建立過程分析一文。RenderWidgetHostViewAndroid類的成員函式OnTouchEvent接下來呼叫這個RenderWidgetHostImpl物件的成員函式ShouldForwardTouchEvent檢查Render程序是否註冊了處理Touch事件的Handler。如果沒有註冊的話,那麼就不需要將引數event描述的Touch事件傳送給它處理了。
我們假設Render程序註冊了處理Touch事件的Handler。在這種情況下,RenderWidgetHostViewAndroid類的成員函式OnTouchEvent就會呼叫函式CreateWebTouchEventFromMotionEvent將引數event描述的Touch事件封裝成一個blink::WebTouchEvent物件,並且呼叫另外一個成員函式SendTouchEvent將該blink::WebTouchEvent物件傳送給Render程序處理。注意,這個blink::WebTouchEvent物件描述的是原始的Touch事件,它不是一個手勢操作。
接下來,我們先分析FilteredGestureProvider類的成員函式OnTouchEvent檢測手勢操作的過程,接著再分析函式CreateWebTouchEventFromMotionEvent建立blink::WebTouchEvent物件的過程,以及RenderWidgetHostViewAndroid類的成員函式SendTouchEvent向Render程序傳送Touch事件的過程。
FilteredGestureProvider類的成員函式OnTouchEvent的實現如下所示:
bool FilteredGestureProvider::OnTouchEvent(const MotionEvent& event) {
DCHECK(!handling_event_);
base::AutoReset<bool> handling_event(&handling_event_, true);
pending_gesture_packet_ = GestureEventDataPacket::FromTouch(event);
if (!gesture_provider_.OnTouchEvent(event))
return false;
TouchDispositionGestureFilter::PacketResult result =
gesture_filter_.OnGesturePacket(pending_gesture_packet_);
if (result != TouchDispositionGestureFilter::SUCCESS) {
NOTREACHED() << "Invalid touch gesture sequence detected.";
return false;
}
return true;
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/filtered_gesture_provider.cc中。FilteredGestureProvider類的成員函式OnTouchEvent首先將成員變數handling_event_的值設定為true,表示當前正處於收集手勢操作的過程中,不要將正在收集的手勢操作傳送給Render程序處理,而是等到全部收集完畢再一起傳送給Render程序處理。注意,當FilteredGestureProvider類的成員函式OnTouchEvent的呼叫結束後,FilteredGestureProvider類的成員變數handling_event的值將自動恢復為false。
FilteredGestureProvider類的成員函式OnTouchEvent接下來呼叫GestureEventDataPacket類的靜態成員函式FromTouch建立一個用來儲存手勢操作的Gesture Event Data Packet,如下所示:
GestureEventDataPacket GestureEventDataPacket::FromTouch(
const ui::MotionEvent& touch) {
return GestureEventDataPacket(touch.GetEventTime(),
ToGestureSource(touch),
gfx::PointF(touch.GetX(), touch.GetY()),
gfx::PointF(touch.GetRawX(), touch.GetRawY()));
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_event_data_packet.cc中。GestureEventDataPacket類的靜態成員函式FromTouch首先呼叫函式ToGestureSource獲得接下來要建立的Gesture Event Data Packet的型別,接著建立一個該型別的Gesture Event Data Packet返回給呼叫者。
函式ToGestureSource的實現如下所示:
GestureEventDataPacket::GestureSource ToGestureSource(
const ui::MotionEvent& event) {
switch (event.GetAction()) {
case ui::MotionEvent::ACTION_DOWN:
return GestureEventDataPacket::TOUCH_SEQUENCE_START;
case ui::MotionEvent::ACTION_UP:
return GestureEventDataPacket::TOUCH_SEQUENCE_END;
case ui::MotionEvent::ACTION_MOVE:
return GestureEventDataPacket::TOUCH_MOVE;
case ui::MotionEvent::ACTION_CANCEL:
return GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL;
case ui::MotionEvent::ACTION_POINTER_DOWN:
return GestureEventDataPacket::TOUCH_START;
case ui::MotionEvent::ACTION_POINTER_UP:
return GestureEventDataPacket::TOUCH_END;
};
NOTREACHED() << "Invalid ui::MotionEvent action: " << event.GetAction();
return GestureEventDataPacket::INVALID;
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_event_data_packet.cc中。函式ToGestureSource返回的Gesture Event Data Packet的型別與引數evnet描述的Touch事件的型別有關。例如,假設event描述的是一個ACTION_MOVE型別的Touch事件,那麼函式ToGestureSource返回的Gesture Event Data Packet的型別就為GestureEventDataPacket::TOUCH_MOVE。
在接下來的分析中,我們就假設當前要處理的是一個ACTION_MOVE型別的Touch事件,這意味著FilteredGestureProvider類的成員函式OnTouchEvent呼叫GestureEventDataPacket類的靜態成員函式FromTouch獲得的是一個型別為GestureEventDataPacket::TOUCH_MOVE的Gesture Event Data Packet。這個Gesture Event Data Packet儲存在FilteredGestureProvider類的成員變數pending_gesture_packet_中。
回到FilteredGestureProvider類的成員函式OnTouchEvent中,它接下來呼叫成員變數gesture_provider_描述的一個GestureProvider物件的成員函式OnTouchEvent檢查引數event描述的Touch事件是否產生了手勢操作。如果產生了,那麼就會將它們儲存在成員變數pending_gesture_packet_描述的Gesture Event Data Packet中。
FilteredGestureProvider類的成員變數gesture_filter_描述的是一個TouchDispositionGestureFilter物件,FilteredGestureProvider類的成員函式OnTouchEvent最後呼叫這個TouchDispositionGestureFilter物件的成員函式OnGesturePacket將成員變數pending_gesture_packet_描述的Gesture Event Data Packet傳送給Render程序處理,也就是將前面檢測到的手勢操作傳送給Render程序處理。
接下來,我們先分析GestureProvider物件的成員函式OnTouchEvent檢測手勢操作的過程,接下來再分析TouchDispositionGestureFilter類的成員函式OnGesturePacket傳送手勢操作給Render程序的過程。
GestureProvider類的成員函式OnTouchEvent的實現如下所示:
bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
......
gesture_listener_->OnTouchEvent(event, in_scale_gesture);
scale_gesture_listener_->OnTouchEvent(event);
......
return true;
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_provider.cc中。GestureProvider類的成員變數gesture_listener_指向的是一個GestureListenerImpl物件。這個GestureListenerImpl物件負責檢測引數event描述的Touch事件是否產生滑動手勢操作。這是通過呼叫它的成員函式OnTouchEvent實現的。
GestureProvider類的成員變數scale_gesture_listener_指向的是一個ScaleGestureListenerImpl物件。這個ScaleGestureListenerImpl物件負責檢測引數event描述的Touch事件是否產生捏合手勢操作。這是通過呼叫它的成員函式OnTouchEvent實現的。
接下來,我們就分別分析GestureListenerImpl類和ScaleGestureListenerImpl類的成員函式OnTouchEvent的實現,以便了解滑動和捏合手勢操作的檢測過程。
GestureListenerImpl類的成員函式OnTouchEvent的實現如下所示:
class GestureProvider::GestureListenerImpl
: public GestureDetector::GestureListener,
public GestureDetector::DoubleTapListener {
public:
......
bool OnTouchEvent(const MotionEvent& e,
bool is_scale_gesture_detection_in_progress) {
......
return gesture_detector_.OnTouchEvent(e);
}
private:
......
GestureDetector gesture_detector_;
......
};
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_provider.cc中。GestureListenerImpl類的成員函式OnTouchEvent通過呼叫成員變數gesture_detector_描述的一個GestureDetector物件的成員函式OnTouchEvent檢測測引數e描述的Touch事件是否產生了滑動手勢操作。
GestureDetector類的成員函式OnTouchEvent的實現如下所示:
bool GestureDetector::OnTouchEvent(const MotionEvent& ev) {
const MotionEvent::Action action = ev.GetAction();
......
const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
const int skip_index = pointer_up ? ev.GetActionIndex() : -1;
// Determine focal point.
float sum_x = 0, sum_y = 0;
const int count = static_cast<int>(ev.GetPointerCount());
for (int i = 0; i < count; i++) {
if (skip_index == i)
continue;
sum_x += ev.GetX(i);
sum_y += ev.GetY(i);
}
const int div = pointer_up ? count - 1 : count;
const float focus_x = sum_x / div;
const float focus_y = sum_y / div;
bool handled = false;
switch (action) {
......
case MotionEvent::ACTION_MOVE:
{
const float scroll_x = last_focus_x_ - focus_x;
const float scroll_y = last_focus_y_ - focus_y;
if (is_double_tapping_) {
// Give the move events of the double-tap.
DCHECK(double_tap_listener_);
handled |= double_tap_listener_->OnDoubleTapEvent(ev);
} else if (always_in_tap_region_) {
const float delta_x = focus_x - down_focus_x_;
const float delta_y = focus_y - down_focus_y_;
const float distance_square = delta_x * delta_x + delta_y * delta_y;
if (distance_square > touch_slop_square_) {
handled = listener_->OnScroll(
*current_down_event_, ev, scroll_x, scroll_y);
last_focus_x_ = focus_x;
last_focus_y_ = focus_y;
always_in_tap_region_ = false;
......
}
......
} else if (std::abs(scroll_x) > kScrollEpsilon ||
std::abs(scroll_y) > kScrollEpsilon) {
handled =
listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y);
last_focus_x_ = focus_x;
last_focus_y_ = focus_y;
}
......
break;
......
}
return handled;
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_detector.cc中。前面我們假設引數ev描述的是一個ACTION_MOVE型別的Touch事件。GestureDetector類的成員函式OnTouchEvent首先會計算這個Touch事件的位置(focus_x, focus_y)。注意,這個Touch事件可能包含了多個觸控點,因此在計算它的位置時,通過將所有的觸控點進行算術平均得到。
GestureDetector類的成員變數last_focus_x_和last_focus_y_記錄的是上一個型別為ACTION_MOVE的Touch事件的位置(last_focus_x_, last_focus_y_)。GestureDetector類的成員函式OnTouchEvent通過比較(last_focus_x_, last_focus_y_)和(focus_x, focus_y)的值,得到連續兩個型別為ACTION_MOVE的Touch事件在網頁的X軸和Y軸上所產生的滑動量scroll_x和scroll_y。
GestureDetector類的成員變數is_double_tapping_是一個布林變數。當它的值等於true的時候,表示使用者在規定的時間和空間內連續點選了兩次網頁。這種情況稱為Double Tap,這時候GestureDetector類的成員函式OnTouchEvent會將引數ev描述的型別為ACTION_MOVE的Touch事件交給成員變數double_tap_listener_指向的一個DoubleTapListener物件的成員函式OnDoubleTapEvent處理。也就是說,Double Tap之後的型別為ACTION_MOVE的Touch事件將不會產生滑動手勢操作。
GestureDetector類的成員變數always_in_tap_region_也是一個布林變數。當它的值等於true的時候,表示使用者之前觸發了一個型別為ACTION_DOWN的Touch事件。在這種情況下,GestureDetector類的成員函式OnTouchEvent需要計算當前發生的型別為ACTION_MOVE的Touch事件與之前觸發的型別為ACTION_DOWN的Touch事件的位置距離。當這個距離大於預設的值之時,GestureDetector類的成員變數always_in_tap_region_會被重置為false,表示後面觸發型別為ACTION_UP的Touch事件時,不要產生一個Single Tap事件。與此同時,需要產生一個滑動手勢操作。這個滑動手勢操作通過呼叫GestureDetector類的成員變數listener_描述的一個GestureListener物件的成員函式OnScroll進行處理。
從前面的分析我們可以看出,Single Tap事件與滑動手勢操作是互斥的。一個Single Tap事件指的是指在規定時間和空間內先後發生了一個型別為ACTION_DOWN的Touch事件和一個型別為ACTION_UP的Touch事件。在這兩個Touch事件之間發生的型別為ACTION_MOVE的Touch事件將不會產生手勢操作。
當GestureDetector類的成員變數is_double_tapping_和always_in_tap_region_ 的值都等於false的時候,GestureDetector類的成員函式OnTouchEvent檢查連續兩個型別為ACTION_MOVE的Touch事件在網頁的X軸和Y軸上所產生的滑動量scroll_x和scroll_y是否超過了預設的閥值。如果超過了,那麼就認為產生了一個滑動手勢操作。這個滑動手勢操作也是通過呼叫GestureDetector類的成員變數listener_描述的一個GestureListener物件的成員函式OnScroll進行處理。
接下來我們主要關注滑動手勢操作的處理過程。GestureDetector類的成員變數listener_指向的實際上是一個GestureListenerImpl物件。這個GestureListenerImpl物件就是前面提到的GestureProvider類的成員變數gesture_listener_所指向的GestureListenerImpl物件。這意味著GestureDetector類的成員函式OnTouchEvent檢測到的滑動手勢操作將由這個GestureListenerImpl物件的成員函式OnScroll進行處理。
GestureListenerImpl類的成員函式OnScroll的實現如下所示:
class GestureProvider::GestureListenerImpl
: public GestureDetector::GestureListener,
public GestureDetector::DoubleTapListener {
public:
......
virtual bool OnScroll(const MotionEvent& e1,
const MotionEvent& e2,
float raw_distance_x,
float raw_distance_y) OVERRIDE {
float distance_x = raw_distance_x;
float distance_y = raw_distance_y;
......
if (!provider_->IsScrollInProgress()) {
// Note that scroll start hints are in distance traveled, where
// scroll deltas are in the opposite direction.
GestureEventDetails scroll_details(
ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
// Use the co-ordinates from the touch down, as these co-ordinates are
// used to determine which layer the scroll should affect.
provider_->Send(CreateGesture(scroll_details,
e2.GetId(),
e2.GetEventTime(),
e1.GetX(),
e1.GetY(),
e1.GetRawX(),
e1.GetRawY(),
e2.GetPointerCount(),
GetBoundingBox(e2)));
}
if (distance_x || distance_y) {
const gfx::RectF bounding_box = GetBoundingBox(e2);
const gfx::PointF center = bounding_box.CenterPoint();
const gfx::PointF raw_center =
center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
GestureEventDetails scroll_details(
ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
provider_->Send(CreateGesture(scroll_details,
e2.GetId(),
e2.GetEventTime(),
center.x(),
center.y(),
raw_center.x(),
raw_center.y(),
e2.GetPointerCount(),
bounding_box));
}
return true;
}
......
};
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_provider.cc中。
GestureListenerImpl類的成員函式OnScroll首先呼叫成員變數provider_指向的一個GestureProvider物件的成員函式IsScrollInProgress判斷網頁當前是否正在滑動過程中。如果不是的話,那麼就說明現在要開始對網頁進行滑動。這時候Browser程序會先發送一個型別為ET_GESTURE_SCROLL_BEGIN的手勢操作給Render程序。
在網頁有滑動的情況下,也就是網頁至少在X軸和Y軸之一有偏移時,GestureListenerImpl類的成員函式OnScroll接下來還會向Render程序傳送一個型別為ET_GESTURE_SCROLL_UPDATE的手勢操作。
不管是型別為ET_GESTURE_SCROLL_BEGIN的手勢操作,還是型別為ET_GESTURE_SCROLL_UPDATE的手勢操作,它們都會通過函式CreateGesture封裝為一個GestureEventData物件。這兩個GestureEventData物件都是通過呼叫GestureListenerImpl類的成員變數provider_指向的GestureProvider物件的成員函式Send傳送給Render程序的,如下所示:
void GestureProvider::Send(GestureEventData gesture) {
......
client_->OnGestureEvent(gesture);
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_provider.cc中。GestureProvider類的成員變數client_指向的是一個FilteredGestureProvider物件。這個FilteredGestureProvider物件就是前面分析的RenderWidgetHostViewAndroid類的成員變數gesture_provider_所指向的FilteredGestureProvider物件。
GestureProvider類的成員函式Send主要是呼叫上述FilteredGestureProvider物件的成員函式OnGestureEvent將引數gesture描述的手勢操作傳送給Render程序處理,如下所示:
void FilteredGestureProvider::OnGestureEvent(const GestureEventData& event) {
if (handling_event_) {
pending_gesture_packet_.Push(event);
return;
}
gesture_filter_.OnGesturePacket(
GestureEventDataPacket::FromTouchTimeout(event));
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/filtered_gesture_provider.cc中。從前面的分析可以知道,當前正在處理的FilteredGestureProvider物件的成員變數handling_event_已經被設定為true,表示此時僅僅收集引數event描述的手勢操作,而不要將它傳送給Render程序。引數event描述的手勢操作被收集在FilteredGestureProvider類的成員變數pending_gesture_packet_描述的一個Gesture Event Data Packet中。從前面的分析可以知道,這個Gesture Event Data Packet的型別為GestureEventDataPacket::TOUCH_MOVE。
如果當前正在處理的FilteredGestureProvider物件的成員變數handling_event_的值不等於true,那麼FilteredGestureProvider類的成員函式OnGestureEvent將會直接將引數event描述的手勢操作傳送給Render程序,這是通過呼叫另外一個成員變數gesture_filter_描述的一個TouchDispositionGestureFilter物件的成員函式OnGesturePacket實現的。後面我們再分析這個傳送過程。
這一步執行完成後,Browser程序就對當前發生的Touch事件進行了滑動手勢檢測,並且檢測到的滑動手勢操作已經儲存在一個Gesture Event Data Packet中。回到前面分析的GestureProvider類的成員函式OnTouchEvent中,接下來它會繼續呼叫另外一個成員變數scale_gesture_listener_指向的是ScaleGestureListenerImpl物件的成員函式OnTouchEvent檢測當前發生的Touch事件是否產生了捏合手勢操作。如果產生了,那麼同樣將它收集在上述的Gesture Event Data Packet中。
接下來我們就繼續分析捏合手勢操作的檢測過程,也就是ScaleGestureListenerImpl類的成員函式OnTouchEvent的實現,如下所示:
class GestureProvider::ScaleGestureListenerImpl
: public ScaleGestureDetector::ScaleGestureListener {
public:
......
bool OnTouchEvent(const MotionEvent& event) {
......
bool handled = scale_gesture_detector_.OnTouchEvent(event);
......
return handled;
}
......
private:
......
ScaleGestureDetector scale_gesture_detector_;
......
};
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_provider.cc中。ScaleGestureListenerImpl類的成員函式OnTouchEvent主要是呼叫成員變數scale_gesture_detector_描述的一個ScaleGestureDetector物件的成員函式OnTouchEvent檢測引數event描述的Touch事件是否產生捏合手勢操作。
ScaleGestureDetector類的成員函式OnTouchEvent的實現如下所示:
bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) {
......
const int action = event.GetAction();
......
// Span is the average distance between touch points through the focal point;
// i.e. the diameter of the circle with a radius of the average deviation from
// the focal point.
const float span_x = dev_x * 2;
const float span_y = dev_y * 2;
float span;
if (InDoubleTapMode()) {
span = span_y;
} else {
span = std::sqrt(span_x * span_x + span_y * span_y);
}
......
const float min_span = InDoubleTapMode() ? span_slop_ : min_span_;
if (!in_progress_ && span >= min_span && (InDoubleTapMode() || count > 1) &&
(was_in_progress || std::abs(span - initial_span_) > span_slop_)) {
......
in_progress_ = listener_->OnScaleBegin(*this, event);
}
// Handle motion; focal point and span/scale factor are changing.
if (action == MotionEvent::ACTION_MOVE) {
......
if (in_progress_) {
update_prev = listener_->OnScale(*this, event);
}
......
}
return true;
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/scale_gesture_detector.cc中。
ScaleGestureDetector類的成員函式OnTouchEvent首先計算網頁被捏合的大小span。這是根據網頁在X軸和Y軸上的捏合大小span_x和span_y計算得到的。
ScaleGestureDetector類的成員函式OnTouchEvent接下來判斷網頁被捏合的大小span是否大於等於預設的閥值。如果大於等於,並且網頁是剛開始被捏合,那麼就會呼叫成員變數listener_指向的一個ScaleGestureListenerImpl物件的成員函式OnScaleBegin,用來詢問是否允許產生一個捏合手勢操作。如果允許的話,ScaleGestureDetector類的成員變數in_progress_就會被設定為true。
上述ScaleGestureListenerImpl物件就是前面分析的GestureProvider類的成員變數scale_gesture_listener_所指向的ScaleGestureListenerImpl物件,它的成員函式OnScaleBegin的實現如下所示:
class GestureProvider::ScaleGestureListenerImpl
: public ScaleGestureDetector::ScaleGestureListener {
public:
......
virtual bool OnScaleBegin(const ScaleGestureDetector& detector,
const MotionEvent& e) OVERRIDE {
if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
return false;
......
return true;
}
......
};
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_provider.cc中。ScaleGestureListenerImpl類的成員函式OnScaleBegin在兩種情況下的返回值為true。第一種情況是當它的成員變數ignore_multitouch_events_的值等於false時。這表示當網頁被多點觸控時,有可能需要對網頁進行縮放,也就是要產生一個捏合手勢操作。第二種情況是網頁被Double Tap時。
回到ScaleGestureDetector類的成員函式OnTouchEvent中。綜合起來,我們就可以知道,當網頁被多點觸控移動或者Double Tap後移動,並且移動的距離或者兩次Tap的距離大於等於預設值時,那麼就會產生捏合手勢操作。這個捏合手勢操作將會交給ScaleGestureDetector類的成員變數listener_指向的ScaleGestureListenerImpl物件的成員函式OnScale處理,如下所示:
class GestureProvider::ScaleGestureListenerImpl
: public ScaleGestureDetector::ScaleGestureListener {
public:
......
virtual bool OnScale(const ScaleGestureDetector& detector,
const MotionEvent& e) OVERRIDE {
......
if (!pinch_event_sent_) {
pinch_event_sent_ = true;
provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
e.GetId(),
detector.GetEventTime(),
detector.GetFocusX(),
detector.GetFocusY(),
detector.GetFocusX() + e.GetRawOffsetX(),
detector.GetFocusY() + e.GetRawOffsetY(),
e.GetPointerCount(),
GetBoundingBox(e)));
}
......
float scale = detector.GetScaleFactor();
......
GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
provider_->Send(CreateGesture(pinch_details,
e.GetId(),
detector.GetEventTime(),
detector.GetFocusX(),
detector.GetFocusY(),
detector.GetFocusX() + e.GetRawOffsetX(),
detector.GetFocusY() + e.GetRawOffsetY(),
e.GetPointerCount(),
GetBoundingBox(e)));
return true;
}
......
};
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/gesture_provider.cc中。ScaleGestureListenerImpl類的成員函式OnScale首先檢查網頁是否剛剛開始被捏合。如果是的話,ScaleGestureListenerImpl類的成員變數pinch_event_sent_的值就會等於false。在這種情況下,Browser程序會先發送一個型別為ET_GESTURE_PINCH_BEGIN的手勢操作給Render程序。
ScaleGestureListenerImpl類的成員函式OnScale接下來又通過呼叫引數detector描述的ScaleGestureDetector物件的成員函式GetScaleFactor得到捏合手勢操作所產生的縮放因子,然後將這個縮放因子封裝在一個型別為ET_GESTURE_PINCH_UPDATE的手勢操作中傳送給Render程序。
與前面提到的型別為ET_GESTURE_SCROLL_BEGIN和ET_GESTURE_SCROLL_UPDATE的手勢操作一樣,型別為ET_GESTURE_PINCH_BEGIN和ET_GESTURE_PINCH_UPDATE的手勢操作也是通過GestureProvider類的成員函式Send傳送給Render程序的。但是在我們這個情景中,GestureProvider類的成員函式Send並沒有將這些手勢操作傳送給Render程序,而僅僅是將它們收集在一個Gesture Event Data Packet中。
這一步執行完成之後,Browser程序就對當前發生的Touch事件進行了滑動手勢和捏合手勢檢測,並且檢測出來的手勢操作(ET_GESTURE_SCROLL_BEGIN、ET_GESTURE_SCROLL_UPDATE、ET_GESTURE_PINCH_BEGIN和ET_GESTURE_PINCH_UPDATE)都儲存了FilteredGestureProvider類的成員變數pending_gesture_packet_描述的一個型別為GestureEventDataPacket::TOUCH_MOVE的Gesture Event Data Packet。
回到FilteredGestureProvider類的成員函式OnTouchEvent中,它接下來要做的工作就將儲存在成員變數pending_gesture_packet_描述的Gesture Event Data Packet中的手勢操作傳送給Render程序處理,這是通過呼叫另外一個成員變數gesture_filter_描述的一個TouchDispositionGestureFilter物件的成員函式OnGesturePacket實現的,如下所示:
TouchDispositionGestureFilter::PacketResult
TouchDispositionGestureFilter::OnGesturePacket(
const GestureEventDataPacket& packet) {
......
if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
Tail().empty()) {
// Handle the timeout packet immediately if the packet preceding the timeout
// has already been dispatched.
FilterAndSendPacket(packet);
return SUCCESS;
}
Tail().push(packet);
return SUCCESS;
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/touch_disposition_gesture_filter.cc中。
TouchDispositionGestureFilter類的成員函式OnGesturePacket首先判斷引數packet描述的Gesture Event Data Packet的型別是否等於GestureEventDataPacket::TOUCH_TIMEOUT。如果等於,並且當前的Gesture Event Data Packet佇列為空,那麼引數packet描述的Gesture Event Data Packet就會馬上被髮送給Render程序。這個傳送過程是通過呼叫TouchDispositionGestureFilter類的成員函式FilterAndSendPacket進行的。
從前面的分析可以知道,引數packet描述的Gesture Event Data Packet的型別為GestureEventDataPacket::TOUCH_MOVE,因此它將不會馬上被髮送給Render程序,而是被儲存在一個Gesture Event Data Packet佇列中。那麼,這個佇列中的Gesture Event Data Packet什麼會被髮送給Render程序呢?當Render程序處理完成Browser程序上一次傳送給它的Gesture Event Data Packet之後,它就會給Browser程序傳送一個ACK。Browser程序接收到這個ACK之後,就會從佇列中取出下一個Gesture Event Data Packet傳送給Render程序處理。這個傳送過程同樣也是通過呼叫TouchDispositionGestureFilter類的成員函式FilterAndSendPacket進行的。因此,接下來我們就繼續分析TouchDispositionGestureFilter類的成員函式FilterAndSendPacket的實現,如下所示:
void TouchDispositionGestureFilter::FilterAndSendPacket(
const GestureEventDataPacket& packet) {
......
for (size_t i = 0; i < packet.gesture_count(); ++i) {
const GestureEventData& gesture = packet.gesture(i);
......
SendGesture(gesture, packet);
}
......
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/touch_disposition_gesture_filter.cc中。TouchDispositionGestureFilter類的成員函式FilterAndSendPacket遍歷儲存在引數packet描述的Gesture Event Data Packet中的每一個手勢操作,並且呼叫另外一個成員函式SendGesture分別將這些手勢操作傳送給Render程序,如下所示:
void TouchDispositionGestureFilter::SendGesture(
const GestureEventData& event,
const GestureEventDataPacket& packet_being_sent) {
......
client_->ForwardGestureEvent(event);
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/touch_disposition_gesture_filter.cc中。TouchDispositionGestureFilter類的成員變數client_指向的是一個FilteredGestureProvider物件。這個FilteredGestureProvider物件就是前面分析的RenderWidgetHostViewAndroid類的成員變數gesture_provider_所描述的FilteredGestureProvider物件。TouchDispositionGestureFilter類的成員函式SendGesture通過呼叫這個FilteredGestureProvider物件的成員函式ForwardGestureEvent將引數event描述的手勢操作傳送給Render程序。
FilteredGestureProvider類的成員函式ForwardGestureEvent的實現如下所示:
void FilteredGestureProvider::ForwardGestureEvent(
const GestureEventData& event) {
client_->OnGestureEvent(event);
}
這個函式定義在檔案external/chromium_org/ui/events/gesture_detection/filtered_gesture_provider.cc中。FilteredGestureProvider類的成員變數client_指向的是一個RenderWidgetHostViewAndroid物件。這個RenderWidgetHostViewAndroid物件就前面描述的在Browser程序中用來載入網頁的控制元件。FilteredGestureProvider類的成員函式ForwardGestureEvent通過呼叫這個RenderWidgetHostViewAndroid物件的成員函式OnGestureEvent將引數event描述的手勢操作傳送給Render程序。
RenderWidgetHostViewAndroid類的成員函式OnGestureEvent的實現如下所示:
void RenderWidgetHostViewAndroid::OnGestureEvent(
const ui::GestureEventData& gesture) {
......
SendGestureEvent(CreateWebGestureEventFromGestureEventData(gesture));
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc中。RenderWidgetHostViewAndroid類的成員函式OnGestureEvent首先呼叫函式CreateWebGestureEventFromGestureEventData將引數gesture描述的手勢操作封裝在一個WebGestureEvent物件中,如下所示:
WebGestureEvent CreateWebGestureEventFromGestureEventData(
const ui::GestureEventData& data) {
WebGestureEvent gesture;
gesture.x = data.x;
gesture.y = data.y;
gesture.globalX = data.raw_x;
gesture.globalY = data.raw_y;
gesture.timeStampSeconds = (data.time - base::TimeTicks()).InSecondsF();
gesture.sourceDevice = blink::WebGestureDeviceTouchscreen;
switch (data.type()) {
......
case ui::ET_GESTURE_SCROLL_UPDATE:
gesture.type = WebInputEvent::GestureScrollUpdate;
gesture.data.scrollUpdate.deltaX = data.details.scroll_x();
gesture.data.scrollUpdate.deltaY = data.details.scroll_y();
break;
......
case ui::ET_GESTURE_PINCH_UPDATE:
gesture.type = WebInputEvent::GesturePinchUpdate;
gesture.data.pinchUpdate.scale = data.details.scale();
break;
......
}
return gesture;
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/web_input_event_util.cc中。函式CreateWebGestureEventFromGestureEventData會將不同型別的手勢操作封裝在不同型別的WebGestureEvent物件中。例如,ui::ET_GESTURE_SCROLL_UPDATE類的手勢操作,即滑動手勢操作,會封裝在一個型別為WebInputEvent::GestureScrollUpdate的WebGestureEvent物件中;又如,ui::ET_GESTURE_PINCH_UPDATE型別的手勢操作,即捏合手勢操作,會封裝在一個型別為WebInputEvent::GesturePinchUpdate的WebGestureEvent物件中。
回到RenderWidgetHostViewAndroid類的成員函式OnGestureEvent中,它將手勢操作封裝在一個WebGestureEvent物件之後,再呼叫另外一個成員函式SendGestureEvent將這個WebGestureEvent物件傳送給Render程序,如下所示:
void RenderWidgetHostViewAndroid::SendGestureEvent(
const blink::WebGestureEvent& event) {
......
if (host_)
host_->ForwardGestureEventWithLatencyInfo(event, CreateLatencyInfo(event));
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc中。RenderWidgetHostViewAndroid類的成員變數host_指向的是一個RenderWidgetHostImpl物件。這個RenderWidgetHostImpl物件描述的是載入當前正在發生輸入事件的網頁的Render程序。RenderWidgetHostViewAndroid類的成員函式SendGestureEvent呼叫這個RenderWidgetHostImpl物件的成員函式ForwardGestureEventWithLatencyInfo將引數event描述的手勢操作傳送給它所描述的Render程序。
RenderWidgetHostImpl類的成員函式ForwardGestureEventWithLatencyInfo的實現如下所示:
void RenderWidgetHostImpl::ForwardGestureEventWithLatencyInfo(
const blink::WebGestureEvent& gesture_event,
const ui::LatencyInfo& ui_latency) {
......
GestureEventWithLatencyInfo gesture_with_latency(gesture_event, latency_info);
input_router_->SendGestureEvent(gesture_with_latency);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。RenderWidgetHostImpl類的成員函式ForwardGestureEventWithLatencyInfo首先將引數gesture_event描述的手勢操作封裝在一個GestureEventWithLatencyInfo物件中。
RenderWidgetHostImpl類的成員變數input_router_指向的是一個InputRouterImpl物件。這個InputRouterImpl負責將輸入事件傳送給Render程序。因此,RenderWidgetHostImpl類的成員函式SendGestureEvent就通過呼叫這個InputRouterImpl物件的成員函式SendGestureEvent將上述封裝了手勢操作的GestureEventWithLatencyInfo物件傳送給Render程序。
InputRouterImpl類的成員函式SendGestureEvent的實現如下所示:
void InputRouterImpl::SendGestureEvent(
const GestureEventWithLatencyInfo& original_gesture_event) {
......
GestureEventWithLatencyInfo gesture_event(original_gesture_event);
......
SendGestureEventImmediately(gesture_event);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc中。InputRouterImpl類的成員函式SendGestureEvent主要是呼叫另外一個成員函式SendGestureEventImmediately將引數original_gesture_event描述的手勢操作傳送給Render程序,如下所示:
void InputRouterImpl::SendGestureEventImmediately(
const GestureEventWithLatencyInfo& gesture_event) {
......
FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc中。InputRouterImpl類的成員函式SendGestureEventImmediately又主要是呼叫另外一個成員函式FilterAndSendWebInputEvent將引數gesture_event描述的手勢操作傳送給Render程序,如下所示:
void InputRouterImpl::FilterAndSendWebInputEvent(
const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
bool is_keyboard_shortcut) {
......
OfferToHandlers(input_event, latency_info, is_keyboard_shortcut);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc中。InputRouterImpl類的成員函式FilterAndSendWebInputEvent又主要是呼叫另外一個成員函式OfferToHandlers將引數input_event描述的手勢操作傳送給Render程序,如下所示:
void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
bool is_keyboard_shortcut) {
......
if (OfferToClient(input_event, latency_info))
return;
OfferToRenderer(input_event, latency_info, is_keyboard_shortcut);
......
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc中。 InputRouterImpl類的成員函式OfferToHandlers首先呼叫成員函式OfferToClient詢問Browser程序是否要過濾引數input_event描述的手勢操作。如果過濾的話,那麼InputRouterImpl類的成員函式OfferToHandlers就不會將它傳送給Render程序。否則的話,就會呼叫另外一個成員函式OfferToRenderer進行傳送。
我們假設Browser程序不過濾引數input_event描述的手勢操作,因此接下來這個手勢就會通過InputRouterImpl類的成員函式OfferToRenderer傳送給Render程序,如下所示:
bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
bool is_keyboard_shortcut) {
if (Send(new InputMsg_HandleInputEvent(
routing_id(), &input_event, latency_info, is_keyboard_shortcut))) {
......
return true;
}
return false;
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc中。從這裡就可以看到,InputRouterImpl類的成員函式OfferToRenderer會將引數input_event描述的手勢操作封裝在一個型別為InputMsg_HandleInputEvent的IPC訊息中,然後再將這個訊息傳送給Render程序處理。這個處理過程我們在接下來的一篇文章中再詳細分析。
這一步執行完成後,Browser程序就將檢測到的手勢操作傳送給Render程序了。回到前面分析的RenderWidgetHostViewAndroid類的成員函式OnTouchEvent中,它接下來再呼叫函式CreateWebTouchEventFromMotionEvent將原始的Touch事件封裝在一個blink::WebTouchEvent物件中,如下所示:
blink::WebTouchEvent CreateWebTouchEventFromMotionEvent(
const ui::MotionEvent& event) {
blink::WebTouchEvent result;
WebTouchEventTraits::ResetType(
ToWebInputEventType(event.GetAction()),
(event.GetEventTime() - base::TimeTicks()).InSecondsF(),
&result);
result.touchesLength =
std::min(event.GetPointerCount(),
static_cast<size_t>(WebTouchEvent::touchesLengthCap));
DCHECK_GT(result.touchesLength, 0U);
for (size_t i = 0; i < result.touchesLength; ++i)
result.touches[i] = CreateWebTouchPoint(event, i);
return result;
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/web_input_event_util.cc中。函式CreateWebTouchEventFromMotionEvent首先呼叫函式ToWebInputEventType獲得接下來要建立的blink::WebTouchEvent物件的型別,如下所示:
WebInputEvent::Type ToWebInputEventType(MotionEvent::Action action) {
switch (action) {
case MotionEvent::ACTION_DOWN:
return WebInputEvent::TouchStart;
case MotionEvent::ACTION_MOVE:
return WebInputEvent::TouchMove;
case MotionEvent::ACTION_UP:
return WebInputEvent::TouchEnd;
case MotionEvent::ACTION_CANCEL:
return WebInputEvent::TouchCancel;
case MotionEvent::ACTION_POINTER_DOWN:
return WebInputEvent::TouchStart;
case MotionEvent::ACTION_POINTER_UP:
return WebInputEvent::TouchEnd;
}
NOTREACHED() << "Invalid MotionEvent::Action.";
return WebInputEvent::Undefined;
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/web_input_event_util.cc中。引數action表示要封裝的Touch事件的型別。函式ToWebInputEventType會根據不同的Touch事件型別返回不同的blink::WebTouchEvent物件型別。例如,對於型別為MotionEvent::ACTION_MOVE的Touch事件,函式ToWebInputEventType返回的blink::WebTouchEvent物件型別為WebInputEvent::TouchMove。
回到函式CreateWebTouchEventFromMotionEvent中,它獲得了接下來要建立的blink::WebTouchEvent物件的型別之後,就會建立這個blink::WebTouchEvent物件,並且會將event描述的Touch事件的所有資訊,例如觸控點位置,儲存在創建出來的blink::WebTouchEvent物件中。
這一步執行完成之後,再回到前面分析的RenderWidgetHostViewAndroid類的成員函式OnTouchEvent中,它接下來就會將前面建立的blink::WebTouchEvent物件傳送給Render程序處理,這是通過呼叫另外一個成員函式SendTouchEvent實現的,如下所示:
void RenderWidgetHostViewAndroid::SendTouchEvent(
const blink::WebTouchEvent& event) {
if (host_)
host_->ForwardTouchEventWithLatencyInfo(event, CreateLatencyInfo(event));
......
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc中。前面提到,RenderWidgetHostViewAndroid類的成員變數host_指向的是一個RenderWidgetHostImpl物件,RenderWidgetHostViewAndroid類的成員函式SendTouchEvent呼叫這個RenderWidgetHostImpl物件的成員函式ForwardTouchEventWithLatencyInfo將引數event描述的Touch事件傳送給Render程序。
RenderWidgetHostImpl類的成員函式ForwardTouchEventWithLatencyInfo的實現如下所示:
void RenderWidgetHostImpl::ForwardTouchEventWithLatencyInfo(
const blink::WebTouchEvent& touch_event,
const ui::LatencyInfo& ui_latency) {
......
ui::LatencyInfo latency_info =
CreateRWHLatencyInfoIfNotExist(&ui_latency, touch_event.type);
TouchEventWithLatencyInfo touch_with_latency(touch_event, latency_info);
input_router_->SendTouchEvent(touch_with_latency);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。RenderWidgetHostImpl類的成員函式ForwardTouchEventWithLatencyInfo首先將引數touch_event描述的Touch事件封裝在一個TouchEventWithLatencyInfo物件中,然後再呼叫成員變數input_router_指向的一個InputRouterImpl物件的成員函式SendTouchEvent將這個TouchEventWithLatencyInfo物件傳送給Render程序。
InputRouterImpl類的成員函式SendTouchEvent的實現如下所示:
void InputRouterImpl::SendTouchEvent(
const TouchEventWithLatencyInfo& touch_event) {
......
touch_event_queue_.QueueEvent(touch_event);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc中。InputRouterImpl類的成員變數touch_event_queue_描述的是一個TouchEventQueue物件,InputRouterImpl類的成員函式SendTouchEvent呼叫這個TouchEventQueue物件的成員函式QueueEvent將引數touch_event描述的Touch事件傳送給Render程序。
TouchEventQueue類的成員函式QueueEvent的實現如下所示:
void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
......
// If the queueing of |event| was triggered by an ack dispatch, defer
// processing the event until the dispatch has finished.
if (touch_queue_.empty() && !dispatching_touch_ack_) {
......
// There is no touch event in the queue. Forward it to the renderer
// immediately.
touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
ForwardNextEventToRenderer();
return;
}
// If the last queued touch-event was a touch-move, and the current event is
// also a touch-move, then the events can be coalesced into a single event.
if (touch_queue_.size() > 1) {
CoalescedWebTouchEvent* last_event = touch_queue_.back();
if (last_event->CoalesceEventIfPossible(event))
return;
}
touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/touch_event_queue.cc中。TouchEventQueue類的成員變數touch_queue_描述的是一個Touch事件佇列。這個佇列用來暫存即將要傳送給Render程序的Touch事件。一個即將要傳送的Touch事件在兩種情況下需要暫存在佇列中:
1. 在它之前的Touch事件還未傳送給Render程序,即Touch事件佇列不為空。
2. Render程序正在傳送一個ACK事件給Browser程序,而Browser程序正在分發這個ACK事件。這個ACK事件分發完成之後,Browser程序才可以將下一個Touch事件傳送給Render程序處理。這時候TouchEventQueue類的成員變數dispatching_touch_ack_的值就不等於NULL,它指向正在分發的ACK事件。
TouchEventQueue類的成員函式QueueEvent所做的事情就是判斷引數event描述的Touch事件是否能夠馬上傳送。如果能馬上傳送,那麼就會將它儲存在Touch事件佇列中,然後再呼叫另外一個成員函式ForwardNextEventToRenderer將它從Touch事件佇列讀取出來,並且傳送給Render程序。如果不能馬上傳送,那麼同樣會將它儲存在Touch事件佇列中,不過要等到上一個傳送給Render程序的Touch事件被ACK之後,才能繼續將它傳送給Render程序。這同樣是通過呼叫TouchEventQueue類的成員函式ForwardNextEventToRenderer進行傳送的。
我們注意到,在將引數event描述的Touch事件儲存在Touch事件佇列之前,如果佇列不為空,那麼TouchEventQueue類的成員函式QueueEvent會判斷引數event描述的Touch事件與佇列中最後一個Touch事件是否是相同的,也就是它們所包含的觸控點都是一樣的。如果相同,那麼就可以合併為一個Touch事件傳送給Render程序。合併後的Touch事件使用一個CoalescedWebTouchEvent物件描述。這樣可以避免重複向Render程序傳送相同的Touch事件。
我們假設引數event描述的Touch事件可以馬上傳送給Render程序,因此接下來我們就繼續分析TouchEventQueue類的成員函式ForwardNextEventToRenderer的實現,如下所示:
void TouchEventQueue::ForwardNextEventToRenderer() {
......
TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
......
// A synchronous ack will reset |dispatching_touch_|, in which case
// the touch timeout should not be started.
base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
SendTouchEventImmediately(touch);
......
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/touch_event_queue.cc中。TouchEventQueue類的成員函式ForwardNextEventToRenderer首先從Touch事件佇列中取出第一個Touch事件,然後呼叫另外一個成員函式SendTouchEventImmediately將該Touch事件傳送給Render程序。在傳送的過程中,TouchEventQueue類的成員變數dispatching_touch_會被設定為true,並且會在傳送結束後(也就是TouchEventQueue類的成員函式ForwardNextEventToRenderer呼叫結束),恢復為false。
TouchEventQueue類的成員函式SendTouchEventImmediately的實現如下所示:
void TouchEventQueue::SendTouchEventImmediately(
const TouchEventWithLatencyInfo& touch) {
......
client_->SendTouchEventImmediately(touch);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/touch_event_queue.cc中。TouchEventQueue類的成員變數client_指向的是一個InputRouterImpl物件。這個InputRouterImpl物件就前面分析的RenderWidgetHostImpl類的成員變數input_router_所指向的InputRouterImpl物件。TouchEventQueue類的成員函式SendTouchEventImmediately呼叫這個InputRouterImpl物件的成員函式SendTouchEventImmediately將引數touch描述的Touch事件傳送給Render程序。
InputRouterImpl類的成員函式SendTouchEventImmediately的實現如下所示:
void InputRouterImpl::SendTouchEventImmediately(
const TouchEventWithLatencyInfo& touch_event) {
......
FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false);
}
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc中。從這裡可以看到,InputRouterImpl類的成員函式SendTouchEventImmediately是呼叫我們前面已經分析過的另外一個成員函式FilterAndSendWebInputEvent將引數touch_event描述的Touch事件傳送給Render程序的。從前面的分析可以知道,這個Touch事件封裝在一個型別為WebInputEvent::TouchMove的WebInputEvent物件中,它