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