走進WebKit——開啟新Tab(一)

來源:互聯網
上載者:User

基於 Safari

在 WebKit2 中

開啟一個新的Tab,首先調用的

 

void WebProcess::createWebPage(uint64_t pageID, const WebPageCreationParameters& parameters){    // It is necessary to check for page existence here since during a window.open() (or targeted    // link) the WebPage gets created both in the synchronous handler and through the normal way.     HashMap<uint64_t, RefPtr<WebPage> >::AddResult result = m_pageMap.add(pageID, 0);    if (result.isNewEntry) {        ASSERT(!result.iterator->value);        result.iterator->value = WebPage::create(pageID, parameters);

 

 

建立WebPage

 

 

PassRefPtr<WebPage> WebPage::create(uint64_t pageID, const WebPageCreationParameters& parameters){    RefPtr<WebPage> page = adoptRef(new WebPage(pageID, parameters));

 

接下來進入 WebPage 的建構函式 ,建構函式中建立 PageClients, WebChromeClient, WebContextMenuClient, WebEditorClient, WebDragClient, WebBackForwardListProxy, WebInspectorClient, WebPluginClient 以及Page 對象等。

 

 

WebPage::WebPage(uint64_t pageID, const WebPageCreationParameters& parameters)    Settings::setDefaultMinDOMTimerInterval(0.004);    Page::PageClients pageClients;    pageClients.chromeClient = new WebChromeClient(this);    pageClients.contextMenuClient = new WebContextMenuClient(this);    pageClients.editorClient = new WebEditorClient(this);    pageClients.dragClient = new WebDragClient(this);    pageClients.backForwardClient = WebBackForwardListProxy::create(this);    m_inspectorClient = new WebInspectorClient(this);    pageClients.inspectorClient = m_inspectorClient;   pageClients.plugInClient = new WebPlugInClient(this);    m_page = adoptPtr(new Page(pageClients));...

 

Page 的建構函式中建立 Chrome 對象,DragCaretController對象,FocusController,ContextMenuController,

InspectorController,Settings 等

//WebCore

 

Page::Page(PageClients& pageClients)    : m_chrome(Chrome::create(this, pageClients.chromeClient))    , m_dragCaretController(DragCaretController::create())#if ENABLE(DRAG_SUPPORT)    , m_dragController(DragController::create(this, pageClients.dragClient))#endif    , m_focusController(FocusController::create(this))#if ENABLE(CONTEXT_MENUS)    , m_contextMenuController(ContextMenuController::create(this, pageClients.contextMenuClient))#endif#if ENABLE(INSPECTOR)    , m_inspectorController(InspectorController::create(this, pageClients.inspectorClient))#endif    , m_settings(Settings::create(this))    , m_progress(ProgressTracker::create())    , m_backForwardController(BackForwardController::create(this, pageClients.backForwardClient))

Page 對象建立完之後 WebPage 還會接著建立以下對象:

 

//WebKit2

 

#if ENABLE(GEOLOCATION)    WebCore::provideGeolocationTo(m_page.get(), new WebGeolocationClient(this));#endif#if ENABLE(NETWORK_INFO)    WebCore::provideNetworkInfoTo(m_page.get(), new WebNetworkInfoClient(this));#endif#if ENABLE(VIBRATION)    WebCore::provideVibrationTo(m_page.get(), new WebVibrationClient(this));#endif#if ENABLE(PROXIMITY_EVENTS)    WebCore::provideDeviceProximityTo(m_page.get(), new WebDeviceProximityClient(this));#endif    m_page->setCanStartMedia(false);    m_mayStartMediaWhenInWindow = parameters.mayStartMediaWhenInWindow;    m_pageGroup = WebProcess::shared().webPageGroup(parameters.pageGroupData);    m_page->setGroupName(m_pageGroup->identifier());    m_page->setDeviceScaleFactor(parameters.deviceScaleFactor);    m_drawingArea = DrawingArea::create(this, parameters);    m_drawingArea->setPaintingEnabled(false);    updatePreferences(parameters.store);    platformInitialize();    m_mainFrame = WebFrame::createMainFrame(this);

這裡先看一下 WebFrame::createMainFrame(this), 建立 WebFrame,並對 frame做初始化。

 

 

PassRefPtr<WebFrame> WebFrame::createMainFrame(WebPage* page){    RefPtr<WebFrame> frame = create();    page->send(Messages::WebPageProxy::DidCreateMainFrame(frame->frameID()));    frame->init(page, String(), 0);    return frame.release(); // 這個 release 只是減少了引用計數}

frame 的 init 過程會建立 Frame對象

 

 

void WebFrame::init(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement){    RefPtr<Frame> frame = Frame::create(page->corePage(), ownerElement, &m_frameLoaderClient);

 

把建立的Frame 設定成 MainFrame,關於MainFrame,Frame,Page,Document 的關係,參看《走進WebKit--開篇》

//WebCore  

 

PassRefPtr<Frame> Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client){    RefPtr<Frame> frame = adoptRef(new Frame(page, ownerElement, client));    if (!ownerElement)        page->setMainFrame(frame);    return frame.release();}

在 Frame 的建構函式中依次給成員變數賦值 

 

 

        Page* m_page; 

        mutable FrameTree m_treeNode; //用來協助管理父幀和子幀,常見的是 main frame 和 iframe之間

        mutable FrameLoader m_loader; // 用來完成 Frame 的載入

        mutable NavigationScheduler m_navigationScheduler; // 頁面跳轉調度器

        HTMLFrameOwnerElement* m_ownerElement;

        RefPtr<FrameView> m_view; // 用於Frame 的排版

        RefPtr<Document> m_doc; // 用來管理DOM節點

        ScriptController m_script; // 指令碼控制器

        mutable Editor m_editor; //處理頁面編輯

        mutable FrameSelection m_selection; // 選取

        mutable EventHandler m_eventHandler; //處理滑鼠事件,按鍵事件等 UI 互動事件

        mutable AnimationController m_animationController;

 

inline Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)    : m_page(page)    , m_treeNode(this, parentFromOwnerElement(ownerElement))    , m_loader(this, frameLoaderClient) //    , m_navigationScheduler(this) //    , m_ownerElement(ownerElement)    , m_script(this)    , m_editor(this) //    , m_selection(this) //    , m_eventHandler(this) //    , m_animationController(this)    , m_pageZoomFactor(parentPageZoomFactor(this))    , m_textZoomFactor(parentTextZoomFactor(this))#if ENABLE(ORIENTATION_EVENTS)    , m_orientation(0)#endif    , m_inViewSourceMode(false)    , m_activeDOMObjectsAndAnimationsSuspendedCount(0){    ASSERT(page);    AtomicString::init();    HTMLNames::init();    QualifiedName::init();    MediaFeatureNames::init();    SVGNames::init();    XLinkNames::init();    MathMLNames::init();    XMLNSNames::init();    XMLNames::init();    WebKitFontFamilyNames::init();

 

FrameLoader 建構函式中初始化成員變數。

 

FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client)    : m_frame(frame)    , m_client(client)    , m_policyChecker(frame)    , m_history(frame)    , m_notifer(frame)    , m_subframeLoader(frame)    , m_icon(frame)    , m_mixedContentChecker(frame)    , m_state(FrameStateProvisional)    , m_loadType(FrameLoadTypeStandard)    , m_delegateIsHandlingProvisionalLoadError(false)    , m_quickRedirectComing(false)    , m_sentRedirectNotification(false)    , m_inStopAllLoaders(false)    , m_isExecutingJavaScriptFormAction(false)    , m_didCallImplicitClose(true)    , m_wasUnloadEventEmitted(false)    , m_pageDismissalEventBeingDispatched(NoDismissal)    , m_isComplete(false)    , m_needsClear(false)    , m_checkTimer(this, &FrameLoader::checkTimerFired)    , m_shouldCallCheckCompleted(false)    , m_shouldCallCheckLoadComplete(false)    , m_opener(0)#if PLATFORM(CHROMIUM)    , m_didAccessInitialDocument(false)    , m_didAccessInitialDocumentTimer(this, &FrameLoader::didAccessInitialDocumentTimerFired)#endif    , m_didPerformFirstNavigation(false)    , m_loadingFromCachedPage(false)    , m_suppressOpenerInNewFrame(false)    , m_forcedSandboxFlags(SandboxNone){}

WebFrame::init 完成後面會調用 frame->init() 進行初始化動作

void WebFrame::init(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement){    RefPtr<Frame> frame = Frame::create(page->corePage(), ownerElement, &m_frameLoaderClient);    m_coreFrame = frame.get();    frame->tree()->setName(frameName);    if (ownerElement) {        ASSERT(ownerElement->document()->frame());        ownerElement->document()->frame()->tree()->appendChild(frame);    }    frame->init();}

其實只做了一件事情,初始化 FrameLoader

 

 

    inline void Frame::init()    {        m_loader.init();    }

 

void FrameLoader::init(){    // This somewhat odd set of steps gives the frame an initial empty document.    setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL(ParsedURLString, emptyString())), SubstituteData()).get());    setProvisionalDocumentLoader(m_policyDocumentLoader.get());    m_provisionalDocumentLoader->startLoadingMainResource();    m_frame->document()->cancelParsing();    m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument);    m_networkingContext = m_client->createNetworkingContext();    m_progressTracker = FrameProgressTracker::create(m_frame);}

 

 

 建立DocumentLoader過程中會建立 CachedResourceLoader,DocumentWriter 等

剛建立的 DocumentLoader 會被 setPolicyDocumentLoader 設定成 m_policyDocumentLoader,然後被

setProvisionalDocumentLoader設定為 m_provisionalDocumentLoader

 FrameLoader 維護了三個 DocumentLoader 對象分別對應三個不同的階段。在後面載入過程中再做分析

   RefPtr<DocumentLoader> m_documentLoader;

   RefPtr<DocumentLoader> m_provisionalDocumentLoader;

   RefPtr<DocumentLoader> m_policyDocumentLoader;

接下來的  m_provisionalDocumentLoader->startLoadingMainResource(); 實際上只判斷是不是載入空頁面就返回了。

 

   if (maybeLoadEmpty())        return;

 

maybeLoadEmpty()中關鍵 call stack 如下: maybeLoadEmpty() 做一些處理之後,調用 finishedLoading(),  finishedLoading()  調用 commitIfReady() ,在 commitProvisionalLoad 中完成提交。

 

 

 

 frame #0: 0x0000000103e402e5 WebCore`WebCore::FrameLoader::transitionToCommitted(this=0x000000010c121080, cachedPage=0x00007fff5fbfc5e8)     frame #1: WebCore`WebCore::FrameLoader::commitProvisionalLoad(this=0x000000010c121080)     frame #2: WebCore`WebCore::DocumentLoader::commitIfReady(this=0x000000010b94d400)     frame #3: WebCore`WebCore::DocumentLoader::finishedLoading(this=0x000000010b94d400)    frame #4: WebCore`WebCore::DocumentLoader::maybeLoadEmpty(this=0x000000010b94d400) 

在 transitionToCommitted 中把前面 m_provisionalDocumentLoader 賦值給 m_documentLoader, 將 m_provisionalDocumentLoader 置空,將 FrameLoader的 

 

m_state 從初始化時的 FrameStateProvisional 設定成FrameStateCommittedPage, DocumentWriter 的MIMEType設定成 “text/html”, 此時 DocumentStateMachine 還是CreatingInitialEmptyDocument 狀態 

m_committed = true;

 

 

    setDocumentLoader(m_provisionalDocumentLoader.get());    setProvisionalDocumentLoader(0);    setState(FrameStateCommittedPage);    m_documentLoader->writer()->setMIMEType(dl->responseMIMEType());     if (m_stateMachine.creatingInitialEmptyDocument())        return;

返回到 finishedLoading() 中

 

void DocumentLoader::finishedLoading(){    commitIfReady();    if (!frameLoader())        return;    if (!maybeCreateArchive()) {        // If this is an empty document, it will not have actually been created yet. Commit dummy data so that        // DocumentWriter::begin() gets called and creates the Document.        if (!m_gotFirstByte)            commitData(0, 0);        frameLoader()->client()->finishedLoading(this);    }    m_writer.end();    if (!m_mainDocumentError.isNull())        return;    clearMainResourceLoader();    if (!frameLoader()->stateMachine()->creatingInitialEmptyDocument())        frameLoader()->checkLoadComplete();}

注意上面的注釋,正是初始化過程的情況。 commitData(0,0) 會調用到DocumentWriter
::begin(), 在裡面建立 Document 和 DOMWindow,從Document中擷取DocumentParser,將WriterState 設定為 StartedWritingState

 

commitData 之後就會調用 

frameLoader()->client()->finishedLoading(this);m_writer.end();

 

m_writer.end() 中會 把 WriterState 改成 FinishedWritingState,然後將 DocumentWriter 維護的DocumentParser 對象清理掉,

void DocumentWriter::end(){    ASSERT(m_frame->page());    ASSERT(m_frame->document());    // The parser is guaranteed to be released after this point. begin() would    // have to be called again before we can start writing more data.    m_state = FinishedWritingState;    // http://bugs.webkit.org/show_bug.cgi?id=10854    // The frame's last ref may be removed and it can be deleted by checkCompleted(),     // so we'll add a protective refcount    RefPtr<Frame> protector(m_frame);    if (!m_parser)        return;    // FIXME: m_parser->finish() should imply m_parser->flush().    m_parser->flush(this);    if (!m_parser)        return;    m_parser->finish();    m_parser = 0;}

 

至此 DocumentWriter 的狀態完成躍遷:

DocumentWriter()begin, end

|   |  |

WriterState : NotStartedWritingState -> StartedWritingState -> FinishedWritingState

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.