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