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.
5 #include "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h"
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"
32 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
33 autofill::GeneratedCreditCardBubbleController
);
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";
44 GeneratedCreditCardBubbleController
* GetOrCreate(content::WebContents
* wc
) {
45 GeneratedCreditCardBubbleController::CreateForWebContents(wc
);
46 return GeneratedCreditCardBubbleController::FromWebContents(wc
);
51 bool TextRange::operator==(const TextRange
& other
) const {
52 return other
.range
== range
&& other
.is_link
== is_link
;
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) {}
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.
71 void GeneratedCreditCardBubbleController::RegisterUserPrefs(
72 user_prefs::PrefRegistrySyncable
* registry
) {
73 registry
->RegisterIntegerPref(
74 ::prefs::kAutofillGeneratedCardBubbleTimesShown
,
76 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
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
);
87 void GeneratedCreditCardBubbleController::DidNavigateMainFrame(
88 const content::LoadCommittedDetails
& details
,
89 const content::FrameNavigateParams
& params
) {
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
)) {
102 should_show_anchor_
= false;
104 web_contents()->RemoveUserData(UserDataKey());
105 // |this| is now deleted.
108 bool GeneratedCreditCardBubbleController::IsHiding() const {
109 return bubble_
&& bubble_
->IsHiding();
112 gfx::Image
GeneratedCreditCardBubbleController::AnchorIcon() const {
113 if (!should_show_anchor_
)
115 return ui::ResourceBundle::GetSharedInstance().GetImageNamed(IDR_WALLET_ICON
);
118 const base::string16
& GeneratedCreditCardBubbleController::TitleText() const {
122 const base::string16
& GeneratedCreditCardBubbleController::ContentsText()
124 return contents_text_
;
127 const std::vector
<TextRange
>& GeneratedCreditCardBubbleController::
128 ContentsTextRanges() const {
129 return contents_text_ranges_
;
132 void GeneratedCreditCardBubbleController::OnAnchorClicked() {
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(¶ms
);
148 base::WeakPtr
<GeneratedCreditCardBubbleController
>
149 GeneratedCreditCardBubbleController::GetWeakPtr() {
150 return weak_ptr_factory_
.GetWeakPtr();
153 base::WeakPtr
<GeneratedCreditCardBubbleView
>
154 GeneratedCreditCardBubbleController::CreateBubble() {
155 return GeneratedCreditCardBubbleView::Create(GetWeakPtr());
158 base::WeakPtr
<GeneratedCreditCardBubbleView
>
159 GeneratedCreditCardBubbleController::bubble() {
163 bool GeneratedCreditCardBubbleController::CanShow() const {
164 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents());
165 return web_contents() == browser
->tab_strip_model()->GetActiveWebContents();
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
;
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());
182 fronting_card_name_
= fronting_card_name
;
183 backing_card_name_
= backing_card_name
;
185 // Clear any generated state or from the last |SetupAndShow()| call.
186 contents_text_
.clear();
187 contents_text_ranges_
.clear();
189 base::string16 to_split
= l10n_util::GetStringFUTF16(
190 IDS_AUTOFILL_GENERATED_CREDIT_CARD_BUBBLE_CONTENTS
,
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
);
199 while (!pieces
.empty()) {
200 base::string16 piece
= pieces
.front();
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();
209 bold_text
.range
= gfx::Range(start
, start
+ piece
.size());
210 bold_text
.is_link
= false;
211 contents_text_ranges_
.push_back(bold_text
);
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);
219 // Add a "Learn more" link at the end of the header text if it's a generated
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();
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
);
231 if (ShouldDisplayBubbleInitially())
235 void GeneratedCreditCardBubbleController::Show(bool was_anchor_click
) {
241 bubble_
= CreateBubble();
243 // TODO(dbeam): Make a bubble on all applicable platforms.
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);
259 void GeneratedCreditCardBubbleController::UpdateAnchor() {
260 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents());
261 if (browser
&& browser
->window() && browser
->window()->GetLocationBar())
262 browser
->window()->GetLocationBar()->UpdateGeneratedCreditCardView();
265 void GeneratedCreditCardBubbleController::Hide() {
266 if (bubble_
&& !bubble_
->IsHiding())
270 } // namespace autofill