在上一篇博文中介绍了WebContents类所包含的各个模块类以及它们的组织结构,这篇博文的内容是介绍一个url的Navigation过程。先看一个调用的关系图。
NavigationControllerImpl
LoadURLWithParams函数中进一步将Params构造成一个NavigationEntryImpl的实例entry,进而调用LoadEntry。LoadEntry主要进行两个操作,SetPendingEntry先将原有的pending_entry_置空,原因是之后需要把当前的entry设为pending_entry_,如果老的pending_entry_没有被committed,则没有意义了;NavigateToPendingEntry将调用WebContentsImpl的NavigateToPendingEntry完成接下去的操作。
就如前文提到的,NavigationControllerImpl主要控制用户对网页的访问逻辑,包括前进、后退和Reload还有直接在地址栏输入URL,这分别在GoForward、GoBack、Reload、LoadUrl函数中实现相应的操作。NavigationController维护数组entries_,当一个新entry被渲染进程commit,NavigationController收到消息DidCommitProvisionalLoad后调用RendererDidNavigate函数,将entry增加到entries_中,并更新last_committed_entry_index_。
NavigationControllerImpl有一类比较特别的entry---transient_entry,这种entry在interstitial page显示时被添加,其中interstitial page是一种比较特别的page,它是在我们进入到目标页面之前需要进行用户验证的页面,常见的例子就是公司的内网认证,当你打开目录页面前会进入验证页面,当正确输入内网密码后会正确定向到目标页面。transient_entry在entries_中只会保存一个,它并不是用户通常想访问的页面,所以在GoForward、GoBack通常并不会考虑该entry,但是在Reload时这个entry是有意义的,如果当前显示的是一个interstitial page,那么Reload会导向该entry对应的url。
WebContents
WebContents::NavigateToPendingEntry进一步调用frame_tree_根节点navigator的NavigateToPendingEntry函数。
NavigatorImpl
主要有两步:
1、调用当前节点上的RenderFrameHostManager的Navigate函数,返回RenderFrameHostImpl的实例dest_render_frame_host。
2、调用dest_render_frame_host的Navigate函数。
RenderFrameHostManager
Navigate函数最重要的就得到一个RenderFrameHostImpl用于对当前url进行导航,在UpdateRendererStateForNavigate函数中会根据需要调用CreateRenderFrame函数建立相应的RenderFrameHostImpl。
1、首先判断是否需要切换BrowsingInstances,由函数ShouldSwapBrowsingInstancesForNavigation实现。
2、为新entry生成一个SiteInstance实例new_instance。如果需要切换BrowsingInstances,调用CreateForURL生成SiteInstance,否则会调用GetRelatedSiteInstance生成SiteInstance,这两个函数的差别在于是否复用原有的BrowsingInstances,如果新entry的url和current_entry的相同,那么就复用current_entry的site_instance。site_instance会决定在哪一个渲染进程进行navigation。
3、如果new entry和current entry对应的site_instance不一致,会调用CreateRenderFrame函数,会为new_instance生成一个pending_render_frame_host_,CreateRenderFrame首先判断new_instance是否存在一个换出的RenderFrameHost,如果存在,那么会复用它;如果不存在会调用CreateRenderFrameHost函数,新建一个RenderFrameHost。
CreateRenderFrameHost函数首先生成RenderViewHost的一个对象render_view_host,然后再以render_view_host为参数生成RenderFrameHost,因为RenderFrameHost里边包含了RenderViewHost。
如果new entry和current entry的BrowsingInstances一致,就充许跨渲染进程的js调用,这时调用WebContents::CreateOpenerRenderViewsForRenderManager函数,递归地调用该标签页opener链上WebContents的CreateOpenerRenderViews函数,为给定的SiteInstance生成换出(swapped out)的RenderViews,允许标签页对它的opener进行跨进程的js调用。
opener是指在一个标签页a上调用Create函数新建一个标签页b时,会调用CreateWithOpener将标签页a设置为标签页b的opener,opener保存在WebContents:opener_属性,可以通过opener_递归地访问opener链。
经过上述3个步骤会返回一个RenderFrameHostImpl的实例dest_render_frame_host。
如果dest_render_frame_host对应的RenderView并没有创建,那么调用WebContents::CreateRenderViewForRenderManager函数创建它。在RenderViewHostImpl::CreateRenderView函数里发送消息ViewMsg_New到渲染进程。
RenderFrameHostImpl
在RenderFrameHostManager创建完RenderFrameHostImpl的实例dest_render_frame_host后,NavigatorImpl调用dest_render_frame_host的Navigate函数,在该函数中,RenderFrameHostImpl发送消息FrameMsg_Navigate完成导航过程。