blob: fa59df070b1b11a3bcd26e15a2b9995c37aad2cc [file] [log] [blame]
// Copyright 2015 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/autofill/save_card_bubble_controller_impl.h"
#include <stddef.h>
#include "chrome/browser/ui/autofill/save_card_bubble_view.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/grit/components_scaled_resources.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/navigation_handle.h"
#include "ui/base/l10n/l10n_util.h"
DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::SaveCardBubbleControllerImpl);
namespace autofill {
namespace {
// Number of seconds the bubble and icon will survive navigations, starting
// from when the bubble is shown.
// TODO(bondd): Share with ManagePasswordsUIController.
const int kSurviveNavigationSeconds = 5;
} // namespace
SaveCardBubbleControllerImpl::SaveCardBubbleControllerImpl(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
save_card_bubble_view_(nullptr) {
DCHECK(web_contents);
}
SaveCardBubbleControllerImpl::~SaveCardBubbleControllerImpl() {
if (save_card_bubble_view_)
save_card_bubble_view_->Hide();
}
void SaveCardBubbleControllerImpl::ShowBubbleForLocalSave(
const CreditCard& card,
const base::Closure& save_card_callback) {
// Don't show the bubble if it's already visible.
if (save_card_bubble_view_)
return;
is_uploading_ = false;
is_reshow_ = false;
should_cvc_be_requested_ = false;
legal_message_lines_.clear();
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, is_uploading_,
is_reshow_);
card_ = card;
save_card_callback_ = save_card_callback;
ShowBubble();
}
void SaveCardBubbleControllerImpl::ShowBubbleForUpload(
const CreditCard& card,
std::unique_ptr<base::DictionaryValue> legal_message,
bool should_cvc_be_requested,
const base::Closure& save_card_callback) {
// Don't show the bubble if it's already visible.
if (save_card_bubble_view_)
return;
is_uploading_ = true;
is_reshow_ = false;
should_cvc_be_requested_ = should_cvc_be_requested;
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, is_uploading_,
is_reshow_);
if (!LegalMessageLine::Parse(*legal_message, &legal_message_lines_)) {
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_END_INVALID_LEGAL_MESSAGE,
is_uploading_, is_reshow_);
return;
}
card_ = card;
save_card_callback_ = save_card_callback;
ShowBubble();
}
void SaveCardBubbleControllerImpl::HideBubble() {
if (save_card_bubble_view_) {
save_card_bubble_view_->Hide();
save_card_bubble_view_ = nullptr;
}
}
void SaveCardBubbleControllerImpl::ReshowBubble() {
// Don't show the bubble if it's already visible.
if (save_card_bubble_view_)
return;
is_reshow_ = true;
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, is_uploading_,
is_reshow_);
ShowBubble();
}
bool SaveCardBubbleControllerImpl::IsIconVisible() const {
return !save_card_callback_.is_null();
}
SaveCardBubbleView* SaveCardBubbleControllerImpl::save_card_bubble_view()
const {
return save_card_bubble_view_;
}
base::string16 SaveCardBubbleControllerImpl::GetWindowTitle() const {
return l10n_util::GetStringUTF16(
is_uploading_ ? IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD
: IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_LOCAL);
}
base::string16 SaveCardBubbleControllerImpl::GetExplanatoryMessage() const {
return is_uploading_ ? l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION)
: base::string16();
}
const CreditCard SaveCardBubbleControllerImpl::GetCard() const {
return card_;
}
int SaveCardBubbleControllerImpl::GetCvcImageResourceId() const {
return card_.type() == kAmericanExpressCard ? IDR_CREDIT_CARD_CVC_HINT_AMEX
: IDR_CREDIT_CARD_CVC_HINT;
}
bool SaveCardBubbleControllerImpl::ShouldRequestCvcFromUser() const {
return should_cvc_be_requested_;
}
base::string16 SaveCardBubbleControllerImpl::GetCvcEnteredByUser() const {
DCHECK(!cvc_entered_by_user_.empty());
return cvc_entered_by_user_;
}
void SaveCardBubbleControllerImpl::OnSaveButton(const base::string16& cvc) {
if (!cvc.empty()) {
// Record the CVC entered by the user so it can be sent in the final
// request.
DCHECK(ShouldRequestCvcFromUser());
DCHECK(InputCvcIsValid(cvc));
base::TrimWhitespace(cvc, base::TRIM_ALL, &cvc_entered_by_user_);
}
save_card_callback_.Run();
save_card_callback_.Reset();
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, is_uploading_,
is_reshow_);
}
void SaveCardBubbleControllerImpl::OnCancelButton() {
save_card_callback_.Reset();
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_END_DENIED, is_uploading_, is_reshow_);
}
void SaveCardBubbleControllerImpl::OnLearnMoreClicked() {
OpenUrl(GURL(kHelpURL));
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_DISMISS_CLICK_LEARN_MORE, is_uploading_,
is_reshow_);
}
void SaveCardBubbleControllerImpl::OnLegalMessageLinkClicked(const GURL& url) {
OpenUrl(url);
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_DISMISS_CLICK_LEGAL_MESSAGE,
is_uploading_, is_reshow_);
}
void SaveCardBubbleControllerImpl::OnBubbleClosed() {
save_card_bubble_view_ = nullptr;
UpdateIcon();
}
const LegalMessageLines& SaveCardBubbleControllerImpl::GetLegalMessageLines()
const {
return legal_message_lines_;
}
bool SaveCardBubbleControllerImpl::InputCvcIsValid(
const base::string16& input_text) const {
base::string16 trimmed_text;
base::TrimWhitespace(input_text, base::TRIM_ALL, &trimmed_text);
return IsValidCreditCardSecurityCode(trimmed_text, card_.type());
}
base::TimeDelta SaveCardBubbleControllerImpl::Elapsed() const {
return timer_->Elapsed();
}
void SaveCardBubbleControllerImpl::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
return;
// Nothing to do if there's no bubble available.
if (save_card_callback_.is_null())
return;
// Don't react to same-document (fragment) navigations.
if (navigation_handle->IsSameDocument())
return;
// Don't do anything if a navigation occurs before a user could reasonably
// interact with the bubble.
if (Elapsed() < base::TimeDelta::FromSeconds(kSurviveNavigationSeconds))
return;
// Otherwise, get rid of the bubble and icon.
save_card_callback_.Reset();
if (save_card_bubble_view_) {
save_card_bubble_view_->Hide();
OnBubbleClosed();
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, is_uploading_,
is_reshow_);
} else {
UpdateIcon();
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_HIDDEN, is_uploading_,
is_reshow_);
}
}
void SaveCardBubbleControllerImpl::ShowBubble() {
DCHECK(!save_card_callback_.is_null());
DCHECK(!save_card_bubble_view_);
// Need to create location bar icon before bubble, otherwise bubble will be
// unanchored.
UpdateIcon();
Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
save_card_bubble_view_ = browser->window()->ShowSaveCreditCardBubble(
web_contents(), this, is_reshow_);
DCHECK(save_card_bubble_view_);
// Update icon after creating |save_card_bubble_view_| so that icon will show
// its "toggled on" state.
UpdateIcon();
timer_.reset(new base::ElapsedTimer());
AutofillMetrics::LogSaveCardPromptMetric(
AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, is_uploading_, is_reshow_);
}
void SaveCardBubbleControllerImpl::UpdateIcon() {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
LocationBar* location_bar = browser->window()->GetLocationBar();
location_bar->UpdateSaveCreditCardIcon();
}
void SaveCardBubbleControllerImpl::OpenUrl(const GURL& url) {
web_contents()->OpenURL(content::OpenURLParams(
url, content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false));
}
} // namespace autofill