userAgent “Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) cordova Safari/538.1”
页面加载的过程
FrameLoader
FrameLoader-状态定义
1 2 3 4 5 6 7
enumFrameState { FrameStateProvisional, // This state indicates we are ready to commit to a page, // which means the view will transition to use the new data source. FrameStateCommittedPage, FrameStateComplete };
voidFrameLoader::load(DocumentLoader* newDocumentLoader) { …… if (m_documentLoader) newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
// When we loading alternate content for an unreachableURL, we'll get in here with a // nil policyDataSource because loading the alternate page will have passed // through this method already, nested; otherwise, policyDataSource should still be set. // // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ... // shouldn't a more explicit type of reload be defined, that means roughly // "load without affecting history" ? if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) { // shouldReloadToHandleUnreachableURL() returns true only when the original load type is back-forward. // In this case we should save the document state now. Otherwise the state can be lost because load type is // changed and updateForBackForwardNavigation() will not be called when loading is committed. history()->saveDocumentAndScrollState(); ASSERT(type == FrameLoadTypeStandard); type = FrameLoadTypeReload; } loadWithDocumentLoader(newDocumentLoader, type, 0); }
注意这里的问题: if (m_documentLoader) newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
1 2 3 4 5 6
// 这里是m_documentLoader在FrameLoader中的定义: // Document loaders for the three phases of frame loading. Note that while // a new request is being loaded, the old document loader may still be referenced. // E.g. while a new request is in the "policy" state, the old document loader may // be consulted in particular as it makes sense to imply certain settings on the new loader. RefPtr<DocumentLoader> m_documentLoader;
// Retain because dispatchBeforeLoadEvent may release the last reference to it. RefPtr<Frame> protect(m_frame); ASSERT(m_client->hasWebView()); // 所以页面在加载前会判断当前的client中是否包含了View的实现。 ……
if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker()->loadType(), newURL)) { RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader; NavigationAction action(loader->request(), policyChecker()->loadType(), isFormSubmission); oldDocumentLoader->setTriggeringAction(action); policyChecker()->stopCheck(); policyChecker()->checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState, callContinueFragmentScrollAfterNavigationPolicy, this); } else { if (Frame* parent = m_frame->tree()->parent()) loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding()); policyChecker()->stopCheck(); // 将新创建的documentloader设置给m_policyDocumentLoader setPolicyDocumentLoader(loader); if (loader->triggeringAction().isEmpty()) // 将本次加载的请求记录在loader的m_triggeringAction中 loader->setTriggeringAction(NavigationAction(loader->request(), policyChecker()->loadType(), isFormSubmission)); if (Element* ownerElement = m_frame->ownerElement()) { // We skip dispatching the beforeload event if we've already // committed a real document load because the event would leak // subsequent activity by the frame which the parent frame isn't // supposed to learn. For example, if the child frame navigated to // a new URL, the parent frame shouldn't learn the URL. if (!m_stateMachine.committedFirstRealDocumentLoad() && !ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) { continueLoadAfterNavigationPolicy(loader->request(), formState, false); return; } } // 使用前面记录的loader.m_triggeringAction做校验,处理空白,重复,不可到达的请求, // 该校验还要包括FrameLoaderClient实现的一些检查,以决定如何处理本次请求。 policyChecker()->checkNavigationPolicy(loader->request(), loader, formState, callContinueLoadAfterNavigationPolicy, this); } }
voidFrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue) { // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a // nil policyDataSource because loading the alternate page will have passed // through this method already, nested; otherwise, policyDataSource should still be set. ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
// Two reasons we can't continue: // 1) Navigation policy delegate said we can't so request is nil. A primary case of this // is the user responding Cancel to the form repost nag sheet. // 2) User responded Cancel to an alert popped up by the before unload event handler. bool canContinue = shouldContinue && shouldClose();
if (!canContinue) { // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we // need to report that the client redirect was cancelled. if (m_quickRedirectComing) clientRedirectCancelledOrFinished(false);
setPolicyDocumentLoader(0);
// If the navigation request came from the back/forward menu, and we punt on it, we have the // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity, // we only do this when punting a navigation for the target frame or top-level frame. if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(policyChecker()->loadType())) { if (Page* page = m_frame->page()) { Frame* mainFrame = page->mainFrame(); if (HistoryItem* resetItem = mainFrame->loader()->history()->currentItem()) { page->backForward()->setCurrentItem(resetItem); m_frame->loader()->client()->updateGlobalHistoryItemForPage(); } } } return; }
FrameLoadType type = policyChecker()->loadType(); // A new navigation is in progress, so don't clear the history's provisional item. stopAllLoaders(ShouldNotClearProvisionalItem);
// <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders() // might detach the current FrameLoader, in which case we should bail on this newly defunct load. if (!m_frame->page()) return;
#if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR) if (Page* page = m_frame->page()) { if (page->mainFrame() == m_frame) m_frame->page()->inspectorController()->resume(); } #endif
// Two reasons we can't continue: // 1) Navigation policy delegate said we can't so request is nil. A primary case of this // is the user responding Cancel to the form repost nag sheet. // 2) User responded Cancel to an alert popped up by the before unload event handler. bool canContinue = shouldContinue && shouldClose();
if (!canContinue) { // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we // need to report that the client redirect was cancelled. if (m_quickRedirectComing) clientRedirectCancelledOrFinished(false);
setPolicyDocumentLoader(0);
// If the navigation request came from the back/forward menu, and we punt on it, we have the // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity, // we only do this when punting a navigation for the target frame or top-level frame. if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(policyChecker()->loadType())) { if (Page* page = m_frame->page()) { Frame* mainFrame = page->mainFrame(); if (HistoryItem* resetItem = mainFrame->loader()->history()->currentItem()) { page->backForward()->setCurrentItem(resetItem); m_frame->loader()->client()->updateGlobalHistoryItemForPage(); } } } return; }
如果用户未取消加载操作,则根据表单状态,执行后续流程。一般都是走else这个分支。
1 2 3 4
if (formState) m_client->dispatchWillSubmitForm(&PolicyChecker::continueLoadAfterWillSubmitForm, formState); else continueLoadAfterWillSubmitForm();
接着开始进入页面的真正加载流程。
开始加载页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
voidFrameLoader::continueLoadAfterWillSubmitForm() { if (!m_provisionalDocumentLoader) return;
prepareForLoadStart();
// The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader, // so we need to null check it again. if (!m_provisionalDocumentLoader) return;
DocumentLoader* activeDocLoader = activeDocumentLoader(); if (activeDocLoader && activeDocLoader->isLoadingMainResource()) return;