Internet Explorer 完全解析 [3]

由于超出博客单文章最长长度,将在这里续写第二章内容。
2.3 CElement的成员函数(续)

  • CElement::SetUniqueNameHelper。对SetIdentifierHelper的封装。 设置unique name。
  • CElement::InvalidateCollection。 将CCollectionCache中的某个元素invalidate。
  • CElement::removeAttribute。 移除属性。获取对应属性的attr dispid,然后调用removeAttributeDispid来移除。
  • CElement::OnTabIndexChange。 TabIndexChange的响应函数。将对应元素置于focus状态。
  • CElement::OnEnterExitInvalidateCollections。 当有name或者id的元素进入tree中时,重新invalidate该collection。调用参数例如CMarkup::SCRIPTS_COLLECTION(<script>)、CMarkup::FRAMES_COLLECTION(<frame> <iframe>)等。
  • CElement::DoElementNameChangeCollections。 按tagname去将所有base elements都invalidate。重建WINDOW_COLLECTION。
  • InlineEvts::Connect。将元素与events给关联起来。

2.4 小结
CElement即元素之源,它很好的囊括了元素的基础操作,例如事件处理,转发、元素遍历、排列树等等,接下来,我们将要看到的是另一个大将:CMarkup。 在第二章中,CMarkup的身影多次出现,事实上,这个类和CElement关联颇多,现在让我们看一下它的真身。

第三章
3.1 CMarkup概述
CMarkup是最基本的Markup存储结构。HTML中的Hyper-Text Markup Language中也有一个Markup,现在我们要介绍的CMarkup所指代的就是这里面的Markup。Markup一词的来源是用蓝色铅笔将作者的草稿里面的东西特别标记出来(Marking up),在这里的意思是将文本与代码区分开来的东西。

例如,HTML中,<p>可以表示paragraph段落,而<a>则表示anchor超链接锚。这些“功能性的”Markup语法使他们和一般文本(Plain Text)相区分。这个就是一个Markup。这里的CMarkup可能稍有不同,向下继续阅读便可知道差距在何处。

3.2 CMarkup的部分成员变量
CMarkup也是一个重要的类,那么它的成员变量肯定也是十分重要的,以下将介绍其,以及其相关类成员变量。
1:CMarkupScriptContext

  • CStr _cstrNamespace。这个Markup的命名空间,主markup的命名空间是“window”,非主markup的命名空间是ms__idX(id1, id2....)。
  • CScriptMethodsTable _ScriptMethodsTable。存储在window对象中暴露的dispid对应的,由脚本暴露的派遣项的dispid所组成的映射表。 比较绕口,也就是说window对象会暴露出许多dispid,而脚本解释引擎又会暴露许多派遣项的dispid,这两者之间的映射关系会组成一个映射表,它们就存储在这里。
  • CStr _cstrUrl。报告这个markup里所用脚本用的。
  • ULONG _cInlineNesting。进入或者离开内联脚本时计数用的。
  • ULONG _cScriptDownloading。 脚本下载计数。
  • DWORD _dwScriptDownloadingCookie、DWORD _dwScriptCookie。 下载cookie和脚本cookie。
  • CAryScriptEnqueued _aryScriptEnqueued。 队列等待wscript引擎执行的脚本。
  • CScriptDebugDocument * _pScriptDebugDocument。 脚本调试文档相关。
  • ULONG _idxDefaultScriptHolder。 存放这个markup中默认脚本解释引擎的脚本宿主的index。
  • BOOL _fWaitScript。为TRUE的时候表明解释器正在消息循环里等待脚本执行。

2: CMarkup
CMarkup继承于CBase。同样,它也是一个巨大无比的类,它的规模和CElement不相上下。CDoc的内容将于第四章说明,CTreePosCTreeNode两个类的内容将于第五章说明。CMarkup也是实现了大量接口,这些接口内容将在下一节介绍,现在让我们先看一看它的成员变量。

  • _LoadStatus。 加载状态
  • _pHtmCtx。 HTML上下文
  • _pProgSink。 Program Sink。
  • _OmDoc。 doc frags向脚本暴露的om document默认的dispatch。
  • _pDoc。 与该markup相关联的CDoc。
  • __lMarkupTreeVersion。 markup tree版本。
  • __lMarkupContentsVersion。 markup content版本。事实上,CDoc也有这两个成员变量,版本计算方法很简单,每调用一次UpdateMarkupTreeVersion(),这两个版本的值就会加一。关联的pDoc的两个版本号也会对应加上1。
  • _pElementRoot。 Root Element。
  • _pElementMaster。 主element。
  • _TxtArray。 存储信息的数据。
  • _pRootParseCtx。 Root的parsing context。
  • _lTopElemsVersion。 顶部元素的版本。
  • _pSelRenSvcProvider。 选区状态相关,Selection Rendering Service Provider。
  • _aryANotification。 通知数据。
  • _pmpFirst。 Markup中的指针链的第一个。
  • 伸展树(Splay Tree)相关数据
    CTreePos _tpRoot。根节点
    CTreePos * _ptpFirst。保存的第一个(最左)节点
    void * _pvPool。 pool block的列表(可被释放)
    CTreeDataPos * _ptdpFree。 free list的第一个
    BYTE _abPoolInitial [ sizeof( void * ) + TREEDATA1SIZE * INITIAL_TREEPOS_POOL_SIZE ]。 TreePos对象的初始池。

另外一提,在Internet Explorer中node的维护事实上是使用了伸展树的结构。

伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由Daniel Sleator和Robert Tarjan创造。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于伸展操作。

1nul.gif
图:元素3被访问时的伸展操作,每一步都让3向根节点靠的更近 (成员变量还没写完)

3.3 CMarkup的成员函数

  • CMarkup::CMarkup。 构造函数,接受2个参数CDoc *pDoc, CElement * pElementMaster。这两个参数分别指定了Markup所属的Doc和Markup对应的Master Element。构造函数中将设置Tree Version和Contents Version为1。
  • CMarkup::~CMarkup。 析构函数。首先,清除所有的旁视指针(lookaside pointers)。lookaside指针组成的链表将是一个双向链表。如果有StyleSheetArray,那么直接调用其CBase的PrivateRelease来释放它的引用,同时删除stylesheet的lookaside。同时释放一个pDoc的引用。
  • CMarkup::UpdateMarkupTreeVersion、CMarkup::UpdateMarkupContentsVersion 。 每调用一次,给对应版本号加一。
  • CMarkup::ClearLookasidePtrs。 字面意思,清空旁视列表指针。释放CollectionCache、释放父Markup。
  • CMarkup::Init。初始化。指定传来的CRootElement*为根元素,同时也是最右最后的元素。并且根据根元素创造一个最初的Markup。
  • CMarkup::UnloadContents。 卸载内容,删除选区服务提供者,删除伸展树,删除上下文,反注册脚本上下文、删除Range选区上下文、释放HTML上下文、卸载program sink、删除StyleSheet数组。
  • CMarkup::Passivate。 释放内容。 所有能释放的都会被释放,是UnloadContents的封装,同时还会释放Style Sheet Subobj,同时调用父类的Passivate。
  • CMarkup::DoEmbedPointers。 将未绑定的node放入伸展树。操作步骤如下:
    1、删除第一个元素,具体方法: a)获取第一个元素存一个副本,然后取得它的下一个元素; b)如果下一个元素不为空,那么把它的前一个元素置为空(不要忘了这是一个双向链表);c)将副本的前后都置为空;
    nulpw3.png
    操作情况大致如上,操作后backup还存有一个独立节点,就是之前的First,同时节点链表中不再有第一个元素。
    2、假设现在这个backup里面放着的是一个Text类型的Markup,由于Markup有顺序,如果有两个以上Markup同时指向一个Text段中间,当第一个Markup被选中后,伸展树操作会导致它出现一次split,会导致后面的Markup就会指向无效的ich,所以这里还需要判断。
    当当前指向的内容超过前一个Text长度范围时,将这个Markup的chRef一直减去前一个文本引用的cch,这样,肯定会有一刻满足chRef < cch。同时,只要文本引用还有效,就一直将文本引用向前移动(Ref的NextTreePos),这样可以确定split的右手侧,也会顺便调整这个指针。
    3、做完上述操作之后,如果当前前一个元素文本节点,同时ref的chRef小于ref的cch,这时做一次split.
    4、以backup为准,调整gravity和cling,新建一个pointerpos
    5、当backup的ref的ichref == cch时,将backup插入。这样可以保证它一直在text pos的最末尾。
    6、设置embedded为TRUE,设置ichref为0。~
  • CMarkup::GetDD。获取这个Markup的默认Dispatch对象。
  • CMarkup::EnsureScriptContext。 确保Script Context是有效的。初始化命名空间,
  • CMarkup::PrivateQueryInterface。 QueryInterface的实现。
  • CMarkup::Load。 多个重载函数都是对CMarkup::Load (HTMLOADINFO * phtmloadinfo)的封装,被封装的Load创建html context,并当Markup是主markup或者html是异步下载时会创建program sink,并将其关联到之前创建的html context上,设置URL,然后下载网页内容。
  • CMarkup::StopDownload。 停止Load函数(调用html context的SetLoad(FALSE, NULL, FALSE)),释放html context。
  • CMarkup::LoadStatus。 返回LoadStatus。
  • CMarkup::OnLoadStatus。LoadStatus变化时的回调函数,根据状态决定是否向CDoc发送消息,或是清理html context等。
  • CMarkup::EnsureTitle。 没有title时,创建一个CElement,类型为ETAG_TITLE_ELEMENT,并添加成Head Element。
  • CMarkup::AddHeadElement。 获取第一个TITLE,同时给head里的元素加引用。
  • CMarkup::SetXML。 将不认识的tag都当作xml tag处理,调用SetGenericParse(_fXML)。
  • CMarkup::PasteClipboard。 从剪贴板粘贴东西,获取剪贴板的对象,然后传到Markup对应的CDoc里,调用AllowPaste来传递剪贴板对象。
  • CMarkup::PasteUnixQuickTextToRange。 CLightDTEngine的同名函数的封装。
  • CMarkup::SetModified。 告诉CDoc数据变化了(OnDataChange)。
  • CMarkup::createTextRange。参数为(IHTMLTxtRange * * ppDisp, CElement * pElemContainer),对4个参数的createTextRange的封装,直接调用createTextRange(ppDisp, pElemContainer, NULL, NULL, TRUE)。
     
    CMarkup::createTextRange(IHTMLTxtRange * * ppDisp, CElement * pElemContainer, IMarkupPointer *pLeft, IMarkupPointer *pRight, BOOL fAdjustPointers)
    对整个文档做一次自动获取range的操作。具体操作:
    根据pElemContainer来建立一个CAutoRange对象,初始化TextRange的Lookaside列表,pLeft和pRight都设置时,对pAutoRange设置左右Markup,否则将pElemContainer作为TextRange设置到pAutoRange里。
    将pAutoRange返回到IHTMLTxtRange*中,引用计数加一。
  • CMarkup::AcceptingUndo。返回是否应该撤销。
  • CMarkup::OwningDoc。 对pDoc的主Markup进行一次QueryInterface,返回ppDoc,即所有者。
  • CMarkup::AddSegment/CMarkup::AddElementSegment/CMarkup::MovePointersToSegment/CMarkup::GetElementSegment/CMarkup::MoveSegmentToPointers/CMarkup::SetElementSegment/CMarkup::ClearSegment/CMarkup::ClearSegments/CMarkup::ClearElementSegments/CMarkup::GetSegmentCount/CMarkup::EnsureSelRenSvc/CMarkup::GetSelectionChunksForLayout/CMarkup::GetFlattenedSelection/CMarkup::HideSelection/CMarkup::ShowSelection/CMarkup::InvalidateSelection/CMarkup::IsElementSelected/CMarkup::GetSelectedElement。 选区渲染服务上下文的同名函数的封装。
  • CMarkup::GetElementTop。获取元素客户端,如果获取失败,返回Root。
  • CMarkup::EnsureTopElems。 cache顶级元素(html、head、title、frameset、body、text slaves,etc)。
  • CMarkup::MetaPersistEnabled。 遍历head的子元素,找到所有meta元素,转为CMetaElement之后调用其IsPersistMeta方法,确定针对特定的元素id,这个meta是否是一个persist meta(这是指一个可以启用或者禁用某一组控件,或者对象的标签,参见参考资料meta)。
  • CMarkup::LocateHeadMeta。 在head里面查找特定的meta。对比head的所有子meta元素,找到一个和给定的meta相同的meta。
  • CMarkup::LocateOrCreateHeadMeta。 找到一个指定meta,如果没有,创建这个meta。
  • CMarkup::FirstElement。 获得源中树的第一个位置上第一个分支的第一个元素。
  • CMarkup::GetContentTreeExtent。 获得树的区间。找到markup中第一个非root元素,到分支上最后一个元素的区间。
  • CMarkup::GetProgSinkC、CMarkup::GetProgSink。 返回_pProgSink。
  • CMarkup::IsEditable。 存在master element时返回master element的可编辑状态,否则返回文档是否处于设计模式。
  • CMarkup::InvokeEx。 InvokeEx的封装。
  • CMarkup::GetDispID。 GetDispatchID的封装。
  • CMarkup::GetNextDispID。 GetNextDispID的封装。
  • CMarkup::GetMemberName。 获取dispatch对应的成员名。
  • CMarkup::GetNameSpaceParent。 对CDoc的GetNameSpaceParent的封装。
  • CMarkup::get_designMode。 获取设计模式。
  • CMarkup::put_designMode。 设置设计模式。
  • CMarkup::open。 open的实现。
  • CMarkup::write。 write的实现。
  • CMarkup::writeln。 writeln的实现。
  • CMarkup::close。 close的实现。
  • CMarkup::clear。 clear的实现。 在ie下这是一个无操作的函数,在脚本中,调用document.open();document.close()即会清空页面内容。
  • CMarkup::get_bgColor/CMarkup::put_bgColor。 bgColor的实现。
  • CMarkup::get_fgColor/CMarkup::put_fgColor。 fgColor的实现。
  • CMarkup::get_linkColor/CMarkup::put_linkColor、alinkColor、vlinkColor。 对应属性的实现。参考linkColor/ alinkColor/ vlinkColor
  • CMarkup::get_parentWindow。 获取父窗口, parentWindow的实现。
  • CMarkup::get_activeElement。 活动元素,activeElement的实现。
  • CMarkup::get_URL/CMarkup::put_URL。 URL属性操作,URL的实现。
  • CMarkup::get_location。 location属性的实现。
  • CMarkup::get_lastModified。 lastModified属性的实现。
  • CMarkup::get_referrer。 referrer属性的实现。
  • CMarkup::get_domain/CMarkup::put_domain。 domain属性的实现。
  • CMarkup::get_readyState。 readyState属性的实现。
  • CMarkup::get_Script、CMarkup::releaseCapture、CMarkup::get_styleSheets、CMarkup::get_selection。
  • CMarkup::get_cookie、CMarkup::put_cookie、 CMarkup::get_expando、 CMarkup::put_expando。
  • CMarkup::get_charset、CMarkup::put_charset、CMarkup::get_defaultCharset、CMarkup::put_defaultCharset
  • CMarkup::get_dir、CMarkup::put_dir、CMarkup::get_mimeType、CMarkup::get_fileSize、CMarkup::get_fileCreatedDate、CMarkup::get_fileModifiedDate、CMarkup::get_fileUpdatedDate、CMarkup::get_security、CMarkup::get_protocol、CMarkup::get_nameProp。这些都是对应属性的实现,上述所有属性实现部分应当都是document的内容,可以说应当放在CDoc中,多个版本的CMarkup中有这些函数,但是并没有实现他们的功能,这一点可以在调试的时候加以区分。
  • CMarkup::toString、CMarkup::attachEvent、CMarkup::detachEvent。 对父类(super)的同名函数的封装。
  • CMarkup::recalc。 调用pDoc的recalc,重新计算所有Markup。
  • CMarkup::createTextNode。 调用pDoc的createTextNode,创建文本节点。
  • CMarkup::get_uniqueID。 调用pDoc的get_uniqueID,获取唯一id。
  • CMarkup::createElement。 根据指定的元素名来创建一个元素。
  • CMarkup::createStyleSheet。 根据指定的参数创建一个LINK或者STYLE。之后修正引用,排除那些不会有style的元素,例如title、meta等。最后,将创建的LINK或者STYLE元素绑入HEADER中。
  • CMarkup::elementFromPoint。 获取指定点上的元素。
  • CMarkup::execCommand、CMarkup::execCommandShowHelp、CMarkup::queryCommandSupported、CMarkup::queryCommandEnabled、CMarkup::queryCommandState、CMarkup::queryCommandIndeterm、CMarkup::queryCommandText、CMarkup::queryCommandValue。 暂未实现。
  • CMarkup::createDocumentFragment。 为pDoc创建一个Markup,并设置为当前Markup的子Markup。
  • CMarkup::get_parentDocument、CMarkup::get_enableDownload、CMarkup::put_enableDownload、CMarkup::get_baseUrl、CMarkup::put_baseUrl。 对应属性的get、put函数。
  • CMarkup::get_childNodes。 获取Root节点的子节点。
  • CMarkup::get_inheritStyleSheets、CMarkup::put_inheritStyleSheets。 inheritStyleSheets的get和put函数。
  • CMarkup::getElementsByName。 getElementsByName的实现,调用GetDispByNameOrID返回一个IHTMLElementCollection**。
  • CMarkup::getElementsByTagName。 getElementsByTagName的实现,根据指定的TagName返回一个IHTMLElementCollection**。
  • CMarkup::getElementById。 getElementById的实现。根据指定的id返回一个IHTMLElement**。
  • CMarkup::zoom、CMarkup::get_zoomNumerator、CMarkup::get_zoomDenominator。 缩放相关属性及操作的实现。
  • CMarkup::GetLookasidePtr。 从Doc中获取Lookaside指针。
  • CMarkup::SetLookasidePtr。 向Doc中设置Lookaside指针。
  • CMarkup::DelLookasidePtr。 删除一个Doc中的Lookaside指针。
  • CMarkup::ReplacePtr。替换两个CMarkup指针,并且给其中一个增加一个计数,被替换的那个减少一个引用计数。
  • CMarkup::SetPtr。 设置一个CMarkup为另一个CMarkup,并给被设置的CMarkup增加一个引用计数。
  • CMarkup::StealPtrSet。 调用SetPtr,同时给另一个CMarkuo减少一个引用计数。
  • CMarkup::StealPtrReplace。 调用ReplacePtr,被赋值的那个多释放一次。
  • CMarkup::ClearPtr。 置指针为空,同时减少其原指针引用对象的一次引用计数。
  • CMarkup::ReleasePtr。 释放一次引用。
    上面用到了Document Fragment类,这是一个小类,在这里也一并介绍了。
  • CDocFrag::PrivateAddRef。 对Markup调用SubAddRef。
  • CDocFrag::PrivateRelease。 对Markup调用SubRelease。
  • CDocFrag::PrivateQueryInterface。 QueryInterface的实现。
  • CDocFrag::get_document。 对Markup QueryInterface,获取IID_IHTMLDocument2对应的ppIHtmlDoc。
  • CDocFrag::GetNameSpaceParent。 对Markup QueryInterface,获取其IDispatchEx。
    只有这几个函数而已。
  • CMarkup::RegisterForDirtyRange、CMarkup::UnRegisterForDirtyRange、CMarkup::GetAndClearDirtyRange、CMarkup::OnDirtyRangeChange、CMarkup::EnsureDirtyRangeContext、。 对已经标记为dirty的range的处理服务,当一个Range内的内容发生变化时,该Range即标记为Dirty。
  • CMarkup::EnsureTopElemCache。 有顶级元素cache时,返回它,否则新建一个CMarkupTopElemCache并设置为顶级元素cache。
  • CMarkup::EnsureTextFragContext。 同上,CMarkupTextFragContext的处理函数。
  • CMarkupTextFragContext::~CMarkupTextFragContext。 CMarkupTextFragContext的析构函数,释放申请的内存资源。
  • CMarkupTextFragContext::AddTextFrag。 申请内存,并填入一个TextFrag。
  • CMarkupTextFragContext::RemoveTextFrag。 移除TextFrag,并释放所占用的空间。
  • CMarkupTextFragContext::FindTextFragAtCp。 根据一个Cp来查找对应的TextFrag。
  • CMarkup::GetTextFragCount。 获得TextFrag的大小。
  • CMarkup::GetTextFrag。 获得TextFrag。申请内存,放置TextFrag并把它最终放入树的对应位置上。
  • CMarkup::RemoveTextFrag。 删除TextFrag。
  • CMarkup::InsertTextFrag。 插入一个TextFrag。
  • CMarkup::FindTextFragFromMarkupPointer。 从一个MarkupPointer中找到一个TextFrag。
  • CMarkup::EnsureStyleSheets。 确保样式表collection的存在,如果不存在就创建一个并关联上。
  • CMarkup::ApplyStyleSheets。 应用样式表,Apply的封装。
  • CMarkup::OnCssChange。 OnCssChange的响应函数。
  • CMarkup::EnsureFormats。 确保树中所有的元素的格式都是正确的,分别调用各个元素的EnsureFormats。

3.4 小结
CMarkup是Internet Explorer中关于document的实现,我们可以看到CMarkup实现了document的几乎所有的属性、事件响应、样式表的操作,在下一章中,我们将介绍最后一个大类CDoc的具体实现细节。在下一章结束后,我们将同时给出一个Internet Explorer 11中的实例分析。

参考
Splay Tree 伸展树,http://zh.wikipedia.org/wiki/%E4%BC%B8%E5%B1%95%E6%A0%91
chRef char reference的缩写
Cch count of char的缩写
split Split(x,S):以x为界,将伸展树S分离为两棵伸展树S1和S2,其中S1中所有元素都小于x,S2中的所有元素都大于x。首先执行Find(x,S),将元素x调整为伸展树的根节点,则x的左子树就是S1,而右子树为S2。

标签:none

添加新评论

captcha
请输入验证码