summaryrefslogtreecommitdiffstats
path: root/components/rlz
diff options
context:
space:
mode:
Diffstat (limited to 'components/rlz')
-rw-r--r--components/rlz/BUILD.gn42
-rw-r--r--components/rlz/DEPS10
-rw-r--r--components/rlz/OWNERS4
-rw-r--r--components/rlz/rlz_tracker.cc529
-rw-r--r--components/rlz/rlz_tracker.h242
-rw-r--r--components/rlz/rlz_tracker_chromeos.cc24
-rw-r--r--components/rlz/rlz_tracker_delegate.cc15
-rw-r--r--components/rlz/rlz_tracker_delegate.h86
-rw-r--r--components/rlz/rlz_tracker_ios.cc19
-rw-r--r--components/rlz/rlz_tracker_mac.cc24
-rw-r--r--components/rlz/rlz_tracker_unittest.cc1009
-rw-r--r--components/rlz/rlz_tracker_win.cc24
12 files changed, 2028 insertions, 0 deletions
diff --git a/components/rlz/BUILD.gn b/components/rlz/BUILD.gn
new file mode 100644
index 0000000..85608322
--- /dev/null
+++ b/components/rlz/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("rlz") {
+ sources = [
+ "rlz_tracker.cc",
+ "rlz_tracker.h",
+ "rlz_tracker_chromeos.cc",
+ "rlz_tracker_delegate.h",
+ "rlz_tracker_ios.cc",
+ "rlz_tracker_mac.cc",
+ "rlz_tracker_win.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//components/google/core/browser",
+ "//net",
+ "//rlz:rlz_lib",
+ ]
+
+ if (is_ios) {
+ deps = [
+ "//ui/base",
+ ]
+ }
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "rlz_tracker_unittest.cc",
+ ]
+
+ deps = [
+ "//net:test_support",
+ "//rlz:test_support",
+ "//ui/base",
+ ":rlz",
+ ]
+}
diff --git a/components/rlz/DEPS b/components/rlz/DEPS
new file mode 100644
index 0000000..a5d3e8a
--- /dev/null
+++ b/components/rlz/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+base",
+ "+components/google",
+ "+net",
+ "+rlz",
+ "+ui/base",
+
+ # rlz is used on iOS.
+ "-content",
+]
diff --git a/components/rlz/OWNERS b/components/rlz/OWNERS
new file mode 100644
index 0000000..d1986d9
--- /dev/null
+++ b/components/rlz/OWNERS
@@ -0,0 +1,4 @@
+cpu@chromium.org
+gwilson@chromium.org
+rogerta@chromium.org
+
diff --git a/components/rlz/rlz_tracker.cc b/components/rlz/rlz_tracker.cc
new file mode 100644
index 0000000..baa166a
--- /dev/null
+++ b/components/rlz/rlz_tracker.cc
@@ -0,0 +1,529 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This code glues the RLZ library DLL with Chrome. It allows Chrome to work
+// with or without the DLL being present. If the DLL is not present the
+// functions do nothing and just return false.
+
+#include "components/rlz/rlz_tracker.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "components/rlz/rlz_tracker_delegate.h"
+#include "net/http/http_util.h"
+
+namespace rlz {
+namespace {
+
+// Maximum and minimum delay for financial ping we would allow to be set through
+// master preferences. Somewhat arbitrary, may need to be adjusted in future.
+const base::TimeDelta kMaxInitDelay = base::TimeDelta::FromSeconds(200);
+const base::TimeDelta kMinInitDelay = base::TimeDelta::FromSeconds(20);
+
+void RecordProductEvents(bool first_run,
+ bool is_google_default_search,
+ bool is_google_homepage,
+ bool is_google_in_startpages,
+ bool already_ran,
+ bool omnibox_used,
+ bool homepage_used,
+ bool app_list_used) {
+ TRACE_EVENT0("RLZ", "RecordProductEvents");
+ // Record the installation of chrome. We call this all the time but the rlz
+ // lib should ignore all but the first one.
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeOmnibox(),
+ rlz_lib::INSTALL);
+#if !defined(OS_IOS)
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeHomePage(),
+ rlz_lib::INSTALL);
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeAppList(),
+ rlz_lib::INSTALL);
+#endif // !defined(OS_IOS)
+
+ if (!already_ran) {
+ // Do the initial event recording if is the first run or if we have an
+ // empty rlz which means we haven't got a chance to do it.
+ char omnibox_rlz[rlz_lib::kMaxRlzLength + 1];
+ if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), omnibox_rlz,
+ rlz_lib::kMaxRlzLength)) {
+ omnibox_rlz[0] = 0;
+ }
+
+ // Record if google is the initial search provider and/or home page.
+ if ((first_run || omnibox_rlz[0] == 0) && is_google_default_search) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeOmnibox(),
+ rlz_lib::SET_TO_GOOGLE);
+ }
+
+#if !defined(OS_IOS)
+ char homepage_rlz[rlz_lib::kMaxRlzLength + 1];
+ if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeHomePage(), homepage_rlz,
+ rlz_lib::kMaxRlzLength)) {
+ homepage_rlz[0] = 0;
+ }
+
+ if ((first_run || homepage_rlz[0] == 0) &&
+ (is_google_homepage || is_google_in_startpages)) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeHomePage(),
+ rlz_lib::SET_TO_GOOGLE);
+ }
+
+ char app_list_rlz[rlz_lib::kMaxRlzLength + 1];
+ if (!rlz_lib::GetAccessPointRlz(RLZTracker::ChromeAppList(), app_list_rlz,
+ rlz_lib::kMaxRlzLength)) {
+ app_list_rlz[0] = 0;
+ }
+
+ // Record if google is the initial search provider and/or home page.
+ if ((first_run || app_list_rlz[0] == 0) && is_google_default_search) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeAppList(),
+ rlz_lib::SET_TO_GOOGLE);
+ }
+#endif // !defined(OS_IOS)
+ }
+
+ // Record first user interaction with the omnibox. We call this all the
+ // time but the rlz lib should ingore all but the first one.
+ if (omnibox_used) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeOmnibox(),
+ rlz_lib::FIRST_SEARCH);
+ }
+
+#if !defined(OS_IOS)
+ // Record first user interaction with the home page. We call this all the
+ // time but the rlz lib should ingore all but the first one.
+ if (homepage_used || is_google_in_startpages) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeHomePage(),
+ rlz_lib::FIRST_SEARCH);
+ }
+
+ // Record first user interaction with the app list. We call this all the
+ // time but the rlz lib should ingore all but the first one.
+ if (app_list_used) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ RLZTracker::ChromeAppList(),
+ rlz_lib::FIRST_SEARCH);
+ }
+#endif // !defined(OS_IOS)
+}
+
+bool SendFinancialPing(const std::string& brand,
+ const base::string16& lang,
+ const base::string16& referral) {
+ rlz_lib::AccessPoint points[] = {RLZTracker::ChromeOmnibox(),
+#if !defined(OS_IOS)
+ RLZTracker::ChromeHomePage(),
+ RLZTracker::ChromeAppList(),
+#endif
+ rlz_lib::NO_ACCESS_POINT};
+ std::string lang_ascii(base::UTF16ToASCII(lang));
+ std::string referral_ascii(base::UTF16ToASCII(referral));
+ std::string product_signature;
+#if defined(OS_CHROMEOS)
+ product_signature = "chromeos";
+#else
+ product_signature = "chrome";
+#endif
+ return rlz_lib::SendFinancialPing(rlz_lib::CHROME, points,
+ product_signature.c_str(),
+ brand.c_str(), referral_ascii.c_str(),
+ lang_ascii.c_str(), false, true);
+}
+
+} // namespace
+
+RLZTracker* RLZTracker::tracker_ = nullptr;
+
+// static
+RLZTracker* RLZTracker::GetInstance() {
+ return tracker_ ? tracker_ : Singleton<RLZTracker>::get();
+}
+
+RLZTracker::RLZTracker()
+ : first_run_(false),
+ send_ping_immediately_(false),
+ is_google_default_search_(false),
+ is_google_homepage_(false),
+ is_google_in_startpages_(false),
+ already_ran_(false),
+ omnibox_used_(false),
+ homepage_used_(false),
+ app_list_used_(false),
+ min_init_delay_(kMinInitDelay) {
+}
+
+RLZTracker::~RLZTracker() {
+}
+
+// static
+void RLZTracker::SetRlzDelegate(scoped_ptr<RLZTrackerDelegate> delegate) {
+ GetInstance()->SetDelegate(delegate.Pass());
+}
+
+void RLZTracker::SetDelegate(scoped_ptr<RLZTrackerDelegate> delegate) {
+ DCHECK(delegate);
+ if (!delegate_) {
+ delegate_ = delegate.Pass();
+ worker_pool_token_ = delegate_->GetBlockingPool()->GetSequenceToken();
+ }
+}
+
+// static
+bool RLZTracker::InitRlzDelayed(bool first_run,
+ bool send_ping_immediately,
+ base::TimeDelta delay,
+ bool is_google_default_search,
+ bool is_google_homepage,
+ bool is_google_in_startpages) {
+ return GetInstance()->Init(first_run, send_ping_immediately, delay,
+ is_google_default_search, is_google_homepage,
+ is_google_in_startpages);
+}
+
+bool RLZTracker::Init(bool first_run,
+ bool send_ping_immediately,
+ base::TimeDelta delay,
+ bool is_google_default_search,
+ bool is_google_homepage,
+ bool is_google_in_startpages) {
+ first_run_ = first_run;
+ is_google_default_search_ = is_google_default_search;
+ is_google_homepage_ = is_google_homepage;
+ is_google_in_startpages_ = is_google_in_startpages;
+ send_ping_immediately_ = send_ping_immediately;
+
+ // Enable zero delays for testing.
+ if (delegate_->ShouldEnableZeroDelayForTesting())
+ EnableZeroDelayForTesting();
+
+ delay = std::min(kMaxInitDelay, std::max(min_init_delay_, delay));
+
+ if (delegate_->GetBrand(&brand_) && !delegate_->IsBrandOrganic(brand_)) {
+ // Register for notifications from the omnibox so that we can record when
+ // the user performs a first search.
+ delegate_->SetOmniboxSearchCallback(
+ base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this),
+ ChromeOmnibox()));
+
+#if !defined(OS_IOS)
+ // Register for notifications from navigations, to see if the user has used
+ // the home page.
+ delegate_->SetHomepageSearchCallback(
+ base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this),
+ ChromeHomePage()));
+#endif
+ }
+ delegate_->GetReactivationBrand(&reactivation_brand_);
+
+ // Could be null; don't run if so. RLZ will try again next restart.
+ net::URLRequestContextGetter* context_getter = delegate_->GetRequestContext();
+ if (context_getter) {
+ rlz_lib::SetURLRequestContext(context_getter);
+ ScheduleDelayedInit(delay);
+ }
+
+#if !defined(OS_IOS)
+ // Prime the RLZ cache for the home page access point so that its avaiable
+ // for the startup page if needed (i.e., when the startup page is set to
+ // the home page).
+ GetAccessPointRlz(ChromeHomePage(), nullptr);
+#endif // !defined(OS_IOS)
+
+ return true;
+}
+
+void RLZTracker::Cleanup() {
+ rlz_cache_.clear();
+ if (delegate_)
+ delegate_->Cleanup();
+}
+
+void RLZTracker::ScheduleDelayedInit(base::TimeDelta delay) {
+ // The RLZTracker is a singleton object that outlives any runnable tasks
+ // that will be queued up.
+ delegate_->GetBlockingPool()->PostDelayedSequencedWorkerTask(
+ worker_pool_token_, FROM_HERE,
+ base::Bind(&RLZTracker::DelayedInit, base::Unretained(this)), delay);
+}
+
+void RLZTracker::DelayedInit() {
+ bool schedule_ping = false;
+
+ // For organic brandcodes do not use rlz at all. Empty brandcode usually
+ // means a chromium install. This is ok.
+ if (!delegate_->IsBrandOrganic(brand_)) {
+ RecordProductEvents(first_run_, is_google_default_search_,
+ is_google_homepage_, is_google_in_startpages_,
+ already_ran_, omnibox_used_, homepage_used_,
+ app_list_used_);
+ schedule_ping = true;
+ }
+
+ // If chrome has been reactivated, record the events for this brand
+ // as well.
+ if (!delegate_->IsBrandOrganic(reactivation_brand_)) {
+ rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
+ RecordProductEvents(first_run_, is_google_default_search_,
+ is_google_homepage_, is_google_in_startpages_,
+ already_ran_, omnibox_used_, homepage_used_,
+ app_list_used_);
+ schedule_ping = true;
+ }
+
+ already_ran_ = true;
+
+ if (schedule_ping)
+ ScheduleFinancialPing();
+}
+
+void RLZTracker::ScheduleFinancialPing() {
+ delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
+ worker_pool_token_, FROM_HERE,
+ base::Bind(&RLZTracker::PingNowImpl, base::Unretained(this)),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+}
+
+void RLZTracker::PingNowImpl() {
+ TRACE_EVENT0("RLZ", "RLZTracker::PingNowImpl");
+ base::string16 lang;
+ delegate_->GetLanguage(&lang);
+ if (lang.empty())
+ lang = base::ASCIIToUTF16("en");
+ base::string16 referral;
+ delegate_->GetReferral(&referral);
+
+ if (!delegate_->IsBrandOrganic(brand_) &&
+ SendFinancialPing(brand_, lang, referral)) {
+ delegate_->ClearReferral();
+
+ {
+ base::AutoLock lock(cache_lock_);
+ rlz_cache_.clear();
+ }
+
+ // Prime the RLZ cache for the access points we are interested in.
+ GetAccessPointRlz(RLZTracker::ChromeOmnibox(), nullptr);
+#if !defined(OS_IOS)
+ GetAccessPointRlz(RLZTracker::ChromeHomePage(), nullptr);
+ GetAccessPointRlz(RLZTracker::ChromeAppList(), nullptr);
+#endif // !defined(OS_IOS)
+ }
+
+ if (!delegate_->IsBrandOrganic(reactivation_brand_)) {
+ rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
+ SendFinancialPing(reactivation_brand_, lang, referral);
+ }
+}
+
+bool RLZTracker::SendFinancialPing(const std::string& brand,
+ const base::string16& lang,
+ const base::string16& referral) {
+ return ::rlz::SendFinancialPing(brand, lang, referral);
+}
+
+// static
+bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id) {
+ return GetInstance()->RecordProductEventImpl(product, point, event_id);
+}
+
+bool RLZTracker::RecordProductEventImpl(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id) {
+ // Make sure we don't access disk outside of the I/O thread.
+ // In such case we repost the task on the right thread and return error.
+ if (ScheduleRecordProductEvent(product, point, event_id))
+ return true;
+
+ bool ret = rlz_lib::RecordProductEvent(product, point, event_id);
+
+ // If chrome has been reactivated, record the event for this brand as well.
+ if (!reactivation_brand_.empty()) {
+ rlz_lib::SupplementaryBranding branding(reactivation_brand_.c_str());
+ ret &= rlz_lib::RecordProductEvent(product, point, event_id);
+ }
+
+ return ret;
+}
+
+bool RLZTracker::ScheduleRecordProductEvent(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id) {
+ if (!delegate_->IsOnUIThread())
+ return false;
+
+ delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
+ worker_pool_token_, FROM_HERE,
+ base::Bind(base::IgnoreResult(&RLZTracker::RecordProductEvent), product,
+ point, event_id),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+
+ return true;
+}
+
+void RLZTracker::RecordFirstSearch(rlz_lib::AccessPoint point) {
+ // Make sure we don't access disk outside of the I/O thread.
+ // In such case we repost the task on the right thread and return error.
+ if (ScheduleRecordFirstSearch(point))
+ return;
+
+ bool* record_used = GetAccessPointRecord(point);
+
+ // Try to record event now, else set the flag to try later when we
+ // attempt the ping.
+ if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH))
+ *record_used = true;
+ else if (send_ping_immediately_ && point == ChromeOmnibox())
+ ScheduleDelayedInit(base::TimeDelta());
+}
+
+bool RLZTracker::ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) {
+ if (!delegate_->IsOnUIThread())
+ return false;
+ delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
+ worker_pool_token_, FROM_HERE,
+ base::Bind(&RLZTracker::RecordFirstSearch, base::Unretained(this), point),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ return true;
+}
+
+bool* RLZTracker::GetAccessPointRecord(rlz_lib::AccessPoint point) {
+ if (point == ChromeOmnibox())
+ return &omnibox_used_;
+#if !defined(OS_IOS)
+ if (point == ChromeHomePage())
+ return &homepage_used_;
+ if (point == ChromeAppList())
+ return &app_list_used_;
+#endif // !defined(OS_IOS)
+ NOTREACHED();
+ return nullptr;
+}
+
+// static
+std::string RLZTracker::GetAccessPointHttpHeader(rlz_lib::AccessPoint point) {
+ TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointHttpHeader");
+ std::string extra_headers;
+ base::string16 rlz_string;
+ RLZTracker::GetAccessPointRlz(point, &rlz_string);
+ if (!rlz_string.empty()) {
+ net::HttpUtil::AppendHeaderIfMissing("X-Rlz-String",
+ base::UTF16ToUTF8(rlz_string),
+ &extra_headers);
+ }
+
+ return extra_headers;
+}
+
+// GetAccessPointRlz() caches RLZ strings for all access points. If we had
+// a successful ping, then we update the cached value.
+bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point,
+ base::string16* rlz) {
+ TRACE_EVENT0("RLZ", "RLZTracker::GetAccessPointRlz");
+ return GetInstance()->GetAccessPointRlzImpl(point, rlz);
+}
+
+// GetAccessPointRlz() caches RLZ strings for all access points. If we had
+// a successful ping, then we update the cached value.
+bool RLZTracker::GetAccessPointRlzImpl(rlz_lib::AccessPoint point,
+ base::string16* rlz) {
+ // If the RLZ string for the specified access point is already cached,
+ // simply return its value.
+ {
+ base::AutoLock lock(cache_lock_);
+ if (rlz_cache_.find(point) != rlz_cache_.end()) {
+ if (rlz)
+ *rlz = rlz_cache_[point];
+ return true;
+ }
+ }
+
+ // Make sure we don't access disk outside of the I/O thread.
+ // In such case we repost the task on the right thread and return error.
+ if (ScheduleGetAccessPointRlz(point))
+ return false;
+
+ char str_rlz[rlz_lib::kMaxRlzLength + 1];
+ if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength))
+ return false;
+
+ base::string16 rlz_local(base::ASCIIToUTF16(str_rlz));
+ if (rlz)
+ *rlz = rlz_local;
+
+ base::AutoLock lock(cache_lock_);
+ rlz_cache_[point] = rlz_local;
+ return true;
+}
+
+bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) {
+ if (!delegate_->IsOnUIThread())
+ return false;
+
+ base::string16* not_used = nullptr;
+ delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
+ worker_pool_token_, FROM_HERE,
+ base::Bind(base::IgnoreResult(&RLZTracker::GetAccessPointRlz), point,
+ not_used),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ return true;
+}
+
+#if defined(OS_CHROMEOS)
+// static
+void RLZTracker::ClearRlzState() {
+ GetInstance()->ClearRlzStateImpl();
+}
+
+void RLZTracker::ClearRlzStateImpl() {
+ if (ScheduleClearRlzState())
+ return;
+ rlz_lib::ClearAllProductEvents(rlz_lib::CHROME);
+}
+
+bool RLZTracker::ScheduleClearRlzState() {
+ if (!delegate_->IsOnUIThread())
+ return false;
+
+ delegate_->GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
+ worker_pool_token_, FROM_HERE,
+ base::Bind(&RLZTracker::ClearRlzStateImpl, base::Unretained(this)),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ return true;
+}
+#endif
+
+// static
+void RLZTracker::CleanupRlz() {
+ GetInstance()->Cleanup();
+ rlz_lib::SetURLRequestContext(nullptr);
+}
+
+// static
+void RLZTracker::EnableZeroDelayForTesting() {
+ GetInstance()->min_init_delay_ = base::TimeDelta();
+}
+
+#if !defined(OS_IOS)
+// static
+void RLZTracker::RecordAppListSearch() {
+ GetInstance()->RecordFirstSearch(RLZTracker::ChromeAppList());
+}
+#endif
+
+} // namespace rlz
diff --git a/components/rlz/rlz_tracker.h b/components/rlz/rlz_tracker.h
new file mode 100644
index 0000000..1236aa2
--- /dev/null
+++ b/components/rlz/rlz_tracker.h
@@ -0,0 +1,242 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_RLZ_RLZ_TRACKER_H_
+#define COMPONENTS_RLZ_RLZ_TRACKER_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string16.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/time/time.h"
+#include "rlz/lib/rlz_lib.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace rlz {
+
+class RLZTrackerDelegate;
+
+// RLZ is a library which is used to measure distribution scenarios.
+// Its job is to record certain lifetime events in the registry and to send
+// them encoded as a compact string at most twice. The sent data does
+// not contain information that can be used to identify a user or to infer
+// browsing habits. The API in this file is a wrapper around the open source
+// RLZ library which can be found at http://code.google.com/p/rlz.
+//
+// For partner or bundled installs, the RLZ might send more information
+// according to the terms disclosed in the EULA.
+
+class RLZTracker {
+ public:
+ // Sets the RLZTrackerDelegate that should be used by the global RLZTracker
+ // instance. Must be called before calling any other method of RLZTracker.
+ static void SetRlzDelegate(scoped_ptr<RLZTrackerDelegate> delegate);
+
+ // Initializes the RLZ library services for use in chrome. Schedules a delayed
+ // task that performs the ping and registers some events when 'first-run' is
+ // true.
+ //
+ // When |send_ping_immediately| is true, a financial ping should be sent
+ // immediately after a first search is recorded, without waiting for |delay|.
+ // However, we only want this behaviour on first run.
+ //
+ // If the chrome brand is organic (no partners) then the pings don't occur.
+ static bool InitRlzDelayed(bool first_run,
+ bool send_ping_immediately,
+ base::TimeDelta delay,
+ bool is_google_default_search,
+ bool is_google_homepage,
+ bool is_google_in_startpages);
+
+ // Records an RLZ event. Some events can be access point independent.
+ // Returns false it the event could not be recorded. Requires write access
+ // to the HKCU registry hive on windows.
+ static bool RecordProductEvent(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id);
+
+ // For the point parameter of RecordProductEvent.
+ static rlz_lib::AccessPoint ChromeOmnibox();
+#if !defined(OS_IOS)
+ static rlz_lib::AccessPoint ChromeHomePage();
+ static rlz_lib::AccessPoint ChromeAppList();
+#endif
+
+ // Gets the HTTP header value that can be added to requests from the
+ // specific access point. The string returned is of the form:
+ //
+ // "X-Rlz-String: <access-point-rlz>\r\n"
+ //
+ static std::string GetAccessPointHttpHeader(rlz_lib::AccessPoint point);
+
+ // Gets the RLZ value of the access point.
+ // Returns false if the rlz string could not be obtained. In some cases
+ // an empty string can be returned which is not an error.
+ static bool GetAccessPointRlz(rlz_lib::AccessPoint point,
+ base::string16* rlz);
+
+ // Invoked during shutdown to clean up any state created by RLZTracker.
+ static void CleanupRlz();
+
+#if defined(OS_CHROMEOS)
+ // Clears all product state. Should be called when turning RLZ off. On other
+ // platforms, this is done by product uninstaller.
+ static void ClearRlzState();
+#endif
+
+ // This method is public for use by the Singleton class.
+ static RLZTracker* GetInstance();
+
+ // Enables zero delay for InitRlzDelayed. For testing only.
+ static void EnableZeroDelayForTesting();
+
+#if !defined(OS_IOS)
+ // Records that the app list search has been used.
+ static void RecordAppListSearch();
+#endif
+
+ // The following methods are made protected so that they can be used for
+ // testing purposes. Production code should never need to call these.
+ protected:
+ RLZTracker();
+ virtual ~RLZTracker();
+
+ // Performs initialization of RLZ tracker that is purposefully delayed so
+ // that it does not interfere with chrome startup time.
+ virtual void DelayedInit();
+
+ // Used by test code to override the default RLZTracker instance returned
+ // by GetInstance().
+ void set_tracker(RLZTracker* tracker) { tracker_ = tracker; }
+
+ // Sends the financial ping to the RLZ servers and invalidates the RLZ string
+ // cache since the response from the RLZ server may have changed then.
+ // Protected so that its accessible from tests.
+ void PingNowImpl();
+
+ private:
+ friend struct DefaultSingletonTraits<RLZTracker>;
+ friend class base::RefCountedThreadSafe<RLZTracker>;
+
+ // Implementation called from SetRlzDelegate() static method.
+ void SetDelegate(scoped_ptr<RLZTrackerDelegate> delegate);
+
+ // Implementation called from InitRlzDelayed() static method.
+ bool Init(bool first_run,
+ bool send_ping_immediately,
+ base::TimeDelta delay,
+ bool is_google_default_search,
+ bool is_google_homepage,
+ bool is_google_in_startpages);
+
+ // Implementation called from CleanupRlz static method.
+ void Cleanup();
+
+ // Implementation called from RecordProductEvent() static method.
+ bool RecordProductEventImpl(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id);
+
+ // Records FIRST_SEARCH event. Passed as bound callback to RLZTrackerDelegate.
+ void RecordFirstSearch(rlz_lib::AccessPoint point);
+
+ // Implementation called from GetAccessPointRlz() static method.
+ bool GetAccessPointRlzImpl(rlz_lib::AccessPoint point, base::string16* rlz);
+
+ // Schedules the delayed initialization. This method is virtual to allow
+ // tests to override how the scheduling is done.
+ virtual void ScheduleDelayedInit(base::TimeDelta delay);
+
+ // Schedules a call to rlz_lib::RecordProductEvent(). This method is virtual
+ // to allow tests to override how the scheduling is done.
+ virtual bool ScheduleRecordProductEvent(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id);
+
+ // Schedules a call to rlz_lib::RecordFirstSearch(). This method is virtual
+ // to allow tests to override how the scheduling is done.
+ virtual bool ScheduleRecordFirstSearch(rlz_lib::AccessPoint point);
+
+ // Schedules a call to rlz_lib::SendFinancialPing(). This method is virtual
+ // to allow tests to override how the scheduling is done.
+ virtual void ScheduleFinancialPing();
+
+ // Schedules a call to GetAccessPointRlz() on the I/O thread if the current
+ // thread is not already the I/O thread, otherwise does nothing. Returns
+ // true if the call was scheduled, and false otherwise. This method is
+ // virtual to allow tests to override how the scheduling is done.
+ virtual bool ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point);
+
+ // Sends the financial ping to the RLZ servers. This method is virtual to
+ // allow tests to override.
+ virtual bool SendFinancialPing(const std::string& brand,
+ const base::string16& lang,
+ const base::string16& referral);
+
+#if defined(OS_CHROMEOS)
+ // Implementation called from ClearRlzState static method.
+ void ClearRlzStateImpl();
+
+ // Schedules a call to ClearRlzStateImpl(). This method is virtual
+ // to allow tests to override how the scheduling is done.
+ virtual bool ScheduleClearRlzState();
+#endif
+
+ // Returns a pointer to the bool corresponding to whether |point| has been
+ // used but not reported.
+ bool* GetAccessPointRecord(rlz_lib::AccessPoint point);
+
+ // Tracker used for testing purposes only. If this value is non-NULL, it
+ // will be returned from GetInstance() instead of the regular singleton.
+ static RLZTracker* tracker_;
+
+ // Delegate abstracting embedder specific knowledge. Must not be null.
+ scoped_ptr<RLZTrackerDelegate> delegate_;
+
+ // Configuation data for RLZ tracker. Set by call to Init().
+ bool first_run_;
+ bool send_ping_immediately_;
+ bool is_google_default_search_;
+ bool is_google_homepage_;
+ bool is_google_in_startpages_;
+
+ // Unique sequence token so that tasks posted by RLZTracker are executed
+ // sequentially in the blocking pool.
+ base::SequencedWorkerPool::SequenceToken worker_pool_token_;
+
+ // Keeps track if the RLZ tracker has already performed its delayed
+ // initialization.
+ bool already_ran_;
+
+ // Keeps a cache of RLZ access point strings, since they rarely change.
+ // The cache must be protected by a lock since it may be accessed from
+ // the UI thread for reading and the IO thread for reading and/or writing.
+ base::Lock cache_lock_;
+ std::map<rlz_lib::AccessPoint, base::string16> rlz_cache_;
+
+ // Keeps track of whether the omnibox, home page or app list have been used.
+ bool omnibox_used_;
+ bool homepage_used_;
+ bool app_list_used_;
+
+ // Main and (optionally) reactivation brand codes, assigned on UI thread.
+ std::string brand_;
+ std::string reactivation_brand_;
+
+ // Minimum delay before sending financial ping after initialization.
+ base::TimeDelta min_init_delay_;
+
+ DISALLOW_COPY_AND_ASSIGN(RLZTracker);
+};
+
+} // namespace rlz
+
+#endif // COMPONENTS_RLZ_RLZ_TRACKER_H_
diff --git a/components/rlz/rlz_tracker_chromeos.cc b/components/rlz/rlz_tracker_chromeos.cc
new file mode 100644
index 0000000..b63c934
--- /dev/null
+++ b/components/rlz/rlz_tracker_chromeos.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/rlz/rlz_tracker.h"
+
+namespace rlz {
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeOmnibox() {
+ return rlz_lib::CHROMEOS_OMNIBOX;
+}
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeHomePage() {
+ return rlz_lib::CHROMEOS_HOME_PAGE;
+}
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeAppList() {
+ return rlz_lib::CHROMEOS_APP_LIST;
+}
+
+} // namespace rlz
diff --git a/components/rlz/rlz_tracker_delegate.cc b/components/rlz/rlz_tracker_delegate.cc
new file mode 100644
index 0000000..84f35aa
--- /dev/null
+++ b/components/rlz/rlz_tracker_delegate.cc
@@ -0,0 +1,15 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/rlz/rlz_tracker_delegate.h"
+
+namespace rlz {
+
+RLZTrackerDelegate::RLZTrackerDelegate() {
+}
+
+RLZTrackerDelegate::~RLZTrackerDelegate() {
+}
+
+} // namespace rlz
diff --git a/components/rlz/rlz_tracker_delegate.h b/components/rlz/rlz_tracker_delegate.h
new file mode 100644
index 0000000..d1270e4
--- /dev/null
+++ b/components/rlz/rlz_tracker_delegate.h
@@ -0,0 +1,86 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_RLZ_RLZ_TRACKER_DELEGATE_H_
+#define COMPONENTS_RLZ_RLZ_TRACKER_DELEGATE_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/threading/sequenced_worker_pool.h"
+
+namespace base {
+class SequencedWorkerPool;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace rlz {
+
+// RLZTrackerDelegate is an abstract interface that provides access to embedder
+// specific singletons or gives information about the embedder environment.
+class RLZTrackerDelegate {
+ public:
+ RLZTrackerDelegate() {}
+ virtual ~RLZTrackerDelegate() {}
+
+ // Invoked during RLZTracker cleanup, to request the cleanup of the delegate.
+ virtual void Cleanup() = 0;
+
+ // Returns whether the current thread is the UI thread.
+ virtual bool IsOnUIThread() = 0;
+
+ // Returns the SequencedWorkerPool where the RLZTracker will post its tasks
+ // that should be executed in the background.
+ virtual base::SequencedWorkerPool* GetBlockingPool() = 0;
+
+ // Returns the URLRequestContextGetter to use for network connections.
+ virtual net::URLRequestContextGetter* GetRequestContext() = 0;
+
+ // Returns the brand code for the installation of Chrome in |brand| and a
+ // boolean indicating whether the operation was a success or not.
+ virtual bool GetBrand(std::string* brand) = 0;
+
+ // Returns whether |brand| is an organic brand.
+ virtual bool IsBrandOrganic(const std::string& brand) = 0;
+
+ // Returns the reactivation brand code for Chrome in |brand| and a boolean
+ // indicating whether the operation was a success or not.
+ virtual bool GetReactivationBrand(std::string* brand) = 0;
+
+ // Returns true if RLZTracker should ignore initial delay for testing.
+ virtual bool ShouldEnableZeroDelayForTesting() = 0;
+
+ // Returns the installation language in |language| and a boolean indicating
+ // whether the operation was a success or not.
+ virtual bool GetLanguage(base::string16* language) = 0;
+
+ // Returns the referral code in |referral| and a boolean indicating whether
+ // the operation was a success or not. Deprecated.
+ virtual bool GetReferral(base::string16* referral) = 0;
+
+ // Clears the referral code. Deprecated.
+ virtual bool ClearReferral() = 0;
+
+ // Registers |callback| to be invoked the next time the user perform a search
+ // using Google search engine via the omnibox. Callback will invoked at most
+ // once.
+ virtual void SetOmniboxSearchCallback(const base::Closure& callback) = 0;
+
+ // Registers |callback| to be invoked the next time the user perform a search
+ // using Google search engine via the homepage. Callback will invoked at most
+ // once.
+ virtual void SetHomepageSearchCallback(const base::Closure& callback) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RLZTrackerDelegate);
+};
+
+} // namespace rlz
+
+#endif // COMPONENTS_RLZ_RLZ_TRACKER_DELEGATE_H_
diff --git a/components/rlz/rlz_tracker_ios.cc b/components/rlz/rlz_tracker_ios.cc
new file mode 100644
index 0000000..e2b110c
--- /dev/null
+++ b/components/rlz/rlz_tracker_ios.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/rlz/rlz_tracker.h"
+
+#include "rlz/lib/rlz_lib.h"
+#include "ui/base/device_form_factor.h"
+
+namespace rlz {
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeOmnibox() {
+ return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_PHONE
+ ? rlz_lib::CHROME_IOS_OMNIBOX_MOBILE
+ : rlz_lib::CHROME_IOS_OMNIBOX_TABLET;
+}
+
+} // namespace rlz
diff --git a/components/rlz/rlz_tracker_mac.cc b/components/rlz/rlz_tracker_mac.cc
new file mode 100644
index 0000000..186f9e6
--- /dev/null
+++ b/components/rlz/rlz_tracker_mac.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/rlz/rlz_tracker.h"
+
+namespace rlz {
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeOmnibox() {
+ return rlz_lib::CHROME_MAC_OMNIBOX;
+}
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeHomePage() {
+ return rlz_lib::CHROME_MAC_HOME_PAGE;
+}
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeAppList() {
+ return rlz_lib::CHROME_MAC_APP_LIST;
+}
+
+} // namespace rlz
diff --git a/components/rlz/rlz_tracker_unittest.cc b/components/rlz/rlz_tracker_unittest.cc
new file mode 100644
index 0000000..eb33d89
--- /dev/null
+++ b/components/rlz/rlz_tracker_unittest.cc
@@ -0,0 +1,1009 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/rlz/rlz_tracker.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/time/time.h"
+#include "components/rlz/rlz_tracker_delegate.h"
+#include "net/url_request/url_request_test_util.h"
+#include "rlz/test/rlz_test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_IOS)
+#include "ui/base/device_form_factor.h"
+#endif
+
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+using testing::AssertionFailure;
+
+namespace rlz {
+namespace {
+
+class TestRLZTrackerDelegate : public RLZTrackerDelegate {
+ public:
+ TestRLZTrackerDelegate()
+ : worker_pool_(new base::SequencedWorkerPool(1, "TestRLZTracker")),
+ request_context_getter_(new net::TestURLRequestContextGetter(
+ base::ThreadTaskRunnerHandle::Get())) {}
+
+ ~TestRLZTrackerDelegate() override { worker_pool_->Shutdown(); }
+
+ void set_brand(const char* brand) { brand_override_ = brand; }
+
+ void set_reactivation_brand(const char* reactivation_brand) {
+ // TODO(thakis): Reactivation doesn't exist on Mac yet.
+ reactivation_brand_override_ = reactivation_brand;
+ }
+
+ void SimulateOmniboxUsage() {
+ using std::swap;
+ base::Closure callback;
+ swap(callback, on_omnibox_search_callback_);
+ if (!callback.is_null())
+ callback.Run();
+ }
+
+ void SimulateHomepageUsage() {
+ using std::swap;
+ base::Closure callback;
+ swap(callback, on_homepage_search_callback_);
+ if (!callback.is_null())
+ callback.Run();
+ }
+
+ // RLZTrackerDelegate implementation.
+ void Cleanup() override {
+ on_omnibox_search_callback_.Reset();
+ on_homepage_search_callback_.Reset();
+ }
+
+ bool IsOnUIThread() override { return true; }
+
+ base::SequencedWorkerPool* GetBlockingPool() override {
+ return worker_pool_.get();
+ }
+
+ net::URLRequestContextGetter* GetRequestContext() override {
+ return request_context_getter_.get();
+ }
+
+ bool GetBrand(std::string* brand) override {
+ *brand = brand_override_;
+ return true;
+ }
+
+ bool IsBrandOrganic(const std::string& brand) override {
+ return brand.empty() || brand == "GGLS" || brand == "GGRS";
+ }
+
+ bool GetReactivationBrand(std::string* brand) override {
+ *brand = reactivation_brand_override_;
+ return true;
+ }
+
+ bool ShouldEnableZeroDelayForTesting() override { return true; }
+
+ bool GetLanguage(base::string16* language) override { return true; }
+
+ bool GetReferral(base::string16* referral) override { return true; }
+
+ bool ClearReferral() override { return true; }
+
+ void SetOmniboxSearchCallback(const base::Closure& callback) override {
+ DCHECK(!callback.is_null());
+ on_omnibox_search_callback_ = callback;
+ }
+
+ void SetHomepageSearchCallback(const base::Closure& callback) override {
+ DCHECK(!callback.is_null());
+ on_homepage_search_callback_ = callback;
+ }
+
+ private:
+ scoped_refptr<base::SequencedWorkerPool> worker_pool_;
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+
+ std::string brand_override_;
+ std::string reactivation_brand_override_;
+ base::Closure on_omnibox_search_callback_;
+ base::Closure on_homepage_search_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestRLZTrackerDelegate);
+};
+
+// Dummy RLZ string for the access points.
+const char kOmniboxRlzString[] = "test_omnibox";
+const char kNewOmniboxRlzString[] = "new_omnibox";
+#if !defined(OS_IOS)
+const char kHomepageRlzString[] = "test_homepage";
+const char kNewHomepageRlzString[] = "new_homepage";
+const char kAppListRlzString[] = "test_applist";
+const char kNewAppListRlzString[] = "new_applist";
+#endif // !defined(OS_IOS)
+
+// Some helper macros to test it a string contains/does not contain a substring.
+
+AssertionResult CmpHelperSTRC(const char* str_expression,
+ const char* substr_expression,
+ const char* str,
+ const char* substr) {
+ if (nullptr != strstr(str, substr)) {
+ return AssertionSuccess();
+ }
+
+ return AssertionFailure() << "Expected: (" << substr_expression << ") in ("
+ << str_expression << "), actual: '"
+ << substr << "' not in '" << str << "'";
+}
+
+AssertionResult CmpHelperSTRNC(const char* str_expression,
+ const char* substr_expression,
+ const char* str,
+ const char* substr) {
+ if (nullptr == strstr(str, substr)) {
+ return AssertionSuccess();
+ }
+
+ return AssertionFailure() << "Expected: (" << substr_expression
+ << ") not in (" << str_expression << "), actual: '"
+ << substr << "' in '" << str << "'";
+}
+
+#define EXPECT_STR_CONTAINS(str, substr) \
+ EXPECT_PRED_FORMAT2(CmpHelperSTRC, str, substr)
+
+#define EXPECT_STR_NOT_CONTAIN(str, substr) \
+ EXPECT_PRED_FORMAT2(CmpHelperSTRNC, str, substr)
+
+} // namespace
+
+// Test class for RLZ tracker. Makes some member functions public and
+// overrides others to make it easier to test.
+class TestRLZTracker : public RLZTracker {
+ public:
+ using RLZTracker::InitRlzDelayed;
+ using RLZTracker::DelayedInit;
+
+ TestRLZTracker() : assume_not_ui_thread_(true) { set_tracker(this); }
+
+ ~TestRLZTracker() override { set_tracker(nullptr); }
+
+ bool was_ping_sent_for_brand(const std::string& brand) const {
+ return pinged_brands_.count(brand) > 0;
+ }
+
+ void set_assume_not_ui_thread(bool assume_not_ui_thread) {
+ assume_not_ui_thread_ = assume_not_ui_thread;
+ }
+
+ private:
+ void ScheduleDelayedInit(base::TimeDelta delay) override {
+ // If the delay is 0, invoke the delayed init now. Otherwise,
+ // don't schedule anything, it will be manually called during tests.
+ if (delay == base::TimeDelta())
+ DelayedInit();
+ }
+
+ void ScheduleFinancialPing() override { PingNowImpl(); }
+
+ bool ScheduleRecordProductEvent(rlz_lib::Product product,
+ rlz_lib::AccessPoint point,
+ rlz_lib::Event event_id) override {
+ return !assume_not_ui_thread_;
+ }
+
+ bool ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) override {
+ return !assume_not_ui_thread_;
+ }
+
+ bool ScheduleRecordFirstSearch(rlz_lib::AccessPoint point) override {
+ return !assume_not_ui_thread_;
+ }
+
+#if defined(OS_CHROMEOS)
+ bool ScheduleClearRlzState() override { return !assume_not_ui_thread_; }
+#endif
+
+ bool SendFinancialPing(const std::string& brand,
+ const base::string16& lang,
+ const base::string16& referral) override {
+ // Don't ping the server during tests, just pretend as if we did.
+ EXPECT_FALSE(brand.empty());
+ pinged_brands_.insert(brand);
+
+ // Set new access points RLZ string, like the actual server ping would have
+ // done.
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeOmnibox(),
+ kNewOmniboxRlzString);
+#if !defined(OS_IOS)
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeHomePage(),
+ kNewHomepageRlzString);
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeAppList(),
+ kNewAppListRlzString);
+#endif // !defined(OS_IOS)
+ return true;
+ }
+
+ std::set<std::string> pinged_brands_;
+ bool assume_not_ui_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestRLZTracker);
+};
+
+class RlzLibTest : public testing::Test {
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ void SetMainBrand(const char* brand);
+ void SetReactivationBrand(const char* brand);
+
+ void SimulateOmniboxUsage();
+ void SimulateHomepageUsage();
+ void SimulateAppListUsage();
+ void InvokeDelayedInit();
+
+ void ExpectEventRecorded(const char* event_name, bool expected);
+ void ExpectRlzPingSent(bool expected);
+ void ExpectReactivationRlzPingSent(bool expected);
+
+ base::MessageLoop message_loop_;
+ TestRLZTrackerDelegate* delegate_;
+ scoped_ptr<TestRLZTracker> tracker_;
+ RlzLibTestNoMachineStateHelper m_rlz_test_helper_;
+};
+
+void RlzLibTest::SetUp() {
+ testing::Test::SetUp();
+ m_rlz_test_helper_.SetUp();
+
+ delegate_ = new TestRLZTrackerDelegate;
+ tracker_.reset(new TestRLZTracker());
+ RLZTracker::SetRlzDelegate(make_scoped_ptr(delegate_));
+
+ // Make sure a non-organic brand code is set in the registry or the RLZTracker
+ // is pretty much a no-op.
+ SetMainBrand("TEST");
+ SetReactivationBrand("");
+}
+
+void RlzLibTest::TearDown() {
+ delegate_ = nullptr;
+ tracker_.reset();
+ testing::Test::TearDown();
+ m_rlz_test_helper_.TearDown();
+}
+
+void RlzLibTest::SetMainBrand(const char* brand) {
+ delegate_->set_brand(brand);
+}
+
+void RlzLibTest::SetReactivationBrand(const char* brand) {
+ delegate_->set_reactivation_brand(brand);
+}
+
+void RlzLibTest::SimulateOmniboxUsage() {
+ delegate_->SimulateOmniboxUsage();
+}
+
+void RlzLibTest::SimulateHomepageUsage() {
+ delegate_->SimulateHomepageUsage();
+}
+
+void RlzLibTest::SimulateAppListUsage() {
+#if !defined(OS_IOS)
+ RLZTracker::RecordAppListSearch();
+#endif // !defined(OS_IOS)
+}
+
+void RlzLibTest::InvokeDelayedInit() {
+ tracker_->DelayedInit();
+}
+
+void RlzLibTest::ExpectEventRecorded(const char* event_name, bool expected) {
+ char cgi[rlz_lib::kMaxCgiLength];
+ GetProductEventsAsCgi(rlz_lib::CHROME, cgi, arraysize(cgi));
+ if (expected) {
+ EXPECT_STR_CONTAINS(cgi, event_name);
+ } else {
+ EXPECT_STR_NOT_CONTAIN(cgi, event_name);
+ }
+}
+
+void RlzLibTest::ExpectRlzPingSent(bool expected) {
+ std::string brand;
+ delegate_->GetBrand(&brand);
+ EXPECT_EQ(expected, tracker_->was_ping_sent_for_brand(brand.c_str()));
+}
+
+void RlzLibTest::ExpectReactivationRlzPingSent(bool expected) {
+ std::string brand;
+ delegate_->GetReactivationBrand(&brand);
+ EXPECT_EQ(expected, tracker_->was_ping_sent_for_brand(brand.c_str()));
+}
+
+// The events that affect the different RLZ scenarios are the following:
+//
+// A: the user starts chrome for the first time
+// B: the user stops chrome
+// C: the user start a subsequent time
+// D: the user stops chrome again
+// I: the RLZTracker::DelayedInit() method is invoked
+// X: the user performs a search using the omnibox
+// Y: the user performs a search using the home page
+// Z: the user performs a search using the app list
+//
+// The events A to D happen in chronological order, but the other events
+// may happen at any point between A-B or C-D, in no particular order.
+//
+// The visible results of the scenarios on Win are:
+//
+// C1I event is recorded
+// C2I event is recorded
+// C7I event is recorded
+// C1F event is recorded
+// C2F event is recorded
+// C7F event is recorded
+// C1S event is recorded
+// C2S event is recorded
+// C7S event is recorded
+// RLZ ping sent
+//
+// On Mac, C5 / C6 / C8 are sent instead of C1 / C2 / C7.
+// On ChromeOS, CA / CB / CC are sent, respectively.
+//
+// On iOS, only the omnibox events are recorded, and the value send depends
+// on the device form factor (phone or tablet).
+//
+// Variations on the above scenarios:
+//
+// - if the delay specified to InitRlzDelayed() is negative, then the RLZ
+// ping should be sent out at the time of event X and not wait for I
+//
+// Also want to test that pre-warming the RLZ string cache works correctly.
+
+#if defined(OS_WIN)
+const char kOmniboxInstall[] = "C1I";
+const char kOmniboxSetToGoogle[] = "C1S";
+const char kOmniboxFirstSearch[] = "C1F";
+
+const char kHomepageInstall[] = "C2I";
+const char kHomepageSetToGoogle[] = "C2S";
+const char kHomepageFirstSearch[] = "C2F";
+
+const char kAppListInstall[] = "C7I";
+const char kAppListSetToGoogle[] = "C7S";
+const char kAppListFirstSearch[] = "C7F";
+#elif defined(OS_IOS)
+const char kOmniboxInstallPhone[] = "CDI";
+const char kOmniboxSetToGooglePhone[] = "CDS";
+const char kOmniboxFirstSearchPhone[] = "CDF";
+
+const char kOmniboxInstallTablet[] = "C9I";
+const char kOmniboxSetToGoogleTablet[] = "C9S";
+const char kOmniboxFirstSearchTablet[] = "C9F";
+#elif defined(OS_MACOSX)
+const char kOmniboxInstall[] = "C5I";
+const char kOmniboxSetToGoogle[] = "C5S";
+const char kOmniboxFirstSearch[] = "C5F";
+
+const char kHomepageInstall[] = "C6I";
+const char kHomepageSetToGoogle[] = "C6S";
+const char kHomepageFirstSearch[] = "C6F";
+
+const char kAppListInstall[] = "C8I";
+const char kAppListSetToGoogle[] = "C8S";
+const char kAppListFirstSearch[] = "C8F";
+#elif defined(OS_CHROMEOS)
+const char kOmniboxInstall[] = "CAI";
+const char kOmniboxSetToGoogle[] = "CAS";
+const char kOmniboxFirstSearch[] = "CAF";
+
+const char kHomepageInstall[] = "CBI";
+const char kHomepageSetToGoogle[] = "CBS";
+const char kHomepageFirstSearch[] = "CBF";
+
+const char kAppListInstall[] = "CCI";
+const char kAppListSetToGoogle[] = "CCS";
+const char kAppListFirstSearch[] = "CCF";
+#endif
+
+const char* OmniboxInstall() {
+#if defined(OS_IOS)
+ return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET
+ ? kOmniboxInstallTablet
+ : kOmniboxInstallPhone;
+#else
+ return kOmniboxInstall;
+#endif
+}
+
+const char* OmniboxSetToGoogle() {
+#if defined(OS_IOS)
+ return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET
+ ? kOmniboxSetToGoogleTablet
+ : kOmniboxSetToGooglePhone;
+#else
+ return kOmniboxSetToGoogle;
+#endif
+}
+
+const char* OmniboxFirstSearch() {
+#if defined(OS_IOS)
+ return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET
+ ? kOmniboxFirstSearchTablet
+ : kOmniboxFirstSearchPhone;
+#else
+ return kOmniboxFirstSearch;
+#endif
+}
+
+const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(20);
+
+TEST_F(RlzLibTest, RecordProductEvent) {
+ RLZTracker::RecordProductEvent(rlz_lib::CHROME, RLZTracker::ChromeOmnibox(),
+ rlz_lib::FIRST_SEARCH);
+
+ ExpectEventRecorded(OmniboxFirstSearch(), true);
+}
+
+TEST_F(RlzLibTest, QuickStopAfterStart) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, true);
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), false);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, false);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, false);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnly) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), true);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, true);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, true);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyGoogleAsStartup) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, false, false, true);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, true);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyNoFirstRunNoRlzStrings) {
+ TestRLZTracker::InitRlzDelayed(false, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), true);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, true);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, true);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyNoFirstRunNoRlzStringsGoogleAsStartup) {
+ TestRLZTracker::InitRlzDelayed(false, false, kDelay, false, false, true);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, true);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyNoFirstRun) {
+ // Set some dummy RLZ strings to simulate that we already ran before and
+ // performed a successful ping to the RLZ server.
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeOmnibox(), kOmniboxRlzString);
+#if !defined(OS_IOS)
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeHomePage(), kHomepageRlzString);
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeAppList(), kAppListRlzString);
+#endif // !defined(OS_IOS)
+
+ TestRLZTracker::InitRlzDelayed(false, false, kDelay, true, true, true);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyNoGoogleDefaultSearchOrHomepageOrStartup) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, false, false, false);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, OmniboxUsageOnly) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ SimulateOmniboxUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), false);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), true);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, false);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, false);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, HomepageUsageOnly) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ SimulateHomepageUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), false);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, false);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, false);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, AppListUsageOnly) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ SimulateAppListUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), false);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, false);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, false);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, true);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, UsageBeforeDelayedInit) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ SimulateOmniboxUsage();
+ SimulateHomepageUsage();
+ SimulateAppListUsage();
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), true);
+ ExpectEventRecorded(OmniboxFirstSearch(), true);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, true);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, true);
+ ExpectEventRecorded(kAppListFirstSearch, true);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, UsageAfterDelayedInit) {
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+ SimulateOmniboxUsage();
+ SimulateHomepageUsage();
+ SimulateAppListUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), true);
+ ExpectEventRecorded(OmniboxFirstSearch(), true);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, true);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, true);
+ ExpectEventRecorded(kAppListFirstSearch, true);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, OmniboxUsageSendsPingWhenSendPingImmediately) {
+ TestRLZTracker::InitRlzDelayed(true, true, kDelay, true, true, false);
+ SimulateOmniboxUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), true);
+ ExpectEventRecorded(OmniboxSetToGoogle(), true);
+ ExpectEventRecorded(OmniboxFirstSearch(), true);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, true);
+ ExpectEventRecorded(kHomepageSetToGoogle, true);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, true);
+ ExpectEventRecorded(kAppListSetToGoogle, true);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, HomepageUsageDoesNotSendPingWhenSendPingImmediately) {
+ TestRLZTracker::InitRlzDelayed(true, true, kDelay, true, true, false);
+ SimulateHomepageUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), false);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, false);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, false);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, StartupUsageDoesNotSendPingWhenSendPingImmediately) {
+ TestRLZTracker::InitRlzDelayed(true, true, kDelay, true, false, true);
+ SimulateHomepageUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), false);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, false);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, true);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, false);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, false);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, AppListUsageDoesNotSendPingWhenSendPingImmediately) {
+ TestRLZTracker::InitRlzDelayed(true, true, kDelay, true, false, false);
+ SimulateAppListUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded(OmniboxInstall(), false);
+ ExpectEventRecorded(OmniboxSetToGoogle(), false);
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+
+#if !defined(OS_IOS)
+ // Home page events.
+ ExpectEventRecorded(kHomepageInstall, false);
+ ExpectEventRecorded(kHomepageSetToGoogle, false);
+ ExpectEventRecorded(kHomepageFirstSearch, false);
+
+ // App list events.
+ ExpectEventRecorded(kAppListInstall, false);
+ ExpectEventRecorded(kAppListSetToGoogle, false);
+ ExpectEventRecorded(kAppListFirstSearch, true);
+#endif // !defined(OS_IOS)
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, GetAccessPointRlzOnIoThread) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeOmnibox(), kOmniboxRlzString);
+
+ base::string16 rlz;
+
+ tracker_->set_assume_not_ui_thread(true);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, base::UTF16ToUTF8(rlz).c_str());
+}
+
+TEST_F(RlzLibTest, GetAccessPointRlzNotOnIoThread) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeOmnibox(), kOmniboxRlzString);
+
+ base::string16 rlz;
+
+ tracker_->set_assume_not_ui_thread(false);
+ EXPECT_FALSE(
+ RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+}
+
+TEST_F(RlzLibTest, GetAccessPointRlzIsCached) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeOmnibox(), kOmniboxRlzString);
+
+ base::string16 rlz;
+
+ tracker_->set_assume_not_ui_thread(false);
+ EXPECT_FALSE(
+ RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+
+ tracker_->set_assume_not_ui_thread(true);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, base::UTF16ToUTF8(rlz).c_str());
+
+ tracker_->set_assume_not_ui_thread(false);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, base::UTF16ToUTF8(rlz).c_str());
+}
+
+TEST_F(RlzLibTest, PingUpdatesRlzCache) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeOmnibox(), kOmniboxRlzString);
+#if !defined(OS_IOS)
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeHomePage(), kHomepageRlzString);
+ rlz_lib::SetAccessPointRlz(RLZTracker::ChromeAppList(), kAppListRlzString);
+#endif // !defined(OS_IOS)
+
+ base::string16 rlz;
+
+ // Prime the cache.
+ tracker_->set_assume_not_ui_thread(true);
+
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, base::UTF16ToUTF8(rlz).c_str());
+#if !defined(OS_IOS)
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(
+ RLZTracker::ChromeHomePage(), &rlz));
+ EXPECT_STREQ(kHomepageRlzString, base::UTF16ToUTF8(rlz).c_str());
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeAppList(), &rlz));
+ EXPECT_STREQ(kAppListRlzString, base::UTF16ToUTF8(rlz).c_str());
+#endif // !defined(OS_IOS)
+
+ // Make sure cache is valid.
+ tracker_->set_assume_not_ui_thread(false);
+
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, base::UTF16ToUTF8(rlz).c_str());
+#if !defined(OS_IOS)
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(
+ RLZTracker::ChromeHomePage(), &rlz));
+ EXPECT_STREQ(kHomepageRlzString, base::UTF16ToUTF8(rlz).c_str());
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeAppList(), &rlz));
+ EXPECT_STREQ(kAppListRlzString, base::UTF16ToUTF8(rlz).c_str());
+#endif // !defined(OS_IOS)
+
+ // Perform ping.
+ tracker_->set_assume_not_ui_thread(true);
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+ ExpectRlzPingSent(true);
+
+ // Make sure cache is now updated.
+ tracker_->set_assume_not_ui_thread(false);
+
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeOmnibox(), &rlz));
+ EXPECT_STREQ(kNewOmniboxRlzString, base::UTF16ToUTF8(rlz).c_str());
+#if !defined(OS_IOS)
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(
+ RLZTracker::ChromeHomePage(), &rlz));
+ EXPECT_STREQ(kNewHomepageRlzString, base::UTF16ToUTF8(rlz).c_str());
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(RLZTracker::ChromeAppList(), &rlz));
+ EXPECT_STREQ(kNewAppListRlzString, base::UTF16ToUTF8(rlz).c_str());
+#endif // !defined(OS_IOS)
+}
+
+// TODO(thakis): Reactivation doesn't exist on Mac yet.
+TEST_F(RlzLibTest, ReactivationNonOrganicNonOrganic) {
+ SetReactivationBrand("REAC");
+
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+
+ ExpectRlzPingSent(true);
+ ExpectReactivationRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, ReactivationOrganicNonOrganic) {
+ SetMainBrand("GGLS");
+ SetReactivationBrand("REAC");
+
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+
+ ExpectRlzPingSent(false);
+ ExpectReactivationRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, ReactivationNonOrganicOrganic) {
+ SetMainBrand("TEST");
+ SetReactivationBrand("GGLS");
+
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+
+ ExpectRlzPingSent(true);
+ ExpectReactivationRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, ReactivationOrganicOrganic) {
+ SetMainBrand("GGLS");
+ SetReactivationBrand("GGRS");
+
+ TestRLZTracker::InitRlzDelayed(true, false, kDelay, true, true, false);
+ InvokeDelayedInit();
+
+ ExpectRlzPingSent(false);
+ ExpectReactivationRlzPingSent(false);
+}
+
+#if defined(OS_CHROMEOS)
+TEST_F(RlzLibTest, ClearRlzState) {
+ RLZTracker::RecordProductEvent(rlz_lib::CHROME, RLZTracker::ChromeOmnibox(),
+ rlz_lib::FIRST_SEARCH);
+
+ ExpectEventRecorded(OmniboxFirstSearch(), true);
+
+ RLZTracker::ClearRlzState();
+
+ ExpectEventRecorded(OmniboxFirstSearch(), false);
+}
+#endif // defined(OS_CHROMEOS)
+
+} // namespace rlz
diff --git a/components/rlz/rlz_tracker_win.cc b/components/rlz/rlz_tracker_win.cc
new file mode 100644
index 0000000..0e2055c
--- /dev/null
+++ b/components/rlz/rlz_tracker_win.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/rlz/rlz_tracker.h"
+
+namespace rlz {
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeOmnibox() {
+ return rlz_lib::CHROME_OMNIBOX;
+}
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeHomePage() {
+ return rlz_lib::CHROME_HOME_PAGE;
+}
+
+// static
+rlz_lib::AccessPoint RLZTracker::ChromeAppList() {
+ return rlz_lib::CHROME_APP_LIST;
+}
+
+} // namespace rlz