Chromium网页滑动和捏合手势处理过程分析.docx
《Chromium网页滑动和捏合手势处理过程分析.docx》由会员分享,可在线阅读,更多相关《Chromium网页滑动和捏合手势处理过程分析.docx(38页珍藏版)》请在冰豆网上搜索。
Chromium网页滑动和捏合手势处理过程分析
Chromium网页滑动和捏合手势处理过程分析
从前面一文可以知道,Chromium的Browser进程从Touch事件中检测到滑动和捏合手势之后,就会将它们发送给Render进程处理。
滑动手势对应于修改网页的Viewport,而捏合手势对应于设置网页的缩放因子。
通常我们比较两个浏览器的流畅程度,就是比较它们的滑动和捏合性能。
因此,浏览器必须要快速地响应用户的滑动和捏合手势。
本文接下来就详细分析Chromium快速响应网页滑动和捏合手势的过程。
从前面文章中可以知道,Browser进程是通过一个类型为InputMsg_HandleInputEvent的IPC消息将网页的滑动和捏合手势发送给Render进程处理的。
Render进程通过一个InputEventFilter截获这两个手势操作,并且分发给Compositor线程处理,如图1所示:
从前面文章中这个系列的文章可以知道,Compositor线程为网页维护了一个CCActiveLayerTree。
这个CCActiveLayerTree描述的是网页当前正在显示的内容。
由于滑动和捏合手势并没有改变网页的内容,仅仅是改变了它的位置和缩放因子,因此Compositor线程会将滑动和捏合手势直接应用在CCActiveLayerTree。
这样就可以快速地响应用户的滑动和捏合操作了。
将滑动和捏合手势应用在网页的CCActiveLayerTree之后,就会造成它与网页的CCLayerTree不一致。
通常情况下,我们都是将CCLayerTree的变化同步到CCActiveLayerTree中去的。
但是在网页被滑动和捏合的情况下,就刚好反过来,我们需要将CCActiveLayerTree的变化同步到CCLayerTree中去,以维持两个Tree的一致性。
那么,CCActiveLayerTree的变化什么时候会同步到CCLayerTree去的呢?
Compositor线程将滑动和捏合手势应用在CCActiveLayerTree之后,会执行两个操作。
第一个操作是请求马上对CCActiveLayerTree进行渲染。
第二个操作是请求执行下一次Commit,这将会触发CCLayerTree被重新绘制。
CCLayerTree在重新绘制的过程中,就会将之前应用在CCActiveLayerTree上的滑动和捏合操作也应用在CCLayerTree上,从而可以将CCActiveLayerTree的变化同步到CCLayerTree中去。
接下来,我们就从Render进程截获Browser进程发送过来的输入事件开始,分析它快速响应网页的滑动和捏合手势的过程。
从前面文章中一文可以知道,Render进程在加载网页之后,会通过调用RenderThreadImpl类的成员函数EnsureWebKitInitialized初始化WebKit,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidRenderThreadImpl:
:
EnsureWebKitInitialized(){
......
boolenable=command_line.HasSwitch(switches:
:
kEnableThreadedCompositing);
if(enable){
......
InputHandlerManagerClient*input_handler_manager_client=NULL;
......
if(!
input_handler_manager_client){
input_event_filter_=
newInputEventFilter(this,compositor_message_loop_proxy_);
AddFilter(input_event_filter_.get());
input_handler_manager_client=input_event_filter_.get();
}
input_handler_manager_.reset(
newInputHandlerManager(compositor_message_loop_proxy_,
input_handler_manager_client));
}
......
}
这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。
在初始化WebKit的过程中,RenderThreadImpl类的成员函数EnsureWebKitInitialized将会创建一个InputEventFilter截获Browser进程发送过来的网页输入事件。
这个InputEventFilter保存在RenderThreadImpl类的成员变量input_event_filter_中。
接下来,我们就分析这个InputEventFilter的创建过程。
InputEventFilter是用来将滑动和捏合手势分发给Render进程的Compositor线程处理的,因此只有Render进程存在Compositor线程的情况下,才会创建InputEventFilter。
当Render进程指定了switches:
:
kEnableThreadedCompositing(即enable-threaded-compositing)启动选项时,Render进程就会存在Compositor线程。
这时候RenderThreadImpl类的成员函数EnsureWebKitInitialized将会创建一个InputEventFilter。
创建出来的InputEventFilter保存在RenderThreadImpl类的成员变量input_event_filter_中,并且在创建的时候,需要指定一个Compositor线程消息循环代理对象。
这个消息循环代理对象由RenderThreadImpl类的另外一个成员变量compositor_message_loop_proxy_描述。
接下来我们分析InputEventFilter类的构造函数的实现,以便了解InputEventFilter的创建过程,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
InputEventFilter:
:
InputEventFilter(
IPC:
:
Listener*main_listener,
constscoped_refptr:
MessageLoopProxy>&target_loop)
:
main_loop_(base:
:
MessageLoopProxy:
:
current()),
main_listener_(main_listener),
......,
target_loop_(target_loop),
......{
......
}
这个函数定义在文件external/chromium_org/content/renderer/input/input_event_filter.cc中。
从前面的调用过程可以知道,参数main_listener指向的是一个RenderThreadImpl对象,它将会保存在InputEventFilter类的成员变量main_listener_中。
以后Compositor线程会通过这个RenderThreadImpl对象将截获到的其它输入事件转发给Main线程处理。
此外,参数target_loop描述的Compositor线程消息循环代理对象将会保存在InputEventFilter类的另外一个成员变量target_loop_。
以后InputEventFilter就会通过这个消息循环代理对象将滑动和捏合手势分发给Compositor线程处理。
InputEventFilter类的构造函数还是调用base:
:
MessageLoopProxy类的静态成员函数current获得当前线程(Main线程)的消息循环代理对象,并且将这个消息循环代理对象保存在InputEventFilter类的成员变量main_loop_。
以后Compositor线程也会通过这个消息循环代理对象将截获到的其它输入事件转发给Main线程处理。
回到RenderThreadImpl类的成员函数EnsureWebKitInitialized中,它创建了一个InputEventFilter之后,接着又要这个InputEventFilter以及Compositor线程的消息循环代理对象创建一个InputHandlerManager对象,并且保存在RenderThreadImpl类的成员变量input_handler_manager_中。
接下来我们继续分析InputHandlerManager对象的创建过程,也就是InputHandlerManager类的构造函数的实现,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
InputHandlerManager:
:
InputHandlerManager(
constscoped_refptr:
MessageLoopProxy>&message_loop_proxy,
InputHandlerManagerClient*client)
:
message_loop_proxy_(message_loop_proxy),
client_(client){
DCHECK(client_);
client_->SetBoundHandler(base:
:
Bind(&InputHandlerManager:
:
HandleInputEvent,
base:
:
Unretained(this)));
}
这个函数定义在文件external/chromium_org/content/renderer/input/input_handler_manager.cc中。
InputHandlerManager类的构造函数首先分别将参数message_loop_proxy描述的Compositor线程消息循环代理对象和参数client描述的InputEventFilter保存成员变量message_loop_proxy_和client_中。
InputHandlerManager类的构造函数接下来又调用参数client描述的InputEventFilter的成员函数SetBoundHandler,用来给后者设置一个Handler,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidInputEventFilter:
:
SetBoundHandler(constHandler&handler){
......
handler_=handler;
}
这个函数定义在文件external/chromium_org/content/renderer/input/input_event_filter.cc中。
从前面的调用过程可以知道,参数handler描述的Hanlder绑定了InputHandlerManager类的成员函数HandleInputEvent,它被保存在InputEventFilter类的成员变量handler_中。
这个Handler以后用来处理网页的滑动和捏合手势。
从前面文章中一文可以知道,网页的CCLayerTree是在RenderViewImpl类的成员函数initializeLayerTreeView中创建的。
创建之后,RenderViewImpl类的成员函数initializeLayerTreeView将会获得一个InputHandler。
这个InputHandler将会注册到前面创建的InputHandlerManager中,用来将网页滑动和捏合手势操作应用在网页的CCActiveLayerTree中。
接下来,我们就继续分析上述InputHandler的获取和注册过程,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidRenderViewImpl:
:
initializeLayerTreeView(){
RenderWidget:
:
initializeLayerTreeView();
RenderWidgetCompositor*rwc=compositor();
......
#if!
defined(OS_MACOSX)//manyeventsareunhandled-
RenderThreadImpl*render_thread=RenderThreadImpl:
:
current();
//render_threadmaybeNULLintests.
InputHandlerManager*input_handler_manager=
render_thread?
render_thread->input_handler_manager():
NULL;
if(input_handler_manager){
input_handler_manager->AddInputHandler(
routing_id_,rwc->GetInputHandler(),AsWeakPtr());
}
#endif
}
这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。
网页的CCLayerTree是通过调用RenderViewImpl的父类RenderWidget的成员函数initializeLayerTreeView创建的。
在创建CCLayerTree的过程中,将会创建一个RenderWidgetCompositor对象。
这个RenderWidgetCompositor对象可以通过调用RenderViewImpl类的成员函数compositor获得。
同时,调用这个RenderWidgetCompositor对象的成员函数GetInputHandler,可以获得一个InputHandler,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
constbase:
:
WeakPtr:
InputHandler>&
RenderWidgetCompositor:
:
GetInputHandler(){
returnlayer_tree_host_->GetInputHandler();
}
这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。
从前面文章中一文可以知道,RenderWidgetCompositor类的成员变量layer_tree_host_指向的是一个LayerTreeHost对象。
这个LayerTreeHost对象负责管理网页的CCLayerTree。
RenderWidgetCompositor类的成员函数GetInputHandler调用这个LayerTreeHost对象的成员函数GetInputHandler获得一个InputHandler,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classCC_EXPORTLayerTreeHost{
public:
......
LayerTreeHostClient*client(){returnclient_;}
constbase:
:
WeakPtr&GetInputHandler(){
returninput_handler_weak_ptr_;
}
......
private:
......
base:
:
WeakPtrinput_handler_weak_ptr_;
......
};
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.h中。
从这里可以看到,LayerTreeHost类的成员函数GetInputHandler返回的是成员变量input_handler_weak_ptr_指向的一个InputHandler。
这个InputHandler的创建过程如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
scoped_ptrLayerTreeHost:
:
CreateLayerTreeHostImpl(
LayerTreeHostImplClient*client){
......
scoped_ptrhost_impl=
LayerTreeHostImpl:
:
Create(settings_,
client,
proxy_.get(),
rendering_stats_instrumentation_.get(),
shared_bitmap_manager_,
id_);
......
input_handler_weak_ptr_=host_impl->AsWeakPtr();
returnhost_impl.Pass();
}
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。
LayerTreeHost类的成员函数CreateLayerTreeHostImpl的调用过程可以参考前面文章中一文。
从这里我们可以看到,LayerTreeHost类的成员变量input_handler_weak_ptr_指向的实际上是一个LayerTreeHostImpl对象。
这个LayerTreeHostImpl对象负责管理网页的CCPendingLayerTree和CCActiveLayerTree。
回到RenderViewImpl类的成员函数initializeLayerTreeView中,它获得了一个类型为LayerTreeHostImpl的InputHandler之后,会将它注册到前面创建的InputHandlerManager中,这是通过调用InputHandlerManager类的成员函数AddInputHandler进行的,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidInputHandlerManager:
:
AddInputHandler(
introuting_id,
constbase:
:
WeakPtr:
InputHandler>&input_handler,
constbase:
:
WeakPtr&render_view_impl){
if(message_loop_proxy_->BelongsToCurrentThread()){
AddInputHandlerOnCompositorThread(routing_id,
base:
:
MessageLoopProxy:
:
current(),
input_handler,
render_view_impl);
}else{
message_loop_proxy_->PostTask(
FROM_HERE,
base:
:
Bind(&InputHandlerManager:
:
AddInputHandlerOnCompositorThread,
base:
:
Unretained(this),
routing_id,
base:
:
MessageLoopProxy:
:
current(),
input_handler,
render_view_impl));
}
}
这个函数定义在文件external/chromium_org/content/renderer/input/input_handler_manager.cc中。
从前面的分析可以知道,InputHandlerManager类的成员变量message_loop_proxy_描述的是Compositor线程的消息循环代理对象。
通过这个消息循环代理对象,InputHandlerManager类的成员函数AddInputHandler可以判断当前线程是否是Compositor线程。
如果是的话,就直接调用InputHandlerManager类的成员函数AddInputHandlerOnCompositorThread将参数input_handler描述的类型为LayerTreeHostImpl的InputHandler注册在内部。
否则的话,就会向Compositor线程发送一个Task,这个Task绑定了InputHandlerManager类的成员函数AddInputHandlerOnCompositorThread。
这意味着接下来InputHandlerManager类的成员函数AddInputHandlerOnCompositorThread也会在Compositor线程中被调用。
InputHandlerManager类的成员函数AddInputHandlerOnCompositorThread的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidInputHandlerManager:
:
AddInputHandlerOnCompositorThread(
introuting_id,
constscoped_refptr:
MessageLoopProxy>&main_loop,
constbase:
:
WeakPtr:
InputHandler>&input_handler,
constbase:
:
WeakPtr&render_view_impl){
......
input_handlers_.add(routing_id,
make_scoped_ptr(newInputHandlerWrapper(this,
routin