blob: 3d09c61f611b5fccba435eb6e103f3064e794112 [file] [log] [blame]
// Copyright (c) 2012 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/utility/chrome_content_utility_client.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "chrome/common/chrome_utility_messages.h"
#include "chrome/common/safe_browsing/zip_analyzer.h"
#include "chrome/common/safe_browsing/zip_analyzer_results.h"
#include "chrome/utility/chrome_content_utility_ipc_whitelist.h"
#include "chrome/utility/safe_json_parser_handler.h"
#include "chrome/utility/utility_message_handler.h"
#include "content/public/child/image_decoder_utils.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/service_registry.h"
#include "content/public/utility/utility_thread.h"
#include "courgette/courgette.h"
#include "courgette/third_party/bsdiff.h"
#include "ipc/ipc_channel.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/zlib/google/zip.h"
#include "ui/gfx/geometry/size.h"
#if defined(OS_CHROMEOS)
#include "ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h"
#endif
#if !defined(OS_ANDROID)
#include "chrome/common/resource_usage_reporter.mojom.h"
#include "chrome/utility/profile_import_handler.h"
#include "net/proxy/mojo_proxy_resolver_factory_impl.h"
#include "net/proxy/proxy_resolver_v8.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h"
#endif
#if defined(OS_WIN)
#include "chrome/utility/font_cache_handler_win.h"
#include "chrome/utility/shell_handler_win.h"
#endif
#if defined(ENABLE_EXTENSIONS)
#include "chrome/common/extensions/chrome_utility_extensions_messages.h"
#include "chrome/utility/extensions/extensions_handler.h"
#include "chrome/utility/image_writer/image_writer_handler.h"
#include "chrome/utility/media_galleries/ipc_data_source.h"
#include "chrome/utility/media_galleries/media_metadata_parser.h"
#endif
#if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
#include "chrome/utility/printing_handler.h"
#endif
#if defined(ENABLE_MDNS)
#include "chrome/utility/local_discovery/service_discovery_message_handler.h"
#endif
#if defined(OS_MACOSX) && defined(FULL_SAFE_BROWSING)
#include "chrome/utility/safe_browsing/mac/dmg_analyzer.h"
#endif
namespace {
bool Send(IPC::Message* message) {
return content::UtilityThread::Get()->Send(message);
}
void ReleaseProcessIfNeeded() {
content::UtilityThread::Get()->ReleaseProcessIfNeeded();
}
#if defined(ENABLE_EXTENSIONS)
void FinishParseMediaMetadata(
metadata::MediaMetadataParser* /* parser */,
const extensions::api::media_galleries::MediaMetadata& metadata,
const std::vector<metadata::AttachedImage>& attached_images) {
Send(new ChromeUtilityHostMsg_ParseMediaMetadata_Finished(
true, *metadata.ToValue(), attached_images));
ReleaseProcessIfNeeded();
}
#endif
#if !defined(OS_ANDROID)
void CreateProxyResolverFactory(
mojo::InterfaceRequest<net::interfaces::ProxyResolverFactory> request) {
// MojoProxyResolverFactoryImpl is strongly bound to the Mojo message pipe it
// is connected to. When that message pipe is closed, either explicitly on the
// other end (in the browser process), or by a connection error, this object
// will be destroyed.
new net::MojoProxyResolverFactoryImpl(request.Pass());
}
class ResourceUsageReporterImpl : public ResourceUsageReporter {
public:
explicit ResourceUsageReporterImpl(
mojo::InterfaceRequest<ResourceUsageReporter> req)
: binding_(this, req.Pass()) {}
~ResourceUsageReporterImpl() override {}
private:
void GetUsageData(
const mojo::Callback<void(ResourceUsageDataPtr)>& callback) override {
ResourceUsageDataPtr data = ResourceUsageData::New();
size_t total_heap_size = net::ProxyResolverV8::GetTotalHeapSize();
if (total_heap_size) {
data->reports_v8_stats = true;
data->v8_bytes_allocated = total_heap_size;
data->v8_bytes_used = net::ProxyResolverV8::GetUsedHeapSize();
}
callback.Run(data.Pass());
}
mojo::StrongBinding<ResourceUsageReporter> binding_;
};
void CreateResourceUsageReporter(
mojo::InterfaceRequest<ResourceUsageReporter> request) {
new ResourceUsageReporterImpl(request.Pass());
}
#endif // OS_ANDROID
} // namespace
int64_t ChromeContentUtilityClient::max_ipc_message_size_ =
IPC::Channel::kMaximumMessageSize;
ChromeContentUtilityClient::ChromeContentUtilityClient()
: filter_messages_(false) {
#if !defined(OS_ANDROID)
handlers_.push_back(new ProfileImportHandler());
#endif
#if defined(ENABLE_EXTENSIONS)
handlers_.push_back(new extensions::ExtensionsHandler());
handlers_.push_back(new image_writer::ImageWriterHandler());
#endif
#if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
handlers_.push_back(new PrintingHandler());
#endif
#if defined(ENABLE_MDNS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kUtilityProcessEnableMDns)) {
handlers_.push_back(new local_discovery::ServiceDiscoveryMessageHandler());
}
#endif
#if defined(OS_WIN)
handlers_.push_back(new ShellHandler());
handlers_.push_back(new FontCacheHandler());
#endif
handlers_.push_back(new SafeJsonParserHandler());
}
ChromeContentUtilityClient::~ChromeContentUtilityClient() {
}
void ChromeContentUtilityClient::UtilityThreadStarted() {
#if defined(ENABLE_EXTENSIONS)
extensions::UtilityHandler::UtilityThreadStarted();
#endif
if (kMessageWhitelistSize > 0) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kUtilityProcessRunningElevated)) {
message_id_whitelist_.insert(kMessageWhitelist,
kMessageWhitelist + kMessageWhitelistSize);
filter_messages_ = true;
}
}
}
bool ChromeContentUtilityClient::OnMessageReceived(
const IPC::Message& message) {
if (filter_messages_ && !ContainsKey(message_id_whitelist_, message.type()))
return false;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient, message)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage, OnDecodeImage)
#if defined(OS_CHROMEOS)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustJPEGDecodeImage,
OnRobustJPEGDecodeImage)
#endif // defined(OS_CHROMEOS)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileBsdiff,
OnPatchFileBsdiff)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileCourgette,
OnPatchFileCourgette)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing, OnStartupPing)
#if defined(FULL_SAFE_BROWSING)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection,
OnAnalyzeZipFileForDownloadProtection)
#if defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeDmgFileForDownloadProtection,
OnAnalyzeDmgFileForDownloadProtection)
#endif
#endif
#if defined(ENABLE_EXTENSIONS)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseMediaMetadata,
OnParseMediaMetadata)
#endif
#if defined(OS_CHROMEOS)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CreateZipFile, OnCreateZipFile)
#endif
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
for (Handlers::iterator it = handlers_.begin();
!handled && it != handlers_.end(); ++it) {
handled = (*it)->OnMessageReceived(message);
}
return handled;
}
void ChromeContentUtilityClient::RegisterMojoServices(
content::ServiceRegistry* registry) {
#if !defined(OS_ANDROID)
registry->AddService<net::interfaces::ProxyResolverFactory>(
base::Bind(CreateProxyResolverFactory));
registry->AddService<ResourceUsageReporter>(
base::Bind(CreateResourceUsageReporter));
#endif
}
// static
void ChromeContentUtilityClient::PreSandboxStartup() {
#if defined(ENABLE_EXTENSIONS)
extensions::ExtensionsHandler::PreSandboxStartup();
#endif
#if defined(ENABLE_MDNS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kUtilityProcessEnableMDns)) {
local_discovery::ServiceDiscoveryMessageHandler::PreSandboxStartup();
}
#endif // ENABLE_MDNS
}
// static
SkBitmap ChromeContentUtilityClient::DecodeImage(
const std::vector<unsigned char>& encoded_data, bool shrink_to_fit) {
SkBitmap decoded_image;
if (encoded_data.empty())
return decoded_image;
decoded_image = content::DecodeImage(&encoded_data[0],
gfx::Size(),
encoded_data.size());
int64_t struct_size = sizeof(ChromeUtilityHostMsg_DecodeImage_Succeeded);
int64_t image_size = decoded_image.computeSize64();
int halves = 0;
while (struct_size + (image_size >> 2*halves) > max_ipc_message_size_)
halves++;
if (halves) {
if (shrink_to_fit) {
// If decoded image is too large for IPC message, shrink it by halves.
// This prevents quality loss, and should never overshrink on displays
// smaller than 3600x2400.
// TODO (Issue 416916): Instead of shrinking, return via shared memory
decoded_image = skia::ImageOperations::Resize(
decoded_image, skia::ImageOperations::RESIZE_LANCZOS3,
decoded_image.width() >> halves, decoded_image.height() >> halves);
} else {
// Image too big for IPC message, but caller didn't request resize;
// pre-delete image so DecodeImageAndSend() will send an error.
decoded_image.reset();
LOG(ERROR) << "Decoded image too large for IPC message";
}
}
return decoded_image;
}
// static
void ChromeContentUtilityClient::DecodeImageAndSend(
const std::vector<unsigned char>& encoded_data,
bool shrink_to_fit,
int request_id) {
SkBitmap decoded_image = DecodeImage(encoded_data, shrink_to_fit);
if (decoded_image.empty()) {
Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
} else {
Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image,
request_id));
}
ReleaseProcessIfNeeded();
}
void ChromeContentUtilityClient::OnDecodeImage(
const std::vector<unsigned char>& encoded_data,
bool shrink_to_fit,
int request_id) {
content::UtilityThread::Get()->EnsureBlinkInitialized();
DecodeImageAndSend(encoded_data, shrink_to_fit, request_id);
}
#if defined(OS_CHROMEOS)
void ChromeContentUtilityClient::OnCreateZipFile(
const base::FilePath& src_dir,
const std::vector<base::FilePath>& src_relative_paths,
const base::FileDescriptor& dest_fd) {
// dest_fd should be closed in the function. See ipc/ipc_message_util.h for
// details.
base::ScopedFD fd_closer(dest_fd.fd);
bool succeeded = true;
// Check sanity of source relative paths. Reject if path is absolute or
// contains any attempt to reference a parent directory ("../" tricks).
for (std::vector<base::FilePath>::const_iterator iter =
src_relative_paths.begin(); iter != src_relative_paths.end();
++iter) {
if (iter->IsAbsolute() || iter->ReferencesParent()) {
succeeded = false;
break;
}
}
if (succeeded)
succeeded = zip::ZipFiles(src_dir, src_relative_paths, dest_fd.fd);
if (succeeded)
Send(new ChromeUtilityHostMsg_CreateZipFile_Succeeded());
else
Send(new ChromeUtilityHostMsg_CreateZipFile_Failed());
ReleaseProcessIfNeeded();
}
#endif // defined(OS_CHROMEOS)
#if defined(OS_CHROMEOS)
void ChromeContentUtilityClient::OnRobustJPEGDecodeImage(
const std::vector<unsigned char>& encoded_data,
int request_id) {
// Our robust jpeg decoding is using IJG libjpeg.
if (!encoded_data.empty()) {
scoped_ptr<SkBitmap> decoded_image(gfx::JPEGCodecRobustSlow::Decode(
&encoded_data[0], encoded_data.size()));
if (!decoded_image.get() || decoded_image->empty()) {
Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
} else {
Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image,
request_id));
}
} else {
Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
}
ReleaseProcessIfNeeded();
}
#endif // defined(OS_CHROMEOS)
void ChromeContentUtilityClient::OnPatchFileBsdiff(
const base::FilePath& input_file,
const base::FilePath& patch_file,
const base::FilePath& output_file) {
if (input_file.empty() || patch_file.empty() || output_file.empty()) {
Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
} else {
const int patch_status = courgette::ApplyBinaryPatch(input_file,
patch_file,
output_file);
Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status));
}
ReleaseProcessIfNeeded();
}
void ChromeContentUtilityClient::OnPatchFileCourgette(
const base::FilePath& input_file,
const base::FilePath& patch_file,
const base::FilePath& output_file) {
if (input_file.empty() || patch_file.empty() || output_file.empty()) {
Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
} else {
const int patch_status = courgette::ApplyEnsemblePatch(
input_file.value().c_str(),
patch_file.value().c_str(),
output_file.value().c_str());
Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status));
}
ReleaseProcessIfNeeded();
}
void ChromeContentUtilityClient::OnStartupPing() {
Send(new ChromeUtilityHostMsg_ProcessStarted);
// Don't release the process, we assume further messages are on the way.
}
#if defined(FULL_SAFE_BROWSING)
void ChromeContentUtilityClient::OnAnalyzeZipFileForDownloadProtection(
const IPC::PlatformFileForTransit& zip_file,
const IPC::PlatformFileForTransit& temp_file) {
safe_browsing::zip_analyzer::Results results;
safe_browsing::zip_analyzer::AnalyzeZipFile(
IPC::PlatformFileForTransitToFile(zip_file),
IPC::PlatformFileForTransitToFile(temp_file), &results);
Send(new ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished(
results));
ReleaseProcessIfNeeded();
}
#if defined(OS_MACOSX)
void ChromeContentUtilityClient::OnAnalyzeDmgFileForDownloadProtection(
const IPC::PlatformFileForTransit& dmg_file) {
safe_browsing::zip_analyzer::Results results;
safe_browsing::dmg::AnalyzeDMGFile(
IPC::PlatformFileForTransitToFile(dmg_file), &results);
Send(new ChromeUtilityHostMsg_AnalyzeDmgFileForDownloadProtection_Finished(
results));
ReleaseProcessIfNeeded();
}
#endif
#endif
#if defined(ENABLE_EXTENSIONS)
// TODO(thestig): Try to move this to
// chrome/utility/extensions/extensions_handler.cc.
void ChromeContentUtilityClient::OnParseMediaMetadata(
const std::string& mime_type, int64 total_size, bool get_attached_images) {
// Only one IPCDataSource may be created and added to the list of handlers.
metadata::IPCDataSource* source = new metadata::IPCDataSource(total_size);
handlers_.push_back(source);
metadata::MediaMetadataParser* parser = new metadata::MediaMetadataParser(
source, mime_type, get_attached_images);
parser->Start(base::Bind(&FinishParseMediaMetadata, base::Owned(parser)));
}
#endif