OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // FIXME(dominicc): Poor confused check-webkit-style demands Attribute.h here. | 5 // FIXME(dominicc): Poor confused check-webkit-style demands Attribute.h here. |
6 #include "core/dom/Attribute.h" | 6 #include "core/dom/Attribute.h" |
7 | 7 |
8 #include "core/HTMLNames.h" | 8 #include "core/HTMLNames.h" |
9 #include "core/SVGNames.h" | 9 #include "core/SVGNames.h" |
10 #include "core/XLinkNames.h" | 10 #include "core/XLinkNames.h" |
(...skipping 16 matching lines...) Expand all Loading... |
27 #include "wtf/Vector.h" | 27 #include "wtf/Vector.h" |
28 #include "wtf/text/AtomicString.h" | 28 #include "wtf/text/AtomicString.h" |
29 #include "wtf/text/WTFString.h" | 29 #include "wtf/text/WTFString.h" |
30 | 30 |
31 // Test that SVG content with JavaScript URLs is sanitized by removing | 31 // Test that SVG content with JavaScript URLs is sanitized by removing |
32 // the URLs. This sanitization happens when the content is pasted or | 32 // the URLs. This sanitization happens when the content is pasted or |
33 // drag-dropped into an editable element. | 33 // drag-dropped into an editable element. |
34 // | 34 // |
35 // There are two vectors for JavaScript URLs in SVG content: | 35 // There are two vectors for JavaScript URLs in SVG content: |
36 // | 36 // |
37 // 1. Attributes, for example xlink:href in an <svg:a> element. | 37 // 1. Attributes, for example xlink:href/href in an <svg:a> element. |
38 // 2. Animations which set those attributes, for example | 38 // 2. Animations which set those attributes, for example |
39 // <animate attributeName="xlink:href" values="javascript:... | 39 // <animate attributeName="xlink:href" values="javascript:... |
40 // | 40 // |
41 // The following SVG elements, although related to animation, cannot | 41 // The following SVG elements, although related to animation, cannot |
42 // set JavaScript URLs: | 42 // set JavaScript URLs: |
43 // | 43 // |
44 // - 'discard' can only remove elements, not set their attributes | 44 // - 'discard' can only remove elements, not set their attributes |
45 // - 'animateMotion' does not use attribute name and produces floats | 45 // - 'animateMotion' does not use attribute name and produces floats |
46 // - 'animateTransform' can only animate transform lists | 46 // - 'animateTransform' can only animate transform lists |
47 | 47 |
(...skipping 24 matching lines...) Expand all Loading... |
72 | 72 |
73 // Integration tests. | 73 // Integration tests. |
74 | 74 |
75 TEST( | 75 TEST( |
76 UnsafeSVGAttributeSanitizationTest, | 76 UnsafeSVGAttributeSanitizationTest, |
77 pasteAnchor_javaScriptHrefIsStripped) | 77 pasteAnchor_javaScriptHrefIsStripped) |
78 { | 78 { |
79 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); | 79 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
80 static const char unsafeContent[] = | 80 static const char unsafeContent[] = |
81 "<svg xmlns='http://www.w3.org/2000/svg' " | 81 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 82 " width='1cm' height='1cm'>" |
| 83 " <a href='javascript:alert()'></a>" |
| 84 "</svg>"; |
| 85 String sanitizedContent = |
| 86 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 87 |
| 88 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 89 "We should have pasted *something*; the document is: " << |
| 90 sanitizedContent.utf8().data(); |
| 91 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 92 "The JavaScript URL is unsafe and should have been stripped; " |
| 93 "instead: " << |
| 94 sanitizedContent.utf8().data(); |
| 95 } |
| 96 |
| 97 TEST( |
| 98 UnsafeSVGAttributeSanitizationTest, |
| 99 pasteAnchor_javaScriptXlinkHrefIsStripped) |
| 100 { |
| 101 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 102 static const char unsafeContent[] = |
| 103 "<svg xmlns='http://www.w3.org/2000/svg' " |
82 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 104 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
83 " width='1cm' height='1cm'>" | 105 " width='1cm' height='1cm'>" |
84 " <a xlink:href='javascript:alert()'></a>" | 106 " <a xlink:href='javascript:alert()'></a>" |
85 "</svg>"; | 107 "</svg>"; |
86 String sanitizedContent = | 108 String sanitizedContent = |
87 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 109 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
88 | 110 |
89 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 111 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
90 "We should have pasted *something*; the document is: " << | 112 "We should have pasted *something*; the document is: " << |
91 sanitizedContent.utf8().data(); | 113 sanitizedContent.utf8().data(); |
92 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 114 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
93 "The JavaScript URL is unsafe and should have been stripped; " | 115 "The JavaScript URL is unsafe and should have been stripped; " |
94 "instead: " << | 116 "instead: " << |
95 sanitizedContent.utf8().data(); | 117 sanitizedContent.utf8().data(); |
96 } | 118 } |
97 | 119 |
98 TEST( | 120 TEST( |
99 UnsafeSVGAttributeSanitizationTest, | 121 UnsafeSVGAttributeSanitizationTest, |
100 pasteAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) | 122 pasteAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) |
101 { | 123 { |
102 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); | 124 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
103 static const char unsafeContent[] = | 125 static const char unsafeContent[] = |
104 "<svg xmlns='http://www.w3.org/2000/svg' " | 126 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 127 " width='1cm' height='1cm'>" |
| 128 " <a href='jAvascriPT:alert()'></a>" |
| 129 "</svg>"; |
| 130 String sanitizedContent = |
| 131 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 132 |
| 133 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 134 "We should have pasted *something*; the document is: " << |
| 135 sanitizedContent.utf8().data(); |
| 136 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 137 "The JavaScript URL is unsafe and should have been stripped; " |
| 138 "instead: " << |
| 139 sanitizedContent.utf8().data(); |
| 140 } |
| 141 |
| 142 TEST( |
| 143 UnsafeSVGAttributeSanitizationTest, |
| 144 pasteAnchor_javaScriptXlinkHrefIsStripped_caseAndEntityInProtocol) |
| 145 { |
| 146 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 147 static const char unsafeContent[] = |
| 148 "<svg xmlns='http://www.w3.org/2000/svg' " |
105 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 149 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
106 " width='1cm' height='1cm'>" | 150 " width='1cm' height='1cm'>" |
107 " <a xlink:href='jAvascriPT:alert()'></a>" | 151 " <a xlink:href='jAvascriPT:alert()'></a>" |
108 "</svg>"; | 152 "</svg>"; |
109 String sanitizedContent = | 153 String sanitizedContent = |
110 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 154 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
111 | 155 |
112 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 156 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
113 "We should have pasted *something*; the document is: " << | 157 "We should have pasted *something*; the document is: " << |
114 sanitizedContent.utf8().data(); | 158 sanitizedContent.utf8().data(); |
115 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 159 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
116 "The JavaScript URL is unsafe and should have been stripped; " | 160 "The JavaScript URL is unsafe and should have been stripped; " |
117 "instead: " << | 161 "instead: " << |
118 sanitizedContent.utf8().data(); | 162 sanitizedContent.utf8().data(); |
119 } | 163 } |
120 | 164 |
121 TEST( | 165 TEST( |
122 UnsafeSVGAttributeSanitizationTest, | 166 UnsafeSVGAttributeSanitizationTest, |
123 pasteAnchor_javaScriptHrefIsStripped_entityWithoutSemicolonInProtocol) | 167 pasteAnchor_javaScriptHrefIsStripped_entityWithoutSemicolonInProtocol) |
124 { | 168 { |
125 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); | 169 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
126 static const char unsafeContent[] = | 170 static const char unsafeContent[] = |
127 "<svg xmlns='http://www.w3.org/2000/svg' " | 171 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 172 " width='1cm' height='1cm'>" |
| 173 " <a href='javascript:alert()'></a>" |
| 174 "</svg>"; |
| 175 String sanitizedContent = |
| 176 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 177 |
| 178 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 179 "We should have pasted *something*; the document is: " << |
| 180 sanitizedContent.utf8().data(); |
| 181 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 182 "The JavaScript URL is unsafe and should have been stripped; " |
| 183 "instead: " << |
| 184 sanitizedContent.utf8().data(); |
| 185 } |
| 186 |
| 187 TEST( |
| 188 UnsafeSVGAttributeSanitizationTest, |
| 189 pasteAnchor_javaScriptXlinkHrefIsStripped_entityWithoutSemicolonInProtocol) |
| 190 { |
| 191 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 192 static const char unsafeContent[] = |
| 193 "<svg xmlns='http://www.w3.org/2000/svg' " |
128 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 194 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
129 " width='1cm' height='1cm'>" | 195 " width='1cm' height='1cm'>" |
130 " <a xlink:href='javascript:alert()'></a>" | 196 " <a xlink:href='javascript:alert()'></a>" |
131 "</svg>"; | 197 "</svg>"; |
132 String sanitizedContent = | 198 String sanitizedContent = |
133 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 199 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
134 | 200 |
135 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 201 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
136 "We should have pasted *something*; the document is: " << | 202 "We should have pasted *something*; the document is: " << |
137 sanitizedContent.utf8().data(); | 203 sanitizedContent.utf8().data(); |
138 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 204 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
139 "The JavaScript URL is unsafe and should have been stripped; " | 205 "The JavaScript URL is unsafe and should have been stripped; " |
140 "instead: " << | 206 "instead: " << |
141 sanitizedContent.utf8().data(); | 207 sanitizedContent.utf8().data(); |
142 } | 208 } |
143 | 209 |
144 // Other sanitization integration tests are layout tests that use | 210 // Other sanitization integration tests are layout tests that use |
145 // document.execCommand('Copy') to source content that they later | 211 // document.execCommand('Copy') to source content that they later |
146 // paste. However SVG animation elements are not serialized when | 212 // paste. However SVG animation elements are not serialized when |
147 // copying, which means we can't test sanitizing these attributes in | 213 // copying, which means we can't test sanitizing these attributes in |
148 // layout tests: there is nowhere to source the unsafe content from. | 214 // layout tests: there is nowhere to source the unsafe content from. |
149 TEST( | 215 TEST( |
150 UnsafeSVGAttributeSanitizationTest, | 216 UnsafeSVGAttributeSanitizationTest, |
151 pasteAnimatedAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) | 217 pasteAnimatedAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) |
152 { | 218 { |
153 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); | 219 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
154 static const char unsafeContent[] = | 220 static const char unsafeContent[] = |
155 "<svg xmlns='http://www.w3.org/2000/svg' " | 221 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 222 " width='1cm' height='1cm'>" |
| 223 " <a href='https://www.google.com/'>" |
| 224 " <animate attributeName='href' values='evil;JaVaSCRIpT:alert()'>
" |
| 225 " </a>" |
| 226 "</svg>"; |
| 227 String sanitizedContent = |
| 228 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 229 |
| 230 EXPECT_TRUE(sanitizedContent.contains("<a href=\"https://www.goo")) << |
| 231 "We should have pasted *something*; the document is: " << |
| 232 sanitizedContent.utf8().data(); |
| 233 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 234 "The JavaScript URL is unsafe and should have been stripped; " |
| 235 "instead: " << |
| 236 sanitizedContent.utf8().data(); |
| 237 } |
| 238 |
| 239 TEST( |
| 240 UnsafeSVGAttributeSanitizationTest, |
| 241 pasteAnimatedAnchor_javaScriptXlinkHrefIsStripped_caseAndEntityInProtocol) |
| 242 { |
| 243 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 244 static const char unsafeContent[] = |
| 245 "<svg xmlns='http://www.w3.org/2000/svg' " |
156 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 246 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
157 " width='1cm' height='1cm'>" | 247 " width='1cm' height='1cm'>" |
158 " <a xlink:href='https://www.google.com/'>" | 248 " <a xlink:href='https://www.google.com/'>" |
159 " <animate xmlns:ng='http://www.w3.org/1999/xlink' " | 249 " <animate xmlns:ng='http://www.w3.org/1999/xlink' " |
160 " attributeName='ng:href' values='evil;JaVaSCRIpT:alert(
)'>" | 250 " attributeName='ng:href' values='evil;JaVaSCRIpT:alert(
)'>" |
161 " </a>" | 251 " </a>" |
162 "</svg>"; | 252 "</svg>"; |
163 String sanitizedContent = | 253 String sanitizedContent = |
164 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 254 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
165 | 255 |
(...skipping 18 matching lines...) Expand all Loading... |
184 RefPtrWillBeRawPtr<Document> document = Document::create(); | 274 RefPtrWillBeRawPtr<Document> document = Document::create(); |
185 RefPtrWillBeRawPtr<SVGElement> target = SVGAElement::create(*document); | 275 RefPtrWillBeRawPtr<SVGElement> target = SVGAElement::create(*document); |
186 RefPtrWillBeRawPtr<SVGAnimateElement> element = SVGAnimateElement::create(*d
ocument); | 276 RefPtrWillBeRawPtr<SVGAnimateElement> element = SVGAnimateElement::create(*d
ocument); |
187 element->setTargetElement(target.get()); | 277 element->setTargetElement(target.get()); |
188 element->setAttributeName(XLinkNames::hrefAttr); | 278 element->setAttributeName(XLinkNames::hrefAttr); |
189 | 279 |
190 // Sanity check that xlink:href was identified as a "string" attribute | 280 // Sanity check that xlink:href was identified as a "string" attribute |
191 EXPECT_EQ(AnimatedString, element->animatedPropertyType()); | 281 EXPECT_EQ(AnimatedString, element->animatedPropertyType()); |
192 | 282 |
193 EXPECT_FALSE(element->animatedPropertyTypeSupportsAddition()); | 283 EXPECT_FALSE(element->animatedPropertyTypeSupportsAddition()); |
| 284 |
| 285 element->setAttributeName(SVGNames::hrefAttr); |
| 286 |
| 287 // Sanity check that href was identified as a "string" attribute |
| 288 EXPECT_EQ(AnimatedString, element->animatedPropertyType()); |
| 289 |
| 290 EXPECT_FALSE(element->animatedPropertyTypeSupportsAddition()); |
194 } | 291 } |
195 | 292 |
196 TEST( | 293 TEST( |
197 UnsafeSVGAttributeSanitizationTest, | 294 UnsafeSVGAttributeSanitizationTest, |
198 stripScriptingAttributes_animateElement) | 295 stripScriptingAttributes_animateElement) |
199 { | 296 { |
200 Vector<Attribute> attributes; | 297 Vector<Attribute> attributes; |
201 attributes.append(Attribute(XLinkNames::hrefAttr, "javascript:alert()")); | 298 attributes.append(Attribute(XLinkNames::hrefAttr, "javascript:alert()")); |
| 299 attributes.append(Attribute(SVGNames::hrefAttr, "javascript:alert()")); |
202 attributes.append(Attribute(SVGNames::fromAttr, "/home")); | 300 attributes.append(Attribute(SVGNames::fromAttr, "/home")); |
203 attributes.append(Attribute(SVGNames::toAttr, "javascript:own3d()")); | 301 attributes.append(Attribute(SVGNames::toAttr, "javascript:own3d()")); |
204 | 302 |
205 RefPtrWillBeRawPtr<Document> document = Document::create(); | 303 RefPtrWillBeRawPtr<Document> document = Document::create(); |
206 RefPtrWillBeRawPtr<Element> element = SVGAnimateElement::create(*document); | 304 RefPtrWillBeRawPtr<Element> element = SVGAnimateElement::create(*document); |
207 element->stripScriptingAttributes(attributes); | 305 element->stripScriptingAttributes(attributes); |
208 | 306 |
209 EXPECT_EQ(2ul, attributes.size()) << | 307 EXPECT_EQ(3ul, attributes.size()) << |
210 "One of the attributes should have been stripped."; | 308 "One of the attributes should have been stripped."; |
211 EXPECT_EQ(XLinkNames::hrefAttr, attributes[0].name()) << | 309 EXPECT_EQ(XLinkNames::hrefAttr, attributes[0].name()) << |
212 "The 'xlink:href' attribute should not have been stripped from " | 310 "The 'xlink:href' attribute should not have been stripped from " |
213 "<animate> because it is not a URL attribute of <animate>."; | 311 "<animate> because it is not a URL attribute of <animate>."; |
214 EXPECT_EQ(SVGNames::fromAttr, attributes[1].name()) << | 312 EXPECT_EQ(SVGNames::hrefAttr, attributes[1].name()) << |
| 313 "The 'href' attribute should not have been stripped from " |
| 314 "<animate> because it is not a URL attribute of <animate>."; |
| 315 EXPECT_EQ(SVGNames::fromAttr, attributes[2].name()) << |
215 "The 'from' attribute should not have been strippef from <animate> " | 316 "The 'from' attribute should not have been strippef from <animate> " |
216 "because its value is innocuous."; | 317 "because its value is innocuous."; |
217 } | 318 } |
218 | 319 |
219 TEST( | 320 TEST( |
220 UnsafeSVGAttributeSanitizationTest, | 321 UnsafeSVGAttributeSanitizationTest, |
| 322 isJavaScriptURLAttribute_hrefContainingJavascriptURL) |
| 323 { |
| 324 Attribute attribute(SVGNames::hrefAttr, "javascript:alert()"); |
| 325 RefPtrWillBeRawPtr<Document> document = Document::create(); |
| 326 RefPtrWillBeRawPtr<Element> element = SVGAElement::create(*document); |
| 327 EXPECT_TRUE( |
| 328 element->isJavaScriptURLAttribute(attribute)) << |
| 329 "The 'a' element should identify an 'href' attribute with a " |
| 330 "JavaScript URL value as a JavaScript URL attribute"; |
| 331 } |
| 332 |
| 333 TEST( |
| 334 UnsafeSVGAttributeSanitizationTest, |
221 isJavaScriptURLAttribute_xlinkHrefContainingJavascriptURL) | 335 isJavaScriptURLAttribute_xlinkHrefContainingJavascriptURL) |
222 { | 336 { |
223 Attribute attribute(XLinkNames::hrefAttr, "javascript:alert()"); | 337 Attribute attribute(XLinkNames::hrefAttr, "javascript:alert()"); |
224 RefPtrWillBeRawPtr<Document> document = Document::create(); | 338 RefPtrWillBeRawPtr<Document> document = Document::create(); |
225 RefPtrWillBeRawPtr<Element> element = SVGAElement::create(*document); | 339 RefPtrWillBeRawPtr<Element> element = SVGAElement::create(*document); |
226 EXPECT_TRUE( | 340 EXPECT_TRUE( |
227 element->isJavaScriptURLAttribute(attribute)) << | 341 element->isJavaScriptURLAttribute(attribute)) << |
228 "The 'a' element should identify an 'xlink:href' attribute with a " | 342 "The 'a' element should identify an 'xlink:href' attribute with a " |
229 "JavaScript URL value as a JavaScript URL attribute"; | 343 "JavaScript URL value as a JavaScript URL attribute"; |
230 } | 344 } |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 Attribute fineAttribute(SVGNames::fromAttr, "hello, world!"); | 405 Attribute fineAttribute(SVGNames::fromAttr, "hello, world!"); |
292 RefPtrWillBeRawPtr<Document> document = Document::create(); | 406 RefPtrWillBeRawPtr<Document> document = Document::create(); |
293 RefPtrWillBeRawPtr<Element> element = SVGSetElement::create(*document); | 407 RefPtrWillBeRawPtr<Element> element = SVGSetElement::create(*document); |
294 EXPECT_FALSE( | 408 EXPECT_FALSE( |
295 element->isSVGAnimationAttributeSettingJavaScriptURL(fineAttribute)) << | 409 element->isSVGAnimationAttributeSettingJavaScriptURL(fineAttribute)) << |
296 "The animate element should not identify a 'from' attribute with an " | 410 "The animate element should not identify a 'from' attribute with an " |
297 "innocuous value as setting a JavaScript URL."; | 411 "innocuous value as setting a JavaScript URL."; |
298 } | 412 } |
299 | 413 |
300 } // namespace blink | 414 } // namespace blink |
OLD | NEW |