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/json/json_reader.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/time/time.h"
12 #include "chrome/common/chrome_utility_messages.h"
13 #include "chrome/common/safe_browsing/zip_analyzer.h"
14 #include "chrome/common/safe_browsing/zip_analyzer_results.h"
15 #include "chrome/utility/chrome_content_utility_ipc_whitelist.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/codec/jpeg_codec.h"
28 #include "ui/gfx/geometry/size.h"
30 #if !defined(OS_ANDROID)
31 #include "chrome/utility/profile_import_handler.h"
32 #include "net/proxy/mojo_proxy_resolver_factory_impl.h"
35 #if defined(OS_ANDROID) && defined(USE_SECCOMP_BPF)
36 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
40 #include "chrome/utility/font_cache_handler_win.h"
41 #include "chrome/utility/shell_handler_win.h"
44 #if defined(ENABLE_EXTENSIONS)
45 #include "chrome/common/extensions/chrome_utility_extensions_messages.h"
46 #include "chrome/utility/extensions/extensions_handler.h"
47 #include "chrome/utility/image_writer/image_writer_handler.h"
48 #include "chrome/utility/media_galleries/ipc_data_source.h"
49 #include "chrome/utility/media_galleries/media_metadata_parser.h"
52 #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
53 #include "chrome/utility/printing_handler.h"
56 #if defined(ENABLE_MDNS)
57 #include "chrome/utility/local_discovery/service_discovery_message_handler.h"
62 bool Send(IPC::Message
* message
) {
63 return content::UtilityThread::Get()->Send(message
);
66 void ReleaseProcessIfNeeded() {
67 content::UtilityThread::Get()->ReleaseProcessIfNeeded();
70 #if defined(ENABLE_EXTENSIONS)
71 void FinishParseMediaMetadata(
72 metadata::MediaMetadataParser
* /* parser */,
73 const extensions::api::media_galleries::MediaMetadata
& metadata
,
74 const std::vector
<metadata::AttachedImage
>& attached_images
) {
75 Send(new ChromeUtilityHostMsg_ParseMediaMetadata_Finished(
76 true, *metadata
.ToValue(), attached_images
));
77 ReleaseProcessIfNeeded();
81 #if !defined(OS_ANDROID)
82 void CreateProxyResolverFactory(
83 mojo::InterfaceRequest
<net::interfaces::ProxyResolverFactory
> request
) {
84 // MojoProxyResolverFactoryImpl is strongly bound to the Mojo message pipe it
85 // is connected to. When that message pipe is closed, either explicitly on the
86 // other end (in the browser process), or by a connection error, this object
88 new net::MojoProxyResolverFactoryImpl(request
.Pass());
94 int64_t ChromeContentUtilityClient::max_ipc_message_size_
=
95 IPC::Channel::kMaximumMessageSize
;
97 ChromeContentUtilityClient::ChromeContentUtilityClient()
98 : filter_messages_(false) {
99 #if !defined(OS_ANDROID)
100 handlers_
.push_back(new ProfileImportHandler());
103 #if defined(ENABLE_EXTENSIONS)
104 handlers_
.push_back(new extensions::ExtensionsHandler());
105 handlers_
.push_back(new image_writer::ImageWriterHandler());
108 #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
109 handlers_
.push_back(new PrintingHandler());
112 #if defined(ENABLE_MDNS)
113 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
114 switches::kUtilityProcessEnableMDns
)) {
115 handlers_
.push_back(new local_discovery::ServiceDiscoveryMessageHandler());
120 handlers_
.push_back(new ShellHandler());
121 handlers_
.push_back(new FontCacheHandler());
125 ChromeContentUtilityClient::~ChromeContentUtilityClient() {
128 void ChromeContentUtilityClient::UtilityThreadStarted() {
129 #if defined(ENABLE_EXTENSIONS)
130 extensions::UtilityHandler::UtilityThreadStarted();
133 if (kMessageWhitelistSize
> 0) {
134 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
135 if (command_line
->HasSwitch(switches::kUtilityProcessRunningElevated
)) {
136 message_id_whitelist_
.insert(kMessageWhitelist
,
137 kMessageWhitelist
+ kMessageWhitelistSize
);
138 filter_messages_
= true;
143 bool ChromeContentUtilityClient::OnMessageReceived(
144 const IPC::Message
& message
) {
145 if (filter_messages_
&& !ContainsKey(message_id_whitelist_
, message
.type()))
149 IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient
, message
)
150 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage
, OnDecodeImage
)
151 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustJPEGDecodeImage
,
152 OnRobustJPEGDecodeImage
)
153 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseJSON
, OnParseJSON
)
154 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileBsdiff
,
156 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileCourgette
,
157 OnPatchFileCourgette
)
158 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing
, OnStartupPing
)
159 #if defined(FULL_SAFE_BROWSING)
160 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection
,
161 OnAnalyzeZipFileForDownloadProtection
)
163 #if defined(ENABLE_EXTENSIONS)
164 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseMediaMetadata
,
165 OnParseMediaMetadata
)
167 #if defined(OS_CHROMEOS)
168 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CreateZipFile
, OnCreateZipFile
)
170 #if defined(OS_ANDROID) && defined(USE_SECCOMP_BPF)
171 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DetectSeccompSupport
,
172 OnDetectSeccompSupport
)
174 IPC_MESSAGE_UNHANDLED(handled
= false)
175 IPC_END_MESSAGE_MAP()
177 for (Handlers::iterator it
= handlers_
.begin();
178 !handled
&& it
!= handlers_
.end(); ++it
) {
179 handled
= (*it
)->OnMessageReceived(message
);
185 void ChromeContentUtilityClient::RegisterMojoServices(
186 content::ServiceRegistry
* registry
) {
187 #if !defined(OS_ANDROID)
188 registry
->AddService
<net::interfaces::ProxyResolverFactory
>(
189 base::Bind(CreateProxyResolverFactory
));
194 void ChromeContentUtilityClient::PreSandboxStartup() {
195 #if defined(ENABLE_EXTENSIONS)
196 extensions::ExtensionsHandler::PreSandboxStartup();
199 #if defined(ENABLE_MDNS)
200 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
201 switches::kUtilityProcessEnableMDns
)) {
202 local_discovery::ServiceDiscoveryMessageHandler::PreSandboxStartup();
204 #endif // ENABLE_MDNS
208 SkBitmap
ChromeContentUtilityClient::DecodeImage(
209 const std::vector
<unsigned char>& encoded_data
, bool shrink_to_fit
) {
210 SkBitmap decoded_image
= content::DecodeImage(&encoded_data
[0],
212 encoded_data
.size());
214 int64_t struct_size
= sizeof(ChromeUtilityHostMsg_DecodeImage_Succeeded
);
215 int64_t image_size
= decoded_image
.computeSize64();
217 while (struct_size
+ (image_size
>> 2*halves
) > max_ipc_message_size_
)
221 // If decoded image is too large for IPC message, shrink it by halves.
222 // This prevents quality loss, and should never overshrink on displays
223 // smaller than 3600x2400.
224 // TODO (Issue 416916): Instead of shrinking, return via shared memory
225 decoded_image
= skia::ImageOperations::Resize(
226 decoded_image
, skia::ImageOperations::RESIZE_LANCZOS3
,
227 decoded_image
.width() >> halves
, decoded_image
.height() >> halves
);
229 // Image too big for IPC message, but caller didn't request resize;
230 // pre-delete image so DecodeImageAndSend() will send an error.
231 decoded_image
.reset();
232 LOG(ERROR
) << "Decoded image too large for IPC message";
236 return decoded_image
;
240 void ChromeContentUtilityClient::DecodeImageAndSend(
241 const std::vector
<unsigned char>& encoded_data
, bool shrink_to_fit
){
242 SkBitmap decoded_image
= DecodeImage(encoded_data
, shrink_to_fit
);
244 if (decoded_image
.empty()) {
245 Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
247 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image
));
249 ReleaseProcessIfNeeded();
252 void ChromeContentUtilityClient::OnDecodeImage(
253 const std::vector
<unsigned char>& encoded_data
, bool shrink_to_fit
) {
254 DecodeImageAndSend(encoded_data
, shrink_to_fit
);
257 #if defined(OS_CHROMEOS)
258 void ChromeContentUtilityClient::OnCreateZipFile(
259 const base::FilePath
& src_dir
,
260 const std::vector
<base::FilePath
>& src_relative_paths
,
261 const base::FileDescriptor
& dest_fd
) {
262 // dest_fd should be closed in the function. See ipc/ipc_message_util.h for
264 base::ScopedFD
fd_closer(dest_fd
.fd
);
265 bool succeeded
= true;
267 // Check sanity of source relative paths. Reject if path is absolute or
268 // contains any attempt to reference a parent directory ("../" tricks).
269 for (std::vector
<base::FilePath
>::const_iterator iter
=
270 src_relative_paths
.begin(); iter
!= src_relative_paths
.end();
272 if (iter
->IsAbsolute() || iter
->ReferencesParent()) {
279 succeeded
= zip::ZipFiles(src_dir
, src_relative_paths
, dest_fd
.fd
);
282 Send(new ChromeUtilityHostMsg_CreateZipFile_Succeeded());
284 Send(new ChromeUtilityHostMsg_CreateZipFile_Failed());
285 ReleaseProcessIfNeeded();
287 #endif // defined(OS_CHROMEOS)
289 #if defined(OS_ANDROID) && defined(USE_SECCOMP_BPF)
290 void ChromeContentUtilityClient::OnDetectSeccompSupport() {
291 bool supports_prctl
= sandbox::SandboxBPF::SupportsSeccompSandbox(
292 sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED
);
293 Send(new ChromeUtilityHostMsg_DetectSeccompSupport_ResultPrctl(
296 bool supports_syscall
= sandbox::SandboxBPF::SupportsSeccompSandbox(
297 sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED
);
298 Send(new ChromeUtilityHostMsg_DetectSeccompSupport_ResultSyscall(
301 ReleaseProcessIfNeeded();
303 #endif // defined(OS_ANDROID) && defined(USE_SECCOMP_BPF)
305 void ChromeContentUtilityClient::OnRobustJPEGDecodeImage(
306 const std::vector
<unsigned char>& encoded_data
) {
307 // Our robust jpeg decoding is using IJG libjpeg.
308 if (gfx::JPEGCodec::JpegLibraryVariant() == gfx::JPEGCodec::IJG_LIBJPEG
&&
309 !encoded_data
.empty()) {
310 scoped_ptr
<SkBitmap
> decoded_image(gfx::JPEGCodec::Decode(
311 &encoded_data
[0], encoded_data
.size()));
312 if (!decoded_image
.get() || decoded_image
->empty()) {
313 Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
315 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image
));
318 Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
320 ReleaseProcessIfNeeded();
323 void ChromeContentUtilityClient::OnParseJSON(const std::string
& json
) {
326 base::Value
* value
= base::JSONReader::ReadAndReturnError(
327 json
, base::JSON_PARSE_RFC
, &error_code
, &error
);
329 base::ListValue wrapper
;
330 wrapper
.Append(value
);
331 Send(new ChromeUtilityHostMsg_ParseJSON_Succeeded(wrapper
));
333 Send(new ChromeUtilityHostMsg_ParseJSON_Failed(error
));
335 ReleaseProcessIfNeeded();
338 void ChromeContentUtilityClient::OnPatchFileBsdiff(
339 const base::FilePath
& input_file
,
340 const base::FilePath
& patch_file
,
341 const base::FilePath
& output_file
) {
342 if (input_file
.empty() || patch_file
.empty() || output_file
.empty()) {
343 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
345 const int patch_status
= courgette::ApplyBinaryPatch(input_file
,
348 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status
));
350 ReleaseProcessIfNeeded();
353 void ChromeContentUtilityClient::OnPatchFileCourgette(
354 const base::FilePath
& input_file
,
355 const base::FilePath
& patch_file
,
356 const base::FilePath
& output_file
) {
357 if (input_file
.empty() || patch_file
.empty() || output_file
.empty()) {
358 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
360 const int patch_status
= courgette::ApplyEnsemblePatch(
361 input_file
.value().c_str(),
362 patch_file
.value().c_str(),
363 output_file
.value().c_str());
364 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status
));
366 ReleaseProcessIfNeeded();
369 void ChromeContentUtilityClient::OnStartupPing() {
370 Send(new ChromeUtilityHostMsg_ProcessStarted
);
371 // Don't release the process, we assume further messages are on the way.
374 #if defined(FULL_SAFE_BROWSING)
375 void ChromeContentUtilityClient::OnAnalyzeZipFileForDownloadProtection(
376 const IPC::PlatformFileForTransit
& zip_file
,
377 const IPC::PlatformFileForTransit
& temp_file
) {
378 safe_browsing::zip_analyzer::Results results
;
379 safe_browsing::zip_analyzer::AnalyzeZipFile(
380 IPC::PlatformFileForTransitToFile(zip_file
),
381 IPC::PlatformFileForTransitToFile(temp_file
), &results
);
382 Send(new ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished(
384 ReleaseProcessIfNeeded();
388 #if defined(ENABLE_EXTENSIONS)
389 // TODO(thestig): Try to move this to
390 // chrome/utility/extensions/extensions_handler.cc.
391 void ChromeContentUtilityClient::OnParseMediaMetadata(
392 const std::string
& mime_type
, int64 total_size
, bool get_attached_images
) {
393 // Only one IPCDataSource may be created and added to the list of handlers.
394 metadata::IPCDataSource
* source
= new metadata::IPCDataSource(total_size
);
395 handlers_
.push_back(source
);
397 metadata::MediaMetadataParser
* parser
= new metadata::MediaMetadataParser(
398 source
, mime_type
, get_attached_images
);
399 parser
->Start(base::Bind(&FinishParseMediaMetadata
, base::Owned(parser
)));