Chromium输入事件处理流程

本文主要讲解Chromium输入事件的处理过程,在Android平台上,当我们在网页上点击一个HTML元素时,系统会产生一个TouchEvent分发给WebView,WebView将该事件传递到Chromium中,Chromium会对该事件进行一系列的处理,最后分发到WebKit中,WebKit则根据输入事件发生的位置在网页中找到对应的HTML元素进行处理。在整个处理过程中,网页的输入事件是在Browser进程中捕捉的,Browser进程捕获输入事件之后,会将事件发送给Render进程处理。接下来,我们就以TouchEvent为例子,详细讲解事件在Chromium和WebKit中的处理流程。

我们从WebView::onTouchEvent开始分析,调用栈比较简单,如下所示:

WebView::onTouchEvent
WebViewChromium::onTouchEvent
AwContents::onTouchEvent
ContentViewCore::onTouchEvent
ContentViewCore::onTouchEventImpl

这是一个比较常见的调用流程,WebView的api基本上都是这样调用到Chromium里面的,所以我们就不拆开分析了,直接看ContentViewCore::onTouchEventImpl的实现:

 private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) {
        try {
            ......
            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.getTouchMinor(), pointerCount > 1 ? event.getTouchMinor(1) : 0,
                    event.getOrientation(), pointerCount > 1 ? event.getOrientation(1) : 0,
                    event.getRawX(), event.getRawY(),
                    event.getToolType(0),
                    pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
                    event.getButtonState(),
                    event.getMetaState(),
                    isTouchHandleEvent);
            ......
        } finally {
            TraceEvent.end("onTouchEvent");
        }
    }

在ContentViewCore::onTouchEventImpl中,通过JNI调用到native层。

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 touch_minor_0,
                                           jfloat touch_minor_1,
                                           jfloat orientation_0,
                                           jfloat orientation_1,
                                           jfloat raw_pos_x,
                                           jfloat raw_pos_y,
                                           jint android_tool_type_0,
                                           jint android_tool_type_1,
                                           jint android_button_state,
                                           jint android_meta_state,
                                           jboolean is_touch_handle_event) {
  RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
  ......

  return is_touch_handle_event ? rwhv->OnTouchHandleEvent(event)
                               : rwhv->OnTouchEvent(event);
}
is_touch_handle_event是从java层的ContentViewCore::onTouchEvent传下来的,为false,所以调用的是RenderWidgetHostViewAndroid:: OnTouchEvent。
bool RenderWidgetHostViewAndroid::OnTouchEvent(
    const ui::MotionEvent& event) {
  ......

  blink::WebTouchEvent web_event =
      ui::CreateWebTouchEventFromMotionEvent(event, result.did_generate_scroll);
  host_->ForwardTouchEventWithLatencyInfo(web_event, ui::LatencyInfo());

  ......
  return true;
}

在RenderWidgetHostViewAndroid:: OnTouchEvent中,先调用函数CreateWebTouchEventFromMotionEvent将原始的Touch事件封装在一个blink::WebTouchEvent对象中,而host描述的是一个RenderWidgetHostImpl对象,所以接下来执行的是RenderWidgetHostImpl:: ForwardTouchEventWithLatencyInfo。

void RenderWidgetHostImpl::ForwardTouchEventWithLatencyInfo(
      const blink::WebTouchEvent& touch_event,
      const ui::LatencyInfo& ui_latency) {
  ......

  TouchEventWithLatencyInfo touch_with_latency(touch_event, ui_latency);
  ......
  input_router_->SendTouchEvent(touch_with_latency);
}

在RenderWidgetHostImpl:: ForwardTouchEventWithLatencyInfo中,首先将参数touch_event描述的Touch事件封装在一个TouchEventWithLatencyInfo对象中,然后再InputRouterImpl::SendTouchEvent将这个TouchEventWithLatencyInfo对象发送给Render进程

void InputRouterImpl::SendTouchEvent(
    const TouchEventWithLatencyInfo& touch_event) {
  input_stream_validator_.Validate(touch_event.event);
  touch_event_queue_.QueueEvent(touch_event);
}

在InputRouterImpl::SendTouchEvent中,touch_event_queue_描述的是一个TouchEventQueue对象,通过TouchEventQueue:: QueueEvent调用将参数touch_event描述的Touch事件发送给Render进程。

void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
  TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");

  // 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_) {
    // Optimization of the case without touch handlers.  Removing this path
    // yields identical results, but this avoids unnecessary allocations.
    PreFilterResult filter_result = FilterBeforeForwarding(event.event);
    if (filter_result != FORWARD_TO_RENDERER) {
      client_->OnTouchEventAck(event,
                               filter_result == ACK_WITH_NO_CONSUMER_EXISTS
                                   ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
                                   : INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
      return;
    }

    // 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));
}
在TouchEventQueue::QueueEvent中,touch_queue_描述的是一个Touch事件队列,这个队列用来暂存即将要发送给Render进程的Touch事件。以下两种情况下,需要将Touch事件暂存在队列中:
1. Touch事件队列touch_queue_不为空,也就是说之前的Touch事件还未发送给Render进程;
2. Render进程发送了一个ACK事件给Browser进程,Browser进程正在分发这个ACK事件,只有当这个ACK事件分发完成之后,Browser进程才可以处理下一个Touch事件,这种情况下,成员变量dispatching_touch_ack_的值就为true,表明当前正在分发ACK事件。
总体上来讲,TouchEventQueue::QueueEvent这个函数的作用就是判断当前这个Touch事件是否可以马上发送。
如果能马上发送,则将它保存在touch_queue_中,然后调用ForwardNextEventToRenderer将它从touch_queue_读取出来并且发送给Render进程。
如果不能马上发送,则同样将它保存在touch_queue_中,但是要等上一个发送给Render进程的Touch事件有ACK之后,才能继续将它发送给Render进程。

不管是什么情况,最终都是通过调用ForwardNextEventToRenderer进行处理的,所以接下来看该函数的实现:

void TouchEventQueue::ForwardNextEventToRenderer() {

  TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
  ......

  SendTouchEventImmediately(&touch);
}

在TouchEventQueue::ForwardNextEventToRenderer 中,首先从Touch事件队列中取出第一个Touch事件,然后调用SendTouchEventImmediately发送该事件。

void TouchEventQueue::SendTouchEventImmediately(
    TouchEventWithLatencyInfo* touch) {
  ......

  base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
  client_->SendTouchEventImmediately(*touch);

  ......
}

在TouchEventQueue::SendTouchEventImmediately中,client_描述的是前面有提到的InputRouterImpl对象,在调用InputRouterImpl:: SendTouchEventImmediately进行下一步处理之前,还将dispatching_touch则为true,表明当前正在发送一个Touch事件,在发送结束后,会恢复为false。

void InputRouterImpl::SendTouchEventImmediately(
    const TouchEventWithLatencyInfo& touch_event) {
  ......

  FilterAndSendWebInputEvent(touch_event.event, touch_event.latency);
}
在InputRouterImpl::SendTouchEventImmediately中,主要调用了另一个成员函数FilterAndSendWebInputEvent进行处理:

void InputRouterImpl::FilterAndSendWebInputEvent(
    const WebInputEvent& input_event,
    const ui::LatencyInfo& latency_info) {
  ......

  OfferToHandlers(input_event, latency_info);
}

在InputRouterImpl::FilterAndSendWebInputEvent中,调用了另一个成员函数OfferToHandlers将参数input_event描述的Touch事件发送给Render进程。

void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event,
                                      const ui::LatencyInfo& latency_info) {
  ......
  if (OfferToClient(input_event, latency_info))
    return;

  OfferToRenderer(input_event, latency_info);

  .....
}

在InputRouterImpl::OfferToHandlers中,先调用OfferToClient判断是否需要过滤该Touch事件,如果不需要过滤,则调用OfferToRenderer进行发送。

bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
                                      const ui::LatencyInfo& latency_info) {
  if (Send(new InputMsg_HandleInputEvent(routing_id(), &input_event,
                                         latency_info))) {
    ......
    return true;
  }
  return false;
}
在InputRouterImpl::OfferToRenderer中,将Touch事件input_event封装在一个IPC消息InputMsg_HandleInputEvent中,然后发送给Render进程处理。
bool RenderWidget::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(RenderWidget, message)
    IPC_MESSAGE_HANDLER(InputMsg_HandleInputEvent, OnHandleInputEvent)
    ......
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

InputMsg_HandleInputEvent消息最终由RenderWidget::OnMessageReceived接收处理,从该函数中可以看到,该消息会分发给OnHandleInputEvent处理。

void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event,
                                      const ui::LatencyInfo& latency_info) {
  ......

  bool prevent_default = false;
  if (WebInputEvent::isMouseEventType(input_event->type)) {
    ......
    prevent_default = WillHandleMouseEvent(mouse_event);
  }

  if (WebInputEvent::isKeyboardEventType(input_event->type)) {
    context_menu_source_type_ = ui::MENU_SOURCE_KEYBOARD;
#if defined(OS_ANDROID)
    const WebKeyboardEvent& key_event =
        *static_cast<const WebKeyboardEvent*>(input_event);
    if (key_event.nativeKeyCode == AKEYCODE_DPAD_CENTER &&
        GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) {
      OnShowImeIfNeeded();
      prevent_default = true;
    }
#endif
  }

  if (WebInputEvent::isGestureEventType(input_event->type)) {
    const WebGestureEvent& gesture_event =
        *static_cast<const WebGestureEvent*>(input_event);
    context_menu_source_type_ = ui::MENU_SOURCE_TOUCH;
    prevent_default = prevent_default || WillHandleGestureEvent(gesture_event);
  }

  bool processed = prevent_default;
  if (input_event->type != WebInputEvent::Char || !suppress_next_char_events_) {
    suppress_next_char_events_ = false;
    if (!processed && webwidget_)
      processed = webwidget_->handleInputEvent(*input_event);
  }
  ......
  InputEventAckState ack_result = processed ?
      INPUT_EVENT_ACK_STATE_CONSUMED : INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
  ......

  bool rate_limiting_wanted =
      input_event->type == WebInputEvent::MouseMove ||
      input_event->type == WebInputEvent::MouseWheel;
  ......

  bool no_ack = ignore_ack_for_mouse_move_from_debugger_ &&
      input_event->type == WebInputEvent::MouseMove;
  if (WebInputEventTraits::WillReceiveAckFromRenderer(*input_event) &&
      !no_ack) {
    ......
    scoped_ptr<IPC::Message> response(
        new InputHostMsg_HandleInputEvent_ACK(routing_id_, ack));
    if (rate_limiting_wanted && frame_pending && !is_hidden_) {
      if (pending_input_event_ack_) {
        Send(pending_input_event_ack_.release());
      }
      pending_input_event_ack_ = response.Pass();
    } else {
      Send(response.release());
    }
  }
  ......
}
RenderWidget:: OnHandleInputEvent的处理过程比较复杂,在将Touch事件分发给WebKit之前,会先做以下判断:
1、 检查input_event是否一个鼠标事件,如果是的话,会调用WillHandleMouseEvent确认是否要对该事件进行处理;
2、 如果是Android平台,检查input_event是否一个DPAD_CENTER键盘事件,如果是的话,并且当前焦点是一个TEXT_INPUT_TYPE_NONE输入控件,那么就调用OnShowImeIfNeeded弹窗输入法;
3、 检查参数input_event是否是一个手势操作,如果是的话,则调用WillHandleGestureEvent确认是否要对该事件进行处理。
如果查看RenderWidget 的成员函数WillHandleMouseEvent和WillHandleGestureEvent就可以知道,这两个函数其实都是直接返回false,则说明RenderWidget不会处理鼠标和手势操作两种输入事件。
我们这里分析的TouchEvent,所以经过上面三个判断之后,prevent_default还是为false,所以接下来会调用webwidget_->handleInputEvent(*input_event)将该事件分发给WebKit处理。
另一方面,在将事件分发给WebKit之后,还要考虑是否需要给Browser进程发送一个ACK,前面我们有提到Browser进程收到Render进程对上一个输入事件的ACK之后,才会给它分发下一个输入事件。因此,Render进程是否给Browser进程发送ACK,可以用来控制Browser进程分发输入事件给Render进程的节奏。
从代码中可以看到,如果是MouseMove或MouseWheel事件,rate_limiting_wanted为true,说明需要控制分发节奏,不会马上进行ACK,因为这类事件可能会大量连续输入,如果每次Render进程都对他们作出响应,那么Render进程负担会很重。
如果是本例中的TouchEvent,则会直接走到Send(response.release()),response为InputHostMsg_HandleInputEvent_ACK,也就是会马上进行ACK。

接下来继续看WebKit里面的处理:WebViewImpl::handleInputEvent

bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent)
{
    ......
    // FIXME: This should take in the intended frame, not the local frame root.
    if (PageWidgetDelegate::handleInputEvent(*this, inputEvent, mainFrameImpl()->frame()))
        return true;

    return false;
}

在WebViewImpl::handleInputEvent中,对于Touch事件,会调用PageWidgetDelegate的静态成员函数handleInputEvent进行处理。

bool PageWidgetDelegate::handleInputEvent(PageWidgetEventHandler& handler, const WebInputEvent& event, LocalFrame* root)
{
    switch (event.type) {
    ......
    case WebInputEvent::TouchStart:
    case WebInputEvent::TouchMove:
    case WebInputEvent::TouchEnd:
    case WebInputEvent::TouchCancel:
        if (!root || !root->view())
            return false;
        return handler.handleTouchEvent(*root, static_cast<const WebTouchEvent&>(event));
    ......
    default:
        return false;
    }
}

PageWidgetDelegate::handleInputEvent会对事件类型进行判断,如果是Touch类型的则继续调用handler.handleTouchEvent,handler是一个WebViewImpl对象,由上一次调用传进来的,WebViewImpl继承了PageWidgetEventHandler,handleTouchEvent函数也是直接从父类继承下来的。

bool PageWidgetEventHandler::handleTouchEvent(LocalFrame& mainFrame, const WebTouchEvent& event)
{
    return mainFrame.eventHandler().handleTouchEvent(PlatformTouchEventBuilder(mainFrame.view(), event));
}
mainFrame是一个LocalFrame对象,该对象是用来在WebKit层描述在当前进程中加载的网页,这里首先调用了LocalFrame的成员函数eventHandler得到一个EventHandler对象,接着调用了EventHandler:: handleTouchEvent

进行处理。

bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
{
    const Vector<PlatformTouchPoint>& points = event.touchPoints();
    ......
    // First do hit tests for any new touch points.
    for (unsigned i = 0; i < points.size(); ++i) {
        const PlatformTouchPoint& point = points[i];
        if (point.state() == PlatformTouchPoint::TouchPressed) {
            HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent | HitTestRequest::ReadOnly | HitTestRequest::Active;
            LayoutPoint pagePoint = roundedLayoutPoint(m_frame->view()->rootFrameToContents(point.pos()));
            HitTestResult result;
            if (!m_touchSequenceDocument) {
                result = hitTestResultAtPoint(pagePoint, hitType);
            } else if (m_touchSequenceDocument->frame()) {
                LayoutPoint framePoint = roundedLayoutPoint(m_touchSequenceDocument->frame()->view()->rootFrameToContents(point.pos()));
                result = hitTestResultInFrame(m_touchSequenceDocument->frame(), framePoint, hitType);
            } else {
                continue;
            }

            Node* node = result.innerNode();
            ......
            if (!m_touchSequenceDocument) {
                m_touchSequenceDocument = &(result.innerNode()->document());
            }
            m_targetForTouchID.set(point.id(), node);

            TouchAction effectiveTouchAction = TouchActionUtil::computeEffectiveTouchAction(*node);
            if (effectiveTouchAction != TouchActionAuto)
                m_frame->page()->chromeClient().setTouchAction(effectiveTouchAction);
        }
    }
    ......

    bool swallowedTouchEvent = dispatchTouchEvents(event, touchInfos, freshTouchEvents,
        allTouchReleased);
    ......
    return swallowedTouchEvent;
}

EventHandler:: handleTouchEvent会对当前发生的Touch事件的每一个Touch Point进行Hit Test,分别找到它们的Target Node,然后再将Touch事件分发给Target Node处理。

这段代码首先获得参数event描述的Touch事件关联的所有Touch Point,保存在本地变量points描述的一个Vector中。每一个Touch Point都是一个PlatformTouchPoint对象,它有五种状态,正常情况下,一个Touch Point的状态变化过程为:TouchPressed->TouchMoved/TouchStationary->TouchReleased/TouchCancelled。

class PlatformTouchPoint {
public:
    enum State {
        TouchReleased,
        TouchPressed,
        TouchMoved,
        TouchStationary,
        TouchCancelled,
        TouchStateEnd // Placeholder: must remain the last item.
    };
    ......
}
回到上面的EventHandler:: handleTouchEvent中,遍历所有points中状态为TouchPressed的Touch Point,找到它们的Target Node,并将这些Node保存在m_targetForTouchID中,键值为Touch Point对应的ID,有了这个m_targetForTouchID,当一个Touch Point从TouchPressed状态变为其它状态时,就可以轻松地知道它的Target Node,避免做重复的Hit Test。
当一个Touch Event第一次做Hit Test时,m_touchSequenceDocument为Null,调用hitTestResultAtPoint做Hit Test,之后m_touchSequenceDocument不为Null,调用hitTestResultInFrame做Hit Test,后者会将Hit Test的范围限制在m_touchSequenceDocument指定的Document中,Hit Test的处理过程也非常复杂,没有详细研究所以就不展开分析了。

在遍历完points之后,会调用dispatchTouchEvents将Touch事件分发给前面找到的Target node处理。

bool EventHandler::dispatchTouchEvents(const PlatformTouchEvent& event,
    WillBeHeapVector<TouchInfo>& touchInfos, bool freshTouchEvents, bool allTouchReleased)
{
    .......
    for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) {
        if (!changedTouches[state].m_touches)
            continue;

        const AtomicString& eventName(touchEventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state)));
        const EventTargetSet& targetsForState = changedTouches[state].m_targets;
        for (const RefPtrWillBeMember<EventTarget>& eventTarget : targetsForState) {
            EventTarget* touchEventTarget = eventTarget.get();
            RefPtrWillBeRawPtr<TouchEvent> touchEvent = TouchEvent::create(
                touches.get(), touchesByTarget.get(touchEventTarget), changedTouches[state].m_touches.get(),
                eventName, touchEventTarget->toNode()->document().domWindow(),
                event.modifiers(), event.cancelable(), event.causesScrollingIfUncanceled(), event.timestamp());

            touchEventTarget->dispatchEvent(touchEvent.get());
            swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled();
        }
    }

    return swallowedEvent;
}
touchEventTarget是一个EventTarget对象,描述是前面找到的Target node,这里通过调用EventTarget:: dispatchEvent进行分发。
bool EventTarget::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
{
    event->setTrusted(true);
    return dispatchEventInternal(event);
}

bool EventTarget::dispatchEventInternal(PassRefPtrWillBeRawPtr<Event> event)
{
    event->setTarget(this);
    event->setCurrentTarget(this);
    event->setEventPhase(Event::AT_TARGET);
    bool defaultWasNotPrevented = fireEventListeners(event.get());
    event->setEventPhase(0);
    return defaultWasNotPrevented;
}

EventTarget:: dispatchEvent的调用过程如上所示,主要是通过fireEventListeners将Touch Event分发给Target Node的DOM Event Handler处理,DOM Event Handler指的是网页通过JS注册的Event处理相关的回调函数。

bool EventTarget::fireEventListeners(Event* event)
{
    EventTargetData* d = eventTargetData();
    if (!d)
        return true;

    EventListenerVector* legacyListenersVector = nullptr;
    AtomicString legacyTypeName = legacyType(event);
    if (!legacyTypeName.isEmpty())
        legacyListenersVector = d->eventListenerMap.find(legacyTypeName);

    EventListenerVector* listenersVector = d->eventListenerMap.find(event->type());

    if (listenersVector) {
        fireEventListeners(event, d, *listenersVector);
    } else if (legacyListenersVector) {
        AtomicString unprefixedTypeName = event->type();
        event->setType(legacyTypeName);
        fireEventListeners(event, d, *legacyListenersVector);
        event->setType(unprefixedTypeName);
    }

    Editor::countEvent(executionContext(), event);
    countLegacyEvents(legacyTypeName, listenersVector, legacyListenersVector);
    return !event->defaultPrevented();
}

在该函数中,通过Touch Event的类型在Target Node注册的DOM Event Handler(eventListenerMap)中找到对应的DOM Event Handler,然后再调用fireEventListeners的另一个重载函数进行处理。

void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry)
{
    ......
    size_t i = 0;
    size_t size = entry.size();
    if (!d->firingEventIterators)
        d->firingEventIterators = adoptPtr(new FiringEventIteratorVector);
    d->firingEventIterators->append(FiringEventIterator(event->type(), i, size));
    while (i < size) {
        RegisteredEventListener& registeredListener = entry[i];
        ++i;
        if (event->eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
            continue;
        if (event->eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
            continue;

        if (event->immediatePropagationStopped())
            break;

        ExecutionContext* context = executionContext();
        if (!context)
            break;
        ......
        registeredListener.listener->handleEvent(context, event);

        InspectorInstrumentation::didHandleEvent(cookie);
    }
    d->firingEventIterators->removeLast();
}
这些DOM Event Handler在注册的时候,会被JavaScript引擎封装在一个V8AbstractEventListener对象中。这里的registeredListener.listener 得到的就是一个V8AbstractEventListener对象,通过调用改对象的成员函数handleEvent将当前发生的Event分发给它们所封装的DOM Event Handler处理。这就会进入到JavaScript引擎里面去执行了。

整个Touch Event的分析过程就到此为了,其中有部分细节其实也还没有搞懂,所以也没法讲得很详细,只能大概把流程整理出来,后续有时间再仔细研究了。


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值