| 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.
|
| +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.
|
| +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) {
|
|
|