WebKit历史项管理的实现

历史项管理依据标准定义,由Page管理一个Joint Session History, 包括了各个子Frame的历史项。逻辑上相应例如如下的关系:

从上面看三个层次:Page,Frame,以及JS Binding的接口。页面载入的核心是由Frame经过FrameLoader来完毕的,HistoryController及BackForwardController可以视为页面载入进行历史项操做的接口。 Frame层次中经过HistoryController, Page层次中经过BackForwardController进行历史项操做。
BackForwardClient及HistoryItem则存储着历史项的详细内容。历史项的变化消息则由FrameLoaderClient负责发送(适配到WebKit层)。


     
Page层次中的主要类关系例如如下:




一个HistoryItem可以理解为标准中定义的state。HistoryItem存储和Joint Session History的关系表现在其存储的成员变量上:
  m_target, m_parent存储的都是Frame名称,可以从FrameTree获取到Frame, 分别表明着此HistoryItem相应的Frame, 以及其父Frame。
  m_scrollPoint则是当前显示的位置。假设是经过Anchor跳转,这个值就会有所区分。
  m_stateObject则是经过HTML5 History API的pushState及replaceState所操做的内容。
 

JSBinding层则是由History经过Frame向JS提供服务。在Frame的层次上,Frame主要经过FrameLoader进行历史项操做。页面跳转操做则由Frame的NavigatorScheduler来完毕。在HTML5 Spec中关于Session History的操做集中在HistoryController里,一部分逻辑分散在NavigationScheduler里,比方NavigationScheduler::mustLockBackForwardList()函数,以及1秒内跳转的处理逻辑。



当页面前进后退时,详细的载入操做仍是以FrameLoader为核心的,HistoryController和BackForwardController充其量还主要是存储操做。历史项的变化也还需要由FrameLoaderClient及其在各个平台的实现来派发到WebKit及UI层(上图中WebHistoryDelegate即为Mac OS下WebView接收历史项相关信息的Delegate)。


当页面经过JS运行pushState,在WebCore就会在HistoryController中生成一个含有此State的HistoryItem,而后加入到BackForwardList(BackForwardClient)中。下面是其时序图:

当中在HistoryContrller::pushState()中会经过主Fame的HistoryContoller::createItemTree()来建立新的历史项,保存当前文档的状态。假设自己就是主Frame则不需要指定历史项间的从属关系。

下面是页面跳转时,向JS发送popState消息的时序图:




详细的行为逻辑,通读标准定义是最合适的。附连接例如如下:
     https://html.spec.whatwg.org/multipage/browsers.html


从WebKit对外的适配,详细的实现差别比較大,但都会以历史项变化的消息通知来相应UI上的前进后退操做及状态显示,这样可以保持一致性。
比方在Mac OS下,一个历史项变化时,WebFrameLoaderClient::updateGlobalHistory()会使用例如如下的方式通知到WebView上:
     
if ([view historyDelegate]) {
        WebHistoryDelegateImplementationCache* implementations = WebViewGetHistoryDelegateImplementations(view);
        if (implementations->navigatedFunc) {
            WebNavigationData *data = [[WebNavigationData alloc] initWithURLString:loader->url()
                                        title:nilOrNSString(loader->title().string())
                                        originalRequest:loader->originalRequestCopy().nsURLRequest(UpdateHTTPBody)
                                        response:loader->response().nsURLResponse()
                                        hasSubstituteData:loader->substituteData().isValid()
                                        clientRedirectSource:loader->clientRedirectSourceForHistory()];


            CallHistoryDelegate(implementations->navigatedFunc, view, @selector(webView:didNavigateWithNavigationData:inFrame:), data, m_webFrame.get());
            [data release];
        }
   
        return;
    }

历史项的建立及通知流程例如如下:

转载请注明出处: http://blog.csdn.net/horkychen
相关文章
相关标签/搜索