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() 的合并人灼。
同樣的 previousSibling 、firstChild 顾翼、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)。