admin';?>

首页 / 开发交流

ffv52 parse

By admin •  2018-06-21 22:10:00 •  138次点击
embedding/browser/nsWebBrowser.cpp
nsWebBrowser::LoadURI(const char16_t* aURI, uint32_t aLoadFlags,
                      nsIURI* aReferringURI,
                      nsIInputStream* aPostDataStream,
                      nsIInputStream* aExtraHeaderStream)
{
   ....
   mDocShellAsNav->LoadURI(
 aURI, aLoadFlags, aReferringURI, aPostDataStream, aExtraHeaderStream);
这里nsDocShell mDocShellAsNav;
nsDocShell::DoURILoad(...
{
  ...
    rv = NS_NewChannelInternal(getter_AddRefs(channel),
                               aURI,
                               loadInfo,
                               nullptr,   // loadGroup
                               static_cast<nsiinterfacerequestor*>(this),
                               loadFlags);

这里创建了channel,并参与到后续生成InputStream的处理,这里根据路径,判断生成何种类型的nsIChannel,我这里加载的是本地文件,所以生成的是nsFileChannel, netwerk/protoco/file/nsFileChannel.h是它的类型声明
至此,nsIChannel与文件名aURI关联起来了。
docshell/base/nsDocShell.cpp
nsDocShell::DoChannelLoad(nsIChannel* aChannel,
                          nsIURILoader* aURILoader,
                          bool aBypassClassifier)
{
    .....
  rv = aURILoader->OpenURI(aChannel, openFlags, this);
nsURILoader   aURILoader; //uriloader/base/nsURILoader.cpp
nsURILoader变换使用 nsIStreamListener和nsIChannel, 实现文件流加载
nsDocShell  》 nsURILoader  》 nsIStreamListener, nsIChannel
NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
                                   uint32_t aFlags,
                                   nsIInterfaceRequestor *aWindowContext)
{
  ....
  nsCOMPtr<nsistreamlistener> loader;
  nsresult rv = OpenChannel(channel,
                            aFlags,
                            aWindowContext,
                            false,
                            getter_AddRefs(loader));
  
  ...
 if (NS_SUCCEEDED(rv)) {
    // this method is not complete!!! Eventually, we should first go
    // to the content listener and ask them for a protocol handler...
    // if they don't give us one, we need to go to the registry and get
    // the preferred protocol handler. 

    // But for now, I'm going to let necko do the work for us....
    rv = channel->AsyncOpen(loader, nullptr); 

//这里channel变nsIStreamListener,为加载内容做准备
nsresult nsURILoader::OpenChannel(nsIChannel* channel,
                                  uint32_t aFlags,
                                  nsIInterfaceRequestor* aWindowContext,
                                  bool aChannelIsOpen,
                                  nsIStreamListener** aListener)
{
...
  // we need to create a DocumentOpenInfo object which will go ahead and open
  // the url and discover the content type....
  RefPtr<nsdocumentopeninfo> loader =
    new nsDocumentOpenInfo(aWindowContext, aFlags, this);
  此处的loader是什么呢?此处的loader就是要返回的nsIStreamListener** aListener
。。。



 rv = channel->AsyncOpen(loader, nullptr);
  =>   nsBaseChannel::BeginPumpingData()
在nsBaseChannel::BeginPumpingData() 中创建了nsInputStreamPump mPump;用于AsyncRead
nsresult
nsBaseChannel::BeginPumpingData()
{
...
  rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0,
                                 true);
  if (NS_SUCCEEDED(rv))
    rv = mPump->AsyncRead(this, nullptr);
AsyncRead中会设置好回调函数并注册nsInputStreamReadyEvent事件。至此nsDocShell::LoadURI工作完成。

主消息中获得事件到达通知,触发之前设置的回调
nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream)
{
 .....
       switch (mState) {
        case STATE_START:
            nextState = OnStateStart();
            break;
        case STATE_TRANSFER:
            nextState = OnStateTransfer();   读数据
            break;
        case STATE_STOP:
            mRetargeting = false;
            nextState = OnStateStop(); 触发解析
            break;
在OnStateTransfer中,netwerk/base/nsInputStreamPump.cpp
nsInputStreamPump::OnStateTransfer()
{
...
 rv = mListener->OnDataAvailable(this, mListenerContext,
                                                mAsyncStream, mStreamOffset,
                                                odaAvail);

nsFileChannel mListener;
netwerk/base/nsBaseChannel.cpp
nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
                               nsIInputStream *stream, uint64_t offset,
                               uint32_t count)
{
  SUSPEND_PUMP_FOR_SCOPE();

  nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream,
                                           offset, count);
nsDocumentOpenInfo  mListener;
uriloader/base/nsURILoader.cpp
nsDocumentOpenInfo::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
                                    nsIInputStream * inStr,
                                    uint64_t sourceOffset, uint32_t count)
{
  // if we have retarged to the end stream listener, then forward the call....
  // otherwise, don't do anything

  nsresult rv = NS_OK;
  
  if (m_targetStreamListener)
    rv = m_targetStreamListener->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count);
  return rv;
}
nsHtml5StreamListener targetStreamListener; 
parser/html/nsHtml5StreamListener.cpp
nsHtml5StreamListener::OnDataAvailable(nsIRequest* aRequest,
                                       nsISupports* aContext,
                                       nsIInputStream* aInStream,
                                       uint64_t aSourceOffset,
                                       uint32_t aLength)
{
  if (MOZ_UNLIKELY(!mDelegate)) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  return mDelegate->OnDataAvailable(aRequest,
                                    aContext,
                                    aInStream,
                                    aSourceOffset,
                                    aLength);
   此处的mDelegate显然就是nsHtml5StreamParser
 }

parser/html/nsHtml5StreamParser.cpp
nsresult
nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
                                     nsISupports* aContext,
                                     nsIInputStream* aInStream,
                                     uint64_t aSourceOffset,
                                     uint32_t aLength)
{
。。。
       // Read directly from response buffer.
    rv = aInStream->ReadSegments(CopySegmentsToParser, this, aLength,
                                 &totalRead);
此处的aInStream是nsPipeInputStream,真正读文件内容的地方。
xpcom/io/nsPipe3.cpp
NS_IMETHODIMP
nsPipeInputStream::ReadSegments(nsWriteSegmentFun aWriter,
                                void* aClosure,
                                uint32_t aCount,
                                uint32_t* aReadCount)
{
。。。

  *aReadCount = 0;
  while (aCount) {
    AutoReadSegment segment(mPipe, mReadState, aCount);
    rv = segment.Status();
    if (NS_FAILED(rv)) {
      // ignore this error if we've already read something.
      if (*aReadCount > 0) {
        rv = NS_OK;
        break;
      }
      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
        // pipe is empty
        if (!mBlocking) {
          break;
        }
        // wait for some data to be written to the pipe
        rv = Wait();
        if (NS_SUCCEEDED(rv)) {
          continue;
        }
      }
      // ignore this error, just return.
      if (rv == NS_BASE_STREAM_CLOSED) {
        rv = NS_OK;
        break;
      }
      mPipe->OnInputStreamException(this, rv);
      break;
    }

    uint32_t writeCount;
    while (segment.Length()) {
      writeCount = 0;

      rv = aWriter(static_cast<nsiasyncinputstream*>(this), aClosure,
                   segment.Data(), *aReadCount, segment.Length(), &writeCount);

      if (NS_FAILED(rv) || writeCount == 0) {
        aCount = 0;
        // any errors returned from the writer end here: do not
        // propagate to the caller of ReadSegments.
        rv = NS_OK;
        break;
      }

      NS_ASSERTION(writeCount <= segment.Length(), "wrote more than expected");
      segment.Advance(writeCount);
      aCount -= writeCount;
      *aReadCount += writeCount;
      mLogicalOffset += writeCount;
    }
  }
。。。
}

nsHtml5StreamParser::ParseAvailableData()
{
  ...
      =》
在OnStateStop中
线程消息中,触发 parser/html/nsHtml5StreamParser.cpp
nsHtml5StreamParser::DoStopRequest()
{ 
。。。
   ParseAvailableData();

nsHtml5StreamParser::ParseAvailableData()
{
  。。。
   mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);

parser/html/nsHtml5Tokenizer.cpp
nsHtml5Tokenizer::tokenizeBuffer(nsHtml5UTF16Buffer* buffer)
nsHtml5Tokenizer::stateLoop 这里是详细分析并确定tagname的地方,处理比较复杂。
nsHtml5Tokenizer::strBufToElementNameString()
parser/html/nsHtml5TreeBuilder.cpp
nsHtml5TreeBuilder::startTag  顾名思义,这里才是由tagname,构建tagNode的地方
nsHtml5TreeBuilder::appendToCurrentNodeAndPushElement(nsHtml5ElementName* elementName, nsHtml5HtmlAttributes* attributes)
{
   。。。
  nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt);

接下来
parser/html/nsHtml5TreeOpExecutor.cpp
nsHtml5TreeOpExecutor::RunFlushLoop()
{
...
  for (;;) {
    ...
    for (nsHtml5TreeOperation* iter = const_cast<nshtml5treeoperation*>(first);;) {
       ....
      nsresult rv = iter->Perform(this, &scriptElement);



parser/html/nsHtml5TreeOperation.cpp
nsHtml5TreeOperation::Perform中根据mOpCode类型,不断循环创建,添加dom节点到nsHtml5TreeOpExecutor所在的doc上。
nsHtml5TreeOperation::CreateElement   这个是创建dom节点即HTMLSharedElement节点的地方
这个是追加节点的地方
nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,  nsIContent** aScriptElement)
{
  switch(mOpCode) {
...
    case eTreeOpAppend: {
      nsIContent* node = *(mOne.node);
      nsIContent* parent = *(mTwo.node);
      return Append(node, parent, aBuilder);
    }
...
    case eTreeOpAppendToDocument: {
      nsIContent* node = *(mOne.node); 此处node节点为moz::HTMLSharedElement即网页上的dom节点
      return AppendToDocument(node, aBuilder);这里的aBuilder为nsHtml5TreeOpExecutor
    }


显然最后的节点都存在nsHtml5TreeOpExecutor中的nsHtml5DocumentBuilder->mOwnedElements中了。 parser/html/nsHtml5TreeOpExecutor.h
class nsHtml5TreeOpExecutor final : public nsHtml5DocumentBuilder
, public nsIContentSink
, public nsAHtml5TreeOpSink
, public mozilla::LinkedListElement
{ 


parser/html/nsHtml5TreeOperation.cpp
nsHtml5TreeOperation::Append(nsIContent* aNode,
                             nsIContent* aParent,
                             nsHtml5DocumentBuilder* aBuilder)
{
...
 rv = aParent->AppendChildTo(aNode, false);


而aParent是FragmentOrElement节点
dom/base/FragmentOrElement.h
class FragmentOrElement : public nsIContent
结果节点都放在nsAttrAndChildArray FragmentOrElement :mAttrsAndChildren中。
nsAttrAndChildArray  管理着dom上所有nsINode节点。
dom/base/nsAttrAndChildArray.h

dom/base/nsINode.h
至此完成parser的工作。
接着处理布局
nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,  nsIContent** aScriptElement)
{
  switch(mOpCode) {
...
    case eTreeOpStartLayout: {
      aBuilder->StartLayout(); // this causes a notification flush anyway
      return NS_OK;
    }
aBuilder->StartLayout();中为节点的显示做准备

在OnStateStart中
uint32_t
nsInputStreamPump::OnStateStart()
{
 。。。
   rv = mListener->OnStartRequest(this, mListenerContext);
mListener就是之前的nsFileChannel

nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
。。。
    mListener->OnStartRequest(this, mListenerContext);
nsCOMPtr         mListener; nsIChannel甩锅给nsDocumentOpenInfo处理数据接收
nsDocumentOpenInfo 声明在 uriloader/base/nsURILoader.cpp
class nsDocumentOpenInfo final : public nsIStreamListener
                               , public nsIThreadRetargetableStreamListener
{
uriloader/base/nsURILoader.cpp
NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
{
   。。。
   rv = DispatchContent(request, aCtxt);
             =》 TryContentListener(m_contentListener, aChannel)    
...
   rv = m_targetStreamListener->OnStartRequest(request, aCtxt); targetStreamListener为nsHtml5StreamListener


nsHtml5StreamListener m_targetStreamListener
parser/html/nsHtml5StreamListener.cpp
nsHtml5StreamListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
nsHtml5StreamListener::OnStartRequest(nsIRequest* aRequest,
                                      nsISupports* aContext)
{
  if (MOZ_UNLIKELY(!mDelegate)) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  return mDelegate->OnStartRequest(aRequest, aContext);
}
nsHtml5StreamParser mDelegate;    parser/html/nsHtml5StreamParser.cpp
nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{ 
...
     mOwner->StartTokenizer(scriptingEnabled);
nsHtml5Parser mOwner;parser/html/nsHtml5Parser.cpp
nsHtml5Parser::StartTokenizer(bool aScriptingEnabled)
{
   。。。。
      mTokenizer->start();
 nsHtml5Tokenizer mTokenizer;
 parser/html/nsHtml5Tokenizer.cpp
nsHtml5Tokenizer::start()
{
  initializeWithoutStarting();
  tokenHandler->startTokenization(this);
}
nsHtml5TreeBuilder  tokenHandler;
parser/html/nsHtml5TreeBuilder.cpp
nsHtml5TreeBuilder::startTokenization(nsHtml5Tokenizer* self)
{


}

nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
                                       nsIChannel* aChannel)
{
   。。。 
     nsresult rv = aListener->DoContent(mContentType,
                                     isPreferred,
                                     aChannel,
                                     getter_AddRefs(m_targetStreamListener),
                                     &abort);
docshell/base/nsDSURIContentListener.cpp
nsDSURIContentListener::DoContent(const nsACString& aContentType,
                                  bool aIsContentPreferred,
                                  nsIRequest* aRequest,
                                  nsIStreamListener** aContentHandler,
                                  bool* aAbortProcess)
{
。。。
     rv = mDocShell->CreateContentViewer(aContentType, aRequest, aContentHandler);  
           =》nsCOMPtr<nsicontentviewer> viewer;
  nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
                                    aContentHandler, getter_AddRefs(viewer));
                       =》   nsresult rv = docLoaderFactory->CreateInstance("view",
                                                 aOpenedChannel,
                                                 aLoadGroup, aContentType,
                                                 this,
                                                 nullptr,
                                                 aContentHandler,
                                                 aViewer);
                                    =》  nsContentDLF::CreateInstance (
          =》Embed(viewer, "", nullptr) 注意缩进,不同的缩进对应不同调用等级
                      =》   SetupNewViewer(aContentViewer);
                                    =》  mContentViewer->Init(widget, bounds)
                                            =》layout/base/nsDocumentViewer.cpp
                                                MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(aBounds.width),
                                                                       mPresContext->DevPixelsToAppUnits(aBounds.height)),
                                                                       containerView);
                                                      =》 nsView* view = mViewManager->CreateView(tbounds, aContainerView);
                                             =》rv = view->CreateWidgetForParent(mParentWidget, initDataPtr, true, false);
                                                       =》。。。
                                                                         =》 nsWindow::Create 创建view采访对应的窗口
                                             =》 layout/base/nsDocumentViewer.cpp
                                                 rv = InitPresentationStuff(!makeCX); load css files

docLoaderFactory->CreateInstance => layout/build/nsContentDLF.cpp
nsContentDLF::CreateInstance(const char* aCommand,
                             nsIChannel* aChannel,
                             nsILoadGroup* aLoadGroup,
                             const nsACString& aContentType,
                             nsIDocShell* aContainer,
                             nsISupports* aExtraInfo,
                             nsIStreamListener** aDocListener,
                             nsIContentViewer** aDocViewer)
{
.....
     CreateDocument(aCommand,
                          aChannel, aLoadGroup,
                          aContainer, kHTMLDocumentCID,
                          aDocListener, aDocViewer);

nsContentDLF::CreateDocument(const char* aCommand,
                             nsIChannel* aChannel,
                             nsILoadGroup* aLoadGroup,
                             nsIDocShell* aContainer,
                             const nsCID& aDocumentCID,
                             nsIStreamListener** aDocListener,
                             nsIContentViewer** aContentViewer)
{
。。。。
     
  // Initialize the document to begin loading the data.  An
  // nsIStreamListener connected to the parser is returned in
  // aDocListener.
  rv = doc->StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, true);
dom/html/nsHTMLDocument.cpp
nsHTMLDocument::StartDocumentLoad(const char* aCommand,
                                  nsIChannel* aChannel,
                                  nsILoadGroup* aLoadGroup,
                                  nsISupports* aContainer,
                                  nsIStreamListener **aDocListener,
                                  bool aReset,
                                  nsIContentSink* aSink)
{
。。。。
     mParser = nsHtml5Module::NewHtml5Parser();
     mParser->MarkAsNotScriptCreated("view");
....
     // parser the content of the URI
    mParser->Parse(uri, nullptr, (void *)this);
}
parser/html/nsHtml5Parser.cpp
nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand)
{
...
      mStreamListener =
    new nsHtml5StreamListener(new nsHtml5StreamParser(mExecutor, this, mode));

}//nsDocumentOpenInfo::TryContentListener







0 回复 | 直到2018-11-19 07:41添加回复

回复

登录发表 or 还没有账号?去注册

他山跨平台混合应用开发框架

现在注册 已注册请 登入

节点列表

产品发布

Windows  Linux  Android  最新进展 

经验交流

功能建议  Windows  Linux  Android  开发交流  资源分享  js  xul 

BUG反馈

Windows  Linux  Android 

学习生活

灌水乐园  大话界面