| 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 "ui/views/controls/menu/menu_controller.h" | 5 #include "ui/views/controls/menu/menu_controller.h" |
| 6 | 6 |
| 7 #include "base/callback.h" | 7 #include "base/callback.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "base/single_thread_task_runner.h" | 10 #include "base/single_thread_task_runner.h" |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 public: | 271 public: |
| 272 TestMenuItemViewShown(MenuDelegate* delegate) : MenuItemView(delegate) { | 272 TestMenuItemViewShown(MenuDelegate* delegate) : MenuItemView(delegate) { |
| 273 submenu_ = new SubmenuViewShown(this); | 273 submenu_ = new SubmenuViewShown(this); |
| 274 } | 274 } |
| 275 ~TestMenuItemViewShown() override {} | 275 ~TestMenuItemViewShown() override {} |
| 276 | 276 |
| 277 void SetController(MenuController* controller) { | 277 void SetController(MenuController* controller) { |
| 278 set_controller(controller); | 278 set_controller(controller); |
| 279 } | 279 } |
| 280 | 280 |
| 281 void AddEmptyMenusForTest() { AddEmptyMenus(); } |
| 282 |
| 281 private: | 283 private: |
| 282 DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown); | 284 DISALLOW_COPY_AND_ASSIGN(TestMenuItemViewShown); |
| 283 }; | 285 }; |
| 284 | 286 |
| 285 class MenuControllerTest : public ViewsTestBase { | 287 class MenuControllerTest : public ViewsTestBase { |
| 286 public: | 288 public: |
| 287 MenuControllerTest() : menu_controller_(nullptr) { | 289 MenuControllerTest() : menu_controller_(nullptr) { |
| 288 } | 290 } |
| 289 | 291 |
| 290 ~MenuControllerTest() override {} | 292 ~MenuControllerTest() override {} |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 329 // Now that the targeter has been destroyed, expect to exit the menu | 331 // Now that the targeter has been destroyed, expect to exit the menu |
| 330 // normally when hitting escape. | 332 // normally when hitting escape. |
| 331 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); | 333 event_generator_->PressKey(ui::VKEY_ESCAPE, 0); |
| 332 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type()); | 334 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type()); |
| 333 } | 335 } |
| 334 | 336 |
| 335 // Verifies that a non-nested menu fully closes when receiving an escape key. | 337 // Verifies that a non-nested menu fully closes when receiving an escape key. |
| 336 void TestAsyncEscapeKey() { | 338 void TestAsyncEscapeKey() { |
| 337 ui::KeyEvent event(ui::EventType::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0); | 339 ui::KeyEvent event(ui::EventType::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0); |
| 338 menu_controller_->OnWillDispatchKeyEvent(&event); | 340 menu_controller_->OnWillDispatchKeyEvent(&event); |
| 339 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type()); | |
| 340 } | 341 } |
| 341 | 342 |
| 342 #endif // defined(OS_LINUX) && defined(USE_X11) | 343 #endif // defined(OS_LINUX) && defined(USE_X11) |
| 343 | 344 |
| 344 #if defined(USE_AURA) | 345 #if defined(USE_AURA) |
| 345 // Verifies that an open menu receives a cancel event, and closes. | 346 // Verifies that an open menu receives a cancel event, and closes. |
| 346 void TestCancelEvent() { | 347 void TestCancelEvent() { |
| 347 EXPECT_EQ(MenuController::EXIT_NONE, menu_controller_->exit_type()); | 348 EXPECT_EQ(MenuController::EXIT_NONE, menu_controller_->exit_type()); |
| 348 ui::CancelModeEvent cancel_event; | 349 ui::CancelModeEvent cancel_event; |
| 349 event_generator_->Dispatch(&cancel_event); | 350 event_generator_->Dispatch(&cancel_event); |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 452 &MenuControllerTest::DestroyMenuController, base::Unretained(this))); | 453 &MenuControllerTest::DestroyMenuController, base::Unretained(this))); |
| 453 menu_controller_->ExitAsyncRun(); | 454 menu_controller_->ExitAsyncRun(); |
| 454 } | 455 } |
| 455 | 456 |
| 456 protected: | 457 protected: |
| 457 void SetPendingStateItem(MenuItemView* item) { | 458 void SetPendingStateItem(MenuItemView* item) { |
| 458 menu_controller_->pending_state_.item = item; | 459 menu_controller_->pending_state_.item = item; |
| 459 menu_controller_->pending_state_.submenu_open = true; | 460 menu_controller_->pending_state_.submenu_open = true; |
| 460 } | 461 } |
| 461 | 462 |
| 463 void SetState(MenuItemView* item) { |
| 464 menu_controller_->state_.item = item; |
| 465 menu_controller_->state_.submenu_open = true; |
| 466 } |
| 467 |
| 462 void ResetSelection() { | 468 void ResetSelection() { |
| 463 menu_controller_->SetSelection( | 469 menu_controller_->SetSelection( |
| 464 nullptr, | 470 nullptr, |
| 465 MenuController::SELECTION_EXIT | | 471 MenuController::SELECTION_EXIT | |
| 466 MenuController::SELECTION_UPDATE_IMMEDIATELY); | 472 MenuController::SELECTION_UPDATE_IMMEDIATELY); |
| 467 } | 473 } |
| 468 | 474 |
| 469 void IncrementSelection() { | 475 void IncrementSelection() { |
| 470 menu_controller_->IncrementSelection( | 476 menu_controller_->IncrementSelection( |
| 471 MenuController::INCREMENT_SELECTION_DOWN); | 477 MenuController::INCREMENT_SELECTION_DOWN); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 537 menu_controller_->set_is_combobox(is_combobox); | 543 menu_controller_->set_is_combobox(is_combobox); |
| 538 } | 544 } |
| 539 | 545 |
| 540 void SetSelectionOnPointerDown(SubmenuView* source, | 546 void SetSelectionOnPointerDown(SubmenuView* source, |
| 541 const ui::LocatedEvent* event) { | 547 const ui::LocatedEvent* event) { |
| 542 menu_controller_->SetSelectionOnPointerDown(source, event); | 548 menu_controller_->SetSelectionOnPointerDown(source, event); |
| 543 } | 549 } |
| 544 | 550 |
| 545 // Note that coordinates of events passed to MenuController must be in that of | 551 // Note that coordinates of events passed to MenuController must be in that of |
| 546 // the MenuScrollViewContainer. | 552 // the MenuScrollViewContainer. |
| 553 void ProcessMousePressed(SubmenuView* source, const ui::MouseEvent& event) { |
| 554 menu_controller_->OnMousePressed(source, event); |
| 555 } |
| 556 |
| 557 void ProcessMouseDragged(SubmenuView* source, const ui::MouseEvent& event) { |
| 558 menu_controller_->OnMouseDragged(source, event); |
| 559 } |
| 560 |
| 547 void ProcessMouseMoved(SubmenuView* source, const ui::MouseEvent& event) { | 561 void ProcessMouseMoved(SubmenuView* source, const ui::MouseEvent& event) { |
| 548 menu_controller_->OnMouseMoved(source, event); | 562 menu_controller_->OnMouseMoved(source, event); |
| 549 } | 563 } |
| 550 | 564 |
| 565 void ProcessMouseReleased(SubmenuView* source, const ui::MouseEvent& event) { |
| 566 menu_controller_->OnMouseReleased(source, event); |
| 567 } |
| 568 |
| 551 void RunMenu() { | 569 void RunMenu() { |
| 552 #if defined(USE_AURA) | 570 #if defined(USE_AURA) |
| 553 std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler( | 571 std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler( |
| 554 new MenuPreTargetHandler(menu_controller_, owner_.get())); | 572 new MenuPreTargetHandler(menu_controller_, owner_.get())); |
| 555 #endif | 573 #endif |
| 556 | 574 |
| 557 menu_controller_->message_loop_depth_++; | 575 menu_controller_->message_loop_depth_++; |
| 558 menu_controller_->RunMessageLoop(); | 576 menu_controller_->RunMessageLoop(); |
| 559 menu_controller_->message_loop_depth_--; | 577 menu_controller_->message_loop_depth_--; |
| 560 } | 578 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 574 void StartDrag() { | 592 void StartDrag() { |
| 575 const gfx::Point location; | 593 const gfx::Point location; |
| 576 menu_controller_->state_.item = menu_item()->GetSubmenu()->GetMenuItemAt(0); | 594 menu_controller_->state_.item = menu_item()->GetSubmenu()->GetMenuItemAt(0); |
| 577 menu_controller_->StartDrag( | 595 menu_controller_->StartDrag( |
| 578 menu_item()->GetSubmenu()->GetMenuItemAt(0)->CreateSubmenu(), location); | 596 menu_item()->GetSubmenu()->GetMenuItemAt(0)->CreateSubmenu(), location); |
| 579 } | 597 } |
| 580 | 598 |
| 581 Widget* owner() { return owner_.get(); } | 599 Widget* owner() { return owner_.get(); } |
| 582 ui::test::EventGenerator* event_generator() { return event_generator_.get(); } | 600 ui::test::EventGenerator* event_generator() { return event_generator_.get(); } |
| 583 TestMenuItemViewShown* menu_item() { return menu_item_.get(); } | 601 TestMenuItemViewShown* menu_item() { return menu_item_.get(); } |
| 602 TestMenuDelegate* menu_delegate() { return menu_delegate_.get(); } |
| 584 TestMenuControllerDelegate* menu_controller_delegate() { | 603 TestMenuControllerDelegate* menu_controller_delegate() { |
| 585 return menu_controller_delegate_.get(); | 604 return menu_controller_delegate_.get(); |
| 586 } | 605 } |
| 587 MenuController* menu_controller() { return menu_controller_; } | 606 MenuController* menu_controller() { return menu_controller_; } |
| 588 const MenuItemView* pending_state_item() const { | 607 const MenuItemView* pending_state_item() const { |
| 589 return menu_controller_->pending_state_.item; | 608 return menu_controller_->pending_state_.item; |
| 590 } | 609 } |
| 591 MenuController::ExitType menu_exit_type() const { | 610 MenuController::ExitType menu_exit_type() const { |
| 592 return menu_controller_->exit_type_; | 611 return menu_controller_->exit_type_; |
| 593 } | 612 } |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 668 menu_item_->SetController(menu_controller_); | 687 menu_item_->SetController(menu_controller_); |
| 669 } | 688 } |
| 670 | 689 |
| 671 // Not owned. | 690 // Not owned. |
| 672 DestructingTestViewsDelegate* test_views_delegate_; | 691 DestructingTestViewsDelegate* test_views_delegate_; |
| 673 | 692 |
| 674 std::unique_ptr<Widget> owner_; | 693 std::unique_ptr<Widget> owner_; |
| 675 std::unique_ptr<ui::test::EventGenerator> event_generator_; | 694 std::unique_ptr<ui::test::EventGenerator> event_generator_; |
| 676 std::unique_ptr<TestMenuItemViewShown> menu_item_; | 695 std::unique_ptr<TestMenuItemViewShown> menu_item_; |
| 677 std::unique_ptr<TestMenuControllerDelegate> menu_controller_delegate_; | 696 std::unique_ptr<TestMenuControllerDelegate> menu_controller_delegate_; |
| 678 std::unique_ptr<MenuDelegate> menu_delegate_; | 697 std::unique_ptr<TestMenuDelegate> menu_delegate_; |
| 679 MenuController* menu_controller_; | 698 MenuController* menu_controller_; |
| 680 TestMenuMessageLoop* test_message_loop_; | 699 TestMenuMessageLoop* test_message_loop_; |
| 681 | 700 |
| 682 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); | 701 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest); |
| 683 }; | 702 }; |
| 684 | 703 |
| 685 #if defined(OS_LINUX) && defined(USE_X11) | 704 #if defined(OS_LINUX) && defined(USE_X11) |
| 686 // Tests that an event targeter which blocks events will be honored by the menu | 705 // Tests that an event targeter which blocks events will be honored by the menu |
| 687 // event dispatcher. | 706 // event dispatcher. |
| 688 TEST_F(MenuControllerTest, EventTargeter) { | 707 TEST_F(MenuControllerTest, EventTargeter) { |
| 689 base::ThreadTaskRunnerHandle::Get()->PostTask( | 708 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 690 FROM_HERE, base::Bind(&MenuControllerTest::TestEventTargeter, | 709 FROM_HERE, base::Bind(&MenuControllerTest::TestEventTargeter, |
| 691 base::Unretained(this))); | 710 base::Unretained(this))); |
| 692 RunMenu(); | 711 RunMenu(); |
| 693 } | 712 } |
| 694 | 713 |
| 695 // Tests that an non-nested menu receiving an escape key will fully shut. This | 714 // Tests that an non-nested menu receiving an escape key will fully shut. This |
| 696 // should not crash by attempting to retarget the key to an inner menu. | 715 // should not crash by attempting to retarget the key to an inner menu. |
| 697 TEST_F(MenuControllerTest, AsyncEscapeKey) { | 716 TEST_F(MenuControllerTest, AsyncEscapeKey) { |
| 698 menu_controller()->SetAsyncRun(true); | 717 menu_controller()->SetAsyncRun(true); |
| 699 TestAsyncEscapeKey(); | 718 TestAsyncEscapeKey(); |
| 719 EXPECT_EQ(MenuController::EXIT_ALL, menu_exit_type()); |
| 700 } | 720 } |
| 701 | 721 |
| 702 #endif // defined(OS_LINUX) && defined(USE_X11) | 722 #endif // defined(OS_LINUX) && defined(USE_X11) |
| 703 | 723 |
| 704 #if defined(USE_X11) | 724 #if defined(USE_X11) |
| 705 // Tests that touch event ids are released correctly. See crbug.com/439051 for | 725 // Tests that touch event ids are released correctly. See crbug.com/439051 for |
| 706 // details. When the ids aren't managed correctly, we get stuck down touches. | 726 // details. When the ids aren't managed correctly, we get stuck down touches. |
| 707 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { | 727 TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) { |
| 708 TestEventHandler test_event_handler; | 728 TestEventHandler test_event_handler; |
| 709 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( | 729 owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler( |
| (...skipping 853 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1563 controller->SetAsyncRun(true); | 1583 controller->SetAsyncRun(true); |
| 1564 | 1584 |
| 1565 int mouse_event_flags = 0; | 1585 int mouse_event_flags = 0; |
| 1566 MenuItemView* run_result = | 1586 MenuItemView* run_result = |
| 1567 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), | 1587 controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), |
| 1568 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); | 1588 MENU_ANCHOR_TOPLEFT, false, false, &mouse_event_flags); |
| 1569 EXPECT_EQ(run_result, nullptr); | 1589 EXPECT_EQ(run_result, nullptr); |
| 1570 TestDestroyedDuringViewsRelease(); | 1590 TestDestroyedDuringViewsRelease(); |
| 1571 } | 1591 } |
| 1572 | 1592 |
| 1593 // Tests that when a context menu is opened above an empty menu item, and a |
| 1594 // right-click occurs over the empty item, that the bottom menu is not hidden, |
| 1595 // that a request to relaunch the context menu is received, and that |
| 1596 // subsequently pressing ESC does not crash the browser. |
| 1597 TEST_F(MenuControllerTest, RepostEventToEmptyMenuItem) { |
| 1598 // Setup a submenu. Additionally hook up appropriate Widget and View |
| 1599 // containers, with bounds, so that hit testing works. |
| 1600 MenuController* controller = menu_controller(); |
| 1601 controller->SetAsyncRun(true); |
| 1602 MenuItemView* base_menu = menu_item(); |
| 1603 base_menu->SetBounds(0, 0, 200, 200); |
| 1604 SubmenuView* base_submenu = base_menu->GetSubmenu(); |
| 1605 base_submenu->SetBounds(0, 0, 200, 200); |
| 1606 base_submenu->ShowAt(owner(), gfx::Rect(0, 0, 200, 200), false); |
| 1607 GetMenuHost(base_submenu) |
| 1608 ->SetContentsView(base_submenu->GetScrollViewContainer()); |
| 1609 |
| 1610 // Build the submenu to have an empty menu item. Additionally hook up |
| 1611 // appropriate Widget and View containersm with counds, so that hit testing |
| 1612 // works. |
| 1613 std::unique_ptr<TestMenuDelegate> sub_menu_item_delegate = |
| 1614 base::MakeUnique<TestMenuDelegate>(); |
| 1615 std::unique_ptr<TestMenuItemViewShown> sub_menu_item = |
| 1616 base::MakeUnique<TestMenuItemViewShown>(sub_menu_item_delegate.get()); |
| 1617 sub_menu_item->AddEmptyMenusForTest(); |
| 1618 sub_menu_item->SetController(controller); |
| 1619 sub_menu_item->SetBounds(0, 50, 50, 50); |
| 1620 base_submenu->AddChildView(sub_menu_item.get()); |
| 1621 SubmenuView* sub_menu_view = sub_menu_item->GetSubmenu(); |
| 1622 sub_menu_view->SetBounds(0, 50, 50, 50); |
| 1623 sub_menu_view->ShowAt(owner(), gfx::Rect(0, 50, 50, 50), false); |
| 1624 GetMenuHost(sub_menu_view) |
| 1625 ->SetContentsView(sub_menu_view->GetScrollViewContainer()); |
| 1626 |
| 1627 // Set that the last selection target was the item which launches the submenu, |
| 1628 // as the empty item can never become a target. |
| 1629 SetPendingStateItem(sub_menu_item.get()); |
| 1630 |
| 1631 // Nest a context menu. |
| 1632 std::unique_ptr<TestMenuDelegate> nested_menu_delegate_1 = |
| 1633 base::MakeUnique<TestMenuDelegate>(); |
| 1634 std::unique_ptr<TestMenuItemViewShown> nested_menu_item_1 = |
| 1635 base::MakeUnique<TestMenuItemViewShown>(nested_menu_delegate_1.get()); |
| 1636 nested_menu_item_1->SetBounds(0, 0, 100, 100); |
| 1637 nested_menu_item_1->SetController(controller); |
| 1638 std::unique_ptr<TestMenuControllerDelegate> nested_controller_delegate_1 = |
| 1639 base::MakeUnique<TestMenuControllerDelegate>(); |
| 1640 controller->AddNestedDelegate(nested_controller_delegate_1.get()); |
| 1641 int mouse_event_flags = 0; |
| 1642 MenuItemView* run_result = controller->Run( |
| 1643 owner(), nullptr, nested_menu_item_1.get(), gfx::Rect(150, 50, 100, 100), |
| 1644 MENU_ANCHOR_TOPLEFT, true, false, &mouse_event_flags); |
| 1645 EXPECT_EQ(run_result, nullptr); |
| 1646 |
| 1647 // Press down outside of the context menu, and within the empty menu item. |
| 1648 // This should close the first context menu. |
| 1649 gfx::Point press_location(sub_menu_view->bounds().CenterPoint()); |
| 1650 ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, press_location, |
| 1651 press_location, ui::EventTimeForNow(), |
| 1652 ui::EF_RIGHT_MOUSE_BUTTON, 0); |
| 1653 ProcessMousePressed(nested_menu_item_1->GetSubmenu(), press_event); |
| 1654 EXPECT_EQ(nested_controller_delegate_1->on_menu_closed_called(), 1); |
| 1655 EXPECT_EQ(menu_controller_delegate(), GetCurrentDelegate()); |
| 1656 |
| 1657 // While the current state is the menu item which launched the sub menu, cause |
| 1658 // a drag in the empty menu item. This should not hide the menu. |
| 1659 SetState(sub_menu_item.get()); |
| 1660 press_location.Offset(-5, 0); |
| 1661 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, press_location, |
| 1662 press_location, ui::EventTimeForNow(), |
| 1663 ui::EF_RIGHT_MOUSE_BUTTON, 0); |
| 1664 ProcessMouseDragged(sub_menu_view, drag_event); |
| 1665 EXPECT_EQ(menu_delegate()->will_hide_menu_count(), 0); |
| 1666 |
| 1667 // Release the mouse in the empty menu item, triggering a context menu |
| 1668 // request. |
| 1669 ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, press_location, |
| 1670 press_location, ui::EventTimeForNow(), |
| 1671 ui::EF_RIGHT_MOUSE_BUTTON, 0); |
| 1672 ProcessMouseReleased(sub_menu_view, release_event); |
| 1673 EXPECT_EQ(sub_menu_item_delegate->show_context_menu_count(), 1); |
| 1674 EXPECT_EQ(sub_menu_item_delegate->show_context_menu_source(), |
| 1675 sub_menu_item.get()); |
| 1676 |
| 1677 // Nest a context menu. |
| 1678 std::unique_ptr<TestMenuDelegate> nested_menu_delegate_2 = |
| 1679 base::MakeUnique<TestMenuDelegate>(); |
| 1680 std::unique_ptr<TestMenuItemViewShown> nested_menu_item_2 = |
| 1681 base::MakeUnique<TestMenuItemViewShown>(nested_menu_delegate_2.get()); |
| 1682 nested_menu_item_2->SetBounds(0, 0, 100, 100); |
| 1683 nested_menu_item_2->SetController(controller); |
| 1684 |
| 1685 std::unique_ptr<TestMenuControllerDelegate> nested_controller_delegate_2 = |
| 1686 base::MakeUnique<TestMenuControllerDelegate>(); |
| 1687 controller->AddNestedDelegate(nested_controller_delegate_2.get()); |
| 1688 run_result = controller->Run( |
| 1689 owner(), nullptr, nested_menu_item_2.get(), gfx::Rect(150, 50, 100, 100), |
| 1690 MENU_ANCHOR_TOPLEFT, true, false, &mouse_event_flags); |
| 1691 EXPECT_EQ(run_result, nullptr); |
| 1692 |
| 1693 // The escapce key should only close the nested menu. SelectByChar should not |
| 1694 // crash. |
| 1695 TestAsyncEscapeKey(); |
| 1696 EXPECT_EQ(nested_controller_delegate_2->on_menu_closed_called(), 1); |
| 1697 EXPECT_EQ(menu_controller_delegate(), GetCurrentDelegate()); |
| 1698 } |
| 1699 |
| 1573 #endif // defined(USE_AURA) | 1700 #endif // defined(USE_AURA) |
| 1574 | 1701 |
| 1575 } // namespace test | 1702 } // namespace test |
| 1576 } // namespace views | 1703 } // namespace views |
| OLD | NEW |