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

原文地址:https://www.cnblogs.com/llguanli/p/8450365.html

时间: 2024-10-10 13:45:30

Chromium网页输入事件捕捉和手势检測过程分析的相关文章

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

连续的输入事件可能会产生一定的手势操作,例如滑动手势和捏合手势.在Chromium中,网页的输入事件是在Browser进程中捕捉的.Browser进程捕获输入事件之后,会进行手势操作检测.检测出来的手势操作将会发送给Render进程处理,因为它们需要应用在网页之上.与此同时,Browser进程也会将原始的输入事件发送给Render进程处理.本文接下来就分析Browser进程处理网页输入事件的过程. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 接下来我们

Chromium分发输入事件给WebKit处理的过程分析

Chromium的Render进程接收到Browser进程分发过来的输入事件之后,会在Compoistor线程中处理掉滑动和捏合手势这两种特殊的输入事件,其它类型的输入事件则交给Main线程处理.Main线程又会进一步将输入事件分发给WebKit处理.WebKit则根据输入事件发生的位置在网页中找到对应的HTML元素进行处理.本文接下来详细分析Chromium分发输入事件给WebKit处理的过程. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 以Touc

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

用户在浏览网页的时候,需要与网页进行交互,常用的操作如滑动.捏合网页,以及点击网页中的链接等.这些交互操作也称为用户输入事件,浏览器需要对它们作出迅速的响应,例如及时更新网页内容或者打开新的网页等.浏览器能够对用户输入事件作出迅速的响应是至关重要的,因为这关乎到用户浏览网页时的体验,尤其是在用户滑动和捏合网页时.本文接下来就简要介绍Chromium对用户输入事件的处理机制,以及制定后续的学习计划. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 在任何一个

GestureDetector封装手势检測上下滑动

项目中须要检測ListView的上滑下滑隐藏顶部View控件,之前在网上也有非常多实现案例.在git上发现个封装非常不错的样例,记录下来. GestureDetector是一个手势检測类,内部有个SimpleOnGestureListener手势监听类. 定义一个抽象类SimpleDetector.继承GestureDetector.SimpleOnGestureListener抽象类,实现View.OnTouchListener接口.这样做有什么优点呢?首先ListView仅仅要setOnTo

Chromium网页绘图表面(Output Surface)创建过程分析

在Chromium中,Render进程在绘制网页之前,要为网页创建一个绘图表面.绘图表面描述的是网页经过渲染之后得到的输出.这个输出需要交给Browser进程处理,才能显示在屏幕上.在硬件加速渲染条件下,这个输出有可能是一个OpenGL纹理,也有可能是一系列需要进一步进行绘制的OpenGL纹理,取决于Render进程使用直接渲染器还是委托渲染器.本文接下来就对网页的绘图表面的创建过程进行详细分析. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 关于网页绘

Chromium网页滑动和捏合手势处理过程分析

从前面一文可以知道,Chromium的Browser进程从Touch事件中检测到滑动和捏合手势之后,就会将它们发送给Render进程处理.滑动手势对应于修改网页的Viewport,而捏合手势对应于设置网页的缩放因子.通常我们比较两个浏览器的流畅程度,就是比较它们的滑动和捏合性能.因此,浏览器必须要快速地响应用户的滑动和捏合手势.本文接下来就详细分析Chromium快速响应网页滑动和捏合手势的过程. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 从前面Ch

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

网页分块的光栅化操作完成后,CC Pending Layer Tree就会激活为CC Active Layer Tree.CC Active Layer Tree代表用户当前在屏幕上看到的网页内容,它可以快速响应用户输入,例如滚动和缩放.本文接下来就分析CC Pending Layer Tree激活为CC Active Layer Tree,以及CC Active Layer Tree的渲染过程. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! CC Pe

Chromium网页Layer Tree绘制过程分析

网页绘图表面创建完成之后,调度器就会请求绘制CC Layer Tree,这样网页在加载完成之后就能快速显示出来.通过CC Layer Tree可以依次找到Graphics Layer Tree.Render Layer Tree和Render Object Tree.有了Render Object Tree之后,就可以执行具体的绘制工作了.接下来我们就分析网页CC Layer Tree的绘制过程. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! CC La

Chromium网页CPU光栅化原理分析

Chromium除了支持网页分块GPU光栅化,还支持CPU光栅化.GPU光栅化的特点是快,缺点是硬件差异可能会导差异性,以及不是所有的绘图操作硬件都能很好地支持.CPU光栅化的特点是通用,以及能够支持所有的绘图操作,缺点是较慢,特别是在网页使用硬件加速渲染的情况下,CPU的光栅化结果还需要上传到GPU去渲染.本文接下来将详细分析CPU光栅化的原理,着重描述它是如何快速地光栅化结果上传到GPU去的. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 从前面Ch