1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/strings/string16.h"
6#include "base/strings/utf_string_conversions.h"
7#include "content/browser/accessibility/browser_accessibility.h"
8#include "content/browser/accessibility/browser_accessibility_manager.h"
9#include "content/common/accessibility_messages.h"
10#include "content/common/accessibility_node_data.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13namespace content {
14namespace {
15
16// Subclass of BrowserAccessibility that counts the number of instances.
17class CountedBrowserAccessibility : public BrowserAccessibility {
18 public:
19  CountedBrowserAccessibility() {
20    global_obj_count_++;
21    native_ref_count_ = 1;
22  }
23  virtual ~CountedBrowserAccessibility() {
24    global_obj_count_--;
25  }
26
27  virtual void NativeAddReference() OVERRIDE {
28    native_ref_count_++;
29  }
30
31  virtual void NativeReleaseReference() OVERRIDE {
32    native_ref_count_--;
33    if (native_ref_count_ == 0)
34      delete this;
35  }
36
37  int native_ref_count_;
38  static int global_obj_count_;
39};
40
41int CountedBrowserAccessibility::global_obj_count_ = 0;
42
43// Factory that creates a CountedBrowserAccessibility.
44class CountedBrowserAccessibilityFactory
45    : public BrowserAccessibilityFactory {
46 public:
47  virtual ~CountedBrowserAccessibilityFactory() {}
48  virtual BrowserAccessibility* Create() OVERRIDE {
49    return new CountedBrowserAccessibility();
50  }
51};
52
53class TestBrowserAccessibilityDelegate
54    : public BrowserAccessibilityDelegate {
55 public:
56  TestBrowserAccessibilityDelegate()
57      : got_fatal_error_(false) {}
58
59  virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE {}
60  virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
61  virtual void AccessibilityScrollToMakeVisible(
62      int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
63  virtual void AccessibilityScrollToPoint(
64      int acc_obj_id, gfx::Point point) OVERRIDE {}
65  virtual void AccessibilitySetTextSelection(
66      int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
67  virtual bool HasFocus() const OVERRIDE {
68    return false;
69  }
70  virtual gfx::Rect GetViewBounds() const OVERRIDE {
71    return gfx::Rect();
72  }
73  virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE {
74    return gfx::Point();
75  }
76  virtual void FatalAccessibilityTreeError() OVERRIDE {
77    got_fatal_error_ = true;
78  }
79
80  bool got_fatal_error() const { return got_fatal_error_; }
81  void reset_got_fatal_error() { got_fatal_error_ = false; }
82
83private:
84  bool got_fatal_error_;
85};
86
87}  // anonymous namespace
88
89TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
90  // Create AccessibilityNodeData objects for a simple document tree,
91  // representing the accessibility information used to initialize
92  // BrowserAccessibilityManager.
93  AccessibilityNodeData button;
94  button.id = 2;
95  button.name = UTF8ToUTF16("Button");
96  button.role = AccessibilityNodeData::ROLE_BUTTON;
97  button.state = 0;
98
99  AccessibilityNodeData checkbox;
100  checkbox.id = 3;
101  checkbox.name = UTF8ToUTF16("Checkbox");
102  checkbox.role = AccessibilityNodeData::ROLE_CHECKBOX;
103  checkbox.state = 0;
104
105  AccessibilityNodeData root;
106  root.id = 1;
107  root.name = UTF8ToUTF16("Document");
108  root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
109  root.state = 0;
110  root.child_ids.push_back(2);
111  root.child_ids.push_back(3);
112
113  // Construct a BrowserAccessibilityManager with this
114  // AccessibilityNodeData tree and a factory for an instance-counting
115  // BrowserAccessibility, and ensure that exactly 3 instances were
116  // created. Note that the manager takes ownership of the factory.
117  CountedBrowserAccessibility::global_obj_count_ = 0;
118  BrowserAccessibilityManager* manager =
119      BrowserAccessibilityManager::Create(
120          root,
121          NULL,
122          new CountedBrowserAccessibilityFactory());
123  manager->UpdateNodesForTesting(button, checkbox);
124
125  ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
126
127  // Delete the manager and test that all 3 instances are deleted.
128  delete manager;
129  ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
130
131  // Construct a manager again, and this time save references to two of
132  // the three nodes in the tree.
133  manager =
134      BrowserAccessibilityManager::Create(
135          root,
136          NULL,
137          new CountedBrowserAccessibilityFactory());
138  manager->UpdateNodesForTesting(button, checkbox);
139  ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
140
141  CountedBrowserAccessibility* root_accessible =
142      static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
143  root_accessible->NativeAddReference();
144  CountedBrowserAccessibility* child1_accessible =
145      static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(1));
146  child1_accessible->NativeAddReference();
147
148  // Now delete the manager, and only one of the three nodes in the tree
149  // should be released.
150  delete manager;
151  ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
152
153  // Release each of our references and make sure that each one results in
154  // the instance being deleted as its reference count hits zero.
155  root_accessible->NativeReleaseReference();
156  ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
157  child1_accessible->NativeReleaseReference();
158  ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
159}
160
161TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
162  // Make sure that changes to a subtree reuse as many objects as possible.
163
164  // Tree 1:
165  //
166  // root
167  //   child1
168  //   child2
169  //   child3
170
171  AccessibilityNodeData tree1_child1;
172  tree1_child1.id = 2;
173  tree1_child1.name = UTF8ToUTF16("Child1");
174  tree1_child1.role = AccessibilityNodeData::ROLE_BUTTON;
175  tree1_child1.state = 0;
176
177  AccessibilityNodeData tree1_child2;
178  tree1_child2.id = 3;
179  tree1_child2.name = UTF8ToUTF16("Child2");
180  tree1_child2.role = AccessibilityNodeData::ROLE_BUTTON;
181  tree1_child2.state = 0;
182
183  AccessibilityNodeData tree1_child3;
184  tree1_child3.id = 4;
185  tree1_child3.name = UTF8ToUTF16("Child3");
186  tree1_child3.role = AccessibilityNodeData::ROLE_BUTTON;
187  tree1_child3.state = 0;
188
189  AccessibilityNodeData tree1_root;
190  tree1_root.id = 1;
191  tree1_root.name = UTF8ToUTF16("Document");
192  tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
193  tree1_root.state = 0;
194  tree1_root.child_ids.push_back(2);
195  tree1_root.child_ids.push_back(3);
196  tree1_root.child_ids.push_back(4);
197
198  // Tree 2:
199  //
200  // root
201  //   child0  <-- inserted
202  //   child1
203  //   child2
204  //           <-- child3 deleted
205
206  AccessibilityNodeData tree2_child0;
207  tree2_child0.id = 5;
208  tree2_child0.name = UTF8ToUTF16("Child0");
209  tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON;
210  tree2_child0.state = 0;
211
212  AccessibilityNodeData tree2_root;
213  tree2_root.id = 1;
214  tree2_root.name = UTF8ToUTF16("DocumentChanged");
215  tree2_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
216  tree2_root.state = 0;
217  tree2_root.child_ids.push_back(5);
218  tree2_root.child_ids.push_back(2);
219  tree2_root.child_ids.push_back(3);
220
221  // Construct a BrowserAccessibilityManager with tree1.
222  CountedBrowserAccessibility::global_obj_count_ = 0;
223  BrowserAccessibilityManager* manager =
224      BrowserAccessibilityManager::Create(
225          tree1_root,
226          NULL,
227          new CountedBrowserAccessibilityFactory());
228  manager->UpdateNodesForTesting(tree1_child1, tree1_child2, tree1_child3);
229  ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
230
231  // Save references to all of the objects.
232  CountedBrowserAccessibility* root_accessible =
233      static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
234  root_accessible->NativeAddReference();
235  CountedBrowserAccessibility* child1_accessible =
236      static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(0));
237  child1_accessible->NativeAddReference();
238  CountedBrowserAccessibility* child2_accessible =
239      static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(1));
240  child2_accessible->NativeAddReference();
241  CountedBrowserAccessibility* child3_accessible =
242      static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(2));
243  child3_accessible->NativeAddReference();
244
245  // Check the index in parent.
246  EXPECT_EQ(0, child1_accessible->index_in_parent());
247  EXPECT_EQ(1, child2_accessible->index_in_parent());
248  EXPECT_EQ(2, child3_accessible->index_in_parent());
249
250  // Process a notification containing the changed subtree.
251  std::vector<AccessibilityHostMsg_NotificationParams> params;
252  params.push_back(AccessibilityHostMsg_NotificationParams());
253  AccessibilityHostMsg_NotificationParams* msg = &params[0];
254  msg->notification_type = AccessibilityNotificationChildrenChanged;
255  msg->nodes.push_back(tree2_root);
256  msg->nodes.push_back(tree2_child0);
257  msg->id = tree2_root.id;
258  manager->OnAccessibilityNotifications(params);
259
260  // There should be 5 objects now: the 4 from the new tree, plus the
261  // reference to child3 we kept.
262  EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
263
264  // Check that our references to the root, child1, and child2 are still valid,
265  // but that the reference to child3 is now invalid.
266  EXPECT_TRUE(root_accessible->instance_active());
267  EXPECT_TRUE(child1_accessible->instance_active());
268  EXPECT_TRUE(child2_accessible->instance_active());
269  EXPECT_FALSE(child3_accessible->instance_active());
270
271  // Check that the index in parent has been updated.
272  EXPECT_EQ(1, child1_accessible->index_in_parent());
273  EXPECT_EQ(2, child2_accessible->index_in_parent());
274
275  // Release our references. The object count should only decrease by 1
276  // for child3.
277  root_accessible->NativeReleaseReference();
278  child1_accessible->NativeReleaseReference();
279  child2_accessible->NativeReleaseReference();
280  child3_accessible->NativeReleaseReference();
281
282  EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
283
284  // Delete the manager and make sure all memory is cleaned up.
285  delete manager;
286  ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
287}
288
289TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
290  // Similar to the test above, but with a more complicated tree.
291
292  // Tree 1:
293  //
294  // root
295  //   container
296  //     child1
297  //       grandchild1
298  //     child2
299  //       grandchild2
300  //     child3
301  //       grandchild3
302
303  AccessibilityNodeData tree1_grandchild1;
304  tree1_grandchild1.id = 4;
305  tree1_grandchild1.name = UTF8ToUTF16("GrandChild1");
306  tree1_grandchild1.role = AccessibilityNodeData::ROLE_BUTTON;
307  tree1_grandchild1.state = 0;
308
309  AccessibilityNodeData tree1_child1;
310  tree1_child1.id = 3;
311  tree1_child1.name = UTF8ToUTF16("Child1");
312  tree1_child1.role = AccessibilityNodeData::ROLE_BUTTON;
313  tree1_child1.state = 0;
314  tree1_child1.child_ids.push_back(4);
315
316  AccessibilityNodeData tree1_grandchild2;
317  tree1_grandchild2.id = 6;
318  tree1_grandchild2.name = UTF8ToUTF16("GrandChild1");
319  tree1_grandchild2.role = AccessibilityNodeData::ROLE_BUTTON;
320  tree1_grandchild2.state = 0;
321
322  AccessibilityNodeData tree1_child2;
323  tree1_child2.id = 5;
324  tree1_child2.name = UTF8ToUTF16("Child2");
325  tree1_child2.role = AccessibilityNodeData::ROLE_BUTTON;
326  tree1_child2.state = 0;
327  tree1_child2.child_ids.push_back(6);
328
329  AccessibilityNodeData tree1_grandchild3;
330  tree1_grandchild3.id = 8;
331  tree1_grandchild3.name = UTF8ToUTF16("GrandChild3");
332  tree1_grandchild3.role = AccessibilityNodeData::ROLE_BUTTON;
333  tree1_grandchild3.state = 0;
334
335  AccessibilityNodeData tree1_child3;
336  tree1_child3.id = 7;
337  tree1_child3.name = UTF8ToUTF16("Child3");
338  tree1_child3.role = AccessibilityNodeData::ROLE_BUTTON;
339  tree1_child3.state = 0;
340  tree1_child3.child_ids.push_back(8);
341
342  AccessibilityNodeData tree1_container;
343  tree1_container.id = 2;
344  tree1_container.name = UTF8ToUTF16("Container");
345  tree1_container.role = AccessibilityNodeData::ROLE_GROUP;
346  tree1_container.state = 0;
347  tree1_container.child_ids.push_back(3);
348  tree1_container.child_ids.push_back(5);
349  tree1_container.child_ids.push_back(7);
350
351  AccessibilityNodeData tree1_root;
352  tree1_root.id = 1;
353  tree1_root.name = UTF8ToUTF16("Document");
354  tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
355  tree1_root.state = 0;
356  tree1_root.child_ids.push_back(2);
357
358  // Tree 2:
359  //
360  // root
361  //   container
362  //     child0         <-- inserted
363  //       grandchild0  <--
364  //     child1
365  //       grandchild1
366  //     child2
367  //       grandchild2
368  //                    <-- child3 (and grandchild3) deleted
369
370  AccessibilityNodeData tree2_grandchild0;
371  tree2_grandchild0.id = 9;
372  tree2_grandchild0.name = UTF8ToUTF16("GrandChild0");
373  tree2_grandchild0.role = AccessibilityNodeData::ROLE_BUTTON;
374  tree2_grandchild0.state = 0;
375
376  AccessibilityNodeData tree2_child0;
377  tree2_child0.id = 10;
378  tree2_child0.name = UTF8ToUTF16("Child0");
379  tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON;
380  tree2_child0.state = 0;
381  tree2_child0.child_ids.push_back(9);
382
383  AccessibilityNodeData tree2_container;
384  tree2_container.id = 2;
385  tree2_container.name = UTF8ToUTF16("Container");
386  tree2_container.role = AccessibilityNodeData::ROLE_GROUP;
387  tree2_container.state = 0;
388  tree2_container.child_ids.push_back(10);
389  tree2_container.child_ids.push_back(3);
390  tree2_container.child_ids.push_back(5);
391
392  // Construct a BrowserAccessibilityManager with tree1.
393  CountedBrowserAccessibility::global_obj_count_ = 0;
394  BrowserAccessibilityManager* manager =
395      BrowserAccessibilityManager::Create(
396          tree1_root,
397          NULL,
398          new CountedBrowserAccessibilityFactory());
399  manager->UpdateNodesForTesting(tree1_container,
400                                 tree1_child1, tree1_grandchild1,
401                                 tree1_child2, tree1_grandchild2,
402                                 tree1_child3, tree1_grandchild3);
403  ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
404
405  // Save references to some objects.
406  CountedBrowserAccessibility* root_accessible =
407      static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
408  root_accessible->NativeAddReference();
409  CountedBrowserAccessibility* container_accessible =
410      static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(0));
411  container_accessible->NativeAddReference();
412  CountedBrowserAccessibility* child2_accessible =
413      static_cast<CountedBrowserAccessibility*>(
414          container_accessible->GetChild(1));
415  child2_accessible->NativeAddReference();
416  CountedBrowserAccessibility* child3_accessible =
417      static_cast<CountedBrowserAccessibility*>(
418          container_accessible->GetChild(2));
419  child3_accessible->NativeAddReference();
420
421  // Check the index in parent.
422  EXPECT_EQ(1, child2_accessible->index_in_parent());
423  EXPECT_EQ(2, child3_accessible->index_in_parent());
424
425  // Process a notification containing the changed subtree rooted at
426  // the container.
427  std::vector<AccessibilityHostMsg_NotificationParams> params;
428  params.push_back(AccessibilityHostMsg_NotificationParams());
429  AccessibilityHostMsg_NotificationParams* msg = &params[0];
430  msg->notification_type = AccessibilityNotificationChildrenChanged;
431  msg->nodes.push_back(tree2_container);
432  msg->nodes.push_back(tree2_child0);
433  msg->nodes.push_back(tree2_grandchild0);
434  msg->id = tree2_container.id;
435  manager->OnAccessibilityNotifications(params);
436
437  // There should be 9 objects now: the 8 from the new tree, plus the
438  // reference to child3 we kept.
439  EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
440
441  // Check that our references to the root and container and child2 are
442  // still valid, but that the reference to child3 is now invalid.
443  EXPECT_TRUE(root_accessible->instance_active());
444  EXPECT_TRUE(container_accessible->instance_active());
445  EXPECT_TRUE(child2_accessible->instance_active());
446  EXPECT_FALSE(child3_accessible->instance_active());
447
448  // Ensure that we retain the parent of the detached subtree.
449  EXPECT_EQ(root_accessible, container_accessible->parent());
450  EXPECT_EQ(0, container_accessible->index_in_parent());
451
452  // Check that the index in parent has been updated.
453  EXPECT_EQ(2, child2_accessible->index_in_parent());
454
455  // Release our references. The object count should only decrease by 1
456  // for child3.
457  root_accessible->NativeReleaseReference();
458  container_accessible->NativeReleaseReference();
459  child2_accessible->NativeReleaseReference();
460  child3_accessible->NativeReleaseReference();
461
462  EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
463
464  // Delete the manager and make sure all memory is cleaned up.
465  delete manager;
466  ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
467}
468
469TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
470  // Tree 1:
471  //
472  // 1
473  //   2
474  //   3
475  //     4
476
477  AccessibilityNodeData tree1_4;
478  tree1_4.id = 4;
479  tree1_4.state = 0;
480
481  AccessibilityNodeData tree1_3;
482  tree1_3.id = 3;
483  tree1_3.state = 0;
484  tree1_3.child_ids.push_back(4);
485
486  AccessibilityNodeData tree1_2;
487  tree1_2.id = 2;
488  tree1_2.state = 0;
489
490  AccessibilityNodeData tree1_1;
491  tree1_1.id = 1;
492  tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
493  tree1_1.state = 0;
494  tree1_1.child_ids.push_back(2);
495  tree1_1.child_ids.push_back(3);
496
497  // Tree 2:
498  //
499  // 1
500  //   4    <-- moves up a level and gains child
501  //     6  <-- new
502  //   5    <-- new
503
504  AccessibilityNodeData tree2_6;
505  tree2_6.id = 6;
506  tree2_6.state = 0;
507
508  AccessibilityNodeData tree2_5;
509  tree2_5.id = 5;
510  tree2_5.state = 0;
511
512  AccessibilityNodeData tree2_4;
513  tree2_4.id = 4;
514  tree2_4.state = 0;
515  tree2_4.child_ids.push_back(6);
516
517  AccessibilityNodeData tree2_1;
518  tree2_1.id = 1;
519  tree2_1.state = 0;
520  tree2_1.child_ids.push_back(4);
521  tree2_1.child_ids.push_back(5);
522
523  // Construct a BrowserAccessibilityManager with tree1.
524  CountedBrowserAccessibility::global_obj_count_ = 0;
525  BrowserAccessibilityManager* manager =
526      BrowserAccessibilityManager::Create(
527          tree1_1,
528          NULL,
529          new CountedBrowserAccessibilityFactory());
530  manager->UpdateNodesForTesting(tree1_2, tree1_3, tree1_4);
531  ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
532
533  // Process a notification containing the changed subtree.
534  std::vector<AccessibilityHostMsg_NotificationParams> params;
535  params.push_back(AccessibilityHostMsg_NotificationParams());
536  AccessibilityHostMsg_NotificationParams* msg = &params[0];
537  msg->notification_type = AccessibilityNotificationChildrenChanged;
538  msg->nodes.push_back(tree2_1);
539  msg->nodes.push_back(tree2_4);
540  msg->nodes.push_back(tree2_5);
541  msg->nodes.push_back(tree2_6);
542  msg->id = tree2_1.id;
543  manager->OnAccessibilityNotifications(params);
544
545  // There should be 4 objects now.
546  EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
547
548  // Delete the manager and make sure all memory is cleaned up.
549  delete manager;
550  ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
551}
552
553TEST(BrowserAccessibilityManagerTest, TestFatalError) {
554  // Test that BrowserAccessibilityManager raises a fatal error
555  // (which will crash the renderer) if the same id is used in
556  // two places in the tree.
557
558  AccessibilityNodeData root;
559  root.id = 1;
560  root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
561  root.child_ids.push_back(2);
562  root.child_ids.push_back(2);
563
564  CountedBrowserAccessibilityFactory* factory =
565      new CountedBrowserAccessibilityFactory();
566  scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
567      new TestBrowserAccessibilityDelegate());
568  scoped_ptr<BrowserAccessibilityManager> manager;
569  ASSERT_FALSE(delegate->got_fatal_error());
570  manager.reset(BrowserAccessibilityManager::Create(
571      root,
572      delegate.get(),
573      factory));
574  ASSERT_TRUE(delegate->got_fatal_error());
575
576  AccessibilityNodeData root2;
577  root2.id = 1;
578  root2.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
579  root2.child_ids.push_back(2);
580  root2.child_ids.push_back(3);
581
582  AccessibilityNodeData child1;
583  child1.id = 2;
584  child1.child_ids.push_back(4);
585  child1.child_ids.push_back(5);
586
587  AccessibilityNodeData child2;
588  child2.id = 3;
589  child2.child_ids.push_back(6);
590  child2.child_ids.push_back(5);  // Duplicate
591
592  delegate->reset_got_fatal_error();
593  factory = new CountedBrowserAccessibilityFactory();
594  manager.reset(BrowserAccessibilityManager::Create(
595      root2,
596      delegate.get(),
597      factory));
598  ASSERT_FALSE(delegate->got_fatal_error());
599  manager->UpdateNodesForTesting(child1, child2);
600  ASSERT_TRUE(delegate->got_fatal_error());
601}
602
603}  // namespace content
604