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 <algorithm>
6 #include <set>
7
8 #include "base/basictypes.h"
9 #include "base/bind.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/base/hit_test.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/gfx/native_widget_types.h"
18 #include "ui/gfx/point.h"
19 #include "ui/views/bubble/bubble_delegate.h"
20 #include "ui/views/controls/textfield/textfield.h"
21 #include "ui/views/test/test_views_delegate.h"
22 #include "ui/views/test/widget_test.h"
23 #include "ui/views/views_delegate.h"
24 #include "ui/views/widget/native_widget_delegate.h"
25 #include "ui/views/widget/root_view.h"
26 #include "ui/views/window/dialog_delegate.h"
27 #include "ui/views/window/native_frame_view.h"
28
29 #if defined(USE_AURA)
30 #include "ui/aura/client/aura_constants.h"
31 #include "ui/aura/client/window_tree_client.h"
32 #include "ui/aura/root_window.h"
33 #include "ui/aura/test/test_window_delegate.h"
34 #include "ui/aura/window.h"
35 #include "ui/views/widget/native_widget_aura.h"
36 #if !defined(OS_CHROMEOS)
37 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
38 #endif
39 #elif defined(OS_WIN)
40 #include "ui/views/widget/native_widget_win.h"
41 #endif
42
43 #if defined(OS_WIN)
44 #include "ui/views/win/hwnd_util.h"
45 #endif
46
47 namespace views {
48 namespace test {
49
50 // A view that keeps track of the events it receives, but consumes no events.
51 class EventCountView : public View {
52 public:
EventCountView()53 EventCountView() {}
~EventCountView()54 virtual ~EventCountView() {}
55
GetEventCount(ui::EventType type)56 int GetEventCount(ui::EventType type) {
57 return event_count_[type];
58 }
59
ResetCounts()60 void ResetCounts() {
61 event_count_.clear();
62 }
63
64 protected:
65 // Overridden from ui::EventHandler:
OnKeyEvent(ui::KeyEvent * event)66 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
67 RecordEvent(*event);
68 }
OnMouseEvent(ui::MouseEvent * event)69 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
70 RecordEvent(*event);
71 }
OnScrollEvent(ui::ScrollEvent * event)72 virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE {
73 RecordEvent(*event);
74 }
OnTouchEvent(ui::TouchEvent * event)75 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE {
76 RecordEvent(*event);
77 }
OnGestureEvent(ui::GestureEvent * event)78 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
79 RecordEvent(*event);
80 }
81
82 private:
RecordEvent(const ui::Event & event)83 void RecordEvent(const ui::Event& event) {
84 ++event_count_[event.type()];
85 }
86
87 std::map<ui::EventType, int> event_count_;
88
89 DISALLOW_COPY_AND_ASSIGN(EventCountView);
90 };
91
92 // A view that keeps track of the events it receives, and consumes all scroll
93 // gesture events.
94 class ScrollableEventCountView : public EventCountView {
95 public:
ScrollableEventCountView()96 ScrollableEventCountView() {}
~ScrollableEventCountView()97 virtual ~ScrollableEventCountView() {}
98
99 private:
100 // Overridden from ui::EventHandler:
OnGestureEvent(ui::GestureEvent * event)101 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
102 EventCountView::OnGestureEvent(event);
103 switch (event->type()) {
104 case ui::ET_GESTURE_SCROLL_BEGIN:
105 case ui::ET_GESTURE_SCROLL_UPDATE:
106 case ui::ET_GESTURE_SCROLL_END:
107 case ui::ET_SCROLL_FLING_START:
108 event->SetHandled();
109 break;
110 default:
111 break;
112 }
113 }
114
115 DISALLOW_COPY_AND_ASSIGN(ScrollableEventCountView);
116 };
117
118 // A view that implements GetMinimumSize.
119 class MinimumSizeFrameView : public NativeFrameView {
120 public:
MinimumSizeFrameView(Widget * frame)121 explicit MinimumSizeFrameView(Widget* frame): NativeFrameView(frame) {}
~MinimumSizeFrameView()122 virtual ~MinimumSizeFrameView() {}
123
124 private:
125 // Overridden from View:
GetMinimumSize()126 virtual gfx::Size GetMinimumSize() OVERRIDE {
127 return gfx::Size(300, 400);
128 }
129
130 DISALLOW_COPY_AND_ASSIGN(MinimumSizeFrameView);
131 };
132
133 // An event handler that simply keeps a count of the different types of events
134 // it receives.
135 class EventCountHandler : public ui::EventHandler {
136 public:
EventCountHandler()137 EventCountHandler() {}
~EventCountHandler()138 virtual ~EventCountHandler() {}
139
GetEventCount(ui::EventType type)140 int GetEventCount(ui::EventType type) {
141 return event_count_[type];
142 }
143
ResetCounts()144 void ResetCounts() {
145 event_count_.clear();
146 }
147
148 protected:
149 // Overridden from ui::EventHandler:
OnEvent(ui::Event * event)150 virtual void OnEvent(ui::Event* event) OVERRIDE {
151 RecordEvent(*event);
152 ui::EventHandler::OnEvent(event);
153 }
154
155 private:
RecordEvent(const ui::Event & event)156 void RecordEvent(const ui::Event& event) {
157 ++event_count_[event.type()];
158 }
159
160 std::map<ui::EventType, int> event_count_;
161
162 DISALLOW_COPY_AND_ASSIGN(EventCountHandler);
163 };
164
GetWidgetShowState(const Widget * widget)165 ui::WindowShowState GetWidgetShowState(const Widget* widget) {
166 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
167 // because the former is implemented on all platforms but the latter is not.
168 return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN :
169 widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED :
170 widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED :
171 ui::SHOW_STATE_NORMAL;
172 }
173
TEST_F(WidgetTest,WidgetInitParams)174 TEST_F(WidgetTest, WidgetInitParams) {
175 ASSERT_FALSE(views_delegate().UseTransparentWindows());
176
177 // Widgets are not transparent by default.
178 Widget::InitParams init1;
179 EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init1.opacity);
180
181 // Non-window widgets are not transparent either.
182 Widget::InitParams init2(Widget::InitParams::TYPE_MENU);
183 EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init2.opacity);
184
185 // A ViewsDelegate can set windows transparent by default.
186 views_delegate().SetUseTransparentWindows(true);
187 Widget::InitParams init3;
188 EXPECT_EQ(Widget::InitParams::TRANSLUCENT_WINDOW, init3.opacity);
189
190 // Non-window widgets stay opaque.
191 Widget::InitParams init4(Widget::InitParams::TYPE_MENU);
192 EXPECT_EQ(Widget::InitParams::INFER_OPACITY, init4.opacity);
193 }
194
195 ////////////////////////////////////////////////////////////////////////////////
196 // Widget::GetTopLevelWidget tests.
197
TEST_F(WidgetTest,GetTopLevelWidget_Native)198 TEST_F(WidgetTest, GetTopLevelWidget_Native) {
199 // Create a hierarchy of native widgets.
200 Widget* toplevel = CreateTopLevelPlatformWidget();
201 gfx::NativeView parent = toplevel->GetNativeView();
202 Widget* child = CreateChildPlatformWidget(parent);
203
204 EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget());
205 EXPECT_EQ(toplevel, child->GetTopLevelWidget());
206
207 toplevel->CloseNow();
208 // |child| should be automatically destroyed with |toplevel|.
209 }
210
211 // Test if a focus manager and an inputmethod work without CHECK failure
212 // when window activation changes.
TEST_F(WidgetTest,ChangeActivation)213 TEST_F(WidgetTest, ChangeActivation) {
214 Widget* top1 = CreateTopLevelPlatformWidget();
215 // CreateInputMethod before activated
216 top1->GetInputMethod();
217 top1->Show();
218 RunPendingMessages();
219
220 Widget* top2 = CreateTopLevelPlatformWidget();
221 top2->Show();
222 RunPendingMessages();
223
224 top1->Activate();
225 RunPendingMessages();
226
227 // Create InputMethod after deactivated.
228 top2->GetInputMethod();
229 top2->Activate();
230 RunPendingMessages();
231
232 top1->Activate();
233 RunPendingMessages();
234
235 top1->CloseNow();
236 top2->CloseNow();
237 }
238
239 // Tests visibility of child widgets.
TEST_F(WidgetTest,Visibility)240 TEST_F(WidgetTest, Visibility) {
241 Widget* toplevel = CreateTopLevelPlatformWidget();
242 gfx::NativeView parent = toplevel->GetNativeView();
243 Widget* child = CreateChildPlatformWidget(parent);
244
245 EXPECT_FALSE(toplevel->IsVisible());
246 EXPECT_FALSE(child->IsVisible());
247
248 child->Show();
249
250 EXPECT_FALSE(toplevel->IsVisible());
251 EXPECT_FALSE(child->IsVisible());
252
253 toplevel->Show();
254
255 EXPECT_TRUE(toplevel->IsVisible());
256 EXPECT_TRUE(child->IsVisible());
257
258 toplevel->CloseNow();
259 // |child| should be automatically destroyed with |toplevel|.
260 }
261
262 #if defined(OS_WIN) && !defined(USE_AURA)
263 // On Windows, it is possible to have child window that are TYPE_POPUP. Unlike
264 // regular child windows, these should be created as hidden and must be shown
265 // explicitly.
TEST_F(WidgetTest,Visibility_ChildPopup)266 TEST_F(WidgetTest, Visibility_ChildPopup) {
267 Widget* toplevel = CreateTopLevelPlatformWidget();
268 Widget* child_popup = CreateChildPopupPlatformWidget(
269 toplevel->GetNativeView());
270
271 EXPECT_FALSE(toplevel->IsVisible());
272 EXPECT_FALSE(child_popup->IsVisible());
273
274 toplevel->Show();
275
276 EXPECT_TRUE(toplevel->IsVisible());
277 EXPECT_FALSE(child_popup->IsVisible());
278
279 child_popup->Show();
280
281 EXPECT_TRUE(child_popup->IsVisible());
282
283 toplevel->CloseNow();
284 // |child_popup| should be automatically destroyed with |toplevel|.
285 }
286 #endif
287
288 ////////////////////////////////////////////////////////////////////////////////
289 // Widget ownership tests.
290 //
291 // Tests various permutations of Widget ownership specified in the
292 // InitParams::Ownership param.
293
294 // A WidgetTest that supplies a toplevel widget for NativeWidget to parent to.
295 class WidgetOwnershipTest : public WidgetTest {
296 public:
WidgetOwnershipTest()297 WidgetOwnershipTest() {}
~WidgetOwnershipTest()298 virtual ~WidgetOwnershipTest() {}
299
SetUp()300 virtual void SetUp() {
301 WidgetTest::SetUp();
302 desktop_widget_ = CreateTopLevelPlatformWidget();
303 }
304
TearDown()305 virtual void TearDown() {
306 desktop_widget_->CloseNow();
307 WidgetTest::TearDown();
308 }
309
310 private:
311 Widget* desktop_widget_;
312
313 DISALLOW_COPY_AND_ASSIGN(WidgetOwnershipTest);
314 };
315
316 // A bag of state to monitor destructions.
317 struct OwnershipTestState {
OwnershipTestStateviews::test::OwnershipTestState318 OwnershipTestState() : widget_deleted(false), native_widget_deleted(false) {}
319
320 bool widget_deleted;
321 bool native_widget_deleted;
322 };
323
324 // A platform NativeWidget subclass that updates a bag of state when it is
325 // destroyed.
326 class OwnershipTestNativeWidget : public NativeWidgetPlatform {
327 public:
OwnershipTestNativeWidget(internal::NativeWidgetDelegate * delegate,OwnershipTestState * state)328 OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate,
329 OwnershipTestState* state)
330 : NativeWidgetPlatform(delegate),
331 state_(state) {
332 }
~OwnershipTestNativeWidget()333 virtual ~OwnershipTestNativeWidget() {
334 state_->native_widget_deleted = true;
335 }
336
337 private:
338 OwnershipTestState* state_;
339
340 DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidget);
341 };
342
343 // A views NativeWidget subclass that updates a bag of state when it is
344 // destroyed.
345 class OwnershipTestNativeWidgetPlatform : public NativeWidgetPlatformForTest {
346 public:
OwnershipTestNativeWidgetPlatform(internal::NativeWidgetDelegate * delegate,OwnershipTestState * state)347 OwnershipTestNativeWidgetPlatform(internal::NativeWidgetDelegate* delegate,
348 OwnershipTestState* state)
349 : NativeWidgetPlatformForTest(delegate),
350 state_(state) {
351 }
~OwnershipTestNativeWidgetPlatform()352 virtual ~OwnershipTestNativeWidgetPlatform() {
353 state_->native_widget_deleted = true;
354 }
355
356 private:
357 OwnershipTestState* state_;
358
359 DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetPlatform);
360 };
361
362 // A Widget subclass that updates a bag of state when it is destroyed.
363 class OwnershipTestWidget : public Widget {
364 public:
OwnershipTestWidget(OwnershipTestState * state)365 explicit OwnershipTestWidget(OwnershipTestState* state) : state_(state) {}
~OwnershipTestWidget()366 virtual ~OwnershipTestWidget() {
367 state_->widget_deleted = true;
368 }
369
370 private:
371 OwnershipTestState* state_;
372
373 DISALLOW_COPY_AND_ASSIGN(OwnershipTestWidget);
374 };
375
376 // Widget owns its NativeWidget, part 1: NativeWidget is a platform-native
377 // widget.
TEST_F(WidgetOwnershipTest,Ownership_WidgetOwnsPlatformNativeWidget)378 TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) {
379 OwnershipTestState state;
380
381 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state));
382 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
383 params.native_widget =
384 new OwnershipTestNativeWidgetPlatform(widget.get(), &state);
385 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
386 widget->Init(params);
387
388 // Now delete the Widget, which should delete the NativeWidget.
389 widget.reset();
390
391 EXPECT_TRUE(state.widget_deleted);
392 EXPECT_TRUE(state.native_widget_deleted);
393
394 // TODO(beng): write test for this ownership scenario and the NativeWidget
395 // being deleted out from under the Widget.
396 }
397
398 // Widget owns its NativeWidget, part 2: NativeWidget is a NativeWidget.
TEST_F(WidgetOwnershipTest,Ownership_WidgetOwnsViewsNativeWidget)399 TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) {
400 OwnershipTestState state;
401
402 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state));
403 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
404 params.native_widget =
405 new OwnershipTestNativeWidgetPlatform(widget.get(), &state);
406 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
407 widget->Init(params);
408
409 // Now delete the Widget, which should delete the NativeWidget.
410 widget.reset();
411
412 EXPECT_TRUE(state.widget_deleted);
413 EXPECT_TRUE(state.native_widget_deleted);
414
415 // TODO(beng): write test for this ownership scenario and the NativeWidget
416 // being deleted out from under the Widget.
417 }
418
419 // Widget owns its NativeWidget, part 3: NativeWidget is a NativeWidget,
420 // destroy the parent view.
TEST_F(WidgetOwnershipTest,Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView)421 TEST_F(WidgetOwnershipTest,
422 Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView) {
423 OwnershipTestState state;
424
425 Widget* toplevel = CreateTopLevelPlatformWidget();
426
427 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state));
428 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
429 params.native_widget =
430 new OwnershipTestNativeWidgetPlatform(widget.get(), &state);
431 params.parent = toplevel->GetNativeView();
432 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
433 widget->Init(params);
434
435 // Now close the toplevel, which deletes the view hierarchy.
436 toplevel->CloseNow();
437
438 RunPendingMessages();
439
440 // This shouldn't delete the widget because it shouldn't be deleted
441 // from the native side.
442 EXPECT_FALSE(state.widget_deleted);
443 EXPECT_FALSE(state.native_widget_deleted);
444
445 // Now delete it explicitly.
446 widget.reset();
447
448 EXPECT_TRUE(state.widget_deleted);
449 EXPECT_TRUE(state.native_widget_deleted);
450 }
451
452 // NativeWidget owns its Widget, part 1: NativeWidget is a platform-native
453 // widget.
TEST_F(WidgetOwnershipTest,Ownership_PlatformNativeWidgetOwnsWidget)454 TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) {
455 OwnershipTestState state;
456
457 Widget* widget = new OwnershipTestWidget(&state);
458 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
459 params.native_widget =
460 new OwnershipTestNativeWidgetPlatform(widget, &state);
461 widget->Init(params);
462
463 // Now destroy the native widget.
464 widget->CloseNow();
465
466 EXPECT_TRUE(state.widget_deleted);
467 EXPECT_TRUE(state.native_widget_deleted);
468 }
469
470 // NativeWidget owns its Widget, part 2: NativeWidget is a NativeWidget.
TEST_F(WidgetOwnershipTest,Ownership_ViewsNativeWidgetOwnsWidget)471 TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) {
472 OwnershipTestState state;
473
474 Widget* toplevel = CreateTopLevelPlatformWidget();
475
476 Widget* widget = new OwnershipTestWidget(&state);
477 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
478 params.native_widget =
479 new OwnershipTestNativeWidgetPlatform(widget, &state);
480 params.parent = toplevel->GetNativeView();
481 widget->Init(params);
482
483 // Now destroy the native widget. This is achieved by closing the toplevel.
484 toplevel->CloseNow();
485
486 // The NativeWidget won't be deleted until after a return to the message loop
487 // so we have to run pending messages before testing the destruction status.
488 RunPendingMessages();
489
490 EXPECT_TRUE(state.widget_deleted);
491 EXPECT_TRUE(state.native_widget_deleted);
492 }
493
494 // NativeWidget owns its Widget, part 3: NativeWidget is a platform-native
495 // widget, destroyed out from under it by the OS.
TEST_F(WidgetOwnershipTest,Ownership_PlatformNativeWidgetOwnsWidget_NativeDestroy)496 TEST_F(WidgetOwnershipTest,
497 Ownership_PlatformNativeWidgetOwnsWidget_NativeDestroy) {
498 OwnershipTestState state;
499
500 Widget* widget = new OwnershipTestWidget(&state);
501 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
502 params.native_widget =
503 new OwnershipTestNativeWidgetPlatform(widget, &state);
504 widget->Init(params);
505
506 // Now simulate a destroy of the platform native widget from the OS:
507 #if defined(USE_AURA)
508 delete widget->GetNativeView();
509 #elif defined(OS_WIN)
510 DestroyWindow(widget->GetNativeView());
511 #endif
512
513 EXPECT_TRUE(state.widget_deleted);
514 EXPECT_TRUE(state.native_widget_deleted);
515 }
516
517 // NativeWidget owns its Widget, part 4: NativeWidget is a NativeWidget,
518 // destroyed by the view hierarchy that contains it.
TEST_F(WidgetOwnershipTest,Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy)519 TEST_F(WidgetOwnershipTest,
520 Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy) {
521 OwnershipTestState state;
522
523 Widget* toplevel = CreateTopLevelPlatformWidget();
524
525 Widget* widget = new OwnershipTestWidget(&state);
526 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
527 params.native_widget =
528 new OwnershipTestNativeWidgetPlatform(widget, &state);
529 params.parent = toplevel->GetNativeView();
530 widget->Init(params);
531
532 // Destroy the widget (achieved by closing the toplevel).
533 toplevel->CloseNow();
534
535 // The NativeWidget won't be deleted until after a return to the message loop
536 // so we have to run pending messages before testing the destruction status.
537 RunPendingMessages();
538
539 EXPECT_TRUE(state.widget_deleted);
540 EXPECT_TRUE(state.native_widget_deleted);
541 }
542
543 // NativeWidget owns its Widget, part 5: NativeWidget is a NativeWidget,
544 // we close it directly.
TEST_F(WidgetOwnershipTest,Ownership_ViewsNativeWidgetOwnsWidget_Close)545 TEST_F(WidgetOwnershipTest,
546 Ownership_ViewsNativeWidgetOwnsWidget_Close) {
547 OwnershipTestState state;
548
549 Widget* toplevel = CreateTopLevelPlatformWidget();
550
551 Widget* widget = new OwnershipTestWidget(&state);
552 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
553 params.native_widget =
554 new OwnershipTestNativeWidgetPlatform(widget, &state);
555 params.parent = toplevel->GetNativeView();
556 widget->Init(params);
557
558 // Destroy the widget.
559 widget->Close();
560 toplevel->CloseNow();
561
562 // The NativeWidget won't be deleted until after a return to the message loop
563 // so we have to run pending messages before testing the destruction status.
564 RunPendingMessages();
565
566 EXPECT_TRUE(state.widget_deleted);
567 EXPECT_TRUE(state.native_widget_deleted);
568 }
569
570 // Widget owns its NativeWidget and has a WidgetDelegateView as its contents.
TEST_F(WidgetOwnershipTest,Ownership_WidgetOwnsNativeWidgetWithWithWidgetDelegateView)571 TEST_F(WidgetOwnershipTest,
572 Ownership_WidgetOwnsNativeWidgetWithWithWidgetDelegateView) {
573 OwnershipTestState state;
574
575 WidgetDelegateView* delegate_view = new WidgetDelegateView;
576
577 scoped_ptr<Widget> widget(new OwnershipTestWidget(&state));
578 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
579 params.native_widget =
580 new OwnershipTestNativeWidgetPlatform(widget.get(), &state);
581 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
582 params.delegate = delegate_view;
583 widget->Init(params);
584 widget->SetContentsView(delegate_view);
585
586 // Now delete the Widget. There should be no crash or use-after-free.
587 widget.reset();
588
589 EXPECT_TRUE(state.widget_deleted);
590 EXPECT_TRUE(state.native_widget_deleted);
591 }
592
593 ////////////////////////////////////////////////////////////////////////////////
594 // Test to verify using various Widget methods doesn't crash when the underlying
595 // NativeView is destroyed.
596 //
597 class WidgetWithDestroyedNativeViewTest : public ViewsTestBase {
598 public:
WidgetWithDestroyedNativeViewTest()599 WidgetWithDestroyedNativeViewTest() {}
~WidgetWithDestroyedNativeViewTest()600 virtual ~WidgetWithDestroyedNativeViewTest() {}
601
InvokeWidgetMethods(Widget * widget)602 void InvokeWidgetMethods(Widget* widget) {
603 widget->GetNativeView();
604 widget->GetNativeWindow();
605 ui::Accelerator accelerator;
606 widget->GetAccelerator(0, &accelerator);
607 widget->GetTopLevelWidget();
608 widget->GetWindowBoundsInScreen();
609 widget->GetClientAreaBoundsInScreen();
610 widget->SetBounds(gfx::Rect(0, 0, 100, 80));
611 widget->SetSize(gfx::Size(10, 11));
612 widget->SetBoundsConstrained(gfx::Rect(0, 0, 120, 140));
613 widget->SetVisibilityChangedAnimationsEnabled(false);
614 widget->StackAtTop();
615 widget->IsClosed();
616 widget->Close();
617 widget->Show();
618 widget->Hide();
619 widget->Activate();
620 widget->Deactivate();
621 widget->IsActive();
622 widget->DisableInactiveRendering();
623 widget->SetAlwaysOnTop(true);
624 widget->IsAlwaysOnTop();
625 widget->Maximize();
626 widget->Minimize();
627 widget->Restore();
628 widget->IsMaximized();
629 widget->IsFullscreen();
630 widget->SetOpacity(0);
631 widget->SetUseDragFrame(true);
632 widget->FlashFrame(true);
633 widget->IsVisible();
634 widget->GetThemeProvider();
635 widget->GetNativeTheme();
636 widget->GetFocusManager();
637 widget->GetInputMethod();
638 widget->SchedulePaintInRect(gfx::Rect(0, 0, 1, 2));
639 widget->IsMouseEventsEnabled();
640 widget->SetNativeWindowProperty("xx", widget);
641 widget->GetNativeWindowProperty("xx");
642 widget->GetFocusTraversable();
643 widget->GetLayer();
644 widget->ReorderNativeViews();
645 widget->SetCapture(widget->GetRootView());
646 widget->ReleaseCapture();
647 widget->HasCapture();
648 widget->GetWorkAreaBoundsInScreen();
649 // These three crash with NativeWidgetWin, so I'm assuming we don't need
650 // them to work for the other NativeWidget impls.
651 // widget->CenterWindow(gfx::Size(50, 60));
652 // widget->GetRestoredBounds();
653 // widget->ShowInactive();
654 }
655
656 private:
657 DISALLOW_COPY_AND_ASSIGN(WidgetWithDestroyedNativeViewTest);
658 };
659
TEST_F(WidgetWithDestroyedNativeViewTest,Test)660 TEST_F(WidgetWithDestroyedNativeViewTest, Test) {
661 {
662 Widget widget;
663 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
664 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
665 widget.Init(params);
666 widget.Show();
667
668 widget.native_widget_private()->CloseNow();
669 InvokeWidgetMethods(&widget);
670 }
671 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
672 {
673 Widget widget;
674 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
675 params.native_widget = new DesktopNativeWidgetAura(&widget);
676 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
677 widget.Init(params);
678 widget.Show();
679
680 widget.native_widget_private()->CloseNow();
681 InvokeWidgetMethods(&widget);
682 }
683 #endif
684 }
685
686 ////////////////////////////////////////////////////////////////////////////////
687 // Widget observer tests.
688 //
689
690 class WidgetObserverTest : public WidgetTest, public WidgetObserver {
691 public:
WidgetObserverTest()692 WidgetObserverTest()
693 : active_(NULL),
694 widget_closed_(NULL),
695 widget_activated_(NULL),
696 widget_shown_(NULL),
697 widget_hidden_(NULL),
698 widget_bounds_changed_(NULL) {
699 }
700
~WidgetObserverTest()701 virtual ~WidgetObserverTest() {}
702
703 // Overridden from WidgetObserver:
OnWidgetDestroying(Widget * widget)704 virtual void OnWidgetDestroying(Widget* widget) OVERRIDE {
705 if (active_ == widget)
706 active_ = NULL;
707 widget_closed_ = widget;
708 }
709
OnWidgetActivationChanged(Widget * widget,bool active)710 virtual void OnWidgetActivationChanged(Widget* widget,
711 bool active) OVERRIDE {
712 if (active) {
713 if (widget_activated_)
714 widget_activated_->Deactivate();
715 widget_activated_ = widget;
716 active_ = widget;
717 } else {
718 if (widget_activated_ == widget)
719 widget_activated_ = NULL;
720 widget_deactivated_ = widget;
721 }
722 }
723
OnWidgetVisibilityChanged(Widget * widget,bool visible)724 virtual void OnWidgetVisibilityChanged(Widget* widget,
725 bool visible) OVERRIDE {
726 if (visible)
727 widget_shown_ = widget;
728 else
729 widget_hidden_ = widget;
730 }
731
OnWidgetBoundsChanged(Widget * widget,const gfx::Rect & new_bounds)732 virtual void OnWidgetBoundsChanged(Widget* widget,
733 const gfx::Rect& new_bounds) OVERRIDE {
734 widget_bounds_changed_ = widget;
735 }
736
reset()737 void reset() {
738 active_ = NULL;
739 widget_closed_ = NULL;
740 widget_activated_ = NULL;
741 widget_deactivated_ = NULL;
742 widget_shown_ = NULL;
743 widget_hidden_ = NULL;
744 widget_bounds_changed_ = NULL;
745 }
746
NewWidget()747 Widget* NewWidget() {
748 Widget* widget = CreateTopLevelNativeWidget();
749 widget->AddObserver(this);
750 return widget;
751 }
752
active() const753 const Widget* active() const { return active_; }
widget_closed() const754 const Widget* widget_closed() const { return widget_closed_; }
widget_activated() const755 const Widget* widget_activated() const { return widget_activated_; }
widget_deactivated() const756 const Widget* widget_deactivated() const { return widget_deactivated_; }
widget_shown() const757 const Widget* widget_shown() const { return widget_shown_; }
widget_hidden() const758 const Widget* widget_hidden() const { return widget_hidden_; }
widget_bounds_changed() const759 const Widget* widget_bounds_changed() const { return widget_bounds_changed_; }
760
761 private:
762 Widget* active_;
763
764 Widget* widget_closed_;
765 Widget* widget_activated_;
766 Widget* widget_deactivated_;
767 Widget* widget_shown_;
768 Widget* widget_hidden_;
769 Widget* widget_bounds_changed_;
770 };
771
TEST_F(WidgetObserverTest,DISABLED_ActivationChange)772 TEST_F(WidgetObserverTest, DISABLED_ActivationChange) {
773 Widget* toplevel = CreateTopLevelPlatformWidget();
774
775 Widget* toplevel1 = NewWidget();
776 Widget* toplevel2 = NewWidget();
777
778 toplevel1->Show();
779 toplevel2->Show();
780
781 reset();
782
783 toplevel1->Activate();
784
785 RunPendingMessages();
786 EXPECT_EQ(toplevel1, widget_activated());
787
788 toplevel2->Activate();
789 RunPendingMessages();
790 EXPECT_EQ(toplevel1, widget_deactivated());
791 EXPECT_EQ(toplevel2, widget_activated());
792 EXPECT_EQ(toplevel2, active());
793
794 toplevel->CloseNow();
795 }
796
TEST_F(WidgetObserverTest,DISABLED_VisibilityChange)797 TEST_F(WidgetObserverTest, DISABLED_VisibilityChange) {
798 Widget* toplevel = CreateTopLevelPlatformWidget();
799
800 Widget* child1 = NewWidget();
801 Widget* child2 = NewWidget();
802
803 toplevel->Show();
804 child1->Show();
805 child2->Show();
806
807 reset();
808
809 child1->Hide();
810 EXPECT_EQ(child1, widget_hidden());
811
812 child2->Hide();
813 EXPECT_EQ(child2, widget_hidden());
814
815 child1->Show();
816 EXPECT_EQ(child1, widget_shown());
817
818 child2->Show();
819 EXPECT_EQ(child2, widget_shown());
820
821 toplevel->CloseNow();
822 }
823
TEST_F(WidgetObserverTest,DestroyBubble)824 TEST_F(WidgetObserverTest, DestroyBubble) {
825 Widget* anchor = CreateTopLevelPlatformWidget();
826 anchor->Show();
827
828 BubbleDelegateView* bubble_delegate =
829 new BubbleDelegateView(anchor->client_view(), BubbleBorder::NONE);
830 Widget* bubble_widget(BubbleDelegateView::CreateBubble(bubble_delegate));
831 bubble_widget->Show();
832 bubble_widget->CloseNow();
833
834 anchor->Hide();
835 anchor->CloseNow();
836 }
837
TEST_F(WidgetObserverTest,WidgetBoundsChanged)838 TEST_F(WidgetObserverTest, WidgetBoundsChanged) {
839 Widget* child1 = NewWidget();
840 Widget* child2 = NewWidget();
841
842 child1->OnNativeWidgetMove();
843 EXPECT_EQ(child1, widget_bounds_changed());
844
845 child2->OnNativeWidgetMove();
846 EXPECT_EQ(child2, widget_bounds_changed());
847
848 child1->OnNativeWidgetSizeChanged(gfx::Size());
849 EXPECT_EQ(child1, widget_bounds_changed());
850
851 child2->OnNativeWidgetSizeChanged(gfx::Size());
852 EXPECT_EQ(child2, widget_bounds_changed());
853 }
854
855 #if !defined(USE_AURA) && defined(OS_WIN)
856 // Aura needs shell to maximize/fullscreen window.
857 // NativeWidgetGtk doesn't implement GetRestoredBounds.
TEST_F(WidgetTest,GetRestoredBounds)858 TEST_F(WidgetTest, GetRestoredBounds) {
859 Widget* toplevel = CreateTopLevelPlatformWidget();
860 EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(),
861 toplevel->GetRestoredBounds().ToString());
862 toplevel->Show();
863 toplevel->Maximize();
864 RunPendingMessages();
865 EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(),
866 toplevel->GetRestoredBounds().ToString());
867 EXPECT_GT(toplevel->GetRestoredBounds().width(), 0);
868 EXPECT_GT(toplevel->GetRestoredBounds().height(), 0);
869
870 toplevel->Restore();
871 RunPendingMessages();
872 EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(),
873 toplevel->GetRestoredBounds().ToString());
874
875 toplevel->SetFullscreen(true);
876 RunPendingMessages();
877 EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(),
878 toplevel->GetRestoredBounds().ToString());
879 EXPECT_GT(toplevel->GetRestoredBounds().width(), 0);
880 EXPECT_GT(toplevel->GetRestoredBounds().height(), 0);
881 }
882 #endif
883
884 // Test that window state is not changed after getting out of full screen.
TEST_F(WidgetTest,ExitFullscreenRestoreState)885 TEST_F(WidgetTest, ExitFullscreenRestoreState) {
886 Widget* toplevel = CreateTopLevelPlatformWidget();
887
888 toplevel->Show();
889 RunPendingMessages();
890
891 // This should be a normal state window.
892 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
893
894 toplevel->SetFullscreen(true);
895 while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN)
896 RunPendingMessages();
897 toplevel->SetFullscreen(false);
898 while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN)
899 RunPendingMessages();
900
901 // And it should still be in normal state after getting out of full screen.
902 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
903
904 // Now, make it maximized.
905 toplevel->Maximize();
906 while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_MAXIMIZED)
907 RunPendingMessages();
908
909 toplevel->SetFullscreen(true);
910 while (GetWidgetShowState(toplevel) != ui::SHOW_STATE_FULLSCREEN)
911 RunPendingMessages();
912 toplevel->SetFullscreen(false);
913 while (GetWidgetShowState(toplevel) == ui::SHOW_STATE_FULLSCREEN)
914 RunPendingMessages();
915
916 // And it stays maximized after getting out of full screen.
917 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
918
919 // Clean up.
920 toplevel->Close();
921 RunPendingMessages();
922 }
923
924 #if defined(USE_AURA)
925 // The key-event propagation from Widget happens differently on aura and
926 // non-aura systems because of the difference in IME. So this test works only on
927 // aura.
TEST_F(WidgetTest,KeyboardInputEvent)928 TEST_F(WidgetTest, KeyboardInputEvent) {
929 Widget* toplevel = CreateTopLevelPlatformWidget();
930 View* container = toplevel->client_view();
931
932 Textfield* textfield = new Textfield();
933 textfield->SetText(ASCIIToUTF16("some text"));
934 container->AddChildView(textfield);
935 toplevel->Show();
936 textfield->RequestFocus();
937
938 // The press gets handled. The release doesn't have an effect.
939 ui::KeyEvent backspace_p(ui::ET_KEY_PRESSED, ui::VKEY_DELETE, 0, false);
940 toplevel->OnKeyEvent(&backspace_p);
941 EXPECT_TRUE(backspace_p.stopped_propagation());
942 ui::KeyEvent backspace_r(ui::ET_KEY_RELEASED, ui::VKEY_DELETE, 0, false);
943 toplevel->OnKeyEvent(&backspace_r);
944 EXPECT_FALSE(backspace_r.handled());
945
946 toplevel->Close();
947 }
948
949 // Verifies bubbles result in a focus lost when shown.
950 // TODO(msw): this tests relies on focus, it needs to be in
951 // interactive_ui_tests.
TEST_F(WidgetTest,DISABLED_FocusChangesOnBubble)952 TEST_F(WidgetTest, DISABLED_FocusChangesOnBubble) {
953 // Create a widget, show and activate it and focus the contents view.
954 View* contents_view = new View;
955 contents_view->SetFocusable(true);
956 Widget widget;
957 Widget::InitParams init_params =
958 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
959 init_params.bounds = gfx::Rect(0, 0, 200, 200);
960 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
961 #if !defined(OS_CHROMEOS)
962 init_params.native_widget = new DesktopNativeWidgetAura(&widget);
963 #endif
964 widget.Init(init_params);
965 widget.SetContentsView(contents_view);
966 widget.Show();
967 widget.Activate();
968 contents_view->RequestFocus();
969 EXPECT_TRUE(contents_view->HasFocus());
970
971 // Show a bubble.
972 BubbleDelegateView* bubble_delegate_view =
973 new BubbleDelegateView(contents_view, BubbleBorder::TOP_LEFT);
974 bubble_delegate_view->SetFocusable(true);
975 BubbleDelegateView::CreateBubble(bubble_delegate_view)->Show();
976 bubble_delegate_view->RequestFocus();
977
978 // |contents_view_| should no longer have focus.
979 EXPECT_FALSE(contents_view->HasFocus());
980 EXPECT_TRUE(bubble_delegate_view->HasFocus());
981
982 bubble_delegate_view->GetWidget()->CloseNow();
983
984 // Closing the bubble should result in focus going back to the contents view.
985 EXPECT_TRUE(contents_view->HasFocus());
986 }
987
988 // Desktop native widget Aura tests are for non Chrome OS platforms.
989 #if !defined(OS_CHROMEOS)
990 // Test to ensure that after minimize, view width is set to zero.
TEST_F(WidgetTest,TestViewWidthAfterMinimizingWidget)991 TEST_F(WidgetTest, TestViewWidthAfterMinimizingWidget) {
992 // Create a widget.
993 Widget widget;
994 Widget::InitParams init_params =
995 CreateParams(Widget::InitParams::TYPE_WINDOW);
996 init_params.show_state = ui::SHOW_STATE_NORMAL;
997 gfx::Rect initial_bounds(0, 0, 300, 400);
998 init_params.bounds = initial_bounds;
999 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1000 init_params.native_widget = new DesktopNativeWidgetAura(&widget);
1001 widget.Init(init_params);
1002 NonClientView* non_client_view = widget.non_client_view();
1003 NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget);
1004 non_client_view->SetFrameView(frame_view);
1005 widget.Show();
1006 widget.Minimize();
1007 EXPECT_EQ(0, frame_view->width());
1008 }
1009
1010 // This class validates whether paints are received for a visible Widget.
1011 // To achieve this it overrides the Show and Close methods on the Widget class
1012 // and sets state whether subsequent paints are expected.
1013 class DesktopAuraTestValidPaintWidget : public views::Widget {
1014 public:
DesktopAuraTestValidPaintWidget()1015 DesktopAuraTestValidPaintWidget()
1016 : expect_paint_(true),
1017 received_paint_while_hidden_(false) {
1018 }
1019
~DesktopAuraTestValidPaintWidget()1020 virtual ~DesktopAuraTestValidPaintWidget() {
1021 }
1022
Show()1023 virtual void Show() OVERRIDE {
1024 expect_paint_ = true;
1025 views::Widget::Show();
1026 }
1027
Close()1028 virtual void Close() OVERRIDE {
1029 expect_paint_ = false;
1030 views::Widget::Close();
1031 }
1032
Hide()1033 void Hide() {
1034 expect_paint_ = false;
1035 views::Widget::Hide();
1036 }
1037
OnNativeWidgetPaint(gfx::Canvas * canvas)1038 virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE {
1039 EXPECT_TRUE(expect_paint_);
1040 if (!expect_paint_)
1041 received_paint_while_hidden_ = true;
1042 views::Widget::OnNativeWidgetPaint(canvas);
1043 }
1044
received_paint_while_hidden() const1045 bool received_paint_while_hidden() const {
1046 return received_paint_while_hidden_;
1047 }
1048
1049 private:
1050 bool expect_paint_;
1051 bool received_paint_while_hidden_;
1052 };
1053
TEST_F(WidgetTest,DesktopNativeWidgetAuraNoPaintAfterCloseTest)1054 TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterCloseTest) {
1055 View* contents_view = new View;
1056 contents_view->SetFocusable(true);
1057 DesktopAuraTestValidPaintWidget widget;
1058 Widget::InitParams init_params =
1059 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1060 init_params.bounds = gfx::Rect(0, 0, 200, 200);
1061 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1062 init_params.native_widget = new DesktopNativeWidgetAura(&widget);
1063 widget.Init(init_params);
1064 widget.SetContentsView(contents_view);
1065 widget.Show();
1066 widget.Activate();
1067 RunPendingMessages();
1068 widget.SchedulePaintInRect(init_params.bounds);
1069 widget.Close();
1070 RunPendingMessages();
1071 EXPECT_FALSE(widget.received_paint_while_hidden());
1072 }
1073
TEST_F(WidgetTest,DesktopNativeWidgetAuraNoPaintAfterHideTest)1074 TEST_F(WidgetTest, DesktopNativeWidgetAuraNoPaintAfterHideTest) {
1075 View* contents_view = new View;
1076 contents_view->SetFocusable(true);
1077 DesktopAuraTestValidPaintWidget widget;
1078 Widget::InitParams init_params =
1079 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1080 init_params.bounds = gfx::Rect(0, 0, 200, 200);
1081 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1082 init_params.native_widget = new DesktopNativeWidgetAura(&widget);
1083 widget.Init(init_params);
1084 widget.SetContentsView(contents_view);
1085 widget.Show();
1086 widget.Activate();
1087 RunPendingMessages();
1088 widget.SchedulePaintInRect(init_params.bounds);
1089 widget.Hide();
1090 RunPendingMessages();
1091 EXPECT_FALSE(widget.received_paint_while_hidden());
1092 widget.Close();
1093 }
1094
1095 // This class provides functionality to create fullscreen and top level popup
1096 // windows. It additionally tests whether the destruction of these windows
1097 // occurs correctly in desktop AURA without crashing.
1098 // It provides facilities to test the following cases:-
1099 // 1. Child window destroyed which should lead to the destruction of the
1100 // parent.
1101 // 2. Parent window destroyed which should lead to the child being destroyed.
1102 class DesktopAuraTopLevelWindowTest
1103 : public views::TestViewsDelegate,
1104 public aura::WindowObserver {
1105 public:
DesktopAuraTopLevelWindowTest()1106 DesktopAuraTopLevelWindowTest()
1107 : top_level_widget_(NULL),
1108 owned_window_(NULL),
1109 owner_destroyed_(false),
1110 owned_window_destroyed_(false) {}
1111
~DesktopAuraTopLevelWindowTest()1112 virtual ~DesktopAuraTopLevelWindowTest() {
1113 EXPECT_TRUE(owner_destroyed_);
1114 EXPECT_TRUE(owned_window_destroyed_);
1115 top_level_widget_ = NULL;
1116 owned_window_ = NULL;
1117 }
1118
1119 // views::TestViewsDelegate overrides.
OnBeforeWidgetInit(Widget::InitParams * params,internal::NativeWidgetDelegate * delegate)1120 virtual void OnBeforeWidgetInit(
1121 Widget::InitParams* params,
1122 internal::NativeWidgetDelegate* delegate) OVERRIDE {
1123 if (!params->native_widget)
1124 params->native_widget = new views::DesktopNativeWidgetAura(delegate);
1125 }
1126
CreateTopLevelWindow(const gfx::Rect & bounds,bool fullscreen)1127 void CreateTopLevelWindow(const gfx::Rect& bounds, bool fullscreen) {
1128 Widget::InitParams init_params;
1129 init_params.type = Widget::InitParams::TYPE_WINDOW;
1130 init_params.bounds = bounds;
1131 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1132 init_params.layer_type = ui::LAYER_NOT_DRAWN;
1133 init_params.accept_events = fullscreen;
1134
1135 widget_.Init(init_params);
1136
1137 owned_window_ = new aura::Window(&child_window_delegate_);
1138 owned_window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
1139 owned_window_->SetName("TestTopLevelWindow");
1140 if (fullscreen) {
1141 owned_window_->SetProperty(aura::client::kShowStateKey,
1142 ui::SHOW_STATE_FULLSCREEN);
1143 } else {
1144 owned_window_->SetType(aura::client::WINDOW_TYPE_MENU);
1145 }
1146 owned_window_->Init(ui::LAYER_TEXTURED);
1147 aura::client::ParentWindowWithContext(
1148 owned_window_,
1149 widget_.GetNativeView()->GetRootWindow(),
1150 gfx::Rect(0, 0, 1900, 1600));
1151 owned_window_->Show();
1152 owned_window_->AddObserver(this);
1153
1154 ASSERT_TRUE(owned_window_->parent() != NULL);
1155 owned_window_->parent()->AddObserver(this);
1156
1157 top_level_widget_ =
1158 views::Widget::GetWidgetForNativeView(owned_window_->parent());
1159 ASSERT_TRUE(top_level_widget_ != NULL);
1160 }
1161
DestroyOwnedWindow()1162 void DestroyOwnedWindow() {
1163 ASSERT_TRUE(owned_window_ != NULL);
1164 delete owned_window_;
1165 }
1166
DestroyOwnerWindow()1167 void DestroyOwnerWindow() {
1168 ASSERT_TRUE(top_level_widget_ != NULL);
1169 top_level_widget_->CloseNow();
1170 }
1171
OnWindowDestroying(aura::Window * window)1172 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
1173 window->RemoveObserver(this);
1174 if (window == owned_window_) {
1175 owned_window_destroyed_ = true;
1176 } else if (window == top_level_widget_->GetNativeView()) {
1177 owner_destroyed_ = true;
1178 } else {
1179 ADD_FAILURE() << "Unexpected window destroyed callback: " << window;
1180 }
1181 }
1182
owned_window()1183 aura::Window* owned_window() {
1184 return owned_window_;
1185 }
1186
top_level_widget()1187 views::Widget* top_level_widget() {
1188 return top_level_widget_;
1189 }
1190
1191 private:
1192 views::Widget widget_;
1193 views::Widget* top_level_widget_;
1194 aura::Window* owned_window_;
1195 bool owner_destroyed_;
1196 bool owned_window_destroyed_;
1197 aura::test::TestWindowDelegate child_window_delegate_;
1198
1199 DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest);
1200 };
1201
TEST_F(WidgetTest,DesktopAuraFullscreenWindowDestroyedBeforeOwnerTest)1202 TEST_F(WidgetTest, DesktopAuraFullscreenWindowDestroyedBeforeOwnerTest) {
1203 ViewsDelegate::views_delegate = NULL;
1204 DesktopAuraTopLevelWindowTest fullscreen_window;
1205 ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
1206 gfx::Rect(0, 0, 200, 200), true));
1207
1208 RunPendingMessages();
1209 ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnedWindow());
1210 RunPendingMessages();
1211 }
1212
TEST_F(WidgetTest,DesktopAuraFullscreenWindowOwnerDestroyed)1213 TEST_F(WidgetTest, DesktopAuraFullscreenWindowOwnerDestroyed) {
1214 ViewsDelegate::views_delegate = NULL;
1215
1216 DesktopAuraTopLevelWindowTest fullscreen_window;
1217 ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
1218 gfx::Rect(0, 0, 200, 200), true));
1219
1220 RunPendingMessages();
1221 ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnerWindow());
1222 RunPendingMessages();
1223 }
1224
TEST_F(WidgetTest,DesktopAuraTopLevelOwnedPopupTest)1225 TEST_F(WidgetTest, DesktopAuraTopLevelOwnedPopupTest) {
1226 ViewsDelegate::views_delegate = NULL;
1227 DesktopAuraTopLevelWindowTest popup_window;
1228 ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
1229 gfx::Rect(0, 0, 200, 200), false));
1230
1231 RunPendingMessages();
1232 ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
1233 RunPendingMessages();
1234 }
1235
1236 #if defined(OS_WIN)
1237 // TODO(ananta)
1238 // Fix this test to work on Linux Aura. Need to implement the
1239 // views::DesktopRootWindowHostX11::SetSize function
1240 // This test validates that when a top level owned popup Aura window is
1241 // resized, the widget is resized as well.
TEST_F(WidgetTest,DesktopAuraTopLevelOwnedPopupResizeTest)1242 TEST_F(WidgetTest, DesktopAuraTopLevelOwnedPopupResizeTest) {
1243 ViewsDelegate::views_delegate = NULL;
1244 DesktopAuraTopLevelWindowTest popup_window;
1245 ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
1246 gfx::Rect(0, 0, 200, 200), false));
1247
1248 gfx::Rect new_size(0, 0, 400, 400);
1249 popup_window.owned_window()->SetBounds(new_size);
1250
1251 EXPECT_EQ(popup_window.top_level_widget()->GetNativeView()->bounds().size(),
1252 new_size.size());
1253 RunPendingMessages();
1254 ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
1255 RunPendingMessages();
1256 }
1257 #endif
1258
1259 // Test to ensure that the aura Window's visiblity state is set to visible if
1260 // the underlying widget is hidden and then shown.
TEST_F(WidgetTest,TestWindowVisibilityAfterHide)1261 TEST_F(WidgetTest, TestWindowVisibilityAfterHide) {
1262 // Create a widget.
1263 Widget widget;
1264 Widget::InitParams init_params =
1265 CreateParams(Widget::InitParams::TYPE_WINDOW);
1266 init_params.show_state = ui::SHOW_STATE_NORMAL;
1267 gfx::Rect initial_bounds(0, 0, 300, 400);
1268 init_params.bounds = initial_bounds;
1269 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1270 init_params.native_widget = new DesktopNativeWidgetAura(&widget);
1271 widget.Init(init_params);
1272 NonClientView* non_client_view = widget.non_client_view();
1273 NonClientFrameView* frame_view = new MinimumSizeFrameView(&widget);
1274 non_client_view->SetFrameView(frame_view);
1275
1276 widget.Hide();
1277 EXPECT_FALSE(widget.GetNativeView()->IsVisible());
1278 widget.Show();
1279 EXPECT_TRUE(widget.GetNativeView()->IsVisible());
1280 }
1281
1282 // The following code verifies we can correctly destroy a Widget from a mouse
1283 // enter/exit. We could test move/drag/enter/exit but in general we don't run
1284 // nested message loops from such events, nor has the code ever really dealt
1285 // with this situation.
1286
1287 // Class that closes the widget (which ends up deleting it immediately) when the
1288 // appropriate event is received.
1289 class CloseWidgetView : public View {
1290 public:
CloseWidgetView(ui::EventType event_type)1291 explicit CloseWidgetView(ui::EventType event_type)
1292 : event_type_(event_type) {
1293 }
1294
1295 // View overrides:
OnMousePressed(const ui::MouseEvent & event)1296 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
1297 if (!CloseWidget(event))
1298 View::OnMousePressed(event);
1299 return true;
1300 }
OnMouseDragged(const ui::MouseEvent & event)1301 virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE {
1302 if (!CloseWidget(event))
1303 View::OnMouseDragged(event);
1304 return true;
1305 }
OnMouseReleased(const ui::MouseEvent & event)1306 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
1307 if (!CloseWidget(event))
1308 View::OnMouseReleased(event);
1309 }
OnMouseMoved(const ui::MouseEvent & event)1310 virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE {
1311 if (!CloseWidget(event))
1312 View::OnMouseMoved(event);
1313 }
OnMouseEntered(const ui::MouseEvent & event)1314 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
1315 if (!CloseWidget(event))
1316 View::OnMouseEntered(event);
1317 }
1318
1319 private:
CloseWidget(const ui::LocatedEvent & event)1320 bool CloseWidget(const ui::LocatedEvent& event) {
1321 if (event.type() == event_type_) {
1322 // Go through NativeWidgetPrivate to simulate what happens if the OS
1323 // deletes the NativeWindow out from under us.
1324 GetWidget()->native_widget_private()->CloseNow();
1325 return true;
1326 }
1327 return false;
1328 }
1329
1330 const ui::EventType event_type_;
1331
1332 DISALLOW_COPY_AND_ASSIGN(CloseWidgetView);
1333 };
1334
1335 // Generates two moves (first generates enter, second real move), a press, drag
1336 // and release stopping at |last_event_type|.
GenerateMouseEvents(Widget * widget,ui::EventType last_event_type)1337 void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) {
1338 const gfx::Rect screen_bounds(widget->GetWindowBoundsInScreen());
1339 ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, screen_bounds.CenterPoint(),
1340 screen_bounds.CenterPoint(), 0);
1341 aura::RootWindowHostDelegate* rwhd =
1342 widget->GetNativeWindow()->GetDispatcher()->AsRootWindowHostDelegate();
1343 rwhd->OnHostMouseEvent(&move_event);
1344 if (last_event_type == ui::ET_MOUSE_ENTERED)
1345 return;
1346 rwhd->OnHostMouseEvent(&move_event);
1347 if (last_event_type == ui::ET_MOUSE_MOVED)
1348 return;
1349
1350 ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(),
1351 screen_bounds.CenterPoint(), 0);
1352 rwhd->OnHostMouseEvent(&press_event);
1353 if (last_event_type == ui::ET_MOUSE_PRESSED)
1354 return;
1355
1356 gfx::Point end_point(screen_bounds.CenterPoint());
1357 end_point.Offset(1, 1);
1358 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point, 0);
1359 rwhd->OnHostMouseEvent(&drag_event);
1360 if (last_event_type == ui::ET_MOUSE_DRAGGED)
1361 return;
1362
1363 ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point, 0);
1364 rwhd->OnHostMouseEvent(&release_event);
1365 }
1366
1367 // Creates a widget and invokes GenerateMouseEvents() with |last_event_type|.
RunCloseWidgetDuringDispatchTest(WidgetTest * test,ui::EventType last_event_type)1368 void RunCloseWidgetDuringDispatchTest(WidgetTest* test,
1369 ui::EventType last_event_type) {
1370 // |widget| is deleted by CloseWidgetView.
1371 Widget* widget = new Widget;
1372 Widget::InitParams params =
1373 test->CreateParams(Widget::InitParams::TYPE_POPUP);
1374 params.native_widget = new DesktopNativeWidgetAura(widget);
1375 params.bounds = gfx::Rect(0, 0, 50, 100);
1376 widget->Init(params);
1377 widget->SetContentsView(new CloseWidgetView(last_event_type));
1378 widget->Show();
1379 GenerateMouseEvents(widget, last_event_type);
1380 }
1381
1382 // Verifies deleting the widget from a mouse pressed event doesn't crash.
TEST_F(WidgetTest,CloseWidgetDuringMousePress)1383 TEST_F(WidgetTest, CloseWidgetDuringMousePress) {
1384 RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_PRESSED);
1385 }
1386
1387 // Verifies deleting the widget from a mouse released event doesn't crash.
TEST_F(WidgetTest,CloseWidgetDuringMouseReleased)1388 TEST_F(WidgetTest, CloseWidgetDuringMouseReleased) {
1389 RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED);
1390 }
1391
1392 #endif // !defined(OS_CHROMEOS)
1393
1394 // Tests that wheel events generated from scroll events are targetted to the
1395 // views under the cursor when the focused view does not processed them.
TEST_F(WidgetTest,WheelEventsFromScrollEventTarget)1396 TEST_F(WidgetTest, WheelEventsFromScrollEventTarget) {
1397 EventCountView* cursor_view = new EventCountView;
1398 cursor_view->SetBounds(60, 0, 50, 40);
1399
1400 Widget* widget = CreateTopLevelPlatformWidget();
1401 widget->GetRootView()->AddChildView(cursor_view);
1402
1403 // Generate a scroll event on the cursor view.
1404 ui::ScrollEvent scroll(ui::ET_SCROLL,
1405 gfx::Point(65, 5),
1406 ui::EventTimeForNow(),
1407 0,
1408 0, 20,
1409 0, 20,
1410 2);
1411 widget->OnScrollEvent(&scroll);
1412
1413 EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_SCROLL));
1414 EXPECT_EQ(1, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL));
1415
1416 cursor_view->ResetCounts();
1417
1418 ui::ScrollEvent scroll2(ui::ET_SCROLL,
1419 gfx::Point(5, 5),
1420 ui::EventTimeForNow(),
1421 0,
1422 0, 20,
1423 0, 20,
1424 2);
1425 widget->OnScrollEvent(&scroll2);
1426
1427 EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_SCROLL));
1428 EXPECT_EQ(0, cursor_view->GetEventCount(ui::ET_MOUSEWHEEL));
1429
1430 widget->CloseNow();
1431 }
1432
1433 #endif // defined(USE_AURA)
1434
1435 // Tests that if a scroll-begin gesture is not handled, then subsequent scroll
1436 // events are not dispatched to any view.
TEST_F(WidgetTest,GestureScrollEventDispatching)1437 TEST_F(WidgetTest, GestureScrollEventDispatching) {
1438 EventCountView* noscroll_view = new EventCountView;
1439 EventCountView* scroll_view = new ScrollableEventCountView;
1440
1441 noscroll_view->SetBounds(0, 0, 50, 40);
1442 scroll_view->SetBounds(60, 0, 40, 40);
1443
1444 Widget* widget = CreateTopLevelPlatformWidget();
1445 widget->GetRootView()->AddChildView(noscroll_view);
1446 widget->GetRootView()->AddChildView(scroll_view);
1447
1448 {
1449 ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN,
1450 5, 5, 0, base::TimeDelta(),
1451 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
1452 1);
1453 widget->OnGestureEvent(&begin);
1454 ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE,
1455 25, 15, 0, base::TimeDelta(),
1456 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10),
1457 1);
1458 widget->OnGestureEvent(&update);
1459 ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END,
1460 25, 15, 0, base::TimeDelta(),
1461 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0),
1462 1);
1463 widget->OnGestureEvent(&end);
1464
1465 EXPECT_EQ(1, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
1466 EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
1467 EXPECT_EQ(0, noscroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END));
1468 }
1469
1470 {
1471 ui::GestureEvent begin(ui::ET_GESTURE_SCROLL_BEGIN,
1472 65, 5, 0, base::TimeDelta(),
1473 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
1474 1);
1475 widget->OnGestureEvent(&begin);
1476 ui::GestureEvent update(ui::ET_GESTURE_SCROLL_UPDATE,
1477 85, 15, 0, base::TimeDelta(),
1478 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 20, 10),
1479 1);
1480 widget->OnGestureEvent(&update);
1481 ui::GestureEvent end(ui::ET_GESTURE_SCROLL_END,
1482 85, 15, 0, base::TimeDelta(),
1483 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0),
1484 1);
1485 widget->OnGestureEvent(&end);
1486
1487 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_BEGIN));
1488 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_UPDATE));
1489 EXPECT_EQ(1, scroll_view->GetEventCount(ui::ET_GESTURE_SCROLL_END));
1490 }
1491
1492 widget->CloseNow();
1493 }
1494
1495 // Tests that event-handlers installed on the RootView get triggered correctly.
TEST_F(WidgetTest,EventHandlersOnRootView)1496 TEST_F(WidgetTest, EventHandlersOnRootView) {
1497 Widget* widget = CreateTopLevelNativeWidget();
1498 View* root_view = widget->GetRootView();
1499
1500 EventCountView* view = new EventCountView;
1501 view->SetBounds(0, 0, 20, 20);
1502 root_view->AddChildView(view);
1503
1504 EventCountHandler h1;
1505 root_view->AddPreTargetHandler(&h1);
1506
1507 EventCountHandler h2;
1508 root_view->AddPostTargetHandler(&h2);
1509
1510 widget->SetBounds(gfx::Rect(0, 0, 100, 100));
1511 widget->Show();
1512
1513 ui::TouchEvent pressed(ui::ET_TOUCH_PRESSED,
1514 gfx::Point(10, 10),
1515 0, 0,
1516 ui::EventTimeForNow(),
1517 1.0, 0.0, 1.0, 0.0);
1518 widget->OnTouchEvent(&pressed);
1519 EXPECT_EQ(1, h1.GetEventCount(ui::ET_TOUCH_PRESSED));
1520 EXPECT_EQ(1, view->GetEventCount(ui::ET_TOUCH_PRESSED));
1521 EXPECT_EQ(1, h2.GetEventCount(ui::ET_TOUCH_PRESSED));
1522
1523 ui::GestureEvent begin(ui::ET_GESTURE_BEGIN,
1524 5, 5, 0, ui::EventTimeForNow(),
1525 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1);
1526 ui::GestureEvent end(ui::ET_GESTURE_END,
1527 5, 5, 0, ui::EventTimeForNow(),
1528 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1);
1529 widget->OnGestureEvent(&begin);
1530 EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_BEGIN));
1531 EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_BEGIN));
1532 EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_BEGIN));
1533
1534 ui::TouchEvent released(ui::ET_TOUCH_RELEASED,
1535 gfx::Point(10, 10),
1536 0, 0,
1537 ui::EventTimeForNow(),
1538 1.0, 0.0, 1.0, 0.0);
1539 widget->OnTouchEvent(&released);
1540 EXPECT_EQ(1, h1.GetEventCount(ui::ET_TOUCH_RELEASED));
1541 EXPECT_EQ(1, view->GetEventCount(ui::ET_TOUCH_RELEASED));
1542 EXPECT_EQ(1, h2.GetEventCount(ui::ET_TOUCH_RELEASED));
1543
1544 widget->OnGestureEvent(&end);
1545 EXPECT_EQ(1, h1.GetEventCount(ui::ET_GESTURE_END));
1546 EXPECT_EQ(1, view->GetEventCount(ui::ET_GESTURE_END));
1547 EXPECT_EQ(1, h2.GetEventCount(ui::ET_GESTURE_END));
1548
1549 ui::ScrollEvent scroll(ui::ET_SCROLL,
1550 gfx::Point(5, 5),
1551 ui::EventTimeForNow(),
1552 0,
1553 0, 20,
1554 0, 20,
1555 2);
1556 widget->OnScrollEvent(&scroll);
1557 EXPECT_EQ(1, h1.GetEventCount(ui::ET_SCROLL));
1558 EXPECT_EQ(1, view->GetEventCount(ui::ET_SCROLL));
1559 EXPECT_EQ(1, h2.GetEventCount(ui::ET_SCROLL));
1560
1561 widget->CloseNow();
1562 }
1563
TEST_F(WidgetTest,SynthesizeMouseMoveEvent)1564 TEST_F(WidgetTest, SynthesizeMouseMoveEvent) {
1565 Widget* widget = CreateTopLevelNativeWidget();
1566 View* root_view = widget->GetRootView();
1567
1568 EventCountView* v1 = new EventCountView();
1569 v1->SetBounds(0, 0, 10, 10);
1570 root_view->AddChildView(v1);
1571 EventCountView* v2 = new EventCountView();
1572 v2->SetBounds(0, 10, 10, 10);
1573 root_view->AddChildView(v2);
1574
1575 gfx::Point cursor_location(5, 5);
1576 ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
1577 ui::EF_NONE);
1578 widget->OnMouseEvent(&move);
1579
1580 EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED));
1581 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED));
1582
1583 delete v1;
1584 v2->SetBounds(0, 0, 10, 10);
1585 EXPECT_EQ(0, v2->GetEventCount(ui::ET_MOUSE_ENTERED));
1586
1587 widget->SynthesizeMouseMoveEvent();
1588 EXPECT_EQ(1, v2->GetEventCount(ui::ET_MOUSE_ENTERED));
1589 }
1590
1591 // Used by SingleWindowClosing to count number of times WindowClosing() has
1592 // been invoked.
1593 class ClosingDelegate : public WidgetDelegate {
1594 public:
ClosingDelegate()1595 ClosingDelegate() : count_(0), widget_(NULL) {}
1596
count() const1597 int count() const { return count_; }
1598
set_widget(views::Widget * widget)1599 void set_widget(views::Widget* widget) { widget_ = widget; }
1600
1601 // WidgetDelegate overrides:
GetWidget()1602 virtual Widget* GetWidget() OVERRIDE { return widget_; }
GetWidget() const1603 virtual const Widget* GetWidget() const OVERRIDE { return widget_; }
WindowClosing()1604 virtual void WindowClosing() OVERRIDE {
1605 count_++;
1606 }
1607
1608 private:
1609 int count_;
1610 views::Widget* widget_;
1611
1612 DISALLOW_COPY_AND_ASSIGN(ClosingDelegate);
1613 };
1614
1615 // Verifies WindowClosing() is invoked correctly on the delegate when a Widget
1616 // is closed.
TEST_F(WidgetTest,SingleWindowClosing)1617 TEST_F(WidgetTest, SingleWindowClosing) {
1618 scoped_ptr<ClosingDelegate> delegate(new ClosingDelegate());
1619 Widget* widget = new Widget(); // Destroyed by CloseNow() below.
1620 Widget::InitParams init_params =
1621 CreateParams(Widget::InitParams::TYPE_WINDOW);
1622 init_params.bounds = gfx::Rect(0, 0, 200, 200);
1623 init_params.delegate = delegate.get();
1624 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
1625 init_params.native_widget = new DesktopNativeWidgetAura(widget);
1626 #endif
1627 widget->Init(init_params);
1628 EXPECT_EQ(0, delegate->count());
1629 widget->CloseNow();
1630 EXPECT_EQ(1, delegate->count());
1631 }
1632
1633 class WidgetWindowTitleTest : public WidgetTest {
1634 protected:
RunTest(bool desktop_native_widget)1635 void RunTest(bool desktop_native_widget) {
1636 Widget* widget = new Widget(); // Destroyed by CloseNow() below.
1637 Widget::InitParams init_params =
1638 CreateParams(Widget::InitParams::TYPE_WINDOW);
1639 widget->Init(init_params);
1640
1641 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
1642 if (desktop_native_widget)
1643 init_params.native_widget = new DesktopNativeWidgetAura(widget);
1644 #else
1645 DCHECK(!desktop_native_widget)
1646 << "DesktopNativeWidget does not exist on non-Aura or on ChromeOS.";
1647 #endif
1648
1649 internal::NativeWidgetPrivate* native_widget =
1650 widget->native_widget_private();
1651
1652 string16 empty;
1653 string16 s1(UTF8ToUTF16("Title1"));
1654 string16 s2(UTF8ToUTF16("Title2"));
1655 string16 s3(UTF8ToUTF16("TitleLong"));
1656
1657 // The widget starts with no title, setting empty should not change
1658 // anything.
1659 EXPECT_FALSE(native_widget->SetWindowTitle(empty));
1660 // Setting the title to something non-empty should cause a change.
1661 EXPECT_TRUE(native_widget->SetWindowTitle(s1));
1662 // Setting the title to something else with the same length should cause a
1663 // change.
1664 EXPECT_TRUE(native_widget->SetWindowTitle(s2));
1665 // Setting the title to something else with a different length should cause
1666 // a change.
1667 EXPECT_TRUE(native_widget->SetWindowTitle(s3));
1668 // Setting the title to the same thing twice should not cause a change.
1669 EXPECT_FALSE(native_widget->SetWindowTitle(s3));
1670
1671 widget->CloseNow();
1672 }
1673 };
1674
TEST_F(WidgetWindowTitleTest,SetWindowTitleChanged_NativeWidget)1675 TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_NativeWidget) {
1676 // Use the default NativeWidget.
1677 bool desktop_native_widget = false;
1678 RunTest(desktop_native_widget);
1679 }
1680
1681 // DesktopNativeWidget does not exist on non-Aura or on ChromeOS.
1682 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
TEST_F(WidgetWindowTitleTest,SetWindowTitleChanged_DesktopNativeWidget)1683 TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_DesktopNativeWidget) {
1684 // Override to use a DesktopNativeWidget.
1685 bool desktop_native_widget = true;
1686 RunTest(desktop_native_widget);
1687 }
1688 #endif // USE_AURA && !OS_CHROMEOS
1689
1690 // Used by SetTopLevelCorrectly to track calls to OnBeforeWidgetInit().
1691 class VerifyTopLevelDelegate : public TestViewsDelegate {
1692 public:
VerifyTopLevelDelegate()1693 VerifyTopLevelDelegate()
1694 : on_before_init_called_(false),
1695 is_top_level_(false) {
1696 }
1697
on_before_init_called() const1698 bool on_before_init_called() const { return on_before_init_called_; }
is_top_level() const1699 bool is_top_level() const { return is_top_level_; }
1700
OnBeforeWidgetInit(Widget::InitParams * params,internal::NativeWidgetDelegate * delegate)1701 virtual void OnBeforeWidgetInit(
1702 Widget::InitParams* params,
1703 internal::NativeWidgetDelegate* delegate) OVERRIDE {
1704 on_before_init_called_ = true;
1705 is_top_level_ = params->top_level;
1706 }
1707
1708 private:
1709 bool on_before_init_called_;
1710 bool is_top_level_;
1711
1712 DISALLOW_COPY_AND_ASSIGN(VerifyTopLevelDelegate);
1713 };
1714
1715 // Verifies |top_level| is correctly passed to
1716 // ViewsDelegate::OnBeforeWidgetInit().
TEST_F(WidgetTest,SetTopLevelCorrectly)1717 TEST_F(WidgetTest, SetTopLevelCorrectly) {
1718 set_views_delegate(NULL);
1719 VerifyTopLevelDelegate* delegate = new VerifyTopLevelDelegate;
1720 set_views_delegate(delegate); // ViewsTestBase takes ownership.
1721 scoped_ptr<Widget> widget(new Widget);
1722 Widget::InitParams params =
1723 CreateParams(views::Widget::InitParams::TYPE_POPUP);
1724 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1725 widget->Init(params);
1726 EXPECT_TRUE(delegate->on_before_init_called());
1727 EXPECT_TRUE(delegate->is_top_level());
1728 }
1729
1730 // A scumbag View that deletes its owning widget OnMousePressed.
1731 class WidgetDeleterView : public View {
1732 public:
WidgetDeleterView()1733 WidgetDeleterView() : View() {}
1734
1735 // Overridden from View.
OnMousePressed(const ui::MouseEvent & event)1736 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
1737 delete GetWidget();
1738 return true;
1739 }
1740
1741 private:
1742 DISALLOW_COPY_AND_ASSIGN(WidgetDeleterView);
1743 };
1744
TEST_F(WidgetTest,TestWidgetDeletedInOnMousePressed)1745 TEST_F(WidgetTest, TestWidgetDeletedInOnMousePressed) {
1746 Widget* widget = new Widget;
1747 Widget::InitParams params =
1748 CreateParams(views::Widget::InitParams::TYPE_POPUP);
1749 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1750 widget->Init(params);
1751
1752 widget->SetContentsView(new WidgetDeleterView);
1753
1754 widget->SetSize(gfx::Size(100, 100));
1755 widget->Show();
1756
1757 gfx::Point click_location(45, 15);
1758 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
1759 ui::EF_LEFT_MOUSE_BUTTON);
1760 widget->OnMouseEvent(&press);
1761
1762 // Yay we did not crash!
1763 }
1764
1765 // See description of RunGetNativeThemeFromDestructor() for details.
1766 class GetNativeThemeFromDestructorView : public WidgetDelegateView {
1767 public:
GetNativeThemeFromDestructorView()1768 GetNativeThemeFromDestructorView() {}
~GetNativeThemeFromDestructorView()1769 virtual ~GetNativeThemeFromDestructorView() {
1770 VerifyNativeTheme();
1771 }
1772
GetContentsView()1773 virtual View* GetContentsView() OVERRIDE {
1774 return this;
1775 }
1776
1777 private:
VerifyNativeTheme()1778 void VerifyNativeTheme() {
1779 ASSERT_TRUE(GetNativeTheme() != NULL);
1780 }
1781
1782 DISALLOW_COPY_AND_ASSIGN(GetNativeThemeFromDestructorView);
1783 };
1784
1785 // Verifies GetNativeTheme() from the destructor of a WidgetDelegateView doesn't
1786 // crash. |is_first_run| is true if this is the first call. A return value of
1787 // true indicates this should be run again with a value of false.
1788 // First run uses DesktopNativeWidgetAura (if possible). Second run doesn't.
RunGetNativeThemeFromDestructor(const Widget::InitParams & in_params,bool is_first_run)1789 bool RunGetNativeThemeFromDestructor(const Widget::InitParams& in_params,
1790 bool is_first_run) {
1791 bool needs_second_run = false;
1792 // Destroyed by CloseNow() below.
1793 Widget* widget = new Widget;
1794 Widget::InitParams params(in_params);
1795 // Deletes itself when the Widget is destroyed.
1796 params.delegate = new GetNativeThemeFromDestructorView;
1797 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
1798 if (is_first_run) {
1799 params.native_widget = new DesktopNativeWidgetAura(widget);
1800 needs_second_run = true;
1801 }
1802 #endif
1803 widget->Init(params);
1804 widget->CloseNow();
1805 return needs_second_run;
1806 }
1807
1808 // See description of RunGetNativeThemeFromDestructor() for details.
TEST_F(WidgetTest,GetNativeThemeFromDestructor)1809 TEST_F(WidgetTest, GetNativeThemeFromDestructor) {
1810 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
1811 if (RunGetNativeThemeFromDestructor(params, true))
1812 RunGetNativeThemeFromDestructor(params, false);
1813 }
1814
1815 // Used by HideCloseDestroy. Allows setting a boolean when the widget is
1816 // destroyed.
1817 class CloseDestroysWidget : public Widget {
1818 public:
CloseDestroysWidget(bool * destroyed)1819 explicit CloseDestroysWidget(bool* destroyed)
1820 : destroyed_(destroyed) {
1821 }
1822
~CloseDestroysWidget()1823 virtual ~CloseDestroysWidget() {
1824 if (destroyed_) {
1825 *destroyed_ = true;
1826 base::MessageLoop::current()->QuitNow();
1827 }
1828 }
1829
Detach()1830 void Detach() { destroyed_ = NULL; }
1831
1832 private:
1833 // If non-null set to true from destructor.
1834 bool* destroyed_;
1835
1836 DISALLOW_COPY_AND_ASSIGN(CloseDestroysWidget);
1837 };
1838
1839 // Verifies Close() results in destroying.
TEST_F(WidgetTest,CloseDestroys)1840 TEST_F(WidgetTest, CloseDestroys) {
1841 bool destroyed = false;
1842 CloseDestroysWidget* widget = new CloseDestroysWidget(&destroyed);
1843 Widget::InitParams params =
1844 CreateParams(views::Widget::InitParams::TYPE_MENU);
1845 params.opacity = Widget::InitParams::OPAQUE_WINDOW;
1846 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
1847 params.native_widget = new DesktopNativeWidgetAura(widget);
1848 #endif
1849 widget->Init(params);
1850 widget->Show();
1851 widget->Hide();
1852 widget->Close();
1853 // Run the message loop as Close() asynchronously deletes.
1854 RunPendingMessages();
1855 EXPECT_TRUE(destroyed);
1856 // Close() should destroy the widget. If not we'll cleanup to avoid leaks.
1857 if (!destroyed) {
1858 widget->Detach();
1859 widget->CloseNow();
1860 }
1861 }
1862
1863 // A view that consumes mouse-pressed event and gesture-tap-down events.
1864 class RootViewTestView : public View {
1865 public:
RootViewTestView()1866 RootViewTestView(): View() {}
1867
1868 private:
OnMousePressed(const ui::MouseEvent & event)1869 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
1870 return true;
1871 }
1872
OnGestureEvent(ui::GestureEvent * event)1873 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
1874 if (event->type() == ui::ET_GESTURE_TAP_DOWN)
1875 event->SetHandled();
1876 }
1877 };
1878
1879 // Checks if RootView::*_handler_ fields are unset when widget is hidden.
1880 // Fails on chromium.webkit Windows bot, see crbug.com/264872.
1881 #if defined(OS_WIN)
1882 #define MAYBE_DisableTestRootViewHandlersWhenHidden\
1883 DISABLED_TestRootViewHandlersWhenHidden
1884 #else
1885 #define MAYBE_DisableTestRootViewHandlersWhenHidden\
1886 TestRootViewHandlersWhenHidden
1887 #endif
TEST_F(WidgetTest,MAYBE_DisableTestRootViewHandlersWhenHidden)1888 TEST_F(WidgetTest, MAYBE_DisableTestRootViewHandlersWhenHidden) {
1889 Widget* widget = CreateTopLevelNativeWidget();
1890 widget->SetBounds(gfx::Rect(0, 0, 300, 300));
1891 View* view = new RootViewTestView();
1892 view->SetBounds(0, 0, 300, 300);
1893 internal::RootView* root_view =
1894 static_cast<internal::RootView*>(widget->GetRootView());
1895 root_view->AddChildView(view);
1896
1897 // Check RootView::mouse_pressed_handler_.
1898 widget->Show();
1899 EXPECT_EQ(NULL, GetMousePressedHandler(root_view));
1900 gfx::Point click_location(45, 15);
1901 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
1902 ui::EF_LEFT_MOUSE_BUTTON);
1903 widget->OnMouseEvent(&press);
1904 EXPECT_EQ(view, GetMousePressedHandler(root_view));
1905 widget->Hide();
1906 EXPECT_EQ(NULL, GetMousePressedHandler(root_view));
1907
1908 // Check RootView::mouse_move_handler_.
1909 widget->Show();
1910 EXPECT_EQ(NULL, GetMouseMoveHandler(root_view));
1911 gfx::Point move_location(45, 15);
1912 ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location, 0);
1913 widget->OnMouseEvent(&move);
1914 EXPECT_EQ(view, GetMouseMoveHandler(root_view));
1915 widget->Hide();
1916 EXPECT_EQ(NULL, GetMouseMoveHandler(root_view));
1917
1918 // Check RootView::gesture_handler_.
1919 widget->Show();
1920 EXPECT_EQ(NULL, GetGestureHandler(root_view));
1921 ui::GestureEvent tap_down(
1922 ui::ET_GESTURE_TAP_DOWN,
1923 15,
1924 15,
1925 0,
1926 base::TimeDelta(),
1927 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0),
1928 1);
1929 widget->OnGestureEvent(&tap_down);
1930 EXPECT_EQ(view, GetGestureHandler(root_view));
1931 widget->Hide();
1932 EXPECT_EQ(NULL, GetGestureHandler(root_view));
1933
1934 widget->Close();
1935 }
1936
1937 // Test the result of Widget::GetAllChildWidgets().
TEST_F(WidgetTest,GetAllChildWidgets)1938 TEST_F(WidgetTest, GetAllChildWidgets) {
1939 // Create the following widget hierarchy:
1940 //
1941 // toplevel
1942 // +-- w1
1943 // +-- w11
1944 // +-- w2
1945 // +-- w21
1946 // +-- w22
1947 Widget* toplevel = CreateTopLevelPlatformWidget();
1948 Widget* w1 = CreateChildPlatformWidget(toplevel->GetNativeView());
1949 Widget* w11 = CreateChildPlatformWidget(w1->GetNativeView());
1950 Widget* w2 = CreateChildPlatformWidget(toplevel->GetNativeView());
1951 Widget* w21 = CreateChildPlatformWidget(w2->GetNativeView());
1952 Widget* w22 = CreateChildPlatformWidget(w2->GetNativeView());
1953
1954 std::set<Widget*> expected;
1955 expected.insert(toplevel);
1956 expected.insert(w1);
1957 expected.insert(w11);
1958 expected.insert(w2);
1959 expected.insert(w21);
1960 expected.insert(w22);
1961
1962 std::set<Widget*> widgets;
1963 Widget::GetAllChildWidgets(toplevel->GetNativeView(), &widgets);
1964
1965 EXPECT_EQ(expected.size(), widgets.size());
1966 EXPECT_TRUE(std::equal(expected.begin(), expected.end(), widgets.begin()));
1967 }
1968
1969 // Used by DestroyChildWidgetsInOrder. On destruction adds the supplied name to
1970 // a vector.
1971 class DestroyedTrackingView : public View {
1972 public:
DestroyedTrackingView(const std::string & name,std::vector<std::string> * add_to)1973 DestroyedTrackingView(const std::string& name,
1974 std::vector<std::string>* add_to)
1975 : name_(name),
1976 add_to_(add_to) {
1977 }
1978
~DestroyedTrackingView()1979 virtual ~DestroyedTrackingView() {
1980 add_to_->push_back(name_);
1981 }
1982
1983 private:
1984 const std::string name_;
1985 std::vector<std::string>* add_to_;
1986
1987 DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingView);
1988 };
1989
1990 class WidgetChildDestructionTest : public WidgetTest {
1991 public:
WidgetChildDestructionTest()1992 WidgetChildDestructionTest() {}
1993
1994 // Creates a top level and a child, destroys the child and verifies the views
1995 // of the child are destroyed before the views of the parent.
RunDestroyChildWidgetsTest(bool top_level_has_desktop_native_widget_aura,bool child_has_desktop_native_widget_aura)1996 void RunDestroyChildWidgetsTest(bool top_level_has_desktop_native_widget_aura,
1997 bool child_has_desktop_native_widget_aura) {
1998 // When a View is destroyed its name is added here.
1999 std::vector<std::string> destroyed;
2000
2001 Widget* top_level = new Widget;
2002 Widget::InitParams params =
2003 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
2004 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
2005 if (top_level_has_desktop_native_widget_aura)
2006 params.native_widget = new DesktopNativeWidgetAura(top_level);
2007 #endif
2008 top_level->Init(params);
2009 top_level->GetRootView()->AddChildView(
2010 new DestroyedTrackingView("parent", &destroyed));
2011 top_level->Show();
2012
2013 Widget* child = new Widget;
2014 Widget::InitParams child_params =
2015 CreateParams(views::Widget::InitParams::TYPE_POPUP);
2016 child_params.parent = top_level->GetNativeView();
2017 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
2018 if (child_has_desktop_native_widget_aura)
2019 child_params.native_widget = new DesktopNativeWidgetAura(child);
2020 #endif
2021 child->Init(child_params);
2022 child->GetRootView()->AddChildView(
2023 new DestroyedTrackingView("child", &destroyed));
2024 child->Show();
2025
2026 // Should trigger destruction of the child too.
2027 top_level->native_widget_private()->CloseNow();
2028
2029 // Child should be destroyed first.
2030 ASSERT_EQ(2u, destroyed.size());
2031 EXPECT_EQ("child", destroyed[0]);
2032 EXPECT_EQ("parent", destroyed[1]);
2033 }
2034
2035 private:
2036 DISALLOW_COPY_AND_ASSIGN(WidgetChildDestructionTest);
2037 };
2038
2039 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
2040 // See description of RunDestroyChildWidgetsTest(). Parent uses
2041 // DesktopNativeWidgetAura.
TEST_F(WidgetChildDestructionTest,DestroyChildWidgetsInOrderWithDesktopNativeWidget)2042 TEST_F(WidgetChildDestructionTest,
2043 DestroyChildWidgetsInOrderWithDesktopNativeWidget) {
2044 RunDestroyChildWidgetsTest(true, false);
2045 }
2046
2047 // See description of RunDestroyChildWidgetsTest(). Both parent and child use
2048 // DesktopNativeWidgetAura.
TEST_F(WidgetChildDestructionTest,DestroyChildWidgetsInOrderWithDesktopNativeWidgetForBoth)2049 TEST_F(WidgetChildDestructionTest,
2050 DestroyChildWidgetsInOrderWithDesktopNativeWidgetForBoth) {
2051 RunDestroyChildWidgetsTest(true, true);
2052 }
2053 #endif
2054
2055 // See description of RunDestroyChildWidgetsTest().
TEST_F(WidgetChildDestructionTest,DestroyChildWidgetsInOrder)2056 TEST_F(WidgetChildDestructionTest, DestroyChildWidgetsInOrder) {
2057 RunDestroyChildWidgetsTest(false, false);
2058 }
2059
2060 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
2061 // Provides functionality to create a window modal dialog.
2062 class ModalDialogDelegate : public DialogDelegateView {
2063 public:
ModalDialogDelegate()2064 ModalDialogDelegate() {}
~ModalDialogDelegate()2065 virtual ~ModalDialogDelegate() {}
2066
2067 // WidgetDelegate overrides.
GetModalType() const2068 virtual ui::ModalType GetModalType() const OVERRIDE {
2069 return ui::MODAL_TYPE_WINDOW;
2070 }
2071
2072 private:
2073 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
2074 };
2075
2076 // This test verifies that whether mouse events when a modal dialog is
2077 // displayed are eaten or recieved by the dialog.
TEST_F(WidgetTest,WindowMouseModalityTest)2078 TEST_F(WidgetTest, WindowMouseModalityTest) {
2079 // Create a top level widget.
2080 Widget top_level_widget;
2081 Widget::InitParams init_params =
2082 CreateParams(Widget::InitParams::TYPE_WINDOW);
2083 init_params.show_state = ui::SHOW_STATE_NORMAL;
2084 gfx::Rect initial_bounds(0, 0, 500, 500);
2085 init_params.bounds = initial_bounds;
2086 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
2087 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
2088 top_level_widget.Init(init_params);
2089 top_level_widget.Show();
2090 EXPECT_TRUE(top_level_widget.IsVisible());
2091
2092 // Create a view and validate that a mouse moves makes it to the view.
2093 EventCountView* widget_view = new EventCountView();
2094 widget_view->SetBounds(0, 0, 10, 10);
2095 top_level_widget.GetRootView()->AddChildView(widget_view);
2096
2097 gfx::Point cursor_location_main(5, 5);
2098 ui::MouseEvent move_main(ui::ET_MOUSE_MOVED,
2099 cursor_location_main,
2100 cursor_location_main,
2101 ui::EF_NONE);
2102 top_level_widget.GetNativeView()->GetDispatcher()->
2103 AsRootWindowHostDelegate()->OnHostMouseEvent(&move_main);
2104
2105 EXPECT_EQ(1, widget_view->GetEventCount(ui::ET_MOUSE_ENTERED));
2106 widget_view->ResetCounts();
2107
2108 // Create a modal dialog and validate that a mouse down message makes it to
2109 // the main view within the dialog.
2110
2111 // This instance will be destroyed when the dialog is destroyed.
2112 ModalDialogDelegate* dialog_delegate = new ModalDialogDelegate;
2113
2114 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
2115 dialog_delegate, NULL, top_level_widget.GetNativeWindow());
2116 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
2117 EventCountView* dialog_widget_view = new EventCountView();
2118 dialog_widget_view->SetBounds(0, 0, 50, 50);
2119 modal_dialog_widget->GetRootView()->AddChildView(dialog_widget_view);
2120 modal_dialog_widget->Show();
2121 EXPECT_TRUE(modal_dialog_widget->IsVisible());
2122
2123 gfx::Point cursor_location_dialog(100, 100);
2124 ui::MouseEvent mouse_down_dialog(ui::ET_MOUSE_PRESSED,
2125 cursor_location_dialog,
2126 cursor_location_dialog,
2127 ui::EF_NONE);
2128 top_level_widget.GetNativeView()->GetDispatcher()->
2129 AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse_down_dialog);
2130 EXPECT_EQ(1, dialog_widget_view->GetEventCount(ui::ET_MOUSE_PRESSED));
2131
2132 // Send a mouse move message to the main window. It should not be received by
2133 // the main window as the modal dialog is still active.
2134 gfx::Point cursor_location_main2(6, 6);
2135 ui::MouseEvent mouse_down_main(ui::ET_MOUSE_MOVED,
2136 cursor_location_main2,
2137 cursor_location_main2,
2138 ui::EF_NONE);
2139 top_level_widget.GetNativeView()->GetDispatcher()->
2140 AsRootWindowHostDelegate()->OnHostMouseEvent(&mouse_down_main);
2141 EXPECT_EQ(0, widget_view->GetEventCount(ui::ET_MOUSE_MOVED));
2142
2143 modal_dialog_widget->CloseNow();
2144 top_level_widget.CloseNow();
2145 }
2146
2147 #if defined(USE_AURA)
2148 // Verifies nativeview visbility matches that of Widget visibility when
2149 // SetFullscreen is invoked.
TEST_F(WidgetTest,FullscreenStatePropagated)2150 TEST_F(WidgetTest, FullscreenStatePropagated) {
2151 Widget::InitParams init_params =
2152 CreateParams(Widget::InitParams::TYPE_WINDOW);
2153 init_params.show_state = ui::SHOW_STATE_NORMAL;
2154 init_params.bounds = gfx::Rect(0, 0, 500, 500);
2155 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
2156
2157 {
2158 Widget top_level_widget;
2159 top_level_widget.Init(init_params);
2160 top_level_widget.SetFullscreen(true);
2161 EXPECT_EQ(top_level_widget.IsVisible(),
2162 top_level_widget.GetNativeView()->IsVisible());
2163 top_level_widget.CloseNow();
2164 }
2165
2166 #if !defined(OS_CHROMEOS)
2167 {
2168 Widget top_level_widget;
2169 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
2170 top_level_widget.Init(init_params);
2171 top_level_widget.SetFullscreen(true);
2172 EXPECT_EQ(top_level_widget.IsVisible(),
2173 top_level_widget.GetNativeView()->IsVisible());
2174 top_level_widget.CloseNow();
2175 }
2176 #endif
2177 }
2178 #endif
2179
2180 #if defined(OS_WIN)
2181
2182 // Provides functionality to test widget activation via an activation flag
2183 // which can be set by an accessor.
2184 class ModalWindowTestWidgetDelegate : public WidgetDelegate {
2185 public:
ModalWindowTestWidgetDelegate()2186 ModalWindowTestWidgetDelegate()
2187 : widget_(NULL),
2188 can_activate_(true) {}
2189
~ModalWindowTestWidgetDelegate()2190 virtual ~ModalWindowTestWidgetDelegate() {}
2191
2192 // Overridden from WidgetDelegate:
DeleteDelegate()2193 virtual void DeleteDelegate() OVERRIDE {
2194 delete this;
2195 }
GetWidget()2196 virtual Widget* GetWidget() OVERRIDE {
2197 return widget_;
2198 }
GetWidget() const2199 virtual const Widget* GetWidget() const OVERRIDE {
2200 return widget_;
2201 }
CanActivate() const2202 virtual bool CanActivate() const OVERRIDE {
2203 return can_activate_;
2204 }
ShouldAdvanceFocusToTopLevelWidget() const2205 virtual bool ShouldAdvanceFocusToTopLevelWidget() const OVERRIDE {
2206 return true;
2207 }
2208
set_can_activate(bool can_activate)2209 void set_can_activate(bool can_activate) {
2210 can_activate_ = can_activate;
2211 }
2212
set_widget(Widget * widget)2213 void set_widget(Widget* widget) {
2214 widget_ = widget;
2215 }
2216
2217 private:
2218 Widget* widget_;
2219 bool can_activate_;
2220
2221 DISALLOW_COPY_AND_ASSIGN(ModalWindowTestWidgetDelegate);
2222 };
2223
2224 // Tests whether we can activate the top level widget when a modal dialog is
2225 // active.
TEST_F(WidgetTest,WindowModalityActivationTest)2226 TEST_F(WidgetTest, WindowModalityActivationTest) {
2227 // Destroyed when the top level widget created below is destroyed.
2228 ModalWindowTestWidgetDelegate* widget_delegate =
2229 new ModalWindowTestWidgetDelegate;
2230 // Create a top level widget.
2231 Widget top_level_widget;
2232 Widget::InitParams init_params =
2233 CreateParams(Widget::InitParams::TYPE_WINDOW);
2234 init_params.show_state = ui::SHOW_STATE_NORMAL;
2235 gfx::Rect initial_bounds(0, 0, 500, 500);
2236 init_params.bounds = initial_bounds;
2237 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
2238 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
2239 init_params.delegate = widget_delegate;
2240 top_level_widget.Init(init_params);
2241 widget_delegate->set_widget(&top_level_widget);
2242 top_level_widget.Show();
2243 EXPECT_TRUE(top_level_widget.IsVisible());
2244
2245 HWND win32_window = views::HWNDForWidget(&top_level_widget);
2246 EXPECT_TRUE(::IsWindow(win32_window));
2247
2248 // This instance will be destroyed when the dialog is destroyed.
2249 ModalDialogDelegate* dialog_delegate = new ModalDialogDelegate;
2250
2251 // We should be able to activate the window even if the WidgetDelegate
2252 // says no, when a modal dialog is active.
2253 widget_delegate->set_can_activate(false);
2254
2255 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
2256 dialog_delegate, NULL, top_level_widget.GetNativeWindow());
2257 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
2258 modal_dialog_widget->Show();
2259 EXPECT_TRUE(modal_dialog_widget->IsVisible());
2260
2261 LRESULT activate_result = ::SendMessage(
2262 win32_window,
2263 WM_MOUSEACTIVATE,
2264 reinterpret_cast<WPARAM>(win32_window),
2265 MAKELPARAM(WM_LBUTTONDOWN, HTCLIENT));
2266 EXPECT_EQ(activate_result, MA_ACTIVATE);
2267
2268 modal_dialog_widget->CloseNow();
2269 top_level_widget.CloseNow();
2270 }
2271 #endif
2272 #endif
2273
2274 namespace {
2275
2276 class FullscreenAwareFrame : public views::NonClientFrameView {
2277 public:
FullscreenAwareFrame(views::Widget * widget)2278 explicit FullscreenAwareFrame(views::Widget* widget)
2279 : widget_(widget), fullscreen_layout_called_(false) {}
~FullscreenAwareFrame()2280 virtual ~FullscreenAwareFrame() {}
2281
2282 // views::NonClientFrameView overrides:
GetBoundsForClientView() const2283 virtual gfx::Rect GetBoundsForClientView() const OVERRIDE {
2284 return gfx::Rect();
2285 }
GetWindowBoundsForClientBounds(const gfx::Rect & client_bounds) const2286 virtual gfx::Rect GetWindowBoundsForClientBounds(
2287 const gfx::Rect& client_bounds) const OVERRIDE {
2288 return gfx::Rect();
2289 }
NonClientHitTest(const gfx::Point & point)2290 virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
2291 return HTNOWHERE;
2292 }
GetWindowMask(const gfx::Size & size,gfx::Path * window_mask)2293 virtual void GetWindowMask(const gfx::Size& size,
2294 gfx::Path* window_mask) OVERRIDE {}
ResetWindowControls()2295 virtual void ResetWindowControls() OVERRIDE {}
UpdateWindowIcon()2296 virtual void UpdateWindowIcon() OVERRIDE {}
UpdateWindowTitle()2297 virtual void UpdateWindowTitle() OVERRIDE {}
2298
2299 // views::View overrides:
Layout()2300 virtual void Layout() OVERRIDE {
2301 if (widget_->IsFullscreen())
2302 fullscreen_layout_called_ = true;
2303 }
2304
fullscreen_layout_called() const2305 bool fullscreen_layout_called() const { return fullscreen_layout_called_; }
2306
2307 private:
2308 views::Widget* widget_;
2309 bool fullscreen_layout_called_;
2310
2311 DISALLOW_COPY_AND_ASSIGN(FullscreenAwareFrame);
2312 };
2313
2314 } // namespace
2315
2316 // Tests that frame Layout is called when a widget goes fullscreen without
2317 // changing its size or title.
TEST_F(WidgetTest,FullscreenFrameLayout)2318 TEST_F(WidgetTest, FullscreenFrameLayout) {
2319 Widget* widget = CreateTopLevelPlatformWidget();
2320 FullscreenAwareFrame* frame = new FullscreenAwareFrame(widget);
2321 widget->non_client_view()->SetFrameView(frame); // Owns |frame|.
2322
2323 widget->Maximize();
2324 RunPendingMessages();
2325
2326 EXPECT_FALSE(frame->fullscreen_layout_called());
2327 widget->SetFullscreen(true);
2328 widget->Show();
2329 RunPendingMessages();
2330 EXPECT_TRUE(frame->fullscreen_layout_called());
2331
2332 widget->CloseNow();
2333 }
2334
2335 #if !defined(OS_CHROMEOS)
2336 namespace {
2337
2338 // Trivial WidgetObserverTest that invokes Widget::IsActive() from
2339 // OnWindowDestroying.
2340 class IsActiveFromDestroyObserver : public WidgetObserver {
2341 public:
IsActiveFromDestroyObserver()2342 IsActiveFromDestroyObserver() {}
~IsActiveFromDestroyObserver()2343 virtual ~IsActiveFromDestroyObserver() {}
OnWidgetDestroying(Widget * widget)2344 virtual void OnWidgetDestroying(Widget* widget) OVERRIDE {
2345 widget->IsActive();
2346 }
2347
2348 private:
2349 DISALLOW_COPY_AND_ASSIGN(IsActiveFromDestroyObserver);
2350 };
2351
2352 } // namespace
2353
2354 // Verifies Widget::IsActive() invoked from
2355 // WidgetObserver::OnWidgetDestroying() in a child widget doesn't crash.
TEST_F(WidgetTest,IsActiveFromDestroy)2356 TEST_F(WidgetTest, IsActiveFromDestroy) {
2357 // Create two widgets, one a child of the other.
2358 IsActiveFromDestroyObserver observer;
2359 Widget parent_widget;
2360 Widget::InitParams parent_params =
2361 CreateParams(Widget::InitParams::TYPE_POPUP);
2362 parent_params.native_widget = new DesktopNativeWidgetAura(&parent_widget);
2363 parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
2364 parent_widget.Init(parent_params);
2365 parent_widget.Show();
2366
2367 Widget child_widget;
2368 Widget::InitParams child_params =
2369 CreateParams(Widget::InitParams::TYPE_POPUP);
2370 child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
2371 child_params.context = parent_widget.GetNativeView();
2372 child_widget.Init(child_params);
2373 child_widget.AddObserver(&observer);
2374 child_widget.Show();
2375
2376 parent_widget.CloseNow();
2377 }
2378 #endif
2379
2380 } // namespace test
2381 } // namespace views
2382