1. 1. 本文基于Qt5.5.1版本中的QWebkit
  2. 2. 页面加载的过程
    1. 2.1. FrameLoader
      1. 2.1.1. FrameLoader-状态定义
      2. 2.1.2. 资源类型定义
      3. 2.1.3. FrameLoader::FrameState::FrameStateProvisional
    2. 2.2. WebPage->WebFrame->FrameLoader的加载过程
      1. 2.2.0.1. Adapter层调用
      2. 2.2.0.2. 通过FrameLoader 执行加载过程
      3. 2.2.0.3. 关于 continueLoadAfterNavigationPolicy()说明
      4. 2.2.0.4. 开始加载页面
    3. 2.2.1. DocumentLoader
    4. 2.2.2. CachedResourceLoader
  • 3. Document
    1. 3.0.1. Document 对象的三个状态具体转换过程
  • 本文基于Qt5.5.1版本中的QWebkit

    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
    enum FrameState {
    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
    };

    资源类型定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    enum Type {
    MainResource, // HTML
    ImageResource, // 图片
    CSSStyleSheet, // CSS文件
    Script, // Javascript文件
    FontResource, // 字体
    RawResource // 其他包括多媒体文件等二进制文件
    #if ENABLE(SVG)
    , SVGDocumentResource
    #endif
    #if ENABLE(XSLT)
    , XSLStyleSheet
    #endif
    #if ENABLE(LINK_PREFETCH)
    , LinkPrefetch
    , LinkSubresource
    #endif
    #if ENABLE(VIDEO_TRACK)
    , TextTrackResource
    #endif
    #if ENABLE(CSS_SHADERS)
    , ShaderResource
    #endif
    };

    在webkit中一般将HTML页面定义为主资源。

    FrameLoader::FrameState::FrameStateProvisional

    Provisional 是Frame第一个定义状态,命名定义该状态是一个临时,且不确定状态
    TODO: 后续需要添加说明,该状态的命名原因;

    WebPage->WebFrame->FrameLoader的加载过程

    Adapter层调用

    1. load -> WebCore::Frame()->loader()获得了 WebCore::FrameLoader
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    void QWebFrameAdapter::load(const QNetworkRequest& req, QNetworkAccessManager::Operation operation, const QByteArray& body)
    {
    if (frame->tree()->parent())
    pageAdapter->insideOpenCall = true;
    QUrl url = ensureAbsoluteUrl(req.url());
    WebCore::ResourceRequest request(url);
    ... ...
    frame->loader()->load(WebCore::FrameLoadRequest(frame, request));
    if (frame->tree()->parent())
    pageAdapter->insideOpenCall = false;
    }
    1. frame->loader()->load(WebCore::FrameLoadRequest(frame, request));
      关键代码 WebCore::FrameLoadRequest() 调用的是 -> FrameLoadRequest(Frame*, const ResourceRequest&, const SubstituteData& = SubstituteData());
      这里WebCore::FrameLoadRequest().frameName() 返回的应该是空的string。
    2. FrameLoadRequest(Frame*, const ResourceRequest&, const SubstituteData& = SubstituteData());
      FrameLoader用空的SubstituteData去创建DocumentLoader,并使用DocumentLoader来完成MainResource的加载,SubstituteData用于在所请求的资源不可到达的时候,提供重定向指导。

    通过FrameLoader 执行加载过程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    void FrameLoader::load(const FrameLoadRequest& passedRequest)
    {
    FrameLoadRequest request(passedRequest);
    if (m_inStopAllLoaders)
    return;
    if (!request.frameName().isEmpty()) {
    Frame* frame = findFrameForNavigation(request.frameName());
    if (frame) {
    request.setShouldCheckNewWindowPolicy(false);
    if (frame->loader() != this) {
    frame->loader()->load(request);
    return;
    }
    }
    }
    if (request.shouldCheckNewWindowPolicy()) {
    policyChecker()->checkNewWindowPolicy(NavigationAction(request.resourceRequest(), NavigationTypeOther), FrameLoader::callContinueLoadAfterNewWindowPolicy, request.resourceRequest(), 0, request.frameName(), this);
    return;
    }
    if (!request.hasSubstituteData())
    request.setSubstituteData(defaultSubstituteDataForURL(request.resourceRequest().url()));
    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request.resourceRequest(), request.substituteData());
    if (request.lockHistory() && m_documentLoader)
    loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());
    load(loader.get());
    }

    建立一个documentloader来加载资源:RefPtr loader = m_client->createDocumentLoader(request.resourceRequest(), request.substituteData());
    注意这里的request.subtitueData是一个空对象。
    然后调用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    void FrameLoader::load(DocumentLoader* newDocumentLoader)
    {
    ……
    if (m_documentLoader)
    newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
    // When we loading alternate content for an unreachable URL that we're
    // visiting in the history list, we treat it as a reload so the history list
    // is appropriately maintained.
    //
    // 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;

    在frame 加载的过程中的三个不同阶段,都将创建并调用到document loader这个类型。请注意一个新的请求正在被加载的时候,先前的document loader可能依然会被其他对象引用到。
    比如,一个新的请求还处于”policy”这个状态时,新的loader依然会关联到先前的document loader中一些具体配置参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
    {
    // 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);
    }
    }

    设置完成以后,通过policyChecker()校验请求后,调用callContinueLoadAfterNavigationPolicy()
    callContinueLoadAfterNavigationPolicy() 中将设置FrameLoader的状态转换为
    Provisional参考FrameLoader的状态定义

    这是一个简单 包裹方法,简单地做了一个转换。实际调用的是continueLoadAfterNavigationPolicy。

    1
    2
    3
    4
    5
    6
    void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,
    const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)
    {
    FrameLoader* loader = static_cast<FrameLoader*>(argument);
    loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
    }

    关于 continueLoadAfterNavigationPolicy()说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    void FrameLoader::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());
    bool isTargetItem = history()->provisionalItem() ? history()->provisionalItem()->isTargetItem() : false;
    // 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
    setProvisionalDocumentLoader(m_policyDocumentLoader.get());
    m_loadType = type;
    setState(FrameStateProvisional);
    setPolicyDocumentLoader(0);
    if (isBackForwardLoadType(type) && history()->provisionalItem()->isInPageCache()) {
    loadProvisionalItemFromCachedPage();
    return;
    }
    ///
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // 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
    void FrameLoader::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;
    m_loadingFromCachedPage = false;
    m_provisionalDocumentLoader->startLoadingMainResource();
    }
    1. prepareForLoadStart(); 通知进度条进行加载准备,向client 分发消息通知进行加载

      1
      2
      3
      4
      5
      6
      void FrameLoader::prepareForLoadStart()
      {
      m_progressTracker->progressStarted();
      m_client->dispatchDidStartProvisionalLoad();
      ... ...
      }
    2. m_provisionalDocumentLoader->startLoadingMainResource(); 开始将首页面作为主资源进行加载,
      这里实际调用的是DocumentLoader::startLoadingMainResource;

    DocumentLoader

    所有资源的加载,都是通过DocumentLoader 以及子类实现的;
    m_provisionalDocumentLoader 就是一个DocumentLoader;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    void DocumentLoader::startLoadingMainResource()
    {
    ... ...
    ResourceRequest request(m_request);
    DEFINE_STATIC_LOCAL(ResourceLoaderOptions, mainResourceLoadOptions,
    (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType));
    CachedResourceRequest cachedResourceRequest(request, mainResourceLoadOptions);
    m_mainResource = m_cachedResourceLoader->requestMainResource(cachedResourceRequest);
    if (!m_mainResource) {
    setRequest(ResourceRequest());
    // If the load was aborted by clearing m_request, it's possible the ApplicationCacheHost
    // is now in a state where starting an empty load will be inconsistent. Replace it with
    // a new ApplicationCacheHost.
    m_applicationCacheHost = adoptPtr(new ApplicationCacheHost(this));
    maybeLoadEmpty();
    return;
    }
    if (!mainResourceLoader()) {
    m_identifierForLoadWithoutResourceLoader = m_frame->page()->progress()->createUniqueIdentifier();
    frameLoader()->notifier()->assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, this, request);
    frameLoader()->notifier()->dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, request, ResourceResponse());
    }
    m_mainResource->addClient(this);
    // A bunch of headers are set when the underlying ResourceLoader is created, and m_request needs to include those.
    if (mainResourceLoader())
    request = mainResourceLoader()->originalRequest();
    // If there was a fragment identifier on m_request, the cache will have stripped it. m_request should include
    // the fragment identifier, so add that back in.
    if (equalIgnoringFragmentIdentifier(m_request.url(), request.url()))
    request.setURL(m_request.url());
    setRequest(request);
    }
    1. 关于ResourceRequest request(m_request);
      这里仅仅是将指针值赋值给新的request 还是将整个m_request 的内容都拷贝给了request?
      如果是整个内容都拷贝给了request,那么下面执行的语句:
      If there was a fragment identifier on m_request, the cache will have stripped it. m_request should include
      1
      2
      3
      // the fragment identifier, so add that back in.
      if (equalIgnoringFragmentIdentifier(m_request.url(), request.url()))
      request.setURL(m_request.url());

    不是太理解这边的执行内容。

    1. 在这里通过创建mainResource,然后将自己注册给mainResource作为回调

      1
      2
      line 1382:
      m_mainResource = m_cachedResourceLoader->requestMainResource(cachedResourceRequest);
      1
      2
      line 1398:
      m_mainResource->addClient(this);

    接着就将请求转向了CachedResourceLoader

    CachedResourceLoader

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 注释说明:
    // The CachedResourceLoader provides a per-context interface to the MemoryCache
    // and enforces a bunch of security checks and rules for resource revalidation.
    // Its lifetime is roughly per-DocumentLoader, in that it is generally created
    // in the DocumentLoader constructor and loses its ability to generate network
    // requests when the DocumentLoader is destroyed. Documents also hold a
    // RefPtr<CachedResourceLoader> for their lifetime (and will create one if they
    // are initialized without a Frame), so a Document can keep a CachedResourceLoader
    // alive past detach if scripts still reference the Document.
    1
    2
    3
    4
    CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request)
    {
    return static_cast<CachedRawResource*>(requestResource(CachedResource::MainResource, request).get());
    }

    通过调用requestResource 来向服务端请求主资源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request)
    {
    // 判断是否由缓存中预取得到相应的资源对象
    ... ...
    const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
    switch (policy) {
    case Reload:
    memoryCache()->remove(resource.get());
    // Fall through
    case Load:
    resource = loadResource(type, request, request.charset());
    break;
    case Revalidate:
    resource = revalidateResource(request, resource.get());
    break;
    case Use:
    if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(resource.get()))
    return 0;
    memoryCache()->resourceAccessed(resource.get());
    break;
    }
    ... ...
    if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) {
    resource->load(this, request.options());
    // We don't support immediate loads, but we do support immediate failure.
    if (resource->errorOccurred()) {
    if (resource->inCache())
    memoryCache()->remove(resource.get());
    return 0;
    }
    }
    ... ...
    return resource;
    }

    const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); 这里通过封装的加载策略,来判断加载方式。
    加载的策略方式有 reLoad, Revalidate, Use, Load。
    这里仅分析在Load的情况,其他情况待分解。
    resource = loadResource(type, request, request.charset()); 该函数调用了static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset)
    来创建指定的资源类型,并将其返回。
    然后程序执行到这句:
    if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) { resource->load(this, request.options());
    新加载的资源对应策略为load,并且主资源未设置Defer,resource->stillNeedsLoad(),默认情况下都返回False。
    所以policy != UseCachedResourceRequest::NoDefer == request.defer()同时命中,执行resource->load方法。
    执行CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
    {
    // 此处忽略若干行
    ... ...
    m_loader = platformStrategies()->loaderStrategy()->resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->frame(), this, request, request.priority(), options);
    if (!m_loader) {
    failBeforeStarting();
    return;
    }
    m_status = Pending;
    }

    由于webkit需要支持不同的平台,所以将不同平台下资源加载的方式使用策略模式进行了封装。然后将资源调用通过resourceLoadScheduler来统一请求和调度。

    1
    2
    3
    4
    5
    6
    7
    PassRefPtr<SubresourceLoader> ResourceLoadScheduler::scheduleSubresourceLoad(Frame* frame, CachedResource* resource, const ResourceRequest& request, ResourceLoadPriority priority, const ResourceLoaderOptions& options)
    {
    RefPtr<SubresourceLoader> loader = SubresourceLoader::create(frame, resource, request, options);
    if (loader)
    scheduleLoad(loader.get(), priority);
    return loader.release();
    }

    这里创建了一个资源调度器,进行加载调用。
    从CachedResource::load -> ResourceLoadScheduler::scheduleSubresourceLoad 这里的调用流程与其他派生资源(如图片、CSS文件、JS文件等)的加载是同一个流程。

    Document

    Document 对象的三个状态具体转换过程

    在Javascript中页面的DOM加载经历三个状态, loading, interactive, complete。
    三个状态定义在Document中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // 三个状态通过readyState 接口返回
    String Document::readyState() const
    {
    DEFINE_STATIC_LOCAL(const String, loading, (ASCIILiteral("loading")));
    DEFINE_STATIC_LOCAL(const String, interactive, (ASCIILiteral("interactive")));
    DEFINE_STATIC_LOCAL(const String, complete, (ASCIILiteral("complete")));
    switch (m_readyState) {
    case Loading:
    return loading;
    case Interactive:
    return interactive;
    case Complete:
    return complete;
    }
    ASSERT_NOT_REACHED();
    return String();
    }
    // 三个状态通过setReadyState接口进行更改
    void Document::setReadyState(ReadyState readyState)
    {
    if (readyState == m_readyState)
    return;
    switch (readyState) {
    case Loading:
    if (!m_documentTiming.domLoading)
    m_documentTiming.domLoading = monotonicallyIncreasingTime();
    break;
    case Interactive:
    if (!m_documentTiming.domInteractive)
    m_documentTiming.domInteractive = monotonicallyIncreasingTime();
    break;
    case Complete:
    if (!m_documentTiming.domComplete)
    m_documentTiming.domComplete = monotonicallyIncreasingTime();
    break;
    }
    m_readyState = readyState;
    /// COMMENT 通知页面上的监听者(JS)
    dispatchEvent(Event::create(eventNames().readystatechangeEvent, false, false));
    if (settings() && settings()->suppressesIncrementalRendering())
    setVisualUpdatesAllowed(readyState);
    }