Chromium网页输入事件捕捉和手势检測过程分析

Posted llguanli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chromium网页输入事件捕捉和手势检測过程分析相关的知识,希望对你有一定的参考价值。

       连续的输入事件可能会产生一定的手势操作。比如滑动手势和捏合手势。

在Chromium中,网页的输入事件是在Browser进程中捕捉的。Browser进程捕获输入事件之后,会进行手势操作检測。检測出来的手势操作将会发送给Render进程处理,由于它们须要应用在网页之上。与此同一时候。Browser进程也会将原始的输入事件发送给Render进程处理。本文接下来就分析Browser进程处理网页输入事件的过程。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

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对象中,它的发送过程与前面分析的滑动手势操作和捏合手势操作的发送过程是一样的,只只是后两者分别封装在类型为WebInputEvent::GestureScrollUpdate和WebInputEvent::GesturePinchUpdate的WebInputEvent对象中。

       至此,我们就以Touch事件为例。分析完毕了Browser进程捕捉网页输入事件,以及从中检測手势操作的过程。这些网页输入事件和手势操作都是通过类型为InputMsg_HandleInputEvent的IPC消息发送给Render进程处理的。在接下来的两篇文章中。我们就具体分析Render进程处理网页输入事件和手势操作的过程。也就是Render进程处理类型为InputMsg_HandleInputEvent的IPC消息的过程,敬请关注!

很多其他的信息也能够关注老罗的新浪微博:http://weibo.com/shengyangluo

以上是关于Chromium网页输入事件捕捉和手势检測过程分析的主要内容,如果未能解决你的问题,请参考以下文章

Chromium网页输入事件处理机制简要介绍和学习计划

Chromium网页Pending Layer Tree激活为Active Layer Tree的过程分析

Chromium网页Layer Tree创建过程分析

Chromium网页Layer Tree创建过程分析

Chromium网页光栅化过程分析

Chromium网页Graphics Layer Tree创建过程分析