地址栏是如何与网页联动的
地址栏在chromium内核中只是一个虚拟的概念,通过Android的开发者模式来观察Chrome浏览器的地址栏我们会发现,地址栏是一个原生的控件,他与网页是无关的,chromium在内核中根据用户的滑动手势,动态的调整网页viewport的偏移同时将偏移位置通知到应用层,应用层基于此偏移动态的调整地址栏控件的偏移位置(margin or translation)从而达到地址栏与网页根据用户手势沉浸式联动的效果。
BrowserControlsOffsetManager是内核中地址栏概念的管理类,它的主要职责有:
1. 管理当前地址栏的模式(仅显示、仅隐藏、自动)
2. 根据网页滑动事件计算当前地址栏的偏移
3. 当用户手势抬起后决定是否启用动画展示或者隐藏地址栏
网页滑动时代码处理流程如下:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| Renderer |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
viewport.ScrollBy
|-->ShouldBrowserControlsConsumeScroll [If Result True]
|-->ScrollBrowserControls
|-->host_impl_->browser_controls_manager()->ScrollBy
|-->BrowserControlsOffsetManager.ScrollBy [Compute Scroll Delta For TopControls]
BrowserControlsOffsetManager依据网页滚动计算偏移用于:
1、通知偏移值到Browser,从而通知地址栏更新位置
2、记录偏移值,再松手后决定是显示还是隐藏地址栏
自动动画显示或者隐藏时代码处理流程如下:
收到滑动事件结束后BrowserControlsOffsetManager准备动画,在下一帧绘制时重新计算偏移
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| Renderer |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BrowserControlsOffsetManager.ScrollEnd
|-->StartAnimationIfNecessary
|-->SetupAnimation
在Render收到绘制指令时BrowserControlsOffsetManager通过动画插值器计算偏移,同时根据动画状态决定是否继续触发下一次绘制
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| Renderer |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SynchronousCompositorProxy.BeginFrame
|-->SynchronousLayerTreeFrameSink.BeginFrame
|-->viz::ExternalBeginFrameSource[begin_frame_source.cc].onBeginFrame
|-->FilterAndIssueBeginFrame
|-->BeginFrameObserver.OnBeginFrame
|-->BeginFrameObserBase[extends BeginFrameObserver].OnBeginFrame
|-->BeginFrameObserBase[begin_frame_source.cc].OnBeginFrameDerivedImpl
|-->Scheduler[extends BeginFrameObserverBase].OnBeginFrameDerivedImpl
|-->BeginImplFrameSynchronous
|-->BeginImplFrame
|-->SchedulerClient.WillBeginImplFrame
|-->ProxyImpl[extends SchedulerClient].WillBeginFrame
|-->LayerTreeHostImpl.WillBeginImplFrame
|-->Animate
|-->AnimateInternal
|-->AnimateBrowserControls
|-->BrowserControlsOffsetManager.Animat [计算偏移]
|-->viewport.ScrollBy [滚动网页窗口]
基于Chromium内核实现一个地址栏
前面说了BrowserControlsOffsetMnanager管理了地址栏的偏移,那么他是如何通知到Android层的呢,代码流程如下:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| Renderer |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SynchronousCompositorProxy.DemandDrawHwAsync [Browser发起绘制]
|-->DemandDraw
|-->SynchronousLayerTreeFrameSink.DemandDrawHw
|-->InvokeComposite
|-->LayerTreeFrameSinkClient.onDraw
|-->LayerTreeHostImpl[extends LayerTreeFrameSinkClient].onDraw
|-->LayerTreeHostImplClient.OnDrawForLayerTreeFrameSink
|-->ProxyImpl[extends LayerTreeHostImplClient].OnDrawForLayerTreeFrameSink
|-->Scheduler.OnDrawForLayerTreeFrameSink
|-->OnBeginImplFrameDeadline
|-->ProcessScheduledActions
|-->DrawIfPossible
|-->SchedulerClient.ScheduledActionDrawIfPossible
|-->ProxyImpl[extends SchedulerClient].ScheduledActionDrawIfPossible
|-->DrawInternal
|-->LayerTreeHostImpl.DrawLayers
|-->GenerateCompositorFrame
|-->MakeRenderFrameMetadata [设置最新的地址栏偏移参数]
|-->RenderFrameMetadataObserver.OnRenderFrameSubmission
|-->RenderFrameMetadataObserverImpl[extends RenderFrameMetadataObserver].OnRenderFrameSubmission
|-->cc::mojom::blink::RenderFrameMetadataObserverClient.OnRenderFrameMetadataChanged [IPC调用,通知Browser] ----|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| Browser |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
RenderFrameMetadataProviderImpl.OnRenderFrameMetadataChanged ---------------------------------mojom-----------------------------------------|
|-->RenderFrameMetadataProvider::Observer.OnRenderFrameMetadataChangedBeforeActivation
|-->RenderWidgetHostViewAndroid[extends RenderFrameMetadataProvider::Observer].OnRenderFrameMetadataChangedBeforeActivation
|-->UpdateControls [按需更新地址栏偏移]
|-->ViewAndroid.OnTopControlsChanged
|-->Java_ViewAndroidDelegate_onTopControlsChanged
|-->ViewAndroidDelegate.onTopControlsChanged [通知到Java端]
因此想要实现一个自己的地址栏效果,我们只需要根据ViewAndroidDelgate中的onTopControlsChanged偏移参数,动态的调整地址栏的偏移
至此,我们了解了地址栏的生效流程,并且也可以基于此完成一个基于chromium内核的定制化地址栏,需要注意一点的是它的设计仅仅是与chromium或者chrome的整体架构兼容的,原生的地址栏偏移通知是滞后一帧的,针对这种情况我们只需要在做偏移的时候,从内核中同步取出准确的地址栏偏移即可,同时还需要处理好事件分发时的坐标偏移。