Skip to content

Commit

Permalink
Remove invisible views from tab order.
Browse files Browse the repository at this point in the history
AutofillDialogViews::Layout() and AutofillDialogViews::GetPreferredSize()
short-circuit logic when either the loading shield or sign-in webview are 
showing. Unfortunately this has the side-effect of keeping any focusable input
in the dialog in the tab order. Recently I made suggested buttons (down arrows)
focusable so this became more noticeable, but the problem has always been there
(if you currently change all sections to add mode and attempt to sign in you'll
have tons of invisible elements to focus).

This CL makes visually invisible nodes also !visible() to remove them from the
tab order (see: views::View::IsFocusable()).

R=isherman@chromium.org
BUG=306484

Review URL: https://codereview.chromium.org/27085002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229111 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
dbeam@chromium.org committed Oct 17, 2013
1 parent 77a8ca1 commit 127c6a1
Show file tree
Hide file tree
Showing 14 changed files with 396 additions and 96 deletions.
11 changes: 11 additions & 0 deletions chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ MockAutofillDialogViewDelegate::MockAutofillDialogViewDelegate() {
gfx::Image(),
string16(),
gfx::Image()));
DefaultValue<FieldIconMap>::Set(FieldIconMap());
DefaultValue<std::vector<DialogNotification> >::Set(
std::vector<DialogNotification>());

// SECTION_CC *must* have a CREDIT_CARD_VERIFICATION_CODE field.
const DetailInput kCreditCardInputs[] = {
Expand All @@ -49,6 +52,11 @@ MockAutofillDialogViewDelegate::MockAutofillDialogViewDelegate() {
.WillByDefault(Return(false));
}

void MockAutofillDialogViewDelegate::SetWebContents(
content::WebContents* contents) {
testing::DefaultValue<content::WebContents*>::Set(contents);
}

MockAutofillDialogViewDelegate::~MockAutofillDialogViewDelegate() {
using testing::DefaultValue;

Expand All @@ -57,6 +65,9 @@ MockAutofillDialogViewDelegate::~MockAutofillDialogViewDelegate() {
DefaultValue<ValidityMessages>::Clear();
DefaultValue<string16>::Clear();
DefaultValue<const DetailInputs&>::Clear();
DefaultValue<FieldIconMap>::Clear();
DefaultValue<std::vector<DialogNotification> >::Clear();
DefaultValue<content::WebContents*>::Clear();
}

} // namespace autofill
15 changes: 9 additions & 6 deletions chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ class MockAutofillDialogViewDelegate : public AutofillDialogViewDelegate {
string16(DialogSection, ServerFieldType, const string16&));
MOCK_METHOD2(InputsAreValid, ValidityMessages(DialogSection,
const DetailOutputMap&));
MOCK_METHOD6(UserEditedOrActivatedInput,void(DialogSection,
const DetailInput*,
gfx::NativeView,
const gfx::Rect&,
const string16&,
bool was_edit));
MOCK_METHOD6(UserEditedOrActivatedInput, void(DialogSection,
const DetailInput*,
gfx::NativeView,
const gfx::Rect&,
const string16&,
bool was_edit));
MOCK_METHOD1(HandleKeyPressEventInInput,
bool(const content::NativeWebKeyboardEvent& event));
MOCK_METHOD0(FocusMoved, void());
Expand All @@ -83,6 +83,9 @@ class MockAutofillDialogViewDelegate : public AutofillDialogViewDelegate {
MOCK_METHOD0(profile, Profile*());
MOCK_METHOD0(GetWebContents, content::WebContents*());

// Set which web contents initiated showing the dialog.
void SetWebContents(content::WebContents* contents);

private:
DetailInputs default_inputs_;
DetailInputs cc_default_inputs_; // Default inputs for SECTION_CC.
Expand Down
57 changes: 50 additions & 7 deletions chrome/browser/ui/views/autofill/autofill_dialog_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,46 @@ void AutofillDialogViews::AccountChooser::OnMenuButtonClicked(
}
}

void AutofillDialogViews::ShowDialogInMode(DialogMode dialog_mode) {
std::vector<views::View*> visible;

if (dialog_mode == LOADING) {
visible.push_back(loading_shield_);
} else if (dialog_mode == SIGN_IN) {
visible.push_back(sign_in_webview_);
} else {
DCHECK_EQ(DETAIL_INPUT, dialog_mode);
visible.push_back(notification_area_);
visible.push_back(scrollable_area_);
}

for (size_t i = 0; i < visible.size(); ++i) {
DCHECK_GE(GetIndexOf(visible[i]), 0);
}

for (int i = 0; i < child_count(); ++i) {
views::View* child = child_at(i);
child->SetVisible(
std::find(visible.begin(), visible.end(), child) != visible.end());
}
}

views::View* AutofillDialogViews::GetLoadingShieldForTesting() {
return loading_shield_;
}

views::View* AutofillDialogViews::GetSignInWebviewForTesting() {
return sign_in_webview_;
}

views::View* AutofillDialogViews::GetNotificationAreaForTesting() {
return notification_area_;
}

views::View* AutofillDialogViews::GetScrollableAreaForTesting() {
return scrollable_area_;
}

void AutofillDialogViews::AccountChooser::LinkClicked(views::Link* source,
int event_flags) {
delegate_->SignInLinkClicked();
Expand Down Expand Up @@ -877,9 +917,10 @@ void AutofillDialogViews::OnWidgetClosing(views::Widget* widget) {
void AutofillDialogViews::OnWidgetBoundsChanged(views::Widget* widget,
const gfx::Rect& new_bounds) {
// Notify the web contents of its new auto-resize limits.
if (sign_in_delegate_ && sign_in_webview_->visible())
if (sign_in_delegate_ && sign_in_webview_->visible()) {
sign_in_delegate_->UpdateLimitsAndEnableAutoResize(
GetMinimumSignInViewSize(), GetMaximumSignInViewSize());
}
HideErrorBubble();
}

Expand Down Expand Up @@ -1358,9 +1399,11 @@ void AutofillDialogViews::UpdateAccountChooser() {
if (show_loading) {
loading_shield_height_ = std::max(kInitialLoadingShieldHeight,
GetContentsBounds().height());
ShowDialogInMode(LOADING);
} else {
ShowDialogInMode(DETAIL_INPUT);
}

loading_shield_->SetVisible(show_loading);
InvalidateLayout();
ContentsPreferredSizeChanged();
}
Expand Down Expand Up @@ -1398,7 +1441,6 @@ void AutofillDialogViews::UpdateButtonStrip() {
}

void AutofillDialogViews::UpdateOverlay() {
DialogOverlayState overlay_state = delegate_->GetDialogOverlay();
overlay_view_->UpdateState();
}

Expand Down Expand Up @@ -1503,15 +1545,16 @@ const content::NavigationController* AutofillDialogViews::ShowSignIn() {
GetMinimumSignInViewSize(), GetMaximumSignInViewSize()));
sign_in_webview_->LoadInitialURL(wallet::GetSignInUrl());

sign_in_webview_->SetVisible(true);
ShowDialogInMode(SIGN_IN);
sign_in_webview_->RequestFocus();

UpdateButtonStrip();
ContentsPreferredSizeChanged();
return &sign_in_webview_->web_contents()->GetController();
}

void AutofillDialogViews::HideSignIn() {
sign_in_webview_->SetVisible(false);
ShowDialogInMode(DETAIL_INPUT);
UpdateButtonStrip();
ContentsPreferredSizeChanged();
}
Expand Down Expand Up @@ -1936,15 +1979,15 @@ void AutofillDialogViews::InitChildViews() {
AddChildView(scrollable_area_);

loading_shield_ = new LoadingAnimationView(delegate_->SpinnerText());
loading_shield_->SetVisible(false);
AddChildView(loading_shield_);

sign_in_webview_ = new views::WebView(delegate_->profile());
sign_in_webview_->SetVisible(false);
AddChildView(sign_in_webview_);

overlay_view_ = new OverlayView(delegate_);
overlay_view_->SetVisible(false);

ShowDialogInMode(DETAIL_INPUT);
}

views::View* AutofillDialogViews::CreateDetailsContainer() {
Expand Down
20 changes: 20 additions & 0 deletions chrome/browser/ui/views/autofill/autofill_dialog_views.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,26 @@ class AutofillDialogViews : public AutofillDialogView,
virtual void OnMenuButtonClicked(views::View* source,
const gfx::Point& point) OVERRIDE;

protected:
// What the entire dialog should be doing (e.g. gathering info from the user,
// asking the user to sign in, etc.).
enum DialogMode {
DETAIL_INPUT,
LOADING,
SIGN_IN,
};

// Changes the function of the whole dialog. Currently this can show a loading
// shield, an embedded sign in web view, or the more typical detail input mode
// (suggestions and form inputs).
void ShowDialogInMode(DialogMode dialog_mode);

// Exposed for testing.
views::View* GetLoadingShieldForTesting();
views::View* GetSignInWebviewForTesting();
views::View* GetNotificationAreaForTesting();
views::View* GetScrollableAreaForTesting();

private:
// A class that creates and manages a widget for error messages.
class ErrorBubble : public views::BubbleDelegateView {
Expand Down
119 changes: 119 additions & 0 deletions chrome/browser/ui/views/autofill/autofill_dialog_views_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/autofill/autofill_dialog_views.h"

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/ui/autofill/mock_autofill_dialog_view_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
#include "components/web_modal/test_web_contents_modal_dialog_host.h"
#include "components/web_modal/test_web_contents_modal_dialog_manager_delegate.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/widget.h"

namespace autofill {

namespace {

using web_modal::WebContentsModalDialogManager;

// A views implementation of the Autofill dialog with slightly more testability.
class TestAutofillDialogViews : public AutofillDialogViews {
public:
explicit TestAutofillDialogViews(AutofillDialogViewDelegate* delegate)
: AutofillDialogViews(delegate) {}
virtual ~TestAutofillDialogViews() {}

void ShowLoadingMode() { ShowDialogInMode(LOADING); }
void ShowSignInMode() { ShowDialogInMode(SIGN_IN); }
void ShowDetailInputMode() { ShowDialogInMode(DETAIL_INPUT); }

using AutofillDialogViews::GetLoadingShieldForTesting;
using AutofillDialogViews::GetSignInWebviewForTesting;
using AutofillDialogViews::GetNotificationAreaForTesting;
using AutofillDialogViews::GetScrollableAreaForTesting;

private:
DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogViews);
};

} // namespace

class AutofillDialogViewsTest : public TestWithBrowserView {
public:
AutofillDialogViewsTest() {}
virtual ~AutofillDialogViewsTest() {}

// TestWithBrowserView:
virtual void SetUp() OVERRIDE {
TestWithBrowserView::SetUp();

AddTab(browser(), GURL());
TabStripModel* tab_strip_model = browser()->tab_strip_model();
content::WebContents* contents = tab_strip_model->GetWebContentsAt(0);
ASSERT_TRUE(contents);
view_delegate_.SetWebContents(contents);

BrowserView* browser_view =
BrowserView::GetBrowserViewForBrowser(browser());
dialog_host_.reset(new web_modal::TestWebContentsModalDialogHost(
browser_view->GetWidget()->GetNativeView()));
dialog_delegate_.set_web_contents_modal_dialog_host(dialog_host_.get());

WebContentsModalDialogManager* dialog_manager =
WebContentsModalDialogManager::FromWebContents(contents);
ASSERT_TRUE(dialog_manager);
dialog_manager->SetDelegate(&dialog_delegate_);

dialog_.reset(new TestAutofillDialogViews(&view_delegate_));
dialog_->Show();
}

virtual void TearDown() OVERRIDE {
dialog_->GetWidget()->CloseNow();

TestWithBrowserView::TearDown();
}

TestAutofillDialogViews* dialog() { return dialog_.get(); }

private:
// Fake dialog delegate and host to isolate test behavior.
web_modal::TestWebContentsModalDialogManagerDelegate dialog_delegate_;
scoped_ptr<web_modal::TestWebContentsModalDialogHost> dialog_host_;

// Mock view delegate as this file only tests the view.
testing::NiceMock<MockAutofillDialogViewDelegate> view_delegate_;

scoped_ptr<TestAutofillDialogViews> dialog_;
};

// Switching between dialog modes should make visually hidden views !visible().
TEST_F(AutofillDialogViewsTest, ShowDialogInMode) {
dialog()->ShowLoadingMode();
EXPECT_TRUE(dialog()->GetLoadingShieldForTesting()->visible());
EXPECT_FALSE(dialog()->GetSignInWebviewForTesting()->visible());
EXPECT_FALSE(dialog()->GetNotificationAreaForTesting()->visible());
EXPECT_FALSE(dialog()->GetScrollableAreaForTesting()->visible());

dialog()->ShowSignInMode();
EXPECT_FALSE(dialog()->GetLoadingShieldForTesting()->visible());
EXPECT_TRUE(dialog()->GetSignInWebviewForTesting()->visible());
EXPECT_FALSE(dialog()->GetNotificationAreaForTesting()->visible());
EXPECT_FALSE(dialog()->GetScrollableAreaForTesting()->visible());

dialog()->ShowDetailInputMode();
EXPECT_FALSE(dialog()->GetLoadingShieldForTesting()->visible());
EXPECT_FALSE(dialog()->GetSignInWebviewForTesting()->visible());
EXPECT_TRUE(dialog()->GetNotificationAreaForTesting()->visible());
EXPECT_TRUE(dialog()->GetScrollableAreaForTesting()->visible());
}

} // namespace autofill
Loading

0 comments on commit 127c6a1

Please sign in to comment.