1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/renderer/pepper/pepper_uma_host.h"
6
7#include "base/metrics/histogram.h"
8#include "base/sha1.h"
9#include "base/strings/string_number_conversions.h"
10#include "chrome/common/chrome_switches.h"
11#include "chrome/common/render_messages.h"
12#include "chrome/renderer/chrome_content_renderer_client.h"
13#include "content/public/renderer/pepper_plugin_instance.h"
14#include "content/public/renderer/render_thread.h"
15#include "content/public/renderer/renderer_ppapi_host.h"
16#include "extensions/common/constants.h"
17#include "extensions/common/extension.h"
18#include "ppapi/c/pp_errors.h"
19#include "ppapi/host/dispatch_host_message.h"
20#include "ppapi/host/host_message_context.h"
21#include "ppapi/host/ppapi_host.h"
22#include "ppapi/proxy/ppapi_messages.h"
23
24#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
25
26namespace {
27
28const char* const kPredefinedAllowedUMAOrigins[] = {
29    "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F",  // see http://crbug.com/317833
30    "4EB74897CB187C7633357C2FE832E0AD6A44883A"   // see http://crbug.com/317833
31};
32
33const char* const kWhitelistedHistogramPrefixes[] = {
34    "22F67DA2061FFC4DC9A4974036348D9C38C22919"  // see http://crbug.com/390221
35};
36
37const char* const kWhitelistedPluginBaseNames[] = {
38#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
39    kWidevineCdmAdapterFileName,  // see http://crbug.com/368743
40                                  // and http://crbug.com/410630
41#endif
42    "libpdf.so"                   // see http://crbug.com/405305
43};
44
45std::string HashPrefix(const std::string& histogram) {
46  const std::string id_hash =
47      base::SHA1HashString(histogram.substr(0, histogram.find('.')));
48  DCHECK_EQ(id_hash.length(), base::kSHA1Length);
49  return base::HexEncode(id_hash.c_str(), id_hash.length());
50}
51
52}  // namespace
53
54PepperUMAHost::PepperUMAHost(content::RendererPpapiHost* host,
55                             PP_Instance instance,
56                             PP_Resource resource)
57    : ResourceHost(host->GetPpapiHost(), instance, resource),
58      document_url_(host->GetDocumentURL(instance)),
59      is_plugin_in_process_(host->IsRunningInProcess()) {
60  if (host->GetPluginInstance(instance)) {
61    plugin_base_name_ =
62        host->GetPluginInstance(instance)->GetModulePath().BaseName();
63  }
64
65  for (size_t i = 0; i < arraysize(kPredefinedAllowedUMAOrigins); ++i)
66    allowed_origins_.insert(kPredefinedAllowedUMAOrigins[i]);
67  for (size_t i = 0; i < arraysize(kWhitelistedHistogramPrefixes); ++i)
68    allowed_histogram_prefixes_.insert(kWhitelistedHistogramPrefixes[i]);
69  for (size_t i = 0; i < arraysize(kWhitelistedPluginBaseNames); ++i)
70    allowed_plugin_base_names_.insert(kWhitelistedPluginBaseNames[i]);
71}
72
73PepperUMAHost::~PepperUMAHost() {}
74
75int32_t PepperUMAHost::OnResourceMessageReceived(
76    const IPC::Message& msg,
77    ppapi::host::HostMessageContext* context) {
78  PPAPI_BEGIN_MESSAGE_MAP(PepperUMAHost, msg)
79    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramCustomTimes,
80                                      OnHistogramCustomTimes)
81    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramCustomCounts,
82                                      OnHistogramCustomCounts)
83    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramEnumeration,
84                                      OnHistogramEnumeration)
85    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
86        PpapiHostMsg_UMA_IsCrashReportingEnabled, OnIsCrashReportingEnabled)
87  PPAPI_END_MESSAGE_MAP()
88  return PP_ERROR_FAILED;
89}
90
91bool PepperUMAHost::IsPluginWhitelisted() {
92#if defined(ENABLE_EXTENSIONS)
93  return ChromeContentRendererClient::IsExtensionOrSharedModuleWhitelisted(
94      document_url_, allowed_origins_);
95#else
96  return false;
97#endif
98}
99
100bool PepperUMAHost::IsHistogramAllowed(const std::string& histogram) {
101  if (is_plugin_in_process_ && histogram.find("NaCl.") == 0) {
102    return true;
103  }
104
105  if (IsPluginWhitelisted() &&
106      allowed_histogram_prefixes_.find(HashPrefix(histogram)) !=
107          allowed_histogram_prefixes_.end()) {
108    return true;
109  }
110
111  if (allowed_plugin_base_names_.find(plugin_base_name_.MaybeAsASCII()) !=
112      allowed_plugin_base_names_.end()) {
113    return true;
114  }
115
116  LOG(ERROR) << "Host or histogram name is not allowed to use the UMA API.";
117  return false;
118}
119
120#define RETURN_IF_BAD_ARGS(_min, _max, _buckets) \
121  do {                                           \
122    if (_min >= _max || _buckets <= 1)           \
123      return PP_ERROR_BADARGUMENT;               \
124  } while (0)
125
126int32_t PepperUMAHost::OnHistogramCustomTimes(
127    ppapi::host::HostMessageContext* context,
128    const std::string& name,
129    int64_t sample,
130    int64_t min,
131    int64_t max,
132    uint32_t bucket_count) {
133  if (!IsHistogramAllowed(name)) {
134    return PP_ERROR_NOACCESS;
135  }
136  RETURN_IF_BAD_ARGS(min, max, bucket_count);
137
138  base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
139      name,
140      base::TimeDelta::FromMilliseconds(min),
141      base::TimeDelta::FromMilliseconds(max),
142      bucket_count,
143      base::HistogramBase::kUmaTargetedHistogramFlag);
144  // The histogram can be NULL if it is constructed with bad arguments.  Ignore
145  // that data for this API.  An error message will be logged.
146  if (counter)
147    counter->AddTime(base::TimeDelta::FromMilliseconds(sample));
148  return PP_OK;
149}
150
151int32_t PepperUMAHost::OnHistogramCustomCounts(
152    ppapi::host::HostMessageContext* context,
153    const std::string& name,
154    int32_t sample,
155    int32_t min,
156    int32_t max,
157    uint32_t bucket_count) {
158  if (!IsHistogramAllowed(name)) {
159    return PP_ERROR_NOACCESS;
160  }
161  RETURN_IF_BAD_ARGS(min, max, bucket_count);
162
163  base::HistogramBase* counter = base::Histogram::FactoryGet(
164      name,
165      min,
166      max,
167      bucket_count,
168      base::HistogramBase::kUmaTargetedHistogramFlag);
169  // The histogram can be NULL if it is constructed with bad arguments.  Ignore
170  // that data for this API.  An error message will be logged.
171  if (counter)
172    counter->Add(sample);
173  return PP_OK;
174}
175
176int32_t PepperUMAHost::OnHistogramEnumeration(
177    ppapi::host::HostMessageContext* context,
178    const std::string& name,
179    int32_t sample,
180    int32_t boundary_value) {
181  if (!IsHistogramAllowed(name)) {
182    return PP_ERROR_NOACCESS;
183  }
184  RETURN_IF_BAD_ARGS(0, boundary_value, boundary_value + 1);
185
186  base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
187      name,
188      1,
189      boundary_value,
190      boundary_value + 1,
191      base::HistogramBase::kUmaTargetedHistogramFlag);
192  // The histogram can be NULL if it is constructed with bad arguments.  Ignore
193  // that data for this API.  An error message will be logged.
194  if (counter)
195    counter->Add(sample);
196  return PP_OK;
197}
198
199int32_t PepperUMAHost::OnIsCrashReportingEnabled(
200    ppapi::host::HostMessageContext* context) {
201  if (!IsPluginWhitelisted())
202    return PP_ERROR_NOACCESS;
203  bool enabled = false;
204  content::RenderThread::Get()->Send(
205      new ChromeViewHostMsg_IsCrashReportingEnabled(&enabled));
206  if (enabled)
207    return PP_OK;
208  return PP_ERROR_FAILED;
209}
210