網頁解碼淺析

轉載出處: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如下流程

[c-sharp] view plaincopy
  1. void FrameLoaderClient::committedLoad(WebCore::DocumentLoader* loader, const char* data, int length)  
  2. {  
  3.     if (!m_pluginView) {  
  4.         ASSERT(loader->frame());  
  5.         loader->commitData(data, length);  
  6.         Frame* coreFrame = loader->frame();  
  7.         if (coreFrame && coreFrame->document()->isMediaDocument())  
  8.             loader->cancelMainResourceLoad(coreFrame->loader()->client()->pluginWillHandleLoadError(loader->response()));  
  9.     }     
  10.    ...  
  11. }  

      調用到DocumentLoader的commitData方法裏面,在這裏,就涉及到了編碼選擇問題,請看代碼

[c-sharp] view plaincopy
  1. void DocumentLoader::commitData(const char* bytes, int length)  
  2. {  
  3.     // Set the text encoding.  This is safe to call multiple times.  
  4.     bool userChosen = true;  
  5.     String encoding = overrideEncoding();  
  6.     if (encoding.isNull()) {  
  7.         userChosen = false;  
  8.         encoding = response().textEncodingName();  
  9.     }     
  10.     m_writer.setEncoding(encoding, userChosen);  
  11.     ASSERT(m_frame->document()->parsing());  
  12.     m_writer.addData(bytes, length);  
  13. }  

      我沒有看到overrideEncoding在這裏有什麼用,所以,這時候,使用的是HTTP Response的TextEncodingName。然後,就告訴DocumentWriter接收數據。

      DocumentWriter的addData會把任務分配給DocumentParser來解析。對於dom來說,這項任務又落在了DocumentParser的子類DecodedDataDocumentParser的身上。

      我們來看看DecodedDataDocumentParser的appendBytes函數,如下

[c-sharp] view plaincopy
  1. void DecodedDataDocumentParser::appendBytes(DocumentWriter* writer , const char* data, int length, bool shouldFlush)  
  2. {  
  3.     if (!length && !shouldFlush)  
  4.         return;  
  5.     TextResourceDecoder* decoder = writer->createDecoderIfNeeded();  
  6.     String decoded = decoder->decode(data, length);  
  7.     if (shouldFlush)  
  8.         decoded += decoder->flush();  
  9.     if (decoded.isEmpty())  
  10.         return;  
  11.     writer->reportDataReceived();  
  12.     append(decoded);  
  13. }  

      在這裏,我們終於看到了decode函數。稍等,我們看看decoder是如何創建出來的。

[c-sharp] view plaincopy
  1. TextResourceDecoder* DocumentWriter::createDecoderIfNeeded()  
  2. {  
  3.    ...  
  4.    if (Settings* settings = m_frame->settings()) {  
  5.             m_decoder = TextResourceDecoder::create(m_mimeType,  
  6.                 settings->defaultTextEncodingName(),  
  7.                 settings->usesEncodingDetector());  
  8.             Frame* parentFrame = m_frame->tree()->parent();  
  9. if (canReferToParentFrameEncoding(m_frame, parentFrame))  
  10.                 m_decoder->setHintEncoding(parentFrame->document()->decoder());  
  11.         } else  
  12.             m_decoder = TextResourceDecoder::create(m_mimeType, String());  
  13.         Frame* parentFrame = m_frame->tree()->parent();  
  14.         if (m_encoding.isEmpty()) {  
  15.             if (canReferToParentFrameEncoding(m_frame, parentFrame))  
  16.                 m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame);  
  17.         } else {  
  18.             m_decoder->setEncoding(m_encoding,  
  19.                 m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);  
  20.         }  
  21.         m_frame->document()->setDecoder(m_decoder.get());  

      這裏,我們看到如果用戶選擇了編碼,就按照用戶的編碼,如果沒有選擇,就使用HTTP header的編碼。

      再回到appendBytes中,最後調用append,把解碼後的數據給了誰呢?給了HTMLDocumentParser,然後就是開始分析tag等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章