QT分析之WebKit(五)

28 篇文章 0 订阅

程序人生 2010-02-21 13:52:33 阅读377 评论0   字号: 订阅

前面分析WebView初始化的时候,在QNetworkReplyHandler::start()里有设定读取数据的处理函数:
    connect(m_reply, SIGNAL(finished()),
            this, SLOT(finish()), Qt::QueuedConnection);

    // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we
    // can send the response as early as possible
    if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
        connect(m_reply, SIGNAL(metaDataChanged()),
                this, SLOT(sendResponseIfNeeded()), Qt::QueuedConnection);

    connect(m_reply, SIGNAL(readyRead()),
            this, SLOT(forwardData()), Qt::QueuedConnection);

先看QNetworkReplyHandler::forwardData()
void QNetworkReplyHandler::forwardData()
{
    m_shouldForwardData = (m_loadMode == LoadDeferred);
    if (m_loadMode == LoadDeferred)
        return;

    sendResponseIfNeeded();

    // don't emit the "Document has moved here" type of HTML
    if (m_redirected)
        return;

    if (!m_resourceHandle)
        return;

    QByteArray data = m_reply->read(m_reply->bytesAvailable());

    ResourceHandleClient* client = m_resourceHandle->client();
    if (!client)
        return;

    if (!data.isEmpty())
        client->didReceiveData(m_resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/);
}
实际就是两个调用:read()和didReceiveData()。其中QNetworkReply::read()前面分析过不再重复;
ResourceHandleClient* client->didReceiveData()实际调用的是MainResourceLoader::didReceiveData()
void MainResourceLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce)
{
    ASSERT(data);
    ASSERT(length != 0);

    // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
    // See <rdar://problem/6304600> for more details.
#if !PLATFORM(CF)
    ASSERT(!defersLoading());
#endif
 
    // The additional processing can do anything including possibly removing the last
    // reference to this object; one example of this is 3266216.
    RefPtr<MainResourceLoader> protect(this);

    ResourceLoader::didReceiveData(data, length, lengthReceived, allAtOnce);
}
进一步看其调用:
void ResourceLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce)
{
    // Protect this in this delegate method since the additional processing can do
    // anything including possibly derefing this; one example of this is Radar 3266216.
    RefPtr<ResourceLoader> protector(this);

    addData(data, length, allAtOnce);
    // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
    // However, with today's computers and networking speeds, this won't happen in practice.
    // Could be an issue with a giant local file.
    if (m_sendResourceLoadCallbacks && m_frame)
        frameLoader()->didReceiveData(this, data, length, static_cast<int>(lengthReceived));
}
在ResourceLoader类中addData()是虚函数,client->didReceiveData()中client指针实际的实体为MainResourceLoader对象,所以addData()先调用MainResourceLoader::addData()
void MainResourceLoader::addData(const char* data, int length, bool allAtOnce)
{
    ResourceLoader::addData(data, length, allAtOnce);
    frameLoader()->receivedData(data, length);
}
这里只有两个调用,前一个是将接收到的数据保存到一个buffer中,供后续语法扫描使用(猜测的),暂不深入分析。看frameLoader->receivedData()
void FrameLoader::receivedData(const char* data, int length)
{
    activeDocumentLoader()->receivedData(data, length);
}

void DocumentLoader::receivedData(const char* data, int length)
{   
    m_gotFirstByte = true;
    if (doesProgressiveLoad(m_response.mimeType()))
        commitLoad(data, length);
}
其中doesProgressiveLoad()会测试MIME的类型,重点是commitLoad()

void DocumentLoader::commitLoad(const char* data, int length)
{
    // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
    // by starting a new load, so retain temporarily.
    RefPtr<DocumentLoader> protect(this);

    commitIfReady();
    if (FrameLoader* frameLoader = DocumentLoader::frameLoader())
        frameLoader->committedLoad(this, data, length);
}
前面一个调用:commitIfReady()是清理前一次页面扫描的中间数据;committedLoad()才是正题。

void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length)
{
    if (ArchiveFactory::isArchiveMimeType(loader->response().mimeType()))
        return;
    m_client->committedLoad(loader, data, length);
}
其中m_client指向的是FrameLoaderClientQT对象实体。

void FrameLoaderClientQt::committedLoad(WebCore::DocumentLoader* loader, const char* data, int length)
{
    if (!m_pluginView) {
        if (!m_frame)
            return;
        FrameLoader *fl = loader->frameLoader();
        if (m_firstData) {
            fl->setEncoding(m_response.textEncodingName(), false);
            m_firstData = false;
        }
        fl->addData(data, length);
    }
   
    // We re-check here as the plugin can have been created
    if (m_pluginView) {
        if (!m_hasSentResponseToPlugin) {
            m_pluginView->didReceiveResponse(loader->response());
            // didReceiveResponse sets up a new stream to the plug-in. on a full-page plug-in, a failure in
            // setting up this stream can cause the main document load to be cancelled, setting m_pluginView
            // to null
            if (!m_pluginView)
                return;
            m_hasSentResponseToPlugin = true;
        }
        m_pluginView->didReceiveData(data, length);
    }
}

其中fl->setEncoding()是根据服务器返回的HTML数据流设定编码格式(例如:中文gb2312),另外处理了其他一些事情,例如Redirect等。fl->addData()是关键:

void FrameLoader::addData(const char* bytes, int length)
{
    ASSERT(m_workingURL.isEmpty());
    ASSERT(m_frame->document());
    ASSERT(m_frame->document()->parsing());
    write(bytes, length);
}
上面的FrameLoader::write()调用,启动了HTML/JS分析扫描。后一篇深入HTML扫描分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值