Markup 和 CMarkup 對(duì)象

Windows 為了實(shí)現(xiàn)瀏覽器功能代碼的復(fù)用,將瀏覽器內(nèi)部 DOM 接口\DHTML接口使用 COM 方式實(shí)現(xiàn),這樣HTML頁(yè)面的內(nèi)容就可以方便的被其他各個(gè)模塊所調(diào)用,如 瀏覽器的javascript、操作瀏覽器組件的C++等。其主要的實(shí)現(xiàn)均存在于 mshtml.dll 中。
其中 Markup 是一系列接口和對(duì)象的集合,主要為用戶提供訪問(wèn)和修改HTML頁(yè)面內(nèi)容的功能。Markup 在IE 瀏覽器中被封裝成一個(gè)類,叫做 CMarkup。

Markup

理解 Markup 首先需要理清如下的幾個(gè)概念

Tags vs Elements

一個(gè)html 標(biāo)簽在瀏覽器內(nèi)部的表示形式被稱為 Element,理解 tag 和 element 的概念尤其重要。HTML 頁(yè)面內(nèi)容包含有 tag,如<B></B>、<A></A>等,在使用瀏覽器訪問(wèn)HTML 頁(yè)面時(shí),瀏覽器的 parser 讀到 <B></B>等標(biāo)簽,并且根據(jù) tag 的不同創(chuàng)建不同的對(duì)象。這些可以操作的對(duì)象稱為 Element。Markup 所能夠操作的也正是這些對(duì)象

舉例來(lái)說(shuō),有如下頁(yè)面

<P>First<P>Second

瀏覽器頁(yè)面 parser 解析這些語(yǔ)句時(shí)則會(huì)變成下面的樣子

<HTML><HEAD><TITLE></TITLE></HEAD><BODY>
<P>First</P><P>Second</P></BODY></HTML>

換而言之,paser 將 HTML 頁(yè)面中的內(nèi)容轉(zhuǎn)變成了 element,并且添加了一些元素以保證頁(yè)面結(jié)構(gòu)的完整性。

另一個(gè)需要理解的概念是 stream 和 tree。
以如下頁(yè)面舉例

My <B>dog</B> has fleas.

上述頁(yè)面會(huì)被解析成為如下的樹(shù)結(jié)構(gòu)

              ROOT
                |
          +-----+------+
          |     |      |
         "My"   B  "has fleas."
                |
              "dog"

而對(duì)于上述文檔的操作看起來(lái)就像是在對(duì)樹(shù)進(jìn)行操作,比如添加或者刪除葉節(jié)點(diǎn)。
然而隨著功能的加強(qiáng),頁(yè)面的內(nèi)容從 ie4.0 開(kāi)始變得不再是上圖中那樣簡(jiǎn)單的樹(shù)結(jié)構(gòu)了。
如下一個(gè)例子

Where do <B>you <I>want to</B> go</I> today?

在這個(gè)頁(yè)面中 <B> 標(biāo)簽和 <I> 標(biāo)簽相互嵌套,這樣一來(lái)頁(yè)面便無(wú)法被簡(jiǎn)單的表示成為樹(shù)結(jié)構(gòu),此時(shí) markup 便應(yīng)運(yùn)而生了。
Markup 將頁(yè)面看作是一個(gè) stream。頁(yè)面中的內(nèi)容均由markup pointer 進(jìn)行索引,對(duì)于頁(yè)面內(nèi)容的操作也是按照markup pointer 指定的范圍進(jìn)行。以上面的頁(yè)面為例,操作重疊的tag 時(shí)使用兩個(gè)markup pointer ,一個(gè)指向tag 的開(kāi)頭另一個(gè)指向tag 的結(jié)尾,這種方式當(dāng)然也可以表示之前的樹(shù)結(jié)構(gòu),換句話說(shuō) stream 是 tree 的超集

合法的和不合法的頁(yè)面

一般的瀏覽器都具有容錯(cuò)性,就像上面舉過(guò)的例子一樣,瀏覽器的 parser 會(huì)在解析過(guò)程中為頁(yè)面添加必要的結(jié)構(gòu)以努力構(gòu)成一個(gè)合法頁(yè)面。一個(gè)合法頁(yè)面至少要包含一個(gè) html、一個(gè)head、一個(gè) TITLE 和一個(gè) body。

markup 為用戶提供接口,使用戶可以在頁(yè)面解析完成、或者尚未完成時(shí)修改頁(yè)面內(nèi)容。

IMarkupServices

MarkupContainer

Container 顧名思義即頁(yè)面Element 的容器,也是Markup 操作的容器,MarkupContainer 用于把創(chuàng)建的Element 對(duì)象和頁(yè)面中的 text 內(nèi)容聯(lián)系起來(lái)。在頁(yè)面解析完成之后,系統(tǒng)會(huì)默認(rèn)創(chuàng)建一個(gè)主 Container,其后每一次頁(yè)面內(nèi)容的操作都需要指定一個(gè) Container,具體的流操作均在這個(gè) Container 上進(jìn)行。
舉例來(lái)說(shuō),下面的代碼想要向一個(gè)頁(yè)面中插入一個(gè)元素

int Insert(
    MSHTML::IHTMLDocument2Ptr pDoc2,
    ....)
{
    HRESULT              hr = S_OK;
    //IHTMLDocument2 *   pDoc2;
    IMarkupServices  *   pMS;
    IMarkupContainer *   pMpContainer;
    IMarkupPointer   *   pPtr1, * pPtr2;
    
    pDoc2->QueryInterface( IID_IMarkupContainer, (void **) & pMpContainer);
    pDoc2->QueryInterface( IID_IMarkupServices, (void **) & pMS );

    // need two pointers for marking
    pMS->CreateMarkupPointer( & pPtr1 );
    // beginning and ending position.
    pMS->CreateMarkupPointer( & pPtr2 ); 

    //
    // Set gravity of this pointer so that when the replacement text
    // is inserted it will float to be after it.
    //
    pPtr1->SetGravity( POINTER_GRAVITY_Right ); // Right gravity set
    pPtr2->SetGravity( POINTER_GRAVITY_Left );


    pPtr1->MoveToContainer( pMpContainer, TRUE );
    pPtr2->MoveToContainer( pMpContainer, TRUE );
    
    ......
    Insert()
}

對(duì)頁(yè)面的插入操作首先需要通過(guò)頁(yè)面對(duì)象獲取對(duì)應(yīng)的 Container 接口,接著使用 markup pointer 遍歷到 Container 中的指定位置,這樣才能執(zhí)行操作。

MarkupPointer

MarkupPointer 并不是 MarkupContainer 的一部分。MarkupPointer 的主要功能是用來(lái)指示tag節(jié)點(diǎn)在文檔中的位置。因此 pointer 可以看作是用于在 Container 中進(jìn)行索引的迭代器。
舉例來(lái)說(shuō)

My <B>d[p1]og</B> has fleas.

在這個(gè)頁(yè)面中,MarkupPointer 出現(xiàn)在[p1]所示的位置上,但它并不會(huì)在頁(yè)面內(nèi)容中添加任何東西,或者對(duì)頁(yè)面內(nèi)容進(jìn)行任何修改。
MarkupPointer 可以被至于頁(yè)面的這些位置:element的開(kāi)始、element的結(jié)束、或者text之中。由于MarkupPointer本身不包含內(nèi)容,因此如果兩個(gè) MarkupPointer 指向了同一個(gè)位置便會(huì)難以區(qū)分。

通過(guò) Markup ,用戶便可以操作頁(yè)面中的內(nèi)容,其主要提供了以下一些功能

放置Markup Pointers

markup pointer 被創(chuàng)建后處于 unpositioned 狀態(tài),表示它還沒(méi)有被放置到頁(yè)面中的任何位置。微軟提供了三個(gè)函數(shù)用來(lái)為markup pointer 指定位置

  • MoveAdjacentToElement
  • MoveToContainer
  • MoveToPointer

MoveAdjacentToElement函數(shù)有兩個(gè)參數(shù),Element和一個(gè)枚舉類型常量,他們協(xié)同指定markup pointer的位置。函數(shù)原型如下

HRESULT MoveAdjacentToElement(
    IHTMLElement *elementTarget,
    ELEMENT_ADJACENCY
);

    enum ELEMENT_ADJACENCY {
         ELEMENT_ADJ_BeforeBegin
         ELEMENT_ADJ_AfterBegin
         ELEMENT_ADJ_BeforeEnd
         ELEMENT_ADJ_AfterEnd
    };

MoveToContainer函數(shù)也有兩個(gè)參數(shù),MarkupContainer 和一個(gè)Bool 類型用以指定 markup pointer 應(yīng)該放在 container 的開(kāi)始還是結(jié)尾。函數(shù)原型如下

HRESULT MoveToContainer(
    IMarkupContainer *containerTarget,
    BOOL fAtStart
);

MoveToPointer函數(shù)只有一個(gè)參數(shù),另一個(gè)markup pointer。函數(shù)功能即把當(dāng)前 pointer 指定到參數(shù) pointer 的位置。函數(shù)原型如下

HRESULT MoveToPointer(
    IMarkupPointer *pointerTarget
);

這個(gè)函數(shù)一般用于在markup pointer執(zhí)行功能的時(shí)候,保存當(dāng)前的位置

比較pointer 的位置

兩個(gè) markup pointer 的位置關(guān)系可以使用下面的函數(shù)進(jìn)行比較


HRESULT IsEqualTo(
    IMarkupPointer *compareTo,
    BOOL *fResult
);

HRESULT IsLeftOf(
    IMarkupPointer *compareTo,
    BOOL *fResult
);

HRESULT IsLeftOfOrEqualTo(
    IMarkupPointer *compareTo,
    BOOL *fResult
);

HRESULT IsRightOf(
    IMarkupPointer *compareTo,
    BOOL *fResult
);

HRESULT IsRightOfOrEqualTo(
    IMarkupPointer *compareTo,
    BOOL *fResult
);
Navigating the Pointer

一旦一個(gè) markup pointer 被放置在一個(gè) markup containter 中。用戶便可以使用這個(gè) pointer 來(lái)檢查周圍的頁(yè)面內(nèi)容,或者遍歷這塊內(nèi)容。用戶只能使用windows 提供的兩個(gè)函數(shù)完成這些功能,Left檢查pointer 的左邊是什么,Right 檢查pointer 的右邊是什么

HRESULT Left(
    BOOL fMove,
    MARKUP_CONTEXT_TYPE pContextType,
    IHTMLElement **ppElement,
    long *plCch,
    OLE_CHAR *pch
);

HRESULT Right(
    BOOL fMove,
    MARKUP_CONTEXT_TYPE pContextType,
    IHTMLElement **ppElement,
    long *plCch,
    OLE_CHAR *pch
);
  • 第一個(gè)參數(shù)指定指針是否可移動(dòng),若不可移動(dòng),則函數(shù)僅僅會(huì)返回指針周圍內(nèi)容的描述;否則,函數(shù)在返回周圍內(nèi)容描述的同時(shí)還會(huì)移動(dòng)過(guò)去。
  • 第二個(gè)參數(shù)為返回值,返回pointer周圍的內(nèi)容類型。
Value Are Example
CONTEXT_TYPE_None pointer左邊或者右邊沒(méi)有東西 [p1]<HTML></HTML>[p2]
CONTEXT_TYPE_Text pointer左邊或者右邊是一個(gè)text tex[p]t
CONTEXT_TYPE_EnterScope 如果是Left,則point左邊是一個(gè)End tag;如果是Right,pointer的右邊是一個(gè)Begin tag 。 </B>[p]<B>
CONTEXT_TYPE_ExitScope 如果是Left,則point左邊是一個(gè)Begin tag;如果是Right,pointer的右邊是一個(gè)End tag 。 <B>[p]</B>
CONTEXT_TYPE_NoScope pointer的左邊或者右邊不是一個(gè)可以成對(duì)的標(biāo)簽 <BR>[p]<BR>
  • 第三個(gè)參數(shù)返回 pointer 左邊或者右邊的element
  • 第四個(gè)參數(shù)用來(lái)限定讀取的text范圍,同時(shí)也用來(lái)返回獲取的text 的大小
  • 第五個(gè)參數(shù)返回pointer 左邊或者右邊的 text

下面以具體的頁(yè)面舉例說(shuō)明

[p1]Where [p2]<I>do </I>[p3]<B>you <BR>[p4]want</B> to go today[p5]?

對(duì)于頁(yè)面上的五個(gè)pointer 分別調(diào)用left,right結(jié)果如下表

Ptr Derection Type Element cch in cch out Text
p1 left None - - - -
p1 right Text - 2 2 Wh
p1 right Text - -1 6 -
p1 right Text - 345 6 Where
p2 left Text - NULL - -
p2 right EnterScope I - - -
p3 left ExitScope I - - -
p4 left NoScope BR - - -
p5 left Text I 100 12 NULL

CurrentScope函數(shù)可以得到Pointer 當(dāng)前指向的Element。函數(shù)原型如下

HRESULT CurrentScope(
    IHTMLElement **ppElementCurrent
);

上述例子中,p1返回值是 NULL;p4返回值是B,因?yàn)锽R不是一個(gè)可以成對(duì)的標(biāo)簽

Pointer Gravity

通常情況下,一個(gè) document 被修改之后,document 中的markup Pointer還會(huì)保留在之前未修改時(shí)的位置。
舉例來(lái)說(shuō)

abc[p1]defg[p2]hij
abc[p1]deXYZfg[p2]hij

當(dāng)?shù)谝粋€(gè)頁(yè)面被修改為第二個(gè)頁(yè)面之后,雖然頁(yè)面的內(nèi)容發(fā)生了改變,但是pointer 的相對(duì)位置仍然保持不變。
但如果頁(yè)面的修改發(fā)生在 point 指向的位置,如上例中,向c、d之間插入一個(gè)Z,p 的位置就會(huì)出現(xiàn)二義性。

abcZ[p1]de  or  abc[p1]Zde

這時(shí)就需要引用另一個(gè)重要的概念gravity,每一個(gè)pointer都有一個(gè) gravity 值標(biāo)識(shí)著其左偏或右偏。仍以上述頁(yè)面為例

abc[p1,right]defg[p2,left]hij 

分別在p1,p2的位置插入一對(duì)<B>標(biāo)簽。這時(shí)由于gravity的存在,頁(yè)面會(huì)變成如下

abc<B>[p1,right]defg[p2,left]</B>hij 

默認(rèn)情況下 pointer 的gravity 值是 left。用戶可以通過(guò) windows 提供的函數(shù)來(lái)查看或者修改 pointer 的 gravity 值

enum POINTER_GRAVITY {
    POINTER_GRAVITY_Left,
    POINTER_GRAVITY_Right
};

HRESULT Gravity(
    POINTER_GRAVITY *pGravityOut
);

HRESULT SetGravity(
    POINTER_GRAVITY newGravity
);
Pointer Cling

有如下例子

[p2]ab[p1]cdxy

當(dāng)bc 段被移動(dòng)到 xy之間時(shí)p1的位置也出現(xiàn)了二義性,是應(yīng)該隨著bc移動(dòng),還是應(yīng)該繼續(xù)保持在原位呢

[p2]a[p1]dxbcy or [p2]adxb[p1]cy

這就需要 cling 的存在,如果p1指定了cling 屬性,那么頁(yè)面操作之后就會(huì)成為右邊所示的情況,否則就會(huì)出現(xiàn)左邊所示的情況

cling 和 gravity 可以協(xié)同作用,比如下面的例子

a[p1]bcxy

b移動(dòng)到x、y之間,如果p1指定了 cling屬性,并且gravity 值為 right,那么p1便會(huì)跟隨b一起到xy之間。這種情況下如果b被刪除,那么p1也會(huì)跟著從content 中移除,但并不會(huì)銷毀,因?yàn)閜1還有可能重新被使用
cling相關(guān)的函數(shù),函數(shù)原型如下

HRESULT Cling(
    BOOL *pClingOut
);

HRESULT SetCling(
    BOOL NewCling
);
創(chuàng)建新Element

動(dòng)態(tài)創(chuàng)建新節(jié)點(diǎn)的操作也是通過(guò) markup 來(lái)完成的,CreateElement 函數(shù)原型如下

enum ELEMENT_TAG_ID {
    TAGTADID_A,
    TAGTADID_ACRONYM,
        ..
    TAGTADID_WBR,
    TAGTADID_XMP
};

HRESULT CreateElement(
    TAG_ID tagID,
    OLECHAR *pchAttrs,
    IHTMLElement **ppNewElement
);

第二個(gè)參數(shù)是屬性串,可以在 Element創(chuàng)建時(shí)就加入屬性。
用戶也可以通過(guò)從一個(gè)已有 element 克隆,來(lái)得到新的 element

插入新 Element

新 element 成功創(chuàng)建之后,如果想加入document 中,還需要通過(guò)markup 將element插入。 函數(shù)原型如下

HRESULT InsertElement(
    IHTMLElement *pElementInsertThis,
    IMarkupPointer *pPointerStart,
    IMarkupPointer *pPointerFinish
);

第二參數(shù)指示這個(gè)element 的begin tag 插入到哪里;第三個(gè)參數(shù)指示這個(gè) element 的end tag應(yīng)該插入到哪里;這兩個(gè)位置必須在同一個(gè) markup Container 中。
舉例來(lái)說(shuō),調(diào)用函數(shù)將 <B> 標(biāo)簽插入下面的頁(yè)面中

My [pstart]dog[pend] has fleas.

默認(rèn)情況下結(jié)果將如下面所示,如果 pointer 的 gravity 改變,情況也會(huì)改變

My [pstart]<B>dog[pend]</B> has fleas.
移除Element

移除 element 并不需要markup pointer ,只需要傳遞給函數(shù)要?jiǎng)h除的 element 就可以。函數(shù)原型如下

HRESULT RemoveElement(
    IHTMLElement *pElementRemoveThis
);

element 被從 document 中移除之后并不會(huì)被刪除,他隨時(shí)可以被重新插入

插入 Text

在 document 中插入 text ,函數(shù)原型如下

HRESULT InsertText(
    OLECHAR *pch,
    long cch,
    IMarkupPointer *pPointerTarget
);

注意到,插入text 只需要一個(gè) markup pointer 來(lái)指定位置

移除內(nèi)容

用戶可以移除在同一個(gè)container 中一段連續(xù)的內(nèi)容,函數(shù)原型如下

HRESULT Remove(
    IMarkupPointer *pPointerSourceStart,
    IMarkupPointer *pPointerSourceFinish
);

兩個(gè)參數(shù)用來(lái)指定remove操作的范圍,所有在這兩個(gè)點(diǎn)之間的內(nèi)容都會(huì)被移除。但是有一點(diǎn)例外,即兩個(gè) pointer 沒(méi)有完全包含的 element 不會(huì)被移除。舉例來(lái)說(shuō)

     <--------- i -----------> <---------- u ----------->
    a<I>b<B>c[pstart]d<S>e</I>f<U>g</S>h[pend]hi</B>j</U>kl
                      <----- s ------->         

remove 操作傳入 pstart、pend 兩個(gè)參數(shù),結(jié)果頁(yè)面被修改為下面的情況

 <------- i --------><------- u -------->
a<I>b<B>c[pstart]</I><U>[pend]hi</B>j</U>kl

<U> 和 <I> 并未被移除。

替換內(nèi)容

插入和移除操作何以合成 Replace 操作

int MarkupSvc::RemoveNReplace(
    MSHTML::IHTMLDocument2Ptr pDoc2,
    _bstr_t bstrinputfrom, _bstr_t bstrinputto)
{
    HRESULT              hr = S_OK;
    //IHTMLDocument2 *   pDoc2;
    IMarkupServices  *   pMS;
    IMarkupContainer *   pMarkup;
    IMarkupPointer   *   pPtr1, * pPtr2;
    TCHAR            *   pstrFrom = _T( bstrinputfrom );
    TCHAR            *   pstrTo = _T( bstrinputto );
    
    pDoc2->QueryInterface( IID_IMarkupContainer, (void **) & pMarkup );
    pDoc2->QueryInterface( IID_IMarkupServices, (void **) & pMS );

    // need two pointers for marking
    pMS->CreateMarkupPointer( & pPtr1 );
    // beginning and ending position of text.
    pMS->CreateMarkupPointer( & pPtr2 ); 

    //
    // Set gravity of this pointer so that when the replacement text
    // is inserted it will float to be after it.
    //
    pPtr1->SetGravity( POINTER_GRAVITY_Right ); // Right gravity set

    //
    // Start the search at the beginning of the primary container
    //

    pPtr1->MoveToContainer( pMarkup, TRUE );

    for ( ; ; )
    {
        hr = pPtr1->FindText( (unsigned short *) pstrFrom, 0, pPtr2, NULL );

        if (hr == S_FALSE) // did not find the text
            break;

        // found it, removing..
        pMS->Remove( pPtr1, pPtr2 );
        
        //inserting new text
        pMS->InsertText( (unsigned short *) pstrTo, -1, pPtr1 );
    }
    if (hr == S_FALSE) return FALSE;
    else return(TRUE);
}
移動(dòng)內(nèi)容

用戶可以使用 Move 移動(dòng)一段頁(yè)面內(nèi)容,函數(shù)原型如下

HRESULT Move(
    IMarkupPointer *pPointerSourceStart,
    IMarkupPointer *pPointerSourceFinish,
    IMarkupPointer *pPointerTarget
);

函數(shù)前兩個(gè)參數(shù)和 remove 類似,函數(shù)會(huì)將這一整段內(nèi)容移動(dòng)到目的 pointer 中。那些與pointer 范圍有重疊的 element,即并不完全包含在 pointers 之間的 element 會(huì)在目的處創(chuàng)建一個(gè)拷貝。
舉例來(lái)說(shuō)


X[pdest]Y

 <--------- i -----------> <---------- u ----------->
a<I>b<B>c[pstart]d<S>e</I>f<U>g</S>h[pend]hi</B>j</U>kl
                  <----- s ------->      

操作之后頁(yè)面變成

X[pdest]<I'>d<S>e</I'>f<U'>g</S>h</U'>Y

 <------- i --------><------- u -------->
a<I>b<B>c[pstart]</I><U>[pend]hi</B>j</U>kl

可以看到完全包含在pointers 中的<S>標(biāo)簽被移動(dòng)到dest 位置,而與 pointers 區(qū)域重疊的 <U>、<I>標(biāo)簽在目標(biāo)位置創(chuàng)建一個(gè)備份。

以上內(nèi)容翻譯自微軟提供的官方 Markup Serivce 文檔。

CMarkup

CMarkup 其本質(zhì)上是對(duì)Markup Service 的封裝,在 IE/EDGE 中方便 js 引擎在操作頁(yè)面時(shí)調(diào)用。簡(jiǎn)單來(lái)說(shuō) CMarkup 可以看作是 Markup Service 中的 MarkupContainer。以下是 IE8 中 CMarkup 的部分結(jié)構(gòu),可以看出其關(guān)聯(lián)了與頁(yè)面相關(guān)的許多重要的元素。不僅如此所有的頁(yè)面元素都保存一個(gè)指向 CMarkup 的指針,在對(duì)頁(yè)面元素進(jìn)行訪問(wèn)時(shí),均需要通過(guò) CMarkup 來(lái)進(jìn)行。

Class CMarkup{
   +0xA0    WindowedMarkupContext
   +0x40    CDocument
   +0x108  COmWindowProxy
   +0x50   CHtmlCtx
   +0x54   CProgSink
   +0x5C  CSecurityContext
   +0x8c   CAPStatr
   +0xc0   CSecurityContext
   +0xc8   CStyleSheetArray
   +0xcc   TagArray
   +0xd0   ComWindowProxy
   +0xdc  obj_name_space
   +0xf4   CHtmlElemeCtxStream
   +0x124  uri
   +0x158  CTimeManager
   +0x16c  CMSPerformanceData
   +0x140  CTreePos
}

以 DOM 節(jié)點(diǎn)固有屬性 nextSibling 舉例,該屬性用于返回其父節(jié)點(diǎn)的 childNodes 列表中緊跟在其后的節(jié)點(diǎn)。通過(guò) js 訪問(wèn)節(jié)點(diǎn)的該屬性,IE 8 內(nèi)部使用 CElement::get_nextSibling 函數(shù)來(lái)實(shí)現(xiàn),對(duì)該函數(shù)進(jìn)行逆向后部分代碼如下。

HRESULT CElement::GetNextSiblingHelper(CElement *this, CElement **nextSibling)
{
  CMarkupPointer * markupPointer;
  CDoc* cDoc;
  HRESULT result;

  cDoc = CElement::Doc(this);
  CMarkupPointer::CMarkupPointer(markupPointer, cDoc);   // 創(chuàng)建 MarkupPointer
  
  result = markupPointer->MoveAdjacentToElement( this, ELEMENT_ADJ_AfterEnd);    // 放置 MarkupPointer
    if ( result == S_OK )
    {
      cDoc = CElement::Doc(this);
      result = sub_74D4A0B3(cDoc , markupPointer, &nextSibling);       // 通過(guò) MarkupPoint 獲取 Element
    }

  result = CBase::SetErrorInfo(markupPointer, result);
  CMarkupPointer::~CMarkupPointer(markupPointer);
  return result;
}

函數(shù)的主要邏輯即,首先新建一個(gè) MarkupPointer 對(duì)象,接著將該 MarkupPointer 放置于目標(biāo)節(jié)點(diǎn)的 ELEMENT_ADJ_AfterEnd 位置,而后通過(guò)該 MarkupPointer 來(lái)檢查周圍的內(nèi)容,這里使用的函數(shù)其實(shí)是 CMarkupPointer::There ,其函數(shù)為 Left() 和 Right() 的合并。

同樣的 previousSibling 、firstChild 、lastChild 的內(nèi)部實(shí)現(xiàn)流程也類似,通過(guò) CMarkupPointer::MoveAdjacentToElement 將 CMarkupPointer 放置在節(jié)點(diǎn)對(duì)象的不同位置,再通過(guò) CMarkupPointer::There 取出對(duì)應(yīng)的節(jié)點(diǎn)信息即可。

childNodes 節(jié)點(diǎn)屬性則是通過(guò) CMarkupPointer 遍歷對(duì)應(yīng) Element 節(jié)點(diǎn)而實(shí)現(xiàn),在 IE 8 中其主要的功能函數(shù)為 CElement::DOMEnumerateChildren ,該函數(shù)逆向后主要功能代碼如下

CElement::DOMEnumerateChildren(CElement children[])
{
     cDoc = CElement::Doc(this);
     CMarkupPointer::CMarkupPointer(markupPtrBegin, cDoc);   
     CMarkupPointer::CMarkupPointer(markupPtrEnd, cDoc);    
     ......
     result = markupPtrBegin->MoveAdjacentToElement( this, ELEMENT_ADJ_AfterBegin);    // 放置 MarkupPointer
     result = markupPtrEnd->MoveAdjacentToElement( this, ELEMENT_ADJ_AfterEnd);    // 放置 MarkupPointer
     do{
        ......
        child = markupPointer->There()
        children[i++] = child;
        result = markupPtrBegin->MoveAdjacentToElement( child, ELEMENT_ADJ_AfterBegin);    // 放置 MarkupPointer
        ......
        }while( !markupPtrBegin->isLeftOf(markupPtrEnd) )
    ......
}

通過(guò)兩個(gè) CMarkupPointer 指針?lè)謩e指向 Element 的開(kāi)始和結(jié)尾,從 Element 的開(kāi)始位置依次遍歷 ,其間所有的節(jié)點(diǎn)均為 Element 的子節(jié)點(diǎn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • [TOC] 一、DOM 流簡(jiǎn)介 DOM (Document Object Model)作為現(xiàn)代瀏覽器的基礎(chǔ),其設(shè)計(jì)...
    o_0xF2B8F2B8閱讀 1,220評(píng)論 0 0
  • 課程介紹 先修課:概率統(tǒng)計(jì),程序設(shè)計(jì)實(shí)習(xí),集合論與圖論 后續(xù)課:算法分析與設(shè)計(jì),編譯原理,操作系統(tǒng),數(shù)據(jù)庫(kù)概論,人...
    ShellyWhen閱讀 2,462評(píng)論 0 3
  • 問(wèn)答題47 /72 常見(jiàn)瀏覽器兼容性問(wèn)題與解決方案? 參考答案 (1)瀏覽器兼容問(wèn)題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 14,108評(píng)論 1 92
  • 1.幾種基本數(shù)據(jù)類型?復(fù)雜數(shù)據(jù)類型?值類型和引用數(shù)據(jù)類型?堆棧數(shù)據(jù)結(jié)構(gòu)? 基本數(shù)據(jù)類型:Undefined、Nul...
    極樂(lè)君閱讀 5,875評(píng)論 0 106
  • 是什么時(shí)候發(fā)現(xiàn)原來(lái)自己是個(gè)沒(méi)有秘密的人? 小學(xué)的時(shí)候喜歡ck先生,好像很多人知道吧?有一次去檢查眼保...
    張笑憂閱讀 374評(píng)論 0 1

友情鏈接更多精彩內(nèi)容