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
相關文章
相關標籤/搜索