Home | History | Annotate | Download | only in native
      1 // Copyright (c) 2013 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_aura.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "ui/aura/client/aura_constants.h"
     10 #include "ui/aura/window.h"
     11 #include "ui/base/cursor/cursor.h"
     12 #include "ui/views/controls/native/native_view_host.h"
     13 #include "ui/views/controls/native/native_view_host_test_base.h"
     14 #include "ui/views/view.h"
     15 #include "ui/views/view_constants_aura.h"
     16 #include "ui/views/widget/widget.h"
     17 
     18 namespace views {
     19 
     20 // Observer watching for window visibility and bounds change events. This is
     21 // used to verify that the child and clipping window operations are done in the
     22 // right order.
     23 class NativeViewHostWindowObserver : public aura::WindowObserver {
     24  public:
     25   enum EventType {
     26     EVENT_NONE,
     27     EVENT_SHOWN,
     28     EVENT_HIDDEN,
     29     EVENT_BOUNDS_CHANGED,
     30   };
     31 
     32   struct EventDetails {
     33     EventType type;
     34     aura::Window* window;
     35     gfx::Rect bounds;
     36     bool operator!=(const EventDetails& rhs) {
     37       return type != rhs.type || window != rhs.window || bounds != rhs.bounds;
     38     }
     39   };
     40 
     41   NativeViewHostWindowObserver() {}
     42   virtual ~NativeViewHostWindowObserver() {}
     43 
     44   const std::vector<EventDetails>& events() const { return events_; }
     45 
     46   // aura::WindowObserver overrides
     47   virtual void OnWindowVisibilityChanged(aura::Window* window,
     48                                          bool visible) OVERRIDE {
     49     EventDetails event;
     50     event.type = visible ? EVENT_SHOWN : EVENT_HIDDEN;
     51     event.window = window;
     52     event.bounds = window->GetBoundsInRootWindow();
     53 
     54     // Dedupe events as a single Hide() call can result in several
     55     // notifications.
     56     if (events_.size() == 0u || events_.back() != event)
     57       events_.push_back(event);
     58   }
     59 
     60   virtual void OnWindowBoundsChanged(aura::Window* window,
     61                                      const gfx::Rect& old_bounds,
     62                                      const gfx::Rect& new_bounds) OVERRIDE {
     63     EventDetails event;
     64     event.type = EVENT_BOUNDS_CHANGED;
     65     event.window = window;
     66     event.bounds = window->GetBoundsInRootWindow();
     67     events_.push_back(event);
     68   }
     69 
     70  private:
     71   std::vector<EventDetails> events_;
     72   gfx::Rect bounds_at_visibility_changed_;
     73 
     74   DISALLOW_COPY_AND_ASSIGN(NativeViewHostWindowObserver);
     75 };
     76 
     77 class NativeViewHostAuraTest : public test::NativeViewHostTestBase {
     78  public:
     79   NativeViewHostAuraTest() {
     80   }
     81 
     82   NativeViewHostAura* native_host() {
     83     return static_cast<NativeViewHostAura*>(GetNativeWrapper());
     84   }
     85 
     86   Widget* child() {
     87     return child_.get();
     88   }
     89 
     90   aura::Window* clipping_window() { return &(native_host()->clipping_window_); }
     91 
     92   void CreateHost() {
     93     CreateTopLevel();
     94     CreateTestingHost();
     95     child_.reset(CreateChildForHost(toplevel()->GetNativeView(),
     96                                     toplevel()->GetRootView(),
     97                                     new View,
     98                                     host()));
     99   }
    100 
    101  private:
    102   scoped_ptr<Widget> child_;
    103 
    104   DISALLOW_COPY_AND_ASSIGN(NativeViewHostAuraTest);
    105 };
    106 
    107 // Verifies NativeViewHostAura stops observing native view on destruction.
    108 TEST_F(NativeViewHostAuraTest, StopObservingNativeViewOnDestruct) {
    109   CreateHost();
    110   aura::Window* child_win = child()->GetNativeView();
    111   NativeViewHostAura* aura_host = native_host();
    112 
    113   EXPECT_TRUE(child_win->HasObserver(aura_host));
    114   DestroyHost();
    115   EXPECT_FALSE(child_win->HasObserver(aura_host));
    116 }
    117 
    118 // Tests that the kHostViewKey is correctly set and cleared.
    119 TEST_F(NativeViewHostAuraTest, HostViewPropertyKey) {
    120   // Create the NativeViewHost and attach a NativeView.
    121   CreateHost();
    122   aura::Window* child_win = child()->GetNativeView();
    123   EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey));
    124   EXPECT_EQ(host()->GetWidget()->GetNativeView(),
    125             child_win->GetProperty(aura::client::kHostWindowKey));
    126   EXPECT_EQ(host(), clipping_window()->GetProperty(views::kHostViewKey));
    127 
    128   host()->Detach();
    129   EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey));
    130   EXPECT_FALSE(child_win->GetProperty(aura::client::kHostWindowKey));
    131   EXPECT_TRUE(clipping_window()->GetProperty(views::kHostViewKey));
    132 
    133   host()->Attach(child_win);
    134   EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey));
    135   EXPECT_EQ(host()->GetWidget()->GetNativeView(),
    136             child_win->GetProperty(aura::client::kHostWindowKey));
    137   EXPECT_EQ(host(), clipping_window()->GetProperty(views::kHostViewKey));
    138 
    139   DestroyHost();
    140   EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey));
    141   EXPECT_FALSE(child_win->GetProperty(aura::client::kHostWindowKey));
    142 }
    143 
    144 // Tests that the NativeViewHost reports the cursor set on its native view.
    145 TEST_F(NativeViewHostAuraTest, CursorForNativeView) {
    146   CreateHost();
    147 
    148   toplevel()->SetCursor(ui::kCursorHand);
    149   child()->SetCursor(ui::kCursorWait);
    150   ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
    151                             gfx::Point(0, 0), 0, 0);
    152 
    153   EXPECT_EQ(ui::kCursorWait, host()->GetCursor(move_event).native_type());
    154 
    155   DestroyHost();
    156 }
    157 
    158 // Test that destroying the top level widget before destroying the attached
    159 // NativeViewHost works correctly. Specifically the associated NVH should be
    160 // destroyed and there shouldn't be any errors.
    161 TEST_F(NativeViewHostAuraTest, DestroyWidget) {
    162   ResetHostDestroyedCount();
    163   CreateHost();
    164   ReleaseHost();
    165   EXPECT_EQ(0, host_destroyed_count());
    166   DestroyTopLevel();
    167   EXPECT_EQ(1, host_destroyed_count());
    168 }
    169 
    170 // Test that the fast resize path places the clipping and content windows were
    171 // they are supposed to be.
    172 TEST_F(NativeViewHostAuraTest, FastResizePath) {
    173   CreateHost();
    174   toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
    175 
    176   // Without fast resize, the clipping window should size to the native view
    177   // with the native view positioned at the origin of the clipping window and
    178   // the clipping window positioned where the native view was requested.
    179   host()->set_fast_resize(false);
    180   native_host()->ShowWidget(5, 10, 100, 100);
    181   EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
    182             host()->native_view()->bounds().ToString());
    183   EXPECT_EQ(gfx::Rect(5, 10, 100, 100).ToString(),
    184             clipping_window()->bounds().ToString());
    185 
    186   // With fast resize, the native view should remain the same size but be
    187   // clipped the requested size.
    188   host()->set_fast_resize(true);
    189   native_host()->ShowWidget(10, 25, 50, 50);
    190   EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
    191             host()->native_view()->bounds().ToString());
    192   EXPECT_EQ(gfx::Rect(10, 25, 50, 50).ToString(),
    193             clipping_window()->bounds().ToString());
    194 
    195   // Turning off fast resize should make the native view start resizing again.
    196   host()->set_fast_resize(false);
    197   native_host()->ShowWidget(10, 25, 50, 50);
    198   EXPECT_EQ(gfx::Rect(0, 0, 50, 50).ToString(),
    199             host()->native_view()->bounds().ToString());
    200   EXPECT_EQ(gfx::Rect(10, 25, 50, 50).ToString(),
    201             clipping_window()->bounds().ToString());
    202 
    203   DestroyHost();
    204 }
    205 
    206 // Test installing and uninstalling a clip.
    207 TEST_F(NativeViewHostAuraTest, InstallClip) {
    208   CreateHost();
    209   toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
    210 
    211   // Without a clip, the clipping window should always be positioned at the
    212   // requested coordinates with the native view positioned at the origin of the
    213   // clipping window.
    214   native_host()->ShowWidget(10, 20, 100, 100);
    215   EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
    216             host()->native_view()->bounds().ToString());
    217   EXPECT_EQ(gfx::Rect(10, 20, 100, 100).ToString(),
    218             clipping_window()->bounds().ToString());
    219 
    220   // Clip to the bottom right quarter of the native view.
    221   native_host()->InstallClip(60, 70, 50, 50);
    222   native_host()->ShowWidget(10, 20, 100, 100);
    223   EXPECT_EQ(gfx::Rect(-50, -50, 100, 100).ToString(),
    224             host()->native_view()->bounds().ToString());
    225   EXPECT_EQ(gfx::Rect(60, 70, 50, 50).ToString(),
    226             clipping_window()->bounds().ToString());
    227 
    228   // Clip to the center of the native view.
    229   native_host()->InstallClip(35, 45, 50, 50);
    230   native_host()->ShowWidget(10, 20, 100, 100);
    231   EXPECT_EQ(gfx::Rect(-25, -25, 100, 100).ToString(),
    232             host()->native_view()->bounds().ToString());
    233   EXPECT_EQ(gfx::Rect(35, 45, 50, 50).ToString(),
    234             clipping_window()->bounds().ToString());
    235 
    236   // Uninstalling the clip should make the clipping window match the native view
    237   // again.
    238   native_host()->UninstallClip();
    239   native_host()->ShowWidget(10, 20, 100, 100);
    240   EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
    241             host()->native_view()->bounds().ToString());
    242   EXPECT_EQ(gfx::Rect(10, 20, 100, 100).ToString(),
    243             clipping_window()->bounds().ToString());
    244 
    245   DestroyHost();
    246 }
    247 
    248 // Ensure native view is parented to the root window after detaching. This is
    249 // a regression test for http://crbug.com/389261.
    250 TEST_F(NativeViewHostAuraTest, ParentAfterDetach) {
    251   CreateHost();
    252   aura::Window* child_win = child()->GetNativeView();
    253   aura::Window* root_window = child_win->GetRootWindow();
    254   aura::WindowTreeHost* child_win_tree_host = child_win->GetHost();
    255 
    256   host()->Detach();
    257   EXPECT_EQ(root_window, child_win->GetRootWindow());
    258   EXPECT_EQ(child_win_tree_host, child_win->GetHost());
    259 
    260   DestroyHost();
    261 }
    262 
    263 // Ensure the clipping window is hidden before setting the native view's bounds.
    264 // This is a regression test for http://crbug.com/388699.
    265 TEST_F(NativeViewHostAuraTest, RemoveClippingWindowOrder) {
    266   CreateHost();
    267   toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
    268   native_host()->ShowWidget(10, 20, 100, 100);
    269 
    270   NativeViewHostWindowObserver test_observer;
    271   clipping_window()->AddObserver(&test_observer);
    272   child()->GetNativeView()->AddObserver(&test_observer);
    273 
    274   host()->Detach();
    275 
    276   ASSERT_EQ(3u, test_observer.events().size());
    277   EXPECT_EQ(NativeViewHostWindowObserver::EVENT_HIDDEN,
    278             test_observer.events()[0].type);
    279   EXPECT_EQ(clipping_window(), test_observer.events()[0].window);
    280   EXPECT_EQ(NativeViewHostWindowObserver::EVENT_BOUNDS_CHANGED,
    281             test_observer.events()[1].type);
    282   EXPECT_EQ(child()->GetNativeView(), test_observer.events()[1].window);
    283   EXPECT_EQ(NativeViewHostWindowObserver::EVENT_HIDDEN,
    284             test_observer.events()[2].type);
    285   EXPECT_EQ(child()->GetNativeView(), test_observer.events()[2].window);
    286 
    287   clipping_window()->RemoveObserver(&test_observer);
    288   child()->GetNativeView()->RemoveObserver(&test_observer);
    289 
    290   DestroyHost();
    291 }
    292 
    293 // Ensure the native view receives the correct bounds notification when it is
    294 // attached. This is a regression test for https://crbug.com/399420.
    295 TEST_F(NativeViewHostAuraTest, Attach) {
    296   CreateHost();
    297   host()->Detach();
    298 
    299   child()->GetNativeView()->SetBounds(gfx::Rect(0, 0, 0, 0));
    300   toplevel()->SetBounds(gfx::Rect(0, 0, 100, 100));
    301   host()->SetBounds(10, 10, 80, 80);
    302 
    303   NativeViewHostWindowObserver test_observer;
    304   child()->GetNativeView()->AddObserver(&test_observer);
    305 
    306   host()->Attach(child()->GetNativeView());
    307 
    308   ASSERT_EQ(3u, test_observer.events().size());
    309   EXPECT_EQ(NativeViewHostWindowObserver::EVENT_BOUNDS_CHANGED,
    310             test_observer.events()[0].type);
    311   EXPECT_EQ(child()->GetNativeView(), test_observer.events()[0].window);
    312   EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(),
    313             test_observer.events()[0].bounds.ToString());
    314   EXPECT_EQ(NativeViewHostWindowObserver::EVENT_SHOWN,
    315             test_observer.events()[1].type);
    316   EXPECT_EQ(child()->GetNativeView(), test_observer.events()[1].window);
    317   EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(),
    318             test_observer.events()[1].bounds.ToString());
    319   EXPECT_EQ(NativeViewHostWindowObserver::EVENT_SHOWN,
    320             test_observer.events()[2].type);
    321   EXPECT_EQ(clipping_window(), test_observer.events()[2].window);
    322   EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(),
    323             test_observer.events()[2].bounds.ToString());
    324 
    325   child()->GetNativeView()->RemoveObserver(&test_observer);
    326   DestroyHost();
    327 }
    328 
    329 // Ensure the clipping window is hidden with the native view. This is a
    330 // regression test for https://crbug.com/408877.
    331 TEST_F(NativeViewHostAuraTest, SimpleShowAndHide) {
    332   CreateHost();
    333 
    334   toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
    335   toplevel()->Show();
    336 
    337   host()->SetBounds(10, 10, 80, 80);
    338   EXPECT_TRUE(clipping_window()->IsVisible());
    339   EXPECT_TRUE(child()->IsVisible());
    340 
    341   host()->SetVisible(false);
    342   EXPECT_FALSE(clipping_window()->IsVisible());
    343   EXPECT_FALSE(child()->IsVisible());
    344 
    345   DestroyHost();
    346   DestroyTopLevel();
    347 }
    348 
    349 }  // namespace views
    350