Update mojo surfaces bindings and mojo/cc/ glue
[chromium-blink-merge.git] / chrome / browser / rlz / rlz.cc
blobd6fac063a95ba3b3858edc987281776b0bfec0f3
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 "chrome/browser/rlz/rlz.h"
11 #include <algorithm>
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/debug/trace_event.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/google/google_brand.h"
23 #include "chrome/browser/omnibox/omnibox_log.h"
24 #include "chrome/browser/prefs/session_startup_pref.h"
25 #include "chrome/browser/search_engines/template_url_service_factory.h"
26 #include "chrome/browser/ui/startup/startup_browser_creator.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/google/core/browser/google_util.h"
30 #include "components/search_engines/template_url.h"
31 #include "components/search_engines/template_url_service.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/navigation_entry.h"
34 #include "content/public/browser/notification_service.h"
35 #include "net/http/http_util.h"
37 #if defined(OS_WIN)
38 #include "chrome/installer/util/google_update_settings.h"
39 #else
40 namespace GoogleUpdateSettings {
41 static bool GetLanguage(base::string16* language) {
42 // TODO(thakis): Implement.
43 NOTIMPLEMENTED();
44 return false;
47 // The referral program is defunct and not used. No need to implement these
48 // functions on non-Win platforms.
49 static bool GetReferral(base::string16* referral) {
50 return true;
52 static bool ClearReferral() {
53 return true;
55 } // namespace GoogleUpdateSettings
56 #endif
58 using content::BrowserThread;
59 using content::NavigationEntry;
61 namespace {
63 // Maximum and minimum delay for financial ping we would allow to be set through
64 // master preferences. Somewhat arbitrary, may need to be adjusted in future.
65 const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200);
66 const base::TimeDelta kMinInitDelay = base::TimeDelta::FromSeconds(20);
68 bool IsBrandOrganic(const std::string& brand) {
69 return brand.empty() || google_brand::IsOrganic(brand);
72 void RecordProductEvents(bool first_run,
73 bool is_google_default_search,
74 bool is_google_homepage,
75 bool is_google_in_startpages,
76 bool already_ran,
77 bool omnibox_used,
78 bool homepage_used,
79 bool app_list_used) {
80 TRACE_EVENT0("RLZ", "RecordProductEvents");
81 // Record the installation of chrome. We call this all the time but the rlz
82 // lib should ignore all but the first one.
83 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
84 RLZTracker::ChromeOmnibox(),
85 rlz_lib::INSTALL);
86 #if !defined(OS_IOS)
87 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
88 RLZTracker::ChromeHomePage(),
89 rlz_lib::INSTALL);
90 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
91 RLZTracker::ChromeAppList(),
92 rlz_lib::INSTALL);
93 #endif // !defined(OS_IOS)
95 if (!already_ran) {
96 // Do the initial event recording if is the first run or if we have an
97 // empty rlz which means we haven't got a chance to do it.
98 char omnibox_rlz[rlz_lib::kMaxRlzLength + 1];
99 if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), omnibox_rlz,
100 rlz_lib::kMaxRlzLength)) {
101 omnibox_rlz[0] = 0;
104 // Record if google is the initial search provider and/or home page.
105 if ((first_run || omnibox_rlz[0] == 0) && is_google_default_search) {
106 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
107 RLZTracker::ChromeOmnibox(),
108 rlz_lib::SET_TO_GOOGLE);
111 #if !defined(OS_IOS)
112 char homepage_rlz[rlz_lib::kMaxRlzLength + 1];
113 if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeHomePage(), homepage_rlz,
114 rlz_lib::kMaxRlzLength)) {
115 homepage_rlz[0] = 0;
118 if ((first_run || homepage_rlz[0] == 0) &&
119 (is_google_homepage || is_google_in_startpages)) {
120 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
121 RLZTracker::ChromeHomePage(),
122 rlz_lib::SET_TO_GOOGLE);
125 char app_list_rlz[rlz_lib::kMaxRlzLength + 1];
126 if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeAppList(), app_list_rlz,
127 rlz_lib::kMaxRlzLength)) {
128 app_list_rlz[0] = 0;
131 // Record if google is the initial search provider and/or home page.
132 if ((first_run || app_list_rlz[0] == 0) && is_google_default_search) {
133 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
134 RLZTracker::ChromeAppList(),
135 rlz_lib::SET_TO_GOOGLE);
137 #endif // !defined(OS_IOS)
140 // Record first user interaction with the omnibox. We call this all the
141 // time but the rlz lib should ingore all but the first one.
142 if (omnibox_used) {
143 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
144 RLZTracker::ChromeOmnibox(),
145 rlz_lib::FIRST_SEARCH);
148 #if !defined(OS_IOS)
149 // Record first user interaction with the home page. We call this all the
150 // time but the rlz lib should ingore all but the first one.
151 if (homepage_used || is_google_in_startpages) {
152 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
153 RLZTracker::ChromeHomePage(),
154 rlz_lib::FIRST_SEARCH);
157 // Record first user interaction with the app list. We call this all the
158 // time but the rlz lib should ingore all but the first one.
159 if (app_list_used) {
160 rlz_lib::RecordProductEvent(rlz_lib::CHROME,
161 RLZTracker::ChromeAppList(),
162 rlz_lib::FIRST_SEARCH);
164 #endif // !defined(OS_IOS)
167 bool SendFinancialPing(const std::string& brand,
168 const base::string16& lang,
169 const base::string16& referral) {
170 rlz_lib::AccessPoint points[] = {RLZTracker::ChromeOmnibox(),
171 #if !defined(OS_IOS)
172 RLZTracker::ChromeHomePage(),
173 RLZTracker::ChromeAppList(),
174 #endif
175 rlz_lib::NO_ACCESS_POINT};
176 std::string lang_ascii(base::UTF16ToASCII(lang));
177 std::string referral_ascii(base::UTF16ToASCII(referral));
178 std::string product_signature;
179 #if defined(OS_CHROMEOS)
180 product_signature = "chromeos";
181 #else
182 product_signature = "chrome";
183 #endif
184 return rlz_lib::SendFinancialPing(rlz_lib::CHROME, points,
185 product_signature.c_str(),
186 brand.c_str(), referral_ascii.c_str(),
187 lang_ascii.c_str(), false, true);
190 } // namespace
192 RLZTracker* RLZTracker::tracker_ = NULL;
194 // static
195 RLZTracker* RLZTracker::GetInstance() {
196 return tracker_ ? tracker_ : Singleton<RLZTracker>::get();
199 RLZTracker::RLZTracker()
200 : first_run_(false),
201 send_ping_immediately_(false),
202 is_google_default_search_(false),
203 is_google_homepage_(false),
204 is_google_in_startpages_(false),
205 worker_pool_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()),
206 already_ran_(false),
207 omnibox_used_(false),
208 homepage_used_(false),
209 app_list_used_(false),
210 min_init_delay_(kMinInitDelay) {
213 RLZTracker::~RLZTracker() {
216 // static
217 bool RLZTracker::InitRlzDelayed(bool first_run,
218 bool send_ping_immediately,
219 base::TimeDelta delay,
220 bool is_google_default_search,
221 bool is_google_homepage,
222 bool is_google_in_startpages) {
223 return GetInstance()->Init(first_run, send_ping_immediately, delay,
224 is_google_default_search, is_google_homepage,
225 is_google_in_startpages);
228 // static
229 bool RLZTracker::InitRlzFromProfileDelayed(Profile* profile,
230 bool first_run,
231 bool send_ping_immediately,
232 base::TimeDelta delay) {
233 bool is_google_default_search = false;
234 TemplateURLService* template_url_service =
235 TemplateURLServiceFactory::GetForProfile(profile);
236 if (template_url_service) {
237 const TemplateURL* url_template =
238 template_url_service->GetDefaultSearchProvider();
239 is_google_default_search =
240 url_template && url_template->url_ref().HasGoogleBaseURLs(
241 template_url_service->search_terms_data());
244 PrefService* pref_service = profile->GetPrefs();
245 bool is_google_homepage = google_util::IsGoogleHomePageUrl(
246 GURL(pref_service->GetString(prefs::kHomePage)));
248 bool is_google_in_startpages = false;
249 #if !defined(OS_IOS)
250 // iOS does not have a notion of startpages.
251 SessionStartupPref session_startup_prefs =
252 StartupBrowserCreator::GetSessionStartupPref(
253 *CommandLine::ForCurrentProcess(), profile);
254 if (session_startup_prefs.type == SessionStartupPref::URLS) {
255 is_google_in_startpages =
256 std::count_if(session_startup_prefs.urls.begin(),
257 session_startup_prefs.urls.end(),
258 google_util::IsGoogleHomePageUrl) > 0;
260 #endif
262 if (!InitRlzDelayed(first_run, send_ping_immediately, delay,
263 is_google_default_search, is_google_homepage,
264 is_google_in_startpages)) {
265 return false;
268 #if !defined(OS_IOS)
269 // Prime the RLZ cache for the home page access point so that its avaiable
270 // for the startup page if needed (i.e., when the startup page is set to
271 // the home page).
272 GetAccessPointRlz(ChromeHomePage(), NULL);
273 #endif // !defined(OS_IOS)
275 return true;
278 bool RLZTracker::Init(bool first_run,
279 bool send_ping_immediately,
280 base::TimeDelta delay,
281 bool is_google_default_search,
282 bool is_google_homepage,
283 bool is_google_in_startpages) {
284 first_run_ = first_run;
285 is_google_default_search_ = is_google_default_search;
286 is_google_homepage_ = is_google_homepage;
287 is_google_in_startpages_ = is_google_in_startpages;
288 send_ping_immediately_ = send_ping_immediately;
290 // Enable zero delays for testing.
291 if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
292 EnableZeroDelayForTesting();
294 delay = std::min(kMaxInitDelay, std::max(min_init_delay_, delay));
296 if (google_brand::GetBrand(&brand_) && !IsBrandOrganic(brand_)) {
297 // Register for notifications from the omnibox so that we can record when
298 // the user performs a first search.
299 registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
300 content::NotificationService::AllSources());
302 #if !defined(OS_IOS)
303 // Register for notifications from navigations, to see if the user has used
304 // the home page.
305 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
306 content::NotificationService::AllSources());
307 #endif // !defined(OS_IOS)
309 google_brand::GetReactivationBrand(&reactivation_brand_);
311 net::URLRequestContextGetter* context_getter =
312 g_browser_process->system_request_context();
314 // Could be NULL; don't run if so. RLZ will try again next restart.
315 if (context_getter) {
316 rlz_lib::SetURLRequestContext(context_getter);
317 ScheduleDelayedInit(delay);
320 return true;
323 void RLZTracker::ScheduleDelayedInit(base::TimeDelta delay) {
324 // The RLZTracker is a singleton object that outlives any runnable tasks
325 // that will be queued up.
326 BrowserThread::GetBlockingPool()->PostDelayedSequencedWorkerTask(
327 worker_pool_token_,
328 FROM_HERE,
329 base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)),
330 delay);
333 void RLZTracker::DelayedInit() {
334 bool schedule_ping = false;
336 // For organic brandcodes do not use rlz at all. Empty brandcode usually
337 // means a chromium install. This is ok.
338 if (!IsBrandOrganic(brand_)) {
339 RecordProductEvents(first_run_, is_google_default_search_,
340 is_google_homepage_, is_google_in_startpages_,
341 already_ran_, omnibox_used_, homepage_used_,
342 app_list_used_);
343 schedule_ping = true;
346 // If chrome has been reactivated, record the events for this brand
347 // as well.
348 if (!IsBrandOrganic(reactivation_brand_)) {
349 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
350 RecordProductEvents(first_run_, is_google_default_search_,
351 is_google_homepage_, is_google_in_startpages_,
352 already_ran_, omnibox_used_, homepage_used_,
353 app_list_used_);
354 schedule_ping = true;
357 already_ran_ = true;
359 if (schedule_ping)
360 ScheduleFinancialPing();
363 void RLZTracker::ScheduleFinancialPing() {
364 BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
365 worker_pool_token_,
366 FROM_HERE,
367 base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this)),
368 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
371 void RLZTracker::PingNowImpl() {
372 TRACE_EVENT0("RLZ", "RLZTracker::PingNowImpl");
373 base::string16 lang;
374 GoogleUpdateSettings::GetLanguage(&lang);
375 if (lang.empty())
376 lang = base::ASCIIToUTF16("en");
377 base::string16 referral;
378 GoogleUpdateSettings::GetReferral(&referral);
380 if (!IsBrandOrganic(brand_) && SendFinancialPing(brand_, lang, referral)) {
381 GoogleUpdateSettings::ClearReferral();
384 base::AutoLock lock(cache_lock_);
385 rlz_cache_.clear();
388 // Prime the RLZ cache for the access points we are interested in.
389 GetAccessPointRlz(RLZTracker::ChromeOmnibox(), NULL);
390 #if !defined(OS_IOS)
391 GetAccessPointRlz(RLZTracker::ChromeHomePage(), NULL);
392 GetAccessPointRlz(RLZTracker::ChromeAppList(), NULL);
393 #endif // !defined(OS_IOS)
396 if (!IsBrandOrganic(reactivation_brand_)) {
397 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
398 SendFinancialPing(reactivation_brand_, lang, referral);
402 bool RLZTracker::SendFinancialPing(const std::string& brand,
403 const base::string16& lang,
404 const base::string16& referral) {
405 return ::SendFinancialPing(brand, lang, referral);
408 void RLZTracker::Observe(int type,
409 const content::NotificationSource& source,
410 const content::NotificationDetails& details) {
411 switch (type) {
412 case chrome::NOTIFICATION_OMNIBOX_OPENED_URL:
413 // In M-36, we made NOTIFICATION_OMNIBOX_OPENED_URL fire more often than
414 // it did previously. The RLZ folks want RLZ's "first search" detection
415 // to remain as unaffected as possible by this change. This test is
416 // there to keep the old behavior.
417 if (!content::Details<OmniboxLog>(details).ptr()->is_popup_open)
418 break;
419 RecordFirstSearch(ChromeOmnibox());
420 registrar_.Remove(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
421 content::NotificationService::AllSources());
422 break;
423 #if !defined(OS_IOS)
424 case content::NOTIFICATION_NAV_ENTRY_PENDING: {
425 const NavigationEntry* entry =
426 content::Details<content::NavigationEntry>(details).ptr();
427 if (entry != NULL &&
428 ((entry->GetTransitionType() &
429 content::PAGE_TRANSITION_HOME_PAGE) != 0)) {
430 RecordFirstSearch(ChromeHomePage());
431 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
432 content::NotificationService::AllSources());
434 break;
436 #endif // !defined(OS_IOS)
437 default:
438 NOTREACHED();
439 break;
443 // static
444 bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
445 rlz_lib::AccessPoint point,
446 rlz_lib::Event event_id) {
447 return GetInstance()->RecordProductEventImpl(product, point, event_id);
450 bool RLZTracker::RecordProductEventImpl(rlz_lib::Product product,
451 rlz_lib::AccessPoint point,
452 rlz_lib::Event event_id) {
453 // Make sure we don't access disk outside of the I/O thread.
454 // In such case we repost the task on the right thread and return error.
455 if (ScheduleRecordProductEvent(product, point, event_id))
456 return true;
458 bool ret = rlz_lib::RecordProductEvent(product, point, event_id);
460 // If chrome has been reactivated, record the event for this brand as well.
461 if (!reactivation_brand_.empty()) {
462 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
463 ret &= rlz_lib::RecordProductEvent(product, point, event_id);
466 return ret;
469 bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product,
470 rlz_lib::AccessPoint point,
471 rlz_lib::Event event_id) {
472 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
473 return false;
475 BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
476 worker_pool_token_,
477 FROM_HERE,
478 base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent),
479 product, point, event_id),
480 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
482 return true;
485 void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) {
486 // Make sure we don't access disk outside of the I/O thread.
487 // In such case we repost the task on the right thread and return error.
488 if (ScheduleRecordFirstSearch(point))
489 return;
491 bool* record_used = GetAccessPointRecord(point);
493 // Try to record event now, else set the flag to try later when we
494 // attempt the ping.
495 if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH))
496 *record_used = true;
497 else if (send_ping_immediately_ && point == ChromeOmnibox())
498 ScheduleDelayedInit(base::TimeDelta());
501 bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) {
502 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
503 return false;
504 BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
505 worker_pool_token_,
506 FROM_HERE,
507 base::Bind(&RLZTracker::RecordFirstSearch,
508 base::Unretained(this), point),
509 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
510 return true;
513 bool* RLZTracker::GetAccessPointRecord(rlz_lib::AccessPoint point) {
514 if (point == ChromeOmnibox())
515 return &omnibox_used_;
516 #if !defined(OS_IOS)
517 if (point == ChromeHomePage())
518 return &homepage_used_;
519 if (point == ChromeAppList())
520 return &app_list_used_;
521 #endif // !defined(OS_IOS)
522 NOTREACHED();
523 return NULL;
526 // static
527 std::string RLZTracker::GetAccessPointHttpHeader(rlz_lib::AccessPoint point) {
528 TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointHttpHeader");
529 std::string extra_headers;
530 base::string16 rlz_string;
531 RLZTracker::GetAccessPointRlz(point, &rlz_string);
532 if (!rlz_string.empty()) {
533 net::HttpUtil::AppendHeaderIfMissing("X-Rlz-String",
534 base::UTF16ToUTF8(rlz_string),
535 &extra_headers);
538 return extra_headers;
541 // GetAccessPointRlz() caches RLZ strings for all access points. If we had
542 // a successful ping, then we update the cached value.
543 bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point,
544 base::string16* rlz) {
545 TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointRlz");
546 return GetInstance()->GetAccessPointRlzImpl(point, rlz);
549 // GetAccessPointRlz() caches RLZ strings for all access points. If we had
550 // a successful ping, then we update the cached value.
551 bool RLZTracker::GetAccessPointRlzImpl(rlz_lib::AccessPoint point,
552 base::string16* rlz) {
553 // If the RLZ string for the specified access point is already cached,
554 // simply return its value.
556 base::AutoLock lock(cache_lock_);
557 if (rlz_cache_.find(point) != rlz_cache_.end()) {
558 if (rlz)
559 *rlz = rlz_cache_[point];
560 return true;
564 // Make sure we don't access disk outside of the I/O thread.
565 // In such case we repost the task on the right thread and return error.
566 if (ScheduleGetAccessPointRlz(point))
567 return false;
569 char str_rlz[rlz_lib::kMaxRlzLength + 1];
570 if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength))
571 return false;
573 base::string16 rlz_local(base::ASCIIToUTF16(std::string(str_rlz)));
574 if (rlz)
575 *rlz = rlz_local;
577 base::AutoLock lock(cache_lock_);
578 rlz_cache_[point] = rlz_local;
579 return true;
582 bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) {
583 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
584 return false;
586 base::string16* not_used = NULL;
587 BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
588 worker_pool_token_,
589 FROM_HERE,
590 base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz), point,
591 not_used),
592 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
593 return true;
596 #if defined(OS_CHROMEOS)
597 // static
598 void RLZTracker::ClearRlzState() {
599 GetInstance()->ClearRlzStateImpl();
602 void RLZTracker::ClearRlzStateImpl() {
603 if (ScheduleClearRlzState())
604 return;
605 rlz_lib::ClearAllProductEvents(rlz_lib::CHROME);
608 bool RLZTracker::ScheduleClearRlzState() {
609 if (!BrowserThread::CurrentlyOn(BrowserThread::UI))
610 return false;
612 BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
613 worker_pool_token_,
614 FROM_HERE,
615 base::Bind(&RLZTracker::ClearRlzStateImpl,
616 base::Unretained(this)),
617 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
618 return true;
620 #endif
622 // static
623 void RLZTracker::CleanupRlz() {
624 GetInstance()->rlz_cache_.clear();
625 GetInstance()->registrar_.RemoveAll();
626 rlz_lib::SetURLRequestContext(NULL);
629 // static
630 void RLZTracker::EnableZeroDelayForTesting() {
631 GetInstance()->min_init_delay_ = base::TimeDelta();
634 #if !defined(OS_IOS)
635 // static
636 void RLZTracker::RecordAppListSearch() {
637 GetInstance()->RecordFirstSearch(RLZTracker::ChromeAppList());
639 #endif