Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(229)

Side by Side Diff: views/focus/focus_manager_unittest.cc

Issue 8588064: views: Move bubble, events, focus and layout to ui/views/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/focus/focus_manager_factory.cc ('k') | views/focus/focus_search.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "base/logging.h"
6 #include "base/string16.h"
7 #include "base/string_number_conversions.h"
8 #include "base/utf_string_conversions.h"
9 #include "third_party/skia/include/core/SkColor.h"
10 #include "ui/base/keycodes/keyboard_codes.h"
11 #include "ui/base/models/accelerator.h"
12 #include "ui/base/models/combobox_model.h"
13 #include "ui/gfx/rect.h"
14 #include "ui/views/test/views_test_base.h"
15 #include "ui/views/window/non_client_view.h"
16 #include "views/background.h"
17 #include "views/border.h"
18 #include "views/controls/button/checkbox.h"
19 #include "views/controls/button/radio_button.h"
20 #include "views/controls/combobox/combobox.h"
21 #include "views/controls/combobox/native_combobox_wrapper.h"
22 #include "views/controls/label.h"
23 #include "views/controls/link.h"
24 #include "views/controls/native/native_view_host.h"
25 #include "views/controls/scroll_view.h"
26 #include "views/controls/tabbed_pane/native_tabbed_pane_wrapper.h"
27 #include "views/controls/tabbed_pane/tabbed_pane.h"
28 #include "views/controls/textfield/textfield.h"
29 #include "views/focus/accelerator_handler.h"
30 #include "views/focus/focus_manager_factory.h"
31 #include "views/widget/root_view.h"
32 #include "views/widget/widget.h"
33 #include "views/widget/widget_delegate.h"
34
35 #if defined(OS_LINUX)
36 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
37 #endif
38
39 namespace {
40 const int kWindowWidth = 600;
41 const int kWindowHeight = 500;
42
43 int count = 1;
44
45 const int kTopCheckBoxID = count++; // 1
46 const int kLeftContainerID = count++;
47 const int kAppleLabelID = count++;
48 const int kAppleTextfieldID = count++;
49 const int kOrangeLabelID = count++; // 5
50 const int kOrangeTextfieldID = count++;
51 const int kBananaLabelID = count++;
52 const int kBananaTextfieldID = count++;
53 const int kKiwiLabelID = count++;
54 const int kKiwiTextfieldID = count++; // 10
55 const int kFruitButtonID = count++;
56 const int kFruitCheckBoxID = count++;
57 const int kComboboxID = count++;
58
59 const int kRightContainerID = count++;
60 const int kAsparagusButtonID = count++; // 15
61 const int kBroccoliButtonID = count++;
62 const int kCauliflowerButtonID = count++;
63
64 const int kInnerContainerID = count++;
65 const int kScrollViewID = count++;
66 const int kRosettaLinkID = count++; // 20
67 const int kStupeurEtTremblementLinkID = count++;
68 const int kDinerGameLinkID = count++;
69 const int kRidiculeLinkID = count++;
70 const int kClosetLinkID = count++;
71 const int kVisitingLinkID = count++; // 25
72 const int kAmelieLinkID = count++;
73 const int kJoyeuxNoelLinkID = count++;
74 const int kCampingLinkID = count++;
75 const int kBriceDeNiceLinkID = count++;
76 const int kTaxiLinkID = count++; // 30
77 const int kAsterixLinkID = count++;
78
79 const int kOKButtonID = count++;
80 const int kCancelButtonID = count++;
81 const int kHelpButtonID = count++;
82
83 const int kStyleContainerID = count++; // 35
84 const int kBoldCheckBoxID = count++;
85 const int kItalicCheckBoxID = count++;
86 const int kUnderlinedCheckBoxID = count++;
87 const int kStyleHelpLinkID = count++;
88 const int kStyleTextEditID = count++; // 40
89
90 const int kSearchContainerID = count++;
91 const int kSearchTextfieldID = count++;
92 const int kSearchButtonID = count++;
93 const int kHelpLinkID = count++;
94
95 const int kThumbnailContainerID = count++; // 45
96 const int kThumbnailStarID = count++;
97 const int kThumbnailSuperStarID = count++;
98
99 } // namespace
100
101 namespace views {
102
103 class FocusManagerTest : public ViewsTestBase, public WidgetDelegate {
104 public:
105 FocusManagerTest()
106 : window_(NULL),
107 content_view_(NULL),
108 focus_change_listener_(NULL) {
109 }
110
111 ~FocusManagerTest() {
112 }
113
114 virtual void SetUp() OVERRIDE {
115 ViewsTestBase::SetUp();
116 window_ = Widget::CreateWindowWithBounds(this, bounds());
117 InitContentView();
118 window_->Show();
119 }
120
121 virtual void TearDown() OVERRIDE {
122 if (focus_change_listener_)
123 GetFocusManager()->RemoveFocusChangeListener(focus_change_listener_);
124 window_->Close();
125
126 // Flush the message loop to make application verifiers happy.
127 RunPendingMessages();
128 ViewsTestBase::TearDown();
129 }
130
131 FocusManager* GetFocusManager() {
132 return window_->GetFocusManager();
133 }
134
135 void FocusNativeView(gfx::NativeView native_view) {
136 #if defined(USE_AURA)
137 NOTIMPLEMENTED();
138 #elif defined(OS_WIN)
139 ::SendMessage(native_view, WM_SETFOCUS, NULL, NULL);
140 #else
141 gint return_val;
142 GdkEventFocus event;
143 event.type = GDK_FOCUS_CHANGE;
144 event.window =
145 gtk_widget_get_root_window(GTK_WIDGET(window_->GetNativeWindow()));
146 event.send_event = TRUE;
147 event.in = TRUE;
148 gtk_signal_emit_by_name(GTK_OBJECT(native_view), "focus-in-event",
149 &event, &return_val);
150 #endif
151 }
152
153 // WidgetDelegate Implementation.
154 virtual View* GetContentsView() OVERRIDE {
155 if (!content_view_)
156 content_view_ = new View();
157 return content_view_;
158 }
159 virtual Widget* GetWidget() OVERRIDE {
160 return content_view_->GetWidget();
161 }
162 virtual const Widget* GetWidget() const OVERRIDE {
163 return content_view_->GetWidget();
164 }
165
166 virtual void InitContentView() {
167 }
168
169 protected:
170 virtual gfx::Rect bounds() {
171 return gfx::Rect(0, 0, 500, 500);
172 }
173
174 // Mocks activating/deactivating the window.
175 void SimulateActivateWindow() {
176 #if defined(USE_AURA)
177 NOTIMPLEMENTED();
178 #elif defined(OS_WIN)
179 ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL);
180 #else
181 gboolean result;
182 g_signal_emit_by_name(G_OBJECT(window_->GetNativeWindow()),
183 "focus_in_event", 0, &result);
184 #endif
185 }
186 void SimulateDeactivateWindow() {
187 #if defined(USE_AURA)
188 NOTIMPLEMENTED();
189 #elif defined(OS_WIN)
190 ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_INACTIVE, NULL);
191 #else
192 gboolean result;
193 g_signal_emit_by_name(G_OBJECT(window_->GetNativeWindow()),
194 "focus_out_event", 0, & result);
195 #endif
196 }
197
198 Widget* window_;
199 View* content_view_;
200
201 void AddFocusChangeListener(FocusChangeListener* listener) {
202 ASSERT_FALSE(focus_change_listener_);
203 focus_change_listener_ = listener;
204 GetFocusManager()->AddFocusChangeListener(listener);
205 }
206
207 #if defined(USE_AURA)
208 void PostKeyDown(ui::KeyboardCode key_code) {
209 NOTIMPLEMENTED();
210 }
211
212 void PostKeyUp(ui::KeyboardCode key_code) {
213 NOTIMPLEMENTED();
214 }
215 #elif defined(OS_WIN)
216 void PostKeyDown(ui::KeyboardCode key_code) {
217 ::PostMessage(window_->GetNativeWindow(), WM_KEYDOWN, key_code, 0);
218 }
219
220 void PostKeyUp(ui::KeyboardCode key_code) {
221 ::PostMessage(window_->GetNativeWindow(), WM_KEYUP, key_code, 0);
222 }
223 #elif defined(OS_LINUX)
224 void PostKeyDown(ui::KeyboardCode key_code) {
225 PostKeyEvent(key_code, true);
226 }
227
228 void PostKeyUp(ui::KeyboardCode key_code) {
229 PostKeyEvent(key_code, false);
230 }
231
232 void PostKeyEvent(ui::KeyboardCode key_code, bool pressed) {
233 int keyval = GdkKeyCodeForWindowsKeyCode(key_code, false);
234 GdkKeymapKey* keys;
235 gint n_keys;
236 gdk_keymap_get_entries_for_keyval(
237 gdk_keymap_get_default(),
238 keyval,
239 &keys,
240 &n_keys);
241 GdkEvent* event = gdk_event_new(pressed ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
242 GdkEventKey* key_event = reinterpret_cast<GdkEventKey*>(event);
243 int modifier = 0;
244 if (pressed)
245 key_event->state = modifier | GDK_KEY_PRESS_MASK;
246 else
247 key_event->state = modifier | GDK_KEY_RELEASE_MASK;
248
249 key_event->window = GTK_WIDGET(window_->GetNativeWindow())->window;
250 DCHECK(key_event->window != NULL);
251 g_object_ref(key_event->window);
252 key_event->send_event = true;
253 key_event->time = GDK_CURRENT_TIME;
254 key_event->keyval = keyval;
255 key_event->hardware_keycode = keys[0].keycode;
256 key_event->group = keys[0].group;
257
258 g_free(keys);
259
260 gdk_event_put(event);
261 gdk_event_free(event);
262 }
263 #endif
264
265 private:
266 FocusChangeListener* focus_change_listener_;
267
268 DISALLOW_COPY_AND_ASSIGN(FocusManagerTest);
269 };
270
271 // BorderView is a view containing a native window with its own view hierarchy.
272 // It is interesting to test focus traversal from a view hierarchy to an inner
273 // view hierarchy.
274 class BorderView : public NativeViewHost {
275 public:
276 explicit BorderView(View* child) : child_(child), widget_(NULL) {
277 DCHECK(child);
278 set_focusable(false);
279 }
280
281 virtual ~BorderView() {}
282
283 virtual internal::RootView* GetContentsRootView() {
284 return static_cast<internal::RootView*>(widget_->GetRootView());
285 }
286
287 virtual FocusTraversable* GetFocusTraversable() {
288 return static_cast<internal::RootView*>(widget_->GetRootView());
289 }
290
291 virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child) {
292 NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
293
294 if (child == this && is_add) {
295 if (!widget_) {
296 widget_ = new Widget;
297 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
298 #if defined(OS_WIN)
299 params.parent = parent->GetWidget()->GetNativeView();
300 #elif defined(TOOLKIT_USES_GTK)
301 params.parent = native_view();
302 #else
303 NOTREACHED();
304 #endif
305 widget_->Init(params);
306 widget_->SetFocusTraversableParentView(this);
307 widget_->SetContentsView(child_);
308 }
309
310 // We have been added to a view hierarchy, attach the native view.
311 Attach(widget_->GetNativeView());
312 // Also update the FocusTraversable parent so the focus traversal works.
313 static_cast<internal::RootView*>(widget_->GetRootView())->
314 SetFocusTraversableParent(GetWidget()->GetFocusTraversable());
315 }
316 }
317
318 private:
319 View* child_;
320 Widget* widget_;
321
322 DISALLOW_COPY_AND_ASSIGN(BorderView);
323 };
324
325 class DummyComboboxModel : public ui::ComboboxModel {
326 public:
327 virtual int GetItemCount() { return 10; }
328
329 virtual string16 GetItemAt(int index) {
330 return ASCIIToUTF16("Item ") + base::IntToString16(index);
331 }
332 };
333
334 // A View that can act as a pane.
335 class PaneView : public View, public FocusTraversable {
336 public:
337 PaneView() : focus_search_(NULL) {}
338
339 // If this method is called, this view will use GetPaneFocusTraversable to
340 // have this provided FocusSearch used instead of the default one, allowing
341 // you to trap focus within the pane.
342 void EnablePaneFocus(FocusSearch* focus_search) {
343 focus_search_ = focus_search;
344 }
345
346 // Overridden from views::View:
347 virtual FocusTraversable* GetPaneFocusTraversable() {
348 if (focus_search_)
349 return this;
350 else
351 return NULL;
352 }
353
354 // Overridden from views::FocusTraversable:
355 virtual views::FocusSearch* GetFocusSearch() {
356 return focus_search_;
357 }
358 virtual FocusTraversable* GetFocusTraversableParent() {
359 return NULL;
360 }
361 virtual View* GetFocusTraversableParentView() {
362 return NULL;
363 }
364
365 private:
366 FocusSearch* focus_search_;
367 };
368
369 class FocusTraversalTest : public FocusManagerTest {
370 public:
371 ~FocusTraversalTest();
372
373 virtual void InitContentView();
374
375 protected:
376 FocusTraversalTest();
377
378 virtual gfx::Rect bounds() {
379 return gfx::Rect(0, 0, 600, 460);
380 }
381
382 View* FindViewByID(int id) {
383 View* view = GetContentsView()->GetViewByID(id);
384 if (view)
385 return view;
386 if (style_tab_)
387 view = style_tab_->GetSelectedTab()->GetViewByID(id);
388 if (view)
389 return view;
390 view = search_border_view_->GetContentsRootView()->GetViewByID(id);
391 if (view)
392 return view;
393 return NULL;
394 }
395
396 protected:
397 TabbedPane* style_tab_;
398 BorderView* search_border_view_;
399 DummyComboboxModel combobox_model_;
400 PaneView* left_container_;
401 PaneView* right_container_;
402
403 DISALLOW_COPY_AND_ASSIGN(FocusTraversalTest);
404 };
405
406 ////////////////////////////////////////////////////////////////////////////////
407 // FocusTraversalTest
408 ////////////////////////////////////////////////////////////////////////////////
409
410 FocusTraversalTest::FocusTraversalTest()
411 : style_tab_(NULL),
412 search_border_view_(NULL) {
413 }
414
415 FocusTraversalTest::~FocusTraversalTest() {
416 }
417
418 void FocusTraversalTest::InitContentView() {
419 // Create a complicated view hierarchy with lots of control types for
420 // use by all of the focus traversal tests.
421 //
422 // Class name, ID, and asterisk next to focusable views:
423 //
424 // View
425 // Checkbox * kTopCheckBoxID
426 // PaneView kLeftContainerID
427 // Label kAppleLabelID
428 // Textfield * kAppleTextfieldID
429 // Label kOrangeLabelID
430 // Textfield * kOrangeTextfieldID
431 // Label kBananaLabelID
432 // Textfield * kBananaTextfieldID
433 // Label kKiwiLabelID
434 // Textfield * kKiwiTextfieldID
435 // NativeButton * kFruitButtonID
436 // Checkbox * kFruitCheckBoxID
437 // Combobox * kComboboxID
438 // PaneView kRightContainerID
439 // RadioButton * kAsparagusButtonID
440 // RadioButton * kBroccoliButtonID
441 // RadioButton * kCauliflowerButtonID
442 // View kInnerContainerID
443 // ScrollView kScrollViewID
444 // View
445 // Link * kRosettaLinkID
446 // Link * kStupeurEtTremblementLinkID
447 // Link * kDinerGameLinkID
448 // Link * kRidiculeLinkID
449 // Link * kClosetLinkID
450 // Link * kVisitingLinkID
451 // Link * kAmelieLinkID
452 // Link * kJoyeuxNoelLinkID
453 // Link * kCampingLinkID
454 // Link * kBriceDeNiceLinkID
455 // Link * kTaxiLinkID
456 // Link * kAsterixLinkID
457 // NativeButton * kOKButtonID
458 // NativeButton * kCancelButtonID
459 // NativeButton * kHelpButtonID
460 // TabbedPane * kStyleContainerID
461 // View
462 // Checkbox * kBoldCheckBoxID
463 // Checkbox * kItalicCheckBoxID
464 // Checkbox * kUnderlinedCheckBoxID
465 // Link * kStyleHelpLinkID
466 // Textfield * kStyleTextEditID
467 // Other
468 // BorderView kSearchContainerID
469 // View
470 // Textfield * kSearchTextfieldID
471 // NativeButton * kSearchButtonID
472 // Link * kHelpLinkID
473 // View * kThumbnailContainerID
474 // NativeButton * kThumbnailStarID
475 // NativeButton * kThumbnailSuperStarID
476
477 content_view_->set_background(
478 Background::CreateSolidBackground(SK_ColorWHITE));
479
480 Checkbox* cb = new Checkbox(ASCIIToUTF16("This is a checkbox"));
481 content_view_->AddChildView(cb);
482 // In this fast paced world, who really has time for non hard-coded layout?
483 cb->SetBounds(10, 10, 200, 20);
484 cb->set_id(kTopCheckBoxID);
485
486 left_container_ = new PaneView();
487 left_container_->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK));
488 left_container_->set_background(
489 Background::CreateSolidBackground(240, 240, 240));
490 left_container_->set_id(kLeftContainerID);
491 content_view_->AddChildView(left_container_);
492 left_container_->SetBounds(10, 35, 250, 200);
493
494 int label_x = 5;
495 int label_width = 50;
496 int label_height = 15;
497 int text_field_width = 150;
498 int y = 10;
499 int gap_between_labels = 10;
500
501 Label* label = new Label(ASCIIToUTF16("Apple:"));
502 label->set_id(kAppleLabelID);
503 left_container_->AddChildView(label);
504 label->SetBounds(label_x, y, label_width, label_height);
505
506 Textfield* text_field = new Textfield();
507 text_field->set_id(kAppleTextfieldID);
508 left_container_->AddChildView(text_field);
509 text_field->SetBounds(label_x + label_width + 5, y,
510 text_field_width, label_height);
511
512 y += label_height + gap_between_labels;
513
514 label = new Label(ASCIIToUTF16("Orange:"));
515 label->set_id(kOrangeLabelID);
516 left_container_->AddChildView(label);
517 label->SetBounds(label_x, y, label_width, label_height);
518
519 text_field = new Textfield();
520 text_field->set_id(kOrangeTextfieldID);
521 left_container_->AddChildView(text_field);
522 text_field->SetBounds(label_x + label_width + 5, y,
523 text_field_width, label_height);
524
525 y += label_height + gap_between_labels;
526
527 label = new Label(ASCIIToUTF16("Banana:"));
528 label->set_id(kBananaLabelID);
529 left_container_->AddChildView(label);
530 label->SetBounds(label_x, y, label_width, label_height);
531
532 text_field = new Textfield();
533 text_field->set_id(kBananaTextfieldID);
534 left_container_->AddChildView(text_field);
535 text_field->SetBounds(label_x + label_width + 5, y,
536 text_field_width, label_height);
537
538 y += label_height + gap_between_labels;
539
540 label = new Label(ASCIIToUTF16("Kiwi:"));
541 label->set_id(kKiwiLabelID);
542 left_container_->AddChildView(label);
543 label->SetBounds(label_x, y, label_width, label_height);
544
545 text_field = new Textfield();
546 text_field->set_id(kKiwiTextfieldID);
547 left_container_->AddChildView(text_field);
548 text_field->SetBounds(label_x + label_width + 5, y,
549 text_field_width, label_height);
550
551 y += label_height + gap_between_labels;
552
553 NativeTextButton* button = new NativeTextButton(NULL,
554 ASCIIToUTF16("Click me"));
555 button->SetBounds(label_x, y + 10, 80, 30);
556 button->set_id(kFruitButtonID);
557 left_container_->AddChildView(button);
558 y += 40;
559
560 cb = new Checkbox(ASCIIToUTF16("This is another check box"));
561 cb->SetBounds(label_x + label_width + 5, y, 180, 20);
562 cb->set_id(kFruitCheckBoxID);
563 left_container_->AddChildView(cb);
564 y += 20;
565
566 Combobox* combobox = new Combobox(&combobox_model_);
567 combobox->SetBounds(label_x + label_width + 5, y, 150, 30);
568 combobox->set_id(kComboboxID);
569 left_container_->AddChildView(combobox);
570
571 right_container_ = new PaneView();
572 right_container_->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK));
573 right_container_->set_background(
574 Background::CreateSolidBackground(240, 240, 240));
575 right_container_->set_id(kRightContainerID);
576 content_view_->AddChildView(right_container_);
577 right_container_->SetBounds(270, 35, 300, 200);
578
579 y = 10;
580 int radio_button_height = 18;
581 int gap_between_radio_buttons = 10;
582 RadioButton* radio_button = new RadioButton(ASCIIToUTF16("Asparagus"), 1);
583 radio_button->set_id(kAsparagusButtonID);
584 right_container_->AddChildView(radio_button);
585 radio_button->SetBounds(5, y, 70, radio_button_height);
586 radio_button->SetGroup(1);
587 y += radio_button_height + gap_between_radio_buttons;
588 radio_button = new RadioButton(ASCIIToUTF16("Broccoli"), 1);
589 radio_button->set_id(kBroccoliButtonID);
590 right_container_->AddChildView(radio_button);
591 radio_button->SetBounds(5, y, 70, radio_button_height);
592 radio_button->SetGroup(1);
593 RadioButton* radio_button_to_check = radio_button;
594 y += radio_button_height + gap_between_radio_buttons;
595 radio_button = new RadioButton(ASCIIToUTF16("Cauliflower"), 1);
596 radio_button->set_id(kCauliflowerButtonID);
597 right_container_->AddChildView(radio_button);
598 radio_button->SetBounds(5, y, 70, radio_button_height);
599 radio_button->SetGroup(1);
600 y += radio_button_height + gap_between_radio_buttons;
601
602 View* inner_container = new View();
603 inner_container->set_border(Border::CreateSolidBorder(1, SK_ColorBLACK));
604 inner_container->set_background(
605 Background::CreateSolidBackground(230, 230, 230));
606 inner_container->set_id(kInnerContainerID);
607 right_container_->AddChildView(inner_container);
608 inner_container->SetBounds(100, 10, 150, 180);
609
610 ScrollView* scroll_view = new ScrollView();
611 scroll_view->set_id(kScrollViewID);
612 inner_container->AddChildView(scroll_view);
613 scroll_view->SetBounds(1, 1, 148, 178);
614
615 View* scroll_content = new View();
616 scroll_content->SetBounds(0, 0, 200, 200);
617 scroll_content->set_background(
618 Background::CreateSolidBackground(200, 200, 200));
619 scroll_view->SetContents(scroll_content);
620
621 static const char* const kTitles[] = {
622 "Rosetta", "Stupeur et tremblement", "The diner game",
623 "Ridicule", "Le placard", "Les Visiteurs", "Amelie",
624 "Joyeux Noel", "Camping", "Brice de Nice",
625 "Taxi", "Asterix"
626 };
627
628 static const int kIDs[] = {
629 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
630 kRidiculeLinkID, kClosetLinkID, kVisitingLinkID, kAmelieLinkID,
631 kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
632 kTaxiLinkID, kAsterixLinkID
633 };
634
635 DCHECK(arraysize(kTitles) == arraysize(kIDs));
636
637 y = 5;
638 for (size_t i = 0; i < arraysize(kTitles); ++i) {
639 Link* link = new Link(ASCIIToUTF16(kTitles[i]));
640 link->SetHorizontalAlignment(Label::ALIGN_LEFT);
641 link->set_id(kIDs[i]);
642 scroll_content->AddChildView(link);
643 link->SetBounds(5, y, 300, 15);
644 y += 15;
645 }
646
647 y = 250;
648 int width = 60;
649 button = new NativeTextButton(NULL, ASCIIToUTF16("OK"));
650 button->set_id(kOKButtonID);
651 button->SetIsDefault(true);
652
653 content_view_->AddChildView(button);
654 button->SetBounds(150, y, width, 30);
655
656 button = new NativeTextButton(NULL, ASCIIToUTF16("Cancel"));
657 button->set_id(kCancelButtonID);
658 content_view_->AddChildView(button);
659 button->SetBounds(220, y, width, 30);
660
661 button = new NativeTextButton(NULL, ASCIIToUTF16("Help"));
662 button->set_id(kHelpButtonID);
663 content_view_->AddChildView(button);
664 button->SetBounds(290, y, width, 30);
665
666 y += 40;
667
668 // Left bottom box with style checkboxes.
669 View* contents = new View();
670 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
671 cb = new Checkbox(ASCIIToUTF16("Bold"));
672 contents->AddChildView(cb);
673 cb->SetBounds(10, 10, 50, 20);
674 cb->set_id(kBoldCheckBoxID);
675
676 cb = new Checkbox(ASCIIToUTF16("Italic"));
677 contents->AddChildView(cb);
678 cb->SetBounds(70, 10, 50, 20);
679 cb->set_id(kItalicCheckBoxID);
680
681 cb = new Checkbox(ASCIIToUTF16("Underlined"));
682 contents->AddChildView(cb);
683 cb->SetBounds(130, 10, 70, 20);
684 cb->set_id(kUnderlinedCheckBoxID);
685
686 Link* link = new Link(ASCIIToUTF16("Help"));
687 contents->AddChildView(link);
688 link->SetBounds(10, 35, 70, 10);
689 link->set_id(kStyleHelpLinkID);
690
691 text_field = new Textfield();
692 contents->AddChildView(text_field);
693 text_field->SetBounds(10, 50, 100, 20);
694 text_field->set_id(kStyleTextEditID);
695
696 style_tab_ = new TabbedPane();
697 style_tab_->set_id(kStyleContainerID);
698 content_view_->AddChildView(style_tab_);
699 style_tab_->SetBounds(10, y, 210, 100);
700 style_tab_->AddTab(ASCIIToUTF16("Style"), contents);
701 style_tab_->AddTab(ASCIIToUTF16("Other"), new View());
702
703 // Right bottom box with search.
704 contents = new View();
705 contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
706 text_field = new Textfield();
707 contents->AddChildView(text_field);
708 text_field->SetBounds(10, 10, 100, 20);
709 text_field->set_id(kSearchTextfieldID);
710
711 button = new NativeTextButton(NULL, ASCIIToUTF16("Search"));
712 contents->AddChildView(button);
713 button->SetBounds(112, 5, 60, 30);
714 button->set_id(kSearchButtonID);
715
716 link = new Link(ASCIIToUTF16("Help"));
717 link->SetHorizontalAlignment(Label::ALIGN_LEFT);
718 link->set_id(kHelpLinkID);
719 contents->AddChildView(link);
720 link->SetBounds(175, 10, 30, 20);
721
722 search_border_view_ = new BorderView(contents);
723 search_border_view_->set_id(kSearchContainerID);
724
725 content_view_->AddChildView(search_border_view_);
726 search_border_view_->SetBounds(300, y, 240, 50);
727
728 y += 60;
729
730 contents = new View();
731 contents->set_focusable(true);
732 contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE));
733 contents->set_id(kThumbnailContainerID);
734 button = new NativeTextButton(NULL, ASCIIToUTF16("Star"));
735 contents->AddChildView(button);
736 button->SetBounds(5, 5, 50, 30);
737 button->set_id(kThumbnailStarID);
738 button = new NativeTextButton(NULL, ASCIIToUTF16("SuperStar"));
739 contents->AddChildView(button);
740 button->SetBounds(60, 5, 100, 30);
741 button->set_id(kThumbnailSuperStarID);
742
743 content_view_->AddChildView(contents);
744 contents->SetBounds(250, y, 200, 50);
745 // We can only call RadioButton::SetChecked() on the radio-button is part of
746 // the view hierarchy.
747 radio_button_to_check->SetChecked(true);
748 }
749
750 ////////////////////////////////////////////////////////////////////////////////
751 // The tests
752 ////////////////////////////////////////////////////////////////////////////////
753
754 enum FocusTestEventType {
755 ON_FOCUS = 0,
756 ON_BLUR
757 };
758
759 struct FocusTestEvent {
760 FocusTestEvent(FocusTestEventType type, int view_id)
761 : type(type),
762 view_id(view_id) {
763 }
764
765 FocusTestEventType type;
766 int view_id;
767 };
768
769 class SimpleTestView : public View {
770 public:
771 SimpleTestView(std::vector<FocusTestEvent>* event_list, int view_id)
772 : event_list_(event_list) {
773 set_focusable(true);
774 set_id(view_id);
775 }
776
777 virtual void OnFocus() {
778 event_list_->push_back(FocusTestEvent(ON_FOCUS, id()));
779 }
780
781 virtual void OnBlur() {
782 event_list_->push_back(FocusTestEvent(ON_BLUR, id()));
783 }
784
785 private:
786 std::vector<FocusTestEvent>* event_list_;
787 };
788
789 // Tests that the appropriate Focus related methods are called when a View
790 // gets/loses focus.
791 TEST_F(FocusManagerTest, ViewFocusCallbacks) {
792 std::vector<FocusTestEvent> event_list;
793 const int kView1ID = 1;
794 const int kView2ID = 2;
795
796 SimpleTestView* view1 = new SimpleTestView(&event_list, kView1ID);
797 SimpleTestView* view2 = new SimpleTestView(&event_list, kView2ID);
798 content_view_->AddChildView(view1);
799 content_view_->AddChildView(view2);
800
801 view1->RequestFocus();
802 ASSERT_EQ(1, static_cast<int>(event_list.size()));
803 EXPECT_EQ(ON_FOCUS, event_list[0].type);
804 EXPECT_EQ(kView1ID, event_list[0].view_id);
805
806 event_list.clear();
807 view2->RequestFocus();
808 ASSERT_EQ(2, static_cast<int>(event_list.size()));
809 EXPECT_EQ(ON_BLUR, event_list[0].type);
810 EXPECT_EQ(kView1ID, event_list[0].view_id);
811 EXPECT_EQ(ON_FOCUS, event_list[1].type);
812 EXPECT_EQ(kView2ID, event_list[1].view_id);
813
814 event_list.clear();
815 GetFocusManager()->ClearFocus();
816 ASSERT_EQ(1, static_cast<int>(event_list.size()));
817 EXPECT_EQ(ON_BLUR, event_list[0].type);
818 EXPECT_EQ(kView2ID, event_list[0].view_id);
819 }
820
821 typedef std::pair<View*, View*> ViewPair;
822 class TestFocusChangeListener : public FocusChangeListener {
823 public:
824 virtual void OnWillChangeFocus(View* focused_before, View* focused_now) {
825 focus_changes_.push_back(ViewPair(focused_before, focused_now));
826 }
827 virtual void OnDidChangeFocus(View* focused_before, View* focused_now) {
828 }
829
830 const std::vector<ViewPair>& focus_changes() const { return focus_changes_; }
831 void ClearFocusChanges() { focus_changes_.clear(); }
832
833 private:
834 // A vector of which views lost/gained focus.
835 std::vector<ViewPair> focus_changes_;
836 };
837
838 TEST_F(FocusManagerTest, FocusChangeListener) {
839 View* view1 = new View();
840 view1->set_focusable(true);
841 View* view2 = new View();
842 view2->set_focusable(true);
843 content_view_->AddChildView(view1);
844 content_view_->AddChildView(view2);
845
846 TestFocusChangeListener listener;
847 AddFocusChangeListener(&listener);
848
849 // Visual Studio 2010 has problems converting NULL to the null pointer for
850 // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/ 520043/error-converting-from-null-to-a-pointer-type-in-std-pair
851 // It will work if we pass nullptr.
852 #if defined(_MSC_VER) && _MSC_VER >= 1600
853 views::View* null_view = nullptr;
854 #else
855 views::View* null_view = NULL;
856 #endif
857
858 view1->RequestFocus();
859 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
860 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view1));
861 listener.ClearFocusChanges();
862
863 view2->RequestFocus();
864 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
865 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view1, view2));
866 listener.ClearFocusChanges();
867
868 GetFocusManager()->ClearFocus();
869 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
870 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view2, null_view));
871 }
872
873 class TestNativeButton : public NativeTextButton {
874 public:
875 explicit TestNativeButton(const string16& text)
876 : NativeTextButton(NULL, text) {
877 };
878 virtual gfx::NativeView TestGetNativeControlView() {
879 return GetWidget()->GetNativeView();
880 }
881 };
882
883 class TestCheckbox : public Checkbox {
884 public:
885 explicit TestCheckbox(const string16& text) : Checkbox(text) {
886 };
887 virtual gfx::NativeView TestGetNativeControlView() {
888 return GetWidget()->GetNativeView();
889 }
890 };
891
892 class TestRadioButton : public RadioButton {
893 public:
894 explicit TestRadioButton(const string16& text)
895 : RadioButton(text, 1) {
896 }
897 virtual gfx::NativeView TestGetNativeControlView() {
898 return GetWidget()->GetNativeView();
899 }
900 };
901
902 class TestTextfield : public Textfield {
903 public:
904 TestTextfield() { }
905 virtual gfx::NativeView TestGetNativeControlView() {
906 return native_wrapper_->GetTestingHandle();
907 }
908 };
909
910 class TestCombobox : public Combobox, public ui::ComboboxModel {
911 public:
912 TestCombobox() : Combobox(this) { }
913 virtual gfx::NativeView TestGetNativeControlView() {
914 return native_wrapper_->GetTestingHandle();
915 }
916 virtual int GetItemCount() {
917 return 10;
918 }
919 virtual string16 GetItemAt(int index) {
920 return ASCIIToUTF16("Hello combo");
921 }
922 };
923
924 class TestTabbedPane : public TabbedPane {
925 public:
926 TestTabbedPane() { }
927 virtual gfx::NativeView TestGetNativeControlView() {
928 return native_tabbed_pane_->GetTestingHandle();
929 }
930 };
931
932 #if !defined(TOUCH_UI)
933 // TODO(oshima): replace TOUCH_UI with PURE_VIEWS
934
935 // Tests that NativeControls do set the focus View appropriately on the
936 // FocusManager.
937 TEST_F(FocusManagerTest, FAILS_FocusNativeControls) {
938 TestTextfield* textfield = new TestTextfield();
939 TestTabbedPane* tabbed_pane = new TestTabbedPane();
940 TestTextfield* textfield2 = new TestTextfield();
941
942 content_view_->AddChildView(textfield);
943 content_view_->AddChildView(tabbed_pane);
944
945 tabbed_pane->AddTab(ASCIIToUTF16("Awesome textfield"), textfield2);
946
947 // Simulate the native view getting the native focus (such as by user click).
948 FocusNativeView(textfield->TestGetNativeControlView());
949 EXPECT_EQ(textfield, GetFocusManager()->GetFocusedView());
950
951 FocusNativeView(tabbed_pane->TestGetNativeControlView());
952 EXPECT_EQ(tabbed_pane, GetFocusManager()->GetFocusedView());
953
954 FocusNativeView(textfield2->TestGetNativeControlView());
955 EXPECT_EQ(textfield2, GetFocusManager()->GetFocusedView());
956 }
957 #endif
958
959 // On linux, we don't store/restore focused view because gtk handles
960 // this (and pure views will be the same).
961 #if defined(OS_WIN)
962
963 // Test that when activating/deactivating the top window, the focus is stored/
964 // restored properly.
965 TEST_F(FocusManagerTest, FocusStoreRestore) {
966 // Simulate an activate, otherwise the deactivate isn't going to do anything.
967 SimulateActivateWindow();
968
969 NativeTextButton* button = new NativeTextButton(NULL,
970 ASCIIToUTF16("Press me"));
971 View* view = new View();
972 view->set_focusable(true);
973
974 content_view_->AddChildView(button);
975 button->SetBounds(10, 10, 200, 30);
976 content_view_->AddChildView(view);
977 RunPendingMessages();
978
979 TestFocusChangeListener listener;
980 AddFocusChangeListener(&listener);
981
982 view->RequestFocus();
983 RunPendingMessages();
984 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler());
985
986 // Visual Studio 2010 has problems converting NULL to the null pointer for
987 // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/ 520043/error-converting-from-null-to-a-pointer-type-in-std-pair
988 // It will work if we pass nullptr.
989 #if defined(_MSC_VER) && _MSC_VER >= 1600
990 views::View* null_view = nullptr;
991 #else
992 views::View* null_view = NULL;
993 #endif
994
995 // Deacivate the window, it should store its focus.
996 SimulateDeactivateWindow();
997 EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView());
998 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size()));
999 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view));
1000 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(view, null_view));
1001 listener.ClearFocusChanges();
1002
1003 // Reactivate, focus should come-back to the previously focused view.
1004 SimulateActivateWindow();
1005 EXPECT_EQ(view, GetFocusManager()->GetFocusedView());
1006 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
1007 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view));
1008 listener.ClearFocusChanges();
1009
1010 // Same test with a NativeControl.
1011 button->RequestFocus();
1012 SimulateDeactivateWindow();
1013 EXPECT_EQ(NULL, GetFocusManager()->GetFocusedView());
1014 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size()));
1015 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view, button));
1016 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(button, null_view));
1017 listener.ClearFocusChanges();
1018
1019 SimulateActivateWindow();
1020 EXPECT_EQ(button, GetFocusManager()->GetFocusedView());
1021 ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
1022 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, button));
1023 listener.ClearFocusChanges();
1024
1025 /*
1026 // Now test that while the window is inactive we can change the focused view
1027 // (we do that in several places).
1028 SimulateDeactivateWindow();
1029 // TODO: would have to mock the window being inactive (with a TestWidgetWin
1030 // that would return false on IsActive()).
1031 GetFocusManager()->SetFocusedView(view);
1032 ::SendMessage(window_->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL);
1033
1034 EXPECT_EQ(view, GetFocusManager()->GetFocusedView());
1035 ASSERT_EQ(2, static_cast<int>(listener.focus_changes().size()));
1036 EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(button, null_view));
1037 EXPECT_TRUE(listener.focus_changes()[1] == ViewPair(null_view, view));
1038 */
1039 }
1040 #endif
1041
1042 #if !defined(TOUCH_UI)
1043 // TODO(oshima): There is no tabbed pane in pure views. Replace it
1044 // with different implementation.
1045
1046 TEST_F(FocusManagerTest, ContainsView) {
1047 View* view = new View();
1048 scoped_ptr<View> detached_view(new View());
1049 TabbedPane* tabbed_pane = new TabbedPane();
1050 TabbedPane* nested_tabbed_pane = new TabbedPane();
1051 NativeTextButton* tab_button = new NativeTextButton(
1052 NULL, ASCIIToUTF16("tab button"));
1053
1054 content_view_->AddChildView(view);
1055 content_view_->AddChildView(tabbed_pane);
1056 // Adding a View inside a TabbedPane to test the case of nested root view.
1057
1058 tabbed_pane->AddTab(ASCIIToUTF16("Awesome tab"), nested_tabbed_pane);
1059 nested_tabbed_pane->AddTab(ASCIIToUTF16("Awesomer tab"), tab_button);
1060
1061 EXPECT_TRUE(GetFocusManager()->ContainsView(view));
1062 EXPECT_TRUE(GetFocusManager()->ContainsView(tabbed_pane));
1063 EXPECT_TRUE(GetFocusManager()->ContainsView(nested_tabbed_pane));
1064 EXPECT_TRUE(GetFocusManager()->ContainsView(tab_button));
1065 EXPECT_FALSE(GetFocusManager()->ContainsView(detached_view.get()));
1066 }
1067 #endif
1068
1069 TEST_F(FocusTraversalTest, NormalTraversal) {
1070 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID,
1071 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
1072 kFruitButtonID, kFruitCheckBoxID, kComboboxID, kBroccoliButtonID,
1073 kRosettaLinkID, kStupeurEtTremblementLinkID,
1074 kDinerGameLinkID, kRidiculeLinkID, kClosetLinkID, kVisitingLinkID,
1075 kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
1076 kTaxiLinkID, kAsterixLinkID, kOKButtonID, kCancelButtonID, kHelpButtonID,
1077 kStyleContainerID, kBoldCheckBoxID, kItalicCheckBoxID,
1078 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
1079 kSearchTextfieldID, kSearchButtonID, kHelpLinkID,
1080 kThumbnailContainerID, kThumbnailStarID, kThumbnailSuperStarID };
1081
1082 // Uncomment the following line if you want to test manually the UI of this
1083 // test.
1084 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler());
1085
1086 // Let's traverse the whole focus hierarchy (several times, to make sure it
1087 // loops OK).
1088 GetFocusManager()->ClearFocus();
1089 for (int i = 0; i < 3; ++i) {
1090 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
1091 GetFocusManager()->AdvanceFocus(false);
1092 View* focused_view = GetFocusManager()->GetFocusedView();
1093 EXPECT_TRUE(focused_view != NULL);
1094 if (focused_view)
1095 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
1096 }
1097 }
1098
1099 // Let's traverse in reverse order.
1100 GetFocusManager()->ClearFocus();
1101 for (int i = 0; i < 3; ++i) {
1102 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
1103 GetFocusManager()->AdvanceFocus(true);
1104 View* focused_view = GetFocusManager()->GetFocusedView();
1105 EXPECT_TRUE(focused_view != NULL);
1106 if (focused_view)
1107 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
1108 }
1109 }
1110 }
1111
1112 TEST_F(FocusTraversalTest, TraversalWithNonEnabledViews) {
1113 const int kDisabledIDs[] = {
1114 kBananaTextfieldID, kFruitCheckBoxID, kComboboxID, kAsparagusButtonID,
1115 kCauliflowerButtonID, kClosetLinkID, kVisitingLinkID, kBriceDeNiceLinkID,
1116 kTaxiLinkID, kAsterixLinkID, kHelpButtonID, kBoldCheckBoxID,
1117 kSearchTextfieldID, kHelpLinkID };
1118
1119 const int kTraversalIDs[] = { kTopCheckBoxID, kAppleTextfieldID,
1120 kOrangeTextfieldID, kKiwiTextfieldID, kFruitButtonID, kBroccoliButtonID,
1121 kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
1122 kRidiculeLinkID, kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID,
1123 kOKButtonID, kCancelButtonID, kStyleContainerID, kItalicCheckBoxID,
1124 kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
1125 kSearchButtonID, kThumbnailContainerID, kThumbnailStarID,
1126 kThumbnailSuperStarID };
1127
1128 // Let's disable some views.
1129 for (size_t i = 0; i < arraysize(kDisabledIDs); i++) {
1130 View* v = FindViewByID(kDisabledIDs[i]);
1131 ASSERT_TRUE(v != NULL);
1132 v->SetEnabled(false);
1133 }
1134
1135 // Uncomment the following line if you want to test manually the UI of this
1136 // test.
1137 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler());
1138
1139 View* focused_view;
1140 // Let's do one traversal (several times, to make sure it loops ok).
1141 GetFocusManager()->ClearFocus();
1142 for (int i = 0; i < 3; ++i) {
1143 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
1144 GetFocusManager()->AdvanceFocus(false);
1145 focused_view = GetFocusManager()->GetFocusedView();
1146 EXPECT_TRUE(focused_view != NULL);
1147 if (focused_view)
1148 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
1149 }
1150 }
1151
1152 // Same thing in reverse.
1153 GetFocusManager()->ClearFocus();
1154 for (int i = 0; i < 3; ++i) {
1155 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
1156 GetFocusManager()->AdvanceFocus(true);
1157 focused_view = GetFocusManager()->GetFocusedView();
1158 EXPECT_TRUE(focused_view != NULL);
1159 if (focused_view)
1160 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
1161 }
1162 }
1163 }
1164
1165 TEST_F(FocusTraversalTest, TraversalWithInvisibleViews) {
1166 const int kInvisibleIDs[] = { kTopCheckBoxID, kOKButtonID,
1167 kThumbnailContainerID };
1168
1169 const int kTraversalIDs[] = { kAppleTextfieldID, kOrangeTextfieldID,
1170 kBananaTextfieldID, kKiwiTextfieldID, kFruitButtonID, kFruitCheckBoxID,
1171 kComboboxID, kBroccoliButtonID, kRosettaLinkID,
1172 kStupeurEtTremblementLinkID, kDinerGameLinkID, kRidiculeLinkID,
1173 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
1174 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID,
1175 kCancelButtonID, kHelpButtonID, kStyleContainerID, kBoldCheckBoxID,
1176 kItalicCheckBoxID, kUnderlinedCheckBoxID, kStyleHelpLinkID,
1177 kStyleTextEditID, kSearchTextfieldID, kSearchButtonID, kHelpLinkID };
1178
1179
1180 // Let's make some views invisible.
1181 for (size_t i = 0; i < arraysize(kInvisibleIDs); i++) {
1182 View* v = FindViewByID(kInvisibleIDs[i]);
1183 ASSERT_TRUE(v != NULL);
1184 v->SetVisible(false);
1185 }
1186
1187 // Uncomment the following line if you want to test manually the UI of this
1188 // test.
1189 // MessageLoopForUI::current()->RunWithDispatcher(new AcceleratorHandler());
1190
1191 View* focused_view;
1192 // Let's do one traversal (several times, to make sure it loops ok).
1193 GetFocusManager()->ClearFocus();
1194 for (int i = 0; i < 3; ++i) {
1195 for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
1196 GetFocusManager()->AdvanceFocus(false);
1197 focused_view = GetFocusManager()->GetFocusedView();
1198 EXPECT_TRUE(focused_view != NULL);
1199 if (focused_view)
1200 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
1201 }
1202 }
1203
1204 // Same thing in reverse.
1205 GetFocusManager()->ClearFocus();
1206 for (int i = 0; i < 3; ++i) {
1207 for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
1208 GetFocusManager()->AdvanceFocus(true);
1209 focused_view = GetFocusManager()->GetFocusedView();
1210 EXPECT_TRUE(focused_view != NULL);
1211 if (focused_view)
1212 EXPECT_EQ(kTraversalIDs[j], focused_view->id());
1213 }
1214 }
1215 }
1216
1217 TEST_F(FocusTraversalTest, PaneTraversal) {
1218 // Tests trapping the traversal within a pane - useful for full
1219 // keyboard accessibility for toolbars.
1220
1221 // First test the left container.
1222 const int kLeftTraversalIDs[] = {
1223 kAppleTextfieldID,
1224 kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
1225 kFruitButtonID, kFruitCheckBoxID, kComboboxID };
1226
1227 FocusSearch focus_search_left(left_container_, true, false);
1228 left_container_->EnablePaneFocus(&focus_search_left);
1229 FindViewByID(kComboboxID)->RequestFocus();
1230
1231 // Traverse the focus hierarchy within the pane several times.
1232 for (int i = 0; i < 3; ++i) {
1233 for (size_t j = 0; j < arraysize(kLeftTraversalIDs); j++) {
1234 GetFocusManager()->AdvanceFocus(false);
1235 View* focused_view = GetFocusManager()->GetFocusedView();
1236 EXPECT_TRUE(focused_view != NULL);
1237 if (focused_view)
1238 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
1239 }
1240 }
1241
1242 // Traverse in reverse order.
1243 FindViewByID(kAppleTextfieldID)->RequestFocus();
1244 for (int i = 0; i < 3; ++i) {
1245 for (int j = arraysize(kLeftTraversalIDs) - 1; j >= 0; --j) {
1246 GetFocusManager()->AdvanceFocus(true);
1247 View* focused_view = GetFocusManager()->GetFocusedView();
1248 EXPECT_TRUE(focused_view != NULL);
1249 if (focused_view)
1250 EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
1251 }
1252 }
1253
1254 // Now test the right container, but this time with accessibility mode.
1255 // Make some links not focusable, but mark one of them as
1256 // "accessibility focusable", so it should show up in the traversal.
1257 const int kRightTraversalIDs[] = {
1258 kBroccoliButtonID, kDinerGameLinkID, kRidiculeLinkID,
1259 kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
1260 kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID };
1261
1262 FocusSearch focus_search_right(right_container_, true, true);
1263 right_container_->EnablePaneFocus(&focus_search_right);
1264 FindViewByID(kRosettaLinkID)->set_focusable(false);
1265 FindViewByID(kStupeurEtTremblementLinkID)->set_focusable(false);
1266 FindViewByID(kDinerGameLinkID)->set_accessibility_focusable(true);
1267 FindViewByID(kDinerGameLinkID)->set_focusable(false);
1268 FindViewByID(kAsterixLinkID)->RequestFocus();
1269
1270 // Traverse the focus hierarchy within the pane several times.
1271 for (int i = 0; i < 3; ++i) {
1272 for (size_t j = 0; j < arraysize(kRightTraversalIDs); j++) {
1273 GetFocusManager()->AdvanceFocus(false);
1274 View* focused_view = GetFocusManager()->GetFocusedView();
1275 EXPECT_TRUE(focused_view != NULL);
1276 if (focused_view)
1277 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
1278 }
1279 }
1280
1281 // Traverse in reverse order.
1282 FindViewByID(kBroccoliButtonID)->RequestFocus();
1283 for (int i = 0; i < 3; ++i) {
1284 for (int j = arraysize(kRightTraversalIDs) - 1; j >= 0; --j) {
1285 GetFocusManager()->AdvanceFocus(true);
1286 View* focused_view = GetFocusManager()->GetFocusedView();
1287 EXPECT_TRUE(focused_view != NULL);
1288 if (focused_view)
1289 EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
1290 }
1291 }
1292 }
1293
1294 // Counts accelerator calls.
1295 class TestAcceleratorTarget : public ui::AcceleratorTarget {
1296 public:
1297 explicit TestAcceleratorTarget(bool process_accelerator)
1298 : accelerator_count_(0), process_accelerator_(process_accelerator) {}
1299
1300 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) {
1301 ++accelerator_count_;
1302 return process_accelerator_;
1303 }
1304
1305 int accelerator_count() const { return accelerator_count_; }
1306
1307 private:
1308 int accelerator_count_; // number of times that the accelerator is activated
1309 bool process_accelerator_; // return value of AcceleratorPressed
1310
1311 DISALLOW_COPY_AND_ASSIGN(TestAcceleratorTarget);
1312 };
1313
1314 TEST_F(FocusManagerTest, CallsNormalAcceleratorTarget) {
1315 FocusManager* focus_manager = GetFocusManager();
1316 ui::Accelerator return_accelerator(ui::VKEY_RETURN, false, false, false);
1317 ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, false, false, false);
1318
1319 TestAcceleratorTarget return_target(true);
1320 TestAcceleratorTarget escape_target(true);
1321 EXPECT_EQ(return_target.accelerator_count(), 0);
1322 EXPECT_EQ(escape_target.accelerator_count(), 0);
1323 EXPECT_EQ(NULL,
1324 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1325 EXPECT_EQ(NULL,
1326 focus_manager->GetCurrentTargetForAccelerator(escape_accelerator));
1327
1328 // Register targets.
1329 focus_manager->RegisterAccelerator(return_accelerator, &return_target);
1330 focus_manager->RegisterAccelerator(escape_accelerator, &escape_target);
1331
1332 // Checks if the correct target is registered.
1333 EXPECT_EQ(&return_target,
1334 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1335 EXPECT_EQ(&escape_target,
1336 focus_manager->GetCurrentTargetForAccelerator(escape_accelerator));
1337
1338 // Hitting the return key.
1339 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
1340 EXPECT_EQ(return_target.accelerator_count(), 1);
1341 EXPECT_EQ(escape_target.accelerator_count(), 0);
1342
1343 // Hitting the escape key.
1344 EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator));
1345 EXPECT_EQ(return_target.accelerator_count(), 1);
1346 EXPECT_EQ(escape_target.accelerator_count(), 1);
1347
1348 // Register another target for the return key.
1349 TestAcceleratorTarget return_target2(true);
1350 EXPECT_EQ(return_target2.accelerator_count(), 0);
1351 focus_manager->RegisterAccelerator(return_accelerator, &return_target2);
1352 EXPECT_EQ(&return_target2,
1353 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1354
1355 // Hitting the return key; return_target2 has the priority.
1356 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
1357 EXPECT_EQ(return_target.accelerator_count(), 1);
1358 EXPECT_EQ(return_target2.accelerator_count(), 1);
1359
1360 // Register a target that does not process the accelerator event.
1361 TestAcceleratorTarget return_target3(false);
1362 EXPECT_EQ(return_target3.accelerator_count(), 0);
1363 focus_manager->RegisterAccelerator(return_accelerator, &return_target3);
1364 EXPECT_EQ(&return_target3,
1365 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1366
1367 // Hitting the return key.
1368 // Since the event handler of return_target3 returns false, return_target2
1369 // should be called too.
1370 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
1371 EXPECT_EQ(return_target.accelerator_count(), 1);
1372 EXPECT_EQ(return_target2.accelerator_count(), 2);
1373 EXPECT_EQ(return_target3.accelerator_count(), 1);
1374
1375 // Unregister return_target2.
1376 focus_manager->UnregisterAccelerator(return_accelerator, &return_target2);
1377 EXPECT_EQ(&return_target3,
1378 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1379
1380 // Hitting the return key. return_target3 and return_target should be called.
1381 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
1382 EXPECT_EQ(return_target.accelerator_count(), 2);
1383 EXPECT_EQ(return_target2.accelerator_count(), 2);
1384 EXPECT_EQ(return_target3.accelerator_count(), 2);
1385
1386 // Unregister targets.
1387 focus_manager->UnregisterAccelerator(return_accelerator, &return_target);
1388 focus_manager->UnregisterAccelerator(return_accelerator, &return_target3);
1389 focus_manager->UnregisterAccelerator(escape_accelerator, &escape_target);
1390
1391 // Now there is no target registered.
1392 EXPECT_EQ(NULL,
1393 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1394 EXPECT_EQ(NULL,
1395 focus_manager->GetCurrentTargetForAccelerator(escape_accelerator));
1396
1397 // Hitting the return key and the escape key. Nothing should happen.
1398 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator));
1399 EXPECT_EQ(return_target.accelerator_count(), 2);
1400 EXPECT_EQ(return_target2.accelerator_count(), 2);
1401 EXPECT_EQ(return_target3.accelerator_count(), 2);
1402 EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator));
1403 EXPECT_EQ(escape_target.accelerator_count(), 1);
1404 }
1405
1406 // Unregisters itself when its accelerator is invoked.
1407 class SelfUnregisteringAcceleratorTarget : public ui::AcceleratorTarget {
1408 public:
1409 SelfUnregisteringAcceleratorTarget(ui::Accelerator accelerator,
1410 FocusManager* focus_manager)
1411 : accelerator_(accelerator),
1412 focus_manager_(focus_manager),
1413 accelerator_count_(0) {
1414 }
1415
1416 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) {
1417 ++accelerator_count_;
1418 focus_manager_->UnregisterAccelerator(accelerator, this);
1419 return true;
1420 }
1421
1422 int accelerator_count() const { return accelerator_count_; }
1423
1424 private:
1425 ui::Accelerator accelerator_;
1426 FocusManager* focus_manager_;
1427 int accelerator_count_;
1428
1429 DISALLOW_COPY_AND_ASSIGN(SelfUnregisteringAcceleratorTarget);
1430 };
1431
1432 TEST_F(FocusManagerTest, CallsSelfDeletingAcceleratorTarget) {
1433 FocusManager* focus_manager = GetFocusManager();
1434 ui::Accelerator return_accelerator(ui::VKEY_RETURN, false, false, false);
1435 SelfUnregisteringAcceleratorTarget target(return_accelerator, focus_manager);
1436 EXPECT_EQ(target.accelerator_count(), 0);
1437 EXPECT_EQ(NULL,
1438 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1439
1440 // Register the target.
1441 focus_manager->RegisterAccelerator(return_accelerator, &target);
1442 EXPECT_EQ(&target,
1443 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1444
1445 // Hitting the return key. The target will be unregistered.
1446 EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
1447 EXPECT_EQ(target.accelerator_count(), 1);
1448 EXPECT_EQ(NULL,
1449 focus_manager->GetCurrentTargetForAccelerator(return_accelerator));
1450
1451 // Hitting the return key again; nothing should happen.
1452 EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator));
1453 EXPECT_EQ(target.accelerator_count(), 1);
1454 }
1455
1456 class MessageTrackingView : public View {
1457 public:
1458 MessageTrackingView() : accelerator_pressed_(false) {
1459 }
1460
1461 virtual bool OnKeyPressed(const KeyEvent& e) {
1462 keys_pressed_.push_back(e.key_code());
1463 return true;
1464 }
1465
1466 virtual bool OnKeyReleased(const KeyEvent& e) {
1467 keys_released_.push_back(e.key_code());
1468 return true;
1469 }
1470
1471 virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) {
1472 accelerator_pressed_ = true;
1473 return true;
1474 }
1475
1476 void Reset() {
1477 accelerator_pressed_ = false;
1478 keys_pressed_.clear();
1479 keys_released_.clear();
1480 }
1481
1482 const std::vector<ui::KeyboardCode>& keys_pressed() const {
1483 return keys_pressed_;
1484 }
1485
1486 const std::vector<ui::KeyboardCode>& keys_released() const {
1487 return keys_released_;
1488 }
1489
1490 bool accelerator_pressed() const {
1491 return accelerator_pressed_;
1492 }
1493
1494 private:
1495 bool accelerator_pressed_;
1496 std::vector<ui::KeyboardCode> keys_pressed_;
1497 std::vector<ui::KeyboardCode> keys_released_;
1498
1499 DISALLOW_COPY_AND_ASSIGN(MessageTrackingView);
1500 };
1501
1502 #if defined(OS_WIN)
1503 // This test is now Windows only. Linux Views port does not handle accelerator
1504 // keys in AcceleratorHandler anymore. The logic has been moved into
1505 // NativeWidgetGtk::OnKeyEvent().
1506 // Tests that the keyup messages are eaten for accelerators.
1507 TEST_F(FocusManagerTest, IgnoreKeyupForAccelerators) {
1508 FocusManager* focus_manager = GetFocusManager();
1509 MessageTrackingView* mtv = new MessageTrackingView();
1510 mtv->AddAccelerator(ui::Accelerator(ui::VKEY_0, false, false, false));
1511 mtv->AddAccelerator(ui::Accelerator(ui::VKEY_1, false, false, false));
1512 content_view_->AddChildView(mtv);
1513 focus_manager->SetFocusedView(mtv);
1514
1515 // First send a non-accelerator key sequence.
1516 PostKeyDown(ui::VKEY_9);
1517 PostKeyUp(ui::VKEY_9);
1518 AcceleratorHandler accelerator_handler;
1519 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
1520 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler);
1521 // Make sure we get a key-up and key-down.
1522 ASSERT_EQ(1U, mtv->keys_pressed().size());
1523 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]);
1524 ASSERT_EQ(1U, mtv->keys_released().size());
1525 EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]);
1526 EXPECT_FALSE(mtv->accelerator_pressed());
1527 mtv->Reset();
1528
1529 // Same thing with repeat and more than one key at once.
1530 PostKeyDown(ui::VKEY_9);
1531 PostKeyDown(ui::VKEY_9);
1532 PostKeyDown(ui::VKEY_8);
1533 PostKeyDown(ui::VKEY_9);
1534 PostKeyDown(ui::VKEY_7);
1535 PostKeyUp(ui::VKEY_9);
1536 PostKeyUp(ui::VKEY_7);
1537 PostKeyUp(ui::VKEY_8);
1538 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
1539 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler);
1540 // Make sure we get a key-up and key-down.
1541 ASSERT_EQ(5U, mtv->keys_pressed().size());
1542 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[0]);
1543 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[1]);
1544 EXPECT_EQ(ui::VKEY_8, mtv->keys_pressed()[2]);
1545 EXPECT_EQ(ui::VKEY_9, mtv->keys_pressed()[3]);
1546 EXPECT_EQ(ui::VKEY_7, mtv->keys_pressed()[4]);
1547 ASSERT_EQ(3U, mtv->keys_released().size());
1548 EXPECT_EQ(ui::VKEY_9, mtv->keys_released()[0]);
1549 EXPECT_EQ(ui::VKEY_7, mtv->keys_released()[1]);
1550 EXPECT_EQ(ui::VKEY_8, mtv->keys_released()[2]);
1551 EXPECT_FALSE(mtv->accelerator_pressed());
1552 mtv->Reset();
1553
1554 // Now send an accelerator key sequence.
1555 PostKeyDown(ui::VKEY_0);
1556 PostKeyUp(ui::VKEY_0);
1557 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
1558 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler);
1559 EXPECT_TRUE(mtv->keys_pressed().empty());
1560 EXPECT_TRUE(mtv->keys_released().empty());
1561 EXPECT_TRUE(mtv->accelerator_pressed());
1562 mtv->Reset();
1563
1564 // Same thing with repeat and more than one key at once.
1565 PostKeyDown(ui::VKEY_0);
1566 PostKeyDown(ui::VKEY_1);
1567 PostKeyDown(ui::VKEY_1);
1568 PostKeyDown(ui::VKEY_0);
1569 PostKeyDown(ui::VKEY_0);
1570 PostKeyUp(ui::VKEY_1);
1571 PostKeyUp(ui::VKEY_0);
1572 MessageLoopForUI::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
1573 MessageLoopForUI::current()->RunWithDispatcher(&accelerator_handler);
1574 EXPECT_TRUE(mtv->keys_pressed().empty());
1575 EXPECT_TRUE(mtv->keys_released().empty());
1576 EXPECT_TRUE(mtv->accelerator_pressed());
1577 mtv->Reset();
1578 }
1579 #endif
1580
1581 #if defined(OS_WIN) && !defined(USE_AURA)
1582 // Test that the focus manager is created successfully for the first view
1583 // window parented to a native dialog.
1584 TEST_F(FocusManagerTest, CreationForNativeRoot) {
1585 // Create a window class.
1586 WNDCLASSEX class_ex;
1587 memset(&class_ex, 0, sizeof(class_ex));
1588 class_ex.cbSize = sizeof(WNDCLASSEX);
1589 class_ex.lpfnWndProc = &DefWindowProc;
1590 class_ex.lpszClassName = L"TestWindow";
1591 ATOM atom = RegisterClassEx(&class_ex);
1592 ASSERT_TRUE(atom);
1593
1594 // Create a native dialog window.
1595 HWND hwnd = CreateWindowEx(0, class_ex.lpszClassName, NULL,
1596 WS_OVERLAPPEDWINDOW, 0, 0, 200, 200,
1597 NULL, NULL, NULL, NULL);
1598 ASSERT_TRUE(hwnd);
1599
1600 // Create a view window parented to native dialog.
1601 scoped_ptr<Widget> widget1(new Widget);
1602 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
1603 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1604 params.parent = hwnd;
1605 params.bounds = gfx::Rect(0, 0, 100, 100);
1606 params.top_level = true; // This is top level in views hierarchy.
1607 widget1->Init(params);
1608
1609 // Get the focus manager directly from the first window. Should exist
1610 // because the first window is the root widget.
1611 views::FocusManager* focus_manager1 = widget1->GetFocusManager();
1612 EXPECT_TRUE(focus_manager1);
1613
1614 // Create another view window parented to the first view window.
1615 scoped_ptr<Widget> widget2(new Widget);
1616 params.parent = widget1->GetNativeView();
1617 params.top_level = false; // This is child widget.
1618 widget2->Init(params);
1619
1620 // Access the shared focus manager directly from the second window.
1621 views::FocusManager* focus_manager2 = widget2->GetFocusManager();
1622 EXPECT_EQ(focus_manager2, focus_manager1);
1623
1624 // Access the shared focus manager indirectly from the first window handle.
1625 gfx::NativeWindow native_window = widget1->GetNativeWindow();
1626 views::Widget* widget =
1627 views::Widget::GetWidgetForNativeWindow(native_window);
1628 EXPECT_EQ(widget->GetFocusManager(), focus_manager1);
1629
1630 // Access the shared focus manager indirectly from the second window handle.
1631 native_window = widget2->GetNativeWindow();
1632 widget = views::Widget::GetWidgetForNativeWindow(native_window);
1633 EXPECT_EQ(widget->GetFocusManager(), focus_manager1);
1634
1635 // Access the shared focus manager indirectly from the first view handle.
1636 gfx::NativeView native_view = widget1->GetNativeView();
1637 widget = views::Widget::GetTopLevelWidgetForNativeView(native_view);
1638 EXPECT_EQ(widget->GetFocusManager(), focus_manager1);
1639
1640 // Access the shared focus manager indirectly from the second view handle.
1641 native_view = widget2->GetNativeView();
1642 widget = views::Widget::GetTopLevelWidgetForNativeView(native_view);
1643 EXPECT_EQ(widget->GetFocusManager(), focus_manager1);
1644
1645 DestroyWindow(hwnd);
1646 }
1647 #endif
1648
1649 class FocusManagerDtorTest : public FocusManagerTest {
1650 protected:
1651 typedef std::vector<std::string> DtorTrackVector;
1652
1653 class FocusManagerDtorTracked : public FocusManager {
1654 public:
1655 FocusManagerDtorTracked(Widget* widget, DtorTrackVector* dtor_tracker)
1656 : FocusManager(widget),
1657 dtor_tracker_(dtor_tracker) {
1658 }
1659
1660 virtual ~FocusManagerDtorTracked() {
1661 dtor_tracker_->push_back("FocusManagerDtorTracked");
1662 }
1663
1664 DtorTrackVector* dtor_tracker_;
1665
1666 private:
1667 DISALLOW_COPY_AND_ASSIGN(FocusManagerDtorTracked);
1668 };
1669
1670 class TestFocusManagerFactory : public FocusManagerFactory {
1671 public:
1672 explicit TestFocusManagerFactory(DtorTrackVector* dtor_tracker)
1673 : dtor_tracker_(dtor_tracker) {
1674 }
1675
1676 FocusManager* CreateFocusManager(Widget* widget) OVERRIDE {
1677 return new FocusManagerDtorTracked(widget, dtor_tracker_);
1678 }
1679
1680 private:
1681 DtorTrackVector* dtor_tracker_;
1682 DISALLOW_COPY_AND_ASSIGN(TestFocusManagerFactory);
1683 };
1684
1685 class NativeButtonDtorTracked : public NativeTextButton {
1686 public:
1687 NativeButtonDtorTracked(const string16& text,
1688 DtorTrackVector* dtor_tracker)
1689 : NativeTextButton(NULL, text),
1690 dtor_tracker_(dtor_tracker) {
1691 };
1692 virtual ~NativeButtonDtorTracked() {
1693 dtor_tracker_->push_back("NativeButtonDtorTracked");
1694 }
1695
1696 DtorTrackVector* dtor_tracker_;
1697 };
1698
1699 class WindowDtorTracked : public Widget {
1700 public:
1701 explicit WindowDtorTracked(DtorTrackVector* dtor_tracker)
1702 : dtor_tracker_(dtor_tracker) {
1703 }
1704
1705 virtual ~WindowDtorTracked() {
1706 dtor_tracker_->push_back("WindowDtorTracked");
1707 }
1708
1709 DtorTrackVector* dtor_tracker_;
1710 };
1711
1712 virtual void SetUp() {
1713 ViewsTestBase::SetUp();
1714 FocusManagerFactory::Install(new TestFocusManagerFactory(&dtor_tracker_));
1715 // Create WindowDtorTracked that uses FocusManagerDtorTracked.
1716 window_ = new WindowDtorTracked(&dtor_tracker_);
1717 Widget::InitParams params;
1718 params.delegate = this;
1719 params.bounds = gfx::Rect(0, 0, 100, 100);
1720 window_->Init(params);
1721
1722 tracked_focus_manager_ =
1723 static_cast<FocusManagerDtorTracked*>(GetFocusManager());
1724 window_->Show();
1725 }
1726
1727 virtual void TearDown() {
1728 if (window_) {
1729 window_->Close();
1730 RunPendingMessages();
1731 }
1732 FocusManagerFactory::Install(NULL);
1733 ViewsTestBase::TearDown();
1734 }
1735
1736 FocusManager* tracked_focus_manager_;
1737 DtorTrackVector dtor_tracker_;
1738 };
1739
1740 TEST_F(FocusManagerDtorTest, FocusManagerDestructedLast) {
1741 // Setup views hierarchy.
1742 TabbedPane* tabbed_pane = new TabbedPane();
1743 content_view_->AddChildView(tabbed_pane);
1744
1745 NativeButtonDtorTracked* button = new NativeButtonDtorTracked(
1746 ASCIIToUTF16("button"), &dtor_tracker_);
1747 tabbed_pane->AddTab(ASCIIToUTF16("Awesome tab"), button);
1748
1749 // Close the window.
1750 window_->Close();
1751 RunPendingMessages();
1752
1753 // Test window, button and focus manager should all be destructed.
1754 ASSERT_EQ(3, static_cast<int>(dtor_tracker_.size()));
1755
1756 // Focus manager should be the last one to destruct.
1757 ASSERT_STREQ("FocusManagerDtorTracked", dtor_tracker_[2].c_str());
1758
1759 // Clear window_ so that we don't try to close it again.
1760 window_ = NULL;
1761 }
1762
1763 } // namespace views
OLDNEW
« no previous file with comments | « views/focus/focus_manager_factory.cc ('k') | views/focus/focus_search.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698