• QT分析之WebKit(三)


    QT分析之WebKit(三)

    分三个阶段对QWebView进行分析:初始化(获取数据)、HTML解析、页面显示。从QT自带的文档中可以知道:

    QWebView -> QWebPage => QWebFrame(一个QWebPage含多个QWebFrame)

    在界面中选择了Open URL,输入URL之后,调用的是:void MainWindow::openUrl()

    void MainWindow::openUrl()
    {
        bool ok;
        QString url = QInputDialog::getText(this, tr("Enter a URL"),
                      tr("URL:"), QLineEdit::Normal, "http://", &ok);

        if (ok && !url.isEmpty()) {
            centralWidget->webView->setUrl(url);
        }
    }

    调用的是QWebView::setUrl()

    void QWebView::setUrl(const QUrl &url)
    {
        page()->mainFrame()->setUrl(url);
    }

    其中page()是获取QWebPage指针,QWebPage::mainFrame()获取的是QWebFrame指针。

    所以调用的是:QWebFrame::setUrl()

    void QWebFrame::setUrl(const QUrl &url)
    {
        d->frame->loader()->begin(ensureAbsoluteUrl(url));
        d->frame->loader()->end();
        load(ensureAbsoluteUrl(url));
    }

    ensureAbsoluteUrl()函数作用是,确保URL是绝对URL(完整URL)。所谓相对URL是指没有输入http://或者https://等前缀的web地址。先看第一句的调用。其中隐含了从QUrl到KURL的变换。

    void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
    {
        // We need to take a reference to the security origin because |clear|
        // might destroy the document that owns it.
        RefPtr<SecurityOrigin> forcedSecurityOrigin = origin;

        bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));
        clear(resetScripting, resetScripting);      // 清除上一次的数据,为本次装载准备
        if (resetScripting)   
            m_frame->script()->updatePlatformScriptObjects();    // 在Windows平台下,这是空函数
        if (dispatch)
            dispatchWindowObjectAvailable();

        m_needsClear = true;
        m_isComplete = false;
        m_didCallImplicitClose = false;
        m_isLoadingMainResource = true;
        m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument;

        KURL ref(url);
        ref.setUser(String());
        ref.setPass(String());
        ref.setRef(String());
        m_outgoingReferrer = ref.string();
        m_URL = url;

        RefPtr<Document> document;
        
        if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType))
            document = PluginDocument::create(m_frame);
        else
            document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());    // 创建DOM文件,m_responseMIMEType不同实体不同。

    // 如果是"text/html"创建HTMLDocument实体;"application/xhtml+xml"创建Document实体

    // 如果是"application/x-ftp-directory"则是FTPDirectoryDocument实体

    // text/vnd.wap.wml 对应 WMLDocument 实体(无线)

    // "application/pdf" /"text/plain" 对应 PluginDocument实体

    // 如果是MediaPlayer::supportsType(type),创建的是MediaDocument实体

    // "image/svg+xml" 对应 SVGDocument实体
        m_frame->setDocument(document);

        document->setURL(m_URL);
        if (m_decoder)
            document->setDecoder(m_decoder.get());
        if (forcedSecurityOrigin)
            document->setSecurityOrigin(forcedSecurityOrigin.get());

        m_frame->domWindow()->setURL(document->url());
        m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());

        updatePolicyBaseURL();   // 更新排布策略的基础URL

        Settings* settings = document->settings();
        document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically());

        if (m_documentLoader) {
            String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control");
            if (!dnsPrefetchControl.isEmpty())
                document->parseDNSPrefetchControlHeader(dnsPrefetchControl);
        }

    #if FRAME_LOADS_USER_STYLESHEET
        KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL();
        if (!userStyleSheet.isEmpty())
            m_frame->setUserStyleSheetLocation(userStyleSheet);
    #endif

        restoreDocumentState();

        document->implicitOpen();

        if (m_frame->view())
            m_frame->view()->setContentsSize(IntSize());

    #if USE(LOW_BANDWIDTH_DISPLAY)
        // Low bandwidth display is a first pass display without external resources
        // used to give an instant visual feedback. We currently only enable it for
        // HTML documents in the top frame.
        if (document->isHTMLDocument() && !m_frame->tree()->parent() && m_useLowBandwidthDisplay) {
            m_pendingSourceInLowBandwidthDisplay = String();
            m_finishedParsingDuringLowBandwidthDisplay = false;
            m_needToSwitchOutLowBandwidthDisplay = false;
            document->setLowBandwidthDisplay(true);
        }
    #endif
    }

    看其中document->implicitOpen()的代码:

    void Document::implicitOpen()
    {
        cancelParsing();

        clear();
        m_tokenizer = createTokenizer();
        setParsing(true);
    }

    Tokenizer *HTMLDocument::createTokenizer()
    {
        bool reportErrors = false;
        if (frame())
            if (Page* page = frame()->page())
                reportErrors = page->inspectorController()->windowVisible();

        return new HTMLTokenizer(this, reportErrors);
    }

    新创建的HTMLTokenizer对象,就是HTML的解析器。

    回到QWebFrame::setUrl()的第二句:d->frame->loader()->end();

    只是把上次未完的解析停止:

    void FrameLoader::endIfNotLoadingMainResource()
    {
        if (m_isLoadingMainResource || !m_frame->page())
            return;

        // 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);

        // make sure nothing's left in there
        if (m_frame->document()) {
            write(0, 0, true);
            m_frame->document()->finishParsing();

       } else
            // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but
            // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to
            // become true. An example is when a subframe is a pure text doc, and that subframe is the
            // last one to complete.
            checkCompleted();
    }

    再来看QWebFrame::setUrl()的第三句:load(ensureAbsoluteUrl(url));

    void QWebFrame::load(const QUrl &url)
    {
       load(QNetworkRequest(ensureAbsoluteUrl(url)));
    }

    新建一个QNetworkRequest对象,然后调用

        void load(const QNetworkRequest &request,
                  QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
                  const QByteArray &body = QByteArray());
    看其代码:

    void QWebFrame::load(const QNetworkRequest &req,
                         QNetworkAccessManager::Operation operation,
                         const QByteArray &body)
    {
        if (d->parentFrame())
            d->page->d->insideOpenCall = true;

        QUrl url = ensureAbsoluteUrl(req.url());

        WebCore::ResourceRequest request(url);

        switch (operation) {
            case QNetworkAccessManager::HeadOperation:
                request.setHTTPMethod("HEAD");
                break;
            case QNetworkAccessManager::GetOperation:
                request.setHTTPMethod("GET");
                break;
            case QNetworkAccessManager::PutOperation:
                request.setHTTPMethod("PUT");
                break;
            case QNetworkAccessManager::PostOperation:
                request.setHTTPMethod("POST");
                break;
            case QNetworkAccessManager::UnknownOperation:
                // eh?
                break;
        }

        QList<QByteArray> httpHeaders = req.rawHeaderList();
        for (int i = 0; i < httpHeaders.size(); ++i) {
            const QByteArray &headerName = httpHeaders.at(i);
            request.addHTTPHeaderField(QString::fromLatin1(headerName), QString::fromLatin1(req.rawHeader(headerName)));
        }

        if (!body.isEmpty())
            request.setHTTPBody(WebCore::FormData::create(body.constData(), body.size()));

        d->frame->loader()->load(request);

        if (d->parentFrame())
            d->page->d->insideOpenCall = false;
    }

    看关键的FrameLoader::load()

    void FrameLoader::load(const ResourceRequest& request)
    {
        load(request, SubstituteData());
    }

    void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData)
    {
        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;
        load(m_client->createDocumentLoader(request, substituteData).get());
    }

    上面m_client对应的是FrameLoaderClientQt实体,m_client->createDocumentLoader()创建的是DocumentLoader对象。进一步看FrameLoader::load(DocumentLoader *)的代码:

    void FrameLoader::load(DocumentLoader* newDocumentLoader)
    {
        ResourceRequest& r = newDocumentLoader->request();
        addExtraFieldsToMainResourceRequest(r);
        FrameLoadType type;

        if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
            r.setCachePolicy(ReloadIgnoringCacheData);
            type = FrameLoadTypeSame;
        } else
            type = FrameLoadTypeStandard;

        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)) {
            ASSERT(type == FrameLoadTypeStandard);
            type = FrameLoadTypeReload;
        }

       loadWithDocumentLoader(newDocumentLoader, type, 0);
    }

  • 相关阅读:
    UWP Composition API
    UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(二)
    UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(一)
    UWP 图片剪切旋转工具
    整理UWP中网络和设备信息获取的帮助类,需要的拿走。
    UWP 设备分辨率
    UWP webview 键盘bug,回退页面,键盘会弹一下。
    UWP 解决Webview在Pivot里面无法左右滑动的问题
    ngnix https
    nginx www解析失败问题解决
  • 原文地址:https://www.cnblogs.com/lexus/p/2499837.html
Copyright © 2020-2023  润新知