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 | 17 |
15 DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::SaveCardBubbleControllerImpl); | 18 DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::SaveCardBubbleControllerImpl); |
16 | 19 |
17 namespace { | 20 namespace { |
18 | 21 |
19 // Number of seconds the bubble and icon will survive navigations, starting | 22 // Number of seconds the bubble and icon will survive navigations, starting |
20 // from when the bubble is shown. | 23 // from when the bubble is shown. |
21 // TODO(bondd): Share with ManagePasswordsUIController. | 24 // TODO(bondd): Share with ManagePasswordsUIController. |
22 const int kSurviveNavigationSeconds = 5; | 25 const int kSurviveNavigationSeconds = 5; |
23 | 26 |
| 27 // Replace "{0}", "{1}", ... in |template_icu| with corresponding strings |
| 28 // from |display_texts|. Sets |out_message| to the resulting string, with |
| 29 // start position of each replacement in |out_offsets|. |
| 30 // Return false on failure. If false is returned then contents of |out_message| |
| 31 // and |out_offsets| are undefined. |
| 32 bool ReplaceTemplatePlaceholders( |
| 33 const base::string16& template_icu, |
| 34 const std::vector<base::string16>& display_texts, |
| 35 base::string16& out_message, |
| 36 std::vector<size_t>& out_offsets) { |
| 37 // Escape "$" -> "$$" for ReplaceStringPlaceholders(). |
| 38 // |
| 39 // Edge cases: |
| 40 // 1. Two or more consecutive $ characters will be incorrectly expanded |
| 41 // ("$$" -> "$$$$", which ReplaceStringPlaceholders() then turns into |
| 42 // "$$$"). |
| 43 // |
| 44 // 2. "${" will cause false to be returned. "${0}" will expand to "$${0}". |
| 45 // FormatWithNumberedArgs() turns it into "$$$1", which |
| 46 // ReplaceStringPlaceholders() then turns into "$$1" without doing the |
| 47 // parameter replacement. This causes false to be returned because each |
| 48 // parameter is not used exactly once. |
| 49 // |
| 50 // Both of these cases are noted in the header file, and are unlikely to |
| 51 // occur in any actual legal message. |
| 52 base::string16 template_icu_escaped; |
| 53 base::ReplaceChars(template_icu, base::ASCIIToUTF16("$"), |
| 54 base::ASCIIToUTF16("$$"), &template_icu_escaped); |
| 55 |
| 56 // Replace "{0}" -> "$1", "{1}" -> "$2", ... to prepare |template_dollars| |
| 57 // for ReplaceStringPlaceholders(). |
| 58 base::string16 template_dollars = |
| 59 base::i18n::MessageFormatter::FormatWithNumberedArgs( |
| 60 template_icu_escaped, "$1", "$2", "$3", "$4", "$5", "$6", "$7"); |
| 61 |
| 62 // FormatWithNumberedArgs() returns an empty string on failure. |
| 63 if (template_dollars.empty() && !template_icu.empty()) |
| 64 return false; |
| 65 |
| 66 // Replace "$1", "$2", ... with the display text of each parameter. |
| 67 out_message = |
| 68 ReplaceStringPlaceholders(template_dollars, display_texts, &out_offsets); |
| 69 |
| 70 // Each parameter must be used exactly once. If a parameter is unused or |
| 71 // used more than once then it can't be determined which |offsets| entry |
| 72 // corresponds to which parameter. |
| 73 return out_offsets.size() == display_texts.size(); |
| 74 } |
| 75 |
| 76 // Parses |line| and appends results to |message|, |link_ranges|, and |
| 77 // |link_urls|. |
| 78 // Returns false on failure. If false is returned then contents of output |
| 79 // variables are undefined. |
| 80 bool ParseAndAppendToLegalMessage(const base::DictionaryValue& line, |
| 81 base::string16* message, |
| 82 std::vector<gfx::Range>* link_ranges, |
| 83 std::vector<std::string>* link_urls) { |
| 84 // |display_texts| elements are the strings that will be substituted for |
| 85 // "{0}", "{1}", etc. in the template string. |
| 86 std::vector<base::string16> display_texts; |
| 87 |
| 88 // Process all the template parameters for the current message line. |
| 89 const base::ListValue* template_parameters = nullptr; |
| 90 if (line.GetList("template_parameter", &template_parameters)) { |
| 91 for (size_t parameter_index = 0; |
| 92 parameter_index < template_parameters->GetSize(); ++parameter_index) { |
| 93 // Get a single element of the "template_parameter" list. |
| 94 const base::DictionaryValue* single_parameter; |
| 95 if (!template_parameters->GetDictionary(parameter_index, |
| 96 &single_parameter)) |
| 97 return false; |
| 98 |
| 99 // Read and store the "display_text" string. |
| 100 display_texts.push_back(base::string16()); |
| 101 if (!single_parameter->GetString("display_text", &display_texts.back())) |
| 102 return false; |
| 103 |
| 104 // Read and store the "url" string. |
| 105 link_urls->push_back(std::string()); |
| 106 if (!single_parameter->GetString("url", &link_urls->back())) |
| 107 return false; |
| 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 base::string16 message_line; |
| 121 std::vector<size_t> offsets; |
| 122 if (!ReplaceTemplatePlaceholders(template_icu, display_texts, message_line, |
| 123 offsets)) |
| 124 return false; |
| 125 |
| 126 // Fill in range values for all links in this line. |
| 127 for (size_t offset_index = 0; offset_index < offsets.size(); ++offset_index) { |
| 128 size_t range_start = message->size() + offsets[offset_index]; |
| 129 link_ranges->push_back(gfx::Range( |
| 130 range_start, range_start + display_texts[offset_index].size())); |
| 131 } |
| 132 |
| 133 message->append(message_line); |
| 134 return true; |
| 135 } |
| 136 |
24 } // namespace | 137 } // namespace |
25 | 138 |
26 namespace autofill { | 139 namespace autofill { |
27 | 140 |
28 SaveCardBubbleControllerImpl::SaveCardBubbleControllerImpl( | 141 SaveCardBubbleControllerImpl::SaveCardBubbleControllerImpl( |
29 content::WebContents* web_contents) | 142 content::WebContents* web_contents) |
30 : content::WebContentsObserver(web_contents), | 143 : content::WebContentsObserver(web_contents), |
31 save_card_bubble_view_(nullptr) { | 144 save_card_bubble_view_(nullptr) { |
32 DCHECK(web_contents); | 145 DCHECK(web_contents); |
33 } | 146 } |
34 | 147 |
35 SaveCardBubbleControllerImpl::~SaveCardBubbleControllerImpl() { | 148 SaveCardBubbleControllerImpl::~SaveCardBubbleControllerImpl() { |
36 if (save_card_bubble_view_) | 149 if (save_card_bubble_view_) |
37 save_card_bubble_view_->ControllerGone(); | 150 save_card_bubble_view_->ControllerGone(); |
38 } | 151 } |
39 | 152 |
40 void SaveCardBubbleControllerImpl::SetCallback( | 153 void SaveCardBubbleControllerImpl::SetCallback( |
41 const base::Closure& save_card_callback) { | 154 const base::Closure& save_card_callback) { |
42 save_card_callback_ = save_card_callback; | 155 save_card_callback_ = save_card_callback; |
43 } | 156 } |
44 | 157 |
| 158 bool SaveCardBubbleControllerImpl::SetLegalMessage( |
| 159 const base::ListValue& lines) { |
| 160 ClearLegalMessage(); |
| 161 base::string16 message; |
| 162 std::vector<gfx::Range> link_ranges; |
| 163 std::vector<std::string> link_urls; |
| 164 |
| 165 // Process all lines of the message. See comment in header file for example |
| 166 // of valid |lines| data. |
| 167 for (size_t line_index = 0; line_index < lines.GetSize(); ++line_index) { |
| 168 const base::DictionaryValue* single_line; |
| 169 if (!lines.GetDictionary(line_index, &single_line)) |
| 170 return false; |
| 171 |
| 172 if (!ParseAndAppendToLegalMessage(*single_line, &message, &link_ranges, |
| 173 &link_urls)) |
| 174 return false; |
| 175 |
| 176 // If this is not the last line of the message then append a newline. |
| 177 if (line_index + 1 < lines.GetSize()) |
| 178 message.push_back('\n'); |
| 179 } |
| 180 |
| 181 legal_message_ = message; |
| 182 legal_message_link_ranges_ = link_ranges; |
| 183 legal_message_link_urls_ = link_urls; |
| 184 return true; |
| 185 } |
| 186 |
| 187 void SaveCardBubbleControllerImpl::ClearLegalMessage() { |
| 188 legal_message_.clear(); |
| 189 legal_message_link_ranges_.clear(); |
| 190 legal_message_link_urls_.clear(); |
| 191 } |
| 192 |
45 void SaveCardBubbleControllerImpl::ShowBubble() { | 193 void SaveCardBubbleControllerImpl::ShowBubble() { |
46 DCHECK(!save_card_callback_.is_null()); | 194 DCHECK(!save_card_callback_.is_null()); |
47 | 195 |
48 // Need to create location bar icon before bubble, otherwise bubble will be | 196 // Need to create location bar icon before bubble, otherwise bubble will be |
49 // unanchored. | 197 // unanchored. |
50 UpdateIcon(); | 198 UpdateIcon(); |
51 | 199 |
52 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); | 200 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); |
53 save_card_bubble_view_ = | 201 save_card_bubble_view_ = |
54 browser->window()->ShowSaveCreditCardBubble(web_contents(), this); | 202 browser->window()->ShowSaveCreditCardBubble(web_contents(), this); |
(...skipping 22 matching lines...) Expand all Loading... |
77 void SaveCardBubbleControllerImpl::OnSaveButton() { | 225 void SaveCardBubbleControllerImpl::OnSaveButton() { |
78 save_card_callback_.Run(); | 226 save_card_callback_.Run(); |
79 save_card_callback_.Reset(); | 227 save_card_callback_.Reset(); |
80 } | 228 } |
81 | 229 |
82 void SaveCardBubbleControllerImpl::OnCancelButton() { | 230 void SaveCardBubbleControllerImpl::OnCancelButton() { |
83 save_card_callback_.Reset(); | 231 save_card_callback_.Reset(); |
84 } | 232 } |
85 | 233 |
86 void SaveCardBubbleControllerImpl::OnLearnMoreClicked() { | 234 void SaveCardBubbleControllerImpl::OnLearnMoreClicked() { |
87 web_contents()->OpenURL(content::OpenURLParams( | 235 OpenUrl(kHelpURL); |
88 GURL(kHelpURL), content::Referrer(), NEW_FOREGROUND_TAB, | 236 } |
89 ui::PAGE_TRANSITION_LINK, false)); | 237 |
| 238 void SaveCardBubbleControllerImpl::OnLegalMessageLinkClicked( |
| 239 const gfx::Range& link_range) { |
| 240 for (size_t i = 0; i < legal_message_link_ranges_.size(); ++i) { |
| 241 if (legal_message_link_ranges_[i] == link_range) { |
| 242 OpenUrl(legal_message_link_urls_[i]); |
| 243 return; |
| 244 } |
| 245 } |
| 246 // link_range was not found. |
| 247 NOTREACHED(); |
90 } | 248 } |
91 | 249 |
92 void SaveCardBubbleControllerImpl::OnBubbleClosed() { | 250 void SaveCardBubbleControllerImpl::OnBubbleClosed() { |
93 save_card_bubble_view_ = nullptr; | 251 save_card_bubble_view_ = nullptr; |
94 UpdateIcon(); | 252 UpdateIcon(); |
95 } | 253 } |
96 | 254 |
| 255 const base::string16& SaveCardBubbleControllerImpl::GetLegalMessage() const { |
| 256 return legal_message_; |
| 257 } |
| 258 |
| 259 const std::vector<gfx::Range>& |
| 260 SaveCardBubbleControllerImpl::GetLegalMessageLinkRanges() const { |
| 261 return legal_message_link_ranges_; |
| 262 } |
| 263 |
| 264 const std::vector<std::string>& |
| 265 SaveCardBubbleControllerImpl::GetLegalMessageLinkUrlsForTesting() const { |
| 266 return legal_message_link_urls_; |
| 267 } |
| 268 |
97 void SaveCardBubbleControllerImpl::UpdateIcon() { | 269 void SaveCardBubbleControllerImpl::UpdateIcon() { |
98 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); | 270 Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); |
99 LocationBar* location_bar = browser->window()->GetLocationBar(); | 271 LocationBar* location_bar = browser->window()->GetLocationBar(); |
100 location_bar->UpdateSaveCreditCardIcon(); | 272 location_bar->UpdateSaveCreditCardIcon(); |
101 } | 273 } |
102 | 274 |
| 275 void SaveCardBubbleControllerImpl::OpenUrl(const std::string& url) { |
| 276 web_contents()->OpenURL( |
| 277 content::OpenURLParams(GURL(url), content::Referrer(), NEW_FOREGROUND_TAB, |
| 278 ui::PAGE_TRANSITION_LINK, false)); |
| 279 } |
| 280 |
103 void SaveCardBubbleControllerImpl::DidNavigateMainFrame( | 281 void SaveCardBubbleControllerImpl::DidNavigateMainFrame( |
104 const content::LoadCommittedDetails& details, | 282 const content::LoadCommittedDetails& details, |
105 const content::FrameNavigateParams& params) { | 283 const content::FrameNavigateParams& params) { |
106 // Nothing to do if there's no bubble available. | 284 // Nothing to do if there's no bubble available. |
107 if (save_card_callback_.is_null()) | 285 if (save_card_callback_.is_null()) |
108 return; | 286 return; |
109 | 287 |
110 // Don't react to in-page (fragment) navigations. | 288 // Don't react to in-page (fragment) navigations. |
111 if (details.is_in_page) | 289 if (details.is_in_page) |
112 return; | 290 return; |
113 | 291 |
114 // Don't do anything if a navigation occurs before a user could reasonably | 292 // Don't do anything if a navigation occurs before a user could reasonably |
115 // interact with the bubble. | 293 // interact with the bubble. |
116 if (timer_->Elapsed() < | 294 if (timer_->Elapsed() < |
117 base::TimeDelta::FromSeconds(kSurviveNavigationSeconds)) | 295 base::TimeDelta::FromSeconds(kSurviveNavigationSeconds)) |
118 return; | 296 return; |
119 | 297 |
120 // Otherwise, get rid of the bubble and icon. | 298 // Otherwise, get rid of the bubble and icon. |
121 save_card_callback_.Reset(); | 299 save_card_callback_.Reset(); |
122 if (save_card_bubble_view_) | 300 if (save_card_bubble_view_) |
123 save_card_bubble_view_->Close(); | 301 save_card_bubble_view_->Close(); |
124 else | 302 else |
125 UpdateIcon(); | 303 UpdateIcon(); |
126 } | 304 } |
127 | 305 |
128 } // namespace autofill | 306 } // namespace autofill |
OLD | NEW |