Android WebKit HTML main resource Loading Process

Source: Internet
Author: User

Android WebKit HTML primary resource loading process Preface

Enter the URL in the browser, and the browser will call the loadUrl () of WebView, and then load the entire web page. The most important step in the entire loading process is the loading of HTML primary resources. WebKit divides webpage resources into MainResource and SubResource ).

WebKit resource category

Primary resource: HTML file.

Sub-resources: CSS, JS, and JPG. All resources except HTML files are called sub-resources.

This chapter mainly describes the loading process of primary resources. The loading process of sub-resources will be analyzed and explained in detail later.

Primary resource request LoadUrl

The request for the primary resource starts with the loadUrl of the WebView. According to the previous explanation of "Android WebKit message processing", WebView operations will all have WebViewClassic for proxy. Resource loading must be handled by WebCore. Therefore, WebVewClassic sends a message to WebViewCore so that WebViewCore can finally pass the loadUrl to the WebKit at the C ++ layer for processing:

    /**     * See {@link WebView#loadUrl(String, Map)}     */    @Override    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {        loadUrlImpl(url, additionalHttpHeaders);    }    private void loadUrlImpl(String url, Map<String, String> extraHeaders) {        switchOutDrawHistory();        WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();        arg.mUrl = url;        arg.mExtraHeaders = extraHeaders;        mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);        clearHelpers();    }

After receiving LOAD_URL, WebViewCore calls nativeLoadUrl through BrowserFrame. This BrowserFrame is connected to the mainFrame at the C ++ layer. Here, by the way, clearHeapers () is used to clean up the dialog box dialog and input methods on the current webpage. This is why the input method and dialog of the current page disappear while loading a new page. After WebViewCore receives the message, it will directly ask BrowserFrame to call JNI: nativeLoadUrl ():

// BrowserFrame.java    public void loadUrl(String url, Map<String, String> extraHeaders) {        mLoadInitFromJava = true;        if (URLUtil.isJavaScriptUrl(url)) {            // strip off the scheme and evaluate the string            stringByEvaluatingJavaScriptFromString(                    url.substring("javascript:".length()));        } else {            /** M: add log */            Xlog.d(XLOGTAG, "browser frame loadUrl: " + url);            nativeLoadUrl(url, extraHeaders);        }        mLoadInitFromJava = false;    }
Because LoadUrl () can Load not only a url, but also execute a javascript code. If a js file is loaded, the js file is not loaded, but is executed directly here. StringByEvaluatingJavaScriptFromString will also be executed in the scriptController of mainFrame by calling the v8 interface through jni. js will write an article about WebKit js for special analysis later on WebKit. So far, LoadUrl simply uses a String to pass strings.

// WebCoreFrameBridge.cppstatic void LoadUrl(JNIEnv *env, jobject obj, jstring url, jobject headers){    WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);    ALOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!");    WTF::String webcoreUrl = jstringToWtfString(env, url);    WebCore::KURL kurl(WebCore::KURL(), webcoreUrl);    WebCore::ResourceRequest request(kurl);    if (headers) {        // dalvikvm will raise exception if any of these fail        jclass mapClass = env->FindClass("java/util/Map");        jmethodID entrySet = env->GetMethodID(mapClass, "entrySet",                "()Ljava/util/Set;");        jobject set = env->CallObjectMethod(headers, entrySet);        jclass setClass = env->FindClass("java/util/Set");        jmethodID iterator = env->GetMethodID(setClass, "iterator",                "()Ljava/util/Iterator;");        jobject iter = env->CallObjectMethod(set, iterator);        jclass iteratorClass = env->FindClass("java/util/Iterator");        jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");        jmethodID next = env->GetMethodID(iteratorClass, "next",                "()Ljava/lang/Object;");        jclass entryClass = env->FindClass("java/util/Map$Entry");        jmethodID getKey = env->GetMethodID(entryClass, "getKey",                "()Ljava/lang/Object;");        jmethodID getValue = env->GetMethodID(entryClass, "getValue",                "()Ljava/lang/Object;");        while (env->CallBooleanMethod(iter, hasNext)) {            jobject entry = env->CallObjectMethod(iter, next);            jstring key = (jstring) env->CallObjectMethod(entry, getKey);            jstring value = (jstring) env->CallObjectMethod(entry, getValue);            request.setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, value));            env->DeleteLocalRef(entry);            env->DeleteLocalRef(key);            env->DeleteLocalRef(value);        }    // ...    pFrame->loader()->load(request, false);}

Next, create ResourceRequest in the jni LoadUrl. Because the java layer of WebView can set the url request header, and then load it through FrameLoader. Here, pFrame is the mainFrame corresponding to the BrowserFrame of the Java layer. At the WebKit level, the bottom layer of HTML is Frame, and then there is Document, which means that HTML Document is also loaded through FrameLoader of Frame:

pFrame->loader()->load(request, false);
The last sentence of calling the stack is to let FrameLoader load the url request. The subsequent call stacks are:
void FrameLoader::load(const ResourceRequest& request, bool lockHistory)void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory)void FrameLoader::load(DocumentLoader* newDocumentLoader)void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument,    const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue)void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)void FrameLoader::continueLoadAfterWillSubmitForm()
The DocumentLoader that loads the Document is created in load:

void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory){    if (m_inStopAllLoaders)        return;            // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted.    m_loadType = FrameLoadTypeStandard;    RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData);    if (lockHistory && m_documentLoader)        loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());    load(loader.get());}
M_client-> createDocumentLoader (request, substituteData); m_client in is FrameLoaderClientAndroid. Subsequent resource downloads also deal with this m_client. Before void FrameLoader: continueLoadAfterWillSubmitForm (), it does not really involve loading primary resources, but it only judges the columns of the URLs to be loaded. On the one hand, it is a security issue, securityOrigin checks the Url security, for example, cross-origin. On the other hand, it is Scroll, because sometimes the Url loaded by the LoadUrl will carry the Url Fragment, that is, hash. For url hash content, see Fragment URLS. Because of the URL hash, it will only scroll to a certain position on the page. In this case, you do not need to actually request mainResource. if these checks have passed, you need to start loading mainResource:

// FrameLoader.cppvoid FrameLoader::continueLoadAfterWillSubmitForm(){    // ...    m_provisionalDocumentLoader->timing()->navigationStart = currentTime();    // ...    if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier))        m_provisionalDocumentLoader->updateLoading();}
StartLoadingMainResource this starts to load the main resource, that is, the preceding html file.
Three DocumentLoader types

M_provisionalDocumentLoader needs to be explained here:

    RefPtr<DocumentLoader> m_documentLoader;    RefPtr<DocumentLoader> m_provisionalDocumentLoader;    RefPtr<DocumentLoader> m_policyDocumentLoader;    void setDocumentLoader(DocumentLoader*);    void setPolicyDocumentLoader(DocumentLoader*);    void setProvisionalDocumentLoader(DocumentLoader*);
We can see that three documentloaders are defined in FrameLoader. h. In fact, WebKit divides these documentloaders by role. Among them: m_documentLoader is the pointer of the last loaded DocumentLoader, and m_policyDocumentLoader is used for some strategic work, such as delayed loading. M_provisionalDocumentLoade is used for actual loading. After a DocumentLoader is completed, the pointer is passed through setXXXXDocumentLoader. The main process of loading according to the URL: PolicyChcek ------> Load MainResouce. That is, the primary resource is loaded only after the policy check is performed. The sequence of the three documentloaders is to pass the pointer after createDocumentLoader to m_pollicyDocumentLoader. After the Policy Check, the pointer is passed to m_provisionalDocumentLoader. After the Document is loaded, pass the pointer to m_documentLoader.

// FrameLoader.cppvoid FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState){    // ...       policyChecker()->stopCheck();    // ...    setPolicyDocumentLoader(loader);    // ..}void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue){    // ...    setProvisionalDocumentLoader(m_policyDocumentLoader.get());    m_loadType = type;    setState(FrameStateProvisional);    // ...    setPolicyDocumentLoader(0);}void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage){    // ...    setDocumentLoader(m_provisionalDocumentLoader.get());    setProvisionalDocumentLoader(0);    // ...}void FrameLoader::checkLoadCompleteForThisFrame(){    switch (m_state) {        case FrameStateProvisional: {                // ...                // If we're in the middle of loading multipart data, we need to restore the document loader.                if (isReplacing() && !m_documentLoader.get())                    setDocumentLoader(m_provisionalDocumentLoader.get());                // Finish resetting the load state, but only if another load hasn't been started by the                // delegate callback.                if (pdl == m_provisionalDocumentLoader)                    clearProvisionalLoad();                    }    // ...}
The above code snippet shows that the undertaking relationship between the three documentloaders is one link. The way index.html is loaded in WebKit is divided into two methods: If it is before and after loading, index.html is loaded from CachedPage, FrameLoader: transitionToCommitted is called after loading from CachedPage, void FrameLoader :: checkLoadCompleteForThisFrame () is called after being loaded from the network.

// FrameLoader.cppvoid FrameLoader::recursiveCheckLoadComplete(){    Vector<RefPtr<Frame>, 10> frames;        for (RefPtr<Frame> frame = m_frame->tree()->firstChild(); frame; frame = frame->tree()->nextSibling())        frames.append(frame);        unsigned size = frames.size();    for (unsigned i = 0; i < size; i++)        frames[i]->loader()->recursiveCheckLoadComplete();        checkLoadCompleteForThisFrame();}// Called every time a resource is completely loaded, or an error is received.void FrameLoader::checkLoadComplete(){    ASSERT(m_client->hasWebView());        m_shouldCallCheckLoadComplete = false;    // FIXME: Always traversing the entire frame tree is a bit inefficient, but     // is currently needed in order to null out the previous history item for all frames.    if (Page* page = m_frame->page())        page->mainFrame()->loader()->recursiveCheckLoadComplete();}
It should be emphasized that WebKit needs to confirm all frames in the Page after loading, and finally setDocumentLoader (). In my personal understanding, there is still room for optimization.
StartLoadingMainResource after m_provisionalDocumentLoader calls startLoadingMainResource, it starts preparing to send network requests. The call stack is as follows:
bool DocumentLoader::startLoadingMainResource(unsigned long identifier)bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)bool MainResourceLoader::loadNow(ResourceRequest& r)PassRefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request,ResourceHandleClient* client,bool defersLoading,bool shouldContentSniff)bool ResourceHandle::start(NetworkingContext* context)PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start(        ResourceHandle* handle, const ResourceRequest& request,FrameLoaderClient* client, bool isMainResource, bool isSync)bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context)
It should be noted that, although LoadUrl is finally executed in The WebCore thread, the final resource download is carried out in the Chromium_net IO thread. After the resources are downloaded, the network data will be handed over to FrameLoaderClientAndroid.
Network Data

After Android WebKit data is downloaded to the Chromium_net IO thread, the WebUrlLoaderClient submits data to WebCore. The WebKt call stack is as follows:

// Finishvoid WebUrlLoaderClient::didFinishLoading()void ResourceLoader::didFinishLoading(ResourceHandle*, double finishTime)void MainResourceLoader::didFinishLoading(double finishTime)void FrameLoader::finishedLoading()void DocumentLoader::finishedLoading()void FrameLoader::finishedLoadingDocument(DocumentLoader* loader)void FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader)void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader,      const char* data, int length)void DocumentLoader::commitData(const char* bytes, int length)// Receive Datavoid WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size)void ResourceLoader::didReceiveData(ResourceHandle*, const char* data, int length,      int encodedDataLength)void ResourceLoader::didReceiveData(const char* data, int length,     long long encodedDataLength, bool allAtOnce)void MainResourceLoader::addData(const char* data, int length, bool allAtOnce)void DocumentLoader::receivedData(const char* data, int length)void DocumentLoader::commitLoad(const char* data, int length)void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader,     const char* data, int length)void DocumentLoader::commitData(const char* bytes, int length)

This process is actually divided into two steps. One step is that Chromium_net receives data, and the other is Chromium_net notifies WebKit that the data has been downloaded and can be completed. Both processes call FrameLoaderClienetAndroid: committedLoad (). But the parameters are different. When we finish, we will set the input length to 0. In this way, we will notify WebKit that the data has been transferred and the reporter WebKit will start to use the data obtained by commitData for parsing, create a Dom Tree and a Render Tree. Next section describes how to build a Dom Tree Render Tree.



Copyright statement:
Reprint Article please indicate the source of the original, any for commercial purposes, please contact tan Haiyan myself: hyman_tan@126.com

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.