// 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 #include #include "base/command_line.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "build/build_config.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" #include "ui/gfx/codec/png_codec.h" #endif #if !defined(OS_ANDROID) #include "chrome/common/resource_usage_reporter.mojom.h" #include "chrome/utility/profile_import_handler.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "net/proxy/mojo_proxy_resolver_factory_impl.h" #include "net/proxy/proxy_resolver_v8.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/utility/extensions/extensions_handler.h" #include "chrome/utility/image_writer/image_writer_handler.h" #endif #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN) #include "chrome/utility/printing_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(OS_ANDROID) void CreateProxyResolverFactory( mojo::InterfaceRequest 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(std::move(request)); } class ResourceUsageReporterImpl : public ResourceUsageReporter { public: explicit ResourceUsageReporterImpl( mojo::InterfaceRequest req) : binding_(this, std::move(req)) {} ~ResourceUsageReporterImpl() override {} private: void GetUsageData( const mojo::Callback& 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(std::move(data)); } mojo::StrongBinding binding_; }; void CreateResourceUsageReporter( mojo::InterfaceRequest request) { new ResourceUsageReporterImpl(std::move(request)); } #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(this)); handlers_.push_back(new image_writer::ImageWriterHandler()); #endif #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN) handlers_.push_back(new printing::PrintingHandler()); #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) IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustPNGDecodeImage, OnRobustPNGDecodeImage) #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 // defined(OS_MACOSX) #endif // defined(FULL_SAFE_BROWSING) #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( base::Bind(CreateProxyResolverFactory)); registry->AddService( base::Bind(CreateResourceUsageReporter)); #endif } void ChromeContentUtilityClient::AddHandler( scoped_ptr handler) { handlers_.push_back(std::move(handler)); } // static void ChromeContentUtilityClient::PreSandboxStartup() { #if defined(ENABLE_EXTENSIONS) extensions::ExtensionsHandler::PreSandboxStartup(); #endif } // static SkBitmap ChromeContentUtilityClient::DecodeImage( const std::vector& 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& 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& 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& 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::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& encoded_data, int request_id) { // Our robust jpeg decoding is using IJG libjpeg. if (!encoded_data.empty()) { scoped_ptr 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(); } void ChromeContentUtilityClient::OnRobustPNGDecodeImage( const std::vector& encoded_data, int request_id) { // Our robust PNG decoding is using libpng. if (!encoded_data.empty()) { SkBitmap decoded_image; if (gfx::PNGCodec::Decode(encoded_data.data(), encoded_data.size(), &decoded_image)) { Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image, request_id)); } else { Send(new ChromeUtilityHostMsg_DecodeImage_Failed(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 // defined(OS_MACOSX) #endif // defined(FULL_SAFE_BROWSING)