• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/utility/chrome_content_utility_client.h"
6 
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"
27 
28 #if !defined(OS_ANDROID)
29 #include "chrome/utility/profile_import_handler.h"
30 #endif
31 
32 #if defined(OS_WIN)
33 #include "chrome/utility/shell_handler_win.h"
34 #endif
35 
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"
42 #endif
43 
44 #if defined(ENABLE_FULL_PRINTING) || defined(OS_WIN)
45 #include "chrome/utility/printing_handler.h"
46 #endif
47 
48 #if defined(ENABLE_MDNS)
49 #include "chrome/utility/local_discovery/service_discovery_message_handler.h"
50 #endif
51 
52 namespace {
53 
Send(IPC::Message * message)54 bool Send(IPC::Message* message) {
55   return content::UtilityThread::Get()->Send(message);
56 }
57 
ReleaseProcessIfNeeded()58 void ReleaseProcessIfNeeded() {
59   content::UtilityThread::Get()->ReleaseProcessIfNeeded();
60 }
61 
62 #if defined(ENABLE_EXTENSIONS)
FinishParseMediaMetadata(metadata::MediaMetadataParser *,const extensions::api::media_galleries::MediaMetadata & metadata,const std::vector<metadata::AttachedImage> & attached_images)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();
70 }
71 #endif
72 
73 }  // namespace
74 
75 int64_t ChromeContentUtilityClient::max_ipc_message_size_ =
76     IPC::Channel::kMaximumMessageSize;
77 
ChromeContentUtilityClient()78 ChromeContentUtilityClient::ChromeContentUtilityClient()
79     : filter_messages_(false) {
80 #if !defined(OS_ANDROID)
81   handlers_.push_back(new ProfileImportHandler());
82 #endif
83 
84 #if defined(ENABLE_EXTENSIONS)
85   handlers_.push_back(new extensions::ExtensionsHandler());
86   handlers_.push_back(new image_writer::ImageWriterHandler());
87 #endif
88 
89 #if defined(ENABLE_FULL_PRINTING) || defined(OS_WIN)
90   handlers_.push_back(new PrintingHandler());
91 #endif
92 
93 #if defined(ENABLE_MDNS)
94   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
95           switches::kUtilityProcessEnableMDns)) {
96     handlers_.push_back(new local_discovery::ServiceDiscoveryMessageHandler());
97   }
98 #endif
99 
100 #if defined(OS_WIN)
101   handlers_.push_back(new ShellHandler());
102 #endif
103 }
104 
~ChromeContentUtilityClient()105 ChromeContentUtilityClient::~ChromeContentUtilityClient() {
106 }
107 
UtilityThreadStarted()108 void ChromeContentUtilityClient::UtilityThreadStarted() {
109 #if defined(ENABLE_EXTENSIONS)
110   extensions::ExtensionsHandler::UtilityThreadStarted();
111 #endif
112 
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;
119     }
120   }
121 }
122 
OnMessageReceived(const IPC::Message & message)123 bool ChromeContentUtilityClient::OnMessageReceived(
124     const IPC::Message& message) {
125   if (filter_messages_ && !ContainsKey(message_id_whitelist_, message.type()))
126     return false;
127 
128   bool handled = true;
129   IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient, message)
130     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_UnpackWebResource,
131                         OnUnpackWebResource)
132     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage, OnDecodeImage)
133     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustJPEGDecodeImage,
134                         OnRobustJPEGDecodeImage)
135     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileBsdiff,
136                         OnPatchFileBsdiff)
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)
143 #endif
144 #if defined(ENABLE_EXTENSIONS)
145     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseMediaMetadata,
146                         OnParseMediaMetadata)
147 #endif
148 #if defined(OS_CHROMEOS)
149     IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CreateZipFile, OnCreateZipFile)
150 #endif
151     IPC_MESSAGE_UNHANDLED(handled = false)
152   IPC_END_MESSAGE_MAP()
153 
154   for (Handlers::iterator it = handlers_.begin();
155        !handled && it != handlers_.end(); ++it) {
156     handled = (*it)->OnMessageReceived(message);
157   }
158 
159   return handled;
160 }
161 
162 // static
PreSandboxStartup()163 void ChromeContentUtilityClient::PreSandboxStartup() {
164 #if defined(ENABLE_EXTENSIONS)
165   extensions::ExtensionsHandler::PreSandboxStartup();
166 #endif
167 
168 #if defined(ENABLE_FULL_PRINTING) || defined(OS_WIN)
169   PrintingHandler::PreSandboxStartup();
170 #endif
171 
172 #if defined(ENABLE_MDNS)
173   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
174           switches::kUtilityProcessEnableMDns)) {
175     local_discovery::ServiceDiscoveryMessageHandler::PreSandboxStartup();
176   }
177 #endif  // ENABLE_MDNS
178 }
179 
180 // static
DecodeImage(const std::vector<unsigned char> & encoded_data,bool shrink_to_fit)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],
184                                                 gfx::Size(),
185                                                 encoded_data.size());
186 
187   int64_t struct_size = sizeof(ChromeUtilityHostMsg_DecodeImage_Succeeded);
188   int64_t image_size = decoded_image.computeSize64();
189   int halves = 0;
190   while (struct_size + (image_size >> 2*halves) > max_ipc_message_size_)
191     halves++;
192   if (halves) {
193     if (shrink_to_fit) {
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);
201     } else {
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";
206     }
207   }
208 
209   return decoded_image;
210 }
211 
212 // static
DecodeImageAndSend(const std::vector<unsigned char> & encoded_data,bool shrink_to_fit)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);
216 
217   if (decoded_image.empty()) {
218     Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
219   } else {
220     Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image));
221   }
222   ReleaseProcessIfNeeded();
223 }
224 
OnUnpackWebResource(const std::string & resource_data)225 void ChromeContentUtilityClient::OnUnpackWebResource(
226     const std::string& resource_data) {
227   // Parse json 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()));
234   } else {
235     Send(new ChromeUtilityHostMsg_UnpackWebResource_Failed(
236         unpacker.error_message()));
237   }
238 
239   ReleaseProcessIfNeeded();
240 }
241 
OnDecodeImage(const std::vector<unsigned char> & encoded_data,bool shrink_to_fit)242 void ChromeContentUtilityClient::OnDecodeImage(
243     const std::vector<unsigned char>& encoded_data, bool shrink_to_fit) {
244   DecodeImageAndSend(encoded_data, shrink_to_fit);
245 }
246 
247 #if defined(OS_CHROMEOS)
OnCreateZipFile(const base::FilePath & src_dir,const std::vector<base::FilePath> & src_relative_paths,const base::FileDescriptor & dest_fd)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;
253 
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();
258        ++iter) {
259     if (iter->IsAbsolute() || iter->ReferencesParent()) {
260       succeeded = false;
261       break;
262     }
263   }
264 
265   if (succeeded)
266     succeeded = zip::ZipFiles(src_dir, src_relative_paths, dest_fd.fd);
267 
268   if (succeeded)
269     Send(new ChromeUtilityHostMsg_CreateZipFile_Succeeded());
270   else
271     Send(new ChromeUtilityHostMsg_CreateZipFile_Failed());
272   ReleaseProcessIfNeeded();
273 }
274 #endif  // defined(OS_CHROMEOS)
275 
OnRobustJPEGDecodeImage(const std::vector<unsigned char> & encoded_data)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());
285     } else {
286       Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image));
287     }
288   } else {
289     Send(new ChromeUtilityHostMsg_DecodeImage_Failed());
290   }
291   ReleaseProcessIfNeeded();
292 }
293 
OnPatchFileBsdiff(const base::FilePath & input_file,const base::FilePath & patch_file,const base::FilePath & output_file)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));
300   } else {
301     const int patch_status = courgette::ApplyBinaryPatch(input_file,
302                                                          patch_file,
303                                                          output_file);
304     Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status));
305   }
306   ReleaseProcessIfNeeded();
307 }
308 
OnPatchFileCourgette(const base::FilePath & input_file,const base::FilePath & patch_file,const base::FilePath & output_file)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));
315   } else {
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));
321   }
322   ReleaseProcessIfNeeded();
323 }
324 
OnStartupPing()325 void ChromeContentUtilityClient::OnStartupPing() {
326   Send(new ChromeUtilityHostMsg_ProcessStarted);
327   // Don't release the process, we assume further messages are on the way.
328 }
329 
330 #if defined(FULL_SAFE_BROWSING)
OnAnalyzeZipFileForDownloadProtection(const IPC::PlatformFileForTransit & zip_file)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(
337       results));
338   ReleaseProcessIfNeeded();
339 }
340 #endif
341 
342 #if defined(ENABLE_EXTENSIONS)
343 // TODO(thestig): Try to move this to
344 // chrome/utility/extensions/extensions_handler.cc.
OnParseMediaMetadata(const std::string & mime_type,int64 total_size,bool get_attached_images)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);
350 
351   metadata::MediaMetadataParser* parser = new metadata::MediaMetadataParser(
352       source, mime_type, get_attached_images);
353   parser->Start(base::Bind(&FinishParseMediaMetadata, base::Owned(parser)));
354 }
355 #endif
356