OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // This code glues the RLZ library DLL with Chrome. It allows Chrome to work | 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 | 6 // with or without the DLL being present. If the DLL is not present the |
7 // functions do nothing and just return false. | 7 // functions do nothing and just return false. |
8 | 8 |
9 #include "components/rlz/rlz_tracker.h" | 9 #include "components/rlz/rlz_tracker.h" |
10 | 10 |
11 #include <algorithm> | 11 #include <algorithm> |
12 #include <utility> | 12 #include <utility> |
13 | 13 |
14 #include "base/bind.h" | 14 #include "base/bind.h" |
15 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
| 16 #include "base/sequenced_task_runner.h" |
16 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
17 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
18 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 20 #include "base/task_scheduler/post_task.h" |
| 21 #include "base/task_scheduler/task_traits.h" |
19 #include "base/trace_event/trace_event.h" | 22 #include "base/trace_event/trace_event.h" |
20 #include "build/build_config.h" | 23 #include "build/build_config.h" |
21 #include "components/rlz/rlz_tracker_delegate.h" | 24 #include "components/rlz/rlz_tracker_delegate.h" |
22 | 25 |
23 namespace rlz { | 26 namespace rlz { |
24 namespace { | 27 namespace { |
25 | 28 |
26 // Maximum and minimum delay for financial ping we would allow to be set through | 29 // Maximum and minimum delay for financial ping we would allow to be set through |
27 // master preferences. Somewhat arbitrary, may need to be adjusted in future. | 30 // master preferences. Somewhat arbitrary, may need to be adjusted in future. |
28 const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200); | 31 const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200); |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 RLZTracker::RLZTracker() | 161 RLZTracker::RLZTracker() |
159 : first_run_(false), | 162 : first_run_(false), |
160 send_ping_immediately_(false), | 163 send_ping_immediately_(false), |
161 is_google_default_search_(false), | 164 is_google_default_search_(false), |
162 is_google_homepage_(false), | 165 is_google_homepage_(false), |
163 is_google_in_startpages_(false), | 166 is_google_in_startpages_(false), |
164 already_ran_(false), | 167 already_ran_(false), |
165 omnibox_used_(false), | 168 omnibox_used_(false), |
166 homepage_used_(false), | 169 homepage_used_(false), |
167 app_list_used_(false), | 170 app_list_used_(false), |
168 min_init_delay_(kMinInitDelay) { | 171 min_init_delay_(kMinInitDelay), |
| 172 background_task_runner_(base::CreateSequencedTaskRunnerWithTraits( |
| 173 {base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, |
| 174 base::WithBaseSyncPrimitives(), base::TaskPriority::BACKGROUND})) { |
| 175 DETACH_FROM_SEQUENCE(sequence_checker_); |
169 } | 176 } |
170 | 177 |
171 RLZTracker::~RLZTracker() { | 178 RLZTracker::~RLZTracker() { |
172 } | 179 } |
173 | 180 |
174 // static | 181 // static |
175 void RLZTracker::SetRlzDelegate(std::unique_ptr<RLZTrackerDelegate> delegate) { | 182 void RLZTracker::SetRlzDelegate(std::unique_ptr<RLZTrackerDelegate> delegate) { |
176 RLZTracker* tracker = GetInstance(); | 183 RLZTracker* tracker = GetInstance(); |
177 if (!tracker->delegate_) { | 184 if (!tracker->delegate_) { |
178 // RLZTracker::SetRlzDelegate is called at Profile creation time which can | 185 // RLZTracker::SetRlzDelegate is called at Profile creation time which can |
179 // happens multiple time on ChromeOS, so do nothing if the delegate already | 186 // happens multiple time on ChromeOS, so do nothing if the delegate already |
180 // exists. | 187 // exists. |
181 tracker->SetDelegate(std::move(delegate)); | 188 tracker->SetDelegate(std::move(delegate)); |
182 } | 189 } |
183 } | 190 } |
184 | 191 |
185 void RLZTracker::SetDelegate(std::unique_ptr<RLZTrackerDelegate> delegate) { | 192 void RLZTracker::SetDelegate(std::unique_ptr<RLZTrackerDelegate> delegate) { |
186 DCHECK(delegate); | 193 DCHECK(delegate); |
187 DCHECK(!delegate_); | 194 DCHECK(!delegate_); |
188 delegate_ = std::move(delegate); | 195 delegate_ = std::move(delegate); |
189 worker_pool_token_ = delegate_->GetBlockingPool()->GetSequenceToken(); | |
190 } | 196 } |
191 | 197 |
192 // static | 198 // static |
193 bool RLZTracker::InitRlzDelayed(bool first_run, | 199 bool RLZTracker::InitRlzDelayed(bool first_run, |
194 bool send_ping_immediately, | 200 bool send_ping_immediately, |
195 base::TimeDelta delay, | 201 base::TimeDelta delay, |
196 bool is_google_default_search, | 202 bool is_google_default_search, |
197 bool is_google_homepage, | 203 bool is_google_homepage, |
198 bool is_google_in_startpages) { | 204 bool is_google_in_startpages) { |
199 return GetInstance()->Init(first_run, send_ping_immediately, delay, | 205 return GetInstance()->Init(first_run, send_ping_immediately, delay, |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 void RLZTracker::Cleanup() { | 263 void RLZTracker::Cleanup() { |
258 rlz_cache_.clear(); | 264 rlz_cache_.clear(); |
259 if (delegate_) | 265 if (delegate_) |
260 delegate_->Cleanup(); | 266 delegate_->Cleanup(); |
261 } | 267 } |
262 | 268 |
263 void RLZTracker::ScheduleDelayedInit(base::TimeDelta delay) { | 269 void RLZTracker::ScheduleDelayedInit(base::TimeDelta delay) { |
264 DCHECK(delegate_) << "RLZTracker used before initialization"; | 270 DCHECK(delegate_) << "RLZTracker used before initialization"; |
265 // The RLZTracker is a singleton object that outlives any runnable tasks | 271 // The RLZTracker is a singleton object that outlives any runnable tasks |
266 // that will be queued up. | 272 // that will be queued up. |
267 delegate_->GetBlockingPool()->PostDelayedSequencedWorkerTask( | 273 background_task_runner_->PostDelayedTask( |
268 worker_pool_token_, FROM_HERE, | 274 FROM_HERE, base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)), |
269 base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)), delay); | 275 delay); |
270 } | 276 } |
271 | 277 |
272 void RLZTracker::DelayedInit() { | 278 void RLZTracker::DelayedInit() { |
| 279 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
273 DCHECK(delegate_) << "RLZTracker used before initialization"; | 280 DCHECK(delegate_) << "RLZTracker used before initialization"; |
274 bool schedule_ping = false; | 281 bool schedule_ping = false; |
275 | 282 |
276 // For organic brandcodes do not use rlz at all. Empty brandcode usually | 283 // For organic brandcodes do not use rlz at all. Empty brandcode usually |
277 // means a chromium install. This is ok. | 284 // means a chromium install. This is ok. |
278 if (!delegate_->IsBrandOrganic(brand_)) { | 285 if (!delegate_->IsBrandOrganic(brand_)) { |
279 RecordProductEvents(first_run_, is_google_default_search_, | 286 RecordProductEvents(first_run_, is_google_default_search_, |
280 is_google_homepage_, is_google_in_startpages_, | 287 is_google_homepage_, is_google_in_startpages_, |
281 already_ran_, omnibox_used_, homepage_used_, | 288 already_ran_, omnibox_used_, homepage_used_, |
282 app_list_used_); | 289 app_list_used_); |
(...skipping 12 matching lines...) Expand all Loading... |
295 } | 302 } |
296 | 303 |
297 already_ran_ = true; | 304 already_ran_ = true; |
298 | 305 |
299 if (schedule_ping) | 306 if (schedule_ping) |
300 ScheduleFinancialPing(); | 307 ScheduleFinancialPing(); |
301 } | 308 } |
302 | 309 |
303 void RLZTracker::ScheduleFinancialPing() { | 310 void RLZTracker::ScheduleFinancialPing() { |
304 DCHECK(delegate_) << "RLZTracker used before initialization"; | 311 DCHECK(delegate_) << "RLZTracker used before initialization"; |
305 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior( | 312 background_task_runner_->PostTask( |
306 worker_pool_token_, FROM_HERE, | 313 FROM_HERE, base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this))); |
307 base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this)), | |
308 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
309 } | 314 } |
310 | 315 |
311 void RLZTracker::PingNowImpl() { | 316 void RLZTracker::PingNowImpl() { |
| 317 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
312 DCHECK(delegate_) << "RLZTracker used before initialization"; | 318 DCHECK(delegate_) << "RLZTracker used before initialization"; |
313 TRACE_EVENT0("RLZ", "RLZTracker::PingNowImpl"); | 319 TRACE_EVENT0("RLZ", "RLZTracker::PingNowImpl"); |
314 base::string16 lang; | 320 base::string16 lang; |
315 delegate_->GetLanguage(&lang); | 321 delegate_->GetLanguage(&lang); |
316 if (lang.empty()) | 322 if (lang.empty()) |
317 lang = base::ASCIIToUTF16("en"); | 323 lang = base::ASCIIToUTF16("en"); |
318 base::string16 referral; | 324 base::string16 referral; |
319 delegate_->GetReferral(&referral); | 325 delegate_->GetReferral(&referral); |
320 | 326 |
321 if (!delegate_->IsBrandOrganic(brand_) && | 327 if (!delegate_->IsBrandOrganic(brand_) && |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
361 | 367 |
362 bool RLZTracker::RecordProductEventImpl(rlz_lib::Product product, | 368 bool RLZTracker::RecordProductEventImpl(rlz_lib::Product product, |
363 rlz_lib::AccessPoint point, | 369 rlz_lib::AccessPoint point, |
364 rlz_lib::Event event_id) { | 370 rlz_lib::Event event_id) { |
365 DCHECK(delegate_) << "RLZTracker used before initialization"; | 371 DCHECK(delegate_) << "RLZTracker used before initialization"; |
366 // Make sure we don't access disk outside of the I/O thread. | 372 // Make sure we don't access disk outside of the I/O thread. |
367 // In such case we repost the task on the right thread and return error. | 373 // In such case we repost the task on the right thread and return error. |
368 if (ScheduleRecordProductEvent(product, point, event_id)) | 374 if (ScheduleRecordProductEvent(product, point, event_id)) |
369 return true; | 375 return true; |
370 | 376 |
| 377 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 378 |
371 bool ret = rlz_lib::RecordProductEvent(product, point, event_id); | 379 bool ret = rlz_lib::RecordProductEvent(product, point, event_id); |
372 | 380 |
373 // If chrome has been reactivated, record the event for this brand as well. | 381 // If chrome has been reactivated, record the event for this brand as well. |
374 if (!reactivation_brand_.empty()) { | 382 if (!reactivation_brand_.empty()) { |
375 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str()); | 383 rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str()); |
376 ret &= rlz_lib::RecordProductEvent(product, point, event_id); | 384 ret &= rlz_lib::RecordProductEvent(product, point, event_id); |
377 } | 385 } |
378 | 386 |
379 return ret; | 387 return ret; |
380 } | 388 } |
381 | 389 |
382 bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product, | 390 bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product, |
383 rlz_lib::AccessPoint point, | 391 rlz_lib::AccessPoint point, |
384 rlz_lib::Event event_id) { | 392 rlz_lib::Event event_id) { |
385 DCHECK(delegate_) << "RLZTracker used before initialization"; | 393 DCHECK(delegate_) << "RLZTracker used before initialization"; |
386 if (!delegate_->IsOnUIThread()) | 394 if (!delegate_->IsOnUIThread()) |
387 return false; | 395 return false; |
388 | 396 |
389 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior( | 397 background_task_runner_->PostTask( |
390 worker_pool_token_, FROM_HERE, | 398 FROM_HERE, base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent), |
391 base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent), product, | 399 product, point, event_id)); |
392 point, event_id), | |
393 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
394 | |
395 return true; | 400 return true; |
396 } | 401 } |
397 | 402 |
398 void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) { | 403 void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) { |
399 DCHECK(delegate_) << "RLZTracker used before initialization"; | 404 DCHECK(delegate_) << "RLZTracker used before initialization"; |
400 // Make sure we don't access disk outside of the I/O thread. | 405 // Make sure we don't access disk outside of the I/O thread. |
401 // In such case we repost the task on the right thread and return error. | 406 // In such case we repost the task on the right thread and return error. |
402 if (ScheduleRecordFirstSearch(point)) | 407 if (ScheduleRecordFirstSearch(point)) |
403 return; | 408 return; |
404 | 409 |
| 410 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 411 |
405 bool* record_used = GetAccessPointRecord(point); | 412 bool* record_used = GetAccessPointRecord(point); |
406 | 413 |
407 // Try to record event now, else set the flag to try later when we | 414 // Try to record event now, else set the flag to try later when we |
408 // attempt the ping. | 415 // attempt the ping. |
409 if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH)) | 416 if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH)) { |
410 *record_used = true; | 417 *record_used = true; |
411 else if (send_ping_immediately_ && point == ChromeOmnibox()) | 418 } else if (send_ping_immediately_ && point == ChromeOmnibox()) { |
412 ScheduleDelayedInit(base::TimeDelta()); | 419 ScheduleDelayedInit(base::TimeDelta()); |
| 420 } |
413 } | 421 } |
414 | 422 |
415 bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) { | 423 bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) { |
416 DCHECK(delegate_) << "RLZTracker used before initialization"; | 424 DCHECK(delegate_) << "RLZTracker used before initialization"; |
417 if (!delegate_->IsOnUIThread()) | 425 if (!delegate_->IsOnUIThread()) |
418 return false; | 426 return false; |
419 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior( | 427 background_task_runner_->PostTask(FROM_HERE, |
420 worker_pool_token_, FROM_HERE, | 428 base::Bind(&RLZTracker::RecordFirstSearch, |
421 base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this), point), | 429 base::Unretained(this), point)); |
422 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
423 return true; | 430 return true; |
424 } | 431 } |
425 | 432 |
426 bool* RLZTracker::GetAccessPointRecord(rlz_lib::AccessPoint point) { | 433 bool* RLZTracker::GetAccessPointRecord(rlz_lib::AccessPoint point) { |
427 if (point == ChromeOmnibox()) | 434 if (point == ChromeOmnibox()) |
428 return &omnibox_used_; | 435 return &omnibox_used_; |
429 #if !defined(OS_IOS) | 436 #if !defined(OS_IOS) |
430 if (point == ChromeHomePage()) | 437 if (point == ChromeHomePage()) |
431 return &homepage_used_; | 438 return &homepage_used_; |
432 if (point == ChromeAppList()) | 439 if (point == ChromeAppList()) |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
482 | 489 |
483 // Make sure we don't access disk outside of the I/O thread. | 490 // Make sure we don't access disk outside of the I/O thread. |
484 // In such case we repost the task on the right thread and return error. | 491 // In such case we repost the task on the right thread and return error. |
485 if (ScheduleGetAccessPointRlz(point)) | 492 if (ScheduleGetAccessPointRlz(point)) |
486 return false; | 493 return false; |
487 | 494 |
488 char str_rlz[rlz_lib::kMaxRlzLength + 1]; | 495 char str_rlz[rlz_lib::kMaxRlzLength + 1]; |
489 if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength)) | 496 if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength)) |
490 return false; | 497 return false; |
491 | 498 |
| 499 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 500 |
492 base::string16 rlz_local(base::ASCIIToUTF16(str_rlz)); | 501 base::string16 rlz_local(base::ASCIIToUTF16(str_rlz)); |
493 if (rlz) | 502 if (rlz) |
494 *rlz = rlz_local; | 503 *rlz = rlz_local; |
495 | 504 |
496 base::AutoLock lock(cache_lock_); | 505 base::AutoLock lock(cache_lock_); |
497 rlz_cache_[point] = rlz_local; | 506 rlz_cache_[point] = rlz_local; |
498 return true; | 507 return true; |
499 } | 508 } |
500 | 509 |
501 bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) { | 510 bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) { |
502 DCHECK(delegate_) << "RLZTracker used before initialization"; | 511 DCHECK(delegate_) << "RLZTracker used before initialization"; |
503 if (!delegate_->IsOnUIThread()) | 512 if (!delegate_->IsOnUIThread()) |
504 return false; | 513 return false; |
505 | 514 |
506 base::string16* not_used = nullptr; | 515 base::string16* not_used = nullptr; |
507 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior( | 516 background_task_runner_->PostTask( |
508 worker_pool_token_, FROM_HERE, | 517 FROM_HERE, base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz), |
509 base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz), point, | 518 point, not_used)); |
510 not_used), | |
511 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
512 return true; | 519 return true; |
513 } | 520 } |
514 | 521 |
515 #if defined(OS_CHROMEOS) | 522 #if defined(OS_CHROMEOS) |
516 // static | 523 // static |
517 void RLZTracker::ClearRlzState() { | 524 void RLZTracker::ClearRlzState() { |
518 RLZTracker* tracker = GetInstance(); | 525 RLZTracker* tracker = GetInstance(); |
519 if (tracker->delegate_) | 526 if (tracker->delegate_) |
520 tracker->ClearRlzStateImpl(); | 527 tracker->ClearRlzStateImpl(); |
521 } | 528 } |
522 | 529 |
523 void RLZTracker::ClearRlzStateImpl() { | 530 void RLZTracker::ClearRlzStateImpl() { |
524 DCHECK(delegate_) << "RLZTracker used before initialization"; | 531 DCHECK(delegate_) << "RLZTracker used before initialization"; |
525 if (ScheduleClearRlzState()) | 532 if (ScheduleClearRlzState()) |
526 return; | 533 return; |
| 534 |
| 535 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
527 rlz_lib::ClearAllProductEvents(rlz_lib::CHROME); | 536 rlz_lib::ClearAllProductEvents(rlz_lib::CHROME); |
528 } | 537 } |
529 | 538 |
530 bool RLZTracker::ScheduleClearRlzState() { | 539 bool RLZTracker::ScheduleClearRlzState() { |
531 DCHECK(delegate_) << "RLZTracker used before initialization"; | 540 DCHECK(delegate_) << "RLZTracker used before initialization"; |
532 if (!delegate_->IsOnUIThread()) | 541 if (!delegate_->IsOnUIThread()) |
533 return false; | 542 return false; |
534 | 543 |
535 delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior( | 544 background_task_runner_->PostTask( |
536 worker_pool_token_, FROM_HERE, | 545 FROM_HERE, |
537 base::Bind(&RLZTracker::ClearRlzStateImpl, base::Unretained(this)), | 546 base::Bind(&RLZTracker::ClearRlzStateImpl, base::Unretained(this))); |
538 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | |
539 return true; | 547 return true; |
540 } | 548 } |
541 #endif | 549 #endif |
542 | 550 |
543 // static | 551 // static |
544 void RLZTracker::CleanupRlz() { | 552 void RLZTracker::CleanupRlz() { |
545 GetInstance()->Cleanup(); | 553 GetInstance()->Cleanup(); |
546 rlz_lib::SetURLRequestContext(nullptr); | 554 rlz_lib::SetURLRequestContext(nullptr); |
547 } | 555 } |
548 | 556 |
549 // static | 557 // static |
550 void RLZTracker::EnableZeroDelayForTesting() { | 558 void RLZTracker::EnableZeroDelayForTesting() { |
551 GetInstance()->min_init_delay_ = base::TimeDelta(); | 559 GetInstance()->min_init_delay_ = base::TimeDelta(); |
552 } | 560 } |
553 | 561 |
554 #if !defined(OS_IOS) | 562 #if !defined(OS_IOS) |
555 // static | 563 // static |
556 void RLZTracker::RecordAppListSearch() { | 564 void RLZTracker::RecordAppListSearch() { |
557 // This method is called during unit tests while the RLZTracker has not been | 565 // This method is called during unit tests while the RLZTracker has not been |
558 // initialized, so check for the presence of a delegate and exit if there is | 566 // initialized, so check for the presence of a delegate and exit if there is |
559 // none registered. | 567 // none registered. |
560 RLZTracker* tracker = GetInstance(); | 568 RLZTracker* tracker = GetInstance(); |
561 if (tracker->delegate_) | 569 if (tracker->delegate_) |
562 tracker->RecordFirstSearch(RLZTracker::ChromeAppList()); | 570 tracker->RecordFirstSearch(RLZTracker::ChromeAppList()); |
563 } | 571 } |
564 #endif | 572 #endif |
565 | 573 |
566 } // namespace rlz | 574 } // namespace rlz |
OLD | NEW |