OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h" | 5 #include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h" |
6 | 6 |
| 7 #include "base/i18n/message_formatter.h" |
| 8 #include "base/strings/string_util.h" |
| 9 #include "base/strings/utf_string_conversions.h" |
7 #include "chrome/browser/ui/autofill/save_card_bubble_view.h" | 10 #include "chrome/browser/ui/autofill/save_card_bubble_view.h" |
8 #include "chrome/browser/ui/browser.h" | 11 #include "chrome/browser/ui/browser.h" |
9 #include "chrome/browser/ui/browser_finder.h" | 12 #include "chrome/browser/ui/browser_finder.h" |
10 #include "chrome/browser/ui/browser_window.h" | 13 #include "chrome/browser/ui/browser_window.h" |
11 #include "chrome/browser/ui/location_bar/location_bar.h" | 14 #include "chrome/browser/ui/location_bar/location_bar.h" |
12 #include "components/autofill/core/common/autofill_constants.h" | 15 #include "components/autofill/core/common/autofill_constants.h" |
13 #include "content/public/browser/navigation_details.h" | 16 #include "content/public/browser/navigation_details.h" |
14 #include "grit/components_strings.h" | 17 #include "grit/components_strings.h" |
15 #include "ui/base/l10n/l10n_util.h" | 18 #include "ui/base/l10n/l10n_util.h" |
16 | 19 |
17 DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::SaveCardBubbleControllerImpl); | 20 DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::SaveCardBubbleControllerImpl); |
18 | 21 |
| 22 namespace autofill { |
| 23 |
19 namespace { | 24 namespace { |
20 | 25 |
21 // Number of seconds the bubble and icon will survive navigations, starting | 26 // Number of seconds the bubble and icon will survive navigations, starting |
22 // from when the bubble is shown. | 27 // from when the bubble is shown. |
23 // TODO(bondd): Share with ManagePasswordsUIController. | 28 // TODO(bondd): Share with ManagePasswordsUIController. |
24 const int kSurviveNavigationSeconds = 5; | 29 const int kSurviveNavigationSeconds = 5; |
25 | 30 |
| 31 // Replace "{0}", "{1}", ... in |template_icu| with corresponding strings |
| 32 // from |display_texts|. Sets |out_message| to the resulting string, with |
| 33 // start position of each replacement in |out_offsets|. |
| 34 // Return false on failure. If false is returned then contents of |out_message| |
| 35 // and |out_offsets| are undefined. |
| 36 bool ReplaceTemplatePlaceholders( |
| 37 const base::string16& template_icu, |
| 38 const std::vector<base::string16>& display_texts, |
| 39 base::string16* out_message, |
| 40 std::vector<size_t>* out_offsets) { |
| 41 // Escape "$" -> "$$" for ReplaceStringPlaceholders(). |
| 42 // |
| 43 // Edge cases: |
| 44 // 1. Two or more consecutive $ characters will be incorrectly expanded |
| 45 // ("$$" -> "$$$$", which ReplaceStringPlaceholders() then turns into |
| 46 // "$$$"). |
| 47 // |
| 48 // 2. "${" will cause false to be returned. "${0}" will expand to "$${0}". |
| 49 // FormatWithNumberedArgs() turns it into "$$$1", which |
| 50 // ReplaceStringPlaceholders() then turns into "$$1" without doing the |
| 51 // parameter replacement. This causes false to be returned because each |
| 52 // parameter is not used exactly once. |
| 53 // |
| 54 // Both of these cases are noted in the header file, and are unlikely to |
| 55 // occur in any actual legal message. |
| 56 base::string16 template_icu_escaped; |
| 57 base::ReplaceChars(template_icu, base::ASCIIToUTF16("$"), |
| 58 base::ASCIIToUTF16("$$"), &template_icu_escaped); |
| 59 |
| 60 // Replace "{0}" -> "$1", "{1}" -> "$2", ... to prepare |template_dollars| |
| 61 // for ReplaceStringPlaceholders(). |
| 62 base::string16 template_dollars = |
| 63 base::i18n::MessageFormatter::FormatWithNumberedArgs( |
| 64 template_icu_escaped, "$1", "$2", "$3", "$4", "$5", "$6", "$7"); |
| 65 |
| 66 // FormatWithNumberedArgs() returns an empty string on failure. |
| 67 if (template_dollars.empty() && !template_icu.empty()) |
| 68 return false; |
| 69 |
| 70 // Replace "$1", "$2", ... with the display text of each parameter. |
| 71 *out_message = base::ReplaceStringPlaceholders(template_dollars, |
| 72 display_texts, out_offsets); |
| 73 |
| 74 // Each parameter must be used exactly once. If a parameter is unused or |
| 75 // used more than once then it can't be determined which |offsets| entry |
| 76 // corresponds to which parameter. |
| 77 return out_offsets->size() == display_texts.size(); |
| 78 } |
| 79 |
| 80 // Parses |line| and sets |out|. |
| 81 // Returns false on failure. |out| is not modified if false is returned. |
| 82 bool ParseLegalMessageLine(const base::DictionaryValue& line, |
| 83 SaveCardBubbleController::LegalMessageLine* out) { |
| 84 SaveCardBubbleController::LegalMessageLine result; |
| 85 |
| 86 // |display_texts| elements are the strings that will be substituted for |
| 87 // "{0}", "{1}", etc. in the template string. |
| 88 std::vector<base::string16> display_texts; |
| 89 |
| 90 // Process all the template parameters. |
| 91 const base::ListValue* template_parameters = nullptr; |
| 92 if (line.GetList("template_parameter", &template_parameters)) { |
| 93 display_texts.resize(template_parameters->GetSize()); |
| 94 result.links.resize(template_parameters->GetSize()); |
| 95 |
| 96 for (size_t parameter_index = 0; |
| 97 parameter_index < template_parameters->GetSize(); ++parameter_index) { |
| 98 const base::DictionaryValue* single_parameter; |
| 99 std::string url; |
| 100 if (!template_parameters->GetDictionary(parameter_index, |
| 101 &single_parameter) || |
| 102 !single_parameter->GetString("display_text", |
| 103 &display_texts[parameter_index]) || |
| 104 !single_parameter->GetString("url", &url)) { |
| 105 return false; |
| 106 } |
| 107 result.links[parameter_index].url = GURL(url); |
| 108 } |
| 109 } |
| 110 |
| 111 // Read the template string. It's a small subset of the ICU message format |
| 112 // syntax. |
| 113 base::string16 template_icu; |
| 114 if (!line.GetString("template", &template_icu)) |
| 115 return false; |
| 116 |
| 117 // Replace the placeholders in |template_icu| with strings from |
| 118 // |display_texts|, and store the start position of each replacement in |
| 119 // |offsets|. |
| 120 std::vector<size_t> offsets; |
| 121 if (!ReplaceTemplatePlaceholders(template_icu, display_texts, &result.text, |
| 122 &offsets)) { |
| 123 return false; |
| 124 } |
| 125 |
| 126 // Fill in range values for all links. |
| 127 for (size_t offset_index = 0; offset_index < offsets.size(); ++offset_index) { |
| 128 size_t range_start = offsets[offset_index]; |
| 129 result.links[offset_index].range = gfx::Range( |
| 130 range_start, range_start + display_texts[offset_index].size()); |
| 131 } |
| 132 |
| 133 *out = result; |
| 134 return true; |
| 135 } |
| 136 |
26 } // namespace | 137 } // namespace |
27 | 138 |
28 namespace autofill { | |
29 | |
30 SaveCardBubbleControllerImpl::SaveCardBubbleControllerImpl( | 139 SaveCardBubbleControllerImpl::SaveCardBubbleControllerImpl( |
31 content::WebContents* web_contents) | 140 content::WebContents* web_contents) |
32 : content::WebContentsObserver(web_contents), | 141 : content::WebContentsObserver(web_contents), |
33 save_card_bubble_view_(nullptr), | 142 save_card_bubble_view_(nullptr), |
34 is_uploading_(false) { | 143 is_uploading_(false) { |
35 DCHECK(web_contents); | 144 DCHECK(web_contents); |
36 } | 145 } |
37 | 146 |
38 SaveCardBubbleControllerImpl::~SaveCardBubbleControllerImpl() { | 147 SaveCardBubbleControllerImpl::~SaveCardBubbleControllerImpl() { |
39 if (save_card_bubble_view_) | 148 if (save_card_bubble_view_) |
40 save_card_bubble_view_->Hide(); | 149 save_card_bubble_view_->Hide(); |
41 } | 150 } |
42 | 151 |
43 void SaveCardBubbleControllerImpl::InitializeForLocalSave( | 152 void SaveCardBubbleControllerImpl::InitializeForLocalSave( |
44 const base::Closure& save_card_callback) { | 153 const base::Closure& save_card_callback) { |
45 is_uploading_ = false; | 154 is_uploading_ = false; |
46 save_card_callback_ = save_card_callback; | 155 save_card_callback_ = save_card_callback; |
47 } | 156 } |
48 | 157 |
49 void SaveCardBubbleControllerImpl::InitializeForUpload( | 158 void SaveCardBubbleControllerImpl::InitializeForUpload( |
50 const base::Closure& save_card_callback, | 159 const base::Closure& save_card_callback, |
51 scoped_ptr<base::DictionaryValue> legal_message) { | 160 scoped_ptr<base::DictionaryValue> legal_message) { |
52 is_uploading_ = true; | 161 is_uploading_ = true; |
53 save_card_callback_ = save_card_callback; | 162 save_card_callback_ = save_card_callback; |
54 // TODO(bondd): Store legal_message here. | 163 // TODO(bondd): Store legal_message here. |
55 } | 164 } |
56 | 165 |
| 166 bool SaveCardBubbleControllerImpl::SetLegalMessage( |
| 167 const base::ListValue& lines) { |
| 168 // Process all lines of the message. See comment in header file for example |
| 169 // of valid |lines| data. |
| 170 legal_message_lines_.resize(lines.GetSize()); |
| 171 for (size_t i = 0; i < lines.GetSize(); ++i) { |
| 172 const base::DictionaryValue* single_line; |
| 173 if (!lines.GetDictionary(i, &single_line) || |
| 174 !ParseLegalMessageLine(*single_line, &legal_message_lines_[i])) { |
| 175 legal_message_lines_.clear(); |
| 176 return false; |
| 177 } |
| 178 } |
| 179 |
| 180 return true; |
| 181 } |
| 182 |
57 void SaveCardBubbleControllerImpl::ShowBubble(bool user_action) { | 183 void SaveCardBubbleControllerImpl::ShowBubble(bool user_action) { |
58 DCHECK(!save_card_callback_.is_null()); | 184 DCHECK(!save_card_callback_.is_null()); |
59 | 185 |
60 // Need to create location bar icon before bubble, otherwise bubble will be | 186 // Need to create location bar icon before bubble, otherwise bubble will be |
61 // unanchored. | 187 // unanchored. |
62 UpdateIcon(); | 188 UpdateIcon(); |
63 | 189 |
64 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); | 190 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); |
65 save_card_bubble_view_ = browser->window()->ShowSaveCreditCardBubble( | 191 save_card_bubble_view_ = browser->window()->ShowSaveCreditCardBubble( |
66 web_contents(), this, user_action); | 192 web_contents(), this, user_action); |
(...skipping 30 matching lines...) Expand all Loading... |
97 void SaveCardBubbleControllerImpl::OnSaveButton() { | 223 void SaveCardBubbleControllerImpl::OnSaveButton() { |
98 save_card_callback_.Run(); | 224 save_card_callback_.Run(); |
99 save_card_callback_.Reset(); | 225 save_card_callback_.Reset(); |
100 } | 226 } |
101 | 227 |
102 void SaveCardBubbleControllerImpl::OnCancelButton() { | 228 void SaveCardBubbleControllerImpl::OnCancelButton() { |
103 save_card_callback_.Reset(); | 229 save_card_callback_.Reset(); |
104 } | 230 } |
105 | 231 |
106 void SaveCardBubbleControllerImpl::OnLearnMoreClicked() { | 232 void SaveCardBubbleControllerImpl::OnLearnMoreClicked() { |
107 web_contents()->OpenURL(content::OpenURLParams( | 233 OpenUrl(GURL(kHelpURL)); |
108 GURL(kHelpURL), content::Referrer(), NEW_FOREGROUND_TAB, | 234 } |
109 ui::PAGE_TRANSITION_LINK, false)); | 235 |
| 236 void SaveCardBubbleControllerImpl::OnLegalMessageLinkClicked(const GURL& url) { |
| 237 OpenUrl(url); |
110 } | 238 } |
111 | 239 |
112 void SaveCardBubbleControllerImpl::OnBubbleClosed() { | 240 void SaveCardBubbleControllerImpl::OnBubbleClosed() { |
113 save_card_bubble_view_ = nullptr; | 241 save_card_bubble_view_ = nullptr; |
114 UpdateIcon(); | 242 UpdateIcon(); |
115 } | 243 } |
116 | 244 |
| 245 const SaveCardBubbleController::LegalMessageLines& |
| 246 SaveCardBubbleControllerImpl::GetLegalMessageLines() const { |
| 247 return legal_message_lines_; |
| 248 } |
| 249 |
117 void SaveCardBubbleControllerImpl::UpdateIcon() { | 250 void SaveCardBubbleControllerImpl::UpdateIcon() { |
118 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); | 251 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); |
119 LocationBar* location_bar = browser->window()->GetLocationBar(); | 252 LocationBar* location_bar = browser->window()->GetLocationBar(); |
120 location_bar->UpdateSaveCreditCardIcon(); | 253 location_bar->UpdateSaveCreditCardIcon(); |
121 } | 254 } |
122 | 255 |
| 256 void SaveCardBubbleControllerImpl::OpenUrl(const GURL& url) { |
| 257 web_contents()->OpenURL( |
| 258 content::OpenURLParams(url, content::Referrer(), NEW_FOREGROUND_TAB, |
| 259 ui::PAGE_TRANSITION_LINK, false)); |
| 260 } |
| 261 |
123 void SaveCardBubbleControllerImpl::DidNavigateMainFrame( | 262 void SaveCardBubbleControllerImpl::DidNavigateMainFrame( |
124 const content::LoadCommittedDetails& details, | 263 const content::LoadCommittedDetails& details, |
125 const content::FrameNavigateParams& params) { | 264 const content::FrameNavigateParams& params) { |
126 // Nothing to do if there's no bubble available. | 265 // Nothing to do if there's no bubble available. |
127 if (save_card_callback_.is_null()) | 266 if (save_card_callback_.is_null()) |
128 return; | 267 return; |
129 | 268 |
130 // Don't react to in-page (fragment) navigations. | 269 // Don't react to in-page (fragment) navigations. |
131 if (details.is_in_page) | 270 if (details.is_in_page) |
132 return; | 271 return; |
133 | 272 |
134 // Don't do anything if a navigation occurs before a user could reasonably | 273 // Don't do anything if a navigation occurs before a user could reasonably |
135 // interact with the bubble. | 274 // interact with the bubble. |
136 if (timer_->Elapsed() < | 275 if (timer_->Elapsed() < |
137 base::TimeDelta::FromSeconds(kSurviveNavigationSeconds)) | 276 base::TimeDelta::FromSeconds(kSurviveNavigationSeconds)) |
138 return; | 277 return; |
139 | 278 |
140 // Otherwise, get rid of the bubble and icon. | 279 // Otherwise, get rid of the bubble and icon. |
141 save_card_callback_.Reset(); | 280 save_card_callback_.Reset(); |
142 if (save_card_bubble_view_) { | 281 if (save_card_bubble_view_) { |
143 save_card_bubble_view_->Hide(); | 282 save_card_bubble_view_->Hide(); |
144 OnBubbleClosed(); | 283 OnBubbleClosed(); |
145 } else { | 284 } else { |
146 UpdateIcon(); | 285 UpdateIcon(); |
147 } | 286 } |
148 } | 287 } |
149 | 288 |
150 } // namespace autofill | 289 } // namespace autofill |
OLD | NEW |