OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 #include <limits> | 5 #include <limits> |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
10 #include "base/path_service.h" | 10 #include "base/path_service.h" |
(...skipping 13 matching lines...) Expand all Loading... |
24 #include "content/test/data/web_ui_test_mojo_bindings.mojom.h" | 24 #include "content/test/data/web_ui_test_mojo_bindings.mojom.h" |
25 #include "grit/content_resources.h" | 25 #include "grit/content_resources.h" |
26 #include "mojo/public/cpp/bindings/allocation_scope.h" | 26 #include "mojo/public/cpp/bindings/allocation_scope.h" |
27 #include "mojo/public/cpp/bindings/remote_ptr.h" | 27 #include "mojo/public/cpp/bindings/remote_ptr.h" |
28 #include "mojo/public/js/bindings/constants.h" | 28 #include "mojo/public/js/bindings/constants.h" |
29 | 29 |
30 namespace content { | 30 namespace content { |
31 namespace { | 31 namespace { |
32 | 32 |
33 bool got_message = false; | 33 bool got_message = false; |
34 int message_count = 0; | 34 int echo_message_count = 0; |
| 35 int flip_bits_message_count = 0; |
35 | 36 |
36 const int kExpectedMessageCount = 100; | 37 const int kExpectedEchoMessageCount = 100; |
| 38 const int kExpectedFlipBitsMessageCount = 2000; |
37 | 39 |
38 // Negative numbers with different values in each byte, the last of | 40 // Negative numbers with different values in each byte, the last of |
39 // which can survive promotion to double and back. | 41 // which can survive promotion to double and back. |
40 const int8 kExpectedInt8Value = -65; | 42 const int8 kExpectedInt8Value = -65; |
41 const int16 kExpectedInt16Value = -16961; | 43 const int16 kExpectedInt16Value = -16961; |
42 const int32 kExpectedInt32Value = -1145258561; | 44 const int32 kExpectedInt32Value = -1145258561; |
43 const int64 kExpectedInt64Value = -77263311946305LL; | 45 const int64 kExpectedInt64Value = -77263311946305LL; |
44 | 46 |
45 // Positive numbers with different values in each byte, the last of | 47 // Positive numbers with different values in each byte, the last of |
46 // which can survive promotion to double and back. | 48 // which can survive promotion to double and back. |
(...skipping 19 matching lines...) Expand all Loading... |
66 #if defined(OS_WIN) | 68 #if defined(OS_WIN) |
67 std::string tmp; | 69 std::string tmp; |
68 base::ReplaceChars(binding_path, "//", "\\", &tmp); | 70 base::ReplaceChars(binding_path, "//", "\\", &tmp); |
69 binding_path.swap(tmp); | 71 binding_path.swap(tmp); |
70 #endif | 72 #endif |
71 base::FilePath file_path; | 73 base::FilePath file_path; |
72 PathService::Get(CHILD_PROCESS_EXE, &file_path); | 74 PathService::Get(CHILD_PROCESS_EXE, &file_path); |
73 return file_path.DirName().AppendASCII(binding_path); | 75 return file_path.DirName().AppendASCII(binding_path); |
74 } | 76 } |
75 | 77 |
| 78 bool IsRunningOnIsolatedBot() { |
| 79 // Currently there is no way to have a generated file included in the isolate |
| 80 // files. If the bindings file doesn't exist assume we're on such a bot and |
| 81 // pass the current test. |
| 82 // TODO(sky): remove this conditional when isolates support copying from gen. |
| 83 const base::FilePath test_file_path( |
| 84 GetFilePathForJSResource( |
| 85 "content/test/data/web_ui_test_mojo_bindings.mojom")); |
| 86 if (!base::PathExists(test_file_path)) { |
| 87 LOG(WARNING) << " mojom binding file doesn't exist, assuming on isolate"; |
| 88 return true; |
| 89 } |
| 90 return false; |
| 91 } |
| 92 |
76 // The bindings for the page are generated from a .mojom file. This code looks | 93 // The bindings for the page are generated from a .mojom file. This code looks |
77 // up the generated file from disk and returns it. | 94 // up the generated file from disk and returns it. |
78 bool GetResource(const std::string& id, | 95 bool GetResource(const std::string& id, |
79 const WebUIDataSource::GotDataCallback& callback) { | 96 const WebUIDataSource::GotDataCallback& callback) { |
80 // These are handled by the WebUIDataSource that AddMojoDataSource() creates. | 97 // These are handled by the WebUIDataSource that AddMojoDataSource() creates. |
81 if (id == mojo::kCodecModuleName || | 98 if (id == mojo::kCodecModuleName || |
82 id == mojo::kConnectionModuleName || | 99 id == mojo::kConnectionModuleName || |
83 id == mojo::kConnectorModuleName || | 100 id == mojo::kConnectorModuleName || |
84 id == mojo::kRouterModuleName) | 101 id == mojo::kRouterModuleName) |
85 return false; | 102 return false; |
(...skipping 20 matching lines...) Expand all Loading... |
106 // mojo::BrowserTarget overrides: | 123 // mojo::BrowserTarget overrides: |
107 virtual void PingResponse() OVERRIDE { | 124 virtual void PingResponse() OVERRIDE { |
108 NOTREACHED(); | 125 NOTREACHED(); |
109 } | 126 } |
110 | 127 |
111 virtual void EchoResponse(const mojo::EchoArgs& arg1, | 128 virtual void EchoResponse(const mojo::EchoArgs& arg1, |
112 const mojo::EchoArgs& arg2) OVERRIDE { | 129 const mojo::EchoArgs& arg2) OVERRIDE { |
113 NOTREACHED(); | 130 NOTREACHED(); |
114 } | 131 } |
115 | 132 |
| 133 virtual void FlipBitsResponse(const mojo::EchoArgs& arg1) OVERRIDE { |
| 134 NOTREACHED(); |
| 135 } |
| 136 |
116 protected: | 137 protected: |
117 mojo::RemotePtr<mojo::RendererTarget> client_; | 138 mojo::RemotePtr<mojo::RendererTarget> client_; |
118 base::RunLoop* run_loop_; | 139 base::RunLoop* run_loop_; |
119 | 140 |
120 private: | 141 private: |
121 DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl); | 142 DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl); |
122 }; | 143 }; |
123 | 144 |
124 class PingBrowserTargetImpl : public BrowserTargetImpl { | 145 class PingBrowserTargetImpl : public BrowserTargetImpl { |
125 public: | 146 public: |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 builder.set_ui32(kExpectedUInt32Value); | 178 builder.set_ui32(kExpectedUInt32Value); |
158 builder.set_ui16(kExpectedUInt16Value); | 179 builder.set_ui16(kExpectedUInt16Value); |
159 builder.set_ui8(kExpectedUInt8Value); | 180 builder.set_ui8(kExpectedUInt8Value); |
160 builder.set_float_val(kExpectedFloatVal); | 181 builder.set_float_val(kExpectedFloatVal); |
161 builder.set_float_inf(kExpectedFloatInf); | 182 builder.set_float_inf(kExpectedFloatInf); |
162 builder.set_float_nan(kExpectedFloatNan); | 183 builder.set_float_nan(kExpectedFloatNan); |
163 builder.set_double_val(kExpectedDoubleVal); | 184 builder.set_double_val(kExpectedDoubleVal); |
164 builder.set_double_inf(kExpectedDoubleInf); | 185 builder.set_double_inf(kExpectedDoubleInf); |
165 builder.set_double_nan(kExpectedDoubleNan); | 186 builder.set_double_nan(kExpectedDoubleNan); |
166 builder.set_name("coming"); | 187 builder.set_name("coming"); |
167 client_->Echo(builder.Finish()); | 188 client_->Echo(kExpectedEchoMessageCount, builder.Finish()); |
168 } | 189 } |
169 | 190 |
170 virtual ~EchoBrowserTargetImpl() {} | 191 virtual ~EchoBrowserTargetImpl() {} |
171 | 192 |
172 // mojo::BrowserTarget overrides: | 193 // mojo::BrowserTarget overrides: |
173 // Check the response, and quit the RunLoop after N calls. | 194 // Check the response, and quit the RunLoop after N calls. |
174 virtual void EchoResponse(const mojo::EchoArgs& arg1, | 195 virtual void EchoResponse(const mojo::EchoArgs& arg1, |
175 const mojo::EchoArgs& arg2) OVERRIDE { | 196 const mojo::EchoArgs& arg2) OVERRIDE { |
176 EXPECT_EQ(kExpectedInt64Value, arg1.si64()); | 197 EXPECT_EQ(kExpectedInt64Value, arg1.si64()); |
177 EXPECT_EQ(kExpectedInt32Value, arg1.si32()); | 198 EXPECT_EQ(kExpectedInt32Value, arg1.si32()); |
(...skipping 10 matching lines...) Expand all Loading... |
188 EXPECT_EQ(kExpectedDoubleInf, arg1.double_inf()); | 209 EXPECT_EQ(kExpectedDoubleInf, arg1.double_inf()); |
189 EXPECT_NAN(arg1.double_nan()); | 210 EXPECT_NAN(arg1.double_nan()); |
190 EXPECT_EQ(std::string("coming"), arg1.name().To<std::string>()); | 211 EXPECT_EQ(std::string("coming"), arg1.name().To<std::string>()); |
191 | 212 |
192 EXPECT_EQ(-1, arg2.si64()); | 213 EXPECT_EQ(-1, arg2.si64()); |
193 EXPECT_EQ(-1, arg2.si32()); | 214 EXPECT_EQ(-1, arg2.si32()); |
194 EXPECT_EQ(-1, arg2.si16()); | 215 EXPECT_EQ(-1, arg2.si16()); |
195 EXPECT_EQ(-1, arg2.si8()); | 216 EXPECT_EQ(-1, arg2.si8()); |
196 EXPECT_EQ(std::string("going"), arg2.name().To<std::string>()); | 217 EXPECT_EQ(std::string("going"), arg2.name().To<std::string>()); |
197 | 218 |
198 message_count += 1; | 219 echo_message_count += 1; |
199 if (message_count == kExpectedMessageCount) | 220 if (echo_message_count == kExpectedEchoMessageCount) |
200 run_loop_->Quit(); | 221 run_loop_->Quit(); |
201 } | 222 } |
202 | 223 |
203 private: | 224 private: |
204 DISALLOW_COPY_AND_ASSIGN(EchoBrowserTargetImpl); | 225 DISALLOW_COPY_AND_ASSIGN(EchoBrowserTargetImpl); |
205 }; | 226 }; |
206 | 227 |
| 228 class FlipBitsBrowserTargetImpl : public BrowserTargetImpl { |
| 229 public: |
| 230 FlipBitsBrowserTargetImpl(mojo::ScopedRendererTargetHandle handle, |
| 231 base::RunLoop* run_loop) |
| 232 : BrowserTargetImpl(handle, run_loop) { |
| 233 mojo::AllocationScope scope; |
| 234 mojo::EchoArgs::Builder builder; |
| 235 builder.set_si64(kExpectedInt64Value); |
| 236 builder.set_si32(kExpectedInt32Value); |
| 237 builder.set_si16(kExpectedInt16Value); |
| 238 builder.set_si8(kExpectedInt8Value); |
| 239 builder.set_ui64(kExpectedUInt64Value); |
| 240 builder.set_ui32(kExpectedUInt32Value); |
| 241 builder.set_ui16(kExpectedUInt16Value); |
| 242 builder.set_ui8(kExpectedUInt8Value); |
| 243 builder.set_float_val(kExpectedFloatVal); |
| 244 builder.set_float_inf(kExpectedFloatInf); |
| 245 builder.set_float_nan(kExpectedFloatNan); |
| 246 builder.set_double_val(kExpectedDoubleVal); |
| 247 builder.set_double_inf(kExpectedDoubleInf); |
| 248 builder.set_double_nan(kExpectedDoubleNan); |
| 249 builder.set_name("flipping"); |
| 250 client_->FlipBits(kExpectedFlipBitsMessageCount, builder.Finish()); |
| 251 } |
| 252 |
| 253 virtual ~FlipBitsBrowserTargetImpl() {} |
| 254 |
| 255 // mojo::BrowserTarget overrides: |
| 256 // Everything is fine so long as the corrupt message dosen't trigger |
| 257 // ASAN errors, so just quit the RunLoop after N calls. |
| 258 virtual void FlipBitsResponse(const mojo::EchoArgs& arg1) OVERRIDE { |
| 259 flip_bits_message_count += 1; |
| 260 if (flip_bits_message_count == kExpectedFlipBitsMessageCount) |
| 261 run_loop_->Quit(); |
| 262 } |
| 263 |
| 264 private: |
| 265 DISALLOW_COPY_AND_ASSIGN(FlipBitsBrowserTargetImpl); |
| 266 }; |
| 267 |
207 // WebUIController that sets up mojo bindings. | 268 // WebUIController that sets up mojo bindings. |
208 class TestWebUIController : public WebUIController { | 269 class TestWebUIController : public WebUIController { |
209 public: | 270 public: |
210 TestWebUIController(WebUI* web_ui, base::RunLoop* run_loop) | 271 TestWebUIController(WebUI* web_ui, base::RunLoop* run_loop) |
211 : WebUIController(web_ui), | 272 : WebUIController(web_ui), |
212 run_loop_(run_loop) { | 273 run_loop_(run_loop) { |
213 content::WebUIDataSource* data_source = | 274 content::WebUIDataSource* data_source = |
214 WebUIDataSource::AddMojoDataSource( | 275 WebUIDataSource::AddMojoDataSource( |
215 web_ui->GetWebContents()->GetBrowserContext()); | 276 web_ui->GetWebContents()->GetBrowserContext()); |
216 data_source->SetRequestFilter(base::Bind(&GetResource)); | 277 data_source->SetRequestFilter(base::Bind(&GetResource)); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 browser_target_.reset(new EchoBrowserTargetImpl( | 320 browser_target_.reset(new EchoBrowserTargetImpl( |
260 pipe.handle_to_peer.Pass(), run_loop_)); | 321 pipe.handle_to_peer.Pass(), run_loop_)); |
261 render_view_host->SetWebUIHandle( | 322 render_view_host->SetWebUIHandle( |
262 mojo::ScopedMessagePipeHandle(pipe.handle_to_self.release())); | 323 mojo::ScopedMessagePipeHandle(pipe.handle_to_self.release())); |
263 } | 324 } |
264 | 325 |
265 private: | 326 private: |
266 DISALLOW_COPY_AND_ASSIGN(EchoTestWebUIController); | 327 DISALLOW_COPY_AND_ASSIGN(EchoTestWebUIController); |
267 }; | 328 }; |
268 | 329 |
| 330 // TestWebUIController that additionally creates the flip bits test |
| 331 // BrowserTarget implementation at the right time. |
| 332 class FlipBitsTestWebUIController : public TestWebUIController { |
| 333 public: |
| 334 FlipBitsTestWebUIController(WebUI* web_ui, base::RunLoop* run_loop) |
| 335 : TestWebUIController(web_ui, run_loop) { |
| 336 } |
| 337 |
| 338 // WebUIController overrides: |
| 339 virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE { |
| 340 mojo::InterfacePipe<mojo::BrowserTarget, mojo::RendererTarget> pipe; |
| 341 browser_target_.reset(new FlipBitsBrowserTargetImpl( |
| 342 pipe.handle_to_peer.Pass(), run_loop_)); |
| 343 render_view_host->SetWebUIHandle( |
| 344 mojo::ScopedMessagePipeHandle(pipe.handle_to_self.release())); |
| 345 } |
| 346 |
| 347 private: |
| 348 DISALLOW_COPY_AND_ASSIGN(FlipBitsTestWebUIController); |
| 349 }; |
| 350 |
269 // WebUIControllerFactory that creates TestWebUIController. | 351 // WebUIControllerFactory that creates TestWebUIController. |
270 class TestWebUIControllerFactory : public WebUIControllerFactory { | 352 class TestWebUIControllerFactory : public WebUIControllerFactory { |
271 public: | 353 public: |
272 TestWebUIControllerFactory() : run_loop_(NULL) {} | 354 TestWebUIControllerFactory() : run_loop_(NULL) {} |
273 | 355 |
274 void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; } | 356 void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; } |
275 | 357 |
276 virtual WebUIController* CreateWebUIControllerForURL( | 358 virtual WebUIController* CreateWebUIControllerForURL( |
277 WebUI* web_ui, const GURL& url) const OVERRIDE { | 359 WebUI* web_ui, const GURL& url) const OVERRIDE { |
278 if (url.query() == "ping") | 360 if (url.query() == "ping") |
279 return new PingTestWebUIController(web_ui, run_loop_); | 361 return new PingTestWebUIController(web_ui, run_loop_); |
280 if (url.query() == "echo") | 362 if (url.query() == "echo") |
281 return new EchoTestWebUIController(web_ui, run_loop_); | 363 return new EchoTestWebUIController(web_ui, run_loop_); |
| 364 if (url.query() == "flipbits") |
| 365 return new FlipBitsTestWebUIController(web_ui, run_loop_); |
282 return NULL; | 366 return NULL; |
283 } | 367 } |
284 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, | 368 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, |
285 const GURL& url) const OVERRIDE { | 369 const GURL& url) const OVERRIDE { |
286 return reinterpret_cast<WebUI::TypeID>(1); | 370 return reinterpret_cast<WebUI::TypeID>(1); |
287 } | 371 } |
288 virtual bool UseWebUIForURL(BrowserContext* browser_context, | 372 virtual bool UseWebUIForURL(BrowserContext* browser_context, |
289 const GURL& url) const OVERRIDE { | 373 const GURL& url) const OVERRIDE { |
290 return true; | 374 return true; |
291 } | 375 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 #define MAYBE_EndToEndPing DISABLED_EndToEndPing | 408 #define MAYBE_EndToEndPing DISABLED_EndToEndPing |
325 #define MAYBE_EndToEndEcho DISABLED_EndToEndEcho | 409 #define MAYBE_EndToEndEcho DISABLED_EndToEndEcho |
326 #else | 410 #else |
327 #define MAYBE_EndToEndPing EndToEndPing | 411 #define MAYBE_EndToEndPing EndToEndPing |
328 #define MAYBE_EndToEndEcho EndToEndEcho | 412 #define MAYBE_EndToEndEcho EndToEndEcho |
329 #endif | 413 #endif |
330 | 414 |
331 // Loads a webui page that contains mojo bindings and verifies a message makes | 415 // Loads a webui page that contains mojo bindings and verifies a message makes |
332 // it from the browser to the page and back. | 416 // it from the browser to the page and back. |
333 IN_PROC_BROWSER_TEST_F(WebUIMojoTest, MAYBE_EndToEndPing) { | 417 IN_PROC_BROWSER_TEST_F(WebUIMojoTest, MAYBE_EndToEndPing) { |
334 // Currently there is no way to have a generated file included in the isolate | 418 if (IsRunningOnIsolatedBot()) |
335 // files. If the bindings file doesn't exist assume we're on such a bot and | |
336 // pass. | |
337 // TODO(sky): remove this conditional when isolates support copying from gen. | |
338 const base::FilePath test_file_path( | |
339 GetFilePathForJSResource( | |
340 "content/test/data/web_ui_test_mojo_bindings.mojom")); | |
341 if (!base::PathExists(test_file_path)) { | |
342 LOG(WARNING) << " mojom binding file doesn't exist, assuming on isolate"; | |
343 return; | 419 return; |
344 } | |
345 | 420 |
346 got_message = false; | 421 got_message = false; |
347 ASSERT_TRUE(test_server()->Start()); | 422 ASSERT_TRUE(test_server()->Start()); |
348 base::RunLoop run_loop; | 423 base::RunLoop run_loop; |
349 factory()->set_run_loop(&run_loop); | 424 factory()->set_run_loop(&run_loop); |
350 GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?ping")); | 425 GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?ping")); |
351 NavigateToURL(shell(), test_url); | 426 NavigateToURL(shell(), test_url); |
352 // RunLoop is quit when message received from page. | 427 // RunLoop is quit when message received from page. |
353 run_loop.Run(); | 428 run_loop.Run(); |
354 EXPECT_TRUE(got_message); | 429 EXPECT_TRUE(got_message); |
355 } | 430 } |
356 | 431 |
357 // Loads a webui page that contains mojo bindings and verifies that | 432 // Loads a webui page that contains mojo bindings and verifies that |
358 // parameters are passed back correctly from JavaScript. | 433 // parameters are passed back correctly from JavaScript. |
359 IN_PROC_BROWSER_TEST_F(WebUIMojoTest, MAYBE_EndToEndEcho) { | 434 IN_PROC_BROWSER_TEST_F(WebUIMojoTest, MAYBE_EndToEndEcho) { |
360 // Currently there is no way to have a generated file included in the isolate | 435 if (IsRunningOnIsolatedBot()) |
361 // files. If the bindings file doesn't exist assume we're on such a bot and | |
362 // pass. | |
363 // TODO(sky): remove this conditional when isolates support copying from gen. | |
364 const base::FilePath test_file_path( | |
365 GetFilePathForJSResource( | |
366 "content/test/data/web_ui_test_mojo_bindings.mojom")); | |
367 if (!base::PathExists(test_file_path)) { | |
368 LOG(WARNING) << " mojom binding file doesn't exist, assuming on isolate"; | |
369 return; | 436 return; |
370 } | |
371 | 437 |
372 message_count = 0; | 438 echo_message_count = 0; |
373 ASSERT_TRUE(test_server()->Start()); | 439 ASSERT_TRUE(test_server()->Start()); |
374 base::RunLoop run_loop; | 440 base::RunLoop run_loop; |
375 factory()->set_run_loop(&run_loop); | 441 factory()->set_run_loop(&run_loop); |
376 GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?echo")); | 442 GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?echo")); |
377 NavigateToURL(shell(), test_url); | 443 NavigateToURL(shell(), test_url); |
378 // RunLoop is quit when response received from page. | 444 // RunLoop is quit when response received from page. |
379 run_loop.Run(); | 445 run_loop.Run(); |
380 EXPECT_EQ(kExpectedMessageCount, message_count); | 446 EXPECT_EQ(kExpectedEchoMessageCount, echo_message_count); |
| 447 } |
| 448 |
| 449 // Loads a webui page that contains mojo bindings and verifies that |
| 450 // corrupted parameters passed back from JavaScript don't trigger |
| 451 // ASAN errors. |
| 452 // TODO(tsepez): disabled due to http://crbug.com/366797. |
| 453 IN_PROC_BROWSER_TEST_F(WebUIMojoTest, DISABLED_EndToEndFlipBits) { |
| 454 if (IsRunningOnIsolatedBot()) |
| 455 return; |
| 456 |
| 457 flip_bits_message_count = 0; |
| 458 ASSERT_TRUE(test_server()->Start()); |
| 459 base::RunLoop run_loop; |
| 460 factory()->set_run_loop(&run_loop); |
| 461 GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?flipbits")); |
| 462 NavigateToURL(shell(), test_url); |
| 463 // RunLoop is quit when response received from page. |
| 464 run_loop.Run(); |
| 465 EXPECT_EQ(kExpectedFlipBitsMessageCount, flip_bits_message_count); |
381 } | 466 } |
382 | 467 |
383 } // namespace | 468 } // namespace |
384 } // namespace content | 469 } // namespace content |
OLD | NEW |