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