diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/rlz | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/rlz')
-rw-r--r-- | chrome/browser/rlz/rlz.cc | 279 | ||||
-rw-r--r-- | chrome/browser/rlz/rlz.h | 129 | ||||
-rw-r--r-- | chrome/browser/rlz/rlz_unittest.cc | 92 |
3 files changed, 500 insertions, 0 deletions
diff --git a/chrome/browser/rlz/rlz.cc b/chrome/browser/rlz/rlz.cc new file mode 100644 index 0000000..00cee6d --- /dev/null +++ b/chrome/browser/rlz/rlz.cc @@ -0,0 +1,279 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// 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 "chrome/browser/rlz/rlz.h" + +#include <windows.h> +#include <process.h> + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/app/google_update_settings.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/browser/template_url_model.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/env_vars.h" +//#include "chrome/common/pref_names.h" +//#include "chrome/common/pref_service.h" + +namespace { + +// The maximum length of an access points RLZ in wide chars. +const DWORD kMaxRlzLength = 64; + +// The RLZ is a DLL that might not be present in the system. We load it +// as needed but never unload it. +volatile HMODULE rlz_dll = NULL; + + +enum { + ACCESS_VALUES_STALE, // Possibly new values available. + ACCESS_VALUES_FRESH // The cached values are current. +}; + +// Tracks if we have tried and succeeded sending the ping. This helps us +// decide if we need to refresh the some cached strings. +volatile int access_values_state = ACCESS_VALUES_STALE; + +extern "C" { +typedef bool (*RecordProductEventFn)(RLZTracker::Product product, + RLZTracker::AccessPoint point, + RLZTracker::Event event_id, + void* reserved); + +typedef bool (*GetAccessPointRlzFn)(RLZTracker::AccessPoint point, + wchar_t* rlz, + DWORD rlz_size, + void* reserved); + +typedef bool (*ClearAllProductEventsFn)(RLZTracker::Product product, + void* reserved); + +typedef bool (*SendFinancialPingFn)(RLZTracker::Product product, + RLZTracker::AccessPoint* access_points, + const WCHAR* product_signature, + const WCHAR* product_brand, + const WCHAR* product_id, + const WCHAR* product_lang, + void* reserved); +} // extern "C" + +RecordProductEventFn record_event = NULL; +GetAccessPointRlzFn get_access_point = NULL; +ClearAllProductEventsFn clear_all_events = NULL; +SendFinancialPingFn send_ping = NULL; + +template <typename FuncT> +FuncT WireExport(HMODULE module, const char* export_name) { + void* entry_point = ::GetProcAddress(module, export_name); + return (module)? reinterpret_cast<FuncT>(entry_point) : NULL; +} + +HMODULE LoadRLZLibraryInternal(int directory_key) { + std::wstring rlz_path; + if (!PathService::Get(directory_key, &rlz_path)) + return NULL; + file_util::AppendToPath(&rlz_path, L"rlz.dll"); + return ::LoadLibraryW(rlz_path.c_str()); +} + +bool LoadRLZLibrary(int directory_key) { + rlz_dll = LoadRLZLibraryInternal(directory_key); + if (!rlz_dll) { + // As a last resort we can try the EXE directory. + if (directory_key != base::DIR_EXE) + rlz_dll = LoadRLZLibraryInternal(base::DIR_EXE); + } + if (rlz_dll) { + record_event = + WireExport<RecordProductEventFn>(rlz_dll, "RecordProductEvent"); + get_access_point = + WireExport<GetAccessPointRlzFn>(rlz_dll, "GetAccessPointRlz"); + clear_all_events = + WireExport<ClearAllProductEventsFn>(rlz_dll, "ClearAllProductEvents"); + send_ping = + WireExport<SendFinancialPingFn>(rlz_dll, "SendFinancialPing"); + return true; + } + return false; +} + +class DailyPingTask : public Task { + public: + virtual ~DailyPingTask() { + } + virtual void Run() { + // We use a transient thread because we have no guarantees about + // how long the RLZ lib can block us. + _beginthread(PingNow, 0, NULL); + } + + private: + // Causes a ping to the server using WinInet. There is logic inside RLZ dll + // that throttles it to a maximum of one ping per day. + static void _cdecl PingNow(void*) { + std::wstring lang; + GoogleUpdateSettings::GetLanguage(&lang); + if (lang.empty()) + lang = L"en"; + std::wstring brand; + GoogleUpdateSettings::GetBrand(&brand); + if (brand.empty()) + brand = L"GGLD"; + if (RLZTracker::SendFinancialPing(RLZTracker::CHROME, L"chrome", + brand.c_str(), NULL, lang.c_str())) { + access_values_state = ACCESS_VALUES_STALE; + } + } +}; + +// Performs late RLZ initialization and RLZ event recording for chrome. +// This task needs to run on the UI thread. +class DelayedInitTask : public Task { + public: + explicit DelayedInitTask(int directory_key, bool first_run) + : directory_key_(directory_key), first_run_(first_run) { + } + virtual ~DelayedInitTask() { + } + virtual void Run() { + if (!LoadRLZLibrary(directory_key_)) + return; + // For non-interactive tests we don't do the rest of the initialization. + if (::GetEnvironmentVariableW(env_vars::kHeadless, NULL, 0)) + return; + if (first_run_) { + // Record the installation of chrome. + RLZTracker::RecordProductEvent(RLZTracker::CHROME, + RLZTracker::CHROME_OMNIBOX, + RLZTracker::INSTALL); + RLZTracker::RecordProductEvent(RLZTracker::CHROME, + RLZTracker::CHROME_HOME_PAGE, + RLZTracker::INSTALL); + // Record if google is the initial search provider. + if (IsGoogleDefaultSearch()) { + RLZTracker::RecordProductEvent(RLZTracker::CHROME, + RLZTracker::CHROME_OMNIBOX, + RLZTracker::SET_TO_GOOGLE); + } + } + // Schedule the daily RLZ ping. + Thread* thread = g_browser_process->file_thread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, new DailyPingTask()); + } + + private: + bool IsGoogleDefaultSearch() { + if (!g_browser_process) + return false; + std::wstring user_data_dir; + if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) + return false; + ProfileManager* profile_manager = g_browser_process->profile_manager(); + Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); + if (!profile) + return false; + const TemplateURL* url_template = + profile->GetTemplateURLModel()->GetDefaultSearchProvider(); + if (!url_template) + return false; + return url_template->url()->HasGoogleBaseURLs(); + } + + int directory_key_; + bool first_run_; + DISALLOW_IMPLICIT_CONSTRUCTORS(DelayedInitTask); +}; + +} // namespace + +bool RLZTracker::InitRlz(int directory_key) { + return LoadRLZLibrary(directory_key); +} + +bool RLZTracker::InitRlzDelayed(int directory_key, bool first_run) { + // Schedule the delayed init items. + const int kOneHundredSeconds = 100000; + MessageLoop::current()->PostDelayedTask(FROM_HERE, + new DelayedInitTask(directory_key, first_run), kOneHundredSeconds); + return true; +} + +bool RLZTracker::RecordProductEvent(Product product, AccessPoint point, + Event event) { + return (record_event) ? record_event(product, point, event, NULL) : false; +} + +bool RLZTracker::ClearAllProductEvents(Product product) { + return (clear_all_events) ? clear_all_events(product, NULL) : false; +} + +// We implement caching of the answer of get_access_point() if the request +// is for CHROME_OMNIBOX. If we had a successful ping, then we update the +// cached value. + +bool RLZTracker::GetAccessPointRlz(AccessPoint point, std::wstring* rlz) { + static std::wstring cached_ommibox_rlz; + if (!get_access_point) + return false; + if ((CHROME_OMNIBOX == point) && + (access_values_state == ACCESS_VALUES_FRESH)) { + *rlz = cached_ommibox_rlz; + return true; + } + wchar_t str_rlz[kMaxRlzLength]; + if (!get_access_point(point, str_rlz, kMaxRlzLength, NULL)) + return false; + if (CHROME_OMNIBOX == point) { + access_values_state = ACCESS_VALUES_FRESH; + cached_ommibox_rlz.assign(str_rlz); + } + *rlz = str_rlz; + return true; +} + +bool RLZTracker::SendFinancialPing(Product product, + const wchar_t* product_signature, + const wchar_t* product_brand, + const wchar_t* product_id, + const wchar_t* product_lang) { + AccessPoint points[] = {CHROME_OMNIBOX, CHROME_HOME_PAGE, NO_ACCESS_POINT}; + return (send_ping) ? send_ping(product, points, product_signature, + product_brand, product_id, product_lang, NULL) : false; +} diff --git a/chrome/browser/rlz/rlz.h b/chrome/browser/rlz/rlz.h new file mode 100644 index 0000000..a05b141 --- /dev/null +++ b/chrome/browser/rlz/rlz.h @@ -0,0 +1,129 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_BROWSER_RLZ_RLZ_H__ +#define CHROME_BROWSER_RLZ_RLZ_H__ + +#include <string> + +#include "base/basictypes.h" + +// RLZ is a library which is used to measure partner distribution deals. +// Its job is to record certain lifetime events in the registry and to send +// them encoded as a compact string at most once per day. 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 to rlz.dll which can be +// removed of the system with no adverse effects on chrome. + +class RLZTracker { + + public: + // An Access Point offers a way to search using Google. Other products + // have specific entries here so do not remove the reserved access points. + enum AccessPoint { + NO_ACCESS_POINT = 0, + RESERVED_ACCESS_POINT_01, + RESERVED_ACCESS_POINT_02, + RESERVED_ACCESS_POINT_03, + RESERVED_ACCESS_POINT_04, + RESERVED_ACCESS_POINT_05, + RESERVED_ACCESS_POINT_06, + RESERVED_ACCESS_POINT_07, + RESERVED_ACCESS_POINT_08, + CHROME_OMNIBOX, + CHROME_HOME_PAGE, + LAST_ACCESS_POINT + }; + + // A product is an entity which wants to gets credit for setting an access + // point. Currently only the browser itself is supported but installed apps + // could have their own entry here. + enum Product { + RESERVED_PRODUCT_01 = 1, + RESERVED_PRODUCT_02, + RESERVED_PRODUCT_03, + RESERVED_PRODUCT_04, + CHROME, + LAST_PRODUCT + }; + + // Life cycle events. Some of them are applicable to all access points. + enum Event { + INVALID_EVENT = 0, + INSTALL = 1, + SET_TO_GOOGLE, + FIRST_SEARCH, + REPORT_RLS, + LAST_EVENT + }; + + // Initializes the RLZ library services. 'directory_key' indicates the base + // directory the RLZ dll would be found. For example base::DIR_CURRENT. + // If the RLZ dll is not found in this directory the code falls back to try + // to load it from base::DIR_EXE. + // Returns false if the dll could not be loaded and initialized. + // This function is intended primarily for testing. + static bool InitRlz(int directory_key); + + // Like InitRlz() this function the RLZ library services for use in chrome. + // Besides binding the dll, it schedules a delayed task that performs the + // daily ping and registers the some events when 'first-run' is true. + static bool InitRlzDelayed(int directory_key, bool first_run); + + // 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(Product product, AccessPoint point, + Event event_id); + + // Get 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(AccessPoint point, std::wstring* rlz); + + // Clear all events reported by this product. In Chrome this will be called + // when it is un-installed. + static bool ClearAllProductEvents(Product product); + + // Called once a day to report the events to the server and to get updated + // RLZs for the different access points. This call uses Wininet to perform + // the http request. Returns true if the transaction succeeded and returns + // false if the transaction failed OR if a previous ping request was done + // in the last 24 hours. + static bool SendFinancialPing(Product product, + const wchar_t* product_signature, + const wchar_t* product_brand, + const wchar_t* product_id, + const wchar_t* product_lang); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(RLZTracker); +}; + +#endif // CHROME_BROWSER_RLZ_RLZ_H__ diff --git a/chrome/browser/rlz/rlz_unittest.cc b/chrome/browser/rlz/rlz_unittest.cc new file mode 100644 index 0000000..c8005c5 --- /dev/null +++ b/chrome/browser/rlz/rlz_unittest.cc @@ -0,0 +1,92 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/rlz/rlz.h" + +#include "base/registry.h" +#include "base/path_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +// Gets rid of registry leftovers from testing. Returns false if there +// is nothing to clean. +bool CleanValue(const wchar_t* key_name, const wchar_t* value) { + RegKey key; + if (!key.Open(HKEY_CURRENT_USER, key_name, KEY_READ | KEY_WRITE)) + return false; + EXPECT_TRUE(key.DeleteValue(value)); + return true; +} + +// The chrome events RLZ key lives here. +const wchar_t kKeyName[] = L"Software\\Google\\Common\\Rlz\\Events\\C"; + +} // namespace + +TEST(RlzLibTest, RecordProductEvent) { + if (!RLZTracker::InitRlz(base::DIR_EXE)) + return; + + DWORD recorded_value = 0; + EXPECT_TRUE(RLZTracker::RecordProductEvent(RLZTracker::CHROME, + RLZTracker::CHROME_OMNIBOX, RLZTracker::FIRST_SEARCH)); + const wchar_t kEvent1[] = L"C1F"; + RegKey key1; + EXPECT_TRUE(key1.Open(HKEY_CURRENT_USER, kKeyName, KEY_READ)); + EXPECT_TRUE(key1.ReadValueDW(kEvent1, &recorded_value)); + EXPECT_EQ(1, recorded_value); + EXPECT_TRUE(CleanValue(kKeyName, kEvent1)); + + EXPECT_TRUE(RLZTracker::RecordProductEvent(RLZTracker::CHROME, + RLZTracker::CHROME_HOME_PAGE, RLZTracker::SET_TO_GOOGLE)); + const wchar_t kEvent2[] = L"C2S"; + RegKey key2; + EXPECT_TRUE(key2.Open(HKEY_CURRENT_USER, kKeyName, KEY_READ)); + DWORD value = 0; + EXPECT_TRUE(key2.ReadValueDW(kEvent2, &recorded_value)); + EXPECT_EQ(1, recorded_value); + EXPECT_TRUE(CleanValue(kKeyName, kEvent2)); +} + +TEST(RlzLibTest, CleanProductEvents) { + if (!RLZTracker::InitRlz(base::DIR_EXE)) + return; + + DWORD recorded_value = 0; + EXPECT_TRUE(RLZTracker::RecordProductEvent(RLZTracker::CHROME, + RLZTracker::CHROME_OMNIBOX, RLZTracker::FIRST_SEARCH)); + const wchar_t kEvent1[] = L"C1F"; + RegKey key1; + EXPECT_TRUE(key1.Open(HKEY_CURRENT_USER, kKeyName, KEY_READ)); + EXPECT_TRUE(key1.ReadValueDW(kEvent1, &recorded_value)); + EXPECT_EQ(1, recorded_value); + + EXPECT_TRUE(RLZTracker::ClearAllProductEvents(RLZTracker::CHROME)); + EXPECT_FALSE(CleanValue(kKeyName, kEvent1)); +} |