blob: 685692d41b69b2d7622732328a6508c9ea302e3b [file] [log] [blame]
[email protected]c83dd912010-04-06 18:50:511// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294//
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 <windows.h>
12#include <process.h>
13
[email protected]c83dd912010-04-06 18:50:5114#include <algorithm>
15
16#include "base/file_path.h"
initial.commit09911bf2008-07-26 23:55:2917#include "base/message_loop.h"
18#include "base/path_service.h"
[email protected]79bbdb52010-01-20 22:25:5719#include "base/string_util.h"
initial.commit09911bf2008-07-26 23:55:2920#include "base/task.h"
21#include "base/thread.h"
initial.commit09911bf2008-07-26 23:55:2922#include "chrome/browser/browser_process.h"
23#include "chrome/browser/profile.h"
24#include "chrome/browser/profile_manager.h"
[email protected]d54e03a52009-01-16 00:31:0425#include "chrome/browser/search_engines/template_url_model.h"
initial.commit09911bf2008-07-26 23:55:2926#include "chrome/common/chrome_paths.h"
27#include "chrome/common/env_vars.h"
[email protected]6199fe62009-05-22 03:18:3328#include "chrome/common/notification_registrar.h"
[email protected]2c3ea7b22008-10-27 19:34:4429#include "chrome/common/notification_service.h"
[email protected]6e93e522008-08-14 19:28:1730#include "chrome/installer/util/google_update_settings.h"
initial.commit09911bf2008-07-26 23:55:2931
32namespace {
33
34// The maximum length of an access points RLZ in wide chars.
35const DWORD kMaxRlzLength = 64;
36
initial.commit09911bf2008-07-26 23:55:2937enum {
38 ACCESS_VALUES_STALE, // Possibly new values available.
39 ACCESS_VALUES_FRESH // The cached values are current.
40};
41
42// Tracks if we have tried and succeeded sending the ping. This helps us
43// decide if we need to refresh the some cached strings.
44volatile int access_values_state = ACCESS_VALUES_STALE;
45
[email protected]1c262172010-06-10 15:25:4646bool SendFinancialPing(const std::wstring& brand, const std::wstring& lang,
47 const std::wstring& referral, bool exclude_id) {
48 rlz_lib::AccessPoint points[] = {rlz_lib::CHROME_OMNIBOX,
49 rlz_lib::CHROME_HOME_PAGE,
50 rlz_lib::NO_ACCESS_POINT};
51 std::string brand_ascii(WideToASCII(brand));
52 std::string lang_ascii(WideToASCII(lang));
53 std::string referral_ascii(WideToASCII(referral));
initial.commit09911bf2008-07-26 23:55:2954
[email protected]1c262172010-06-10 15:25:4655 return rlz_lib::SendFinancialPing(rlz_lib::CHROME, points, "chrome",
56 brand_ascii.c_str(), referral_ascii.c_str(),
57 lang_ascii.c_str(), exclude_id, NULL, true);
[email protected]2c3ea7b22008-10-27 19:34:4458}
59
[email protected]a675e532008-11-01 18:39:3760// This class leverages the AutocompleteEditModel notification to know when
[email protected]2c3ea7b22008-10-27 19:34:4461// the user first interacted with the omnibox and set a global accordingly.
62class OmniBoxUsageObserver : public NotificationObserver {
63 public:
64 OmniBoxUsageObserver() {
[email protected]6199fe62009-05-22 03:18:3365 registrar_.Add(this, NotificationType::OMNIBOX_OPENED_URL,
66 NotificationService::AllSources());
[email protected]2c3ea7b22008-10-27 19:34:4467 omnibox_used_ = false;
[email protected]46632132009-01-28 21:53:1668 DCHECK(!instance_);
69 instance_ = this;
[email protected]2c3ea7b22008-10-27 19:34:4470 }
71
72 virtual void Observe(NotificationType type,
73 const NotificationSource& source,
74 const NotificationDetails& details) {
75 // Try to record event now, else set the flag to try later when we
76 // attempt the ping.
[email protected]1c262172010-06-10 15:25:4677 if (!RLZTracker::RecordProductEvent(rlz_lib::CHROME,
78 rlz_lib::CHROME_OMNIBOX,
79 rlz_lib::FIRST_SEARCH))
[email protected]2c3ea7b22008-10-27 19:34:4480 omnibox_used_ = true;
81 delete this;
82 }
83
84 static bool used() {
85 return omnibox_used_;
86 }
87
[email protected]46632132009-01-28 21:53:1688 // Deletes the single instance of OmniBoxUsageObserver.
89 static void DeleteInstance() {
90 delete instance_;
91 }
92
[email protected]2c3ea7b22008-10-27 19:34:4493 private:
94 // Dtor is private so the object cannot be created on the stack.
95 ~OmniBoxUsageObserver() {
[email protected]46632132009-01-28 21:53:1696 instance_ = NULL;
[email protected]2c3ea7b22008-10-27 19:34:4497 }
98
99 static bool omnibox_used_;
[email protected]46632132009-01-28 21:53:16100
101 // There should only be one instance created at a time, and instance_ points
102 // to that instance.
103 // NOTE: this is only non-null for the amount of time it is needed. Once the
[email protected]6199fe62009-05-22 03:18:33104 // instance_ is no longer needed (or Chrome is exiting), this is null.
[email protected]46632132009-01-28 21:53:16105 static OmniBoxUsageObserver* instance_;
[email protected]6199fe62009-05-22 03:18:33106
107 NotificationRegistrar registrar_;
[email protected]2c3ea7b22008-10-27 19:34:44108};
109
110bool OmniBoxUsageObserver::omnibox_used_ = false;
[email protected]46632132009-01-28 21:53:16111OmniBoxUsageObserver* OmniBoxUsageObserver::instance_ = NULL;
[email protected]2c3ea7b22008-10-27 19:34:44112
113// This task is run in the file thread, so to not block it for a long time
114// we use a throwaway thread to do the blocking url request.
initial.commit09911bf2008-07-26 23:55:29115class DailyPingTask : public Task {
116 public:
117 virtual ~DailyPingTask() {
118 }
119 virtual void Run() {
120 // We use a transient thread because we have no guarantees about
121 // how long the RLZ lib can block us.
122 _beginthread(PingNow, 0, NULL);
123 }
124
125 private:
[email protected]f725b312010-04-01 22:46:35126 // Causes a ping to the server using WinInet.
initial.commit09911bf2008-07-26 23:55:29127 static void _cdecl PingNow(void*) {
128 std::wstring lang;
129 GoogleUpdateSettings::GetLanguage(&lang);
130 if (lang.empty())
131 lang = L"en";
132 std::wstring brand;
133 GoogleUpdateSettings::GetBrand(&brand);
[email protected]27845ca2008-12-05 19:36:03134 std::wstring referral;
135 GoogleUpdateSettings::GetReferral(&referral);
[email protected]1c262172010-06-10 15:25:46136 if (SendFinancialPing(brand, lang, referral, is_organic(brand))) {
initial.commit09911bf2008-07-26 23:55:29137 access_values_state = ACCESS_VALUES_STALE;
[email protected]2a0d872c2009-01-07 22:30:09138 GoogleUpdateSettings::ClearReferral();
139 }
[email protected]2c3ea7b22008-10-27 19:34:44140 }
141
142 // Organic brands all start with GG, such as GGCM.
[email protected]5984e472010-01-07 20:23:34143 static bool is_organic(const std::wstring& brand) {
[email protected]c83dd912010-04-06 18:50:51144 return (brand.size() < 2) ? false : (brand.substr(0, 2) == L"GG");
initial.commit09911bf2008-07-26 23:55:29145 }
146};
147
148// Performs late RLZ initialization and RLZ event recording for chrome.
149// This task needs to run on the UI thread.
150class DelayedInitTask : public Task {
151 public:
[email protected]1c262172010-06-10 15:25:46152 explicit DelayedInitTask(bool first_run)
153 : first_run_(first_run) {
initial.commit09911bf2008-07-26 23:55:29154 }
155 virtual ~DelayedInitTask() {
156 }
157 virtual void Run() {
[email protected]0aaaa252008-09-23 20:34:33158 // For non-interactive tests we don't do the rest of the initialization
159 // because sometimes the very act of loading the dll causes QEMU to crash.
[email protected]c83dd912010-04-06 18:50:51160 if (::GetEnvironmentVariableW(ASCIIToWide(env_vars::kHeadless).c_str(),
161 NULL, 0)) {
initial.commit09911bf2008-07-26 23:55:29162 return;
[email protected]c83dd912010-04-06 18:50:51163 }
[email protected]5984e472010-01-07 20:23:34164 // For organic brandcodes do not use rlz at all. Empty brandcode usually
165 // means a chromium install. This is ok.
166 std::wstring brand;
167 GoogleUpdateSettings::GetBrand(&brand);
[email protected]cbe7a162010-07-02 20:34:09168 if (GoogleUpdateSettings::IsOrganic(brand))
[email protected]5984e472010-01-07 20:23:34169 return;
170
[email protected]0aaaa252008-09-23 20:34:33171 // Do the initial event recording if is the first run or if we have an
172 // empty rlz which means we haven't got a chance to do it.
173 std::wstring omnibox_rlz;
[email protected]1c262172010-06-10 15:25:46174 RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &omnibox_rlz);
[email protected]0aaaa252008-09-23 20:34:33175
176 if (first_run_ || omnibox_rlz.empty()) {
initial.commit09911bf2008-07-26 23:55:29177 // Record the installation of chrome.
[email protected]1c262172010-06-10 15:25:46178 RLZTracker::RecordProductEvent(rlz_lib::CHROME,
179 rlz_lib::CHROME_OMNIBOX,
180 rlz_lib::INSTALL);
181 RLZTracker::RecordProductEvent(rlz_lib::CHROME,
182 rlz_lib::CHROME_HOME_PAGE,
183 rlz_lib::INSTALL);
initial.commit09911bf2008-07-26 23:55:29184 // Record if google is the initial search provider.
185 if (IsGoogleDefaultSearch()) {
[email protected]1c262172010-06-10 15:25:46186 RLZTracker::RecordProductEvent(rlz_lib::CHROME,
187 rlz_lib::CHROME_OMNIBOX,
188 rlz_lib::SET_TO_GOOGLE);
initial.commit09911bf2008-07-26 23:55:29189 }
[email protected]07203722010-02-18 23:51:39190 }
191 // Record first user interaction with the omnibox. We call this all the
192 // time but the rlz lib should ingore all but the first one.
193 if (OmniBoxUsageObserver::used()) {
[email protected]1c262172010-06-10 15:25:46194 RLZTracker::RecordProductEvent(rlz_lib::CHROME,
195 rlz_lib::CHROME_OMNIBOX,
196 rlz_lib::FIRST_SEARCH);
initial.commit09911bf2008-07-26 23:55:29197 }
198 // Schedule the daily RLZ ping.
[email protected]ab820df2008-08-26 05:55:10199 base::Thread* thread = g_browser_process->file_thread();
initial.commit09911bf2008-07-26 23:55:29200 if (thread)
201 thread->message_loop()->PostTask(FROM_HERE, new DailyPingTask());
202 }
203
204 private:
205 bool IsGoogleDefaultSearch() {
206 if (!g_browser_process)
207 return false;
[email protected]ef5ff1b2009-09-09 20:00:13208 FilePath user_data_dir;
initial.commit09911bf2008-07-26 23:55:29209 if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
210 return false;
211 ProfileManager* profile_manager = g_browser_process->profile_manager();
[email protected]ef5ff1b2009-09-09 20:00:13212 Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
initial.commit09911bf2008-07-26 23:55:29213 if (!profile)
214 return false;
215 const TemplateURL* url_template =
216 profile->GetTemplateURLModel()->GetDefaultSearchProvider();
217 if (!url_template)
218 return false;
[email protected]e9737f72009-10-05 19:36:55219 const TemplateURLRef* urlref = url_template->url();
220 if (!urlref)
221 return false;
222 return urlref->HasGoogleBaseURLs();
initial.commit09911bf2008-07-26 23:55:29223 }
224
initial.commit09911bf2008-07-26 23:55:29225 bool first_run_;
226 DISALLOW_IMPLICIT_CONSTRUCTORS(DelayedInitTask);
227};
228
229} // namespace
230
[email protected]1c262172010-06-10 15:25:46231bool RLZTracker::InitRlzDelayed(bool first_run, int delay) {
[email protected]cd1c7832009-07-06 20:19:29232 // Maximum and minimum delay we would allow to be set through master
233 // preferences. Somewhat arbitrary, may need to be adjusted in future.
234 const int kMaxDelay = 200 * 1000;
235 const int kMinDelay = 20 * 1000;
236
237 delay *= 1000;
238 delay = (delay < kMinDelay) ? kMinDelay : delay;
239 delay = (delay > kMaxDelay) ? kMaxDelay : delay;
240
[email protected]46632132009-01-28 21:53:16241 if (!OmniBoxUsageObserver::used())
242 new OmniBoxUsageObserver();
[email protected]cd1c7832009-07-06 20:19:29243
initial.commit09911bf2008-07-26 23:55:29244 // Schedule the delayed init items.
initial.commit09911bf2008-07-26 23:55:29245 MessageLoop::current()->PostDelayedTask(FROM_HERE,
[email protected]1c262172010-06-10 15:25:46246 new DelayedInitTask(first_run), delay);
initial.commit09911bf2008-07-26 23:55:29247 return true;
248}
249
[email protected]1c262172010-06-10 15:25:46250bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
251 rlz_lib::AccessPoint point,
252 rlz_lib::Event event_id) {
253 return rlz_lib::RecordProductEvent(product, point, event_id);
initial.commit09911bf2008-07-26 23:55:29254}
255
[email protected]1c262172010-06-10 15:25:46256bool RLZTracker::ClearAllProductEvents(rlz_lib::Product product) {
257 return rlz_lib::ClearAllProductEvents(product);
initial.commit09911bf2008-07-26 23:55:29258}
259
260// We implement caching of the answer of get_access_point() if the request
261// is for CHROME_OMNIBOX. If we had a successful ping, then we update the
262// cached value.
263
[email protected]1c262172010-06-10 15:25:46264bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point,
265 std::wstring* rlz) {
initial.commit09911bf2008-07-26 23:55:29266 static std::wstring cached_ommibox_rlz;
[email protected]1c262172010-06-10 15:25:46267 if ((rlz_lib::CHROME_OMNIBOX == point) &&
initial.commit09911bf2008-07-26 23:55:29268 (access_values_state == ACCESS_VALUES_FRESH)) {
269 *rlz = cached_ommibox_rlz;
270 return true;
271 }
[email protected]1c262172010-06-10 15:25:46272 char str_rlz[kMaxRlzLength + 1];
273 if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength, NULL))
initial.commit09911bf2008-07-26 23:55:29274 return false;
[email protected]1c262172010-06-10 15:25:46275 *rlz = ASCIIToWide(std::string(str_rlz));
276 if (rlz_lib::CHROME_OMNIBOX == point) {
initial.commit09911bf2008-07-26 23:55:29277 access_values_state = ACCESS_VALUES_FRESH;
[email protected]1c262172010-06-10 15:25:46278 cached_ommibox_rlz.assign(*rlz);
initial.commit09911bf2008-07-26 23:55:29279 }
initial.commit09911bf2008-07-26 23:55:29280 return true;
281}
282
[email protected]46632132009-01-28 21:53:16283// static
284void RLZTracker::CleanupRlz() {
285 OmniBoxUsageObserver::DeleteInstance();
286}