轉載出處:http://blog.csdn.net/awebkit
網頁是如何正確顯示出來而不會亂碼的呢?本文就這個問題討論一下
當我們調用loadURL之後,經過一系列的check,終於,我們到了MainResourceLoader,開始load->loadNow,在這裏,我們發送了網絡請求。
下面這段大致講了網絡部分的結構,熟悉這部分的可以直接忽略。
ResourceHandleManager作爲網絡部分的管理者,需要聽命ResourceHandle這個決策者,而ResourceHandle有個祕書代爲實現一切,這個祕書就是ResourceHandleInternal。
我們發送網絡請求,就是把請求告訴ResourceHanlde,ResourceHandle調用ResourceHandle::create,往ResourceHandleManager裏面添加一個job(ResourceHanlde)。ResourceHandleManager由timer驅動下載任務,下載完成後通知ResourceHandle,但是,ResourceHandle的工作太重了,就把這部分工作給了ResourceHandleClient。ResourceHandleClient就是負責下載的資源管理的。
ResouceHandleClient只是個接口,實現部分又有ResourceLoader來承接公共部分,ResourceLoader的子類MainResourceLoader和SubResourceLoader來承擔具體的任務。
說了這麼多廢話,如果前面你都懂,下面開始正題了
網絡把資源給了MainResourceLoader之後(didReceiveData),然後通知DocumentLoader(documentLoader()->receivedData),觸發commitLoad,調用FrameLoaderClient的commitLoad( frameLoader->client()->committedLoad)
一般的FrameLoaderClient中的committedLoad如下流程
- void FrameLoaderClient::committedLoad(WebCore::DocumentLoader* loader, const char* data, int length)
- {
- if (!m_pluginView) {
- ASSERT(loader->frame());
- loader->commitData(data, length);
- Frame* coreFrame = loader->frame();
- if (coreFrame && coreFrame->document()->isMediaDocument())
- loader->cancelMainResourceLoad(coreFrame->loader()->client()->pluginWillHandleLoadError(loader->response()));
- }
- ...
- }
調用到DocumentLoader的commitData方法裏面,在這裏,就涉及到了編碼選擇問題,請看代碼
- void DocumentLoader::commitData(const char* bytes, int length)
- {
- // Set the text encoding. This is safe to call multiple times.
- bool userChosen = true;
- String encoding = overrideEncoding();
- if (encoding.isNull()) {
- userChosen = false;
- encoding = response().textEncodingName();
- }
- m_writer.setEncoding(encoding, userChosen);
- ASSERT(m_frame->document()->parsing());
- m_writer.addData(bytes, length);
- }
我沒有看到overrideEncoding在這裏有什麼用,所以,這時候,使用的是HTTP Response的TextEncodingName。然後,就告訴DocumentWriter接收數據。
DocumentWriter的addData會把任務分配給DocumentParser來解析。對於dom來說,這項任務又落在了DocumentParser的子類DecodedDataDocumentParser的身上。
我們來看看DecodedDataDocumentParser的appendBytes函數,如下
- void DecodedDataDocumentParser::appendBytes(DocumentWriter* writer , const char* data, int length, bool shouldFlush)
- {
- if (!length && !shouldFlush)
- return;
- TextResourceDecoder* decoder = writer->createDecoderIfNeeded();
- String decoded = decoder->decode(data, length);
- if (shouldFlush)
- decoded += decoder->flush();
- if (decoded.isEmpty())
- return;
- writer->reportDataReceived();
- append(decoded);
- }
在這裏,我們終於看到了decode函數。稍等,我們看看decoder是如何創建出來的。
- TextResourceDecoder* DocumentWriter::createDecoderIfNeeded()
- {
- ...
- if (Settings* settings = m_frame->settings()) {
- m_decoder = TextResourceDecoder::create(m_mimeType,
- settings->defaultTextEncodingName(),
- settings->usesEncodingDetector());
- Frame* parentFrame = m_frame->tree()->parent();
- if (canReferToParentFrameEncoding(m_frame, parentFrame))
- m_decoder->setHintEncoding(parentFrame->document()->decoder());
- } else
- m_decoder = TextResourceDecoder::create(m_mimeType, String());
- Frame* parentFrame = m_frame->tree()->parent();
- if (m_encoding.isEmpty()) {
- if (canReferToParentFrameEncoding(m_frame, parentFrame))
- m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame);
- } else {
- m_decoder->setEncoding(m_encoding,
- m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
- }
- m_frame->document()->setDecoder(m_decoder.get());
這裏,我們看到如果用戶選擇了編碼,就按照用戶的編碼,如果沒有選擇,就使用HTTP header的編碼。
再回到appendBytes中,最後調用append,把解碼後的數據給了誰呢?給了HTMLDocumentParser,然後就是開始分析tag等。