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. 8 9 #include "chrome/browser/rlz/rlz.h" 10 11 #include <algorithm> 12 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" 36 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; 45 } 46 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; 51 } 52 static bool ClearReferral() { 53 return true; 54 } 55 } // namespace GoogleUpdateSettings 56 #endif 57 58 using content::BrowserThread; 59 using content::NavigationEntry; 60 61 namespace { 62 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); 67 68 bool IsBrandOrganic(const std::string& brand) { 69 return brand.empty() || google_brand::IsOrganic(brand); 70 } 71 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) 94 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; 102 } 103 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); 109 } 110 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; 116 } 117 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); 123 } 124 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; 129 } 130 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); 136 } 137 #endif // !defined(OS_IOS) 138 } 139 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); 146 } 147 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); 155 } 156 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); 163 } 164 #endif // !defined(OS_IOS) 165 } 166 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); 188 } 189 190 } // namespace 191 192 RLZTracker* RLZTracker::tracker_ = NULL; 193 194 // static 195 RLZTracker* RLZTracker::GetInstance() { 196 return tracker_ ? tracker_ : Singleton<RLZTracker>::get(); 197 } 198 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) { 211 } 212 213 RLZTracker::~RLZTracker() { 214 } 215 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); 226 } 227 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()); 242 } 243 244 PrefService* pref_service = profile->GetPrefs(); 245 bool is_google_homepage = google_util::IsGoogleHomePageUrl( 246 GURL(pref_service->GetString(prefs::kHomePage))); 247 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; 259 } 260 #endif 261 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; 266 } 267 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) 274 275 return true; 276 } 277 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; 289 290 // Enable zero delays for testing. 291 if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType)) 292 EnableZeroDelayForTesting(); 293 294 delay = std::min(kMaxInitDelay, std::max(min_init_delay_, delay)); 295 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()); 301 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) 308 } 309 google_brand::GetReactivationBrand(&reactivation_brand_); 310 311 net::URLRequestContextGetter* context_getter = 312 g_browser_process->system_request_context(); 313 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); 318 } 319 320 return true; 321 } 322 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); 331 } 332 333 void RLZTracker::DelayedInit() { 334 bool schedule_ping = false; 335 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; 344 } 345 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; 355 } 356 357 already_ran_ = true; 358 359 if (schedule_ping) 360 ScheduleFinancialPing(); 361 } 362 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); 369 } 370 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); 379 380 if (!IsBrandOrganic(brand_) && SendFinancialPing(brand_, lang, referral)) { 381 GoogleUpdateSettings::ClearReferral(); 382 383 { 384 base::AutoLock lock(cache_lock_); 385 rlz_cache_.clear(); 386 } 387 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) 394 } 395 396 if (!IsBrandOrganic(reactivation_brand_)) { 397 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str()); 398 SendFinancialPing(reactivation_brand_, lang, referral); 399 } 400 } 401 402 bool RLZTracker::SendFinancialPing(const std::string& brand, 403 const base::string16& lang, 404 const base::string16& referral) { 405 return ::SendFinancialPing(brand, lang, referral); 406 } 407 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 ui::PAGE_TRANSITION_HOME_PAGE) != 0)) { 430 RecordFirstSearch(ChromeHomePage()); 431 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING, 432 content::NotificationService::AllSources()); 433 } 434 break; 435 } 436 #endif // !defined(OS_IOS) 437 default: 438 NOTREACHED(); 439 break; 440 } 441 } 442 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); 448 } 449 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; 457 458 bool ret = rlz_lib::RecordProductEvent(product, point, event_id); 459 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); 464 } 465 466 return ret; 467 } 468 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; 474 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); 481 482 return true; 483 } 484 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; 490 491 bool* record_used = GetAccessPointRecord(point); 492 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()); 499 } 500 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; 511 } 512 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; 524 } 525 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); 536 } 537 538 return extra_headers; 539 } 540 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); 547 } 548 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. 555 { 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; 561 } 562 } 563 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; 568 569 char str_rlz[rlz_lib::kMaxRlzLength + 1]; 570 if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength)) 571 return false; 572 573 base::string16 rlz_local(base::ASCIIToUTF16(std::string(str_rlz))); 574 if (rlz) 575 *rlz = rlz_local; 576 577 base::AutoLock lock(cache_lock_); 578 rlz_cache_[point] = rlz_local; 579 return true; 580 } 581 582 bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) { 583 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) 584 return false; 585 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; 594 } 595 596 #if defined(OS_CHROMEOS) 597 // static 598 void RLZTracker::ClearRlzState() { 599 GetInstance()->ClearRlzStateImpl(); 600 } 601 602 void RLZTracker::ClearRlzStateImpl() { 603 if (ScheduleClearRlzState()) 604 return; 605 rlz_lib::ClearAllProductEvents(rlz_lib::CHROME); 606 } 607 608 bool RLZTracker::ScheduleClearRlzState() { 609 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) 610 return false; 611 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; 619 } 620 #endif 621 622 // static 623 void RLZTracker::CleanupRlz() { 624 GetInstance()->rlz_cache_.clear(); 625 GetInstance()->registrar_.RemoveAll(); 626 rlz_lib::SetURLRequestContext(NULL); 627 } 628 629 // static 630 void RLZTracker::EnableZeroDelayForTesting() { 631 GetInstance()->min_init_delay_ = base::TimeDelta(); 632 } 633 634 #if !defined(OS_IOS) 635 // static 636 void RLZTracker::RecordAppListSearch() { 637 GetInstance()->RecordFirstSearch(RLZTracker::ChromeAppList()); 638 } 639 #endif 640