1 // Copyright (c) 2012 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.
5 #include "chrome/utility/chrome_content_utility_client.h"
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/time/time.h"
11 #include "chrome/common/chrome_utility_messages.h"
12 #include "chrome/common/safe_browsing/zip_analyzer.h"
13 #include "chrome/common/safe_browsing/zip_analyzer_results.h"
14 #include "chrome/utility/chrome_content_utility_ipc_whitelist.h"
15 #include "chrome/utility/safe_json_parser_handler.h"
16 #include "chrome/utility/utility_message_handler.h"
17 #include "content/public/child/image_decoder_utils.h"
18 #include "content/public/common/content_switches.h"
19 #include "content/public/common/service_registry.h"
20 #include "content/public/utility/utility_thread.h"
21 #include "courgette/courgette.h"
22 #include "courgette/third_party/bsdiff.h"
23 #include "ipc/ipc_channel.h"
24 #include "skia/ext/image_operations.h"
25 #include "third_party/skia/include/core/SkBitmap.h"
26 #include "third_party/zlib/google/zip.h"
27 #include "ui/gfx/geometry/size.h"
29 #if defined(OS_CHROMEOS)
30 #include "ui/gfx/chromeos/codec/jpeg_codec_robust_slow.h"
33 #if !defined(OS_ANDROID)
34 #include "chrome/common/resource_usage_reporter.mojom.h"
35 #include "chrome/utility/profile_import_handler.h"
36 #include "net/proxy/mojo_proxy_resolver_factory_impl.h"
37 #include "net/proxy/proxy_resolver_v8.h"
38 #include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h"
42 #include "chrome/utility/font_cache_handler_win.h"
43 #include "chrome/utility/shell_handler_win.h"
46 #if defined(ENABLE_EXTENSIONS)
47 #include "chrome/common/extensions/chrome_utility_extensions_messages.h"
48 #include "chrome/utility/extensions/extensions_handler.h"
49 #include "chrome/utility/image_writer/image_writer_handler.h"
50 #include "chrome/utility/media_galleries/ipc_data_source.h"
51 #include "chrome/utility/media_galleries/media_metadata_parser.h"
54 #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
55 #include "chrome/utility/printing_handler.h"
58 #if defined(ENABLE_MDNS)
59 #include "chrome/utility/local_discovery/service_discovery_message_handler.h"
62 #if defined(OS_MACOSX) && defined(FULL_SAFE_BROWSING)
63 #include "chrome/utility/safe_browsing/mac/dmg_analyzer.h"
68 bool Send(IPC::Message
* message
) {
69 return content::UtilityThread::Get()->Send(message
);
72 void ReleaseProcessIfNeeded() {
73 content::UtilityThread::Get()->ReleaseProcessIfNeeded();
76 #if defined(ENABLE_EXTENSIONS)
77 void FinishParseMediaMetadata(
78 metadata::MediaMetadataParser
* /* parser */,
79 const extensions::api::media_galleries::MediaMetadata
& metadata
,
80 const std::vector
<metadata::AttachedImage
>& attached_images
) {
81 Send(new ChromeUtilityHostMsg_ParseMediaMetadata_Finished(
82 true, *metadata
.ToValue(), attached_images
));
83 ReleaseProcessIfNeeded();
87 #if !defined(OS_ANDROID)
88 void CreateProxyResolverFactory(
89 mojo::InterfaceRequest
<net::interfaces::ProxyResolverFactory
> request
) {
90 // MojoProxyResolverFactoryImpl is strongly bound to the Mojo message pipe it
91 // is connected to. When that message pipe is closed, either explicitly on the
92 // other end (in the browser process), or by a connection error, this object
94 new net::MojoProxyResolverFactoryImpl(request
.Pass());
97 class ResourceUsageReporterImpl
: public ResourceUsageReporter
{
99 explicit ResourceUsageReporterImpl(
100 mojo::InterfaceRequest
<ResourceUsageReporter
> req
)
101 : binding_(this, req
.Pass()) {}
102 ~ResourceUsageReporterImpl() override
{}
106 const mojo::Callback
<void(ResourceUsageDataPtr
)>& callback
) override
{
107 ResourceUsageDataPtr data
= ResourceUsageData::New();
108 size_t total_heap_size
= net::ProxyResolverV8::GetTotalHeapSize();
109 if (total_heap_size
) {
110 data
->reports_v8_stats
= true;
111 data
->v8_bytes_allocated
= total_heap_size
;
112 data
->v8_bytes_used
= net::ProxyResolverV8::GetUsedHeapSize();
114 callback
.Run(data
.Pass());
117 mojo::StrongBinding
<ResourceUsageReporter
> binding_
;
120 void CreateResourceUsageReporter(
121 mojo::InterfaceRequest
<ResourceUsageReporter
> request
) {
122 new ResourceUsageReporterImpl(request
.Pass());
128 int64_t ChromeContentUtilityClient::max_ipc_message_size_
=
129 IPC::Channel::kMaximumMessageSize
;
131 ChromeContentUtilityClient::ChromeContentUtilityClient()
132 : filter_messages_(false) {
133 #if !defined(OS_ANDROID)
134 handlers_
.push_back(new ProfileImportHandler());
137 #if defined(ENABLE_EXTENSIONS)
138 handlers_
.push_back(new extensions::ExtensionsHandler());
139 handlers_
.push_back(new image_writer::ImageWriterHandler());
142 #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
143 handlers_
.push_back(new PrintingHandler());
146 #if defined(ENABLE_MDNS)
147 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
148 switches::kUtilityProcessEnableMDns
)) {
149 handlers_
.push_back(new local_discovery::ServiceDiscoveryMessageHandler());
154 handlers_
.push_back(new ShellHandler());
155 handlers_
.push_back(new FontCacheHandler());
158 handlers_
.push_back(new SafeJsonParserHandler());
161 ChromeContentUtilityClient::~ChromeContentUtilityClient() {
164 void ChromeContentUtilityClient::UtilityThreadStarted() {
165 #if defined(ENABLE_EXTENSIONS)
166 extensions::UtilityHandler::UtilityThreadStarted();
169 if (kMessageWhitelistSize
> 0) {
170 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
171 if (command_line
->HasSwitch(switches::kUtilityProcessRunningElevated
)) {
172 message_id_whitelist_
.insert(kMessageWhitelist
,
173 kMessageWhitelist
+ kMessageWhitelistSize
);
174 filter_messages_
= true;
179 bool ChromeContentUtilityClient::OnMessageReceived(
180 const IPC::Message
& message
) {
181 if (filter_messages_
&& !ContainsKey(message_id_whitelist_
, message
.type()))
185 IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient
, message
)
186 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage
, OnDecodeImage
)
187 #if defined(OS_CHROMEOS)
188 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustJPEGDecodeImage
,
189 OnRobustJPEGDecodeImage
)
190 #endif // defined(OS_CHROMEOS)
191 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileBsdiff
,
193 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileCourgette
,
194 OnPatchFileCourgette
)
195 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing
, OnStartupPing
)
196 #if defined(FULL_SAFE_BROWSING)
197 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection
,
198 OnAnalyzeZipFileForDownloadProtection
)
199 #if defined(OS_MACOSX)
200 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeDmgFileForDownloadProtection
,
201 OnAnalyzeDmgFileForDownloadProtection
)
204 #if defined(ENABLE_EXTENSIONS)
205 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseMediaMetadata
,
206 OnParseMediaMetadata
)
208 #if defined(OS_CHROMEOS)
209 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CreateZipFile
, OnCreateZipFile
)
211 IPC_MESSAGE_UNHANDLED(handled
= false)
212 IPC_END_MESSAGE_MAP()
214 for (Handlers::iterator it
= handlers_
.begin();
215 !handled
&& it
!= handlers_
.end(); ++it
) {
216 handled
= (*it
)->OnMessageReceived(message
);
222 void ChromeContentUtilityClient::RegisterMojoServices(
223 content::ServiceRegistry
* registry
) {
224 #if !defined(OS_ANDROID)
225 registry
->AddService
<net::interfaces::ProxyResolverFactory
>(
226 base::Bind(CreateProxyResolverFactory
));
227 registry
->AddService
<ResourceUsageReporter
>(
228 base::Bind(CreateResourceUsageReporter
));
233 void ChromeContentUtilityClient::PreSandboxStartup() {
234 #if defined(ENABLE_EXTENSIONS)
235 extensions::ExtensionsHandler::PreSandboxStartup();
238 #if defined(ENABLE_MDNS)
239 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
240 switches::kUtilityProcessEnableMDns
)) {
241 local_discovery::ServiceDiscoveryMessageHandler::PreSandboxStartup();
243 #endif // ENABLE_MDNS
247 SkBitmap
ChromeContentUtilityClient::DecodeImage(
248 const std::vector
<unsigned char>& encoded_data
, bool shrink_to_fit
) {
249 SkBitmap decoded_image
;
250 if (encoded_data
.empty())
251 return decoded_image
;
253 decoded_image
= content::DecodeImage(&encoded_data
[0],
255 encoded_data
.size());
257 int64_t struct_size
= sizeof(ChromeUtilityHostMsg_DecodeImage_Succeeded
);
258 int64_t image_size
= decoded_image
.computeSize64();
260 while (struct_size
+ (image_size
>> 2*halves
) > max_ipc_message_size_
)
264 // If decoded image is too large for IPC message, shrink it by halves.
265 // This prevents quality loss, and should never overshrink on displays
266 // smaller than 3600x2400.
267 // TODO (Issue 416916): Instead of shrinking, return via shared memory
268 decoded_image
= skia::ImageOperations::Resize(
269 decoded_image
, skia::ImageOperations::RESIZE_LANCZOS3
,
270 decoded_image
.width() >> halves
, decoded_image
.height() >> halves
);
272 // Image too big for IPC message, but caller didn't request resize;
273 // pre-delete image so DecodeImageAndSend() will send an error.
274 decoded_image
.reset();
275 LOG(ERROR
) << "Decoded image too large for IPC message";
279 return decoded_image
;
283 void ChromeContentUtilityClient::DecodeImageAndSend(
284 const std::vector
<unsigned char>& encoded_data
,
287 SkBitmap decoded_image
= DecodeImage(encoded_data
, shrink_to_fit
);
289 if (decoded_image
.empty()) {
290 Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id
));
292 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image
,
295 ReleaseProcessIfNeeded();
298 void ChromeContentUtilityClient::OnDecodeImage(
299 const std::vector
<unsigned char>& encoded_data
,
302 content::UtilityThread::Get()->EnsureBlinkInitialized();
303 DecodeImageAndSend(encoded_data
, shrink_to_fit
, request_id
);
306 #if defined(OS_CHROMEOS)
307 void ChromeContentUtilityClient::OnCreateZipFile(
308 const base::FilePath
& src_dir
,
309 const std::vector
<base::FilePath
>& src_relative_paths
,
310 const base::FileDescriptor
& dest_fd
) {
311 // dest_fd should be closed in the function. See ipc/ipc_message_util.h for
313 base::ScopedFD
fd_closer(dest_fd
.fd
);
314 bool succeeded
= true;
316 // Check sanity of source relative paths. Reject if path is absolute or
317 // contains any attempt to reference a parent directory ("../" tricks).
318 for (std::vector
<base::FilePath
>::const_iterator iter
=
319 src_relative_paths
.begin(); iter
!= src_relative_paths
.end();
321 if (iter
->IsAbsolute() || iter
->ReferencesParent()) {
328 succeeded
= zip::ZipFiles(src_dir
, src_relative_paths
, dest_fd
.fd
);
331 Send(new ChromeUtilityHostMsg_CreateZipFile_Succeeded());
333 Send(new ChromeUtilityHostMsg_CreateZipFile_Failed());
334 ReleaseProcessIfNeeded();
336 #endif // defined(OS_CHROMEOS)
338 #if defined(OS_CHROMEOS)
339 void ChromeContentUtilityClient::OnRobustJPEGDecodeImage(
340 const std::vector
<unsigned char>& encoded_data
,
342 // Our robust jpeg decoding is using IJG libjpeg.
343 if (!encoded_data
.empty()) {
344 scoped_ptr
<SkBitmap
> decoded_image(gfx::JPEGCodecRobustSlow::Decode(
345 &encoded_data
[0], encoded_data
.size()));
346 if (!decoded_image
.get() || decoded_image
->empty()) {
347 Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id
));
349 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image
,
353 Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id
));
355 ReleaseProcessIfNeeded();
357 #endif // defined(OS_CHROMEOS)
359 void ChromeContentUtilityClient::OnPatchFileBsdiff(
360 const base::FilePath
& input_file
,
361 const base::FilePath
& patch_file
,
362 const base::FilePath
& output_file
) {
363 if (input_file
.empty() || patch_file
.empty() || output_file
.empty()) {
364 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
366 const int patch_status
= courgette::ApplyBinaryPatch(input_file
,
369 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status
));
371 ReleaseProcessIfNeeded();
374 void ChromeContentUtilityClient::OnPatchFileCourgette(
375 const base::FilePath
& input_file
,
376 const base::FilePath
& patch_file
,
377 const base::FilePath
& output_file
) {
378 if (input_file
.empty() || patch_file
.empty() || output_file
.empty()) {
379 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
381 const int patch_status
= courgette::ApplyEnsemblePatch(
382 input_file
.value().c_str(),
383 patch_file
.value().c_str(),
384 output_file
.value().c_str());
385 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status
));
387 ReleaseProcessIfNeeded();
390 void ChromeContentUtilityClient::OnStartupPing() {
391 Send(new ChromeUtilityHostMsg_ProcessStarted
);
392 // Don't release the process, we assume further messages are on the way.
395 #if defined(FULL_SAFE_BROWSING)
396 void ChromeContentUtilityClient::OnAnalyzeZipFileForDownloadProtection(
397 const IPC::PlatformFileForTransit
& zip_file
,
398 const IPC::PlatformFileForTransit
& temp_file
) {
399 safe_browsing::zip_analyzer::Results results
;
400 safe_browsing::zip_analyzer::AnalyzeZipFile(
401 IPC::PlatformFileForTransitToFile(zip_file
),
402 IPC::PlatformFileForTransitToFile(temp_file
), &results
);
403 Send(new ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished(
405 ReleaseProcessIfNeeded();
408 #if defined(OS_MACOSX)
409 void ChromeContentUtilityClient::OnAnalyzeDmgFileForDownloadProtection(
410 const IPC::PlatformFileForTransit
& dmg_file
) {
411 safe_browsing::zip_analyzer::Results results
;
412 safe_browsing::dmg::AnalyzeDMGFile(
413 IPC::PlatformFileForTransitToFile(dmg_file
), &results
);
414 Send(new ChromeUtilityHostMsg_AnalyzeDmgFileForDownloadProtection_Finished(
416 ReleaseProcessIfNeeded();
422 #if defined(ENABLE_EXTENSIONS)
423 // TODO(thestig): Try to move this to
424 // chrome/utility/extensions/extensions_handler.cc.
425 void ChromeContentUtilityClient::OnParseMediaMetadata(
426 const std::string
& mime_type
, int64 total_size
, bool get_attached_images
) {
427 // Only one IPCDataSource may be created and added to the list of handlers.
428 metadata::IPCDataSource
* source
= new metadata::IPCDataSource(total_size
);
429 handlers_
.push_back(source
);
431 metadata::MediaMetadataParser
* parser
= new metadata::MediaMetadataParser(
432 source
, mime_type
, get_attached_images
);
433 parser
->Start(base::Bind(&FinishParseMediaMetadata
, base::Owned(parser
)));