分类 技术分享 下的文章

arm的flash还是那么个老大难的问题

乌云换来个cubieboard3,本想这个东西性能比树莓派高多了,终于有心情折腾折腾桌面了,这样早上吃饭的时候,就不需要用蠢萌的电视盒子来看东西了,开bilibili多好,于是用上iceweasel之后就开始寻思着下flash插件,可是下回来以后,iceweasel死活就是不认,何故?查查文件在不在(为了方便管理,我用户名都改成pi了):

root@BLASTN2:/usr/lib/iceweasel/plugins# ls -al /usr/lib/mozilla/plugins/
total 17396
drwxr-xr-x 2 root root     4096 Oct 22 22:03 .
drwxr-xr-x 4 root root     4096 Jul  2  2013 ..
-rw-r--r-- 1 root root    72248 May  3  2012 gecko-mediaplayer-dvx.so
-rw-r--r-- 1 root root    72248 May  3  2012 gecko-mediaplayer-qt.so
-rw-r--r-- 1 root root    72248 May  3  2012 gecko-mediaplayer-rm.so
-rw-r--r-- 1 root root    72248 May  3  2012 gecko-mediaplayer.so
-rw-r--r-- 1 root root    72248 May  3  2012 gecko-mediaplayer-wmp.so
-rw-r--r-- 1 root root 17426852 Oct 22 22:03 libflashplayer.so
-rw-r--r-- 1 root root     5492 Jul 28  2012 librhythmbox-itms-detection-plugin.so

查了下其他插件,确实也就在这一个地儿了

root@BLASTN2:/usr/lib/iceweasel/plugins# find / -name gecko-mediaplayer-*
/usr/lib/mozilla/plugins/gecko-mediaplayer-qt.so
/usr/lib/mozilla/plugins/gecko-mediaplayer-dvx.so
/usr/lib/mozilla/plugins/gecko-mediaplayer-rm.so
/usr/lib/mozilla/plugins/gecko-mediaplayer-wmp.so

还是不双击打开它,直接terminal里面运行算了

root@BLASTN2:/usr/lib/iceweasel/plugins# iceweasel
LoadPlugin: failed to initialize shared library /root/.mozilla/plugins/libflashplayer.so [/root/.mozilla/plugins/libflashplayer.so: cannot open shared object file: No such file or directory]
LoadPlugin: failed to initialize shared library /usr/lib/mozilla/plugins/libflashplayer.so [/usr/lib/mozilla/plugins/libflashplayer.so: cannot open shared object file: No such file or directory]

** (iceweasel:7420): WARNING **: The connection is closed

** (iceweasel:7420): WARNING **: The connection is closed

结果比较蛋疼,你tm在逗我?我文件好端端的放在你说的目录里面了,不过No such file or directory纯粹是在扯,真实原因是:

pi@BLASTN2:~$ file /usr/lib/iceweasel/iceweasel
/usr/lib/iceweasel/iceweasel: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x77328e6ff7d090e25316bf43572429498166a376, stripped
pi@BLASTN2:~$ file /usr/lib/mozilla/plugins/libflashplayer.so
/usr/lib/mozilla/plugins/libflashplayer.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped
pi@BLASTN2:~$ 

好吧,查来查去,只有德仪提供了arm 的flash,http://www.ti.com.cn/tool/cn/adobeflash-a8,看介绍说只支持部分型号的Ti,死心了。

Internet Explorer 完全解析[A1] : Markup Services的自我介绍

Markup服务是一组可以允许你操作HTML文档内容的对象和接口。 本文将介绍这些对象和接口。

标签(tags)、元素 (elements)
首先,在此引入一些概念帮助理解Markup服务。 第一个概念就是html tag(标签)和它在浏览器里面对应的表现形式,也就是我们所知的element(元素)。

查看HTML内容时,区分标签和元素是很重要的。HTML内容包含各种标签,例如<B>。 这个标签会指定文档(document)的文本的一个表达形式(加粗)。当浏览器访问一个页面时,HTML解析器会读取文件内容,并且从tag中解析生成element。这些就是可以作为一个被编程修改的对象的元素。当然,这也是Markup服务可以操作的元素。

例如,一个HTML文件可能有如下内容:

<P>First<P>Second

当浏览器的解析器读取这个文本时,内部的元素配置会让文档的形式变为类似这样的:(当然,有时候也可以称作标准化,主要是我习惯这么称呼)

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

或者可以说,解析器将HTML内容转为了元素。在这个过程中,为了内容完整,有一些原始文档没有的内容加进去了,例如html、head、title、body会自动的被解析器构造出来。同时,解析器遇到第二个p(段落)的时候,会自动的把第一个p给封闭起来。尽管你的文件没有封闭p标签,但是IE将会自动的给每个元素都加上封闭标签。还有必要但是你没有写入的标签,比如<html>、<body>,都会自动的被IE添加上,当然,他们的封闭标签也会被加上。

第二个需要注意的概念是tree和stream(树、流)的区别,比如:

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

这里有“My dog has fleas”和一对b标签,在这个例子中,可以被转化为如下的树。text被当为树叶,element被作为内节点。

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

通过把文档转为tree,所有的操作都会变为类似对树的操作,例如增删孩子节点。提供此类操作的API被称为Tree Services。

当然,自IE4.0之后,元素的模型操作比简单的树更强悍,比如这个例子:

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

B、I的范围互相交叉,这是一个部分互相交叉元素的例子,但是在HTML里面却很常见。因此,Markup Services不提供类似树的操作,而是为内容的控制暴露了一个基于流操作的模型。因此,Markup Service实际上是用来避免产生这种模型层间的疑惑的,因为这个时候,浏览器便不再使用Tree Service,而是使用Markup Service来控制基于流操作的模型。

基于树的模型中,网页内容被当作树的节点来处理,每个元素,或者一块Text都是一个节点。节点通过这种类似对树的操作方式来操作,例如从父节点中增删一个子节点。

基于流的模型的内容操作方式中,比如现在说的这种通过Markup Service来操作的,文档的内容会通过使用类似迭代器的对象来操作。 比如使用Markup Pointer,然后文档的内容则通过类似Range的操作来控制。 这个就像是在处理上面Where do <B>you <I>want to</B> go</I> today?的例子一样,这些带有部分重叠的元素通过两个Markup Pointer来区分,每个Markup Pointer指定着Tag从哪儿开始,Tag到哪儿结束。基于流的模型是基于树的模型的一个超集。

f1.png
图: elment overlapping

有效和无效的文档
另一个让Markup Service更加容易理解的概念就是创建和操作无效文档的过程。

注意之前“My dog has fleas”的例子都可能不会被认为是一个有效的HTML文档。如果把它拷贝到文件中,然后在浏览器中打开的话,浏览器的解析器有可能会生成一些完全不一样的文档内容。例如,Internet Explorer解析器可能将这个文档解析成这样:

<HTML><HEAD><TITLE></TITLE></HEAD>
<BODY>My <B>dog</B> has fleas.</BODY></HTML>

解析器会试图读取一个指定的输入,然后通过它生成一个有效的HTML文档。最简单的有效HTML文档至少要有html、head、title和body四个元素。当你提供的内容中没有这些元素时,解析器会自动为你建立这些,然后把它们放到合适的位置上。

在文档解析完成甚至是还没解析完成的时候,你都可以使用Markup Service来用任意方法删除或者重新排列文档内容。例如,你可以整块删除html和/或body元素。你可以将head放到body里面,但是这些样子的文档都会被认为是无效文档。

上面这些描绘出来了基本的Marup Service的概念,现在可以更进一步的看一下Markup Service的接口了。最好的入手点当然是IMarkupService接口。这个接口是所有的Markup Service的初始点,例如IMarkupContainer和IMarkupPointer也不例外。IMarkupService界面也包含了所有的可以修改文档中的元素的方法。

你可以通过QueryInterface来指定IID_IMarkupService来获取IMarkupServices。

MarkupContainer
元素可以不通过IMarkupContainer的上下文来创建,但是如果需要将元素和文本互相关联起来的话,IMarkupContainer还是必须要用的。

下面的例子将介绍如何使用IMarkupServices::CreateMarkupContainer从IMarkupServices中创建一个IMarkupContainer。

HRESULT CreateMarkupContainer(
    IMarkupContainer **ppContainer
);

最开始,新创建的IMarkupContainer不会包含有任何的Markup。而且,也不会有html、head、body元素。所以,IMarkupContainer的最初状态不是像是由解析器解析一个空文件的时候的样子(解析空文件的时候就会自动产生上述元素)。

正常情况下,IMarkupContainer用来存储等待加入主IMarkupContainer的元素。主IMarkupContainer是一个浏览器用来承载HTML解析之后内容的东西。你可以通过在一个HTML文档上执行QueryInterface IID_IMarkupContainer操作来获取主IMarkupContainer。 例如你可以从IID_IMarkupContainer获取IHTMLDocument2接口。

MarkupPointer
IMarkupPointer不是IMarkupContainer(这个就是一个文档)的内容的某一部分。使用IMarkupPointer的主要目的是指定文档中的某个特定位置。比如下面这个例子:

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

p1指针表示IMarkupPointer的位置,尽管p1指在d和o之间,但是这个并不是说这里有任何其他的看不见的文字在文档里面,或者例子里面这个内容已经被修改了。文档里面可以存在任意多个指针,这些指针和文档是独立的,也就是说根本不需要也不会修改文档。

Markup指针被放在了文档内容中间的某个地方,这些地方可以是:1、一个元素开始生效的区域(作用域开始);2、一个元素中止生效的区域;3、文本。因此,Markup指针更像是编辑器里面的脱字符(| ,或者通俗的叫光标,一闪一闪的这个东西)。因为Markup指针自己并不是文档内容,如果他们指向HTML内容中的同样的位置,这样他们也是不能互相区分开的。也就是说,如果两个Markup指针都指到一个地方,要区分哪个是左,哪个是右是不可能的。只能说,他们都指在了内容的同一个地点上。

你可以通过 IMarkupServices::CreateMarkupPointer 方法来创建一个Markup指针。

HRESULT CreateMarkupPointer(
    IMarkupPointer **ppPointer
);

定位Markup指针
当一个Markup指针被创建的时候,它将处于一个特殊的状态——未指向状态,意思就是它事实上没指向任何内容。你可以使用这三个方法来把一个Markup指针放到一个Markup上。

IMarkupPointer::MoveAdjacentToElement
IMarkupPointer::MoveToContainer
IMarkupPointer::MoveToPointer

IMarkupPointer::MoveAdjacentToElement方法接收2个参数, 一个IHTMLElement和一个枚举量,指定要放置指针的那个元素的相对偏移。这个枚举量有以下4个值。

HRESULT MoveAdjacentToElement(
    IHTMLElement *elementTarget,
    ELEMENT_ADJACENCY
);

    enum ELEMENT_ADJACENCY {
         ELEMENT_ADJ_BeforeBegin
         ELEMENT_ADJ_AfterBegin
         ELEMENT_ADJ_BeforeEnd
         ELEMENT_ADJ_AfterEnd
    };

因此,把p1放到b结束前(ELEMENT_ADJ_BeforeEnd)的话,差不多就是这个结果:

My <B>dog[p1]</B> has fleas.

现在考虑如下例子:

a<B>[p1]<I>b</I></B>c

p1现在可以说是放在b刚开始的地方,或者放在i开始之前。这两个描述方式都对,所以Markup指针放置的位置指定方式是多种多样的。

另一个方式来放置一个Markup指针的方式是使用 IMarkupPointer::MoveToContainer 方式。这个方法会把一个IMarkupContainer接口和一个决定指针位置是在IMarkupContainer开始还是结束地方的布尔值常量。

HRESULT MoveToContainer(
    IMarkupContainer *containerTarget,
    BOOL fAtStart
);

因此,你可以把一个指针放在一个文档的最边缘处,例如

[p1]<HTML><BODY>a<B><I>b</I></B>c</BODY></HTML>[p2]

p1在最左,而p2是最右。第三个方式是使用IMarkupPointer::MoveToPointer把一个指针移动到另一个已经定位过的IMarkupPointer的位置上。

HRESULT MoveToPointer(
    IMarkupPointer *pointerTarget
);

通常,IMarkupPointer::MoveToPointer在一个指针用来检查环绕元素时用来记录这个指针指向的位置。

比较指针位置
可以通过IMarkupPointer提供的一组函数来比较两个Markup指针的相对位置,函数列举如下:

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

因此,当你像知道p1是否与p2不等,而且在p2的左边的时候,就可以这么用:

BOOL fResult;
IMarkupPointer * pointer 1, * pointer 2;

..

[p1]->IsLeftOf( pointer2, & fResult );

if (fResult)
{
    // [p1] is to the left of pointer2
}

导向指针
当一个IMarkupPointer指针被放置在一个IMarkupContainer中时,你可以使用它来检查环绕内容,并且/或者将它移动到那个内容之外。 IMarkupPointer::Left、 IMarkupPointer::Right两个方法可以做到这个。

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

除了第一个参数之外都是可选的,fMove参数控制着指针是否穿过环绕的内容。如果它的值是FALSE,指针不会移动,这里代表着环绕的内容。如果是TRUE,这里不仅会描述环绕的内容,还会把指针从这个环绕内容上移动过去。

也就是说,如果你想知道一个指针的左边是什么,尽管调用 IMarkupPointer::Left 就可以了。右边也是,换成Right即可。 pContextType参数返回挨着Pointer后面的内容。

以下是可选的内容类型:

CONTEXT_TYPE_None 指针左边或者右边没有内容,这个仅当指针指向IMarkupContainer最左或者最右的时候会用到。
CONTEXT_TYPE_Text 给定方向上的内容是文本。
CONTEXT_TYPE_EnterScope 给定方向上的元素正在进入一个区域(scope)。也就是说,如果向左看是一个终止tag(带/的tag),向右看是一个起始tag。
CONTEXT_TYPE_ExitScope 在给定方向上,一个元素即将离开一个区域。也即,向左看的时候是一个起始tag,而朝右看是一个终止tag。
CONTEXT_TYPE_NoScope 给定的选区中有一个无区域元素,你不能用IMarkupPointer指向这类元素,例如br。

如果ppElement参数是非NULL的话,那么上下文的类型就是EnterScope、ExitScope、NoScope中的一种,ppElement参数会返回进入、退出、无scope的元素。

如果上下文是Text,pCch和pch参数就是有意义的。pCch参数提供这三个主要作用:

  • 它限制了IMarkupPointer::Left或者Right会查询的字数。
  • 它限制了给出方向上应该有多少文本实际存在。
  • 它描述了pch参数会指向多大的缓冲区(如果它指向的内容是非空的话)

pCch参数可以是NULL,或者-1 。这两个值表示 IMarkupPointer::Left 或者 IMarkupPointer::Right应该查询任意数量的文字,直到找到下一个无scope的元素或者找到某个元素的scope位置。

IMarkupPointer::Left 和 IMarkupPointer::Right两个方法提供了遍历文档的功能。要确定IMarkupPointer挡墙指着哪儿,使用如下IMarkupPointer::CurrentScope方法:

HRESULT CurrentScope(
    IHTMLElement **ppElementCurrent
);

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

比如上面的"Where do you want to go today?"例子, p1使用IMarkupPointer::CurrentScope的话,获取的值是NULL,因为它的左边没有任何未结束的起始tag。 而p4则是<B> tag。注意br是一个无scope类型的tag。

指针重力
一般地,当一个文档被修改之后,文档中之前的那些指针还是停在操作发生之前的位置,比如下面这个有2个指针插入的文档:

abc[p1]defg[p2]hij

现在文档内容发生了变化,XYZ插入了e和f之间,现在文档的内容如下:

abc[p1]deXYZfg[p2]hij

注意p1和p2还是指向操作前的同样的文本。比如下面的例子:

x[p1]y

现在考虑一下,如果Z插在了x、y中间是什么情况。记住指针并不会成为内容的一部分,因此x、y是互相挨着的。在插入之后,有可能有如下两个情况:

x[p1]Zy
xZ[p1]y

现在就需要有重力这个设定了。比如,通常当内容准确地插入了指针所在的位置的时候,指针的终止区域判定就会编的有歧义。通过引入重力设定,可以消除这种歧义。左重力会让指针定位到新插入的内容的左边,右重力下则是右边。

重力的不仅仅会影响到文本,还会影响到元素的插入,例如:

a[p1,right][p2,left]b

这里,p1有右重力,p2有左重力,如果b的周围插入了一个<B>标签会怎样?结果是:

a[p2,left]<B>[p1,right]b</B>

注意现在指针是如何从之前的相对位置上转换成现在的样子的。插入B时这两个指针的位置的移动方向都是有歧义的。

默认的重力是左重力,你可以通过IMarkupPointer接口的如下方法来设置IMarkupPointer的重力值。

enum POINTER_GRAVITY {
    POINTER_GRAVITY_Left,
    POINTER_GRAVITY_Right
};

HRESULT Gravity(
    POINTER_GRAVITY *pGravityOut
);

HRESULT SetGravity(
    POINTER_GRAVITY newGravity
);

指针粘滞(cling)
有如下Markup:

[p2]ab[p1]cdxy

现在考虑一下,当之前这个例子中,bc两个字被移动到x、y中间的时候,p1会发生什么?可能答案有两种:

1、 [p2]a[p1]dxbcy
2、 [p2]adxb[p1]cy

这两个例子里面,可以确定的是p2没有受到影响,因为它并不在被操作的部分附近。上面两个结果中,(1)里面的p1并没有IMarkupPointer::Cling, 而(2)则是有设置IMarkupPointer::Cling。 IMarkupPointer::Cling设置的结果导致了当一部分内容移动的时候,这个内容中间被Cling的部分也会跟着移动。不管内容移动到哪儿,有IMarkupPointer::Cling的指针都会在那块内容中。

但是,这个很有可能产生歧义。比如带有IMarkupPointer::Cling的p1:

a[p1]bcxy

如果b被移动到了x、y中间,p1是否应该跟着b走呢?因此,这里就要用到之前说的重力。如果p1有右重力,那么它会跟着b跑,如果是左重力,那么就会跟着它左边的内容,也就是a,而不会跟着b跑。

如果p1所在的内容被删除了,IMarkupPointer::Cling依然会控制指针的目标。比如下面的例子:

ab[p1]cd

如果b、c被删除了,而且p1没有IMarkupPointer::Cling, p1会继续在文档中,夹在还剩下来的,环绕着它的内容里面:

a[p1]d

如果p1有IMarkupPointer::Cling,这个时候p1就会变成未指定位置的状态,就像已经被删除一样。 (p1此时虽然被从文档里面移除removed了,但是它本身并没有被删除destroy,所以以后也可以重用。 这个设计理念导致出漏洞的话,也一样会被"重用"。)

ad

IMarkupPointer::Cling可以通过IMarkupPointer::SetCling来设置,IMarkupPointer::Cling来查询。

HRESULT Cling(
    BOOL *pClingOut
);

HRESULT SetCling(
    BOOL NewCling
);

新建元素
可以通过IMarkupService::CreateElement来创建新元素,

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

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

例如,IMarkupServices::CreateElement ( TAGID_B, "id=anID", & pElement )将会创建一个B元素,而且IHTMLElement::id的属性会设置为anID,当然,这里的属性项是可选的。在元素建立之后也是可以设置属性的,但是在创建元素时就指定属性的话,会让Internet Explorer处理时有更高的效率。也有一些属性是只能在元素创建时指定的。

还可以通过克隆一个已经存在的元素,使用IMarkupService::Clone即可:

HRESULT CloneElement(
    IHTMLElement *pElementCloneElementMe,
         IHTMLElement **ppNewElement
);

插入一个元素
通过调用IMarkupServices::InsertElement可以插入一个元素。

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

pPointerStart描述了元素从哪里开始进入一个Scope,pPointerFinish描述了元素从哪里开始离开Scope。当前正准备插入的元素必须是一个不在当前文档中的元素,而且两个指针都必须在同一个IMarkupContainer中定位。比如,假如调用IMarkupServices::InsertElement插入一个B元素,指针如下:

My [pstart]dog[pend] has fleas.

插入的结果将在文档中体现如下:

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

至于什么新元素可以插入到哪儿,这个倒没有什么严格限制。因此,你甚至可以插入n个BODY到文档里面,或者插入n个B到文档的head部分。但是,如果你的文档最终是要用来显示出来的话,这个状态是未定义的,而且会导致Markup Service发生变化。

删除一个元素
删除一个元素并不需要使用Markup 指针。调用IMarkupService::RemoveElement,然后传入要删除的元素就可以了。

HRESULT RemoveElement(
    IHTMLElement *pElementRemoveThis
);

要操作的元素必须要在文档里面,操作完成之后,元素就不在文档里了,因此是可以再次被插入的。

注意 要删除一个元素,然后把它插入到同一个位置上,你必须在删除之前把Markup指针插入到紧挨着这个元素区域的开始和结束位置。这个情况下,Markup指针将记录该元素在该Markup里影响到的范围。接下来Markup指针就可以被用来重新插入这个元素。当然,需要确保的是这个指针没有IMarkupPointer::Cling属性,因为它们可能在元素被移除时变成未定位的状态。

插入文本
要向Markup中插入文本,可以使用IMarkupServices::InsertText函数。

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

这个函数只接收单单一个IMarkupPointer,然后把text插入到markup里面。Markup指针在插入之后的位置(包括新插入的文本的位置也是)取决于IMarkupPointer的重力属性。 cch参数可以设置为-1, 这个表示这个函数应该认为插入的文本是以NULL终止的。

另外一提,Internet Explorer中的cch大多数是指 count of char的意思。

移除内容
你可以使用IMarkupContainer::Remove来移除IMarkupContainer中一片连续区域。

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

这里提供了两个Markup指针,一个指定从哪儿开始删除,另一个指定删除区域的末尾。所有这两个指针中间的文本内容都会被删除,而且,所有完全落入这个区域的Markup都会被删除,任何起始早于Start、终止晚于End的Markup不会被删除,例如:

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

当调用IMarkupServices::Remove之后,结果变成了:

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

注意,现在s元素彻底小时了,i、u还在文档里,尽管它们的tags的一部分在移除区域的中间。元素b包含整个删除区域,因此它也是不受影响的。

替换内容
前两个例子可以用来删除和插入内容,整合这两个操作可以用来替换内容,例如:

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.. http://nul.pw
        pMS->Remove( pPtr1, pPtr2 );

        //inserting new text
        pMS->InsertText( (unsigned short *) pstrTo, -1, pPtr1 );
    }
    if (hr == S_FALSE) return FALSE;
    else return(TRUE);
}

移动内容
你可以通过IMarkupServices::Move方法来把一组区域内的内容移动到另一个地方。

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

IMarkupServices::Move接受3个Markup指针,2个用来指明要移动的原始位置,第三个指定目标地点。范围的影响可以参考IMarkupServices::Remove操作的。在Source区域内的内容将被移动到Target指定的位置。

所有被Source范围包括起来的内容都会原样移动到Target去。也就是说,这些元素的信息都会被保留。在区域外的元素不会受到影响,也不会被弄到目标地址上。但是,和区域部分重叠的内容会被克隆,它们的IMarkupService::CloneElement会被移动到Target上。因此,之前Move操作的例子中,如果这个区域改为移动的话:

X[pdest]Y

结果会是:

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

注意,pdest在新插入的移动的内容的左边,这是因为它有左重力。而且还有I'和U'元素,他们是原来的I、U元素的克隆。因为元素只可以存在于一个Markup中,而且必须在一个Markup中影响到一个连续的范围。但是s这个元素却不会被IMarkupService::CloneElement影响到,这是因为s元素在移动时已经被start和end两个指针完全环绕了。

注意 经常在一次移动(或者一次拷贝)之后,你会需要两个指针指向新插入的内容的左边和右边。要实现这个的话,在Move之前创建2个Markup指针,一个设置为左重力,一个设置为右重力,右重力的那个指针会指向移动/复制的内容的右边,左重力的当然是指向左边。

移动操作的目标可以在Source开始和End区域中间。

复制内容
使用IMarkupServices::Copy可以复制一个内容区域。

HRESULT Copy(
    IMarkupPointer *SourceStart,
    IMarkupPointer *SourceEnd,
    IMarkupPointer *Target
);

对目标Markup来说,Copy的影响和Move一样,不会影响到源。

参考资料
以下资料可以提供更多与组件对象模型COM的知识:

Internet Explorer 完全解析 [5]

5 对前四个大类的总结
对前四个大类的总结,归纳如下,当然,全部都是用自然语言描述的。我们可以勾勒出这样一个模型:
f1.gif
当然,方框之间只是表示“有关系”,而不是“从属关系”

我们知道,CBase作为基类,提供了许多虚函数以及基础的抽象实现。 派生出来的CElement和CMarkup对CBase进行了功能上的扩充。CMarkup作为Markup语言的解析器、处理者、辅助工具而存在,CElement则是CMarkup操作管理对象的具象。

同样,CElement也是许多其他类的基类,例如Anchor、Button、RadioBox,均是由此派生而来。

CDoc则依旧作为经典的Doc-View视图中的文档部分存在着,管理着下属的数据,包括其中的CMarkup(s)、CElement(s),以及更多的例如网络状态、网页状态、网页属性、层叠样式表的管理、下载管理、内容编码管理等等。CDoc依附于宿主而存在,CView也依旧担负着View部分的责任,负责处理CDoc发来的数据,并在宿主之上建立视图。

各个类各司其职,完成了从HTML到网页展示的转换。如果你有学过MFC,或许你会对这个架构理解的十分容易。

Internet Explorer 完全解析 [4]

第四章 文档的具象实现
前几个类的介绍都不是完整的,以后有需要再补充。 免责声明:本类文章全部是我的个人理解,可能存在理解错误,如有发现,敬请指正。
4.1 介绍
CDoc910类将是我们要详细叙述的最后一个类,它可以说是网页文档的具象实现,同时,它也会贯穿在所有的类中,在分析时经常会看到许多类都关联着CDoc *pDoc这样一个成员函数,让我们看一看CDoc的真身吧。

4.2 CDoc概述
CDoc(formknl)也就是传说中的根对象(root object),对应传统的文档视图结构的文档部分。有些像是MFC中的CDoc,事实上Internet Explorer中也确实还有一个CView类。CDoc作为IHTMLDocument2/3接口的一个具体的实现,它自身封装了许多与Markup有关的内容,例如创建、增删Markup等等。对Markup具体的介绍请见完全解析Internet Explorer [3]、[5]以及“对Markup的介绍[译]”。
可以极为简化地说,一个CDoc管理着一组CMarkup,而CMarkup又与一组CElement有关。这个便是前几个内容之间的内在联系。

4.3 CDoc的成员变量
CDoc的成员变量列举如下,即使在日常调试中没有私有符号,我想,这依然可以作为参考,至少通过成员变量可以了解这个类具体能做什么。

  • CDefaultElement * _pElementDefault。 指定默认元素,如果文档中没有任何可用元素的话,那么就会用到这个默认元素。
  • CMarkup * _pPrimaryMarkup。 主markup,也就是指向当前根元素的markup。
  • CEditRouter _EditRouter。 提供垂直消息路由(也就是元素层级中传递消息)的。
  • CCaret * _pCaret。判断用户是否在编辑文档时用的。
  • CDocInfo _dci。 文档信息,文档内容传输时用。
  • CElement * _pElemEditContext。存储着当前正在编辑的选区,或者就是当前选区。
  • CElement * _pElemUIActive。当前正在显示着的UI。
  • CElement * _pElemCurrent。当前获得焦点的元素,上面说的消息路由也会从这儿开始。
  • CElement * _pElemDefault。 默认元素。
  • long _lSubCurrent。用来细分当前元素功能用。
  • CElement * _pElemNext。下一个会获得焦点的元素。
  • CRect * _pRectFocus。上一个获得焦点并且被渲染的元素的区域。
  • int _cSurface/_c3DSurface。 计数器。
  • OPTIONSETTINGS * _pOptionSettings。指向当前用户可以编辑的属性,例如文字颜色等。
  • CODEPAGESETTINGS * _pCodepageSettings。 指定codepage。
  • long _icfDefault; 。默认charformat index。
  • const CCharFormat * _pcfDefault;。 默认的charformat。
  • unsigned _cInval; 调用CDoc的Invalidate的次数。
  • unsigned _cProcessingTimeout;。 脚本执行时清空分块的超时值。
  • SHORT _iWheelDeltaRemainder;。 缩放值(zDelta)
  • CStr _cstrPasteUrl;。 粘贴时会用到的url。
  • RADIOGRPNAME *_pRadioGrpName;。 Radio Box的名字。
  • LONG _lRecursionLevel;。 最大递归层次。
  • DWORD _dwHistoryIndex;。 下一个可用的历史记录index。
  • CView _view;。 View的支持。
  • long _lLastTextID; 。 TextID。
  • long __lDocTreeVersion;。 当doc关联的markup中任何元素导致树变化时,这个值会加一。仅仅改变树中文本节点的值的时候不会改变这个版本号。
  • long __lDocContentsVersion;。 任何内容的改变都会导致这个版本加一。不管是markup还是文本节点。
  • CSelectionObject * _pCSelectionObject;。 选区对象。
  • CAtomTable _AtomTable;。 存储着元素-名字(name)的映射
  • IDocHostUIHandler * _pHostUIHandler;。 宿主的整合,这个是UIHandler的接口指针。
  • IDocHostUIHandler * _pBackupHostUIHandler;。 上一个(称为主UIHandler)失败时调用这个备选的。
  • IOleCommandTarget * _pHostUICommandHandler;。 UIHandler的Command Target。
  • DWORD _dwFlagsHostInfo;。 宿主的flags
  • DWORD _dwFrameOptions;。 框架选项。
  • CStr _cstrHostCss; 。宿主下发下来的css规则。
  • CStr _cstrHostNS;。 命名空间列表,用分号分割。
  • CElement * _pElementOMCapture;。 用于处理鼠标捕获。
  • PFN_VOID_MOUSECAPTURE _pfnCapture;。 鼠标捕获事件的处理函数。
  • void * _pvCaptureObject;。 触发事件的对象。
  • CElement * _pMenuObject;。 当前调用site(ie的这个术语当成元素来看比较靠谱?)的菜单对象。
  • CTreeNode * _pNodeLastMouseOver;。 上一次触发mouseOver事件的元素。
  • long _lSubDivisionLast; 鼠标移动过的上一个区域。
  • CTreeNode * _pNodeGotButtonDown;。 捕获到鼠标按下的site。
  • HMENU _hMenuCtx;。 菜单上下文。
  • USHORT _usNumVerbs;。 上下文菜单中的verb(活动元素)
  • HWND _hwndCached;。 处理状态时负责承载的窗口。
  • ULONG _cFreeze;。 冻结计数
  • IUnknown * _punkMimeOle;。 维持MimeOle对象的计数用。(保活)
  • IStream * _pStmDirty;。 脏文档数据流,用于刷新。
  • IMoniker * _pmkName; 。 为IPersistMoniker保存的当前使用的Moniker。
  • CDwnPost * _pDwnPost;。 用于获取当前doc的推送数据。
  • CStr _cstrUrl; 。 内部使用,当前doc的base url。
  • CStr _cstrSetDomain;。 URL Host name的子集。
  • SAFETYLEVEL _safetylevel;。 当前页面的安全等级。
  • SSL_SECURITY_STATE _sslSecurity;。 当前页面安全等级,不安全,混合,安全。
  • SSL_PROMPT_STATE _sslPrompt; 。 SSL提示,允许,询问,拒绝。
  • LONG _cInSslPrompt;。 当前的提示字段指示。
  • IHlinkBrowseContext *_phlbc;。 超链接的浏览上下文,用于历史等地方。
  • DWORD _dwLoadf;。 加载标识,离线,安静,等等。
  • IUrlHistoryStg *_pUrlHistoryStg;。 历史存储(history storage)
  • CTaskLookForBookmark *_pTaskLookForBookmark;。 检查书签的task。
  • CMapElement * _pMapHead;。指向树中的map对象。
  • long _readyState;。 readystate的指示。
  • ULONG _ulProgressPos;。 进度位置。
  • ULONG _ulProgressMax;。 最大进度指示。
  • (以下挑重要的来了,这里的东西实在是太多了)
  • CDoc* _pDocParent;。 父文档的指针
  • IHTMLEditor* _pIHTMLEditor;。 选区管理器
  • CStyleSheetArray *_pHostStyleSheets;。 宿主传来的所有的样式表。

4.4 CDoc的成员函数
CDoc提供了一大堆的成员函数,在这里我们将列举出部分重要或者常用的成员函数,以供参考。

  • InitDocClass。 初始化,生成一个CDoc类,由DLL的LibMain调用。
  • CDoc::CDoc。 CDoc的构造函数,这个和CDoc::Init合起来完成了CDoc的初始化,当然,构造函数是第一个被调用的。
  • CDoc::~CDoc。 CDoc类的析构函数。
  • CDoc::CreateRoot。为当前CDoc创建一个主根元素。
  • CDoc::Init。 初始化的第二阶段。
  • CDoc::Passivate。释放对其他元素的引用,藉由可以释放主对象。
  • CDoc::UnloadContents。 释放所有资源。
  • CDoc::RunningToLoaded。 通知主根元素OS_RUNNING事件。
  • CDoc::HitTestPoint。 在指定点击位置上向CView发出请求,找到对应位置的元素。
  • CDoc::Update。 更新对应View的缓存。
  • CDoc::IsUpToDate。 获取Cache状态是否为最新。
  • CDoc::Close。 关闭当前对象。
  • CDoc::ParentFrameSite。 当当前CDoc是在frameset中呈现的话,这个会返回它的parent site,否则返回null。
  • CDoc::ParentIFrameSite。 同上,不过是iframe。
  • CDoc::BroadcastNotify。向整个树中广播通知。
  • CDoc::SetDocParent。 设置parent。

  • 有参考意义的网页 http://msdn.microsoft.com/en-us/library/aa752038%28v=vs.85%29.aspxhttp://msdn.microsoft.com/en-us/library/aa741317%28v=vs.85%29.aspxhttp://msdn.microsoft.com/en-us/library/bb508514%28VS.85%29.aspx

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。