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/utility/chrome_content_utility_ipc_whitelist.h"
14 #include "chrome/utility/utility_message_handler.h"
15 #include "chrome/utility/web_resource_unpacker.h"
16 #include "content/public/child/image_decoder_utils.h"
17 #include "content/public/common/content_switches.h"
18 #include "content/public/utility/utility_thread.h"
19 #include "courgette/courgette.h"
20 #include "courgette/third_party/bsdiff.h"
21 #include "ipc/ipc_channel.h"
22 #include "skia/ext/image_operations.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24 #include "third_party/zlib/google/zip.h"
25 #include "ui/gfx/codec/jpeg_codec.h"
26 #include "ui/gfx/size.h"
28 #if !defined(OS_ANDROID)
29 #include "chrome/utility/profile_import_handler.h"
33 #include "chrome/utility/shell_handler_win.h"
36 #if defined(ENABLE_EXTENSIONS)
37 #include "chrome/common/extensions/chrome_utility_extensions_messages.h"
38 #include "chrome/utility/extensions/extensions_handler.h"
39 #include "chrome/utility/image_writer/image_writer_handler.h"
40 #include "chrome/utility/media_galleries/ipc_data_source.h"
41 #include "chrome/utility/media_galleries/media_metadata_parser.h"
44 #if defined(ENABLE_FULL_PRINTING) || defined(OS_WIN)
45 #include "chrome/utility/printing_handler.h"
48 #if defined(ENABLE_MDNS)
49 #include "chrome/utility/local_discovery/service_discovery_message_handler.h"
54 bool Send(IPC::Message
* message
) {
55 return content::UtilityThread::Get()->Send(message
);
58 void ReleaseProcessIfNeeded() {
59 content::UtilityThread::Get()->ReleaseProcessIfNeeded();
62 #if defined(ENABLE_EXTENSIONS)
63 void FinishParseMediaMetadata(
64 metadata::MediaMetadataParser
* /* parser */,
65 const extensions::api::media_galleries::MediaMetadata
& metadata
,
66 const std::vector
<metadata::AttachedImage
>& attached_images
) {
67 Send(new ChromeUtilityHostMsg_ParseMediaMetadata_Finished(
68 true, *metadata
.ToValue(), attached_images
));
69 ReleaseProcessIfNeeded();
75 int64_t ChromeContentUtilityClient::max_ipc_message_size_
=
76 IPC::Channel::kMaximumMessageSize
;
78 ChromeContentUtilityClient::ChromeContentUtilityClient()
79 : filter_messages_(false) {
80 #if !defined(OS_ANDROID)
81 handlers_
.push_back(new ProfileImportHandler());
84 #if defined(ENABLE_EXTENSIONS)
85 handlers_
.push_back(new extensions::ExtensionsHandler());
86 handlers_
.push_back(new image_writer::ImageWriterHandler());
89 #if defined(ENABLE_FULL_PRINTING) || defined(OS_WIN)
90 handlers_
.push_back(new PrintingHandler());
93 #if defined(ENABLE_MDNS)
94 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
95 switches::kUtilityProcessEnableMDns
)) {
96 handlers_
.push_back(new local_discovery::ServiceDiscoveryMessageHandler());
101 handlers_
.push_back(new ShellHandler());
105 ChromeContentUtilityClient::~ChromeContentUtilityClient() {
108 void ChromeContentUtilityClient::UtilityThreadStarted() {
109 #if defined(ENABLE_EXTENSIONS)
110 extensions::ExtensionsHandler::UtilityThreadStarted();
113 if (kMessageWhitelistSize
> 0) {
114 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
115 if (command_line
->HasSwitch(switches::kUtilityProcessRunningElevated
)) {
116 message_id_whitelist_
.insert(kMessageWhitelist
,
117 kMessageWhitelist
+ kMessageWhitelistSize
);
118 filter_messages_
= true;
123 bool ChromeContentUtilityClient::OnMessageReceived(
124 const IPC::Message
& message
) {
125 if (filter_messages_
&& !ContainsKey(message_id_whitelist_
, message
.type()))
129 IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient
, message
)
130 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_UnpackWebResource
,
132 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage
, OnDecodeImage
)
133 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustJPEGDecodeImage
,
134 OnRobustJPEGDecodeImage
)
135 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileBsdiff
,
137 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileCourgette
,
138 OnPatchFileCourgette
)
139 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing
, OnStartupPing
)
140 #if defined(FULL_SAFE_BROWSING)
141 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection
,
142 OnAnalyzeZipFileForDownloadProtection
)
144 #if defined(ENABLE_EXTENSIONS)
145 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseMediaMetadata
,
146 OnParseMediaMetadata
)
148 #if defined(OS_CHROMEOS)
149 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CreateZipFile
, OnCreateZipFile
)
151 IPC_MESSAGE_UNHANDLED(handled
= false)
152 IPC_END_MESSAGE_MAP()
154 for (Handlers::iterator it
= handlers_
.begin();
155 !handled
&& it
!= handlers_
.end(); ++it
) {
156 handled
= (*it
)->OnMessageReceived(message
);
163 void ChromeContentUtilityClient::PreSandboxStartup() {
164 #if defined(ENABLE_EXTENSIONS)
165 extensions::ExtensionsHandler::PreSandboxStartup();
168 #if defined(ENABLE_FULL_PRINTING) || defined(OS_WIN)
169 PrintingHandler::PreSandboxStartup();
172 #if defined(ENABLE_MDNS)
173 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
174 switches::kUtilityProcessEnableMDns
)) {
175 local_discovery::ServiceDiscoveryMessageHandler::PreSandboxStartup();
177 #endif // ENABLE_MDNS
181 SkBitmap
ChromeContentUtilityClient::DecodeImage(
182 const std::vector
<unsigned char>& encoded_data
, bool shrink_to_fit
) {
183 SkBitmap decoded_image
= content::DecodeImage(&encoded_data
[0],
185 encoded_data
.size());
187 int64_t struct_size
= sizeof(ChromeUtilityHostMsg_DecodeImage_Succeeded
);
188 int64_t image_size
= decoded_image
.computeSize64();
190 while (struct_size
+ (image_size
>> 2*halves
) > max_ipc_message_size_
)
194 // If decoded image is too large for IPC message, shrink it by halves.
195 // This prevents quality loss, and should never overshrink on displays
196 // smaller than 3600x2400.
197 // TODO (Issue 416916): Instead of shrinking, return via shared memory
198 decoded_image
= skia::ImageOperations::Resize(
199 decoded_image
, skia::ImageOperations::RESIZE_LANCZOS3
,
200 decoded_image
.width() >> halves
, decoded_image
.height() >> halves
);
202 // Image too big for IPC message, but caller didn't request resize;
203 // pre-delete image so DecodeImageAndSend() will send an error.
204 decoded_image
.reset();
205 LOG(ERROR
) << "Decoded image too large for IPC message";
209 return decoded_image
;
213 void ChromeContentUtilityClient::DecodeImageAndSend(
214 const std::vector
<unsigned char>& encoded_data
, bool shrink_to_fit
){
215 SkBitmap decoded_image
= DecodeImage(encoded_data
, shrink_to_fit
);
217 if (decoded_image
.empty()) {
218 Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
220 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image
));
222 ReleaseProcessIfNeeded();
225 void ChromeContentUtilityClient::OnUnpackWebResource(
226 const std::string
& resource_data
) {
228 // TODO(mrc): Add the possibility of a template that controls parsing, and
229 // the ability to download and verify images.
230 WebResourceUnpacker
unpacker(resource_data
);
231 if (unpacker
.Run()) {
232 Send(new ChromeUtilityHostMsg_UnpackWebResource_Succeeded(
233 *unpacker
.parsed_json()));
235 Send(new ChromeUtilityHostMsg_UnpackWebResource_Failed(
236 unpacker
.error_message()));
239 ReleaseProcessIfNeeded();
242 void ChromeContentUtilityClient::OnDecodeImage(
243 const std::vector
<unsigned char>& encoded_data
, bool shrink_to_fit
) {
244 DecodeImageAndSend(encoded_data
, shrink_to_fit
);
247 #if defined(OS_CHROMEOS)
248 void ChromeContentUtilityClient::OnCreateZipFile(
249 const base::FilePath
& src_dir
,
250 const std::vector
<base::FilePath
>& src_relative_paths
,
251 const base::FileDescriptor
& dest_fd
) {
252 bool succeeded
= true;
254 // Check sanity of source relative paths. Reject if path is absolute or
255 // contains any attempt to reference a parent directory ("../" tricks).
256 for (std::vector
<base::FilePath
>::const_iterator iter
=
257 src_relative_paths
.begin(); iter
!= src_relative_paths
.end();
259 if (iter
->IsAbsolute() || iter
->ReferencesParent()) {
266 succeeded
= zip::ZipFiles(src_dir
, src_relative_paths
, dest_fd
.fd
);
269 Send(new ChromeUtilityHostMsg_CreateZipFile_Succeeded());
271 Send(new ChromeUtilityHostMsg_CreateZipFile_Failed());
272 ReleaseProcessIfNeeded();
274 #endif // defined(OS_CHROMEOS)
276 void ChromeContentUtilityClient::OnRobustJPEGDecodeImage(
277 const std::vector
<unsigned char>& encoded_data
) {
278 // Our robust jpeg decoding is using IJG libjpeg.
279 if (gfx::JPEGCodec::JpegLibraryVariant() == gfx::JPEGCodec::IJG_LIBJPEG
&&
280 !encoded_data
.empty()) {
281 scoped_ptr
<SkBitmap
> decoded_image(gfx::JPEGCodec::Decode(
282 &encoded_data
[0], encoded_data
.size()));
283 if (!decoded_image
.get() || decoded_image
->empty()) {
284 Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
286 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image
));
289 Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
291 ReleaseProcessIfNeeded();
294 void ChromeContentUtilityClient::OnPatchFileBsdiff(
295 const base::FilePath
& input_file
,
296 const base::FilePath
& patch_file
,
297 const base::FilePath
& output_file
) {
298 if (input_file
.empty() || patch_file
.empty() || output_file
.empty()) {
299 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
301 const int patch_status
= courgette::ApplyBinaryPatch(input_file
,
304 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status
));
306 ReleaseProcessIfNeeded();
309 void ChromeContentUtilityClient::OnPatchFileCourgette(
310 const base::FilePath
& input_file
,
311 const base::FilePath
& patch_file
,
312 const base::FilePath
& output_file
) {
313 if (input_file
.empty() || patch_file
.empty() || output_file
.empty()) {
314 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
316 const int patch_status
= courgette::ApplyEnsemblePatch(
317 input_file
.value().c_str(),
318 patch_file
.value().c_str(),
319 output_file
.value().c_str());
320 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status
));
322 ReleaseProcessIfNeeded();
325 void ChromeContentUtilityClient::OnStartupPing() {
326 Send(new ChromeUtilityHostMsg_ProcessStarted
);
327 // Don't release the process, we assume further messages are on the way.
330 #if defined(FULL_SAFE_BROWSING)
331 void ChromeContentUtilityClient::OnAnalyzeZipFileForDownloadProtection(
332 const IPC::PlatformFileForTransit
& zip_file
) {
333 safe_browsing::zip_analyzer::Results results
;
334 safe_browsing::zip_analyzer::AnalyzeZipFile(
335 IPC::PlatformFileForTransitToFile(zip_file
), &results
);
336 Send(new ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished(
338 ReleaseProcessIfNeeded();
342 #if defined(ENABLE_EXTENSIONS)
343 // TODO(thestig): Try to move this to
344 // chrome/utility/extensions/extensions_handler.cc.
345 void ChromeContentUtilityClient::OnParseMediaMetadata(
346 const std::string
& mime_type
, int64 total_size
, bool get_attached_images
) {
347 // Only one IPCDataSource may be created and added to the list of handlers.
348 metadata::IPCDataSource
* source
= new metadata::IPCDataSource(total_size
);
349 handlers_
.push_back(source
);
351 metadata::MediaMetadataParser
* parser
= new metadata::MediaMetadataParser(
352 source
, mime_type
, get_attached_images
);
353 parser
->Start(base::Bind(&FinishParseMediaMetadata
, base::Owned(parser
)));