summaryrefslogtreecommitdiff
path: root/chromium/components/search_engines
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2021-05-20 09:47:09 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2021-06-07 11:15:42 +0000
commit189d4fd8fad9e3c776873be51938cd31a42b6177 (patch)
tree6497caeff5e383937996768766ab3bb2081a40b2 /chromium/components/search_engines
parent8bc75099d364490b22f43a7ce366b366c08f4164 (diff)
downloadqtwebengine-chromium-189d4fd8fad9e3c776873be51938cd31a42b6177.tar.gz
BASELINE: Update Chromium to 90.0.4430.221
Change-Id: Iff4d9d18d2fcf1a576f3b1f453010f744a232920 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/search_engines')
-rw-r--r--chromium/components/search_engines/BUILD.gn2
-rw-r--r--chromium/components/search_engines/DEPS2
-rw-r--r--chromium/components/search_engines/DIR_METADATA3
-rw-r--r--chromium/components/search_engines/OWNERS2
-rw-r--r--chromium/components/search_engines/android/BUILD.gn2
-rw-r--r--chromium/components/search_engines/android/DIR_METADATA3
-rw-r--r--chromium/components/search_engines/android/OWNERS2
-rw-r--r--chromium/components/search_engines/android/template_url_service_android.cc29
-rw-r--r--chromium/components/search_engines/android/template_url_service_android.h2
-rw-r--r--chromium/components/search_engines/default_search_manager.cc8
-rw-r--r--chromium/components/search_engines/default_search_manager.h3
-rw-r--r--chromium/components/search_engines/keyword_web_data_service.cc11
-rw-r--r--chromium/components/search_engines/prepopulated_engines.json8
-rw-r--r--chromium/components/search_engines/search_terms_data.cc16
-rw-r--r--chromium/components/search_engines/search_terms_data.h5
-rw-r--r--chromium/components/search_engines/template_url.cc62
-rw-r--r--chromium/components/search_engines/template_url.h11
-rw-r--r--chromium/components/search_engines/template_url_fetcher.cc29
-rw-r--r--chromium/components/search_engines/template_url_fetcher.h1
-rw-r--r--chromium/components/search_engines/template_url_prepopulate_data_unittest.cc2
-rw-r--r--chromium/components/search_engines/template_url_service.cc560
-rw-r--r--chromium/components/search_engines/template_url_service.h147
-rw-r--r--chromium/components/search_engines/template_url_unittest.cc31
23 files changed, 517 insertions, 424 deletions
diff --git a/chromium/components/search_engines/BUILD.gn b/chromium/components/search_engines/BUILD.gn
index d95854ac609..c2b09f07178 100644
--- a/chromium/components/search_engines/BUILD.gn
+++ b/chromium/components/search_engines/BUILD.gn
@@ -68,6 +68,7 @@ static_library("search_engines") {
"//components/database_utils",
"//components/history/core/browser",
"//components/infobars/core",
+ "//components/lens:lens",
"//components/policy:generated",
"//components/policy/core/browser",
"//components/pref_registry",
@@ -78,6 +79,7 @@ static_library("search_engines") {
"//net",
"//services/data_decoder/public/cpp",
"//services/network/public/cpp",
+ "//services/network/public/mojom",
"//sql",
"//third_party/metrics_proto",
"//ui/base",
diff --git a/chromium/components/search_engines/DEPS b/chromium/components/search_engines/DEPS
index c2e314ee180..68a433c82e9 100644
--- a/chromium/components/search_engines/DEPS
+++ b/chromium/components/search_engines/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"+components/history/core",
"+components/infobars/core",
"+components/keyed_service/core",
+ "+components/lens",
"+components/policy",
"+components/pref_registry",
"+components/prefs",
@@ -18,6 +19,7 @@ include_rules = [
"+net",
"+services/data_decoder/public",
"+services/network/public/cpp",
+ "+services/network/public/mojom",
"+services/network/test",
"+sql",
"+ui/base",
diff --git a/chromium/components/search_engines/DIR_METADATA b/chromium/components/search_engines/DIR_METADATA
new file mode 100644
index 00000000000..4bbdcaebcc8
--- /dev/null
+++ b/chromium/components/search_engines/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "UI>Browser>Search"
+}
diff --git a/chromium/components/search_engines/OWNERS b/chromium/components/search_engines/OWNERS
index d645718719c..7d3da37e00f 100644
--- a/chromium/components/search_engines/OWNERS
+++ b/chromium/components/search_engines/OWNERS
@@ -1,5 +1,3 @@
file://components/omnibox/OWNERS
vasilii@chromium.org
-
-# COMPONENT: UI>Browser>Search
diff --git a/chromium/components/search_engines/android/BUILD.gn b/chromium/components/search_engines/android/BUILD.gn
index 8d711dd3a2c..e80cb104c0b 100644
--- a/chromium/components/search_engines/android/BUILD.gn
+++ b/chromium/components/search_engines/android/BUILD.gn
@@ -9,7 +9,7 @@ android_library("java") {
"//base:base_java",
"//base:jni_java",
"//content/public/android:content_java",
- "//third_party/android_deps:androidx_annotation_annotation_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
"//url:gurl_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/chromium/components/search_engines/android/DIR_METADATA b/chromium/components/search_engines/android/DIR_METADATA
new file mode 100644
index 00000000000..4bbdcaebcc8
--- /dev/null
+++ b/chromium/components/search_engines/android/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+ component: "UI>Browser>Search"
+}
diff --git a/chromium/components/search_engines/android/OWNERS b/chromium/components/search_engines/android/OWNERS
index 52b99d7e26c..697c69edec7 100644
--- a/chromium/components/search_engines/android/OWNERS
+++ b/chromium/components/search_engines/android/OWNERS
@@ -1,3 +1 @@
file://chrome/android/OWNERS
-
-# COMPONENT: UI>Browser>Search
diff --git a/chromium/components/search_engines/android/template_url_service_android.cc b/chromium/components/search_engines/android/template_url_service_android.cc
index ab52b89d5eb..70828e0531a 100644
--- a/chromium/components/search_engines/android/template_url_service_android.cc
+++ b/chromium/components/search_engines/android/template_url_service_android.cc
@@ -36,8 +36,8 @@ TemplateUrlServiceAndroid::TemplateUrlServiceAndroid(
TemplateURLService* template_url_service)
: template_url_service_(template_url_service) {
template_url_subscription_ = template_url_service_->RegisterOnLoadedCallback(
- base::Bind(&TemplateUrlServiceAndroid::OnTemplateURLServiceLoaded,
- base::Unretained(this)));
+ base::BindOnce(&TemplateUrlServiceAndroid::OnTemplateURLServiceLoaded,
+ base::Unretained(this)));
template_url_service_->AddObserver(this);
}
@@ -119,7 +119,7 @@ TemplateUrlServiceAndroid::IsSearchResultsPageFromDefaultSearchProvider(
}
void TemplateUrlServiceAndroid::OnTemplateURLServiceLoaded() {
- template_url_subscription_.reset();
+ template_url_subscription_ = {};
JNIEnv* env = base::android::AttachCurrentThread();
if (!java_ref_)
return;
@@ -289,8 +289,19 @@ jboolean TemplateUrlServiceAndroid::SetPlayAPISearchEngine(
auto existing_play_api_turl = std::find_if(
template_urls.cbegin(), template_urls.cend(),
[](const TemplateURL* turl) { return turl->created_from_play_api(); });
- if (existing_play_api_turl != template_urls.cend())
+ if (existing_play_api_turl != template_urls.cend()) {
+ // Migrate old Play API database entries that were incorrectly marked as
+ // safe_for_autoreplace() before M89.
+ // TODO(tommycli): Delete this once the below metric approaches zero.
+ TemplateURL* turl = *existing_play_api_turl;
+ if (turl->safe_for_autoreplace()) {
+ TemplateURLService::LogSearchTemplateURLEvent(
+ TemplateURLService::MIGRATE_SAFE_FOR_AUTOREPLACE_PLAY_API_ENGINE);
+ template_url_service_->ResetTemplateURL(turl, turl->short_name(),
+ turl->keyword(), turl->url());
+ }
return false;
+ }
base::string16 keyword =
base::android::ConvertJavaStringToUTF16(env, jkeyword);
@@ -305,12 +316,14 @@ jboolean TemplateUrlServiceAndroid::SetPlayAPISearchEngine(
favicon_url = base::android::ConvertJavaStringToUTF8(jfavicon_url);
}
- TemplateURL* t_url =
- template_url_service_->CreateOrUpdateTemplateURLFromPlayAPIData(
- name, keyword, search_url, suggest_url, favicon_url);
+ TemplateURL* t_url = template_url_service_->CreatePlayAPISearchEngine(
+ name, keyword, search_url, suggest_url, favicon_url);
- if (set_as_default && template_url_service_->CanMakeDefault(t_url))
+ // CanMakeDefault() will prevent us from taking over a policy or extension
+ // defined default search engine.
+ if (set_as_default && template_url_service_->CanMakeDefault(t_url)) {
template_url_service_->SetUserSelectedDefaultSearchProvider(t_url);
+ }
return true;
}
diff --git a/chromium/components/search_engines/android/template_url_service_android.h b/chromium/components/search_engines/android/template_url_service_android.h
index 715bbeb2bba..4174566f3e5 100644
--- a/chromium/components/search_engines/android/template_url_service_android.h
+++ b/chromium/components/search_engines/android/template_url_service_android.h
@@ -122,7 +122,7 @@ class TemplateUrlServiceAndroid : public TemplateURLServiceObserver {
// Pointer to the TemplateUrlService for the main profile.
TemplateURLService* template_url_service_;
- std::unique_ptr<TemplateURLService::Subscription> template_url_subscription_;
+ base::CallbackListSubscription template_url_subscription_;
DISALLOW_COPY_AND_ASSIGN(TemplateUrlServiceAndroid);
};
diff --git a/chromium/components/search_engines/default_search_manager.cc b/chromium/components/search_engines/default_search_manager.cc
index 82f5878b84a..28c6f03ff7c 100644
--- a/chromium/components/search_engines/default_search_manager.cc
+++ b/chromium/components/search_engines/default_search_manager.cc
@@ -87,12 +87,12 @@ DefaultSearchManager::DefaultSearchManager(
pref_change_registrar_.Init(pref_service_);
pref_change_registrar_.Add(
kDefaultSearchProviderDataPrefName,
- base::Bind(&DefaultSearchManager::OnDefaultSearchPrefChanged,
- base::Unretained(this)));
+ base::BindRepeating(&DefaultSearchManager::OnDefaultSearchPrefChanged,
+ base::Unretained(this)));
pref_change_registrar_.Add(
prefs::kSearchProviderOverrides,
- base::Bind(&DefaultSearchManager::OnOverridesPrefChanged,
- base::Unretained(this)));
+ base::BindRepeating(&DefaultSearchManager::OnOverridesPrefChanged,
+ base::Unretained(this)));
}
LoadPrepopulatedDefaultSearch();
LoadDefaultSearchEngineFromPrefs();
diff --git a/chromium/components/search_engines/default_search_manager.h b/chromium/components/search_engines/default_search_manager.h
index 5e4c6daa3ea..8c3f4000010 100644
--- a/chromium/components/search_engines/default_search_manager.h
+++ b/chromium/components/search_engines/default_search_manager.h
@@ -75,7 +75,8 @@ class DefaultSearchManager {
FROM_POLICY,
};
- typedef base::Callback<void(const TemplateURLData*, Source)> ObserverCallback;
+ using ObserverCallback =
+ base::RepeatingCallback<void(const TemplateURLData*, Source)>;
DefaultSearchManager(PrefService* pref_service,
const ObserverCallback& change_observer);
diff --git a/chromium/components/search_engines/keyword_web_data_service.cc b/chromium/components/search_engines/keyword_web_data_service.cc
index 265bbdd8426..4f2de437237 100644
--- a/chromium/components/search_engines/keyword_web_data_service.cc
+++ b/chromium/components/search_engines/keyword_web_data_service.cc
@@ -122,17 +122,17 @@ WebDataServiceBase::Handle KeywordWebDataService::GetKeywords(
CommitQueuedOperations();
return wdbs_->ScheduleDBTaskWithResult(
- FROM_HERE, base::Bind(&GetKeywordsImpl), consumer);
+ FROM_HERE, base::BindOnce(&GetKeywordsImpl), consumer);
}
void KeywordWebDataService::SetDefaultSearchProviderID(TemplateURLID id) {
wdbs_->ScheduleDBTask(FROM_HERE,
- base::Bind(&SetDefaultSearchProviderIDImpl, id));
+ base::BindOnce(&SetDefaultSearchProviderIDImpl, id));
}
void KeywordWebDataService::SetBuiltinKeywordVersion(int version) {
wdbs_->ScheduleDBTask(FROM_HERE,
- base::Bind(&SetBuiltinKeywordVersionImpl, version));
+ base::BindOnce(&SetBuiltinKeywordVersionImpl, version));
}
void KeywordWebDataService::ShutdownOnUISequence() {
@@ -167,8 +167,9 @@ void KeywordWebDataService::AdjustBatchModeLevel(bool entering_batch_mode) {
void KeywordWebDataService::CommitQueuedOperations() {
if (!queued_keyword_operations_.empty()) {
- wdbs_->ScheduleDBTask(FROM_HERE, base::Bind(&PerformKeywordOperationsImpl,
- queued_keyword_operations_));
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ base::BindOnce(&PerformKeywordOperationsImpl,
+ queued_keyword_operations_));
queued_keyword_operations_.clear();
}
timer_.Stop();
diff --git a/chromium/components/search_engines/prepopulated_engines.json b/chromium/components/search_engines/prepopulated_engines.json
index cb4bc61f0c6..269a4897ffc 100644
--- a/chromium/components/search_engines/prepopulated_engines.json
+++ b/chromium/components/search_engines/prepopulated_engines.json
@@ -28,7 +28,7 @@
// Increment this if you change the data in ways that mean users with
// existing data should get a new version. Otherwise, existing data may
// continue to be used and updates made here will not always appear.
- "kCurrentDataVersion": 123
+ "kCurrentDataVersion": 125
},
// The following engines are included in country lists and are added to the
@@ -116,9 +116,9 @@
"name": "Google",
"keyword": "google.com",
"favicon_url": "https://www.google.com/images/branding/product/ico/googleg_lodp.ico",
- "search_url": "{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}",
+ "search_url": "{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:prefetchSource}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}",
"suggest_url": "{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:omniboxFocusType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}",
- "image_url": "{google:baseURL}searchbyimage/upload",
+ "image_url": "{google:baseSearchByImageURL}upload",
"contextual_search_url": "{google:baseURL}_/contextualsearch?{google:contextualSearchVersion}{google:contextualSearchContextData}",
"image_url_post_params": "encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}",
"alternate_urls": [
@@ -208,7 +208,7 @@
"alternate_urls": [
"https://m.sogou.com/web/{google:pathWildcard}?ie={inputEncoding}&keyword={searchTerms}"
],
- "suggest_url": "http://api.sugg.sogou.com/su?type=addrbar&key={searchTerms}&ie={inputEncoding}",
+ "suggest_url": "https://sugg.sogou.com/sugg/ajaj_json.jsp?type=addrbar&key={searchTerms}&ie={inputEncoding}&from=google",
"type": "SEARCH_ENGINE_SOGOU",
"id": 56
},
diff --git a/chromium/components/search_engines/search_terms_data.cc b/chromium/components/search_engines/search_terms_data.cc
index 0005ed87a8d..83965f45ac2 100644
--- a/chromium/components/search_engines/search_terms_data.cc
+++ b/chromium/components/search_engines/search_terms_data.cc
@@ -6,18 +6,26 @@
#include "base/check.h"
#include "components/google/core/common/google_util.h"
+#include "components/lens/lens_features.h"
#include "url/gurl.h"
-SearchTermsData::SearchTermsData() {
-}
+SearchTermsData::SearchTermsData() = default;
-SearchTermsData::~SearchTermsData() {
-}
+SearchTermsData::~SearchTermsData() = default;
std::string SearchTermsData::GoogleBaseURLValue() const {
return google_util::kGoogleHomepageURL;
}
+std::string SearchTermsData::GoogleBaseSearchByImageURLValue() const {
+ const std::string kGoogleHomepageURLPath = std::string("searchbyimage/");
+
+ if (base::FeatureList::IsEnabled(lens::features::kLensStandalone)) {
+ return lens::features::GetHomepageURL();
+ }
+ return google_util::kGoogleHomepageURL + kGoogleHomepageURLPath;
+}
+
std::string SearchTermsData::GoogleBaseSuggestURLValue() const {
// Start with the Google base URL.
const GURL base_url(GoogleBaseURLValue());
diff --git a/chromium/components/search_engines/search_terms_data.h b/chromium/components/search_engines/search_terms_data.h
index 6fc30745273..d6820917770 100644
--- a/chromium/components/search_engines/search_terms_data.h
+++ b/chromium/components/search_engines/search_terms_data.h
@@ -22,6 +22,11 @@ class SearchTermsData {
// implementation simply returns the default value.
virtual std::string GoogleBaseURLValue() const;
+ // Returns the value to use for the GOOGLE_BASE_SEARCH_BY_IMAGE_URL. Points
+ // at Lens if the user is enrolled in the Lens experiment, and defaults to
+ // Image Search otherwise.
+ virtual std::string GoogleBaseSearchByImageURLValue() const;
+
// Returns the value for the GOOGLE_BASE_SUGGEST_URL term. This
// implementation simply returns the default value.
std::string GoogleBaseSuggestURLValue() const;
diff --git a/chromium/components/search_engines/template_url.cc b/chromium/components/search_engines/template_url.cc
index f195c74444a..3e427599038 100644
--- a/chromium/components/search_engines/template_url.cc
+++ b/chromium/components/search_engines/template_url.cc
@@ -11,13 +11,13 @@
#include "base/base64.h"
#include "base/check_op.h"
#include "base/command_line.h"
+#include "base/containers/contains.h"
#include "base/format_macros.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/i18n/rtl.h"
#include "base/metrics/field_trial.h"
#include "base/notreached.h"
-#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
@@ -72,6 +72,8 @@ const char kDefaultCount[] = "10";
// Used if the output encoding parameter is required.
const char kOutputEncodingType[] = "UTF-8";
+const size_t kMaxStringEncodeStringLength = 1'000'000;
+
// Attempts to encode |terms| and |original_query| in |encoding| and escape
// them. |terms| may be escaped as path or query depending on |is_in_query|;
// |original_query| is always escaped as query. If |force_encode| is true
@@ -86,12 +88,23 @@ bool TryEncoding(const base::string16& terms,
base::string16* escaped_original_query) {
DCHECK(escaped_terms);
DCHECK(escaped_original_query);
+
+ // Both |base::UTF16ToCodepage()| and |net::Escape*()| invocations below
+ // create strings longer than their inputs. To ensure doing so does not crash,
+ // this truncates |terms| to |kMaxStringEncodeStringLength|.
+ const base::string16& truncated_terms =
+ terms.size() > kMaxStringEncodeStringLength
+ ? terms.substr(0, kMaxStringEncodeStringLength)
+ : terms;
+
base::OnStringConversionError::Type error_handling =
force_encode ? base::OnStringConversionError::SKIP
: base::OnStringConversionError::FAIL;
std::string encoded_terms;
- if (!base::UTF16ToCodepage(terms, encoding, error_handling, &encoded_terms))
+ if (!base::UTF16ToCodepage(truncated_terms, encoding, error_handling,
+ &encoded_terms)) {
return false;
+ }
*escaped_terms = base::UTF8ToUTF16(is_in_query ?
net::EscapeQueryParamValue(encoded_terms, true) :
net::EscapePath(encoded_terms));
@@ -636,10 +649,11 @@ bool TemplateURLRef::ParseParameter(size_t start,
length--;
}
- const base::StringPiece parameter(original_url.begin() + start + 1,
- original_url.begin() + start + 1 + length);
- const base::StringPiece full_parameter(original_url.begin() + start,
- original_url.begin() + end + 1);
+ const auto parameter =
+ base::MakeStringPiece(original_url.begin() + start + 1,
+ original_url.begin() + start + 1 + length);
+ const auto full_parameter = base::MakeStringPiece(
+ original_url.begin() + start, original_url.begin() + end + 1);
// Remove the parameter from the string. For parameters who replacement is
// constant and already known, just replace them directly. For other cases,
// like parameters whose values may change over time, use |replacements|.
@@ -653,6 +667,9 @@ bool TemplateURLRef::ParseParameter(size_t start,
replacements->push_back(Replacement(GOOGLE_ASSISTED_QUERY_STATS, start));
} else if (parameter == "google:baseURL") {
replacements->push_back(Replacement(GOOGLE_BASE_URL, start));
+ } else if (parameter == "google:baseSearchByImageURL") {
+ replacements->push_back(
+ Replacement(GOOGLE_BASE_SEARCH_BY_IMAGE_URL, start));
} else if (parameter == "google:baseSuggestURL") {
replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL, start));
} else if (parameter == "google:currentPageUrl") {
@@ -700,6 +717,8 @@ bool TemplateURLRef::ParseParameter(size_t start,
// Do nothing, we just want the path wildcard removed from the URL.
} else if (parameter == "google:prefetchQuery") {
replacements->push_back(Replacement(GOOGLE_PREFETCH_QUERY, start));
+ } else if (parameter == "google:prefetchSource") {
+ replacements->push_back(Replacement(GOOGLE_PREFETCH_SOURCE, start));
} else if (parameter == "google:RLZ") {
replacements->push_back(Replacement(GOOGLE_RLZ, start));
} else if (parameter == "google:searchClient") {
@@ -1043,6 +1062,13 @@ std::string TemplateURLRef::HandleReplacements(
std::string(), search_terms_data.GoogleBaseURLValue(), *i, &url);
break;
+ case GOOGLE_BASE_SEARCH_BY_IMAGE_URL:
+ DCHECK(!i->is_post_param);
+ HandleReplacement(std::string(),
+ search_terms_data.GoogleBaseSearchByImageURLValue(),
+ *i, &url);
+ break;
+
case GOOGLE_BASE_SUGGEST_URL:
DCHECK(!i->is_post_param);
HandleReplacement(
@@ -1116,6 +1142,19 @@ std::string TemplateURLRef::HandleReplacements(
break;
}
+ case GOOGLE_PREFETCH_SOURCE: {
+ if (search_terms_args.is_prefetch) {
+ // Currently, Chrome only support "cs" for prefetches, but if new
+ // prefetch sources (outside of suggestions) are added, a new prefetch
+ // source value is needed. These should denote the source of the
+ // prefetch to allow the search server to treat the requests based on
+ // source. "cs" represents Chrome Suggestions as the source. Adding a
+ // new source should be supported by the Search engine.
+ HandleReplacement(std::string(), "pf=cs&", *i, &url);
+ }
+ break;
+ }
+
case GOOGLE_RLZ: {
DCHECK(!i->is_post_param);
// On platforms that don't have RLZ, we still want this branch
@@ -1335,8 +1374,15 @@ bool TemplateURL::IsBetterThanEngineWithConflictingKeyword(
: base::Time(),
// Prefer engines that CANNOT be auto-replaced.
!engine->safe_for_autoreplace(),
- // More recently modified engines win.
- engine->last_modified(),
+ // Prefer engines created by Play API.
+ engine->created_from_play_api(),
+ // Favor prepopulated engines over other auto-generated engines.
+ engine->prepopulate_id() > 0,
+ // Favor engines derived from OpenSearch descriptions over
+ // autogenerated engines heuristically generated from searchable forms.
+ engine->originating_url().is_valid(),
+ // More recently modified engines or created engines win.
+ engine->last_modified(), engine->date_created(),
// TODO(tommycli): This should be a tie-breaker than provides a total
// ordering of all TemplateURLs so that distributed clients resolve
// conflicts identically. This sync_guid is not globally unique today,
diff --git a/chromium/components/search_engines/template_url.h b/chromium/components/search_engines/template_url.h
index 78a4a8fb1f0..a44e77dfabb 100644
--- a/chromium/components/search_engines/template_url.h
+++ b/chromium/components/search_engines/template_url.h
@@ -248,6 +248,10 @@ class TemplateURLRef {
// Source of the search or suggest request.
RequestSource request_source = SEARCHBOX;
+ // Whether the query is being fetched as a prefetch request before the user
+ // actually searches for the search terms.
+ bool is_prefetch = false;
+
ContextualSearchParams contextual_search_params;
};
@@ -377,6 +381,7 @@ class TemplateURLRef {
ENCODING,
GOOGLE_ASSISTED_QUERY_STATS,
GOOGLE_BASE_URL,
+ GOOGLE_BASE_SEARCH_BY_IMAGE_URL,
GOOGLE_BASE_SUGGEST_URL,
GOOGLE_CONTEXTUAL_SEARCH_VERSION,
GOOGLE_CONTEXTUAL_SEARCH_CONTEXT_DATA,
@@ -395,6 +400,7 @@ class TemplateURLRef {
GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
GOOGLE_PAGE_CLASSIFICATION,
GOOGLE_PREFETCH_QUERY,
+ GOOGLE_PREFETCH_SOURCE,
GOOGLE_RLZ,
GOOGLE_SEARCH_CLIENT,
GOOGLE_SEARCH_FIELDTRIAL_GROUP,
@@ -584,11 +590,12 @@ class TemplateURL {
enum Type {
// Installed only on this device. Should not be synced. This is not common.
LOCAL = 0,
- // Regular search engine. This is the most common.
+ // Regular search engine. This is the most common, and the ONLY type synced.
NORMAL = 1,
- // Installed by extension through Override Settings API.
+ // Installed by extension through Override Settings API. Not synced.
NORMAL_CONTROLLED_BY_EXTENSION = 2,
// The keyword associated with an extension that uses the Omnibox API.
+ // Not synced.
OMNIBOX_API_EXTENSION = 3,
};
diff --git a/chromium/components/search_engines/template_url_fetcher.cc b/chromium/components/search_engines/template_url_fetcher.cc
index d34bb702108..82439fd9f61 100644
--- a/chromium/components/search_engines/template_url_fetcher.cc
+++ b/chromium/components/search_engines/template_url_fetcher.cc
@@ -16,6 +16,7 @@
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
namespace {
@@ -67,7 +68,6 @@ class TemplateURLFetcher::RequestDelegate {
const url::Origin& initiator,
network::mojom::URLLoaderFactory* url_loader_factory,
int render_frame_id,
- int resource_type,
int32_t request_id);
// If data contains a valid OSDD, a TemplateURL is created and added to
@@ -92,7 +92,7 @@ class TemplateURLFetcher::RequestDelegate {
const GURL osdd_url_;
const GURL favicon_url_;
- std::unique_ptr<TemplateURLService::Subscription> template_url_subscription_;
+ base::CallbackListSubscription template_url_subscription_;
DISALLOW_COPY_AND_ASSIGN(RequestDelegate);
};
@@ -105,7 +105,6 @@ TemplateURLFetcher::RequestDelegate::RequestDelegate(
const url::Origin& initiator,
network::mojom::URLLoaderFactory* url_loader_factory,
int render_frame_id,
- int resource_type,
int32_t request_id)
: fetcher_(fetcher),
keyword_(keyword),
@@ -117,8 +116,8 @@ TemplateURLFetcher::RequestDelegate::RequestDelegate(
if (!model->loaded()) {
// Start the model load and set-up waiting for it.
template_url_subscription_ = model->RegisterOnLoadedCallback(
- base::Bind(&TemplateURLFetcher::RequestDelegate::OnLoaded,
- base::Unretained(this)));
+ base::BindOnce(&TemplateURLFetcher::RequestDelegate::OnLoaded,
+ base::Unretained(this)));
model->Load();
}
@@ -126,7 +125,11 @@ TemplateURLFetcher::RequestDelegate::RequestDelegate(
resource_request->url = osdd_url;
resource_request->request_initiator = initiator;
resource_request->render_frame_id = render_frame_id;
- resource_request->resource_type = resource_type;
+ // TODO(crbug.com/1059639): Remove |resource_type| once the request is handled
+ // with RequestDestination without ResourceType.
+ resource_request->resource_type =
+ /* blink::mojom::ResourceType::kSubResource */ 6;
+ resource_request->destination = network::mojom::RequestDestination::kEmpty;
resource_request->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES;
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), kTrafficAnnotation);
@@ -164,7 +167,7 @@ void TemplateURLFetcher::RequestDelegate::OnTemplateURLParsed(
}
void TemplateURLFetcher::RequestDelegate::OnLoaded() {
- template_url_subscription_.reset();
+ template_url_subscription_ = {};
if (!template_url_)
return;
AddSearchProvider();
@@ -195,16 +198,12 @@ void TemplateURLFetcher::RequestDelegate::AddSearchProvider() {
DCHECK(model);
DCHECK(model->loaded());
- const TemplateURL* existing_url = nullptr;
- if (!model->CanAddAutogeneratedKeyword(keyword_, GURL(template_url_->url()),
- &existing_url)) {
+ if (!model->CanAddAutogeneratedKeyword(keyword_,
+ GURL(template_url_->url()))) {
fetcher_->RequestCompleted(this); // WARNING: Deletes us!
return;
}
- if (existing_url)
- model->Remove(existing_url);
-
// The short name is what is shown to the user. We preserve original names
// since it is better when generated keyword in many cases.
TemplateURLData data(template_url_->data());
@@ -216,6 +215,7 @@ void TemplateURLFetcher::RequestDelegate::AddSearchProvider() {
data.favicon_url = favicon_url_;
// Mark the keyword as replaceable so it can be removed if necessary.
+ // Add() will automatically remove conflicting keyword replaceable engines.
data.safe_for_autoreplace = true;
model->Add(std::make_unique<TemplateURL>(data));
@@ -238,7 +238,6 @@ void TemplateURLFetcher::ScheduleDownload(
const url::Origin& initiator,
network::mojom::URLLoaderFactory* url_loader_factory,
int render_frame_id,
- int resource_type,
int32_t request_id) {
DCHECK(osdd_url.is_valid());
DCHECK(!keyword.empty());
@@ -264,7 +263,7 @@ void TemplateURLFetcher::ScheduleDownload(
requests_.push_back(std::make_unique<RequestDelegate>(
this, keyword, osdd_url, favicon_url, initiator, url_loader_factory,
- render_frame_id, resource_type, request_id));
+ render_frame_id, request_id));
}
void TemplateURLFetcher::RequestCompleted(RequestDelegate* request) {
diff --git a/chromium/components/search_engines/template_url_fetcher.h b/chromium/components/search_engines/template_url_fetcher.h
index 448bd68b5f7..870da17d169 100644
--- a/chromium/components/search_engines/template_url_fetcher.h
+++ b/chromium/components/search_engines/template_url_fetcher.h
@@ -52,7 +52,6 @@ class TemplateURLFetcher : public KeyedService {
const url::Origin& initiator,
network::mojom::URLLoaderFactory* url_loader_factory,
int render_frame_id,
- int resource_type,
int32_t request_id);
// The current number of outstanding requests.
diff --git a/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc b/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
index c1b926ac99d..d10abd4cbef 100644
--- a/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
+++ b/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
@@ -10,8 +10,8 @@
#include <utility>
#include "base/command_line.h"
+#include "base/containers/contains.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/country_codes/country_codes.h"
diff --git a/chromium/components/search_engines/template_url_service.cc b/chromium/components/search_engines/template_url_service.cc
index 120df608bdc..0e458474ac3 100644
--- a/chromium/components/search_engines/template_url_service.cc
+++ b/chromium/components/search_engines/template_url_service.cc
@@ -11,11 +11,12 @@
#include "base/base64url.h"
#include "base/bind.h"
#include "base/callback.h"
+#include "base/containers/contains.h"
#include "base/debug/crash_logging.h"
#include "base/format_macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
-#include "base/stl_util.h"
+#include "base/ranges/algorithm.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -58,18 +59,6 @@ enum DeleteSyncedSearchEngineEvent {
DELETE_ENGINE_MAX,
};
-const char kSearchTemplateURLEventsHistogramName[] =
- "Search.TemplateURL.Events";
-
-// Values for an enumerated histogram used to track TemplateURL edge cases.
-// These are persisted. Do not re-number.
-enum SearchTemplateURLEvent {
- SYNC_DELETE_SUCCESS = 0,
- SYNC_DELETE_FAIL_NONEXISTENT_ENGINE = 1,
- SYNC_DELETE_FAIL_DEFAULT_SEARCH_PROVIDER = 2,
- SEARCH_TEMPLATE_URL_EVENT_MAX,
-};
-
// Returns true iff the change in |change_list| at index |i| should not be sent
// up to the server based on its GUIDs presence in |sync_data| or when compared
// to changes after it in |change_list|.
@@ -289,7 +278,7 @@ TemplateURLService::TemplateURLService(
std::unique_ptr<SearchTermsData> search_terms_data,
const scoped_refptr<KeywordWebDataService>& web_data_service,
std::unique_ptr<TemplateURLServiceClient> client,
- const base::Closure& dsp_change_callback)
+ const base::RepeatingClosure& dsp_change_callback)
: prefs_(prefs),
search_terms_data_(std::move(search_terms_data)),
web_data_service_(web_data_service),
@@ -318,6 +307,13 @@ TemplateURLService::~TemplateURLService() {
}
// static
+void TemplateURLService::LogSearchTemplateURLEvent(
+ SearchTemplateURLEvent event) {
+ UMA_HISTOGRAM_ENUMERATION("Search.TemplateURL.Events", event,
+ SEARCH_TEMPLATE_URL_EVENT_MAX);
+}
+
+// static
void TemplateURLService::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
#if defined(OS_IOS) || defined(OS_ANDROID)
@@ -345,18 +341,18 @@ base::android::ScopedJavaLocalRef<jobject> TemplateURLService::GetJavaObject() {
bool TemplateURLService::CanAddAutogeneratedKeyword(
const base::string16& keyword,
- const GURL& url,
- const TemplateURL** template_url_to_replace) {
+ const GURL& url) {
DCHECK(!keyword.empty()); // This should only be called for non-empty
// keywords. If we need to support empty kewords
// the code needs to change slightly.
const TemplateURL* existing_url = GetTemplateURLForKeyword(keyword);
- if (template_url_to_replace)
- *template_url_to_replace = existing_url;
if (existing_url) {
- // We already have a TemplateURL for this keyword. Only allow it to be
- // replaced if the TemplateURL can be replaced.
- return CanReplace(existing_url);
+ // TODO(tommycli): Currently, this code goes one step beyond
+ // safe_for_autoreplace() and also forbids automatically modifying
+ // prepopulated engines. That's debatable, as we already update prepopulated
+ // provider favicons as the user browses. See UpdateProviderFavicons().
+ return existing_url->safe_for_autoreplace() &&
+ existing_url->prepopulate_id() == 0;
}
// We don't have a TemplateURL with keyword. We still may not allow this
@@ -481,7 +477,38 @@ TemplateURL* TemplateURLService::AddWithOverrides(
}
void TemplateURLService::Remove(const TemplateURL* template_url) {
- DCHECK_NE(template_url, default_search_provider_);
+ // CHECK that we aren't trying to Remove() the default search provider.
+ // This has happened before, and causes permanent damage to the user Profile,
+ // which can then be Synced to other installations. It's better to crash
+ // immediately, and that's why this isn't a DCHECK. https://crbug.com/1164024
+ {
+ const TemplateURL* default_provider = GetDefaultSearchProvider();
+
+ // TODO(tommycli): Once we are sure this never happens in practice, we can
+ // remove this CrashKeyString, but we should keep the CHECK.
+ static base::debug::CrashKeyString* crash_key =
+ base::debug::AllocateCrashKeyString("removed_turl_keyword",
+ base::debug::CrashKeySize::Size256);
+ base::debug::ScopedCrashKeyString auto_clear(
+ crash_key, base::UTF16ToUTF8(template_url->keyword()));
+
+ CHECK_NE(template_url, default_provider);
+
+ // Before we are loaded, we want to CHECK that we aren't accidentally
+ // removing the in-table representation of the Default Search Engine.
+ //
+ // But users in the wild do indeed have engines with duplicated sync GUIDs.
+ // For instance, Extensions Override Settings API used to have a bug that
+ // would clone GUIDs. So therefore skip the check after loading.
+ // https://crbug.com/1166372#c13
+ if (!loaded() && default_provider &&
+ default_provider->type() !=
+ TemplateURL::Type::NORMAL_CONTROLLED_BY_EXTENSION &&
+ template_url->type() !=
+ TemplateURL::Type::NORMAL_CONTROLLED_BY_EXTENSION) {
+ CHECK_NE(template_url->sync_guid(), default_provider->sync_guid());
+ }
+ }
auto i = FindTemplateURL(&template_urls_, template_url);
if (i == template_urls_.end())
@@ -539,25 +566,25 @@ void TemplateURLService::RemoveAutoGeneratedSince(base::Time created_after) {
void TemplateURLService::RemoveAutoGeneratedBetween(base::Time created_after,
base::Time created_before) {
- RemoveAutoGeneratedForUrlsBetween(base::Callback<bool(const GURL&)>(),
- created_after, created_before);
+ RemoveAutoGeneratedForUrlsBetween(base::NullCallback(), created_after,
+ created_before);
}
void TemplateURLService::RemoveAutoGeneratedForUrlsBetween(
- const base::Callback<bool(const GURL&)>& url_filter,
+ const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time created_after,
base::Time created_before) {
Scoper scoper(this);
for (size_t i = 0; i < template_urls_.size();) {
- if (template_urls_[i]->date_created() >= created_after &&
- (created_before.is_null() ||
- template_urls_[i]->date_created() < created_before) &&
- CanReplace(template_urls_[i].get()) &&
+ TemplateURL* turl = template_urls_[i].get();
+ if (turl->date_created() >= created_after &&
+ (created_before.is_null() || turl->date_created() < created_before) &&
+ turl->safe_for_autoreplace() && turl->prepopulate_id() == 0 &&
+ !MatchesDefaultSearchProvider(turl) &&
(url_filter.is_null() ||
- url_filter.Run(
- template_urls_[i]->GenerateSearchURL(search_terms_data())))) {
- Remove(template_urls_[i].get());
+ url_filter.Run(turl->GenerateSearchURL(search_terms_data())))) {
+ Remove(turl);
} else {
++i;
}
@@ -625,30 +652,36 @@ void TemplateURLService::ResetTemplateURL(TemplateURL* url,
Update(url, TemplateURL(data));
}
-TemplateURL* TemplateURLService::CreateOrUpdateTemplateURLFromPlayAPIData(
+TemplateURL* TemplateURLService::CreatePlayAPISearchEngine(
const base::string16& title,
const base::string16& keyword,
const std::string& search_url,
const std::string& suggestions_url,
const std::string& favicon_url) {
- TemplateURL* existing_turl = FindNonExtensionTemplateURLForKeyword(keyword);
+ // It's the caller's responsibility to check that there are no existing
+ // Play API for engine, but still CHECK this to avoid polluting the database.
+ // Currently, we never update Play API engine data. If we ever want to do
+ // that, we need to change how this method behaves.
+ const auto match_range = keyword_to_turl_and_length_.equal_range(keyword);
+ for (auto it = match_range.first; it != match_range.second; ++it) {
+ CHECK(!it->second.first->created_from_play_api());
+ }
+
TemplateURLData data;
- if (existing_turl)
- data = existing_turl->data();
data.SetShortName(title);
data.SetKeyword(keyword);
data.SetURL(search_url);
data.suggestions_url = suggestions_url;
data.favicon_url = GURL(favicon_url);
- data.safe_for_autoreplace = true;
data.created_from_play_api = true;
- if (existing_turl) {
- Update(existing_turl, TemplateURL(data));
- } else {
- existing_turl = Add(std::make_unique<TemplateURL>(data));
- DCHECK(existing_turl);
- }
- return existing_turl;
+ // Play API engines are created by explicit user gesture, and should not be
+ // auto-replaceable by an auto-generated engine as the user browses.
+ data.safe_for_autoreplace = false;
+
+ // The Play API search engine is not guaranteed to be the best engine for
+ // |keyword|, if there are user-defined, extension, or policy engines.
+ // In practice on Android, this rarely happens, as there is only policy.
+ return Add(std::make_unique<TemplateURL>(data));
}
void TemplateURLService::UpdateProviderFavicons(
@@ -840,11 +873,10 @@ void TemplateURLService::Load() {
ChangeToLoadedState();
}
-std::unique_ptr<TemplateURLService::Subscription>
-TemplateURLService::RegisterOnLoadedCallback(
- const base::RepeatingClosure& callback) {
- return loaded_ ? std::unique_ptr<TemplateURLService::Subscription>()
- : on_loaded_callbacks_.Add(callback);
+base::CallbackListSubscription TemplateURLService::RegisterOnLoadedCallback(
+ base::OnceClosure callback) {
+ return loaded_ ? base::CallbackListSubscription()
+ : on_loaded_callbacks_.Add(std::move(callback));
}
void TemplateURLService::OnWebDataServiceRequestDone(
@@ -1007,17 +1039,10 @@ base::Optional<syncer::ModelError> TemplateURLService::ProcessSyncChanges(
const std::string error_msg =
"ProcessSyncChanges failed on ChangeType " +
syncer::SyncChange::ChangeTypeToString(iter->change_type());
- if (iter->change_type() == syncer::SyncChange::ACTION_INVALID) {
- error = sync_error_factory_->CreateAndUploadError(FROM_HERE, error_msg);
- continue;
- }
-
if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
if (!existing_turl) {
// Can't DELETE a non-existent engine, although we log it.
- UMA_HISTOGRAM_ENUMERATION(kSearchTemplateURLEventsHistogramName,
- SYNC_DELETE_FAIL_NONEXISTENT_ENGINE,
- SEARCH_TEMPLATE_URL_EVENT_MAX);
+ LogSearchTemplateURLEvent(SYNC_DELETE_FAIL_NONEXISTENT_ENGINE);
error = sync_error_factory_->CreateAndUploadError(FROM_HERE, error_msg);
continue;
}
@@ -1035,59 +1060,61 @@ base::Optional<syncer::ModelError> TemplateURLService::ProcessSyncChanges(
// likely a source of duplicate search engine entries. crbug.com/1022775
if (existing_turl != GetDefaultSearchProvider()) {
Remove(existing_turl);
- UMA_HISTOGRAM_ENUMERATION(kSearchTemplateURLEventsHistogramName,
- SYNC_DELETE_SUCCESS,
- SEARCH_TEMPLATE_URL_EVENT_MAX);
+ LogSearchTemplateURLEvent(SYNC_DELETE_SUCCESS);
} else {
- UMA_HISTOGRAM_ENUMERATION(kSearchTemplateURLEventsHistogramName,
- SYNC_DELETE_FAIL_DEFAULT_SEARCH_PROVIDER,
- SEARCH_TEMPLATE_URL_EVENT_MAX);
+ LogSearchTemplateURLEvent(SYNC_DELETE_FAIL_DEFAULT_SEARCH_PROVIDER);
}
continue;
}
- if ((iter->change_type() == syncer::SyncChange::ACTION_ADD &&
- existing_turl) ||
- (iter->change_type() == syncer::SyncChange::ACTION_UPDATE &&
- !existing_turl)) {
- // Can't ADD an already-existing engine, and can't UPDATE a non-existent
- // engine. Early exit here to avoid ResolvingSyncKeywordConflict().
- error = sync_error_factory_->CreateAndUploadError(FROM_HERE, error_msg);
- continue;
- }
+ // Because TemplateURLService sometimes ignores remote Sync changes which
+ // we cannot cleanly apply, we need to handle ADD and UPDATE together.
+ // Ignore what the other Sync layers THINK the change type is. Instead:
+ // If we have an existing engine, treat as an update.
+ DCHECK(iter->change_type() == syncer::SyncChange::ACTION_ADD ||
+ iter->change_type() == syncer::SyncChange::ACTION_UPDATE);
- // Explicitly don't check for conflicts against extension keywords; in this
- // case the functions which modify the keyword map know how to handle the
- // conflicts.
- // TODO(mpcomplete): If we allow editing extension keywords, then those will
- // need to undergo conflict resolution.
- TemplateURL* existing_keyword_turl =
- FindNonExtensionTemplateURLForKeyword(turl->keyword());
- const bool has_conflict =
- existing_keyword_turl && (existing_keyword_turl != existing_turl);
- if (has_conflict) {
- // Resolve any conflicts with other entries so we can safely update the
- // keyword.
- ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl,
- &new_changes);
- }
+ if (!existing_turl) {
+ if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE) {
+ // This can happen if we have silently deleted a replaceable engine due
+ // to keyword conflict, and Sync server sends us an UPDATE to it.
+ LogSearchTemplateURLEvent(SYNC_UPDATE_CONVERTED_TO_ADD);
+ }
- if (iter->change_type() == syncer::SyncChange::ACTION_ADD) {
base::AutoReset<DefaultSearchChangeOrigin> change_origin(
&dsp_change_origin_, DSP_CHANGE_SYNC_ADD);
// Force the local ID to kInvalidTemplateURLID so we can add it.
TemplateURLData data(turl->data());
data.id = kInvalidTemplateURLID;
- auto added_ptr = std::make_unique<TemplateURL>(data);
- TemplateURL* added = added_ptr.get();
- if (Add(std::move(added_ptr)))
+
+ TemplateURL* added = Add(std::make_unique<TemplateURL>(data));
+ if (added) {
MaybeUpdateDSEViaPrefs(added);
- continue;
- }
- DCHECK_EQ(syncer::SyncChange::ACTION_UPDATE, iter->change_type());
- if (Update(existing_turl, *turl))
+ LogSearchTemplateURLEvent(SYNC_ADD_SUCCESS);
+ } else {
+ // Currently, in practice, this means that we tried to add a replaceable
+ // duplicate that was worse than our existing entry, but the API doesn't
+ // promise that, so we just log a generic SYNC_ADD_FAIL_OTHER_ERROR.
+ LogSearchTemplateURLEvent(SYNC_ADD_FAIL_OTHER_ERROR);
+ }
+ } else {
+ if (iter->change_type() == syncer::SyncChange::ACTION_ADD) {
+ // This can happen if we have ignored a DELETE request in the past to
+ // avoid deleting the default search provider, and later on, Sync tries
+ // to re-ADD something it thinks it has deleted.
+ LogSearchTemplateURLEvent(SYNC_ADD_CONVERTED_TO_UPDATE);
+ }
+
+ // Since we've already found |existing_turl| by GUID, this Update() should
+ // always return true, but we still don't want to crash if it fails.
+ DCHECK(existing_turl);
+ bool update_success = Update(existing_turl, *turl);
+ DCHECK(update_success);
+
MaybeUpdateDSEViaPrefs(existing_turl);
+ LogSearchTemplateURLEvent(SYNC_UPDATE_SUCCESS);
+ }
}
// If something went wrong, we want to prematurely exit to avoid pushing
@@ -1231,7 +1258,6 @@ void TemplateURLService::ProcessTemplateURLChange(
const base::Location& from_here,
const TemplateURL* turl,
syncer::SyncChange::SyncChangeType type) {
- DCHECK_NE(type, syncer::SyncChange::ACTION_INVALID);
DCHECK(turl);
if (!models_associated_)
@@ -1430,7 +1456,7 @@ void TemplateURLService::Init(const Initializer* initializers,
pref_change_registrar_.Init(prefs_);
pref_change_registrar_.Add(
prefs::kSyncedDefaultSearchProviderGUID,
- base::Bind(
+ base::BindRepeating(
&TemplateURLService::OnSyncedDefaultSearchProviderGUIDChanged,
base::Unretained(this)));
}
@@ -1568,25 +1594,6 @@ bool TemplateURLService::CanAddAutogeneratedKeywordForHost(
});
}
-bool TemplateURLService::CanReplace(const TemplateURL* t_url) const {
- return !ShowInDefaultList(t_url) && t_url->safe_for_autoreplace();
-}
-
-TemplateURL* TemplateURLService::FindNonExtensionTemplateURLForKeyword(
- const base::string16& keyword) {
- TemplateURL* keyword_turl = GetTemplateURLForKeyword(keyword);
- if (!keyword_turl || (keyword_turl->type() == TemplateURL::NORMAL))
- return keyword_turl;
- // The extension keyword in the model may be hiding a replaceable
- // non-extension keyword. Look for it.
- for (const auto& turl : template_urls_) {
- if ((turl->type() == TemplateURL::NORMAL) &&
- (turl->keyword() == keyword))
- return turl.get();
- }
- return nullptr;
-}
-
bool TemplateURLService::Update(TemplateURL* existing_turl,
const TemplateURL& new_values) {
DCHECK(existing_turl);
@@ -1600,32 +1607,11 @@ bool TemplateURLService::Update(TemplateURL* existing_turl,
TemplateURLID previous_id = existing_turl->id();
RemoveFromMaps(existing_turl);
- // Check for new keyword conflicts with another normal engine.
- // This is possible when autogeneration of the keyword for a Google default
- // search provider at load time causes it to conflict with an existing
- // keyword. If the conflicting engines are replaceable, we delete them.
- // If they're not replaceable, we leave them alone, and trust AddToMaps() to
- // choose the best engine to assign the keyword.
- std::vector<TemplateURL*> turls_to_remove;
- for (const auto& turl : template_urls_) {
- // TODO(tommycli): Investigate also replacing TemplateURL::LOCAL engines.
- if (turl.get() != existing_turl && (turl->type() == TemplateURL::NORMAL) &&
- (turl->keyword() == new_values.keyword()) && CanReplace(turl.get())) {
- // Remove() invalidates iterators.
- turls_to_remove.push_back(turl.get());
- }
- }
- for (TemplateURL* turl : turls_to_remove) {
- Remove(turl);
- }
-
- // Update existing turl with new values. This must happen after calling
- // Remove(conflicting_keyword_turl) above, since otherwise during that
- // function RemoveFromMaps() may find |existing_turl| as an alternate engine
- // for the same keyword. Duplicate keyword handling is only meant for the
- // case of extensions, and if done here would leave internal state
- // inconsistent (e.g. |existing_turl| would already be re-added to maps before
- // calling AddToMaps() below).
+ // Update existing turl with new values and add back to the map.
+ // We don't do any keyword conflict handling here, as TemplateURLService
+ // already can pick the best engine out of duplicates. Replaceable duplicates
+ // will be culled during next startup's Add() loop. We did this to keep
+ // Update() simple: it never fails, and never deletes |existing_engine|.
existing_turl->CopyFrom(new_values);
existing_turl->data_.id = previous_id;
@@ -1834,6 +1820,8 @@ bool TemplateURLService::ApplyDefaultSearchChangeNoMetrics(
// (2) If the user deleted the pre-populated default and we subsequently
// lost their user-selected value.
default_search_provider_ = Add(std::make_unique<TemplateURL>(*data));
+ DCHECK(default_search_provider_)
+ << "Add() to repair the DSE must never fail.";
}
} else if (source == DefaultSearchManager::FROM_USER) {
default_search_provider_ = GetTemplateURLForGUID(data->sync_guid);
@@ -1847,6 +1835,8 @@ bool TemplateURLService::ApplyDefaultSearchChangeNoMetrics(
} else {
new_data.id = kInvalidTemplateURLID;
default_search_provider_ = Add(std::make_unique<TemplateURL>(new_data));
+ DCHECK(default_search_provider_)
+ << "Add() to repair the DSE must never fail.";
}
if (default_search_provider_ && prefs_) {
prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID,
@@ -1876,35 +1866,12 @@ TemplateURL* TemplateURLService::Add(std::unique_ptr<TemplateURL> template_url,
template_url->ResetKeywordIfNecessary(search_terms_data(), false);
- // If |template_url| is not created by an extension, its keyword must not
- // conflict with any already in the model.
- if (!IsCreatedByExtension(template_url.get())) {
- TemplateURL* existing_turl =
- FindNonExtensionTemplateURLForKeyword(template_url->keyword());
-
- // Note that we can reach here during the loading phase while processing the
- // template URLs from the web data service. In this case,
- // GetTemplateURLForKeyword() will look not only at what's already in the
- // model, but at the |initial_default_search_provider_|. Since this engine
- // will presumably also be present in the web data, we need to double-check
- // that any "pre-existing" entries we find are actually coming from
- // |template_urls_|, lest we detect a "conflict" between the
- // |initial_default_search_provider_| and the web data version of itself.
- if (existing_turl && Contains(&template_urls_, existing_turl)) {
- DCHECK_NE(existing_turl, template_url.get());
- if (CanReplace(existing_turl)) {
- Remove(existing_turl);
- } else if (CanReplace(template_url.get())) {
- return nullptr;
- } else {
- // Neither engine can be replaced. Uniquify the existing keyword.
- base::string16 new_keyword = UniquifyKeyword(*existing_turl, false);
- ResetTemplateURL(existing_turl, existing_turl->short_name(),
- new_keyword, existing_turl->url());
- DCHECK_EQ(new_keyword, existing_turl->keyword());
- }
- }
+ // Early exit if the newly added TemplateURL was a replaceable duplicate.
+ // No need to inform either Sync or flag on the model-mutated in that case.
+ if (RemoveDuplicateReplaceableEnginesOf(template_url.get())) {
+ return nullptr;
}
+
TemplateURL* template_url_ptr = template_url.get();
template_urls_.push_back(std::move(template_url));
AddToMaps(template_url_ptr);
@@ -1973,6 +1940,7 @@ void TemplateURLService::UpdateProvidersCreatedByPolicy(
if (new_data.sync_guid.empty())
new_data.GenerateSyncGUID();
new_data.created_by_policy = true;
+ new_data.safe_for_autoreplace = false;
std::unique_ptr<TemplateURL> new_dse_ptr =
std::make_unique<TemplateURL>(new_data);
TemplateURL* new_dse = new_dse_ptr.get();
@@ -1991,85 +1959,6 @@ void TemplateURLService::ResetTemplateURLGUID(TemplateURL* url,
Update(url, TemplateURL(data));
}
-base::string16 TemplateURLService::UniquifyKeyword(const TemplateURL& turl,
- bool force) {
- DCHECK(!IsCreatedByExtension(&turl));
- if (!force) {
- // Already unique.
- if (!GetTemplateURLForKeyword(turl.keyword()))
- return turl.keyword();
-
- // First, try to return the generated keyword for the TemplateURL.
- GURL gurl(turl.url());
- if (gurl.is_valid()) {
- base::string16 keyword_candidate = TemplateURL::GenerateKeyword(gurl);
- if (!GetTemplateURLForKeyword(keyword_candidate))
- return keyword_candidate;
- }
- }
-
- // We try to uniquify the keyword by appending a special character to the end.
- // This is a best-effort approach where we try to preserve the original
- // keyword and let the user do what they will after our attempt.
- base::string16 keyword_candidate(turl.keyword());
- do {
- keyword_candidate.append(base::ASCIIToUTF16("_"));
- } while (GetTemplateURLForKeyword(keyword_candidate));
-
- return keyword_candidate;
-}
-
-bool TemplateURLService::IsLocalTemplateURLBetter(
- const TemplateURL* local_turl,
- const TemplateURL* sync_turl,
- bool prefer_local_default) const {
- DCHECK(GetTemplateURLForGUID(local_turl->sync_guid()));
- return local_turl->last_modified() > sync_turl->last_modified() ||
- local_turl->created_by_policy() ||
- (prefer_local_default && local_turl == GetDefaultSearchProvider());
-}
-
-void TemplateURLService::ResolveSyncKeywordConflict(
- TemplateURL* unapplied_sync_turl,
- TemplateURL* applied_sync_turl,
- syncer::SyncChangeList* change_list) {
- DCHECK(loaded_);
- DCHECK(unapplied_sync_turl);
- DCHECK(applied_sync_turl);
- DCHECK(change_list);
- DCHECK_EQ(applied_sync_turl->keyword(), unapplied_sync_turl->keyword());
- DCHECK_EQ(TemplateURL::NORMAL, applied_sync_turl->type());
-
- Scoper scoper(this);
-
- // Both |unapplied_sync_turl| and |applied_sync_turl| are known to Sync, so
- // don't delete either of them. Instead, determine which is "better" and
- // uniquify the other one, sending an update to the server for the updated
- // entry.
- const bool applied_turl_is_better =
- IsLocalTemplateURLBetter(applied_sync_turl, unapplied_sync_turl);
- TemplateURL* loser = applied_turl_is_better ?
- unapplied_sync_turl : applied_sync_turl;
- base::string16 new_keyword = UniquifyKeyword(*loser, false);
- DCHECK(!GetTemplateURLForKeyword(new_keyword));
- if (applied_turl_is_better) {
- // Just set the keyword of |unapplied_sync_turl|. The caller is responsible
- // for adding or updating unapplied_sync_turl in the local model.
- unapplied_sync_turl->data_.SetKeyword(new_keyword);
- } else {
- // Update |applied_sync_turl| in the local model with the new keyword.
- TemplateURLData data(applied_sync_turl->data());
- data.SetKeyword(new_keyword);
- Update(applied_sync_turl, TemplateURL(data));
- }
- // The losing TemplateURL should have their keyword updated. Send a change to
- // the server to reflect this change.
- syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*loser);
- change_list->push_back(syncer::SyncChange(FROM_HERE,
- syncer::SyncChange::ACTION_UPDATE,
- sync_data));
-}
-
void TemplateURLService::MergeInSyncTemplateURL(
TemplateURL* sync_turl,
const SyncDataMap& sync_data,
@@ -2079,49 +1968,63 @@ void TemplateURLService::MergeInSyncTemplateURL(
DCHECK(!GetTemplateURLForGUID(sync_turl->sync_guid()));
DCHECK(IsFromSync(sync_turl, sync_data));
- TemplateURL* conflicting_turl =
- FindNonExtensionTemplateURLForKeyword(sync_turl->keyword());
bool should_add_sync_turl = true;
Scoper scoper(this);
- // Resolve conflicts with local TemplateURLs.
- if (conflicting_turl) {
- // Modify |conflicting_turl| to make room for |sync_turl|.
+ // First resolve conflicts with local duplicate keyword NORMAL TemplateURLs,
+ // working from best to worst.
+ DCHECK(sync_turl->type() == TemplateURL::NORMAL);
+ std::vector<TemplateURL*> local_duplicates;
+ const auto match_range =
+ keyword_to_turl_and_length_.equal_range(sync_turl->keyword());
+ for (auto it = match_range.first; it != match_range.second; ++it) {
+ TemplateURL* local_turl = it->second.first;
+ if (local_turl->type() == TemplateURL::NORMAL) {
+ local_duplicates.push_back(local_turl);
+ }
+ }
+ base::ranges::sort(local_duplicates, [&](const auto& a, const auto& b) {
+ return a->IsBetterThanEngineWithConflictingKeyword(b);
+ });
+ for (TemplateURL* conflicting_turl : local_duplicates) {
if (IsFromSync(conflicting_turl, sync_data)) {
// |conflicting_turl| is already known to Sync, so we're not allowed to
- // remove it. In this case, we want to uniquify the worse one and send an
- // update for the changed keyword to sync. We can reuse the logic from
- // ResolveSyncKeywordConflict for this.
- ResolveSyncKeywordConflict(sync_turl, conflicting_turl, change_list);
+ // remove it. Just leave it. TemplateURLService can tolerate duplicates.
+ // TODO(tommycli): Eventually we should figure out a way to merge
+ // substantively identical ones or somehow otherwise cull the herd.
+ continue;
+ }
+
+ // |conflicting_turl| is not yet known to Sync. If it is better, then we
+ // want to transfer its values up to sync. Otherwise, we remove it and
+ // allow the entry from Sync to overtake it in the model.
+ const std::string guid = conflicting_turl->sync_guid();
+ if (conflicting_turl == GetDefaultSearchProvider() ||
+ conflicting_turl->IsBetterThanEngineWithConflictingKeyword(sync_turl)) {
+ ResetTemplateURLGUID(conflicting_turl, sync_turl->sync_guid());
+ syncer::SyncData sync_data =
+ CreateSyncDataFromTemplateURL(*conflicting_turl);
+ change_list->push_back(syncer::SyncChange(
+ FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
+ // Note that in this case we do not add the Sync TemplateURL to the
+ // local model, since we've effectively "merged" it in by updating the
+ // local conflicting entry with its sync_guid.
+ should_add_sync_turl = false;
} else {
- // |conflicting_turl| is not yet known to Sync. If it is better, then we
- // want to transfer its values up to sync. Otherwise, we remove it and
- // allow the entry from Sync to overtake it in the model.
- const std::string guid = conflicting_turl->sync_guid();
- if (IsLocalTemplateURLBetter(conflicting_turl, sync_turl)) {
- ResetTemplateURLGUID(conflicting_turl, sync_turl->sync_guid());
- syncer::SyncData sync_data =
- CreateSyncDataFromTemplateURL(*conflicting_turl);
- change_list->push_back(syncer::SyncChange(
- FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
- // Note that in this case we do not add the Sync TemplateURL to the
- // local model, since we've effectively "merged" it in by updating the
- // local conflicting entry with its sync_guid.
- should_add_sync_turl = false;
- } else {
- // We guarantee that this isn't the local search provider. Otherwise,
- // local would have won.
- DCHECK(conflicting_turl != GetDefaultSearchProvider());
- Remove(conflicting_turl);
- }
- // This TemplateURL was either removed or overwritten in the local model.
- // Remove the entry from the local data so it isn't pushed up to Sync.
- local_data->erase(guid);
+ // We guarantee that this isn't the local search provider. Otherwise,
+ // local would have won.
+ DCHECK(conflicting_turl != GetDefaultSearchProvider());
+ Remove(conflicting_turl);
}
- // prepopulate_id 0 effectively means unspecified; i.e. that the turl isn't
- // a pre-populated one, so we want to ignore that case.
- } else if (sync_turl->prepopulate_id() != 0) {
+ // This TemplateURL was either removed or overwritten in the local model.
+ // Remove the entry from the local data so it isn't pushed up to Sync.
+ local_data->erase(guid);
+ }
+
+ // Try to take over a local prepopulated entry, assuming we haven't already
+ // run into a keyword conflict.
+ if (local_duplicates.empty() && sync_turl->prepopulate_id() != 0) {
// Check for a turl with a conflicting prepopulate_id. This detects the case
// where the user changes a prepopulated engine's keyword on one client,
// then begins syncing on another client. We want to reflect this keyword
@@ -2142,8 +2045,8 @@ void TemplateURLService::MergeInSyncTemplateURL(
// the relevant changes in, we give up and leave both intact.
if (conflicting_prepopulated_turl &&
!IsFromSync(conflicting_prepopulated_turl, sync_data) &&
- !IsLocalTemplateURLBetter(conflicting_prepopulated_turl, sync_turl,
- false)) {
+ sync_turl->IsBetterThanEngineWithConflictingKeyword(
+ conflicting_prepopulated_turl)) {
std::string guid = conflicting_prepopulated_turl->sync_guid();
if (conflicting_prepopulated_turl == default_search_provider_) {
bool pref_matched =
@@ -2274,3 +2177,88 @@ TemplateURL* TemplateURLService::FindMatchingDefaultExtensionTemplateURL(
}
return nullptr;
}
+
+bool TemplateURLService::RemoveDuplicateReplaceableEnginesOf(
+ TemplateURL* candidate) {
+ DCHECK(candidate);
+ const base::string16& keyword = candidate->keyword();
+
+ // If there's not at least one conflicting TemplateURL, there's nothing to do.
+ const auto match_range = keyword_to_turl_and_length_.equal_range(keyword);
+ if (match_range.first == match_range.second) {
+ return false;
+ }
+
+ // Gather the replaceable TemplateURLs to be removed. We don't do it in-place,
+ // because Remove() invalidates iterators.
+ std::vector<TemplateURL*> replaceable_turls;
+ for (auto it = match_range.first; it != match_range.second; ++it) {
+ TemplateURL* turl = it->second.first;
+ DCHECK_NE(turl, candidate) << "This algorithm runs BEFORE |candidate| is "
+ "added to the keyword map.";
+
+ // Prepopulated engines are marked as safe_for_autoreplace(). But because
+ // they are shown in the Default Search Engines Settings UI, users would
+ // find it confusing if they were ever automatically removed.
+ // https://crbug.com/1164024
+ if (turl->safe_for_autoreplace() && turl->prepopulate_id() == 0) {
+ replaceable_turls.push_back(turl);
+ }
+ }
+
+ // Find the BEST engine for |keyword| factoring in the new |candidate|.
+ TemplateURL* best = GetTemplateURLForKeyword(keyword);
+ if (!best || candidate->IsBetterThanEngineWithConflictingKeyword(best)) {
+ best = candidate;
+ }
+
+ // Remove all the replaceable TemplateURLs that are not the best.
+ for (TemplateURL* turl : replaceable_turls) {
+ DCHECK_NE(turl, candidate);
+
+ // Never actually remove the DSE during this phase. This handling defers
+ // deleting the DSE until it's no longer set as the DSE, analagous to how
+ // we handle ACTION_DELETE of the DSE in ProcessSyncChanges().
+ if (turl != best && !MatchesDefaultSearchProvider(turl)) {
+ Remove(turl);
+ }
+ }
+
+ // Caller needs to know if |candidate| would have been deleted.
+ // Also always successfully add prepopulated engines, for two reasons:
+ // 1. The DSE repair logic in ApplyDefaultSearchChangeNoMetrics() relies on
+ // Add()ing back the DSE always succeeding. https://crbug.com/1164024
+ // 2. If we don't do this, we have a weird order-dependence on the
+ // replaceability of prepopulated engines, given that we refuse to add
+ // prepopulated engines to the |replaceable_engines| vector.
+ //
+ // Given the necessary special casing of prepopulated engines, we may consider
+ // marking prepopulated engines as NOT safe_for_autoreplace(), but there's a
+ // few obstacles to this:
+ // 1. Prepopulated engines are not user-created, and therefore meet the
+ // definition of safe_for_autoreplace().
+ // 2. If we mark them as NOT safe_for_autoreplace(), we can no longer
+ // distinguish between prepopulated engines that user has edited, vs. not
+ // edited.
+ //
+ // One more caveat: In 2019, we made prepopulated engines have a
+ // deterministically generated Sync GUID in order to prevent duplicate
+ // prepopulated engines when two clients start syncing at the same time.
+ // When combined with the requirement that we can never fail to add a
+ // prepopulated engine, this could leads to two engines having the same GUID.
+ //
+ // TODO(tommycli): After M89, we need to investigate solving the contradiction
+ // above. Most probably: the solution is to stop Syncing prepopulated engines
+ // and make the GUIDs actually globally unique again.
+ return candidate != best && candidate->safe_for_autoreplace() &&
+ candidate->prepopulate_id() == 0;
+}
+
+bool TemplateURLService::MatchesDefaultSearchProvider(TemplateURL* turl) const {
+ DCHECK(turl);
+ const TemplateURL* default_provider = GetDefaultSearchProvider();
+ if (!default_provider)
+ return false;
+
+ return turl->sync_guid() == default_provider->sync_guid();
+}
diff --git a/chromium/components/search_engines/template_url_service.h b/chromium/components/search_engines/template_url_service.h
index 7f0210819e2..c4a56815b59 100644
--- a/chromium/components/search_engines/template_url_service.h
+++ b/chromium/components/search_engines/template_url_service.h
@@ -14,6 +14,7 @@
#include <utility>
#include <vector>
+#include "base/callback.h"
#include "base/callback_list.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
@@ -80,7 +81,6 @@ class TemplateURLService : public WebDataServiceConsumer,
using TemplateURLVector = TemplateURL::TemplateURLVector;
using OwnedTemplateURLVector = TemplateURL::OwnedTemplateURLVector;
using SyncDataMap = std::map<std::string, syncer::SyncData>;
- using Subscription = base::CallbackList<void(void)>::Subscription;
// We may want to treat the keyword in a TemplateURL as being a different
// length than it actually is. For example, for keywords that end in a
@@ -103,6 +103,21 @@ class TemplateURLService : public WebDataServiceConsumer,
bool is_keyword_transition;
};
+ // Values for an enumerated histogram used to track TemplateURL edge cases.
+ // These are persisted. Do not re-number.
+ enum SearchTemplateURLEvent {
+ SYNC_DELETE_SUCCESS = 0,
+ SYNC_DELETE_FAIL_NONEXISTENT_ENGINE = 1,
+ SYNC_DELETE_FAIL_DEFAULT_SEARCH_PROVIDER = 2,
+ SYNC_ADD_SUCCESS = 3,
+ SYNC_ADD_CONVERTED_TO_UPDATE = 4,
+ SYNC_ADD_FAIL_OTHER_ERROR = 5,
+ SYNC_UPDATE_SUCCESS = 6,
+ SYNC_UPDATE_CONVERTED_TO_ADD = 7,
+ MIGRATE_SAFE_FOR_AUTOREPLACE_PLAY_API_ENGINE = 8,
+ SEARCH_TEMPLATE_URL_EVENT_MAX,
+ };
+
TemplateURLService(
PrefService* prefs,
std::unique_ptr<SearchTermsData> search_terms_data,
@@ -113,6 +128,9 @@ class TemplateURLService : public WebDataServiceConsumer,
TemplateURLService(const Initializer* initializers, const int count);
~TemplateURLService() override;
+ // Log a SearchTemplateURLEvent.
+ static void LogSearchTemplateURLEvent(SearchTemplateURLEvent event);
+
// Register Profile preferences in |registry|.
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
@@ -121,15 +139,12 @@ class TemplateURLService : public WebDataServiceConsumer,
#endif
// Returns true if there is no TemplateURL that conflicts with the
- // keyword/url pair, or there is one but it can be replaced. If there is an
- // existing keyword that can be replaced and template_url_to_replace is
- // non-NULL, template_url_to_replace is set to the keyword to replace.
+ // keyword/url pair, or there is one but it can be replaced.
//
// |url| is the URL of the search query. This is used to prevent auto-adding
// a keyword for hosts already associated with a manually-edited keyword.
bool CanAddAutogeneratedKeyword(const base::string16& keyword,
- const GURL& url,
- const TemplateURL** template_url_to_replace);
+ const GURL& url);
// Returns whether the engine is a "pre-existing" engine, either from the
// prepopulate list or created by policy.
@@ -211,7 +226,7 @@ class TemplateURLService : public WebDataServiceConsumer,
// range and match |url_filter|. If |url_filter| is_null(), deletes all
// auto-generated keywords in the range.
void RemoveAutoGeneratedForUrlsBetween(
- const base::Callback<bool(const GURL&)>& url_filter,
+ const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time created_after,
base::Time created_before);
@@ -239,15 +254,17 @@ class TemplateURLService : public WebDataServiceConsumer,
const base::string16& keyword,
const std::string& search_url);
- // Creates TemplateURL, populating it with data from Play API. If TemplateURL
- // with matching keyword already exists then merges Play API data into it.
- // Sets |created_from_play_api| flag.
- TemplateURL* CreateOrUpdateTemplateURLFromPlayAPIData(
- const base::string16& title,
- const base::string16& keyword,
- const std::string& search_url,
- const std::string& suggestions_url,
- const std::string& favicon_url);
+ // Creates a TemplateURL for |keyword| marked with created_from_play_api().
+ // Returns the newly created engine.
+ //
+ // This method must NOT be called multiple times for the same |keyword|,
+ // because that would create duplicate engines. Caller is responsible for
+ // verifying there are no existing |keyword| created_from_play_api() engines.
+ TemplateURL* CreatePlayAPISearchEngine(const base::string16& title,
+ const base::string16& keyword,
+ const std::string& search_url,
+ const std::string& suggestions_url,
+ const std::string& favicon_url);
// Updates any search providers matching |potential_search_url| with the new
// favicon location |favicon_url|.
@@ -319,8 +336,8 @@ class TemplateURLService : public WebDataServiceConsumer,
// Registers a callback to be called when the service has loaded.
//
// If the service has already loaded, this function does nothing.
- std::unique_ptr<Subscription> RegisterOnLoadedCallback(
- const base::RepeatingClosure& callback);
+ base::CallbackListSubscription RegisterOnLoadedCallback(
+ base::OnceClosure callback);
#if defined(UNIT_TEST)
void set_loaded(bool value) { loaded_ = value; }
@@ -440,15 +457,10 @@ class TemplateURLService : public WebDataServiceConsumer,
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, AddOmniboxExtensionKeyword);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, ExtensionsWithSameKeywords);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest,
- CheckEnginesWithSameKeywords);
+ KeywordConflictNonReplaceableEngines);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, LastVisitedTimeUpdate);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest,
RepairPrepopulatedSearchEngines);
- FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest, UniquifyKeyword);
- FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest,
- IsLocalTemplateURLBetter);
- FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest,
- ResolveSyncKeywordConflict);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest, PreSyncDeletes);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest, MergeInSyncTemplateURL);
FRIEND_TEST_ALL_PREFIXES(LocationBarModelTest, GoogleBaseURL);
@@ -464,12 +476,14 @@ class TemplateURLService : public WebDataServiceConsumer,
// A mapping from keywords to the corresponding TemplateURLs and their
// meaningful keyword lengths. This is a multimap, so the system can
// efficiently tolerate multiple engines with the same keyword, like from
- // extensions. The values are not sorted from best to worst for each keyword,
- // since multimaps don't sort on value. Users that want the best value for
- // each key must traverse through all matching items, but we expect there to
- // be below three values per key.
+ // extensions.
+ //
+ // The values for any given keyword are not sorted. Users that want the best
+ // value for each key must traverse through all matching items. The vast
+ // majority of keywords should only have one item.
using KeywordToTURLAndMeaningfulLength =
std::multimap<base::string16, TURLAndMeaningfulLength>;
+
// Declaration of values to be used in an enumerated histogram to tally
// changes to the default search provider from various entry points. In
// particular, we use this to see what proportion of changes are from Sync
@@ -543,16 +557,6 @@ class TemplateURLService : public WebDataServiceConsumer,
// specified host and that TemplateURL has been manually modified.
bool CanAddAutogeneratedKeywordForHost(const std::string& host) const;
- // Returns true if the TemplateURL is replaceable. This doesn't look at the
- // uniqueness of the keyword or host and is intended to be called after those
- // checks have been done. This returns true if the TemplateURL doesn't appear
- // in the default list and is marked as safe_for_autoreplace.
- bool CanReplace(const TemplateURL* t_url) const;
-
- // Like GetTemplateURLForKeyword(), but ignores extension-provided keywords.
- TemplateURL* FindNonExtensionTemplateURLForKeyword(
- const base::string16& keyword);
-
// Updates the information in |existing_turl| using the information from
// |new_values|, but the ID for |existing_turl| is retained. Returns whether
// |existing_turl| was found in |template_urls_| and thus could be updated.
@@ -612,44 +616,6 @@ class TemplateURLService : public WebDataServiceConsumer,
// to the database. This does not notify observers.
void ResetTemplateURLGUID(TemplateURL* url, const std::string& guid);
- // Attempts to generate a unique keyword for |turl| based on its original
- // keyword. If its keyword is already unique, that is returned. Otherwise, it
- // tries to return the autogenerated keyword if that is unique to the Service,
- // and finally it repeatedly appends special characters to the keyword until
- // it is unique to the Service. If |force| is true, then this will only
- // execute the special character appending functionality.
- base::string16 UniquifyKeyword(const TemplateURL& turl, bool force);
-
- // Returns true iff |local_turl| is considered "better" than |sync_turl| for
- // the purposes of resolving conflicts. |local_turl| must be a TemplateURL
- // known to the local model (though it may already be synced), and |sync_turl|
- // is a new TemplateURL known to Sync but not yet known to the local model.
- // The criteria for if |local_turl| is better than |sync_turl| is whether any
- // of the following are true:
- // * |local_turl|'s last_modified timestamp is newer than sync_turl.
- // * |local_turl| is created by policy.
- // * |prefer_local_default| is true and |local_turl| is the local default
- // search provider
- //
- // TODO(tommycli): Consolidate into using
- // TemplateURL::IsBetterThanEngineWithConflictingKeyword. Likely we will
- // eliminate the |prefer_local_default| mechanism.
- bool IsLocalTemplateURLBetter(const TemplateURL* local_turl,
- const TemplateURL* sync_turl,
- bool prefer_local_default = true) const;
-
- // Given two synced TemplateURLs with a conflicting keyword, one of which
- // needs to be added to or updated in the local model (|unapplied_sync_turl|)
- // and one which is already known to the local model (|applied_sync_turl|),
- // prepares the local model so that |unapplied_sync_turl| can be added to it,
- // or applied as an update to an existing TemplateURL.
- // Since both entries are known to Sync and one of their keywords will change,
- // an ACTION_UPDATE will be appended to |change_list| to reflect this change.
- // Note that |applied_sync_turl| must not be an extension keyword.
- void ResolveSyncKeywordConflict(TemplateURL* unapplied_sync_turl,
- TemplateURL* applied_sync_turl,
- syncer::SyncChangeList* change_list);
-
// Adds |sync_turl| into the local model, possibly removing or updating a
// local TemplateURL to make room for it. This expects |sync_turl| to be a new
// entry from Sync, not currently known to the local model. |sync_data| should
@@ -661,8 +627,6 @@ class TemplateURLService : public WebDataServiceConsumer,
// model during MergeDataAndStartSyncing. If |sync_turl| replaces a local
// entry, that entry is removed from |initial_data| to prevent it from being
// sent up to Sync.
- // |merge_result| tracks the changes made to the local model. Added/modified/
- // deleted are updated depending on how the |sync_turl| is merged in.
// This should only be called from MergeDataAndStartSyncing.
void MergeInSyncTemplateURL(TemplateURL* sync_turl,
const SyncDataMap& sync_data,
@@ -699,6 +663,26 @@ class TemplateURLService : public WebDataServiceConsumer,
TemplateURL* FindMatchingDefaultExtensionTemplateURL(
const TemplateURLData& data);
+ // This method removes all TemplateURLs that meet all three criteria:
+ // - Duplicate: Shares the same keyword as |candidate|.
+ // - Replaceable: Engine is eligible for automatic removal. See CanReplace().
+ // - Worse: There exists a better engine with the same keyword.
+ //
+ // This method must run BEFORE |candidate| is added to the engine list / map.
+ // It would be simpler to run the algorithm AFTER |candidate| is added, but
+ // that makes extra sync updates, observer notifications, and database churn.
+ //
+ // This method returns true if |candidate| ITSELF is rendundant.
+ // But notably, this method NEVER calls Remove() on |candidate|, leaving the
+ // correct handling to its caller.
+ bool RemoveDuplicateReplaceableEnginesOf(TemplateURL* candidate);
+
+ // Returns true if |turl| matches the default search provider. This method
+ // does both a GUID comparison, because while the model is being loaded, the
+ // DSE may be sourced from prefs, and we still want to consider the
+ // corresponding database entry a match. https://crbug.com/1164024
+ bool MatchesDefaultSearchProvider(TemplateURL* turl) const;
+
// ---------- Browser state related members ---------------------------------
PrefService* prefs_ = nullptr;
@@ -750,6 +734,11 @@ class TemplateURLService : public WebDataServiceConsumer,
// Once loaded, the default search provider. This is a pointer to a
// TemplateURL owned by |template_urls_|.
+ //
+ // TODO(tommycli): Can we combine this with initial_default_search_provider_?
+ // Essentially all direct usages of this variable need to first check that
+ // |loading_| is true, and should call GetDefaultSearchProvider() instead.
+ // Example of a regression due to this mistake: https://crbug.com/1164024.
TemplateURL* default_search_provider_ = nullptr;
// A temporary location for the DSE until Web Data has been loaded and it can
@@ -799,7 +788,7 @@ class TemplateURLService : public WebDataServiceConsumer,
DefaultSearchChangeOrigin dsp_change_origin_ = DSP_CHANGE_OTHER;
// Stores a list of callbacks to be run after TemplateURLService has loaded.
- base::CallbackList<void(void)> on_loaded_callbacks_;
+ base::OnceClosureList on_loaded_callbacks_;
// Similar to |on_loaded_callbacks_| but used for WaitUntilReadyToSync().
base::OnceClosure on_loaded_callback_for_sync_;
diff --git a/chromium/components/search_engines/template_url_unittest.cc b/chromium/components/search_engines/template_url_unittest.cc
index ef435ac629a..f9bc1ec8dff 100644
--- a/chromium/components/search_engines/template_url_unittest.cc
+++ b/chromium/components/search_engines/template_url_unittest.cc
@@ -766,6 +766,37 @@ TEST_F(TemplateURLTest, ReplaceOmniboxFocusType) {
}
}
+// Tests replacing prefetch source (&pf=).
+TEST_F(TemplateURLTest, ReplaceIsPrefetch) {
+ struct TestData {
+ const base::string16 search_term;
+ bool is_prefetch;
+ const std::string url;
+ const std::string expected_result;
+ } test_data[] = {
+ {ASCIIToUTF16("foo"), false,
+ "{google:baseURL}?{searchTerms}&{google:prefetchSource}",
+ "http://www.google.com/?foo&"},
+ {ASCIIToUTF16("foo"), true,
+ "{google:baseURL}?{searchTerms}&{google:prefetchSource}",
+ "http://www.google.com/?foo&pf=cs&"},
+ };
+ TemplateURLData data;
+ data.input_encodings.push_back("UTF-8");
+ for (size_t i = 0; i < base::size(test_data); ++i) {
+ data.SetURL(test_data[i].url);
+ TemplateURL url(data);
+ EXPECT_TRUE(url.url_ref().IsValid(search_terms_data_));
+ ASSERT_TRUE(url.url_ref().SupportsReplacement(search_terms_data_));
+ TemplateURLRef::SearchTermsArgs search_terms_args(test_data[i].search_term);
+ search_terms_args.is_prefetch = test_data[i].is_prefetch;
+ GURL result(url.url_ref().ReplaceSearchTerms(search_terms_args,
+ search_terms_data_));
+ ASSERT_TRUE(result.is_valid());
+ EXPECT_EQ(test_data[i].expected_result, result.spec());
+ }
+}
+
// Tests replacing currentPageUrl.
TEST_F(TemplateURLTest, ReplaceCurrentPageUrl) {
struct TestData {