[Sync] Componentize UIModelWorker.
[chromium-blink-merge.git] / components / rlz / rlz_tracker.cc
blob5531ce1e7eef912fb2a55a484b21a75ea14909e2
1 // Copyright (c) 2012 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 //
5 // This code glues the RLZ library DLL with Chrome. It allows Chrome to work
6 // with or without the DLL being present. If the DLL is not present the
7 // functions do nothing and just return false.
9 #include "components/rlz/rlz_tracker.h"
11 #include <algorithm>
13 #include "base/bind.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/trace_event/trace_event.h"
18 #include "components/rlz/rlz_tracker_delegate.h"
19 #include "net/http/http_util.h"
21 namespace rlz {
22 namespace {
24 // Maximum and minimum delay for financial ping we would allow to be set through
25 // master preferences. Somewhat arbitrary, may need to be adjusted in future.
26 const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200);
27 const base::TimeDelta kMinInitDelay = base::TimeDelta::FromSeconds(20);
29 void RecordProductEvents(bool first_run,
30 bool is_google_default_search,
31 bool is_google_homepage,
32 bool is_google_in_startpages,
33 bool already_ran,
34 bool omnibox_used,
35 bool homepage_used,
36 bool app_list_used) {
37 TRACE_EVENT0("RLZ", "RecordProductEvents");
38 // Record the installation of chrome. We call this all the time but the rlz
39 // lib should ignore all but the first one.
40 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
41 RLZTracker::ChromeOmnibox(),
42 rlz_lib::INSTALL);
43 #if !defined(OS_IOS)
44 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
45 RLZTracker::ChromeHomePage(),
46 rlz_lib::INSTALL);
47 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
48 RLZTracker::ChromeAppList(),
49 rlz_lib::INSTALL);
50 #endif // !defined(OS_IOS)
52 if (!already_ran) {
53 // Do the initial event recording if is the first run or if we have an
54 // empty rlz which means we haven't got a chance to do it.
55 char omnibox_rlz[rlz_lib::kMaxRlzLength + 1];
56 if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), omnibox_rlz,
57 rlz_lib::kMaxRlzLength)) {
58 omnibox_rlz[0] = 0;
61 // Record if google is the initial search provider and/or home page.
62 if ((first_run || omnibox_rlz[0] == 0) && is_google_default_search) {
63 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
64 RLZTracker::ChromeOmnibox(),
65 rlz_lib::SET_TO_GOOGLE);
68 #if !defined(OS_IOS)
69 char homepage_rlz[rlz_lib::kMaxRlzLength + 1];
70 if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeHomePage(), homepage_rlz,
71 rlz_lib::kMaxRlzLength)) {
72 homepage_rlz[0] = 0;
75 if ((first_run || homepage_rlz[0] == 0) &&
76 (is_google_homepage || is_google_in_startpages)) {
77 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
78 RLZTracker::ChromeHomePage(),
79 rlz_lib::SET_TO_GOOGLE);
82 char app_list_rlz[rlz_lib::kMaxRlzLength + 1];
83 if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeAppList(), app_list_rlz,
84 rlz_lib::kMaxRlzLength)) {
85 app_list_rlz[0] = 0;
88 // Record if google is the initial search provider and/or home page.
89 if ((first_run || app_list_rlz[0] == 0) && is_google_default_search) {
90 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
91 RLZTracker::ChromeAppList(),
92 rlz_lib::SET_TO_GOOGLE);
94 #endif // !defined(OS_IOS)
97 // Record first user interaction with the omnibox. We call this all the
98 // time but the rlz lib should ingore all but the first one.
99 if (omnibox_used) {
100 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
101 RLZTracker::ChromeOmnibox(),
102 rlz_lib::FIRST_SEARCH);
105 #if !defined(OS_IOS)
106 // Record first user interaction with the home page. We call this all the
107 // time but the rlz lib should ingore all but the first one.
108 if (homepage_used || is_google_in_startpages) {
109 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
110 RLZTracker::ChromeHomePage(),
111 rlz_lib::FIRST_SEARCH);
114 // Record first user interaction with the app list. We call this all the
115 // time but the rlz lib should ingore all but the first one.
116 if (app_list_used) {
117 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
118 RLZTracker::ChromeAppList(),
119 rlz_lib::FIRST_SEARCH);
121 #endif // !defined(OS_IOS)
124 bool SendFinancialPing(const std::string& brand,
125 const base::string16& lang,
126 const base::string16& referral) {
127 rlz_lib::AccessPoint points[] = {RLZTracker::ChromeOmnibox(),
128 #if !defined(OS_IOS)
129 RLZTracker::ChromeHomePage(),
130 RLZTracker::ChromeAppList(),
131 #endif
132 rlz_lib::NO_ACCESS_POINT};
133 std::string lang_ascii(base::UTF16ToASCII(lang));
134 std::string referral_ascii(base::UTF16ToASCII(referral));
135 std::string product_signature;
136 #if defined(OS_CHROMEOS)
137 product_signature = "chromeos";
138 #else
139 product_signature = "chrome";
140 #endif
141 return rlz_lib::SendFinancialPing(rlz_lib::CHROME, points,
142 product_signature.c_str(),
143 brand.c_str(), referral_ascii.c_str(),
144 lang_ascii.c_str(), false, true);
147 } // namespace
149 RLZTracker* RLZTracker::tracker_ = nullptr;
151 // static
152 RLZTracker* RLZTracker::GetInstance() {
153 return tracker_ ? tracker_ : base::Singleton<RLZTracker>::get();
156 RLZTracker::RLZTracker()
157 : first_run_(false),
158 send_ping_immediately_(false),
159 is_google_default_search_(false),
160 is_google_homepage_(false),
161 is_google_in_startpages_(false),
162 already_ran_(false),
163 omnibox_used_(false),
164 homepage_used_(false),
165 app_list_used_(false),
166 min_init_delay_(kMinInitDelay) {
169 RLZTracker::~RLZTracker() {
172 // static
173 void RLZTracker::SetRlzDelegate(scoped_ptr<RLZTrackerDelegate> delegate) {
174 RLZTracker* tracker = GetInstance();
175 if (!tracker->delegate_) {
176 // RLZTracker::SetRlzDelegate is called at Profile creation time which can
177 // happens multiple time on ChromeOS, so do nothing if the delegate already
178 // exists.
179 tracker->SetDelegate(delegate.Pass());
183 void RLZTracker::SetDelegate(scoped_ptr<RLZTrackerDelegate> delegate) {
184 DCHECK(delegate);
185 DCHECK(!delegate_);
186 delegate_ = delegate.Pass();
187 worker_pool_token_ = delegate_->GetBlockingPool()->GetSequenceToken();
190 // static
191 bool RLZTracker::InitRlzDelayed(bool first_run,
192 bool send_ping_immediately,
193 base::TimeDelta delay,
194 bool is_google_default_search,
195 bool is_google_homepage,
196 bool is_google_in_startpages) {
197 return GetInstance()->Init(first_run, send_ping_immediately, delay,
198 is_google_default_search, is_google_homepage,
199 is_google_in_startpages);
202 bool RLZTracker::Init(bool first_run,
203 bool send_ping_immediately,
204 base::TimeDelta delay,
205 bool is_google_default_search,
206 bool is_google_homepage,
207 bool is_google_in_startpages) {
208 DCHECK(delegate_) << "RLZTracker used before initialization";
209 first_run_ = first_run;
210 is_google_default_search_ = is_google_default_search;
211 is_google_homepage_ = is_google_homepage;
212 is_google_in_startpages_ = is_google_in_startpages;
213 send_ping_immediately_ = send_ping_immediately;
215 // Enable zero delays for testing.
216 if (delegate_->ShouldEnableZeroDelayForTesting())
217 EnableZeroDelayForTesting();
219 delay = std::min(kMaxInitDelay, std::max(min_init_delay_, delay));
221 if (delegate_->GetBrand(&brand_) && !delegate_->IsBrandOrganic(brand_)) {
222 // Register for notifications from the omnibox so that we can record when
223 // the user performs a first search.
224 delegate_->SetOmniboxSearchCallback(
225 base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this),
226 ChromeOmnibox()));
228 #if !defined(OS_IOS)
229 // Register for notifications from navigations, to see if the user has used
230 // the home page.
231 delegate_->SetHomepageSearchCallback(
232 base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this),
233 ChromeHomePage()));
234 #endif
236 delegate_->GetReactivationBrand(&reactivation_brand_);
238 // Could be null; don't run if so. RLZ will try again next restart.
239 net::URLRequestContextGetter* context_getter = delegate_->GetRequestContext();
240 if (context_getter) {
241 rlz_lib::SetURLRequestContext(context_getter);
242 ScheduleDelayedInit(delay);
245 #if !defined(OS_IOS)
246 // Prime the RLZ cache for the home page access point so that its avaiable
247 // for the startup page if needed (i.e., when the startup page is set to
248 // the home page).
249 GetAccessPointRlz(ChromeHomePage(), nullptr);
250 #endif // !defined(OS_IOS)
252 return true;
255 void RLZTracker::Cleanup() {
256 rlz_cache_.clear();
257 if (delegate_)
258 delegate_->Cleanup();
261 void RLZTracker::ScheduleDelayedInit(base::TimeDelta delay) {
262 DCHECK(delegate_) << "RLZTracker used before initialization";
263 // The RLZTracker is a singleton object that outlives any runnable tasks
264 // that will be queued up.
265 delegate_->GetBlockingPool()->PostDelayedSequencedWorkerTask(
266 worker_pool_token_, FROM_HERE,
267 base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)), delay);
270 void RLZTracker::DelayedInit() {
271 DCHECK(delegate_) << "RLZTracker used before initialization";
272 bool schedule_ping = false;
274 // For organic brandcodes do not use rlz at all. Empty brandcode usually
275 // means a chromium install. This is ok.
276 if (!delegate_->IsBrandOrganic(brand_)) {
277 RecordProductEvents(first_run_, is_google_default_search_,
278 is_google_homepage_, is_google_in_startpages_,
279 already_ran_, omnibox_used_, homepage_used_,
280 app_list_used_);
281 schedule_ping = true;
284 // If chrome has been reactivated, record the events for this brand
285 // as well.
286 if (!delegate_->IsBrandOrganic(reactivation_brand_)) {
287 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
288 RecordProductEvents(first_run_, is_google_default_search_,
289 is_google_homepage_, is_google_in_startpages_,
290 already_ran_, omnibox_used_, homepage_used_,
291 app_list_used_);
292 schedule_ping = true;
295 already_ran_ = true;
297 if (schedule_ping)
298 ScheduleFinancialPing();
301 void RLZTracker::ScheduleFinancialPing() {
302 DCHECK(delegate_) << "RLZTracker used before initialization";
303 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
304 worker_pool_token_, FROM_HERE,
305 base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this)),
306 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
309 void RLZTracker::PingNowImpl() {
310 DCHECK(delegate_) << "RLZTracker used before initialization";
311 TRACE_EVENT0("RLZ", "RLZTracker::PingNowImpl");
312 base::string16 lang;
313 delegate_->GetLanguage(&lang);
314 if (lang.empty())
315 lang = base::ASCIIToUTF16("en");
316 base::string16 referral;
317 delegate_->GetReferral(&referral);
319 if (!delegate_->IsBrandOrganic(brand_) &&
320 SendFinancialPing(brand_, lang, referral)) {
321 delegate_->ClearReferral();
324 base::AutoLock lock(cache_lock_);
325 rlz_cache_.clear();
328 // Prime the RLZ cache for the access points we are interested in.
329 GetAccessPointRlz(RLZTracker::ChromeOmnibox(), nullptr);
330 #if !defined(OS_IOS)
331 GetAccessPointRlz(RLZTracker::ChromeHomePage(), nullptr);
332 GetAccessPointRlz(RLZTracker::ChromeAppList(), nullptr);
333 #endif // !defined(OS_IOS)
336 if (!delegate_->IsBrandOrganic(reactivation_brand_)) {
337 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
338 SendFinancialPing(reactivation_brand_, lang, referral);
342 bool RLZTracker::SendFinancialPing(const std::string& brand,
343 const base::string16& lang,
344 const base::string16& referral) {
345 return ::rlz::SendFinancialPing(brand, lang, referral);
348 // static
349 bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
350 rlz_lib::AccessPoint point,
351 rlz_lib::Event event_id) {
352 // This method is called during unit tests while the RLZTracker has not been
353 // initialized, so check for the presence of a delegate and exit if there is
354 // none registered.
355 RLZTracker* tracker = GetInstance();
356 return !tracker->delegate_ ? false : tracker->RecordProductEventImpl(
357 product, point, event_id);
360 bool RLZTracker::RecordProductEventImpl(rlz_lib::Product product,
361 rlz_lib::AccessPoint point,
362 rlz_lib::Event event_id) {
363 DCHECK(delegate_) << "RLZTracker used before initialization";
364 // Make sure we don't access disk outside of the I/O thread.
365 // In such case we repost the task on the right thread and return error.
366 if (ScheduleRecordProductEvent(product, point, event_id))
367 return true;
369 bool ret = rlz_lib::RecordProductEvent(product, point, event_id);
371 // If chrome has been reactivated, record the event for this brand as well.
372 if (!reactivation_brand_.empty()) {
373 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
374 ret &= rlz_lib::RecordProductEvent(product, point, event_id);
377 return ret;
380 bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product,
381 rlz_lib::AccessPoint point,
382 rlz_lib::Event event_id) {
383 DCHECK(delegate_) << "RLZTracker used before initialization";
384 if (!delegate_->IsOnUIThread())
385 return false;
387 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
388 worker_pool_token_, FROM_HERE,
389 base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent), product,
390 point, event_id),
391 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
393 return true;
396 void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) {
397 DCHECK(delegate_) << "RLZTracker used before initialization";
398 // Make sure we don't access disk outside of the I/O thread.
399 // In such case we repost the task on the right thread and return error.
400 if (ScheduleRecordFirstSearch(point))
401 return;
403 bool* record_used = GetAccessPointRecord(point);
405 // Try to record event now, else set the flag to try later when we
406 // attempt the ping.
407 if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH))
408 *record_used = true;
409 else if (send_ping_immediately_ && point == ChromeOmnibox())
410 ScheduleDelayedInit(base::TimeDelta());
413 bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) {
414 DCHECK(delegate_) << "RLZTracker used before initialization";
415 if (!delegate_->IsOnUIThread())
416 return false;
417 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
418 worker_pool_token_, FROM_HERE,
419 base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this), point),
420 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
421 return true;
424 bool* RLZTracker::GetAccessPointRecord(rlz_lib::AccessPoint point) {
425 if (point == ChromeOmnibox())
426 return &omnibox_used_;
427 #if !defined(OS_IOS)
428 if (point == ChromeHomePage())
429 return &homepage_used_;
430 if (point == ChromeAppList())
431 return &app_list_used_;
432 #endif // !defined(OS_IOS)
433 NOTREACHED();
434 return nullptr;
437 // static
438 std::string RLZTracker::GetAccessPointHttpHeader(rlz_lib::AccessPoint point) {
439 TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointHttpHeader");
440 std::string extra_headers;
441 base::string16 rlz_string;
442 RLZTracker::GetAccessPointRlz(point, &rlz_string);
443 if (!rlz_string.empty()) {
444 net::HttpUtil::AppendHeaderIfMissing("X-Rlz-String",
445 base::UTF16ToUTF8(rlz_string),
446 &extra_headers);
449 return extra_headers;
452 // GetAccessPointRlz() caches RLZ strings for all access points. If we had
453 // a successful ping, then we update the cached value.
454 // static
455 bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point,
456 base::string16* rlz) {
457 // This method is called during unit tests while the RLZTracker has not been
458 // initialized, so check for the presence of a delegate and exit if there is
459 // none registered.
460 TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointRlz");
461 RLZTracker* tracker = GetInstance();
462 return !tracker->delegate_ ? false
463 : tracker->GetAccessPointRlzImpl(point, rlz);
466 // GetAccessPointRlz() caches RLZ strings for all access points. If we had
467 // a successful ping, then we update the cached value.
468 bool RLZTracker::GetAccessPointRlzImpl(rlz_lib::AccessPoint point,
469 base::string16* rlz) {
470 DCHECK(delegate_) << "RLZTracker used before initialization";
471 // If the RLZ string for the specified access point is already cached,
472 // simply return its value.
474 base::AutoLock lock(cache_lock_);
475 if (rlz_cache_.find(point) != rlz_cache_.end()) {
476 if (rlz)
477 *rlz = rlz_cache_[point];
478 return true;
482 // Make sure we don't access disk outside of the I/O thread.
483 // In such case we repost the task on the right thread and return error.
484 if (ScheduleGetAccessPointRlz(point))
485 return false;
487 char str_rlz[rlz_lib::kMaxRlzLength + 1];
488 if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength))
489 return false;
491 base::string16 rlz_local(base::ASCIIToUTF16(str_rlz));
492 if (rlz)
493 *rlz = rlz_local;
495 base::AutoLock lock(cache_lock_);
496 rlz_cache_[point] = rlz_local;
497 return true;
500 bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) {
501 DCHECK(delegate_) << "RLZTracker used before initialization";
502 if (!delegate_->IsOnUIThread())
503 return false;
505 base::string16* not_used = nullptr;
506 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
507 worker_pool_token_, FROM_HERE,
508 base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz), point,
509 not_used),
510 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
511 return true;
514 #if defined(OS_CHROMEOS)
515 // static
516 void RLZTracker::ClearRlzState() {
517 RLZTracker* tracker = GetInstance();
518 if (tracker->delegate_)
519 tracker->ClearRlzStateImpl();
522 void RLZTracker::ClearRlzStateImpl() {
523 DCHECK(delegate_) << "RLZTracker used before initialization";
524 if (ScheduleClearRlzState())
525 return;
526 rlz_lib::ClearAllProductEvents(rlz_lib::CHROME);
529 bool RLZTracker::ScheduleClearRlzState() {
530 DCHECK(delegate_) << "RLZTracker used before initialization";
531 if (!delegate_->IsOnUIThread())
532 return false;
534 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
535 worker_pool_token_, FROM_HERE,
536 base::Bind(&RLZTracker::ClearRlzStateImpl, base::Unretained(this)),
537 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
538 return true;
540 #endif
542 // static
543 void RLZTracker::CleanupRlz() {
544 GetInstance()->Cleanup();
545 rlz_lib::SetURLRequestContext(nullptr);
548 // static
549 void RLZTracker::EnableZeroDelayForTesting() {
550 GetInstance()->min_init_delay_ = base::TimeDelta();
553 #if !defined(OS_IOS)
554 // static
555 void RLZTracker::RecordAppListSearch() {
556 // This method is called during unit tests while the RLZTracker has not been
557 // initialized, so check for the presence of a delegate and exit if there is
558 // none registered.
559 RLZTracker* tracker = GetInstance();
560 if (tracker->delegate_)
561 tracker->RecordFirstSearch(RLZTracker::ChromeAppList());
563 #endif
565 } // namespace rlz