[email protected] | c83dd91 | 2010-04-06 18:50:51 | [diff] [blame] | 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 4 | // |
| 5 | // This code glues the RLZ library DLL with Chrome. It allows Chrome to work |
| 6 | // with or without the DLL being present. If the DLL is not present the |
| 7 | // functions do nothing and just return false. |
| 8 | |
| 9 | #include "chrome/browser/rlz/rlz.h" |
| 10 | |
| 11 | #include <windows.h> |
| 12 | #include <process.h> |
| 13 | |
[email protected] | c83dd91 | 2010-04-06 18:50:51 | [diff] [blame] | 14 | #include <algorithm> |
| 15 | |
| 16 | #include "base/file_path.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 17 | #include "base/message_loop.h" |
| 18 | #include "base/path_service.h" |
[email protected] | 79bbdb5 | 2010-01-20 22:25:57 | [diff] [blame] | 19 | #include "base/string_util.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 20 | #include "base/task.h" |
| 21 | #include "base/thread.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 22 | #include "chrome/browser/browser_process.h" |
| 23 | #include "chrome/browser/profile.h" |
| 24 | #include "chrome/browser/profile_manager.h" |
[email protected] | d54e03a5 | 2009-01-16 00:31:04 | [diff] [blame] | 25 | #include "chrome/browser/search_engines/template_url_model.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 26 | #include "chrome/common/chrome_paths.h" |
| 27 | #include "chrome/common/env_vars.h" |
[email protected] | 6199fe6 | 2009-05-22 03:18:33 | [diff] [blame] | 28 | #include "chrome/common/notification_registrar.h" |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 29 | #include "chrome/common/notification_service.h" |
[email protected] | 6e93e52 | 2008-08-14 19:28:17 | [diff] [blame] | 30 | #include "chrome/installer/util/google_update_settings.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 31 | |
| 32 | namespace { |
| 33 | |
| 34 | // The maximum length of an access points RLZ in wide chars. |
| 35 | const DWORD kMaxRlzLength = 64; |
| 36 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 37 | enum { |
| 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. |
| 44 | volatile int access_values_state = ACCESS_VALUES_STALE; |
| 45 | |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 46 | bool 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 54 | |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 55 | 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] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 58 | } |
| 59 | |
[email protected] | a675e53 | 2008-11-01 18:39:37 | [diff] [blame] | 60 | // This class leverages the AutocompleteEditModel notification to know when |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 61 | // the user first interacted with the omnibox and set a global accordingly. |
| 62 | class OmniBoxUsageObserver : public NotificationObserver { |
| 63 | public: |
| 64 | OmniBoxUsageObserver() { |
[email protected] | 6199fe6 | 2009-05-22 03:18:33 | [diff] [blame] | 65 | registrar_.Add(this, NotificationType::OMNIBOX_OPENED_URL, |
| 66 | NotificationService::AllSources()); |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 67 | omnibox_used_ = false; |
[email protected] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 68 | DCHECK(!instance_); |
| 69 | instance_ = this; |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 70 | } |
| 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] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 77 | if (!RLZTracker::RecordProductEvent(rlz_lib::CHROME, |
| 78 | rlz_lib::CHROME_OMNIBOX, |
| 79 | rlz_lib::FIRST_SEARCH)) |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 80 | omnibox_used_ = true; |
| 81 | delete this; |
| 82 | } |
| 83 | |
| 84 | static bool used() { |
| 85 | return omnibox_used_; |
| 86 | } |
| 87 | |
[email protected] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 88 | // Deletes the single instance of OmniBoxUsageObserver. |
| 89 | static void DeleteInstance() { |
| 90 | delete instance_; |
| 91 | } |
| 92 | |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 93 | private: |
| 94 | // Dtor is private so the object cannot be created on the stack. |
| 95 | ~OmniBoxUsageObserver() { |
[email protected] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 96 | instance_ = NULL; |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | static bool omnibox_used_; |
[email protected] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 100 | |
| 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] | 6199fe6 | 2009-05-22 03:18:33 | [diff] [blame] | 104 | // instance_ is no longer needed (or Chrome is exiting), this is null. |
[email protected] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 105 | static OmniBoxUsageObserver* instance_; |
[email protected] | 6199fe6 | 2009-05-22 03:18:33 | [diff] [blame] | 106 | |
| 107 | NotificationRegistrar registrar_; |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 108 | }; |
| 109 | |
| 110 | bool OmniBoxUsageObserver::omnibox_used_ = false; |
[email protected] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 111 | OmniBoxUsageObserver* OmniBoxUsageObserver::instance_ = NULL; |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 112 | |
| 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 115 | class 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] | f725b31 | 2010-04-01 22:46:35 | [diff] [blame] | 126 | // Causes a ping to the server using WinInet. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 127 | 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] | 27845ca | 2008-12-05 19:36:03 | [diff] [blame] | 134 | std::wstring referral; |
| 135 | GoogleUpdateSettings::GetReferral(&referral); |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 136 | if (SendFinancialPing(brand, lang, referral, is_organic(brand))) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 137 | access_values_state = ACCESS_VALUES_STALE; |
[email protected] | 2a0d872c | 2009-01-07 22:30:09 | [diff] [blame] | 138 | GoogleUpdateSettings::ClearReferral(); |
| 139 | } |
[email protected] | 2c3ea7b2 | 2008-10-27 19:34:44 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | // Organic brands all start with GG, such as GGCM. |
[email protected] | 5984e47 | 2010-01-07 20:23:34 | [diff] [blame] | 143 | static bool is_organic(const std::wstring& brand) { |
[email protected] | c83dd91 | 2010-04-06 18:50:51 | [diff] [blame] | 144 | return (brand.size() < 2) ? false : (brand.substr(0, 2) == L"GG"); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 145 | } |
| 146 | }; |
| 147 | |
| 148 | // Performs late RLZ initialization and RLZ event recording for chrome. |
| 149 | // This task needs to run on the UI thread. |
| 150 | class DelayedInitTask : public Task { |
| 151 | public: |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 152 | explicit DelayedInitTask(bool first_run) |
| 153 | : first_run_(first_run) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 154 | } |
| 155 | virtual ~DelayedInitTask() { |
| 156 | } |
| 157 | virtual void Run() { |
[email protected] | 0aaaa25 | 2008-09-23 20:34:33 | [diff] [blame] | 158 | // 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] | c83dd91 | 2010-04-06 18:50:51 | [diff] [blame] | 160 | if (::GetEnvironmentVariableW(ASCIIToWide(env_vars::kHeadless).c_str(), |
| 161 | NULL, 0)) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 162 | return; |
[email protected] | c83dd91 | 2010-04-06 18:50:51 | [diff] [blame] | 163 | } |
[email protected] | 5984e47 | 2010-01-07 20:23:34 | [diff] [blame] | 164 | // 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] | cbe7a16 | 2010-07-02 20:34:09 | [diff] [blame] | 168 | if (GoogleUpdateSettings::IsOrganic(brand)) |
[email protected] | 5984e47 | 2010-01-07 20:23:34 | [diff] [blame] | 169 | return; |
| 170 | |
[email protected] | 0aaaa25 | 2008-09-23 20:34:33 | [diff] [blame] | 171 | // 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] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 174 | RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &omnibox_rlz); |
[email protected] | 0aaaa25 | 2008-09-23 20:34:33 | [diff] [blame] | 175 | |
| 176 | if (first_run_ || omnibox_rlz.empty()) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 177 | // Record the installation of chrome. |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 178 | 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 184 | // Record if google is the initial search provider. |
| 185 | if (IsGoogleDefaultSearch()) { |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 186 | RLZTracker::RecordProductEvent(rlz_lib::CHROME, |
| 187 | rlz_lib::CHROME_OMNIBOX, |
| 188 | rlz_lib::SET_TO_GOOGLE); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 189 | } |
[email protected] | 0720372 | 2010-02-18 23:51:39 | [diff] [blame] | 190 | } |
| 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] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 194 | RLZTracker::RecordProductEvent(rlz_lib::CHROME, |
| 195 | rlz_lib::CHROME_OMNIBOX, |
| 196 | rlz_lib::FIRST_SEARCH); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 197 | } |
| 198 | // Schedule the daily RLZ ping. |
[email protected] | ab820df | 2008-08-26 05:55:10 | [diff] [blame] | 199 | base::Thread* thread = g_browser_process->file_thread(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 200 | 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] | ef5ff1b | 2009-09-09 20:00:13 | [diff] [blame] | 208 | FilePath user_data_dir; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 209 | if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) |
| 210 | return false; |
| 211 | ProfileManager* profile_manager = g_browser_process->profile_manager(); |
[email protected] | ef5ff1b | 2009-09-09 20:00:13 | [diff] [blame] | 212 | Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 213 | if (!profile) |
| 214 | return false; |
| 215 | const TemplateURL* url_template = |
| 216 | profile->GetTemplateURLModel()->GetDefaultSearchProvider(); |
| 217 | if (!url_template) |
| 218 | return false; |
[email protected] | e9737f7 | 2009-10-05 19:36:55 | [diff] [blame] | 219 | const TemplateURLRef* urlref = url_template->url(); |
| 220 | if (!urlref) |
| 221 | return false; |
| 222 | return urlref->HasGoogleBaseURLs(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 223 | } |
| 224 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 225 | bool first_run_; |
| 226 | DISALLOW_IMPLICIT_CONSTRUCTORS(DelayedInitTask); |
| 227 | }; |
| 228 | |
| 229 | } // namespace |
| 230 | |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 231 | bool RLZTracker::InitRlzDelayed(bool first_run, int delay) { |
[email protected] | cd1c783 | 2009-07-06 20:19:29 | [diff] [blame] | 232 | // 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] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 241 | if (!OmniBoxUsageObserver::used()) |
| 242 | new OmniBoxUsageObserver(); |
[email protected] | cd1c783 | 2009-07-06 20:19:29 | [diff] [blame] | 243 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 244 | // Schedule the delayed init items. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 245 | MessageLoop::current()->PostDelayedTask(FROM_HERE, |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 246 | new DelayedInitTask(first_run), delay); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 247 | return true; |
| 248 | } |
| 249 | |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 250 | bool 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 254 | } |
| 255 | |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 256 | bool RLZTracker::ClearAllProductEvents(rlz_lib::Product product) { |
| 257 | return rlz_lib::ClearAllProductEvents(product); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 258 | } |
| 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] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 264 | bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point, |
| 265 | std::wstring* rlz) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 266 | static std::wstring cached_ommibox_rlz; |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 267 | if ((rlz_lib::CHROME_OMNIBOX == point) && |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 268 | (access_values_state == ACCESS_VALUES_FRESH)) { |
| 269 | *rlz = cached_ommibox_rlz; |
| 270 | return true; |
| 271 | } |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 272 | char str_rlz[kMaxRlzLength + 1]; |
| 273 | if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength, NULL)) |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 274 | return false; |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 275 | *rlz = ASCIIToWide(std::string(str_rlz)); |
| 276 | if (rlz_lib::CHROME_OMNIBOX == point) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 277 | access_values_state = ACCESS_VALUES_FRESH; |
[email protected] | 1c26217 | 2010-06-10 15:25:46 | [diff] [blame] | 278 | cached_ommibox_rlz.assign(*rlz); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 279 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 280 | return true; |
| 281 | } |
| 282 | |
[email protected] | 4663213 | 2009-01-28 21:53:16 | [diff] [blame] | 283 | // static |
| 284 | void RLZTracker::CleanupRlz() { |
| 285 | OmniBoxUsageObserver::DeleteInstance(); |
| 286 | } |