Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / components / variations / study_filtering.cc
blobf654b971b690b7fdde590264b9a7129f43e8da86
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.
5 #include "components/variations/study_filtering.h"
7 #include <set>
9 namespace variations {
11 namespace {
13 Study_Platform GetCurrentPlatform() {
14 #if defined(OS_WIN)
15 return Study_Platform_PLATFORM_WINDOWS;
16 #elif defined(OS_IOS)
17 return Study_Platform_PLATFORM_IOS;
18 #elif defined(OS_MACOSX)
19 return Study_Platform_PLATFORM_MAC;
20 #elif defined(OS_CHROMEOS)
21 return Study_Platform_PLATFORM_CHROMEOS;
22 #elif defined(OS_ANDROID)
23 return Study_Platform_PLATFORM_ANDROID;
24 #elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
25 // Default BSD and SOLARIS to Linux to not break those builds, although these
26 // platforms are not officially supported by Chrome.
27 return Study_Platform_PLATFORM_LINUX;
28 #else
29 #error Unknown platform
30 #endif
33 // Converts |date_time| in Study date format to base::Time.
34 base::Time ConvertStudyDateToBaseTime(int64 date_time) {
35 return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(date_time);
38 } // namespace
40 namespace internal {
42 bool CheckStudyChannel(const Study_Filter& filter, Study_Channel channel) {
43 // An empty channel list matches all channels.
44 if (filter.channel_size() == 0)
45 return true;
47 for (int i = 0; i < filter.channel_size(); ++i) {
48 if (filter.channel(i) == channel)
49 return true;
51 return false;
54 bool CheckStudyFormFactor(const Study_Filter& filter,
55 Study_FormFactor form_factor) {
56 // An empty form factor list matches all form factors.
57 if (filter.form_factor_size() == 0)
58 return true;
60 for (int i = 0; i < filter.form_factor_size(); ++i) {
61 if (filter.form_factor(i) == form_factor)
62 return true;
64 return false;
67 bool CheckStudyHardwareClass(const Study_Filter& filter,
68 const std::string& hardware_class) {
69 // Empty hardware_class and exclude_hardware_class matches all.
70 if (filter.hardware_class_size() == 0 &&
71 filter.exclude_hardware_class_size() == 0) {
72 return true;
75 // Checks if we are supposed to filter for a specified set of
76 // hardware_classes. Note that this means this overrides the
77 // exclude_hardware_class in case that ever occurs (which it shouldn't).
78 if (filter.hardware_class_size() > 0) {
79 for (int i = 0; i < filter.hardware_class_size(); ++i) {
80 // Check if the entry is a substring of |hardware_class|.
81 size_t position = hardware_class.find(filter.hardware_class(i));
82 if (position != std::string::npos)
83 return true;
85 // None of the requested hardware_classes match.
86 return false;
89 // Omit if matches any of the exclude entries.
90 for (int i = 0; i < filter.exclude_hardware_class_size(); ++i) {
91 // Check if the entry is a substring of |hardware_class|.
92 size_t position = hardware_class.find(
93 filter.exclude_hardware_class(i));
94 if (position != std::string::npos)
95 return false;
98 // None of the exclusions match, so this accepts.
99 return true;
102 bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale) {
103 // An empty locale list matches all locales.
104 if (filter.locale_size() == 0)
105 return true;
107 for (int i = 0; i < filter.locale_size(); ++i) {
108 if (filter.locale(i) == locale)
109 return true;
111 return false;
114 bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform) {
115 // An empty platform list matches all platforms.
116 if (filter.platform_size() == 0)
117 return true;
119 for (int i = 0; i < filter.platform_size(); ++i) {
120 if (filter.platform(i) == platform)
121 return true;
123 return false;
126 bool CheckStudyStartDate(const Study_Filter& filter,
127 const base::Time& date_time) {
128 if (filter.has_start_date()) {
129 const base::Time start_date =
130 ConvertStudyDateToBaseTime(filter.start_date());
131 return date_time >= start_date;
134 return true;
137 bool CheckStudyVersion(const Study_Filter& filter,
138 const base::Version& version) {
139 if (filter.has_min_version()) {
140 if (version.CompareToWildcardString(filter.min_version()) < 0)
141 return false;
144 if (filter.has_max_version()) {
145 if (version.CompareToWildcardString(filter.max_version()) > 0)
146 return false;
149 return true;
152 bool IsStudyExpired(const Study& study, const base::Time& date_time) {
153 if (study.has_expiry_date()) {
154 const base::Time expiry_date =
155 ConvertStudyDateToBaseTime(study.expiry_date());
156 return date_time >= expiry_date;
159 return false;
162 bool ShouldAddStudy(
163 const Study& study,
164 const std::string& locale,
165 const base::Time& reference_date,
166 const base::Version& version,
167 Study_Channel channel,
168 Study_FormFactor form_factor,
169 const std::string& hardware_class) {
170 if (study.has_filter()) {
171 if (!CheckStudyChannel(study.filter(), channel)) {
172 DVLOG(1) << "Filtered out study " << study.name() << " due to channel.";
173 return false;
176 if (!CheckStudyFormFactor(study.filter(), form_factor)) {
177 DVLOG(1) << "Filtered out study " << study.name() <<
178 " due to form factor.";
179 return false;
182 if (!CheckStudyLocale(study.filter(), locale)) {
183 DVLOG(1) << "Filtered out study " << study.name() << " due to locale.";
184 return false;
187 if (!CheckStudyPlatform(study.filter(), GetCurrentPlatform())) {
188 DVLOG(1) << "Filtered out study " << study.name() << " due to platform.";
189 return false;
192 if (!CheckStudyVersion(study.filter(), version)) {
193 DVLOG(1) << "Filtered out study " << study.name() << " due to version.";
194 return false;
197 if (!CheckStudyStartDate(study.filter(), reference_date)) {
198 DVLOG(1) << "Filtered out study " << study.name() <<
199 " due to start date.";
200 return false;
203 if (!CheckStudyHardwareClass(study.filter(), hardware_class)) {
204 DVLOG(1) << "Filtered out study " << study.name() <<
205 " due to hardware_class.";
206 return false;
210 DVLOG(1) << "Kept study " << study.name() << ".";
211 return true;
214 } // namespace internal
216 void FilterAndValidateStudies(
217 const VariationsSeed& seed,
218 const std::string& locale,
219 const base::Time& reference_date,
220 const base::Version& version,
221 Study_Channel channel,
222 Study_FormFactor form_factor,
223 const std::string& hardware_class,
224 std::vector<ProcessedStudy>* filtered_studies) {
225 DCHECK(version.IsValid());
227 // Add expired studies (in a disabled state) only after all the non-expired
228 // studies have been added (and do not add an expired study if a corresponding
229 // non-expired study got added). This way, if there's both an expired and a
230 // non-expired study that applies, the non-expired study takes priority.
231 std::set<std::string> created_studies;
232 std::vector<const Study*> expired_studies;
234 for (int i = 0; i < seed.study_size(); ++i) {
235 const Study& study = seed.study(i);
236 if (!internal::ShouldAddStudy(study, locale, reference_date, version,
237 channel, form_factor, hardware_class)) {
238 continue;
241 if (internal::IsStudyExpired(study, reference_date)) {
242 expired_studies.push_back(&study);
243 } else if (!ContainsKey(created_studies, study.name())) {
244 ProcessedStudy::ValidateAndAppendStudy(&study, false, filtered_studies);
245 created_studies.insert(study.name());
249 for (size_t i = 0; i < expired_studies.size(); ++i) {
250 if (!ContainsKey(created_studies, expired_studies[i]->name())) {
251 ProcessedStudy::ValidateAndAppendStudy(expired_studies[i], true,
252 filtered_studies);
257 } // namespace variations