程序人生 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扫描分析。