Home | History | Annotate | Download | only in autofill
      1 // Copyright 2013 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 "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h"
      6 
      7 #include <climits>
      8 
      9 #include "base/logging.h"
     10 #include "base/prefs/pref_service.h"
     11 #include "base/strings/string_split.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
     15 #include "chrome/browser/ui/autofill/generated_credit_card_bubble_view.h"
     16 #include "chrome/browser/ui/browser_finder.h"
     17 #include "chrome/browser/ui/browser_navigator.h"
     18 #include "chrome/browser/ui/browser_window.h"
     19 #include "chrome/browser/ui/location_bar/location_bar.h"
     20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     21 #include "chrome/common/pref_names.h"
     22 #include "chrome/grit/generated_resources.h"
     23 #include "components/pref_registry/pref_registry_syncable.h"
     24 #include "content/public/browser/navigation_details.h"
     25 #include "content/public/browser/navigation_entry.h"
     26 #include "content/public/browser/web_contents.h"
     27 #include "grit/components_strings.h"
     28 #include "grit/theme_resources.h"
     29 #include "ui/base/l10n/l10n_util.h"
     30 #include "ui/base/resource/resource_bundle.h"
     31 
     32 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
     33     autofill::GeneratedCreditCardBubbleController);
     34 
     35 namespace autofill {
     36 
     37 namespace {
     38 
     39 static const int kMaxGeneratedCardTimesToShow = INT_MAX;
     40 static const base::char16 kRangeSeparator = '|';
     41 static const char kWalletGeneratedCardLearnMoreLink[] =
     42     "http://support.google.com/wallet/bin/answer.py?hl=en&answer=2740044";
     43 
     44 GeneratedCreditCardBubbleController* GetOrCreate(content::WebContents* wc) {
     45   GeneratedCreditCardBubbleController::CreateForWebContents(wc);
     46   return GeneratedCreditCardBubbleController::FromWebContents(wc);
     47 }
     48 
     49 }  // namespace
     50 
     51 bool TextRange::operator==(const TextRange& other) const {
     52   return other.range == range && other.is_link == is_link;
     53 }
     54 
     55 GeneratedCreditCardBubbleController::GeneratedCreditCardBubbleController(
     56     content::WebContents* web_contents)
     57     : WebContentsObserver(web_contents),
     58       web_contents_(web_contents),
     59       title_text_(l10n_util::GetStringUTF16(
     60           IDS_AUTOFILL_GENERATED_CREDIT_CARD_BUBBLE_TITLE)),
     61       should_show_anchor_(true),
     62       weak_ptr_factory_(this) {}
     63 
     64 GeneratedCreditCardBubbleController::~GeneratedCreditCardBubbleController() {
     65   // In the case that the tab is closed, the controller can be deleted while
     66   // bubble is showing. Always calling |Hide()| ensures that the bubble closes.
     67   Hide();
     68 }
     69 
     70 // static
     71 void GeneratedCreditCardBubbleController::RegisterUserPrefs(
     72     user_prefs::PrefRegistrySyncable* registry) {
     73   registry->RegisterIntegerPref(
     74       ::prefs::kAutofillGeneratedCardBubbleTimesShown,
     75       0,
     76       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
     77 }
     78 
     79 // static
     80 void GeneratedCreditCardBubbleController::Show(
     81     content::WebContents* contents,
     82     const base::string16& fronting_card_name,
     83     const base::string16& backing_card_name) {
     84   GetOrCreate(contents)->SetupAndShow(fronting_card_name, backing_card_name);
     85 }
     86 
     87 void GeneratedCreditCardBubbleController::DidNavigateMainFrame(
     88     const content::LoadCommittedDetails& details,
     89     const content::FrameNavigateParams& params) {
     90   if (!details.entry)
     91     return;
     92 
     93   // Don't destory the bubble due to reloads, form submits, or redirects right
     94   // after the dialog succeeds. Merchants often navigate to a confirmation page.
     95   ui::PageTransition transition = details.entry->GetTransitionType();
     96   if (transition == ui::PAGE_TRANSITION_FORM_SUBMIT ||
     97       transition == ui::PAGE_TRANSITION_RELOAD ||
     98       ui::PageTransitionIsRedirect(transition)) {
     99     return;
    100   }
    101 
    102   should_show_anchor_ = false;
    103   UpdateAnchor();
    104   web_contents()->RemoveUserData(UserDataKey());
    105   // |this| is now deleted.
    106 }
    107 
    108 bool GeneratedCreditCardBubbleController::IsHiding() const {
    109   return bubble_ && bubble_->IsHiding();
    110 }
    111 
    112 gfx::Image GeneratedCreditCardBubbleController::AnchorIcon() const {
    113   if (!should_show_anchor_)
    114     return gfx::Image();
    115   return ui::ResourceBundle::GetSharedInstance().GetImageNamed(IDR_WALLET_ICON);
    116 }
    117 
    118 const base::string16& GeneratedCreditCardBubbleController::TitleText() const {
    119   return title_text_;
    120 }
    121 
    122 const base::string16& GeneratedCreditCardBubbleController::ContentsText()
    123     const {
    124   return contents_text_;
    125 }
    126 
    127 const std::vector<TextRange>& GeneratedCreditCardBubbleController::
    128     ContentsTextRanges() const {
    129   return contents_text_ranges_;
    130 }
    131 
    132 void GeneratedCreditCardBubbleController::OnAnchorClicked() {
    133   Show(true);
    134 }
    135 
    136 void GeneratedCreditCardBubbleController::OnLinkClicked() {
    137   // Open a new tab to the Online Wallet help link.
    138   chrome::NavigateParams params(
    139       chrome::FindBrowserWithWebContents(web_contents()),
    140       GURL(kWalletGeneratedCardLearnMoreLink),
    141       ui::PAGE_TRANSITION_AUTO_BOOKMARK);
    142   params.disposition = NEW_FOREGROUND_TAB;
    143   chrome::Navigate(&params);
    144 
    145   Hide();
    146 }
    147 
    148 base::WeakPtr<GeneratedCreditCardBubbleController>
    149     GeneratedCreditCardBubbleController::GetWeakPtr() {
    150   return weak_ptr_factory_.GetWeakPtr();
    151 }
    152 
    153 base::WeakPtr<GeneratedCreditCardBubbleView>
    154     GeneratedCreditCardBubbleController::CreateBubble() {
    155   return GeneratedCreditCardBubbleView::Create(GetWeakPtr());
    156 }
    157 
    158 base::WeakPtr<GeneratedCreditCardBubbleView>
    159     GeneratedCreditCardBubbleController::bubble() {
    160   return bubble_;
    161 }
    162 
    163 bool GeneratedCreditCardBubbleController::CanShow() const {
    164   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
    165   return web_contents() == browser->tab_strip_model()->GetActiveWebContents();
    166 }
    167 
    168 bool GeneratedCreditCardBubbleController::ShouldDisplayBubbleInitially() const {
    169   Profile* profile = Profile::FromBrowserContext(
    170       web_contents_->GetBrowserContext());
    171   int times_shown = profile->GetPrefs()->GetInteger(
    172       ::prefs::kAutofillGeneratedCardBubbleTimesShown);
    173   return times_shown < kMaxGeneratedCardTimesToShow;
    174 }
    175 
    176 void GeneratedCreditCardBubbleController::SetupAndShow(
    177     const base::string16& fronting_card_name,
    178     const base::string16& backing_card_name) {
    179   DCHECK(!fronting_card_name.empty());
    180   DCHECK(!backing_card_name.empty());
    181 
    182   fronting_card_name_ = fronting_card_name;
    183   backing_card_name_ = backing_card_name;
    184 
    185   // Clear any generated state or from the last |SetupAndShow()| call.
    186   contents_text_.clear();
    187   contents_text_ranges_.clear();
    188 
    189   base::string16 to_split = l10n_util::GetStringFUTF16(
    190       IDS_AUTOFILL_GENERATED_CREDIT_CARD_BUBBLE_CONTENTS,
    191       fronting_card_name_,
    192       backing_card_name_);
    193 
    194   // Split the full text on '|' to highlight certain parts. For example, "sly"
    195   // and "jumped" would be bolded in "The |sly| fox |jumped| over the lazy dog".
    196   std::vector<base::string16> pieces;
    197   base::SplitStringDontTrim(to_split, kRangeSeparator, &pieces);
    198 
    199   while (!pieces.empty()) {
    200     base::string16 piece = pieces.front();
    201 
    202     // Every second piece should be bolded. Because |base::SplitString*()|
    203     // leaves an empty "" even if '|' is the first character, this is guaranteed
    204     // to work for "|highlighting| starts here". Ignore empty pieces because
    205     // there's nothing to highlight.
    206     if (!piece.empty() && pieces.size() % 2 == 0) {
    207       const size_t start = contents_text_.size();
    208       TextRange bold_text;
    209       bold_text.range = gfx::Range(start, start + piece.size());
    210       bold_text.is_link = false;
    211       contents_text_ranges_.push_back(bold_text);
    212     }
    213 
    214     // Append the piece whether it's bolded or not and move on to the next one.
    215     contents_text_.append(piece);
    216     pieces.erase(pieces.begin(), pieces.begin() + 1);
    217   }
    218 
    219   // Add a "Learn more" link at the end of the header text if it's a generated
    220   // card bubble.
    221   base::string16 learn_more = l10n_util::GetStringUTF16(IDS_LEARN_MORE);
    222   contents_text_.append(base::ASCIIToUTF16(" ") + learn_more);
    223   const size_t header_size = contents_text_.size();
    224   TextRange end_link;
    225   end_link.range = gfx::Range(header_size - learn_more.size(), header_size);
    226   end_link.is_link = true;
    227   contents_text_ranges_.push_back(end_link);
    228 
    229   UpdateAnchor();
    230 
    231   if (ShouldDisplayBubbleInitially())
    232     Show(false);
    233 }
    234 
    235 void GeneratedCreditCardBubbleController::Show(bool was_anchor_click) {
    236   Hide();
    237 
    238   if (!CanShow())
    239     return;
    240 
    241   bubble_ = CreateBubble();
    242   if (!bubble_) {
    243     // TODO(dbeam): Make a bubble on all applicable platforms.
    244     return;
    245   }
    246 
    247   bubble_->Show();
    248 
    249   if (!was_anchor_click) {
    250     // If the bubble was an automatically created "you generated a card" bubble,
    251     // count it as a show. If the user clicked the omnibox icon, don't count it.
    252     PrefService* prefs = Profile::FromBrowserContext(
    253         web_contents()->GetBrowserContext())->GetPrefs();
    254     prefs->SetInteger(::prefs::kAutofillGeneratedCardBubbleTimesShown,
    255         prefs->GetInteger(::prefs::kAutofillGeneratedCardBubbleTimesShown) + 1);
    256   }
    257 }
    258 
    259 void GeneratedCreditCardBubbleController::UpdateAnchor() {
    260   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
    261   if (browser && browser->window() && browser->window()->GetLocationBar())
    262     browser->window()->GetLocationBar()->UpdateGeneratedCreditCardView();
    263 }
    264 
    265 void GeneratedCreditCardBubbleController::Hide() {
    266   if (bubble_ && !bubble_->IsHiding())
    267     bubble_->Hide();
    268 }
    269 
    270 }  // namespace autofill
    271