Home | History | Annotate | Download | only in native
      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 "ui/views/controls/native/native_view_host.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "ui/aura/window.h"
     10 #include "ui/views/test/views_test_base.h"
     11 #include "ui/views/widget/widget.h"
     12 
     13 namespace views {
     14 
     15 class NativeViewHostTest : public ViewsTestBase {
     16  public:
     17   NativeViewHostTest() {
     18   }
     19 
     20   virtual void SetUp() OVERRIDE {
     21     ViewsTestBase::SetUp();
     22 
     23     // Create the top level widget.
     24     toplevel_.reset(new Widget);
     25     Widget::InitParams toplevel_params =
     26         CreateParams(Widget::InitParams::TYPE_WINDOW);
     27     toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     28     toplevel_->Init(toplevel_params);
     29   }
     30 
     31   // Create a child widget whose native parent is |native_parent_view|, uses
     32   // |contents_view|, and is attached to |host| which is added as a child to
     33   // |parent_view|.
     34   Widget* CreateChildForHost(gfx::NativeView native_parent_view,
     35                              View* parent_view,
     36                              View* contents_view,
     37                              NativeViewHost* host) {
     38     Widget* child = new Widget;
     39     Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL);
     40     child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     41     child_params.parent = native_parent_view;
     42     child->Init(child_params);
     43     child->SetContentsView(contents_view);
     44 
     45     // Owned by |parent_view|.
     46     parent_view->AddChildView(host);
     47     host->Attach(child->GetNativeView());
     48 
     49     return child;
     50   }
     51 
     52   Widget* toplevel() {
     53     return toplevel_.get();
     54   }
     55 
     56  private:
     57   scoped_ptr<Widget> toplevel_;
     58 
     59   DISALLOW_COPY_AND_ASSIGN(NativeViewHostTest);
     60 };
     61 
     62 namespace {
     63 
     64 // View implementation used by NativeViewHierarchyChanged to count number of
     65 // times NativeViewHierarchyChanged() is invoked.
     66 class NativeViewHierarchyChangedTestView : public View {
     67  public:
     68   NativeViewHierarchyChangedTestView() : notification_count_(0) {
     69   }
     70 
     71   void ResetCount() {
     72     notification_count_ = 0;
     73   }
     74 
     75   int notification_count() const { return notification_count_; }
     76 
     77   // Overriden from View:
     78   virtual void NativeViewHierarchyChanged() OVERRIDE {
     79     ++notification_count_;
     80     View::NativeViewHierarchyChanged();
     81   }
     82 
     83  private:
     84   int notification_count_;
     85 
     86   DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView);
     87 };
     88 
     89 aura::Window* GetNativeParent(aura::Window* window) {
     90   return window->parent();
     91 }
     92 
     93 class ViewHierarchyChangedTestHost : public NativeViewHost {
     94  public:
     95   ViewHierarchyChangedTestHost()
     96       : num_parent_changes_(0) {
     97   }
     98 
     99   void ResetParentChanges() {
    100     num_parent_changes_ = 0;
    101   }
    102 
    103   int num_parent_changes() const {
    104     return num_parent_changes_;
    105   }
    106 
    107   // Overriden from NativeViewHost:
    108   virtual void ViewHierarchyChanged(
    109       const ViewHierarchyChangedDetails& details) OVERRIDE {
    110     gfx::NativeView parent_before = native_view() ?
    111         GetNativeParent(native_view()) : NULL;
    112     NativeViewHost::ViewHierarchyChanged(details);
    113     gfx::NativeView parent_after = native_view() ?
    114         GetNativeParent(native_view()) : NULL;
    115     if (parent_before != parent_after)
    116       ++num_parent_changes_;
    117   }
    118 
    119  private:
    120   int num_parent_changes_;
    121 
    122   DISALLOW_COPY_AND_ASSIGN(ViewHierarchyChangedTestHost);
    123 };
    124 
    125 }  // namespace
    126 
    127 // Verifies NativeViewHierarchyChanged is sent.
    128 TEST_F(NativeViewHostTest, NativeViewHierarchyChanged) {
    129   // Create a child widget.
    130   NativeViewHierarchyChangedTestView* test_view =
    131       new NativeViewHierarchyChangedTestView;
    132   NativeViewHost* host = new NativeViewHost;
    133   scoped_ptr<Widget> child(CreateChildForHost(toplevel()->GetNativeView(),
    134                                               toplevel()->GetRootView(),
    135                                               test_view,
    136                                               host));
    137 
    138   EXPECT_EQ(0, test_view->notification_count());
    139   test_view->ResetCount();
    140 
    141   // Detaching should send a NativeViewHierarchyChanged() notification and
    142   // change the parent.
    143   host->Detach();
    144   EXPECT_EQ(1, test_view->notification_count());
    145   EXPECT_NE(toplevel()->GetNativeView(),
    146             GetNativeParent(child->GetNativeView()));
    147   test_view->ResetCount();
    148 
    149   // Attaching should send a NativeViewHierarchyChanged() notification and
    150   // reset the parent.
    151   host->Attach(child->GetNativeView());
    152   EXPECT_EQ(1, test_view->notification_count());
    153   EXPECT_EQ(toplevel()->GetNativeView(),
    154             GetNativeParent(child->GetNativeView()));
    155 }
    156 
    157 // Verifies ViewHierarchyChanged handles NativeViewHost remove, add and move
    158 // (reparent) operations with correct parent changes.
    159 // This exercises the non-recursive code paths in
    160 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
    161 TEST_F(NativeViewHostTest, ViewHierarchyChangedForHost) {
    162   // Original tree:
    163   // toplevel
    164   // +-- host0 (NativeViewHost)
    165   //     +-- child0 (Widget, attached to host0)
    166   //     +-- test_host (ViewHierarchyChangedTestHost)
    167   //         +-- test_child (Widget, attached to test_host)
    168   // +-- host1 (NativeViewHost)
    169   //     +-- child1 (Widget, attached to host1)
    170 
    171   // Add two children widgets attached to a NativeViewHost, and a test
    172   // grandchild as child widget of host0.
    173   NativeViewHost* host0 = new NativeViewHost;
    174   scoped_ptr<Widget> child0(CreateChildForHost(toplevel()->GetNativeView(),
    175                                                toplevel()->GetRootView(),
    176                                                new View,
    177                                                host0));
    178   NativeViewHost* host1 = new NativeViewHost;
    179   scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(),
    180                                                toplevel()->GetRootView(),
    181                                                new View,
    182                                                host1));
    183   ViewHierarchyChangedTestHost* test_host = new ViewHierarchyChangedTestHost;
    184   scoped_ptr<Widget> test_child(CreateChildForHost(host0->native_view(),
    185                                                    host0,
    186                                                    new View,
    187                                                    test_host));
    188 
    189   // Remove test_host from host0, expect 1 parent change.
    190   test_host->ResetParentChanges();
    191   EXPECT_EQ(0, test_host->num_parent_changes());
    192   host0->RemoveChildView(test_host);
    193   EXPECT_EQ(1, test_host->num_parent_changes());
    194 
    195   // Add test_host back to host0, expect 1 parent change.
    196   test_host->ResetParentChanges();
    197   EXPECT_EQ(0, test_host->num_parent_changes());
    198   host0->AddChildView(test_host);
    199   EXPECT_EQ(1, test_host->num_parent_changes());
    200 
    201   // Reparent test_host to host1, expect no parent change because the old and
    202   // new parents, host0 and host1, belong to the same toplevel widget.
    203   test_host->ResetParentChanges();
    204   EXPECT_EQ(0, test_host->num_parent_changes());
    205   host1->AddChildView(test_host);
    206   EXPECT_EQ(0, test_host->num_parent_changes());
    207 
    208   // Reparent test_host to contents view of child0, expect 2 parent changes
    209   // because the old parent belongs to the toplevel widget whereas the new
    210   // parent belongs to the child0.
    211   test_host->ResetParentChanges();
    212   EXPECT_EQ(0, test_host->num_parent_changes());
    213   child0->GetContentsView()->AddChildView(test_host);
    214   EXPECT_EQ(2, test_host->num_parent_changes());
    215 }
    216 
    217 // Verifies ViewHierarchyChanged handles NativeViewHost's parent remove, add and
    218 // move (reparent) operations with correct parent changes.
    219 // This exercises the recursive code paths in
    220 // View::PropagateRemoveNotifications() and View::PropagateAddNotifications().
    221 TEST_F(NativeViewHostTest, ViewHierarchyChangedForHostParent) {
    222   // Original tree:
    223   // toplevel
    224   // +-- view0 (View)
    225   //     +-- host0 (NativeViewHierarchyChangedTestHost)
    226   //         +-- child0 (Widget, attached to host0)
    227   // +-- view1 (View)
    228   //     +-- host1 (NativeViewHierarchyChangedTestHost)
    229   //         +-- child1 (Widget, attached to host1)
    230 
    231   // Add two children views.
    232   View* view0 = new View;
    233   toplevel()->GetRootView()->AddChildView(view0);
    234   View* view1 = new View;
    235   toplevel()->GetRootView()->AddChildView(view1);
    236 
    237   // To each child view, add a child widget.
    238   ViewHierarchyChangedTestHost* host0 = new ViewHierarchyChangedTestHost;
    239   scoped_ptr<Widget> child0(CreateChildForHost(toplevel()->GetNativeView(),
    240                                                view0,
    241                                                new View,
    242                                                host0));
    243   ViewHierarchyChangedTestHost* host1 = new ViewHierarchyChangedTestHost;
    244   scoped_ptr<Widget> child1(CreateChildForHost(toplevel()->GetNativeView(),
    245                                                view1,
    246                                                new View,
    247                                                host1));
    248 
    249   // Remove view0 from top level, expect 1 parent change.
    250   host0->ResetParentChanges();
    251   EXPECT_EQ(0, host0->num_parent_changes());
    252   toplevel()->GetRootView()->RemoveChildView(view0);
    253   EXPECT_EQ(1, host0->num_parent_changes());
    254 
    255   // Add view0 back to top level, expect 1 parent change.
    256   host0->ResetParentChanges();
    257   EXPECT_EQ(0, host0->num_parent_changes());
    258   toplevel()->GetRootView()->AddChildView(view0);
    259   EXPECT_EQ(1, host0->num_parent_changes());
    260 
    261   // Reparent view0 to view1, expect no parent change because the old and new
    262   // parents of both view0 and view1 belong to the same toplevel widget.
    263   host0->ResetParentChanges();
    264   host1->ResetParentChanges();
    265   EXPECT_EQ(0, host0->num_parent_changes());
    266   EXPECT_EQ(0, host1->num_parent_changes());
    267   view1->AddChildView(view0);
    268   EXPECT_EQ(0, host0->num_parent_changes());
    269   EXPECT_EQ(0, host1->num_parent_changes());
    270 
    271   // Restore original view hierarchy by adding back view0 to top level.
    272   // Then, reparent view1 to contents view of child0.
    273   // Expect 2 parent changes because the old parent belongs to the toplevel
    274   // widget whereas the new parent belongs to the 1st child widget.
    275   toplevel()->GetRootView()->AddChildView(view0);
    276   host0->ResetParentChanges();
    277   host1->ResetParentChanges();
    278   EXPECT_EQ(0, host0->num_parent_changes());
    279   EXPECT_EQ(0, host1->num_parent_changes());
    280   child0->GetContentsView()->AddChildView(view1);
    281   EXPECT_EQ(0, host0->num_parent_changes());
    282   EXPECT_EQ(2, host1->num_parent_changes());
    283 }
    284 
    285 }  // namespace views
    286