Index: chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc |
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc |
index 2e43699e3f435ffe870588f220a00da460322e94..627464a0946452c5e72b5fbdb051126de8ba00fe 100644 |
--- a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc |
+++ b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc |
@@ -4,6 +4,9 @@ |
#include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h" |
+#include "base/i18n/message_formatter.h" |
+#include "base/strings/string_util.h" |
+#include "base/strings/utf_string_conversions.h" |
#include "chrome/browser/ui/autofill/save_card_bubble_view.h" |
#include "chrome/browser/ui/browser.h" |
#include "chrome/browser/ui/browser_finder.h" |
@@ -21,6 +24,116 @@ namespace { |
// TODO(bondd): Share with ManagePasswordsUIController. |
const int kSurviveNavigationSeconds = 5; |
+// Replace "{0}", "{1}", ... in |template_icu| with corresponding strings |
+// from |display_texts|. Sets |out_message| to the resulting string, with |
+// start position of each replacement in |out_offsets|. |
+// Return false on failure. If false is returned then contents of |out_message| |
+// and |out_offsets| are undefined. |
bondd
2015/11/11 01:53:36
Leaves output vars undefined on failure because it
|
+bool ReplaceTemplatePlaceholders( |
+ const base::string16& template_icu, |
+ const std::vector<base::string16>& display_texts, |
+ base::string16& out_message, |
+ std::vector<size_t>& out_offsets) { |
+ // Escape "$" -> "$$" for ReplaceStringPlaceholders(). |
+ // |
+ // Edge cases: |
+ // 1. Two or more consecutive $ characters will be incorrectly expanded |
+ // ("$$" -> "$$$$", which ReplaceStringPlaceholders() then turns into |
+ // "$$$"). |
+ // |
+ // 2. "${" will cause false to be returned. "${0}" will expand to "$${0}". |
+ // FormatWithNumberedArgs() turns it into "$$$1", which |
+ // ReplaceStringPlaceholders() then turns into "$$1" without doing the |
+ // parameter replacement. This causes false to be returned because each |
+ // parameter is not used exactly once. |
+ // |
+ // Both of these cases are noted in the header file, and are unlikely to |
+ // occur in any actual legal message. |
+ base::string16 template_icu_escaped; |
+ base::ReplaceChars(template_icu, base::ASCIIToUTF16("$"), |
+ base::ASCIIToUTF16("$$"), &template_icu_escaped); |
+ |
+ // Replace "{0}" -> "$1", "{1}" -> "$2", ... to prepare |template_dollars| |
+ // for ReplaceStringPlaceholders(). |
+ base::string16 template_dollars = |
+ base::i18n::MessageFormatter::FormatWithNumberedArgs( |
+ template_icu_escaped, "$1", "$2", "$3", "$4", "$5", "$6", "$7"); |
+ |
+ // FormatWithNumberedArgs() returns an empty string on failure. |
+ if (template_dollars.empty() && !template_icu.empty()) |
+ return false; |
+ |
+ // Replace "$1", "$2", ... with the display text of each parameter. |
+ out_message = |
+ ReplaceStringPlaceholders(template_dollars, display_texts, &out_offsets); |
+ |
+ // Each parameter must be used exactly once. If a parameter is unused or |
+ // used more than once then it can't be determined which |offsets| entry |
+ // corresponds to which parameter. |
+ return out_offsets.size() == display_texts.size(); |
+} |
+ |
+// Parses |line| and appends results to |message|, |link_ranges|, and |
+// |link_urls|. |
+// Returns false on failure. If false is returned then contents of output |
+// variables are undefined. |
bondd
2015/11/11 01:53:36
Same reasoning about undefined vars as for Replace
|
+bool ParseAndAppendToLegalMessage(const base::DictionaryValue& line, |
+ base::string16* message, |
+ std::vector<gfx::Range>* link_ranges, |
+ std::vector<std::string>* link_urls) { |
+ // |display_texts| elements are the strings that will be substituted for |
+ // "{0}", "{1}", etc. in the template string. |
+ std::vector<base::string16> display_texts; |
+ |
+ // Process all the template parameters for the current message line. |
+ const base::ListValue* template_parameters = nullptr; |
+ if (line.GetList("template_parameter", &template_parameters)) { |
+ for (size_t parameter_index = 0; |
+ parameter_index < template_parameters->GetSize(); ++parameter_index) { |
+ // Get a single element of the "template_parameter" list. |
+ const base::DictionaryValue* single_parameter; |
+ if (!template_parameters->GetDictionary(parameter_index, |
+ &single_parameter)) |
+ return false; |
+ |
+ // Read and store the "display_text" string. |
+ display_texts.push_back(base::string16()); |
+ if (!single_parameter->GetString("display_text", &display_texts.back())) |
+ return false; |
+ |
+ // Read and store the "url" string. |
+ link_urls->push_back(std::string()); |
+ if (!single_parameter->GetString("url", &link_urls->back())) |
+ return false; |
+ } |
+ } |
+ |
+ // Read the template string. It's a small subset of the ICU message format |
+ // syntax. |
+ base::string16 template_icu; |
+ if (!line.GetString("template", &template_icu)) |
+ return false; |
+ |
+ // Replace the placeholders in |template_icu| with strings from |
+ // |display_texts|, and store the start position of each replacement in |
+ // |offsets|. |
+ base::string16 message_line; |
+ std::vector<size_t> offsets; |
+ if (!ReplaceTemplatePlaceholders(template_icu, display_texts, message_line, |
+ offsets)) |
+ return false; |
+ |
+ // Fill in range values for all links in this line. |
+ for (size_t offset_index = 0; offset_index < offsets.size(); ++offset_index) { |
+ size_t range_start = message->size() + offsets[offset_index]; |
+ link_ranges->push_back(gfx::Range( |
+ range_start, range_start + display_texts[offset_index].size())); |
+ } |
+ |
+ message->append(message_line); |
+ return true; |
+} |
+ |
} // namespace |
namespace autofill { |
@@ -42,6 +155,41 @@ void SaveCardBubbleControllerImpl::SetCallback( |
save_card_callback_ = save_card_callback; |
} |
+bool SaveCardBubbleControllerImpl::SetLegalMessage( |
+ const base::ListValue& lines) { |
+ ClearLegalMessage(); |
+ base::string16 message; |
+ std::vector<gfx::Range> link_ranges; |
+ std::vector<std::string> link_urls; |
+ |
+ // Process all lines of the message. See comment in header file for example |
+ // of valid |lines| data. |
+ for (size_t line_index = 0; line_index < lines.GetSize(); ++line_index) { |
+ const base::DictionaryValue* single_line; |
+ if (!lines.GetDictionary(line_index, &single_line)) |
+ return false; |
+ |
+ if (!ParseAndAppendToLegalMessage(*single_line, &message, &link_ranges, |
+ &link_urls)) |
+ return false; |
+ |
+ // If this is not the last line of the message then append a newline. |
+ if (line_index + 1 < lines.GetSize()) |
+ message.push_back('\n'); |
+ } |
+ |
+ legal_message_ = message; |
+ legal_message_link_ranges_ = link_ranges; |
+ legal_message_link_urls_ = link_urls; |
+ return true; |
+} |
+ |
+void SaveCardBubbleControllerImpl::ClearLegalMessage() { |
+ legal_message_.clear(); |
+ legal_message_link_ranges_.clear(); |
+ legal_message_link_urls_.clear(); |
+} |
+ |
void SaveCardBubbleControllerImpl::ShowBubble() { |
DCHECK(!save_card_callback_.is_null()); |
@@ -84,9 +232,19 @@ void SaveCardBubbleControllerImpl::OnCancelButton() { |
} |
void SaveCardBubbleControllerImpl::OnLearnMoreClicked() { |
- web_contents()->OpenURL(content::OpenURLParams( |
- GURL(kHelpURL), content::Referrer(), NEW_FOREGROUND_TAB, |
- ui::PAGE_TRANSITION_LINK, false)); |
+ OpenUrl(kHelpURL); |
+} |
+ |
+void SaveCardBubbleControllerImpl::OnLegalMessageLinkClicked( |
+ const gfx::Range& link_range) { |
+ for (size_t i = 0; i < legal_message_link_ranges_.size(); ++i) { |
+ if (legal_message_link_ranges_[i] == link_range) { |
+ OpenUrl(legal_message_link_urls_[i]); |
+ return; |
+ } |
+ } |
+ // link_range was not found. |
+ NOTREACHED(); |
} |
void SaveCardBubbleControllerImpl::OnBubbleClosed() { |
@@ -94,12 +252,32 @@ void SaveCardBubbleControllerImpl::OnBubbleClosed() { |
UpdateIcon(); |
} |
+const base::string16& SaveCardBubbleControllerImpl::GetLegalMessage() const { |
+ return legal_message_; |
+} |
+ |
+const std::vector<gfx::Range>& |
+SaveCardBubbleControllerImpl::GetLegalMessageLinkRanges() const { |
+ return legal_message_link_ranges_; |
+} |
+ |
+const std::vector<std::string>& |
+SaveCardBubbleControllerImpl::GetLegalMessageLinkUrlsForTesting() const { |
+ return legal_message_link_urls_; |
+} |
+ |
void SaveCardBubbleControllerImpl::UpdateIcon() { |
Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); |
LocationBar* location_bar = browser->window()->GetLocationBar(); |
location_bar->UpdateSaveCreditCardIcon(); |
} |
+void SaveCardBubbleControllerImpl::OpenUrl(const std::string& url) { |
+ web_contents()->OpenURL( |
+ content::OpenURLParams(GURL(url), content::Referrer(), NEW_FOREGROUND_TAB, |
+ ui::PAGE_TRANSITION_LINK, false)); |
+} |
+ |
void SaveCardBubbleControllerImpl::DidNavigateMainFrame( |
const content::LoadCommittedDetails& details, |
const content::FrameNavigateParams& params) { |