网页解码浅析

转载出处: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等。

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