Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ui / views / focus / focus_traversal_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/focus/focus_manager.h"
6
7 #include "base/run_loop.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "ui/base/models/combobox_model.h"
11 #include "ui/views/background.h"
12 #include "ui/views/border.h"
13 #include "ui/views/controls/button/checkbox.h"
14 #include "ui/views/controls/button/label_button.h"
15 #include "ui/views/controls/button/radio_button.h"
16 #include "ui/views/controls/combobox/combobox.h"
17 #include "ui/views/controls/label.h"
18 #include "ui/views/controls/link.h"
19 #include "ui/views/controls/native/native_view_host.h"
20 #include "ui/views/controls/scroll_view.h"
21 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
22 #include "ui/views/controls/textfield/textfield.h"
23 #include "ui/views/focus/focus_manager_test.h"
24 #include "ui/views/widget/root_view.h"
25 #include "ui/views/widget/widget.h"
26
27 using base::ASCIIToUTF16;
28
29 namespace views {
30
31 namespace {
32
33 int count = 1;
34
35 const int kTopCheckBoxID = count++;  // 1
36 const int kLeftContainerID = count++;
37 const int kAppleLabelID = count++;
38 const int kAppleTextfieldID = count++;
39 const int kOrangeLabelID = count++;  // 5
40 const int kOrangeTextfieldID = count++;
41 const int kBananaLabelID = count++;
42 const int kBananaTextfieldID = count++;
43 const int kKiwiLabelID = count++;
44 const int kKiwiTextfieldID = count++;  // 10
45 const int kFruitButtonID = count++;
46 const int kFruitCheckBoxID = count++;
47 const int kComboboxID = count++;
48
49 const int kRightContainerID = count++;
50 const int kAsparagusButtonID = count++;  // 15
51 const int kBroccoliButtonID = count++;
52 const int kCauliflowerButtonID = count++;
53
54 const int kInnerContainerID = count++;
55 const int kScrollViewID = count++;
56 const int kRosettaLinkID = count++;  // 20
57 const int kStupeurEtTremblementLinkID = count++;
58 const int kDinerGameLinkID = count++;
59 const int kRidiculeLinkID = count++;
60 const int kClosetLinkID = count++;
61 const int kVisitingLinkID = count++;  // 25
62 const int kAmelieLinkID = count++;
63 const int kJoyeuxNoelLinkID = count++;
64 const int kCampingLinkID = count++;
65 const int kBriceDeNiceLinkID = count++;
66 const int kTaxiLinkID = count++;  // 30
67 const int kAsterixLinkID = count++;
68
69 const int kOKButtonID = count++;
70 const int kCancelButtonID = count++;
71 const int kHelpButtonID = count++;
72
73 const int kStyleContainerID = count++;  // 35
74 const int kBoldCheckBoxID = count++;
75 const int kItalicCheckBoxID = count++;
76 const int kUnderlinedCheckBoxID = count++;
77 const int kStyleHelpLinkID = count++;
78 const int kStyleTextEditID = count++;  // 40
79
80 const int kSearchContainerID = count++;
81 const int kSearchTextfieldID = count++;
82 const int kSearchButtonID = count++;
83 const int kHelpLinkID = count++;
84
85 const int kThumbnailContainerID = count++;  // 45
86 const int kThumbnailStarID = count++;
87 const int kThumbnailSuperStarID = count++;
88
89 class DummyComboboxModel : public ui::ComboboxModel {
90  public:
91   // Overridden from ui::ComboboxModel:
92   virtual int GetItemCount() const OVERRIDE { return 10; }
93   virtual base::string16 GetItemAt(int index) OVERRIDE {
94     return ASCIIToUTF16("Item ") + base::IntToString16(index);
95   }
96 };
97
98 // A View that can act as a pane.
99 class PaneView : public View, public FocusTraversable {
100  public:
101   PaneView() : focus_search_(NULL) {}
102
103   // If this method is called, this view will use GetPaneFocusTraversable to
104   // have this provided FocusSearch used instead of the default one, allowing
105   // you to trap focus within the pane.
106   void EnablePaneFocus(FocusSearch* focus_search) {
107     focus_search_ = focus_search;
108   }
109
110   // Overridden from View:
111   virtual FocusTraversable* GetPaneFocusTraversable() OVERRIDE {
112     if (focus_search_)
113       return this;
114     else
115       return NULL;
116   }
117
118   // Overridden from FocusTraversable:
119   virtual views::FocusSearch* GetFocusSearch() OVERRIDE {
120     return focus_search_;
121   }
122   virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE {
123     return NULL;
124   }
125   virtual View* GetFocusTraversableParentView() OVERRIDE {
126     return NULL;
127   }
128
129  private:
130   FocusSearch* focus_search_;
131 };
132
133 // BorderView is a view containing a native window with its own view hierarchy.
134 // It is interesting to test focus traversal from a view hierarchy to an inner
135 // view hierarchy.
136 class BorderView : public NativeViewHost {
137  public:
138   explicit BorderView(View* child) : child_(child), widget_(NULL) {
139     DCHECK(child);
140     SetFocusable(false);
141   }
142
143   virtual ~BorderView() {}
144
145   virtual internal::RootView* GetContentsRootView() {
146     return static_cast<internal::RootView*>(widget_->GetRootView());
147   }
148
149   virtual FocusTraversable* GetFocusTraversable() OVERRIDE {
150     return static_cast<internal::RootView*>(widget_->GetRootView());
151   }
152
153   virtual void ViewHierarchyChanged(
154       const ViewHierarchyChangedDetails& details) OVERRIDE {
155     NativeViewHost::ViewHierarchyChanged(details);
156
157     if (details.child == this && details.is_add) {
158       if (!widget_) {
159         widget_ = new Widget;
160         Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
161 #if defined(OS_WIN) || defined(USE_AURA)
162         params.parent = details.parent->GetWidget()->GetNativeView();
163 #else
164         NOTREACHED();
165 #endif
166         widget_->Init(params);
167         widget_->SetFocusTraversableParentView(this);
168         widget_->SetContentsView(child_);
169       }
170
171       // We have been added to a view hierarchy, attach the native view.
172       Attach(widget_->GetNativeView());
173       // Also update the FocusTraversable parent so the focus traversal works.
174       static_cast<internal::RootView*>(widget_->GetRootView())->
175           SetFocusTraversableParent(GetWidget()->GetFocusTraversable());
176     }
177   }
178
179  private:
180   View* child_;
181   Widget* widget_;
182
183   DISALLOW_COPY_AND_ASSIGN(BorderView);
184 };
185
186 }  // namespace
187
188 class FocusTraversalTest : public FocusManagerTest {
189  public:
190   virtual ~FocusTraversalTest();
191
192   virtual void InitContentView() OVERRIDE;
193
194  protected:
195   FocusTraversalTest();
196
197   View* FindViewByID(int id) {
198     View* view = GetContentsView()->GetViewByID(id);
199     if (view)
200       return view;
201     if (style_tab_)
202       view = style_tab_->GetSelectedTab()->GetViewByID(id);
203     if (view)
204       return view;
205     view = search_border_view_->GetContentsRootView()->GetViewByID(id);
206     if (view)
207       return view;
208     return NULL;
209   }
210
211  protected:
212   TabbedPane* style_tab_;
213   BorderView* search_border_view_;
214   DummyComboboxModel combobox_model_;
215   PaneView* left_container_;
216   PaneView* right_container_;
217
218   DISALLOW_COPY_AND_ASSIGN(FocusTraversalTest);
219 };
220
221 FocusTraversalTest::FocusTraversalTest()
222     : style_tab_(NULL),
223       search_border_view_(NULL) {
224 }
225
226 FocusTraversalTest::~FocusTraversalTest() {
227 }
228
229 void FocusTraversalTest::InitContentView() {
230   // Create a complicated view hierarchy with lots of control types for
231   // use by all of the focus traversal tests.
232   //
233   // Class name, ID, and asterisk next to focusable views:
234   //
235   // View
236   //   Checkbox            * kTopCheckBoxID
237   //   PaneView              kLeftContainerID
238   //     Label               kAppleLabelID
239   //     Textfield         * kAppleTextfieldID
240   //     Label               kOrangeLabelID
241   //     Textfield         * kOrangeTextfieldID
242   //     Label               kBananaLabelID
243   //     Textfield         * kBananaTextfieldID
244   //     Label               kKiwiLabelID
245   //     Textfield         * kKiwiTextfieldID
246   //     NativeButton      * kFruitButtonID
247   //     Checkbox          * kFruitCheckBoxID
248   //     Combobox          * kComboboxID
249   //   PaneView              kRightContainerID
250   //     RadioButton       * kAsparagusButtonID
251   //     RadioButton       * kBroccoliButtonID
252   //     RadioButton       * kCauliflowerButtonID
253   //     View                kInnerContainerID
254   //       ScrollView        kScrollViewID
255   //         View
256   //           Link        * kRosettaLinkID
257   //           Link        * kStupeurEtTremblementLinkID
258   //           Link        * kDinerGameLinkID
259   //           Link        * kRidiculeLinkID
260   //           Link        * kClosetLinkID
261   //           Link        * kVisitingLinkID
262   //           Link        * kAmelieLinkID
263   //           Link        * kJoyeuxNoelLinkID
264   //           Link        * kCampingLinkID
265   //           Link        * kBriceDeNiceLinkID
266   //           Link        * kTaxiLinkID
267   //           Link        * kAsterixLinkID
268   //   NativeButton        * kOKButtonID
269   //   NativeButton        * kCancelButtonID
270   //   NativeButton        * kHelpButtonID
271   //   TabbedPane          * kStyleContainerID
272   //     View
273   //       Checkbox        * kBoldCheckBoxID
274   //       Checkbox        * kItalicCheckBoxID
275   //       Checkbox        * kUnderlinedCheckBoxID
276   //       Link            * kStyleHelpLinkID
277   //       Textfield       * kStyleTextEditID
278   //     Other
279   //   BorderView            kSearchContainerID
280   //     View
281   //       Textfield       * kSearchTextfieldID
282   //       NativeButton    * kSearchButtonID
283   //       Link            * kHelpLinkID
284   //   View                * kThumbnailContainerID
285   //     NativeButton      * kThumbnailStarID
286   //     NativeButton      * kThumbnailSuperStarID
287
288   GetContentsView()->set_background(
289       Background::CreateSolidBackground(SK_ColorWHITE));
290
291   Checkbox* cb = new Checkbox(ASCIIToUTF16("This is a checkbox"));
292   GetContentsView()->AddChildView(cb);
293   // In this fast paced world, who really has time for non hard-coded layout?
294   cb->SetBounds(10, 10, 200, 20);
295   cb->set_id(kTopCheckBoxID);
296
297   left_container_ = new PaneView();
298   left_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
299   left_container_->set_background(
300       Background::CreateSolidBackground(240, 240, 240));
301   left_container_->set_id(kLeftContainerID);
302   GetContentsView()->AddChildView(left_container_);
303   left_container_->SetBounds(10, 35, 250, 200);
304
305   int label_x = 5;
306   int label_width = 50;
307   int label_height = 15;
308   int text_field_width = 150;
309   int y = 10;
310   int gap_between_labels = 10;
311
312   Label* label = new Label(ASCIIToUTF16("Apple:"));
313   label->set_id(kAppleLabelID);
314   left_container_->AddChildView(label);
315   label->SetBounds(label_x, y, label_width, label_height);
316
317   Textfield* text_field = new Textfield();
318   text_field->set_id(kAppleTextfieldID);
319   left_container_->AddChildView(text_field);
320   text_field->SetBounds(label_x + label_width + 5, y,
321                         text_field_width, label_height);
322
323   y += label_height + gap_between_labels;
324
325   label = new Label(ASCIIToUTF16("Orange:"));
326   label->set_id(kOrangeLabelID);
327   left_container_->AddChildView(label);
328   label->SetBounds(label_x, y, label_width, label_height);
329
330   text_field = new Textfield();
331   text_field->set_id(kOrangeTextfieldID);
332   left_container_->AddChildView(text_field);
333   text_field->SetBounds(label_x + label_width + 5, y,
334                         text_field_width, label_height);
335
336   y += label_height + gap_between_labels;
337
338   label = new Label(ASCIIToUTF16("Banana:"));
339   label->set_id(kBananaLabelID);
340   left_container_->AddChildView(label);
341   label->SetBounds(label_x, y, label_width, label_height);
342
343   text_field = new Textfield();
344   text_field->set_id(kBananaTextfieldID);
345   left_container_->AddChildView(text_field);
346   text_field->SetBounds(label_x + label_width + 5, y,
347                         text_field_width, label_height);
348
349   y += label_height + gap_between_labels;
350
351   label = new Label(ASCIIToUTF16("Kiwi:"));
352   label->set_id(kKiwiLabelID);
353   left_container_->AddChildView(label);
354   label->SetBounds(label_x, y, label_width, label_height);
355
356   text_field = new Textfield();
357   text_field->set_id(kKiwiTextfieldID);
358   left_container_->AddChildView(text_field);
359   text_field->SetBounds(label_x + label_width + 5, y,
360                         text_field_width, label_height);
361
362   y += label_height + gap_between_labels;
363
364   LabelButton* button = new LabelButton(NULL, ASCIIToUTF16("Click me"));
365   button->SetStyle(Button::STYLE_BUTTON);
366   button->SetBounds(label_x, y + 10, 80, 30);
367   button->set_id(kFruitButtonID);
368   left_container_->AddChildView(button);
369   y += 40;
370
371   cb =  new Checkbox(ASCIIToUTF16("This is another check box"));
372   cb->SetBounds(label_x + label_width + 5, y, 180, 20);
373   cb->set_id(kFruitCheckBoxID);
374   left_container_->AddChildView(cb);
375   y += 20;
376
377   Combobox* combobox =  new Combobox(&combobox_model_);
378   combobox->SetBounds(label_x + label_width + 5, y, 150, 30);
379   combobox->set_id(kComboboxID);
380   left_container_->AddChildView(combobox);
381
382   right_container_ = new PaneView();
383   right_container_->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
384   right_container_->set_background(
385       Background::CreateSolidBackground(240, 240, 240));
386   right_container_->set_id(kRightContainerID);
387   GetContentsView()->AddChildView(right_container_);
388   right_container_->SetBounds(270, 35, 300, 200);
389
390   y = 10;
391   int radio_button_height = 18;
392   int gap_between_radio_buttons = 10;
393   RadioButton* radio_button = new RadioButton(ASCIIToUTF16("Asparagus"), 1);
394   radio_button->set_id(kAsparagusButtonID);
395   right_container_->AddChildView(radio_button);
396   radio_button->SetBounds(5, y, 70, radio_button_height);
397   radio_button->SetGroup(1);
398   y += radio_button_height + gap_between_radio_buttons;
399   radio_button = new RadioButton(ASCIIToUTF16("Broccoli"), 1);
400   radio_button->set_id(kBroccoliButtonID);
401   right_container_->AddChildView(radio_button);
402   radio_button->SetBounds(5, y, 70, radio_button_height);
403   radio_button->SetGroup(1);
404   RadioButton* radio_button_to_check = radio_button;
405   y += radio_button_height + gap_between_radio_buttons;
406   radio_button = new RadioButton(ASCIIToUTF16("Cauliflower"), 1);
407   radio_button->set_id(kCauliflowerButtonID);
408   right_container_->AddChildView(radio_button);
409   radio_button->SetBounds(5, y, 70, radio_button_height);
410   radio_button->SetGroup(1);
411   y += radio_button_height + gap_between_radio_buttons;
412
413   View* inner_container = new View();
414   inner_container->SetBorder(Border::CreateSolidBorder(1, SK_ColorBLACK));
415   inner_container->set_background(
416       Background::CreateSolidBackground(230, 230, 230));
417   inner_container->set_id(kInnerContainerID);
418   right_container_->AddChildView(inner_container);
419   inner_container->SetBounds(100, 10, 150, 180);
420
421   ScrollView* scroll_view = new ScrollView();
422   scroll_view->set_id(kScrollViewID);
423   inner_container->AddChildView(scroll_view);
424   scroll_view->SetBounds(1, 1, 148, 178);
425
426   View* scroll_content = new View();
427   scroll_content->SetBounds(0, 0, 200, 200);
428   scroll_content->set_background(
429       Background::CreateSolidBackground(200, 200, 200));
430   scroll_view->SetContents(scroll_content);
431
432   static const char* const kTitles[] = {
433       "Rosetta", "Stupeur et tremblement", "The diner game",
434       "Ridicule", "Le placard", "Les Visiteurs", "Amelie",
435       "Joyeux Noel", "Camping", "Brice de Nice",
436       "Taxi", "Asterix"
437   };
438
439   static const int kIDs[] = {
440       kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
441       kRidiculeLinkID, kClosetLinkID, kVisitingLinkID, kAmelieLinkID,
442       kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
443       kTaxiLinkID, kAsterixLinkID
444   };
445
446   DCHECK(arraysize(kTitles) == arraysize(kIDs));
447
448   y = 5;
449   for (size_t i = 0; i < arraysize(kTitles); ++i) {
450     Link* link = new Link(ASCIIToUTF16(kTitles[i]));
451     link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
452     link->set_id(kIDs[i]);
453     scroll_content->AddChildView(link);
454     link->SetBounds(5, y, 300, 15);
455     y += 15;
456   }
457
458   y = 250;
459   int width = 60;
460   button = new LabelButton(NULL, ASCIIToUTF16("OK"));
461   button->SetStyle(Button::STYLE_BUTTON);
462   button->set_id(kOKButtonID);
463   button->SetIsDefault(true);
464
465   GetContentsView()->AddChildView(button);
466   button->SetBounds(150, y, width, 30);
467
468   button = new LabelButton(NULL, ASCIIToUTF16("Cancel"));
469   button->SetStyle(Button::STYLE_BUTTON);
470   button->set_id(kCancelButtonID);
471   GetContentsView()->AddChildView(button);
472   button->SetBounds(220, y, width, 30);
473
474   button = new LabelButton(NULL, ASCIIToUTF16("Help"));
475   button->SetStyle(Button::STYLE_BUTTON);
476   button->set_id(kHelpButtonID);
477   GetContentsView()->AddChildView(button);
478   button->SetBounds(290, y, width, 30);
479
480   y += 40;
481
482   View* contents = NULL;
483   Link* link = NULL;
484
485   // Left bottom box with style checkboxes.
486   contents = new View();
487   contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
488   cb = new Checkbox(ASCIIToUTF16("Bold"));
489   contents->AddChildView(cb);
490   cb->SetBounds(10, 10, 50, 20);
491   cb->set_id(kBoldCheckBoxID);
492
493   cb = new Checkbox(ASCIIToUTF16("Italic"));
494   contents->AddChildView(cb);
495   cb->SetBounds(70, 10, 50, 20);
496   cb->set_id(kItalicCheckBoxID);
497
498   cb = new Checkbox(ASCIIToUTF16("Underlined"));
499   contents->AddChildView(cb);
500   cb->SetBounds(130, 10, 70, 20);
501   cb->set_id(kUnderlinedCheckBoxID);
502
503   link = new Link(ASCIIToUTF16("Help"));
504   contents->AddChildView(link);
505   link->SetBounds(10, 35, 70, 10);
506   link->set_id(kStyleHelpLinkID);
507
508   text_field = new Textfield();
509   contents->AddChildView(text_field);
510   text_field->SetBounds(10, 50, 100, 20);
511   text_field->set_id(kStyleTextEditID);
512
513   style_tab_ = new TabbedPane();
514   style_tab_->set_id(kStyleContainerID);
515   GetContentsView()->AddChildView(style_tab_);
516   style_tab_->SetBounds(10, y, 210, 100);
517   style_tab_->AddTab(ASCIIToUTF16("Style"), contents);
518   style_tab_->AddTab(ASCIIToUTF16("Other"), new View());
519
520   // Right bottom box with search.
521   contents = new View();
522   contents->set_background(Background::CreateSolidBackground(SK_ColorWHITE));
523   text_field = new Textfield();
524   contents->AddChildView(text_field);
525   text_field->SetBounds(10, 10, 100, 20);
526   text_field->set_id(kSearchTextfieldID);
527
528   button = new LabelButton(NULL, ASCIIToUTF16("Search"));
529   button->SetStyle(Button::STYLE_BUTTON);
530   contents->AddChildView(button);
531   button->SetBounds(112, 5, 60, 30);
532   button->set_id(kSearchButtonID);
533
534   link = new Link(ASCIIToUTF16("Help"));
535   link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
536   link->set_id(kHelpLinkID);
537   contents->AddChildView(link);
538   link->SetBounds(175, 10, 30, 20);
539
540   search_border_view_ = new BorderView(contents);
541   search_border_view_->set_id(kSearchContainerID);
542
543   GetContentsView()->AddChildView(search_border_view_);
544   search_border_view_->SetBounds(300, y, 240, 50);
545
546   y += 60;
547
548   contents = new View();
549   contents->SetFocusable(true);
550   contents->set_background(Background::CreateSolidBackground(SK_ColorBLUE));
551   contents->set_id(kThumbnailContainerID);
552   button = new LabelButton(NULL, ASCIIToUTF16("Star"));
553   button->SetStyle(Button::STYLE_BUTTON);
554   contents->AddChildView(button);
555   button->SetBounds(5, 5, 50, 30);
556   button->set_id(kThumbnailStarID);
557   button = new LabelButton(NULL, ASCIIToUTF16("SuperStar"));
558   button->SetStyle(Button::STYLE_BUTTON);
559   contents->AddChildView(button);
560   button->SetBounds(60, 5, 100, 30);
561   button->set_id(kThumbnailSuperStarID);
562
563   GetContentsView()->AddChildView(contents);
564   contents->SetBounds(250, y, 200, 50);
565   // We can only call RadioButton::SetChecked() on the radio-button is part of
566   // the view hierarchy.
567   radio_button_to_check->SetChecked(true);
568 }
569
570 TEST_F(FocusTraversalTest, NormalTraversal) {
571   const int kTraversalIDs[] = { kTopCheckBoxID,  kAppleTextfieldID,
572       kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
573       kFruitButtonID, kFruitCheckBoxID, kComboboxID, kBroccoliButtonID,
574       kRosettaLinkID, kStupeurEtTremblementLinkID,
575       kDinerGameLinkID, kRidiculeLinkID, kClosetLinkID, kVisitingLinkID,
576       kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID, kBriceDeNiceLinkID,
577       kTaxiLinkID, kAsterixLinkID, kOKButtonID, kCancelButtonID, kHelpButtonID,
578       kStyleContainerID, kBoldCheckBoxID, kItalicCheckBoxID,
579       kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
580       kSearchTextfieldID, kSearchButtonID, kHelpLinkID,
581       kThumbnailContainerID, kThumbnailStarID, kThumbnailSuperStarID };
582
583   // Let's traverse the whole focus hierarchy (several times, to make sure it
584   // loops OK).
585   GetFocusManager()->ClearFocus();
586   for (int i = 0; i < 3; ++i) {
587     for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
588       GetFocusManager()->AdvanceFocus(false);
589       View* focused_view = GetFocusManager()->GetFocusedView();
590       EXPECT_TRUE(focused_view != NULL);
591       if (focused_view)
592         EXPECT_EQ(kTraversalIDs[j], focused_view->id());
593     }
594   }
595
596   // Let's traverse in reverse order.
597   GetFocusManager()->ClearFocus();
598   for (int i = 0; i < 3; ++i) {
599     for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
600       GetFocusManager()->AdvanceFocus(true);
601       View* focused_view = GetFocusManager()->GetFocusedView();
602       EXPECT_TRUE(focused_view != NULL);
603       if (focused_view)
604         EXPECT_EQ(kTraversalIDs[j], focused_view->id());
605     }
606   }
607 }
608
609 TEST_F(FocusTraversalTest, TraversalWithNonEnabledViews) {
610   const int kDisabledIDs[] = {
611       kBananaTextfieldID, kFruitCheckBoxID, kComboboxID, kAsparagusButtonID,
612       kCauliflowerButtonID, kClosetLinkID, kVisitingLinkID, kBriceDeNiceLinkID,
613       kTaxiLinkID, kAsterixLinkID, kHelpButtonID, kBoldCheckBoxID,
614       kSearchTextfieldID, kHelpLinkID };
615
616   const int kTraversalIDs[] = { kTopCheckBoxID,  kAppleTextfieldID,
617       kOrangeTextfieldID, kKiwiTextfieldID, kFruitButtonID, kBroccoliButtonID,
618       kRosettaLinkID, kStupeurEtTremblementLinkID, kDinerGameLinkID,
619       kRidiculeLinkID, kAmelieLinkID, kJoyeuxNoelLinkID, kCampingLinkID,
620       kOKButtonID, kCancelButtonID, kStyleContainerID, kItalicCheckBoxID,
621       kUnderlinedCheckBoxID, kStyleHelpLinkID, kStyleTextEditID,
622       kSearchButtonID, kThumbnailContainerID, kThumbnailStarID,
623       kThumbnailSuperStarID };
624
625   // Let's disable some views.
626   for (size_t i = 0; i < arraysize(kDisabledIDs); i++) {
627     View* v = FindViewByID(kDisabledIDs[i]);
628     ASSERT_TRUE(v != NULL);
629     v->SetEnabled(false);
630   }
631
632   View* focused_view;
633   // Let's do one traversal (several times, to make sure it loops ok).
634   GetFocusManager()->ClearFocus();
635   for (int i = 0; i < 3; ++i) {
636     for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
637       GetFocusManager()->AdvanceFocus(false);
638       focused_view = GetFocusManager()->GetFocusedView();
639       EXPECT_TRUE(focused_view != NULL);
640       if (focused_view)
641         EXPECT_EQ(kTraversalIDs[j], focused_view->id());
642     }
643   }
644
645   // Same thing in reverse.
646   GetFocusManager()->ClearFocus();
647   for (int i = 0; i < 3; ++i) {
648     for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
649       GetFocusManager()->AdvanceFocus(true);
650       focused_view = GetFocusManager()->GetFocusedView();
651       EXPECT_TRUE(focused_view != NULL);
652       if (focused_view)
653         EXPECT_EQ(kTraversalIDs[j], focused_view->id());
654     }
655   }
656 }
657
658 TEST_F(FocusTraversalTest, TraversalWithInvisibleViews) {
659   const int kInvisibleIDs[] = { kTopCheckBoxID, kOKButtonID,
660       kThumbnailContainerID };
661
662   const int kTraversalIDs[] = { kAppleTextfieldID, kOrangeTextfieldID,
663       kBananaTextfieldID, kKiwiTextfieldID, kFruitButtonID, kFruitCheckBoxID,
664       kComboboxID, kBroccoliButtonID, kRosettaLinkID,
665       kStupeurEtTremblementLinkID, kDinerGameLinkID, kRidiculeLinkID,
666       kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
667       kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID,
668       kCancelButtonID, kHelpButtonID, kStyleContainerID, kBoldCheckBoxID,
669       kItalicCheckBoxID, kUnderlinedCheckBoxID, kStyleHelpLinkID,
670       kStyleTextEditID, kSearchTextfieldID, kSearchButtonID, kHelpLinkID };
671
672
673   // Let's make some views invisible.
674   for (size_t i = 0; i < arraysize(kInvisibleIDs); i++) {
675     View* v = FindViewByID(kInvisibleIDs[i]);
676     ASSERT_TRUE(v != NULL);
677     v->SetVisible(false);
678   }
679
680   View* focused_view;
681   // Let's do one traversal (several times, to make sure it loops ok).
682   GetFocusManager()->ClearFocus();
683   for (int i = 0; i < 3; ++i) {
684     for (size_t j = 0; j < arraysize(kTraversalIDs); j++) {
685       GetFocusManager()->AdvanceFocus(false);
686       focused_view = GetFocusManager()->GetFocusedView();
687       EXPECT_TRUE(focused_view != NULL);
688       if (focused_view)
689         EXPECT_EQ(kTraversalIDs[j], focused_view->id());
690     }
691   }
692
693   // Same thing in reverse.
694   GetFocusManager()->ClearFocus();
695   for (int i = 0; i < 3; ++i) {
696     for (int j = arraysize(kTraversalIDs) - 1; j >= 0; --j) {
697       GetFocusManager()->AdvanceFocus(true);
698       focused_view = GetFocusManager()->GetFocusedView();
699       EXPECT_TRUE(focused_view != NULL);
700       if (focused_view)
701         EXPECT_EQ(kTraversalIDs[j], focused_view->id());
702     }
703   }
704 }
705
706 TEST_F(FocusTraversalTest, PaneTraversal) {
707   // Tests trapping the traversal within a pane - useful for full
708   // keyboard accessibility for toolbars.
709
710   // First test the left container.
711   const int kLeftTraversalIDs[] = {
712     kAppleTextfieldID,
713     kOrangeTextfieldID, kBananaTextfieldID, kKiwiTextfieldID,
714     kFruitButtonID, kFruitCheckBoxID, kComboboxID };
715
716   FocusSearch focus_search_left(left_container_, true, false);
717   left_container_->EnablePaneFocus(&focus_search_left);
718   FindViewByID(kComboboxID)->RequestFocus();
719
720   // Traverse the focus hierarchy within the pane several times.
721   for (int i = 0; i < 3; ++i) {
722     for (size_t j = 0; j < arraysize(kLeftTraversalIDs); j++) {
723       GetFocusManager()->AdvanceFocus(false);
724       View* focused_view = GetFocusManager()->GetFocusedView();
725       EXPECT_TRUE(focused_view != NULL);
726       if (focused_view)
727         EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
728     }
729   }
730
731   // Traverse in reverse order.
732   FindViewByID(kAppleTextfieldID)->RequestFocus();
733   for (int i = 0; i < 3; ++i) {
734     for (int j = arraysize(kLeftTraversalIDs) - 1; j >= 0; --j) {
735       GetFocusManager()->AdvanceFocus(true);
736       View* focused_view = GetFocusManager()->GetFocusedView();
737       EXPECT_TRUE(focused_view != NULL);
738       if (focused_view)
739         EXPECT_EQ(kLeftTraversalIDs[j], focused_view->id());
740     }
741   }
742
743   // Now test the right container, but this time with accessibility mode.
744   // Make some links not focusable, but mark one of them as
745   // "accessibility focusable", so it should show up in the traversal.
746   const int kRightTraversalIDs[] = {
747     kBroccoliButtonID, kDinerGameLinkID, kRidiculeLinkID,
748     kClosetLinkID, kVisitingLinkID, kAmelieLinkID, kJoyeuxNoelLinkID,
749     kCampingLinkID, kBriceDeNiceLinkID, kTaxiLinkID, kAsterixLinkID };
750
751   FocusSearch focus_search_right(right_container_, true, true);
752   right_container_->EnablePaneFocus(&focus_search_right);
753   FindViewByID(kRosettaLinkID)->SetFocusable(false);
754   FindViewByID(kStupeurEtTremblementLinkID)->SetFocusable(false);
755   FindViewByID(kDinerGameLinkID)->SetAccessibilityFocusable(true);
756   FindViewByID(kDinerGameLinkID)->SetFocusable(false);
757   FindViewByID(kAsterixLinkID)->RequestFocus();
758
759   // Traverse the focus hierarchy within the pane several times.
760   for (int i = 0; i < 3; ++i) {
761     for (size_t j = 0; j < arraysize(kRightTraversalIDs); j++) {
762       GetFocusManager()->AdvanceFocus(false);
763       View* focused_view = GetFocusManager()->GetFocusedView();
764       EXPECT_TRUE(focused_view != NULL);
765       if (focused_view)
766         EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
767     }
768   }
769
770   // Traverse in reverse order.
771   FindViewByID(kBroccoliButtonID)->RequestFocus();
772   for (int i = 0; i < 3; ++i) {
773     for (int j = arraysize(kRightTraversalIDs) - 1; j >= 0; --j) {
774       GetFocusManager()->AdvanceFocus(true);
775       View* focused_view = GetFocusManager()->GetFocusedView();
776       EXPECT_TRUE(focused_view != NULL);
777       if (focused_view)
778         EXPECT_EQ(kRightTraversalIDs[j], focused_view->id());
779     }
780   }
781 }
782
783 }  // namespace views