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