Chromium網頁輸入事件捕捉和手勢檢測過程分析

       連續的輸入事件可能會產生必定的手勢操做。好比滑動手勢和捏合手勢。html

在Chromium中,網頁的輸入事件是在Browser進程中捕捉的。Browser進程捕獲輸入事件以後,會進行手勢操做檢測。檢測出來的手勢操做將會發送給Render進程處理,因爲它們需要應用在網頁之上。與此同一時候。Browser進程也會將原始的輸入事件發送給Render進程處理。本文接下來就分析Browser進程處理網頁輸入事件的過程。java

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!
android

《Android系統源碼情景分析》一書正在進擊的程序猿網(http://0xcc0xcd.com)中連載,點擊進入。
web

       接下來咱們將以Chromium自帶的Content Shell APK爲例,說明Chromium的Browser進程捕獲網頁輸入事件以及檢測手勢操做的過程,如圖1所看到的:app


圖1 Browser進程處理網頁輸入事件的過程ide

       從前面Chromium網頁輸入事件處理機制簡要介紹和學習計劃一文可以知道,Content Shell APK將網頁渲染在一個SurfaceView控件上。函數

這個SurfaceView又是嵌入在一個ContentView控件裏面的。學習

當用戶在網頁上觸發了一個輸入事件時,好比觸發一個Touch事件時,這個Touch事件就會被系統分發給上述ContentView控件處理。表現爲該ContentView控件的成員函數onTouchEvent被調用。ui

       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進程處理。this

       注意。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對象中,它的發送過程與前面分析的滑動手勢操做和捏合手勢操做的發送過程是同樣的,只只是後二者分別封裝在類型爲WebInputEvent::GestureScrollUpdate和WebInputEvent::GesturePinchUpdate的WebInputEvent對象中。

       至此,咱們就以Touch事件爲例。分析完畢了Browser進程捕捉網頁輸入事件,以及從中檢測手勢操做的過程。這些網頁輸入事件和手勢操做都是經過類型爲InputMsg_HandleInputEvent的IPC消息發送給Render進程處理的。在接下來的兩篇文章中。咱們就具體分析Render進程處理網頁輸入事件和手勢操做的過程。也就是Render進程處理類型爲InputMsg_HandleInputEvent的IPC消息的過程,敬請關注!

不少其餘的信息也可以關注老羅的新浪微博:http://weibo.com/shengyangluo

相關文章
相關標籤/搜索