summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome_frame/bho.cc27
-rw-r--r--chrome_frame/bho.h6
-rw-r--r--chrome_frame/chrome_active_document.cc4
-rw-r--r--chrome_frame/chrome_frame.gyp7
-rw-r--r--chrome_frame/chrome_frame_automation.cc17
-rw-r--r--chrome_frame/crash_metrics.cc92
-rw-r--r--chrome_frame/crash_metrics.h65
-rw-r--r--chrome_frame/crash_reporting/crash_report.cc3
-rw-r--r--chrome_frame/metrics_service.cc467
-rw-r--r--chrome_frame/metrics_service.h146
-rw-r--r--chrome_frame/urlmon_url_request.cc25
-rw-r--r--chrome_frame/utils.cc22
-rw-r--r--chrome_frame/utils.h8
13 files changed, 860 insertions, 29 deletions
diff --git a/chrome_frame/bho.cc b/chrome_frame/bho.cc
index 6cdb0a6..3033b9a 100644
--- a/chrome_frame/bho.cc
+++ b/chrome_frame/bho.cc
@@ -14,8 +14,10 @@
#include "base/scoped_variant_win.h"
#include "base/string_util.h"
#include "chrome_tab.h" // NOLINT
+#include "chrome_frame/crash_metrics.h"
#include "chrome_frame/extra_system_apis.h"
#include "chrome_frame/http_negotiate.h"
+#include "chrome_frame/metrics_service.h"
#include "chrome_frame/protocol_sink_wrap.h"
#include "chrome_frame/urlmon_moniker.h"
#include "chrome_frame/utils.h"
@@ -48,6 +50,13 @@ _ATL_FUNC_INFO Bho::kNavigateComplete2Info = {
}
};
+_ATL_FUNC_INFO Bho::kDocumentCompleteInfo = {
+ CC_STDCALL, VT_EMPTY, 2, {
+ VT_DISPATCH,
+ VT_VARIANT | VT_BYREF
+ }
+};
+
Bho::Bho() {
}
@@ -84,6 +93,7 @@ STDMETHODIMP Bho::SetSite(IUnknown* site) {
// information for a URL.
AddRef();
RegisterThreadInstance();
+ MetricsService::Start();
} else {
UnregisterThreadInstance();
Release();
@@ -134,6 +144,23 @@ STDMETHODIMP_(void) Bho::NavigateComplete2(IDispatch* dispatch, VARIANT* url) {
DLOG(INFO) << __FUNCTION__;
}
+STDMETHODIMP_(void) Bho::DocumentComplete(IDispatch* dispatch, VARIANT* url) {
+ DLOG(INFO) << __FUNCTION__;
+
+ ScopedComPtr<IWebBrowser2> web_browser2;
+ if (dispatch)
+ web_browser2.QueryFrom(dispatch);
+
+ if (web_browser2) {
+ VARIANT_BOOL is_top_level = VARIANT_FALSE;
+ web_browser2->get_TopLevelContainer(&is_top_level);
+ if (is_top_level) {
+ CrashMetricsReporter::GetInstance()->IncrementMetric(
+ CrashMetricsReporter::NAVIGATION_COUNT);
+ }
+ }
+}
+
namespace {
// See comments in Bho::OnHttpEquiv for details.
diff --git a/chrome_frame/bho.h b/chrome_frame/bho.h
index 466dcdb..bbf6202 100644
--- a/chrome_frame/bho.h
+++ b/chrome_frame/bho.h
@@ -78,6 +78,8 @@ BEGIN_SINK_MAP(Bho)
BeforeNavigate2, &kBeforeNavigate2Info)
SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2,
NavigateComplete2, &kNavigateComplete2Info)
+ SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE,
+ DocumentComplete, &kDocumentCompleteInfo)
END_SINK_MAP()
Bho();
@@ -87,10 +89,13 @@ END_SINK_MAP()
// IObjectWithSite
STDMETHODIMP SetSite(IUnknown* site);
+
+ // WebBrowser2 event sinks.
STDMETHOD(BeforeNavigate2)(IDispatch* dispatch, VARIANT* url, VARIANT* flags,
VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers,
VARIANT_BOOL* cancel);
STDMETHOD_(void, NavigateComplete2)(IDispatch* dispatch, VARIANT* url);
+ STDMETHOD_(void, DocumentComplete)(IDispatch* dispatch, VARIANT* url);
// mshtml sends an IOleCommandTarget::Exec of OLECMDID_HTTPEQUIV
// (and OLECMDID_HTTPEQUIV_DONE) as soon as it parses a meta tag.
@@ -115,6 +120,7 @@ END_SINK_MAP()
static _ATL_FUNC_INFO kBeforeNavigate2Info;
static _ATL_FUNC_INFO kNavigateComplete2Info;
+ static _ATL_FUNC_INFO kDocumentCompleteInfo;
};
#endif // CHROME_FRAME_BHO_H_
diff --git a/chrome_frame/chrome_active_document.cc b/chrome_frame/chrome_active_document.cc
index fda2d5b..70b1656 100644
--- a/chrome_frame/chrome_active_document.cc
+++ b/chrome_frame/chrome_active_document.cc
@@ -38,6 +38,7 @@
#include "chrome/test/automation/tab_proxy.h"
#include "chrome_frame/bho.h"
#include "chrome_frame/bind_context_info.h"
+#include "chrome_frame/crash_metrics.h"
#include "chrome_frame/utils.h"
const wchar_t kChromeAttachExternalTabPrefix[] = L"attach_external_tab";
@@ -609,6 +610,9 @@ void ChromeActiveDocument::OnDidNavigate(int tab_handle,
", Type: " << nav_info.navigation_type << ", Relative Offset: " <<
nav_info.relative_offset << ", Index: " << nav_info.navigation_index;
+ CrashMetricsReporter::GetInstance()->IncrementMetric(
+ CrashMetricsReporter::CHROME_FRAME_NAVIGATION_COUNT);
+
// This could be NULL if the active document instance is being destroyed.
if (!m_spInPlaceSite) {
DLOG(INFO) << __FUNCTION__ << "m_spInPlaceSite is NULL. Returning";
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp
index 54c5840..3bb4fa9 100644
--- a/chrome_frame/chrome_frame.gyp
+++ b/chrome_frame/chrome_frame.gyp
@@ -588,7 +588,8 @@
'../chrome/chrome.gyp:common',
'../chrome/chrome.gyp:utility',
'../build/temp_gyp/googleurl.gyp:googleurl',
-
+ '../third_party/libxml/libxml.gyp:libxml',
+ '../third_party/bzip2/bzip2.gyp:bzip2',
],
'sources': [
'bho.cc',
@@ -620,6 +621,8 @@
'com_message_event.h',
'com_type_info_holder.cc',
'com_type_info_holder.h',
+ 'crash_metrics.cc',
+ 'crash_metrics.h',
'delete_chrome_history.cc',
'delete_chrome_history.h',
'exception_barrier.cc',
@@ -633,6 +636,8 @@
'http_negotiate.h',
'iids.cc',
'in_place_menu.h',
+ 'metrics_service.cc',
+ 'metrics_service.h',
'module_utils.cc',
'module_utils.h',
'ole_document_impl.h',
diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc
index ee1ed5e..9deaeb5 100644
--- a/chrome_frame/chrome_frame_automation.cc
+++ b/chrome_frame/chrome_frame_automation.cc
@@ -24,6 +24,7 @@
#include "chrome/test/automation/tab_proxy.h"
#include "chrome_frame/chrome_launcher_utils.h"
#include "chrome_frame/custom_sync_call_context.h"
+#include "chrome_frame/crash_metrics.h"
#include "chrome_frame/utils.h"
#ifdef NDEBUG
@@ -250,9 +251,13 @@ void ProxyFactory::GetAutomationServer(
entry->thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
&ProxyFactory::CreateProxy, entry, params, delegate));
- entry->thread->message_loop()->PostDelayedTask(FROM_HERE,
- NewRunnableMethod(this, &ProxyFactory::SendUMAData, entry),
- uma_send_interval_);
+ // IE uses the chrome frame provided UMA data uploading scheme. NPAPI
+ // continues to use Chrome to upload UMA data.
+ if (!CrashMetricsReporter::GetInstance()->active()) {
+ entry->thread->message_loop()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(this, &ProxyFactory::SendUMAData, entry),
+ uma_send_interval_);
+ }
}
void ProxyFactory::CreateProxy(ProxyFactory::ProxyCacheEntry* entry,
@@ -420,6 +425,12 @@ void ProxyFactory::ReleaseProxy(ProxyCacheEntry* entry,
Singleton<ProxyFactory> g_proxy_factory;
void ProxyFactory::SendUMAData(ProxyCacheEntry* proxy_entry) {
+ // IE uses the chrome frame provided UMA data uploading scheme. NPAPI
+ // continues to use Chrome to upload UMA data.
+ if (CrashMetricsReporter::GetInstance()->active()) {
+ return;
+ }
+
if (!proxy_entry) {
NOTREACHED() << __FUNCTION__ << " Invalid proxy entry";
return;
diff --git a/chrome_frame/crash_metrics.cc b/chrome_frame/crash_metrics.cc
new file mode 100644
index 0000000..8b1a97a
--- /dev/null
+++ b/chrome_frame/crash_metrics.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2010 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 "chrome_frame/crash_metrics.h"
+
+#include "base/histogram.h"
+#include "base/registry.h"
+#include "chrome_frame/utils.h"
+
+static const wchar_t kChromeFrameMetricsKey[] =
+ L"Software\\Google\\ChromeFrameMetrics";
+
+base::LazyInstance<CrashMetricsReporter>
+ g_crash_metrics_instance_(base::LINKER_INITIALIZED);
+
+wchar_t* CrashMetricsReporter::g_metric_names[LAST_METRIC] = {
+ L"navigationcount",
+ L"crashcount",
+ L"chrome_frame_navigationcount",
+ L"sessionid",
+};
+
+CrashMetricsReporter* CrashMetricsReporter::GetInstance() {
+ return &g_crash_metrics_instance_.Get();
+}
+
+bool CrashMetricsReporter::SetMetric(Metric metric, int value) {
+ DCHECK(metric >= NAVIGATION_COUNT && metric <= LAST_METRIC);
+
+ RegKey metric_key;
+ if (metric_key.Create(HKEY_CURRENT_USER, kChromeFrameMetricsKey,
+ KEY_SET_VALUE)) {
+ if (metric_key.WriteValue(g_metric_names[metric], value)) {
+ return true;
+ } else {
+ DLOG(ERROR) << "Failed to read ChromeFrame metric:"
+ << g_metric_names[metric];
+ }
+ } else {
+ DLOG(ERROR) << "Failed to create ChromeFrame metrics key";
+ }
+ return false;
+}
+
+int CrashMetricsReporter::GetMetric(Metric metric) {
+ DCHECK(metric >= NAVIGATION_COUNT && metric <= LAST_METRIC);
+
+ int ret = 0;
+ RegKey metric_key;
+ if (metric_key.Open(HKEY_CURRENT_USER, kChromeFrameMetricsKey,
+ KEY_QUERY_VALUE)) {
+ int value = 0;
+ if (metric_key.ReadValueDW(g_metric_names[metric],
+ reinterpret_cast<DWORD*>(&value))) {
+ ret = value;
+ }
+ }
+ return ret;
+}
+
+int CrashMetricsReporter::IncrementMetric(Metric metric) {
+ DCHECK(metric >= NAVIGATION_COUNT && metric <= LAST_METRIC);
+ int metric_value = GetMetric(metric);
+ metric_value++;
+ SetMetric(metric, metric_value);
+ return metric_value;
+}
+
+void CrashMetricsReporter::RecordCrashMetrics() {
+ int navigation_count = GetMetric(NAVIGATION_COUNT);
+ if (navigation_count > 0) {
+ THREAD_SAFE_UMA_HISTOGRAM_COUNTS("ChromeFrame.HostNavigationCount",
+ navigation_count);
+ SetMetric(NAVIGATION_COUNT, 0);
+ }
+
+ int chrome_frame_navigation_count = GetMetric(CHROME_FRAME_NAVIGATION_COUNT);
+ if (chrome_frame_navigation_count > 0) {
+ THREAD_SAFE_UMA_HISTOGRAM_COUNTS("ChromeFrame.CFNavigationCount",
+ chrome_frame_navigation_count);
+ SetMetric(CHROME_FRAME_NAVIGATION_COUNT, 0);
+ }
+
+ int crash_count = GetMetric(CRASH_COUNT);
+ if (crash_count > 0) {
+ THREAD_SAFE_UMA_HISTOGRAM_COUNTS("ChromeFrame.HostCrashCount",
+ crash_count);
+ SetMetric(CRASH_COUNT, 0);
+ }
+}
+
diff --git a/chrome_frame/crash_metrics.h b/chrome_frame/crash_metrics.h
new file mode 100644
index 0000000..f0812c6
--- /dev/null
+++ b/chrome_frame/crash_metrics.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 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 file defines a service that collects information about the user
+// experience in order to help improve future versions of the app.
+
+#ifndef CHROME_FRAME_CRASH_METRICS_H_
+#define CHROME_FRAME_CRASH_METRICS_H_
+
+#include "base/basictypes.h"
+#include "base/lazy_instance.h"
+#include "base/thread_local.h"
+
+// This class provides functionality to track counters like successful page
+// loads in the host browser, total number of crashes, page loads in chrome
+// frame.
+class CrashMetricsReporter {
+ public:
+ enum Metric {
+ NAVIGATION_COUNT,
+ CRASH_COUNT,
+ CHROME_FRAME_NAVIGATION_COUNT,
+ SESSION_ID,
+ LAST_METRIC,
+ };
+ // Returns the global instance of this class.
+ static CrashMetricsReporter* GetInstance();
+
+ // The following function pair return/set/increment the specified user
+ // metrics value from the registry. These values are set under the
+ // following key:-
+ // HKCU\Software\\Google\\ChromeFrame\\UserMetrics
+ int GetMetric(Metric metric);
+ bool SetMetric(Metric metric, int value);
+ int IncrementMetric(Metric metric);
+
+ // Records the crash metrics counters like navigation count, crash count.
+ void RecordCrashMetrics();
+
+ bool active() const {
+ return active_;
+ }
+
+ void set_active(bool active) {
+ active_ = active;
+ }
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<CrashMetricsReporter>;
+
+ CrashMetricsReporter()
+ : active_(false) {}
+ virtual ~CrashMetricsReporter() {}
+
+ // Indicates whether the crash metrics reporter instance is active.
+ bool active_;
+
+ static wchar_t* g_metric_names[LAST_METRIC];
+
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporter);
+};
+
+#endif // CHROME_FRAME_CRASH_METRICS_H_
+
diff --git a/chrome_frame/crash_reporting/crash_report.cc b/chrome_frame/crash_reporting/crash_report.cc
index 4589bab..f821320 100644
--- a/chrome_frame/crash_reporting/crash_report.cc
+++ b/chrome_frame/crash_reporting/crash_report.cc
@@ -9,6 +9,7 @@
#include "base/basictypes.h"
#include "base/lock.h"
#include "breakpad/src/client/windows/handler/exception_handler.h"
+#include "chrome_frame/crash_metrics.h"
// TODO(joshia): factor out common code with chrome used for crash reporting
const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
@@ -172,6 +173,8 @@ bool ShutdownVectoredCrashReporting() {
bool WriteMinidumpForException(EXCEPTION_POINTERS* p) {
AutoLock lock(g_breakpad_lock);
+ CrashMetricsReporter::GetInstance()->IncrementMetric(
+ CrashMetricsReporter::CRASH_COUNT);
bool success = false;
if (g_breakpad) {
success = g_breakpad->WriteMinidumpForException(p);
diff --git a/chrome_frame/metrics_service.cc b/chrome_frame/metrics_service.cc
new file mode 100644
index 0000000..57fdf6e
--- /dev/null
+++ b/chrome_frame/metrics_service.cc
@@ -0,0 +1,467 @@
+// Copyright (c) 2010 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.
+
+
+//------------------------------------------------------------------------------
+// Description of the life cycle of a instance of MetricsService.
+//
+// OVERVIEW
+//
+// A MetricsService instance is created at ChromeFrame startup in
+// the IE process. It is the central controller for the UMA log data.
+// Its major job is to manage logs, prepare them for transmission.
+// Currently only histogram data is tracked in log. When MetricsService
+// prepares log for submission it snapshots the current stats of histograms,
+// translates log to XML. Transmission includes submitting a compressed log
+// as data in a URL-get, and is performed using functionality provided by
+// Urlmon
+// The actual transmission is performed using a windows timer procedure which
+// basically means that the thread on which the MetricsService object is
+// instantiated needs a message pump. Also on IE7 where every tab is created
+// on its own thread we would have a case where the timer procedures can
+// compete for sending histograms.
+//
+// When preparing log for submission we acquire a list of all local histograms
+// that have been flagged for upload to the UMA server.
+//
+// When ChromeFrame shuts down, there will typically be a fragment of an ongoing
+// log that has not yet been transmitted. Currently this data is ignored.
+//
+// With the above overview, we can now describe the state machine's various
+// stats, based on the State enum specified in the state_ member. Those states
+// are:
+//
+// INITIALIZED, // Constructor was called.
+// ACTIVE, // Accumalating log data.
+// STOPPED, // Service has stopped.
+//
+//-----------------------------------------------------------------------------
+
+#include "chrome_frame/metrics_service.h"
+
+#include <windows.h>
+#include <objbase.h>
+
+#if defined(USE_SYSTEM_LIBBZ2)
+#include <bzlib.h>
+#else
+#include "third_party/bzip2/bzlib.h"
+#endif
+
+#include "base/file_version_info.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/app/chrome_version_info.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/chrome_frame_distribution.h"
+#include "chrome/installer/util/google_update_settings.h"
+#include "chrome_frame/bind_status_callback_impl.h"
+#include "chrome_frame/crash_metrics.h"
+#include "chrome_frame/utils.h"
+#include "net/base/upload_data.h"
+#include "chrome_frame/urlmon_bind_status_callback.h"
+
+using base::Time;
+using base::TimeDelta;
+
+static const char kMetricsType[] =
+ "Content-Type: application/vnd.mozilla.metrics.bz2\r\n";
+
+// The delay, in seconds, after startup before sending the first log message.
+static const int kInitialInterlogDuration = 60; // one minute
+
+static const int kUMAUploadTimeoutMilliSeconds = 30000;
+
+base::LazyInstance<base::ThreadLocalPointer<MetricsService> >
+ MetricsService::g_metrics_instance_(base::LINKER_INITIALIZED);
+
+extern base::LazyInstance<StatisticsRecorder> g_statistics_recorder_;
+
+// This class provides functionality to upload the ChromeFrame UMA data to the
+// server. An instance of this class is created whenever we have data to be
+// uploaded to the server.
+class ChromeFrameMetricsDataUploader : public BSCBImpl {
+ public:
+ ChromeFrameMetricsDataUploader()
+ : cache_stream_(NULL),
+ upload_data_size_(0) {
+ DLOG(INFO) << __FUNCTION__;
+ }
+
+ ~ChromeFrameMetricsDataUploader() {
+ DLOG(INFO) << __FUNCTION__;
+ }
+
+ static HRESULT ChromeFrameMetricsDataUploader::UploadDataHelper(
+ const std::string& upload_data) {
+ CComObject<ChromeFrameMetricsDataUploader>* data_uploader = NULL;
+ CComObject<ChromeFrameMetricsDataUploader>::CreateInstance(&data_uploader);
+ DCHECK(data_uploader != NULL);
+
+ data_uploader->AddRef();
+ HRESULT hr = data_uploader->UploadData(upload_data);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to initialize ChromeFrame UMA data uploader: Err"
+ << hr;
+ }
+ data_uploader->Release();
+ return hr;
+ }
+
+ HRESULT UploadData(const std::string& upload_data) {
+ if (upload_data.empty()) {
+ NOTREACHED() << "Invalid upload data";
+ return E_INVALIDARG;
+ }
+
+ DCHECK(cache_stream_.get() == NULL);
+
+ upload_data_size_ = upload_data.size() + 1;
+
+ HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, cache_stream_.Receive());
+ if (FAILED(hr)) {
+ NOTREACHED() << "Failed to create stream. Error:"
+ << hr;
+ return hr;
+ }
+
+ DCHECK(cache_stream_.get());
+
+ unsigned long written = 0;
+ cache_stream_->Write(upload_data.c_str(), upload_data_size_, &written);
+ DCHECK(written == upload_data_size_);
+
+ RewindStream(cache_stream_);
+
+ BrowserDistribution* dist = ChromeFrameDistribution::GetDistribution();
+ server_url_ = dist->GetStatsServerURL();
+ DCHECK(!server_url_.empty());
+
+ hr = CreateURLMoniker(NULL, server_url_.c_str(),
+ upload_moniker_.Receive());
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to create url moniker for url:"
+ << server_url_.c_str()
+ << " Error:"
+ << hr;
+ } else {
+ ScopedComPtr<IBindCtx> context;
+ hr = CreateAsyncBindCtx(0, this, NULL, context.Receive());
+ DCHECK(SUCCEEDED(hr));
+ DCHECK(context);
+
+ ScopedComPtr<IStream> stream;
+ hr = upload_moniker_->BindToStorage(
+ context, NULL, IID_IStream,
+ reinterpret_cast<void**>(stream.Receive()));
+ if (FAILED(hr)) {
+ NOTREACHED();
+ DLOG(ERROR) << "Failed to bind to upload data moniker. Error:"
+ << hr;
+ }
+ }
+ return hr;
+ }
+
+ STDMETHOD(BeginningTransaction)(LPCWSTR url, LPCWSTR headers, DWORD reserved,
+ LPWSTR* additional_headers) {
+ std::string new_headers;
+ new_headers = StringPrintf("Content-Length: %s\r\n",
+ Int64ToString(upload_data_size_).c_str());
+ new_headers += kMetricsType;
+
+ *additional_headers = reinterpret_cast<wchar_t*>(
+ CoTaskMemAlloc((new_headers.size() + 1) * sizeof(wchar_t)));
+
+ lstrcpynW(*additional_headers, ASCIIToWide(new_headers).c_str(),
+ new_headers.size());
+ return BSCBImpl::BeginningTransaction(url, headers, reserved,
+ additional_headers);
+ }
+
+ STDMETHOD(GetBindInfo)(DWORD* bind_flags, BINDINFO* bind_info) {
+ if ((bind_info == NULL) || (bind_info->cbSize == 0) ||
+ (bind_flags == NULL))
+ return E_INVALIDARG;
+
+ *bind_flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
+ // Bypass caching proxies on POSTs and PUTs and avoid writing responses to
+ // these requests to the browser's cache
+ *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_PRAGMA_NO_CACHE;
+
+ DCHECK(cache_stream_.get());
+
+ // Initialize the STGMEDIUM.
+ memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM));
+ bind_info->grfBindInfoF = 0;
+ bind_info->szCustomVerb = NULL;
+ bind_info->dwBindVerb = BINDVERB_POST;
+ bind_info->stgmedData.tymed = TYMED_ISTREAM;
+ bind_info->stgmedData.pstm = cache_stream_.get();
+ bind_info->stgmedData.pstm->AddRef();
+ return BSCBImpl::GetBindInfo(bind_flags, bind_info);
+ }
+
+ STDMETHOD(OnResponse)(DWORD response_code, LPCWSTR response_headers,
+ LPCWSTR request_headers, LPWSTR* additional_headers) {
+ DLOG(INFO) << __FUNCTION__ << " headers: \n" << response_headers;
+ return BSCBImpl::OnResponse(response_code, response_headers,
+ request_headers, additional_headers);
+ }
+
+ private:
+ std::wstring server_url_;
+ size_t upload_data_size_;
+ ScopedComPtr<IStream> cache_stream_;
+ ScopedComPtr<IMoniker> upload_moniker_;
+};
+
+MetricsService* MetricsService::GetInstance() {
+ if (g_metrics_instance_.Pointer()->Get())
+ return g_metrics_instance_.Pointer()->Get();
+
+ g_metrics_instance_.Pointer()->Set(new MetricsService);
+ return g_metrics_instance_.Pointer()->Get();
+}
+
+MetricsService::MetricsService()
+ : recording_active_(false),
+ reporting_active_(false),
+ user_permits_upload_(false),
+ state_(INITIALIZED),
+ thread_(NULL) {
+}
+
+MetricsService::~MetricsService() {
+ SetRecording(false);
+ if (pending_log_) {
+ delete pending_log_;
+ pending_log_ = NULL;
+ }
+ if (current_log_) {
+ delete current_log_;
+ current_log_ = NULL;
+ }
+}
+
+void MetricsService::InitializeMetricsState() {
+ DCHECK(state_ == INITIALIZED);
+
+ thread_ = PlatformThread::CurrentId();
+
+ user_permits_upload_ = GoogleUpdateSettings::GetCollectStatsConsent();
+ // Update session ID
+ session_id_ = CrashMetricsReporter::GetInstance()->IncrementMetric(
+ CrashMetricsReporter::SESSION_ID);
+
+ // Ensure that an instance of the StatisticsRecorder object is created.
+ g_statistics_recorder_.Get();
+
+ CrashMetricsReporter::GetInstance()->set_active(true);
+}
+
+// static
+void MetricsService::Start() {
+ if (GetInstance()->state_ == ACTIVE)
+ return;
+
+ GetInstance()->InitializeMetricsState();
+ GetInstance()->SetRecording(true);
+ GetInstance()->SetReporting(true);
+}
+
+// static
+void MetricsService::Stop() {
+ GetInstance()->SetReporting(false);
+ GetInstance()->SetRecording(false);
+}
+
+void MetricsService::SetRecording(bool enabled) {
+ DCHECK_EQ(thread_, PlatformThread::CurrentId());
+ if (enabled == recording_active_)
+ return;
+
+ if (enabled) {
+ if (client_id_.empty()) {
+ client_id_ = GenerateClientID();
+ // Save client id somewhere.
+ }
+ StartRecording();
+
+ } else {
+ state_ = STOPPED;
+ }
+ recording_active_ = enabled;
+}
+
+// static
+std::string MetricsService::GenerateClientID() {
+ const int kGUIDSize = 39;
+
+ GUID guid;
+ HRESULT guid_result = CoCreateGuid(&guid);
+ DCHECK(SUCCEEDED(guid_result));
+
+ std::wstring guid_string;
+ int result = StringFromGUID2(guid,
+ WriteInto(&guid_string, kGUIDSize), kGUIDSize);
+ DCHECK(result == kGUIDSize);
+ return WideToUTF8(guid_string.substr(1, guid_string.length() - 2));
+}
+
+// static
+void CALLBACK MetricsService::TransmissionTimerProc(HWND window,
+ unsigned int message,
+ unsigned int event_id,
+ unsigned int time) {
+ DLOG(INFO) << "Transmission timer notified";
+ DCHECK(GetInstance() != NULL);
+ GetInstance()->UploadData();
+}
+
+void MetricsService::SetReporting(bool enable) {
+ static const int kChromeFrameMetricsTimerId = 0xFFFFFFFF;
+
+ DCHECK_EQ(thread_, PlatformThread::CurrentId());
+ if (reporting_active_ != enable) {
+ reporting_active_ = enable;
+ if (reporting_active_) {
+ SetTimer(NULL, kChromeFrameMetricsTimerId, kUMAUploadTimeoutMilliSeconds,
+ reinterpret_cast<TIMERPROC>(TransmissionTimerProc));
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Recording control methods
+
+void MetricsService::StartRecording() {
+ DCHECK_EQ(thread_, PlatformThread::CurrentId());
+ if (current_log_)
+ return;
+
+ current_log_ = new MetricsLogBase(client_id_, session_id_,
+ GetVersionString());
+ if (state_ == INITIALIZED)
+ state_ = ACTIVE;
+}
+
+void MetricsService::StopRecording(bool save_log) {
+ DCHECK_EQ(thread_, PlatformThread::CurrentId());
+ if (!current_log_)
+ return;
+
+ // Put incremental histogram deltas at the end of all log transmissions.
+ // Don't bother if we're going to discard current_log_.
+ if (save_log) {
+ CrashMetricsReporter::GetInstance()->RecordCrashMetrics();
+ RecordCurrentHistograms();
+ }
+
+ if (save_log) {
+ pending_log_ = current_log_;
+ }
+ current_log_ = NULL;
+}
+
+void MetricsService::MakePendingLog() {
+ DCHECK_EQ(thread_, PlatformThread::CurrentId());
+ if (pending_log())
+ return;
+
+ switch (state_) {
+ case INITIALIZED: // We should be further along by now.
+ DCHECK(false);
+ return;
+
+ case ACTIVE:
+ StopRecording(true);
+ StartRecording();
+ break;
+
+ default:
+ DCHECK(false);
+ return;
+ }
+
+ DCHECK(pending_log());
+}
+
+bool MetricsService::TransmissionPermitted() const {
+ // If the user forbids uploading that's their business, and we don't upload
+ // anything.
+ return user_permits_upload_;
+}
+
+std::string MetricsService::PrepareLogSubmissionString() {
+ DCHECK_EQ(thread_, PlatformThread::CurrentId());
+
+ MakePendingLog();
+ DCHECK(pending_log());
+ if (pending_log_== NULL) {
+ return std::string();
+ }
+
+ pending_log_->CloseLog();
+ std::string pending_log_text = pending_log_->GetEncodedLogString();
+ DCHECK(!pending_log_text.empty());
+ DiscardPendingLog();
+ return pending_log_text;
+}
+
+bool MetricsService::UploadData() {
+ DCHECK_EQ(thread_, PlatformThread::CurrentId());
+
+ if (!GetInstance()->TransmissionPermitted())
+ return false;
+
+ static long currently_uploading = 0;
+ if (InterlockedCompareExchange(&currently_uploading, 1, 0)) {
+ DLOG(INFO) << "Contention for uploading metrics data. Backing off";
+ return false;
+ }
+
+ pending_log_text_ = PrepareLogSubmissionString();
+ DCHECK(!pending_log_text_.empty());
+
+ // Allow security conscious users to see all metrics logs that we send.
+ LOG(INFO) << "METRICS LOG: " << pending_log_text_;
+
+ bool ret = true;
+
+ std::string compressed_log;
+ if (!Bzip2Compress(pending_log_text_, &compressed_log)) {
+ NOTREACHED() << "Failed to compress log for transmission.";
+ ret = false;
+ } else {
+ HRESULT hr = ChromeFrameMetricsDataUploader::UploadDataHelper(
+ compressed_log);
+ DCHECK(SUCCEEDED(hr));
+ }
+ DiscardPendingLog();
+
+ currently_uploading = 0;
+ return ret;
+}
+
+// static
+std::string MetricsService::GetVersionString() {
+ scoped_ptr<FileVersionInfo> version_info(
+ chrome_app::GetChromeVersionInfo());
+ if (version_info.get()) {
+ std::string version = WideToUTF8(version_info->product_version());
+ // Add the -F extensions to ensure that UMA data uploaded by ChromeFrame
+ // lands in the ChromeFrame bucket.
+ version += "-F";
+ if (!version_info->is_official_build())
+ version.append("-devel");
+ return version;
+ } else {
+ NOTREACHED() << "Unable to retrieve version string.";
+ }
+
+ return std::string();
+}
+
diff --git a/chrome_frame/metrics_service.h b/chrome_frame/metrics_service.h
new file mode 100644
index 0000000..766a423
--- /dev/null
+++ b/chrome_frame/metrics_service.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2010 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 file defines a service that collects information about the user
+// experience in order to help improve future versions of the app.
+
+#ifndef CHROME_FRAME_METRICS_METRICS_SERVICE_H_
+#define CHROME_FRAME_METRICS_METRICS_SERVICE_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/histogram.h"
+#include "base/lazy_instance.h"
+#include "base/scoped_ptr.h"
+#include "base/thread_local.h"
+#include "chrome/common/metrics_helpers.h"
+
+// TODO(ananta)
+// Refactor more common code from chrome/browser/metrics/metrics_service.h into
+// the MetricsServiceBase class.
+class MetricsService : public MetricsServiceBase {
+ public:
+ static MetricsService* GetInstance();
+ // Start/stop the metrics recording and uploading machine. These should be
+ // used on startup and when the user clicks the checkbox in the prefs.
+ // StartRecordingOnly starts the metrics recording but not reporting, for use
+ // in tests only.
+ static void Start();
+ static void Stop();
+ // Set up client ID, session ID, etc.
+ void InitializeMetricsState();
+
+ private:
+ MetricsService();
+ virtual ~MetricsService();
+ // The MetricsService has a lifecycle that is stored as a state.
+ // See metrics_service.cc for description of this lifecycle.
+ enum State {
+ INITIALIZED, // Constructor was called.
+ ACTIVE, // Accumalating log data
+ STOPPED, // Service has stopped
+ };
+
+ // Maintain a map of histogram names to the sample stats we've sent.
+ typedef std::map<std::string, Histogram::SampleSet> LoggedSampleMap;
+
+ // Sets and gets whether metrics recording is active.
+ // SetRecording(false) also forces a persistent save of logging state (if
+ // anything has been recorded, or transmitted).
+ void SetRecording(bool enabled);
+
+ // Enable/disable transmission of accumulated logs and crash reports (dumps).
+ // Return value "true" indicates setting was definitively set as requested).
+ // Return value of "false" indicates that the enable state is effectively
+ // stuck in the other logical setting.
+ // Google Update maintains the authoritative preference in the registry, so
+ // the caller *might* not be able to actually change the setting.
+ // It is always possible to set this to at least one value, which matches the
+ // current value reported by querying Google Update.
+ void SetReporting(bool enabled);
+
+ // If in_idle is true, sets idle_since_last_transmission to true.
+ // If in_idle is false and idle_since_last_transmission_ is true, sets
+ // idle_since_last_transmission to false and starts the timer (provided
+ // starting the timer is permitted).
+ void HandleIdleSinceLastTransmission(bool in_idle);
+
+ // Generates a new client ID to use to identify self to metrics server.
+ static std::string GenerateClientID();
+
+ // ChromeFrame UMA data is uploaded when this timer proc gets invoked.
+ static void CALLBACK TransmissionTimerProc(HWND window, unsigned int message,
+ unsigned int event_id,
+ unsigned int time);
+
+ // Called to start recording user experience metrics.
+ // Constructs a new, empty current_log_.
+ void StartRecording();
+
+ // Called to stop recording user experience metrics.
+ void StopRecording(bool save_log);
+
+ // Takes whatever log should be uploaded next (according to the state_)
+ // and makes it the pending log. If pending_log_ is not NULL,
+ // MakePendingLog does nothing and returns.
+ void MakePendingLog();
+
+ // Determines from state_ and permissions set out by the server and by
+ // the user whether the pending_log_ should be sent or discarded. Called by
+ // TryToStartTransmission.
+ bool TransmissionPermitted() const;
+
+ bool recording_active() const {
+ return recording_active_;
+ }
+
+ bool reporting_active() const {
+ return reporting_active_;
+ }
+
+ // Convert pending_log_ to XML in pending_log_text_ for transmission.
+ std::string PrepareLogSubmissionString();
+
+ // Upload pending data to the server by converting it to XML and compressing
+ // it. Returns true on success.
+ bool UploadData();
+
+ // Get the current version of the application as a string.
+ static std::string GetVersionString();
+
+ // Indicate whether recording and reporting are currently happening.
+ // These should not be set directly, but by calling SetRecording and
+ // SetReporting.
+ bool recording_active_;
+ bool reporting_active_;
+
+ // Coincides with the check box in options window that lets the user control
+ // whether to upload.
+ bool user_permits_upload_;
+
+ // The progession of states made by the browser are recorded in the following
+ // state.
+ State state_;
+
+ // The URL for the metrics server.
+ std::wstring server_url_;
+
+ // The identifier that's sent to the server with the log reports.
+ std::string client_id_;
+
+ // A number that identifies the how many times the app has been launched.
+ int session_id_;
+
+ static base::LazyInstance<base::ThreadLocalPointer<MetricsService> >
+ g_metrics_instance_;
+
+ PlatformThreadId thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsService);
+};
+
+#endif // CHROME_FRAME_METRICS_METRICS_SERVICE_H_
+
diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc
index 093b19f..642a438 100644
--- a/chrome_frame/urlmon_url_request.cc
+++ b/chrome_frame/urlmon_url_request.cc
@@ -21,31 +21,6 @@
#include "net/http/http_util.h"
#include "net/http/http_response_headers.h"
-namespace {
-
-// Reads data from a stream into a string.
-HRESULT ReadStream(IStream* stream, size_t size, std::string* data) {
- DCHECK(stream);
- DCHECK(data);
-
- DWORD read = 0;
- HRESULT hr = stream->Read(WriteInto(data, size + 1), size, &read);
- DCHECK(hr == S_OK || hr == S_FALSE || hr == E_PENDING);
- if (read) {
- data->erase(read);
- DCHECK_EQ(read, data->length());
- } else {
- data->clear();
- // Return S_FALSE if the underlying stream returned S_OK and zero bytes.
- if (hr == S_OK)
- hr = S_FALSE;
- }
-
- return hr;
-}
-
-} // end namespace
-
UrlmonUrlRequest::UrlmonUrlRequest()
: pending_read_size_(0),
headers_received_(false),
diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc
index b375dcc..735ec6f 100644
--- a/chrome_frame/utils.cc
+++ b/chrome_frame/utils.cc
@@ -1191,3 +1191,25 @@ std::string Bscf2Str(DWORD flags) {
return s;
#undef ADD_BSCF_FLAG
}
+
+// Reads data from a stream into a string.
+HRESULT ReadStream(IStream* stream, size_t size, std::string* data) {
+ DCHECK(stream);
+ DCHECK(data);
+
+ DWORD read = 0;
+ HRESULT hr = stream->Read(WriteInto(data, size + 1), size, &read);
+ DCHECK(hr == S_OK || hr == S_FALSE || hr == E_PENDING);
+ if (read) {
+ data->erase(read);
+ DCHECK_EQ(read, data->length());
+ } else {
+ data->clear();
+ // Return S_FALSE if the underlying stream returned S_OK and zero bytes.
+ if (hr == S_OK)
+ hr = S_FALSE;
+ }
+
+ return hr;
+}
+
diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h
index c60dfab..5f25e00 100644
--- a/chrome_frame/utils.h
+++ b/chrome_frame/utils.h
@@ -393,6 +393,11 @@ extern Lock g_ChromeFrameHistogramLock;
UMA_HISTOGRAM_TIMES(name, sample); \
}
+#define THREAD_SAFE_UMA_HISTOGRAM_COUNTS(name, sample) { \
+ AutoLock lock(g_ChromeFrameHistogramLock); \
+ UMA_HISTOGRAM_COUNTS(name, sample); \
+}
+
// Fired when we want to notify IE about privacy changes.
#define WM_FIRE_PRIVACY_CHANGE_NOTIFICATION (WM_APP + 1)
@@ -456,4 +461,7 @@ std::string BindStatus2Str(ULONG bind_status);
std::string PiFlags2Str(DWORD flags);
std::string Bscf2Str(DWORD flags);
+// Reads data from a stream into a string.
+HRESULT ReadStream(IStream* stream, size_t size, std::string* data);
+
#endif // CHROME_FRAME_UTILS_H_