variations_ids_provider.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/variations/variations_ids_provider.h"
  5. #include <algorithm>
  6. #include "base/base64.h"
  7. #include "base/containers/contains.h"
  8. #include "base/metrics/histogram_macros.h"
  9. #include "base/no_destructor.h"
  10. #include "base/observer_list.h"
  11. #include "base/strings/string_number_conversions.h"
  12. #include "base/strings/string_split.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/synchronization/lock.h"
  15. #include "components/variations/proto/client_variations.pb.h"
  16. #include "components/variations/variations_associated_data.h"
  17. #include "components/variations/variations_client.h"
  18. #include "components/variations/variations_features.h"
  19. namespace variations {
  20. namespace {
  21. // Range of low entropy source values (8000) as variation ids for the
  22. // X-Client-Data header. This range is reserved in cl/333331461 (internal CL).
  23. const int kLowEntropySourceVariationIdRangeMin = 3320978;
  24. const int kLowEntropySourceVariationIdRangeMax = 3328977;
  25. VariationsIdsProvider* g_instance = nullptr;
  26. base::Lock& GetInstanceLock() {
  27. static base::NoDestructor<base::Lock> lock;
  28. return *lock;
  29. }
  30. } // namespace
  31. bool VariationsHeaderKey::operator<(const VariationsHeaderKey& other) const {
  32. if (is_signed_in != other.is_signed_in)
  33. return is_signed_in < other.is_signed_in;
  34. return web_visibility < other.web_visibility;
  35. }
  36. // Adding/removing headers is implemented by request consumers, and how it is
  37. // implemented depends on the request type.
  38. // There are three cases:
  39. // 1. Subresources request in renderer, it is implemented by
  40. // WebURLLoaderImpl::Context::Start() by adding a VariationsURLLoaderThrottle
  41. // to a content::URLLoaderThrottle vector.
  42. // 2. Navigations/Downloads request in browser, it is implemented in
  43. // ChromeContentBrowserClient::CreateURLLoaderThrottles() which calls
  44. // CreateContentBrowserURLLoaderThrottles which also adds a
  45. // VariationsURLLoaderThrottle to a content::URLLoaderThrottle vector.
  46. // 3. SimpleURLLoader in browser, it is implemented in a SimpleURLLoader wrapper
  47. // function variations::CreateSimpleURLLoaderWithVariationsHeader().
  48. // static
  49. VariationsIdsProvider* VariationsIdsProvider::Create(Mode mode) {
  50. base::AutoLock lock(GetInstanceLock());
  51. DCHECK(!g_instance);
  52. g_instance = new VariationsIdsProvider(mode);
  53. return g_instance;
  54. }
  55. // static
  56. VariationsIdsProvider* VariationsIdsProvider::GetInstance() {
  57. base::AutoLock lock(GetInstanceLock());
  58. DCHECK(g_instance);
  59. return g_instance;
  60. }
  61. variations::mojom::VariationsHeadersPtr
  62. VariationsIdsProvider::GetClientDataHeaders(bool is_signed_in) {
  63. // Lazily initialize the header, if not already done, before attempting to
  64. // transmit it.
  65. InitVariationIDsCacheIfNeeded();
  66. if (mode_ == Mode::kIgnoreSignedInState)
  67. is_signed_in = true;
  68. else if (mode_ == Mode::kDontSendSignedInVariations)
  69. is_signed_in = false;
  70. std::string first_party_header_copy;
  71. std::string any_context_header_copy;
  72. {
  73. base::AutoLock lock(lock_);
  74. first_party_header_copy = GetClientDataHeaderWhileLocked(
  75. is_signed_in, Study_GoogleWebVisibility_FIRST_PARTY);
  76. any_context_header_copy = GetClientDataHeaderWhileLocked(
  77. is_signed_in, Study_GoogleWebVisibility_ANY);
  78. }
  79. if (first_party_header_copy.empty() && any_context_header_copy.empty())
  80. return nullptr;
  81. base::flat_map<variations::mojom::GoogleWebVisibility, std::string> headers =
  82. {{variations::mojom::GoogleWebVisibility::FIRST_PARTY,
  83. first_party_header_copy},
  84. {variations::mojom::GoogleWebVisibility::ANY, any_context_header_copy}};
  85. return variations::mojom::VariationsHeaders::New(headers);
  86. }
  87. std::string VariationsIdsProvider::GetVariationsString(
  88. const std::set<IDCollectionKey>& keys) {
  89. // Construct a space-separated string with leading and trailing spaces from
  90. // the VariationIDs set. The IDs in the string are in sorted order as per the
  91. // std::set contract.
  92. std::string ids_string = " ";
  93. for (const VariationID& id : GetVariationsVector(keys)) {
  94. ids_string.append(base::NumberToString(id));
  95. ids_string.push_back(' ');
  96. }
  97. return ids_string;
  98. }
  99. std::string VariationsIdsProvider::GetGoogleAppVariationsString() {
  100. return GetVariationsString({GOOGLE_APP});
  101. }
  102. std::string VariationsIdsProvider::GetVariationsString() {
  103. return GetVariationsString(
  104. {GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, GOOGLE_WEB_PROPERTIES_FIRST_PARTY});
  105. }
  106. std::vector<VariationID> VariationsIdsProvider::GetVariationsVector(
  107. const std::set<IDCollectionKey>& keys) {
  108. return GetVariationsVectorImpl(keys);
  109. }
  110. std::vector<VariationID>
  111. VariationsIdsProvider::GetVariationsVectorForWebPropertiesKeys() {
  112. const std::set<IDCollectionKey> web_properties_keys{
  113. GOOGLE_WEB_PROPERTIES_ANY_CONTEXT,
  114. GOOGLE_WEB_PROPERTIES_FIRST_PARTY,
  115. GOOGLE_WEB_PROPERTIES_SIGNED_IN,
  116. GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT,
  117. GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY,
  118. };
  119. return GetVariationsVectorImpl(web_properties_keys);
  120. }
  121. void VariationsIdsProvider::SetLowEntropySourceValue(
  122. absl::optional<int> low_entropy_source_value) {
  123. // The low entropy source value is an integer that is between 0 and 7999,
  124. // inclusive. See components/metrics/metrics_state_manager.cc for the logic to
  125. // generate it.
  126. if (low_entropy_source_value) {
  127. DCHECK_GE(low_entropy_source_value.value(), 0);
  128. DCHECK_LE(low_entropy_source_value.value(), 7999);
  129. }
  130. low_entropy_source_value_ = low_entropy_source_value;
  131. }
  132. VariationsIdsProvider::ForceIdsResult VariationsIdsProvider::ForceVariationIds(
  133. const std::vector<std::string>& variation_ids,
  134. const std::string& command_line_variation_ids) {
  135. force_enabled_ids_set_.clear();
  136. if (!AddVariationIdsToSet(variation_ids, /*should_dedupe=*/true,
  137. &force_enabled_ids_set_))
  138. return ForceIdsResult::INVALID_VECTOR_ENTRY;
  139. if (!ParseVariationIdsParameter(command_line_variation_ids,
  140. /*should_dedupe=*/true,
  141. &force_enabled_ids_set_)) {
  142. return ForceIdsResult::INVALID_SWITCH_ENTRY;
  143. }
  144. if (variation_ids_cache_initialized_) {
  145. // Update the cached variation ids header value after cache initialization,
  146. // otherwise the change won't be in the cache.
  147. base::AutoLock scoped_lock(lock_);
  148. UpdateVariationIDsHeaderValue();
  149. }
  150. return ForceIdsResult::SUCCESS;
  151. }
  152. bool VariationsIdsProvider::ForceDisableVariationIds(
  153. const std::string& command_line_variation_ids) {
  154. force_disabled_ids_set_.clear();
  155. // |should_dedupe| is false here in order to add the IDs specified in
  156. // |command_line_variation_ids| to |force_disabled_ids_set_| even if they were
  157. // defined before. The IDs are not marked as active; they are marked as
  158. // disabled.
  159. if (!ParseVariationIdsParameter(command_line_variation_ids,
  160. /*should_dedupe=*/false,
  161. &force_disabled_ids_set_)) {
  162. return false;
  163. }
  164. if (variation_ids_cache_initialized_) {
  165. // Update the cached variation ids header value after cache initialization,
  166. // otherwise the change won't be in the cache.
  167. base::AutoLock scoped_lock(lock_);
  168. UpdateVariationIDsHeaderValue();
  169. }
  170. return true;
  171. }
  172. void VariationsIdsProvider::AddObserver(Observer* observer) {
  173. observer_list_.AddObserver(observer);
  174. }
  175. void VariationsIdsProvider::RemoveObserver(Observer* observer) {
  176. observer_list_.RemoveObserver(observer);
  177. }
  178. void VariationsIdsProvider::ResetForTesting() {
  179. base::AutoLock scoped_lock(lock_);
  180. // Stop observing field trials so that it can be restarted when this is
  181. // re-inited. Note: This is a no-op if this is not currently observing.
  182. base::FieldTrialList::RemoveObserver(this);
  183. variation_ids_cache_initialized_ = false;
  184. variation_ids_set_.clear();
  185. force_enabled_ids_set_.clear();
  186. synthetic_variation_ids_set_.clear();
  187. force_disabled_ids_set_.clear();
  188. variations_headers_map_.clear();
  189. }
  190. VariationsIdsProvider::VariationsIdsProvider(Mode mode)
  191. : mode_(mode), variation_ids_cache_initialized_(false) {}
  192. VariationsIdsProvider::~VariationsIdsProvider() {
  193. base::FieldTrialList::RemoveObserver(this);
  194. }
  195. // static
  196. void VariationsIdsProvider::CreateInstanceForTesting(Mode mode) {
  197. base::AutoLock lock(GetInstanceLock());
  198. delete g_instance;
  199. g_instance = new VariationsIdsProvider(mode);
  200. }
  201. // static
  202. void VariationsIdsProvider::DestroyInstanceForTesting() {
  203. base::AutoLock lock(GetInstanceLock());
  204. delete g_instance;
  205. g_instance = nullptr;
  206. }
  207. void VariationsIdsProvider::OnFieldTrialGroupFinalized(
  208. const std::string& trial_name,
  209. const std::string& group_name) {
  210. base::AutoLock scoped_lock(lock_);
  211. const size_t old_size = variation_ids_set_.size();
  212. CacheVariationsId(trial_name, group_name);
  213. if (variation_ids_set_.size() != old_size)
  214. UpdateVariationIDsHeaderValue();
  215. }
  216. void VariationsIdsProvider::OnSyntheticTrialsChanged(
  217. const std::vector<SyntheticTrialGroup>& groups) {
  218. base::AutoLock scoped_lock(lock_);
  219. synthetic_variation_ids_set_.clear();
  220. for (const SyntheticTrialGroup& group : groups) {
  221. VariationID id = GetGoogleVariationIDFromHashes(
  222. GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, group.id());
  223. // TODO(crbug/1294948): Handle duplicated IDs in such a way that is visible
  224. // to developers, but non-intrusive to users. See
  225. // crrev/c/3628020/comments/e278cd12_2bb863ef for discussions.
  226. if (id != EMPTY_ID) {
  227. synthetic_variation_ids_set_.insert(
  228. VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_ANY_CONTEXT));
  229. }
  230. id = GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES_SIGNED_IN,
  231. group.id());
  232. if (id != EMPTY_ID) {
  233. synthetic_variation_ids_set_.insert(
  234. VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_SIGNED_IN));
  235. }
  236. // Google App IDs omitted because they should never be defined
  237. // synthetically.
  238. }
  239. UpdateVariationIDsHeaderValue();
  240. }
  241. void VariationsIdsProvider::InitVariationIDsCacheIfNeeded() {
  242. base::AutoLock scoped_lock(lock_);
  243. if (variation_ids_cache_initialized_)
  244. return;
  245. // Query the kRestrictGoogleWebVisibility feature to activate the
  246. // associated field trial, if any, so that querying it in
  247. // OnFieldTrialGroupFinalized() does not result in deadlock.
  248. //
  249. // Note: Must be done before the AddObserver() call below.
  250. base::FeatureList::IsEnabled(internal::kRestrictGoogleWebVisibility);
  251. // Register for additional cache updates. This is done before initializing the
  252. // cache to avoid a race that could cause registered FieldTrials to be missed.
  253. bool success = base::FieldTrialList::AddObserver(this);
  254. DCHECK(success);
  255. base::FieldTrial::ActiveGroups initial_groups;
  256. base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
  257. for (const auto& entry : initial_groups) {
  258. CacheVariationsId(entry.trial_name, entry.group_name);
  259. }
  260. UpdateVariationIDsHeaderValue();
  261. variation_ids_cache_initialized_ = true;
  262. }
  263. void VariationsIdsProvider::CacheVariationsId(const std::string& trial_name,
  264. const std::string& group_name) {
  265. for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
  266. IDCollectionKey key = static_cast<IDCollectionKey>(i);
  267. const VariationID id = GetGoogleVariationID(key, trial_name, group_name);
  268. // TODO(crbug/1294948): Handle duplicated IDs in such a way that is visible
  269. // to developers, but non-intrusive to users. See
  270. // crrev/c/3628020/comments/e278cd12_2bb863ef for discussions.
  271. if (id != EMPTY_ID)
  272. variation_ids_set_.insert(VariationIDEntry(id, key));
  273. }
  274. }
  275. void VariationsIdsProvider::UpdateVariationIDsHeaderValue() {
  276. lock_.AssertAcquired();
  277. variations_headers_map_.clear();
  278. // Note that the list of IDs and the header could be temporarily out of sync
  279. // if IDs are added as the header is recreated. The receiving servers are OK
  280. // with such discrepancies.
  281. variations_headers_map_[VariationsHeaderKey{/*is_signed_in=*/false,
  282. Study_GoogleWebVisibility_ANY}] =
  283. GenerateBase64EncodedProto(/*is_signed_in=*/false,
  284. /*is_first_party_context=*/false);
  285. variations_headers_map_[VariationsHeaderKey{
  286. /*is_signed_in=*/false, Study_GoogleWebVisibility_FIRST_PARTY}] =
  287. GenerateBase64EncodedProto(/*is_signed_in=*/false,
  288. /*is_first_party_context=*/true);
  289. variations_headers_map_[VariationsHeaderKey{/*is_signed_in=*/true,
  290. Study_GoogleWebVisibility_ANY}] =
  291. GenerateBase64EncodedProto(/*is_signed_in=*/true,
  292. /*is_first_party_context=*/false);
  293. variations_headers_map_[VariationsHeaderKey{
  294. /*is_signed_in=*/true, Study_GoogleWebVisibility_FIRST_PARTY}] =
  295. GenerateBase64EncodedProto(/*is_signed_in=*/true,
  296. /*is_first_party_context=*/true);
  297. for (auto& observer : observer_list_) {
  298. observer.VariationIdsHeaderUpdated();
  299. }
  300. }
  301. std::string VariationsIdsProvider::GenerateBase64EncodedProto(
  302. bool is_signed_in,
  303. bool is_first_party_context) {
  304. std::set<VariationIDEntry> all_variation_ids_set = GetAllVariationIds();
  305. ClientVariations proto;
  306. for (const VariationIDEntry& entry : all_variation_ids_set) {
  307. switch (entry.second) {
  308. case GOOGLE_WEB_PROPERTIES_SIGNED_IN:
  309. if (is_signed_in)
  310. proto.add_variation_id(entry.first);
  311. break;
  312. case GOOGLE_WEB_PROPERTIES_ANY_CONTEXT:
  313. proto.add_variation_id(entry.first);
  314. break;
  315. case GOOGLE_WEB_PROPERTIES_FIRST_PARTY:
  316. if (base::FeatureList::IsEnabled(
  317. internal::kRestrictGoogleWebVisibility)) {
  318. if (is_first_party_context)
  319. proto.add_variation_id(entry.first);
  320. } else {
  321. // When the feature is not enabled, treat VariationIDs associated with
  322. // GOOGLE_WEB_PROPERTIES_FIRST_PARTY in the same way as those
  323. // associated with GOOGLE_WEB_PROPERTIES_ANY_CONTEXT.
  324. proto.add_variation_id(entry.first);
  325. }
  326. break;
  327. case GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT:
  328. proto.add_trigger_variation_id(entry.first);
  329. break;
  330. case GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY:
  331. if (base::FeatureList::IsEnabled(
  332. internal::kRestrictGoogleWebVisibility)) {
  333. if (is_first_party_context)
  334. proto.add_trigger_variation_id(entry.first);
  335. } else {
  336. // When the feature is not enabled, treat VariationIDs associated with
  337. // GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY in the same way as those
  338. // associated with GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT.
  339. proto.add_trigger_variation_id(entry.first);
  340. }
  341. break;
  342. case GOOGLE_APP:
  343. // These IDs should not be added into Google Web headers.
  344. break;
  345. case ID_COLLECTION_COUNT:
  346. // This case included to get full enum coverage for switch, so that
  347. // new enums introduce compiler warnings. Nothing to do for this.
  348. break;
  349. }
  350. }
  351. const size_t total_id_count =
  352. proto.variation_id_size() + proto.trigger_variation_id_size();
  353. if (total_id_count == 0)
  354. return std::string();
  355. // This is the bottleneck for the creation of the header, so validate the size
  356. // here. Force a hard maximum on the ID count in case the Variations server
  357. // returns too many IDs and DOSs receiving servers with large requests.
  358. DCHECK_LE(total_id_count, 50U);
  359. UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
  360. total_id_count);
  361. if (total_id_count > 75)
  362. return std::string();
  363. std::string serialized;
  364. proto.SerializeToString(&serialized);
  365. std::string hashed;
  366. base::Base64Encode(serialized, &hashed);
  367. return hashed;
  368. }
  369. bool VariationsIdsProvider::AddVariationIdsToSet(
  370. const std::vector<std::string>& variation_ids,
  371. bool should_dedupe,
  372. std::set<VariationIDEntry>* target_set) {
  373. for (const std::string& entry : variation_ids) {
  374. if (entry.empty()) {
  375. target_set->clear();
  376. return false;
  377. }
  378. bool trigger_id =
  379. base::StartsWith(entry, "t", base::CompareCase::SENSITIVE);
  380. // Remove the "t" prefix if it's there.
  381. std::string trimmed_entry = trigger_id ? entry.substr(1) : entry;
  382. int variation_id = 0;
  383. if (!base::StringToInt(trimmed_entry, &variation_id)) {
  384. target_set->clear();
  385. return false;
  386. }
  387. if (should_dedupe && IsDuplicateId(variation_id)) {
  388. DVLOG(1) << "Invalid variation ID specified: " << entry
  389. << " (it is already in use)";
  390. target_set->clear();
  391. return false;
  392. }
  393. target_set->insert(VariationIDEntry(
  394. variation_id, trigger_id ? GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT
  395. : GOOGLE_WEB_PROPERTIES_ANY_CONTEXT));
  396. }
  397. return true;
  398. }
  399. bool VariationsIdsProvider::ParseVariationIdsParameter(
  400. const std::string& command_line_variation_ids,
  401. bool should_dedupe,
  402. std::set<VariationIDEntry>* target_set) {
  403. if (command_line_variation_ids.empty())
  404. return true;
  405. std::vector<std::string> variation_ids_from_command_line =
  406. base::SplitString(command_line_variation_ids, ",", base::TRIM_WHITESPACE,
  407. base::SPLIT_WANT_ALL);
  408. return AddVariationIdsToSet(variation_ids_from_command_line, should_dedupe,
  409. target_set);
  410. }
  411. std::string VariationsIdsProvider::GetClientDataHeaderWhileLocked(
  412. bool is_signed_in,
  413. Study_GoogleWebVisibility web_visibility) {
  414. lock_.AssertAcquired();
  415. auto it = variations_headers_map_.find(
  416. VariationsHeaderKey{is_signed_in, web_visibility});
  417. if (it == variations_headers_map_.end())
  418. return "";
  419. // Deliberately return a copy.
  420. return it->second;
  421. }
  422. std::set<VariationsIdsProvider::VariationIDEntry>
  423. VariationsIdsProvider::GetAllVariationIds() {
  424. lock_.AssertAcquired();
  425. std::set<VariationIDEntry> all_variation_ids_set = force_enabled_ids_set_;
  426. for (const VariationIDEntry& entry : variation_ids_set_) {
  427. all_variation_ids_set.insert(entry);
  428. }
  429. for (const VariationIDEntry& entry : synthetic_variation_ids_set_) {
  430. all_variation_ids_set.insert(entry);
  431. }
  432. for (const VariationIDEntry& entry : force_disabled_ids_set_) {
  433. all_variation_ids_set.erase(entry);
  434. }
  435. // Add the low entropy source value, if it exists, which has one of
  436. // 8000 possible values (between kLowEntropySourceVariationIdRange[Min/Max],
  437. // ~13 bits). This is the value that has been used for deriving the variation
  438. // ids included in the X-Client-Data header and therefore does not reveal
  439. // additional information about the client when there are more than 13
  440. // variations. A typical Chrome client has more than 13 variation ids
  441. // reported.
  442. //
  443. // The entropy source value is used for retrospective A/A tests to validate
  444. // that there's no existing bias between two randomized groups of clients for
  445. // a later A/B study.
  446. if (low_entropy_source_value_) {
  447. int source_value = low_entropy_source_value_.value() +
  448. kLowEntropySourceVariationIdRangeMin;
  449. DCHECK_GE(source_value, kLowEntropySourceVariationIdRangeMin);
  450. DCHECK_LE(source_value, kLowEntropySourceVariationIdRangeMax);
  451. all_variation_ids_set.insert(
  452. VariationIDEntry(source_value, GOOGLE_WEB_PROPERTIES_FIRST_PARTY));
  453. }
  454. return all_variation_ids_set;
  455. }
  456. std::vector<VariationID> VariationsIdsProvider::GetVariationsVectorImpl(
  457. const std::set<IDCollectionKey>& keys) {
  458. InitVariationIDsCacheIfNeeded();
  459. // Get all the active variation ids while holding the lock.
  460. std::set<VariationIDEntry> all_variation_ids;
  461. {
  462. base::AutoLock scoped_lock(lock_);
  463. all_variation_ids = GetAllVariationIds();
  464. }
  465. // Copy the requested variations to the output vector.
  466. std::vector<VariationID> result;
  467. result.reserve(all_variation_ids.size());
  468. for (const VariationIDEntry& entry : all_variation_ids) {
  469. if (keys.find(entry.second) != keys.end())
  470. result.push_back(entry.first);
  471. }
  472. // Make sure each entry is unique. As a side effect, the output is sorted.
  473. std::sort(result.begin(), result.end());
  474. result.erase(std::unique(result.begin(), result.end()), result.end());
  475. return result;
  476. }
  477. bool VariationsIdsProvider::IsDuplicateId(VariationID id) {
  478. for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
  479. IDCollectionKey key = static_cast<IDCollectionKey>(i);
  480. // GOOGLE_APP ids may be duplicated. Further validation is done in
  481. // GroupMapAccessor::ValidateID().
  482. if (key == GOOGLE_APP)
  483. continue;
  484. VariationIDEntry entry(id, key);
  485. if (base::Contains(variation_ids_set_, entry) ||
  486. base::Contains(force_enabled_ids_set_, entry) ||
  487. base::Contains(synthetic_variation_ids_set_, entry)) {
  488. return true;
  489. }
  490. }
  491. return false;
  492. }
  493. } // namespace variations