Markup 和 CMarkup 對(duì)象

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

Markup

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

Tags vs Elements

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

舉例來說缸逃,有如下頁面

<P>First<P>Second

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

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

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

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

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

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

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

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

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

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

合法的和不合法的頁面

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

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

IMarkupServices

MarkupContainer

Container 顧名思義即頁面Element 的容器,也是Markup 操作的容器掷空,MarkupContainer 用于把創(chuàng)建的Element 對(duì)象和頁面中的 text 內(nèi)容聯(lián)系起來肋殴。在頁面解析完成之后,系統(tǒng)會(huì)默認(rèn)創(chuàng)建一個(gè)主 Container坦弟,其后每一次頁面內(nèi)容的操作都需要指定一個(gè) Container护锤,具體的流操作均在這個(gè) Container 上進(jìn)行。
舉例來說酿傍,下面的代碼想要向一個(gè)頁面中插入一個(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ì)頁面的插入操作首先需要通過頁面對(duì)象獲取對(duì)應(yīng)的 Container 接口烙懦,接著使用 markup pointer 遍歷到 Container 中的指定位置,這樣才能執(zhí)行操作赤炒。

MarkupPointer

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

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

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

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

放置Markup Pointers

markup pointer 被創(chuàng)建后處于 unpositioned 狀態(tài)炉旷,表示它還沒有被放置到頁面中的任何位置签孔。微軟提供了三個(gè)函數(shù)用來為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 的開始還是結(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 來檢查周圍的頁面內(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)過去孕索。
  • 第二個(gè)參數(shù)為返回值,返回pointer周圍的內(nèi)容類型躏碳。
Value Are Example
CONTEXT_TYPE_None pointer左邊或者右邊沒有東西 [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ù)用來限定讀取的text范圍丹诀,同時(shí)也用來返回獲取的text 的大小
  • 第五個(gè)參數(shù)返回pointer 左邊或者右邊的 text

下面以具體的頁面舉例說明

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

對(duì)于頁面上的五個(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í)的位置。
舉例來說

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

當(dāng)?shù)谝粋€(gè)頁面被修改為第二個(gè)頁面之后橄妆,雖然頁面的內(nèi)容發(fā)生了改變衙伶,但是pointer 的相對(duì)位置仍然保持不變祈坠。
但如果頁面的修改發(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í)著其左偏或右偏。仍以上述頁面為例

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

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

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

默認(rèn)情況下 pointer 的gravity 值是 left。用戶可以通過 windows 提供的函數(shù)來查看或者修改 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 屬性隘击,那么頁面操作之后就會(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)的操作也是通過 markup 來完成的咏窿,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í)就加入屬性素征。
用戶也可以通過從一個(gè)已有 element 克隆集嵌,來得到新的 element

插入新 Element

新 element 成功創(chuàng)建之后,如果想加入document 中御毅,還需要通過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 中凤粗。
舉例來說,調(diào)用函數(shù)將 <B> 標(biāo)簽插入下面的頁面中

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 來指定位置

移除內(nèi)容

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

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

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

     <--------- 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é)果頁面被修改為下面的情況

 <------- 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)一段頁面內(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è)拷貝凳鬓。
舉例來說


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 ------->      

操作之后頁面變成

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 引擎在操作頁面時(shí)調(diào)用仅孩。簡單來說 CMarkup 可以看作是 Markup Service 中的 MarkupContainer托猩。以下是 IE8 中 CMarkup 的部分結(jié)構(gòu),可以看出其關(guān)聯(lián)了與頁面相關(guān)的許多重要的元素辽慕。不僅如此所有的頁面元素都保存一個(gè)指向 CMarkup 的指針京腥,在對(duì)頁面元素進(jìn)行訪問時(shí),均需要通過 CMarkup 來進(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)。通過 js 訪問節(jié)點(diǎn)的該屬性温艇,IE 8 內(nèi)部使用 CElement::get_nextSibling 函數(shù)來實(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);       // 通過 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 位置,而后通過該 MarkupPointer 來檢查周圍的內(nèi)容,這里使用的函數(shù)其實(shí)是 CMarkupPointer::There 卫旱,其函數(shù)為 Left() 和 Right() 的合并人灼。

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

childNodes 節(jié)點(diǎn)屬性則是通過 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) )
    ......
}

通過兩個(gè) CMarkupPointer 指針分別指向 Element 的開始和結(jié)尾拜姿,從 Element 的開始位置依次遍歷 烙样,其間所有的節(jié)點(diǎn)均為 Element 的子節(jié)點(diǎn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蕊肥,一起剝皮案震驚了整個(gè)濱河市谒获,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌壁却,老刑警劉巖批狱,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異展东,居然都是意外死亡赔硫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門琅锻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卦停,“玉大人向胡,你說我怎么就攤上這事恼蓬。” “怎么了僵芹?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵处硬,是天一觀的道長。 經(jīng)常有香客問我拇派,道長荷辕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任件豌,我火速辦了婚禮疮方,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茧彤。我一直安慰自己骡显,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惫谤,像睡著了一般壁顶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上溜歪,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天若专,我揣著相機(jī)與錄音,去河邊找鬼蝴猪。 笑死调衰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拯腮。 我是一名探鬼主播窖式,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼动壤!你這毒婦竟也來了萝喘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤琼懊,失蹤者是張志新(化名)和其女友劉穎阁簸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哼丈,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡启妹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了醉旦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饶米。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖车胡,靈堂內(nèi)的尸體忽然破棺而出檬输,到底是詐尸還是另有隱情,我是刑警寧澤匈棘,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布丧慈,位于F島的核電站,受9級(jí)特大地震影響主卫,放射性物質(zhì)發(fā)生泄漏逃默。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一簇搅、第九天 我趴在偏房一處隱蔽的房頂上張望完域。 院中可真熱鬧,春花似錦瘩将、人聲如沸吟税。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乌妙。三九已至使兔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間藤韵,已是汗流浹背虐沥。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泽艘,地道東北人欲险。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像匹涮,于是被迫代替她去往敵國和親天试。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

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