Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Chromium網頁輸入事件捕捉和手勢檢測過程分析

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

編輯:關於Android編程

連續的輸入事件可能會產生一定的手勢操作,例如滑動手勢和捏合手勢。在Chromium中,網頁的輸入事件是在Browser進程中捕捉的。Browser進程捕獲輸入事件之後,會進行手勢操作檢測。檢測出來的手勢操作將會發送給Render進程處理,因為它們需要應用在網頁之上。與此同時,Browser進程也會將原始的輸入事件發送給Render進程處理。本文接下來就分析Browser進程處理網頁輸入事件的過程。

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

\

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

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(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 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(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(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 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消息的過程,敬請關注!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved