diff options
19 files changed, 182 insertions, 34 deletions
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index 146c429..64c4db8 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -717,13 +717,15 @@ void AutofillManager::OnHideAutofillPopup() { external_delegate_->HideAutofillPopup(); } -void AutofillManager::OnShowPasswordGenerationPopup(const gfx::Rect& bounds) { +void AutofillManager::OnShowPasswordGenerationPopup( + const gfx::Rect& bounds, + const webkit::forms::PasswordForm& form) { #if defined(OS_ANDROID) NOTIMPLEMENTED(); #else Browser* browser = browser::FindLastActiveWithProfile( Profile::FromBrowserContext(web_contents()->GetBrowserContext())); - browser->window()->ShowPasswordGenerationBubble(bounds); + browser->window()->ShowPasswordGenerationBubble(bounds, form); #endif // #if defined(OS_ANDROID) } diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index b483097..f70314b6 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -56,6 +56,7 @@ namespace webkit { namespace forms { struct FormData; struct FormField; +struct PasswordForm; struct PasswordFormFillData; } } @@ -93,7 +94,8 @@ class AutofillManager : public content::NotificationObserver, void OnDidFillAutofillFormData(const base::TimeTicks& timestamp); void OnShowAutofillDialog(); void OnDidPreviewAutofillFormData(); - void OnShowPasswordGenerationPopup(const gfx::Rect& bounds); + void OnShowPasswordGenerationPopup(const gfx::Rect& bounds, + const webkit::forms::PasswordForm& form); // Remove the credit card or Autofill profile that matches |unique_id| // from the database. diff --git a/chrome/browser/password_manager/password_form_manager.cc b/chrome/browser/password_manager/password_form_manager.cc index 1e5d20e..13c942d 100644 --- a/chrome/browser/password_manager/password_form_manager.cc +++ b/chrome/browser/password_manager/password_form_manager.cc @@ -26,6 +26,7 @@ PasswordFormManager::PasswordFormManager(Profile* profile, : best_matches_deleter_(&best_matches_), observed_form_(observed_form), is_new_login_(true), + has_generated_password_(false), password_manager_(password_manager), pending_login_query_(0), preferred_match_(NULL), @@ -142,6 +143,17 @@ bool PasswordFormManager::IsNewLogin() { return is_new_login_; } +void PasswordFormManager::SetHasGeneratedPassword() { + has_generated_password_ = true; +} + +bool PasswordFormManager::HasGeneratedPassword() { + // This check is permissive, as the user may have generated a password and + // then edited it in the form itself. However, even in this case the user + // has already given consent, so we treat these cases the same. + return has_generated_password_; +} + bool PasswordFormManager::HasValidPasswordForm() { DCHECK_EQ(state_, POST_MATCHING_PHASE); // Non-HTML password forms (primarily HTTP and FTP autentication) diff --git a/chrome/browser/password_manager/password_form_manager.h b/chrome/browser/password_manager/password_form_manager.h index 49d44a6..10c0d77 100644 --- a/chrome/browser/password_manager/password_form_manager.h +++ b/chrome/browser/password_manager/password_form_manager.h @@ -67,6 +67,11 @@ class PasswordFormManager : public PasswordStoreConsumer { // login or password field are not considered valid. bool HasValidPasswordForm(); + // These functions are used to determine if this form has had it's password + // auto generated by the browser. + bool HasGeneratedPassword(); + void SetHasGeneratedPassword(); + // Determines if we need to autofill given the results of the query. void OnRequestDone( int handle, const std::vector<webkit::forms::PasswordForm*>& result); @@ -196,6 +201,9 @@ class PasswordFormManager : public PasswordStoreConsumer { // to an existing one. bool is_new_login_; + // Whether this form has an auto generated password. + bool has_generated_password_; + // PasswordManager owning this. const PasswordManager* const password_manager_; diff --git a/chrome/browser/password_manager/password_manager.cc b/chrome/browser/password_manager/password_manager.cc index cceed28..f83ce24 100644 --- a/chrome/browser/password_manager/password_manager.cc +++ b/chrome/browser/password_manager/password_manager.cc @@ -74,6 +74,27 @@ PasswordManager::PasswordManager(WebContents* web_contents, PasswordManager::~PasswordManager() { } +void PasswordManager::SetFormHasGeneratedPassword(const PasswordForm& form) { + for (ScopedVector<PasswordFormManager>::iterator iter = + pending_login_managers_.begin(); + iter != pending_login_managers_.end(); ++iter) { + if ((*iter)->DoesManage(form)) { + (*iter)->SetHasGeneratedPassword(); + return; + } + } + // If there is no corresponding PasswordFormManager, we create one. This is + // not the common case, and should only happen when there is a bug in our + // ability to detect forms. + bool ssl_valid = (form.origin.SchemeIsSecure() && + !delegate_->DidLastPageLoadEncounterSSLErrors()); + PasswordFormManager* manager = + new PasswordFormManager(delegate_->GetProfile(), this, form, ssl_valid); + pending_login_managers_.push_back(manager); + manager->SetHasGeneratedPassword(); + // TODO(gcasto): Add UMA stats to track this. +} + bool PasswordManager::IsSavingEnabled() const { return IsFillingEnabled() && !delegate_->GetProfile()->IsOffTheRecord(); } @@ -205,15 +226,16 @@ void PasswordManager::OnPasswordFormsRendered( return; } - // Looks like a successful login attempt. Either show an infobar or update - // the previously saved login data. + // Looks like a successful login attempt. Either show an infobar or + // automatically save the login data. We prompt when the user hasn't already + // given consent, either through previously accepting the infobar or by having + // the browser generate the password. provisional_save_manager_->SubmitPassed(); - if (provisional_save_manager_->IsNewLogin()) { + if (provisional_save_manager_->IsNewLogin() && + !provisional_save_manager_->HasGeneratedPassword()) { delegate_->AddSavePasswordInfoBarIfPermitted( provisional_save_manager_.release()); } else { - // If the save is not a new username entry, then we just want to save this - // data (since the user already has related data saved), so don't prompt. provisional_save_manager_->Save(); provisional_save_manager_.reset(); } diff --git a/chrome/browser/password_manager/password_manager.h b/chrome/browser/password_manager/password_manager.h index 50379f6..afeab33 100644 --- a/chrome/browser/password_manager/password_manager.h +++ b/chrome/browser/password_manager/password_manager.h @@ -53,6 +53,9 @@ class PasswordManager : public LoginModel, // LoginModel implementation. virtual void SetObserver(LoginModelObserver* observer) OVERRIDE; + // Mark this form as having a generated password. + void SetFormHasGeneratedPassword(const webkit::forms::PasswordForm& form); + // TODO(isherman): This should not be public, but is currently being used by // the LoginPrompt code. // When a form is submitted, we prepare to save the password but wait diff --git a/chrome/browser/password_manager/password_manager_unittest.cc b/chrome/browser/password_manager/password_manager_unittest.cc index 25dc33b..58363e8 100644 --- a/chrome/browser/password_manager/password_manager_unittest.cc +++ b/chrome/browser/password_manager/password_manager_unittest.cc @@ -136,6 +136,35 @@ TEST_F(PasswordManagerTest, FormSubmitEmptyStore) { form_to_save->Save(); } +TEST_F(PasswordManagerTest, GeneratedPasswordFormSubmitEmptyStore) { + // This test is the same FormSubmitEmptyStore, except that it simulates the + // user generating the password through the browser. + std::vector<PasswordForm*> result; // Empty password store. + EXPECT_CALL(delegate_, FillPasswordForm(_)).Times(Exactly(0)); + EXPECT_CALL(*store_, GetLogins(_,_)) + .WillOnce(DoAll(WithArg<1>(InvokeConsumer(0, result)), Return(0))); + std::vector<PasswordForm> observed; + PasswordForm form(MakeSimpleForm()); + observed.push_back(form); + manager()->OnPasswordFormsParsed(observed); // The initial load. + manager()->OnPasswordFormsRendered(observed); // The initial layout. + + // Simulate the user generating the password and submitting the form. + manager()->SetFormHasGeneratedPassword(form); + manager()->ProvisionallySavePassword(form); + + // The user should not be presented with an infobar as they have already given + // consent. The form should be saved once navigation occurs. + EXPECT_CALL(delegate_, + AddSavePasswordInfoBarIfPermitted(_)).Times(Exactly(0)); + EXPECT_CALL(*store_, AddLogin(FormMatches(form))); + + // Now the password manager waits for the navigation to complete. + observed.clear(); + manager()->OnPasswordFormsParsed(observed); // The post-navigation load. + manager()->OnPasswordFormsRendered(observed); // The post-navigation layout. +} + TEST_F(PasswordManagerTest, FormSubmitNoGoodMatch) { // Same as above, except with an existing form for the same signon realm, // but different origin. Detailed cases like this are covered by diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h index 0753c33..7532fedb 100644 --- a/chrome/browser/ui/browser_window.h +++ b/chrome/browser/ui/browser_window.h @@ -45,6 +45,11 @@ class Rect; class Size; } +namespace webkit { +namespace forms { +struct PasswordForm; +} +} enum DevToolsDockSide { DEVTOOLS_DOCK_SIDE_BOTTOM = 0, @@ -363,10 +368,13 @@ class BrowserWindow : public BaseWindow { // Shows the avatar bubble on the window frame off of the avatar button. virtual void ShowAvatarBubbleFromAvatarButton() = 0; - // Show bubble for password generation positioned relative to |rect|. A stub - // implementation is provided since this feature is currently only available - // for Windows. - virtual void ShowPasswordGenerationBubble(const gfx::Rect& rect) {} + // Show bubble for password generation positioned relative to |rect|. |form| + // is the form that contains the password field that the bubble will be + // associated with. A stub implementation is provided since this feature is + // currently not available on mac. + virtual void ShowPasswordGenerationBubble( + const gfx::Rect& rect, + const webkit::forms::PasswordForm& form) {} protected: friend void browser::CloseAllBrowsers(); diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc index 1bc976a..1c0074e 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.cc +++ b/chrome/browser/ui/gtk/browser_window_gtk.cc @@ -1288,16 +1288,25 @@ void BrowserWindowGtk::ShowAvatarBubbleFromAvatarButton() { titlebar_->avatar_button()->ShowAvatarBubble(); } -void BrowserWindowGtk::ShowPasswordGenerationBubble(const gfx::Rect& rect) { +void BrowserWindowGtk::ShowPasswordGenerationBubble( + const gfx::Rect& rect, + const webkit::forms::PasswordForm& form) { WebContents* web_contents = browser_->GetSelectedWebContents(); if (!web_contents || !web_contents->GetContentNativeView()) { return; } + TabContentsWrapper* tab_contents = + TabContentsWrapper::GetCurrentWrapperForContents(web_contents); + if (!tab_contents) + return; + new PasswordGenerationBubbleGtk(rect, + form, web_contents->GetContentNativeView(), browser()->profile(), - web_contents->GetRenderViewHost()); + web_contents->GetRenderViewHost(), + tab_contents->password_manager()); } void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads() { diff --git a/chrome/browser/ui/gtk/browser_window_gtk.h b/chrome/browser/ui/gtk/browser_window_gtk.h index 13e52a9..2e31480 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.h +++ b/chrome/browser/ui/gtk/browser_window_gtk.h @@ -169,7 +169,9 @@ class BrowserWindowGtk : public BrowserWindow, virtual void ShowAvatarBubble(content::WebContents* web_contents, const gfx::Rect& rect) OVERRIDE; virtual void ShowAvatarBubbleFromAvatarButton() OVERRIDE; - virtual void ShowPasswordGenerationBubble(const gfx::Rect& rect) OVERRIDE; + virtual void ShowPasswordGenerationBubble( + const gfx::Rect& rect, + const webkit::forms::PasswordForm& form) OVERRIDE; // Overridden from NotificationObserver: virtual void Observe(int type, diff --git a/chrome/browser/ui/gtk/password_generation_bubble_gtk.cc b/chrome/browser/ui/gtk/password_generation_bubble_gtk.cc index 2215496..b1a1a1e 100644 --- a/chrome/browser/ui/gtk/password_generation_bubble_gtk.cc +++ b/chrome/browser/ui/gtk/password_generation_bubble_gtk.cc @@ -5,6 +5,7 @@ #include "chrome/browser/ui/gtk/password_generation_bubble_gtk.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/password_manager/password_manager.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/gtk/bubble/bubble_gtk.h" @@ -24,10 +25,15 @@ const int kHorizontalSpacing = 4; PasswordGenerationBubbleGtk::PasswordGenerationBubbleGtk( const gfx::Rect& anchor_rect, + const webkit::forms::PasswordForm& form, GtkWidget* anchor_widget, Profile* profile, - content::RenderViewHost* render_view_host) - : profile_(profile), render_view_host_(render_view_host) { + content::RenderViewHost* render_view_host, + PasswordManager* password_manager) + : profile_(profile), + form_(form), + render_view_host_(render_view_host), + password_manager_(password_manager) { // TODO(gcasto): Localize text after we have finalized the UI. // crbug.com/118062 GtkWidget* content = gtk_vbox_new(FALSE, 5); @@ -87,6 +93,7 @@ void PasswordGenerationBubbleGtk::OnAcceptClicked(GtkWidget* widget) { render_view_host_->Send(new AutofillMsg_GeneratedPasswordAccepted( render_view_host_->GetRoutingID(), UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(text_field_))))); + password_manager_->SetFormHasGeneratedPassword(form_); bubble_->Close(); } diff --git a/chrome/browser/ui/gtk/password_generation_bubble_gtk.h b/chrome/browser/ui/gtk/password_generation_bubble_gtk.h index 1de4e2d..d82ba3e 100644 --- a/chrome/browser/ui/gtk/password_generation_bubble_gtk.h +++ b/chrome/browser/ui/gtk/password_generation_bubble_gtk.h @@ -11,24 +11,28 @@ #include "chrome/browser/autofill/password_generator.h" #include "ui/base/gtk/gtk_signal.h" #include "ui/gfx/rect.h" +#include "webkit/forms/password_form.h" namespace content { class RenderViewHost; } class BubbleGtk; +class PasswordManager; class Profile; // PasswordGenerationBubbleGtk is a bubble use to show possible generated // passwords to users. It is set in page content, anchored at |anchor_rect|. // If the generated password is accepted by the user, the renderer associated -// with |render_view_host| is informed of this password. +// with |render_view_host| and the |password_manager| are informed. class PasswordGenerationBubbleGtk { public: PasswordGenerationBubbleGtk(const gfx::Rect& anchor_rect, + const webkit::forms::PasswordForm& form, GtkWidget* anchor_widget, Profile* profile, - content::RenderViewHost* render_view_host); + content::RenderViewHost* render_view_host, + PasswordManager* password_manager); virtual ~PasswordGenerationBubbleGtk(); private: @@ -41,9 +45,16 @@ class PasswordGenerationBubbleGtk { GtkWidget* text_field_; Profile* profile_; + // Form that contains the password field that we are generating a password + // for. Used by the password_manager_. + webkit::forms::PasswordForm form_; + // RenderViewHost associated with the button that spawned this bubble. content::RenderViewHost* render_view_host_; + // PasswordManager for this tab. + PasswordManager* password_manager_; + // Class that deals with generating passwords. autofill::PasswordGenerator password_generator_; diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 3926c7e..f813cc4 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -2401,7 +2401,9 @@ void BrowserView::ShowAvatarBubbleFromAvatarButton() { button->ShowAvatarBubble(); } -void BrowserView::ShowPasswordGenerationBubble(const gfx::Rect& rect) { +void BrowserView::ShowPasswordGenerationBubble( + const gfx::Rect& rect, + const webkit::forms::PasswordForm& form) { // Create a rect in the content bounds that the bubble will point to. gfx::Point origin(rect.origin()); views::View::ConvertPointToScreen(GetTabContentsContainerView(), &origin); @@ -2409,14 +2411,18 @@ void BrowserView::ShowPasswordGenerationBubble(const gfx::Rect& rect) { // Create the bubble. WebContents* web_contents = GetSelectedWebContents(); - if (!web_contents) + TabContentsWrapper* wrapper = GetSelectedTabContentsWrapper(); + if (!web_contents || !wrapper) return; PasswordGenerationBubbleView* bubble = new PasswordGenerationBubbleView(bounds, + form, this, web_contents->GetRenderViewHost(), - browser_.get()); + browser_.get(), + wrapper->password_manager()); + views::BubbleDelegateView::CreateBubble(bubble); bubble->SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE); bubble->Show(); diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h index 1320262..026dd3d 100644 --- a/chrome/browser/ui/views/frame/browser_view.h +++ b/chrome/browser/ui/views/frame/browser_view.h @@ -317,7 +317,9 @@ class BrowserView : public BrowserWindow, virtual void ShowAvatarBubble(content::WebContents* web_contents, const gfx::Rect& rect) OVERRIDE; virtual void ShowAvatarBubbleFromAvatarButton() OVERRIDE; - virtual void ShowPasswordGenerationBubble(const gfx::Rect& rect) OVERRIDE; + virtual void ShowPasswordGenerationBubble( + const gfx::Rect& rect, + const webkit::forms::PasswordForm& form) OVERRIDE; // Overridden from BrowserWindowTesting: virtual BookmarkBarView* GetBookmarkBarView() const OVERRIDE; diff --git a/chrome/browser/ui/views/password_generation_bubble_view.cc b/chrome/browser/ui/views/password_generation_bubble_view.cc index 68750d1..6cffa42 100644 --- a/chrome/browser/ui/views/password_generation_bubble_view.cc +++ b/chrome/browser/ui/views/password_generation_bubble_view.cc @@ -6,6 +6,7 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/password_generator.h" +#include "chrome/browser/password_manager/password_manager.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/common/autofill_messages.h" @@ -27,15 +28,19 @@ using views::GridLayout; PasswordGenerationBubbleView::PasswordGenerationBubbleView( const gfx::Rect& anchor_rect, + const webkit::forms::PasswordForm& form, views::View* anchor_view, content::RenderViewHost* render_view_host, - content::PageNavigator* navigator) + content::PageNavigator* navigator, + PasswordManager* password_manager) : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT), accept_button_(NULL), text_field_(NULL), anchor_rect_(anchor_rect), + form_(form), render_view_host_(render_view_host), - navigator_(navigator) {} + navigator_(navigator), + password_manager_(password_manager) {} PasswordGenerationBubbleView::~PasswordGenerationBubbleView() {} @@ -93,6 +98,7 @@ void PasswordGenerationBubbleView::ButtonPressed(views::Button* sender, if (sender == accept_button_) { render_view_host_->Send(new AutofillMsg_GeneratedPasswordAccepted( render_view_host_->GetRoutingID(), text_field_->text())); + password_manager_->SetFormHasGeneratedPassword(form_); StartFade(false); } } diff --git a/chrome/browser/ui/views/password_generation_bubble_view.h b/chrome/browser/ui/views/password_generation_bubble_view.h index c3e69ba..fb0dd2e 100644 --- a/chrome/browser/ui/views/password_generation_bubble_view.h +++ b/chrome/browser/ui/views/password_generation_bubble_view.h @@ -12,6 +12,7 @@ #include "ui/views/controls/button/button.h" #include "ui/views/controls/link_listener.h" #include "ui/views/view.h" +#include "webkit/forms/password_form.h" namespace content { class PageNavigator; @@ -23,18 +24,22 @@ class TextButton; class Textfield; } +class PasswordManager; + // PasswordGenerationBubbleView is a bubble used to show possible generated // passwords to users. It is set in the page content, anchored at |anchor_rect|. // If the generated password is accepted by the user, the renderer associated -// with |render_view_host| is informed. +// with |render_view_host| and the |password_manager| are informed. class PasswordGenerationBubbleView : public views::BubbleDelegateView, public views::ButtonListener, public views::LinkListener { public: PasswordGenerationBubbleView(const gfx::Rect& anchor_rect, + const webkit::forms::PasswordForm& form, views::View* anchor_view, content::RenderViewHost* render_view_host, - content::PageNavigator* navigator); + content::PageNavigator* navigator, + PasswordManager* password_manager); virtual ~PasswordGenerationBubbleView(); private: @@ -56,6 +61,9 @@ class PasswordGenerationBubbleView : public views::BubbleDelegateView, // Location that the bubble points to gfx::Rect anchor_rect_; + // The form associated with the password field(s) that we are generated. + webkit::forms::PasswordForm form_; + // RenderViewHost associated with the button that spawned this bubble. content::RenderViewHost* render_view_host_; @@ -63,6 +71,9 @@ class PasswordGenerationBubbleView : public views::BubbleDelegateView, // within this UI. content::PageNavigator* navigator_; + // PasswordManager associated with this tab. + PasswordManager* password_manager_; + // Class to generate passwords autofill::PasswordGenerator password_generator_; diff --git a/chrome/common/autofill_messages.h b/chrome/common/autofill_messages.h index fa17176..a2c7fba 100644 --- a/chrome/common/autofill_messages.h +++ b/chrome/common/autofill_messages.h @@ -199,9 +199,10 @@ IPC_MESSAGE_ROUTED0(AutofillHostMsg_HideAutofillPopup) // Instructs the browser to show the password generation bubble at the // specified location. This location should be specified in the renderers -// coordinate system. -IPC_MESSAGE_ROUTED1(AutofillHostMsg_ShowPasswordGenerationPopup, - gfx::Rect /* source location */) +// coordinate system. Form is the form associated with the password field. +IPC_MESSAGE_ROUTED2(AutofillHostMsg_ShowPasswordGenerationPopup, + gfx::Rect /* source location */, + webkit::forms::PasswordForm) // Instruct the browser that a password mapping has been found for a field. IPC_MESSAGE_ROUTED2(AutofillHostMsg_AddPasswordFormMapping, diff --git a/chrome/renderer/autofill/password_generation_manager.cc b/chrome/renderer/autofill/password_generation_manager.cc index 65d9603..b4bc16f 100644 --- a/chrome/renderer/autofill/password_generation_manager.cc +++ b/chrome/renderer/autofill/password_generation_manager.cc @@ -80,8 +80,15 @@ void PasswordGenerationManager::FocusedNodeChanged( if (!input_element.isNull() && account_creation_elements_.first == input_element) { gfx::Rect rect(input_element.boundsInViewportSpace()); - Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(), - rect)); + webkit::forms::PasswordForm* password_form( + webkit::forms::PasswordFormDomManager::CreatePasswordForm( + input_element.form())); + + if (password_form) { + Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(), + rect, + *password_form)); + } } } diff --git a/chrome/renderer/autofill/password_generation_manager_browsertest.cc b/chrome/renderer/autofill/password_generation_manager_browsertest.cc index 12a5158..ce9367f 100644 --- a/chrome/renderer/autofill/password_generation_manager_browsertest.cc +++ b/chrome/renderer/autofill/password_generation_manager_browsertest.cc @@ -73,14 +73,14 @@ class PasswordGenerationManagerTest : public ChromeRenderViewTest { }; const char kSigninFormHTML[] = - "<FORM name = 'blah' action = 'www.random.com/'> " + "<FORM name = 'blah' action = 'http://www.random.com/'> " " <INPUT type = 'text' id = 'username'/> " " <INPUT type = 'password' id = 'password'/> " " <INPUT type = 'submit' value = 'LOGIN' />" "</FORM>"; const char kAccountCreationFormHTML[] = - "<FORM name = 'blah' action = 'www.random.com/'> " + "<FORM name = 'blah' action = 'http://www.random.com/'> " " <INPUT type = 'text' id = 'username'/> " " <INPUT type = 'password' id = 'first_password'/> " " <INPUT type = 'password' id = 'second_password'/> " |