基於 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