| // Copyright 2014 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/renderer/pepper/pepper_uma_host.h" |
| |
| #include <stddef.h> |
| |
| #include "base/metrics/histogram.h" |
| #include "base/sha1.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "chrome/common/chrome_content_client.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/render_messages.h" |
| #include "chrome/renderer/chrome_content_renderer_client.h" |
| #include "content/public/renderer/pepper_plugin_instance.h" |
| #include "content/public/renderer/render_thread.h" |
| #include "content/public/renderer/renderer_ppapi_host.h" |
| #include "extensions/buildflags/buildflags.h" |
| #include "media/media_buildflags.h" |
| #include "ppapi/buildflags/buildflags.h" |
| #include "ppapi/c/pp_errors.h" |
| #include "ppapi/host/dispatch_host_message.h" |
| #include "ppapi/host/host_message_context.h" |
| #include "ppapi/host/ppapi_host.h" |
| #include "ppapi/proxy/ppapi_messages.h" |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| #include "extensions/common/constants.h" |
| #include "extensions/common/extension.h" |
| #endif // BUILDFLAG(ENABLE_EXTENSIONS) |
| |
| namespace { |
| |
| const char* const kPredefinedAllowedUMAOrigins[] = { |
| "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F", // see http://crbug.com/317833 |
| "4EB74897CB187C7633357C2FE832E0AD6A44883A", // see http://crbug.com/317833 |
| "9E527CDA9D7C50844E8A5DB964A54A640AE48F98", // see http://crbug.com/521189 |
| "DF52618D0B040D8A054D8348D2E84DDEEE5974E7", // see http://crbug.com/521189 |
| "269D721F163E587BC53C6F83553BF9CE2BB143CD", // see http://crbug.com/521189 |
| "6B55A5329E3F1F30F6032BDB20B2EB4378DBF767", // see http://crbug.com/521189 |
| "C449A798C495E6CF7D6AF10162113D564E67AD12", // see http://crbug.com/521189 |
| "01E9FFA9A8F3C18271FE91BEE46207F3B81755CC", // see http://crbug.com/521189 |
| "97B23E01B2AA064E8332EE43A7A85C628AADC3F2", // see http://crbug.com/521189 |
| }; |
| |
| const char* const kWhitelistedHistogramPrefixes[] = { |
| "22F67DA2061FFC4DC9A4974036348D9C38C22919", // see http://crbug.com/390221 |
| "3FEA4650221C5E6C39CF5C5C9F464FF74EAB6CE1", // see http://crbug.com/521189 |
| }; |
| |
| const base::FilePath::CharType* const kWhitelistedPluginBaseNames[] = { |
| ChromeContentClient::kPDFPluginPath, |
| }; |
| |
| std::string HashPrefix(const std::string& histogram) { |
| const std::string id_hash = |
| base::SHA1HashString(histogram.substr(0, histogram.find('.'))); |
| DCHECK_EQ(id_hash.length(), base::kSHA1Length); |
| return base::HexEncode(id_hash.c_str(), id_hash.length()); |
| } |
| |
| } // namespace |
| |
| PepperUMAHost::PepperUMAHost(content::RendererPpapiHost* host, |
| PP_Instance instance, |
| PP_Resource resource) |
| : ResourceHost(host->GetPpapiHost(), instance, resource), |
| document_url_(host->GetDocumentURL(instance)), |
| is_plugin_in_process_(host->IsRunningInProcess()) { |
| if (host->GetPluginInstance(instance)) { |
| plugin_base_name_ = |
| host->GetPluginInstance(instance)->GetModulePath().BaseName(); |
| } |
| |
| for (size_t i = 0; i < base::size(kPredefinedAllowedUMAOrigins); ++i) |
| allowed_origins_.insert(kPredefinedAllowedUMAOrigins[i]); |
| for (size_t i = 0; i < base::size(kWhitelistedHistogramPrefixes); ++i) |
| allowed_histogram_prefixes_.insert(kWhitelistedHistogramPrefixes[i]); |
| for (size_t i = 0; i < base::size(kWhitelistedPluginBaseNames); ++i) |
| allowed_plugin_base_names_.insert(kWhitelistedPluginBaseNames[i]); |
| } |
| |
| PepperUMAHost::~PepperUMAHost() {} |
| |
| int32_t PepperUMAHost::OnResourceMessageReceived( |
| const IPC::Message& msg, |
| ppapi::host::HostMessageContext* context) { |
| PPAPI_BEGIN_MESSAGE_MAP(PepperUMAHost, msg) |
| PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramCustomTimes, |
| OnHistogramCustomTimes) |
| PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramCustomCounts, |
| OnHistogramCustomCounts) |
| PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramEnumeration, |
| OnHistogramEnumeration) |
| PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( |
| PpapiHostMsg_UMA_IsCrashReportingEnabled, OnIsCrashReportingEnabled) |
| PPAPI_END_MESSAGE_MAP() |
| return PP_ERROR_FAILED; |
| } |
| |
| bool PepperUMAHost::IsPluginWhitelisted() { |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| return ChromeContentRendererClient::IsExtensionOrSharedModuleWhitelisted( |
| document_url_, allowed_origins_); |
| #else |
| return false; |
| #endif |
| } |
| |
| bool PepperUMAHost::IsHistogramAllowed(const std::string& histogram) { |
| if (is_plugin_in_process_ && |
| base::StartsWith(histogram, "NaCl.", base::CompareCase::SENSITIVE)) { |
| return true; |
| } |
| |
| if (IsPluginWhitelisted() && |
| base::ContainsKey(allowed_histogram_prefixes_, HashPrefix(histogram))) { |
| return true; |
| } |
| |
| if (base::ContainsKey(allowed_plugin_base_names_, plugin_base_name_.value())) |
| return true; |
| |
| LOG(ERROR) << "Host or histogram name is not allowed to use the UMA API."; |
| return false; |
| } |
| |
| #define RETURN_IF_BAD_ARGS(_min, _max, _buckets) \ |
| do { \ |
| if (_min >= _max || _buckets <= 1) \ |
| return PP_ERROR_BADARGUMENT; \ |
| } while (0) |
| |
| int32_t PepperUMAHost::OnHistogramCustomTimes( |
| ppapi::host::HostMessageContext* context, |
| const std::string& name, |
| int64_t sample, |
| int64_t min, |
| int64_t max, |
| uint32_t bucket_count) { |
| if (!IsHistogramAllowed(name)) { |
| return PP_ERROR_NOACCESS; |
| } |
| RETURN_IF_BAD_ARGS(min, max, bucket_count); |
| |
| base::HistogramBase* counter = base::Histogram::FactoryTimeGet( |
| name, |
| base::TimeDelta::FromMilliseconds(min), |
| base::TimeDelta::FromMilliseconds(max), |
| bucket_count, |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| // The histogram can be NULL if it is constructed with bad arguments. Ignore |
| // that data for this API. An error message will be logged. |
| if (counter) |
| counter->AddTime(base::TimeDelta::FromMilliseconds(sample)); |
| return PP_OK; |
| } |
| |
| int32_t PepperUMAHost::OnHistogramCustomCounts( |
| ppapi::host::HostMessageContext* context, |
| const std::string& name, |
| int32_t sample, |
| int32_t min, |
| int32_t max, |
| uint32_t bucket_count) { |
| if (!IsHistogramAllowed(name)) { |
| return PP_ERROR_NOACCESS; |
| } |
| RETURN_IF_BAD_ARGS(min, max, bucket_count); |
| |
| base::HistogramBase* counter = base::Histogram::FactoryGet( |
| name, |
| min, |
| max, |
| bucket_count, |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| // The histogram can be NULL if it is constructed with bad arguments. Ignore |
| // that data for this API. An error message will be logged. |
| if (counter) |
| counter->Add(sample); |
| return PP_OK; |
| } |
| |
| int32_t PepperUMAHost::OnHistogramEnumeration( |
| ppapi::host::HostMessageContext* context, |
| const std::string& name, |
| int32_t sample, |
| int32_t boundary_value) { |
| if (!IsHistogramAllowed(name)) { |
| return PP_ERROR_NOACCESS; |
| } |
| RETURN_IF_BAD_ARGS(0, boundary_value, boundary_value + 1); |
| |
| base::HistogramBase* counter = base::LinearHistogram::FactoryGet( |
| name, |
| 1, |
| boundary_value, |
| boundary_value + 1, |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| // The histogram can be NULL if it is constructed with bad arguments. Ignore |
| // that data for this API. An error message will be logged. |
| if (counter) |
| counter->Add(sample); |
| return PP_OK; |
| } |
| |
| int32_t PepperUMAHost::OnIsCrashReportingEnabled( |
| ppapi::host::HostMessageContext* context) { |
| if (!IsPluginWhitelisted()) |
| return PP_ERROR_NOACCESS; |
| bool enabled = false; |
| content::RenderThread::Get()->Send( |
| new ChromeViewHostMsg_IsCrashReportingEnabled(&enabled)); |
| if (enabled) |
| return PP_OK; |
| return PP_ERROR_FAILED; |
| } |