CEF中访问修改HTML DOM元素

 

有时你可能想在C++代码中直接操作HTML中的某个元素,比如改变某个按钮的状态(文字、颜色)等,此时可以使用CEF提供的CefDomVisitor、CefDOMDocument、CefDomNode这三个类,包含cef_dom.h即可。

我们可以用它们完成下列任务:

使用DOM模型访问HTML的各种节点(Element、Attribute、Text、CDATA、Comment、Document等)
修改某个元素的属性
修改某个Text节点的值
下面简要说说各个类的用法。

CefDOMDocument
CefDOMDocument对应JS里的document,不过功能少一些,类声明如下:

class CefDOMDocument : public virtual CefBase {
 public:
  typedef cef_dom_document_type_t Type;

  ///
  // Returns the document type.
  ///
  /*--cef(default_retval=DOM_DOCUMENT_TYPE_UNKNOWN)--*/
  virtual Type GetType() =0;

  ///
  // Returns the root document node.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetDocument() =0;

  ///
  // Returns the BODY node of an HTML document.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetBody() =0;

  ///
  // Returns the HEAD node of an HTML document.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetHead() =0;

  ///
  // Returns the title of an HTML document.
  ///
  /*--cef()--*/
  virtual CefString GetTitle() =0;

  ///
  // Returns the document element with the specified ID value.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetElementById(const CefString& id) =0;

  ///
  // Returns the node that currently has keyboard focus.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetFocusedNode() =0;

  ///
  // Returns true if a portion of the document is selected.
  ///
  /*--cef()--*/
  virtual bool HasSelection() =0;

  ///
  // Returns the selection offset within the start node.
  ///
  /*--cef()--*/
  virtual int GetSelectionStartOffset() =0;

  ///
  // Returns the selection offset within the end node.
  ///
  /*--cef()--*/
  virtual int GetSelectionEndOffset() =0;

  ///
  // Returns the contents of this selection as markup.
  ///
  /*--cef()--*/
  virtual CefString GetSelectionAsMarkup() =0;

  ///
  // Returns the contents of this selection as text.
  ///
  /*--cef()--*/
  virtual CefString GetSelectionAsText() =0;

  ///
  // Returns the base URL for the document.
  ///
  /*--cef()--*/
  virtual CefString GetBaseURL() =0;

  ///
  // Returns a complete URL based on the document base URL and the specified
  // partial URL.
  ///
  /*--cef()--*/
  virtual CefString GetCompleteURL(const CefString& partialURL) =0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
如你所见,它能获取一些字符串值(URL、标题等),能根据id查找某个元素(在JS里我们最常用的方式),能返回Document、Head、Body等节点,这些节点的类型是CefDOMNode。

注意这个类的方法只能在Renderer进程的主线程上调用(TID_RENDERER)。

CefDOMNode
在 HTML DOM (文档对象模型)中,每个部分都是节点:

文档本身是文档节点
所有 HTML 元素是元素节点
所有 HTML 属性是属性节点
HTML 元素内的文本是文本节点
注释是注释节点
CefDOMNode代表了一个HTML DOM节点,它的声明如下:

class CefDOMNode : public virtual CefBase {
 public:
  typedef std::map<CefString, CefString> AttributeMap;
  typedef cef_dom_node_type_t Type;

  ///
  // Returns the type for this node.
  ///
  /*--cef(default_retval=DOM_NODE_TYPE_UNSUPPORTED)--*/
  virtual Type GetType() =0;

  ///
  // Returns true if this is a text node.
  ///
  /*--cef()--*/
  virtual bool IsText() =0;

  ///
  // Returns true if this is an element node.
  ///
  /*--cef()--*/
  virtual bool IsElement() =0;

  ///
  // Returns true if this is an editable node.
  ///
  /*--cef()--*/
  virtual bool IsEditable() =0;

  ///
  // Returns true if this is a form control element node.
  ///
  /*--cef()--*/
  virtual bool IsFormControlElement() =0;

  ///
  // Returns the type of this form control element node.
  ///
  /*--cef()--*/
  virtual CefString GetFormControlElementType() =0;

  ///
  // Returns true if this object is pointing to the same handle as |that|
  // object.
  ///
  /*--cef()--*/
  virtual bool IsSame(CefRefPtr<CefDOMNode> that) =0;

  ///
  // Returns the name of this node.
  ///
  /*--cef()--*/
  virtual CefString GetName() =0;

  ///
  // Returns the value of this node.
  ///
  /*--cef()--*/
  virtual CefString GetValue() =0;

  ///
  // Set the value of this node. Returns true on success.
  ///
  /*--cef()--*/
  virtual bool SetValue(const CefString& value) =0;

  ///
  // Returns the contents of this node as markup.
  ///
  /*--cef()--*/
  virtual CefString GetAsMarkup() =0;

  ///
  // Returns the document associated with this node.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMDocument> GetDocument() =0;

  ///
  // Returns the parent node.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetParent() =0;

  ///
  // Returns the previous sibling node.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetPreviousSibling() =0;

  ///
  // Returns the next sibling node.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetNextSibling() =0;

  ///
  // Returns true if this node has child nodes.
  ///
  /*--cef()--*/
  virtual bool HasChildren() =0;

  ///
  // Return the first child node.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetFirstChild() =0;

  ///
  // Returns the last child node.
  ///
  /*--cef()--*/
  virtual CefRefPtr<CefDOMNode> GetLastChild() =0;

  // The following methods are valid only for element nodes.

  ///
  // Returns the tag name of this element.
  ///
  /*--cef()--*/
  virtual CefString GetElementTagName() =0;

  ///
  // Returns true if this element has attributes.
  ///
  /*--cef()--*/
  virtual bool HasElementAttributes() =0;

  ///
  // Returns true if this element has an attribute named |attrName|.
  ///
  /*--cef()--*/
  virtual bool HasElementAttribute(const CefString& attrName) =0;

  ///
  // Returns the element attribute named |attrName|.
  ///
  /*--cef()--*/
  virtual CefString GetElementAttribute(const CefString& attrName) =0;

  ///
  // Returns a map of all element attributes.
  ///
  /*--cef()--*/
  virtual void GetElementAttributes(AttributeMap& attrMap) =0;

  ///
  // Set the value for the element attribute named |attrName|. Returns true on
  // success.
  ///
  /*--cef()--*/
  virtual bool SetElementAttribute(const CefString& attrName,
                                   const CefString& value) =0;

  ///
  // Returns the inner text of the element.
  ///
  /*--cef()--*/
  virtual CefString GetElementInnerText() =0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
注意这个类的方法只能在Renderer进程的主线程上调用(TID_RENDERER)。

结合对HTML DOM节点的理解以及上面的代码,就能理解我们能使用CefDOMNode做什么:

使用IsXXX或GetType判断节点类型
使用GetNextSibling、GetPreviousSibling遍历兄弟节点
如果是Text节点(叶子节点),SetValue可以改变其文本
如果是Element节点,可以使用GetFirstChild、GetLastChild获取孩子,使用SetElementAttribute(s)改变属性,使用GetElementAttibute(s)获取属性
HTML DOM中的Element,有appendChild、insertBefore等方法,可以很方便地动态插入节点改变DOM和网页展示效果,而这个CefDOMNode就没有相应的方法,好像不太方便……

CefDOMVisitor
这个类的声明如下:

class CefDOMVisitor : public virtual CefBase {
 public:
  ///
  // Method executed for visiting the DOM. The document object passed to this
  // method represents a snapshot of the DOM at the time this method is
  // executed. DOM objects are only valid for the scope of this method. Do not
  // keep references to or attempt to access any DOM objects outside the scope
  // of this method.
  ///
  /*--cef()--*/
  virtual void Visit(CefRefPtr<CefDOMDocument> document) =0;
};
1
2
3
4
5
6
7
8
9
10
11
12
要访问或修改HTML DOM,就必须实现这个类,然后将其对象传递给CefFrame::VisitDOM(CefRefPtr visitor)方法,最后你的Visit方法就被调用来访问或修改HTML DOM。

看一个简单的实现,显示DomVisitTestor类的声明:

class DomVisitTestor : public CefDOMVisitor
{
public:
    DomVisitTestor();
    void TestAccess(CefRefPtr<CefDOMDocument> document);
    void TestModify(CefRefPtr<CefDOMDocument> document);

    void Visit(CefRefPtr<CefDOMDocument> document) OVERRIDE;

    IMPLEMENT_REFCOUNTING(DomVisitTestor);
};
1
2
3
4
5
6
7
8
9
10
11
然后是DomVisitTestor的实现:

void DomVisitTestor::TestAccess(CefRefPtr<CefDOMDocument> document)
{
    OutputDebugStringW(L"DomVisitTestor::TestAccess\r\n");
    OutputDebugStringW(document->GetTitle().ToWString().c_str());
    OutputDebugStringW(document->GetBaseURL().ToWString().c_str());

    CefRefPtr<CefDOMNode> headNode = document->GetHead();
    OutputDebugStringW(headNode->GetName().ToWString().c_str());
    OutputDebugStringW(headNode->GetAsMarkup().ToWString().c_str());
    wchar_t szLog[512] = { 0 };
    if (headNode->HasChildren())
    {
        CefRefPtr<CefDOMNode> childNode = headNode->GetFirstChild();

        do 
        {
            swprintf_s(szLog, 256, L"node name -%s, type-%d, value-%s\r\n",
                childNode->GetName().ToWString().c_str(), childNode->GetType(), childNode->GetValue());
            OutputDebugStringW(szLog);
        } while ( (childNode = childNode->GetNextSibling()).get() );
    }

    CefRefPtr<CefDOMNode> bodyNode = document->GetBody();
    OutputDebugStringW(bodyNode->GetAsMarkup().ToWString().c_str());
    if (bodyNode->HasChildren())
    {
        CefRefPtr<CefDOMNode> childNode = bodyNode->GetFirstChild();
        do
        {
            swprintf_s(szLog, 256, L"node name -%s, type-%d, value-%s\r\n",
                childNode->GetName().ToWString().c_str(), childNode->GetType(), childNode->GetValue());
            OutputDebugStringW(szLog);
        } while ((childNode = childNode->GetNextSibling()).get());
    }
}

void DomVisitTestor::TestModify(CefRefPtr<CefDOMDocument> document)
{
    OutputDebugStringW(L"DomVisitTestor::TestModify\r\n");
    CefRefPtr<CefDOMNode> bodyNode = document->GetBody();
    if (bodyNode->HasChildren())
    {
        CefRefPtr<CefDOMNode> childNode = bodyNode->GetFirstChild();
        wchar_t szLog[512] = { 0 };
        do{
            swprintf_s(szLog, 256, L"node name -%s,tagName-%s type-%d, value-%s\r\n",
                childNode->GetName().ToWString().c_str(), 
                childNode->GetElementTagName().ToWString().c_str(),
                childNode->GetType(), childNode->GetValue());
            OutputDebugStringW(szLog);
            if (childNode->IsElement() && childNode->GetElementTagName() == "H1"
                && childNode->GetElementAttribute("id") == "hello")
            {
                CefRefPtr<CefDOMNode> textNode = childNode->GetFirstChild();
                swprintf_s(szLog, 512, L"found hello, text - %s\r\n", textNode->GetValue().ToWString().c_str());
                OutputDebugStringW(szLog);
                textNode->SetValue("Hello World Modified!");
                break;
            }
        } while ((childNode = childNode->GetNextSibling()).get());
    }

    CefRefPtr<CefDOMNode> hello = document->GetElementById("hello");
    if (hello.get())
    {
        hello->SetElementAttribute("align", "center");
        OutputDebugStringW(L"Change hello align\r\n");
    }
}

void DomVisitTestor::Visit(CefRefPtr<CefDOMDocument> document)
{
    TestAccess(document);
    TestModify(document);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
注意,这个类的方法也应当在Renderer进程的主线程(TID_RENDERER)上使用。

测试用的HTML文件如下:

<!DOCTYPE html>
<html>
  <!--
  Copyright (c) 2016 foruok(微信订阅号“程序视界”)
  -->
    <style type="text/css">
    #divtest
    {
        position: relative;
        left: 30px;
        top: 10px;
        padding: 10px;
        width: 300px;
        height: 200px;
        background-color: gray;
    }
    </style>
<head>
    <script type="text/javascript">
      function test(){
        window.DomVisitTest();
      }
    </script>
    <title>Dom Visit Test</title>
</head>

<body>
<h1 id="hello">Hello</h1>
<form>
  <input  type="button" value="VisitDom" οnclick="test()"/>
</form>
one<br>two
<p>This is text</p>
<div id="divtest">
  <p>div hello</p>
</div>
</body>
</html>

发布了557 篇原创文章 · 获赞 47 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/huang714/article/details/105088468