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