// 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/autofill/generated_credit_card_bubble_controller.h" #include #include "base/logging.h" #include "base/prefs/pref_service.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/autofill/generated_credit_card_bubble_view.h" #include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/omnibox/location_bar.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/pref_names.h" #include "components/user_prefs/pref_registry_syncable.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" DEFINE_WEB_CONTENTS_USER_DATA_KEY( autofill::GeneratedCreditCardBubbleController); namespace autofill { namespace { static const int kMaxGeneratedCardTimesToShow = INT_MAX; static const base::char16 kRangeSeparator = '|'; static const char kWalletGeneratedCardLearnMoreLink[] = "http://support.google.com/wallet/bin/answer.py?hl=en&answer=2740044"; GeneratedCreditCardBubbleController* GetOrCreate(content::WebContents* wc) { GeneratedCreditCardBubbleController::CreateForWebContents(wc); return GeneratedCreditCardBubbleController::FromWebContents(wc); } } // namespace bool TextRange::operator==(const TextRange& other) const { return other.range == range && other.is_link == is_link; } GeneratedCreditCardBubbleController::GeneratedCreditCardBubbleController( content::WebContents* web_contents) : WebContentsObserver(web_contents), web_contents_(web_contents), title_text_(l10n_util::GetStringUTF16( IDS_AUTOFILL_GENERATED_CREDIT_CARD_BUBBLE_TITLE)), should_show_anchor_(true), weak_ptr_factory_(this) {} GeneratedCreditCardBubbleController::~GeneratedCreditCardBubbleController() { // In the case that the tab is closed, the controller can be deleted while // bubble is showing. Always calling |Hide()| ensures that the bubble closes. Hide(); } // static void GeneratedCreditCardBubbleController::RegisterUserPrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterIntegerPref( ::prefs::kAutofillGeneratedCardBubbleTimesShown, 0, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); } // static void GeneratedCreditCardBubbleController::Show( content::WebContents* contents, const base::string16& fronting_card_name, const base::string16& backing_card_name) { GetOrCreate(contents)->SetupAndShow(fronting_card_name, backing_card_name); } void GeneratedCreditCardBubbleController::DidNavigateMainFrame( const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) { if (details.entry && !content::PageTransitionIsRedirect(details.entry->GetTransitionType())) { should_show_anchor_ = false; UpdateAnchor(); web_contents()->RemoveUserData(UserDataKey()); // |this| is now deleted. } } bool GeneratedCreditCardBubbleController::IsHiding() const { return bubble_ && bubble_->IsHiding(); } gfx::Image GeneratedCreditCardBubbleController::AnchorIcon() const { if (!should_show_anchor_) return gfx::Image(); return ui::ResourceBundle::GetSharedInstance().GetImageNamed(IDR_WALLET_ICON); } const base::string16& GeneratedCreditCardBubbleController::TitleText() const { return title_text_; } const base::string16& GeneratedCreditCardBubbleController::ContentsText() const { return contents_text_; } const std::vector& GeneratedCreditCardBubbleController:: ContentsTextRanges() const { return contents_text_ranges_; } void GeneratedCreditCardBubbleController::OnAnchorClicked() { Show(true); } void GeneratedCreditCardBubbleController::OnLinkClicked() { // Open a new tab to the Online Wallet help link. chrome::NavigateParams params( chrome::FindBrowserWithWebContents(web_contents()), GURL(kWalletGeneratedCardLearnMoreLink), content::PAGE_TRANSITION_AUTO_BOOKMARK); params.disposition = NEW_FOREGROUND_TAB; chrome::Navigate(¶ms); Hide(); } base::WeakPtr GeneratedCreditCardBubbleController::GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } base::WeakPtr GeneratedCreditCardBubbleController::CreateBubble() { return GeneratedCreditCardBubbleView::Create(GetWeakPtr()); } base::WeakPtr GeneratedCreditCardBubbleController::bubble() { return bubble_; } bool GeneratedCreditCardBubbleController::CanShow() const { Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); return web_contents() == browser->tab_strip_model()->GetActiveWebContents(); } bool GeneratedCreditCardBubbleController::ShouldDisplayBubbleInitially() const { Profile* profile = Profile::FromBrowserContext( web_contents_->GetBrowserContext()); int times_shown = profile->GetPrefs()->GetInteger( ::prefs::kAutofillGeneratedCardBubbleTimesShown); return times_shown < kMaxGeneratedCardTimesToShow; } void GeneratedCreditCardBubbleController::SetupAndShow( const base::string16& fronting_card_name, const base::string16& backing_card_name) { DCHECK(!fronting_card_name.empty()); DCHECK(!backing_card_name.empty()); fronting_card_name_ = fronting_card_name; backing_card_name_ = backing_card_name; // Clear any generated state or from the last |SetupAndShow()| call. contents_text_.clear(); contents_text_ranges_.clear(); base::string16 to_split = l10n_util::GetStringFUTF16( IDS_AUTOFILL_GENERATED_CREDIT_CARD_BUBBLE_CONTENTS, fronting_card_name_, backing_card_name_); // Split the full text on '|' to highlight certain parts. For example, "sly" // and "jumped" would be bolded in "The |sly| fox |jumped| over the lazy dog". std::vector pieces; base::SplitStringDontTrim(to_split, kRangeSeparator, &pieces); while (!pieces.empty()) { base::string16 piece = pieces.front(); // Every second piece should be bolded. Because |base::SplitString*()| // leaves an empty "" even if '|' is the first character, this is guaranteed // to work for "|highlighting| starts here". Ignore empty pieces because // there's nothing to highlight. if (!piece.empty() && pieces.size() % 2 == 0) { const size_t start = contents_text_.size(); TextRange bold_text; bold_text.range = gfx::Range(start, start + piece.size()); bold_text.is_link = false; contents_text_ranges_.push_back(bold_text); } // Append the piece whether it's bolded or not and move on to the next one. contents_text_.append(piece); pieces.erase(pieces.begin(), pieces.begin() + 1); } // Add a "Learn more" link at the end of the header text if it's a generated // card bubble. base::string16 learn_more = l10n_util::GetStringUTF16(IDS_LEARN_MORE); contents_text_.append(ASCIIToUTF16(" ") + learn_more); const size_t header_size = contents_text_.size(); TextRange end_link; end_link.range = gfx::Range(header_size - learn_more.size(), header_size); end_link.is_link = true; contents_text_ranges_.push_back(end_link); UpdateAnchor(); if (ShouldDisplayBubbleInitially()) Show(false); } void GeneratedCreditCardBubbleController::Show(bool was_anchor_click) { Hide(); if (!CanShow()) return; bubble_ = CreateBubble(); if (!bubble_) { // TODO(dbeam): Make a bubble on all applicable platforms. return; } bubble_->Show(); if (!was_anchor_click) { // If the bubble was an automatically created "you generated a card" bubble, // count it as a show. If the user clicked the omnibox icon, don't count it. PrefService* prefs = Profile::FromBrowserContext( web_contents()->GetBrowserContext())->GetPrefs(); prefs->SetInteger(::prefs::kAutofillGeneratedCardBubbleTimesShown, prefs->GetInteger(::prefs::kAutofillGeneratedCardBubbleTimesShown) + 1); } } void GeneratedCreditCardBubbleController::UpdateAnchor() { Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); if (browser && browser->window() && browser->window()->GetLocationBar()) browser->window()->GetLocationBar()->UpdateGeneratedCreditCardView(); } void GeneratedCreditCardBubbleController::Hide() { if (bubble_ && !bubble_->IsHiding()) bubble_->Hide(); } } // namespace autofill