Add ICU message format support
[chromium-blink-merge.git] / chrome / utility / chrome_content_utility_client.cc
blob440ff96a24a140f0bbe74cbdb2943d738af39a93
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"
31 #endif
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"
39 #endif
41 #if defined(OS_WIN)
42 #include "chrome/utility/font_cache_handler_win.h"
43 #include "chrome/utility/shell_handler_win.h"
44 #endif
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"
52 #endif
54 #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
55 #include "chrome/utility/printing_handler.h"
56 #endif
58 #if defined(ENABLE_MDNS)
59 #include "chrome/utility/local_discovery/service_discovery_message_handler.h"
60 #endif
62 namespace {
64 bool Send(IPC::Message* message) {
65 return content::UtilityThread::Get()->Send(message);
68 void ReleaseProcessIfNeeded() {
69 content::UtilityThread::Get()->ReleaseProcessIfNeeded();
72 #if defined(ENABLE_EXTENSIONS)
73 void FinishParseMediaMetadata(
74 metadata::MediaMetadataParser* /* parser */,
75 const extensions::api::media_galleries::MediaMetadata& metadata,
76 const std::vector<metadata::AttachedImage>& attached_images) {
77 Send(new ChromeUtilityHostMsg_ParseMediaMetadata_Finished(
78 true, *metadata.ToValue(), attached_images));
79 ReleaseProcessIfNeeded();
81 #endif
83 #if !defined(OS_ANDROID)
84 void CreateProxyResolverFactory(
85 mojo::InterfaceRequest<net::interfaces::ProxyResolverFactory> request) {
86 // MojoProxyResolverFactoryImpl is strongly bound to the Mojo message pipe it
87 // is connected to. When that message pipe is closed, either explicitly on the
88 // other end (in the browser process), or by a connection error, this object
89 // will be destroyed.
90 new net::MojoProxyResolverFactoryImpl(request.Pass());
93 class ResourceUsageReporterImpl : public ResourceUsageReporter {
94 public:
95 explicit ResourceUsageReporterImpl(
96 mojo::InterfaceRequest<ResourceUsageReporter> req)
97 : binding_(this, req.Pass()) {}
98 ~ResourceUsageReporterImpl() override {}
100 private:
101 void GetUsageData(
102 const mojo::Callback<void(ResourceUsageDataPtr)>& callback) override {
103 ResourceUsageDataPtr data = ResourceUsageData::New();
104 size_t total_heap_size = net::ProxyResolverV8::GetTotalHeapSize();
105 if (total_heap_size) {
106 data->reports_v8_stats = true;
107 data->v8_bytes_allocated = total_heap_size;
108 data->v8_bytes_used = net::ProxyResolverV8::GetUsedHeapSize();
110 callback.Run(data.Pass());
113 mojo::StrongBinding<ResourceUsageReporter> binding_;
116 void CreateResourceUsageReporter(
117 mojo::InterfaceRequest<ResourceUsageReporter> request) {
118 new ResourceUsageReporterImpl(request.Pass());
120 #endif // OS_ANDROID
122 } // namespace
124 int64_t ChromeContentUtilityClient::max_ipc_message_size_ =
125 IPC::Channel::kMaximumMessageSize;
127 ChromeContentUtilityClient::ChromeContentUtilityClient()
128 : filter_messages_(false) {
129 #if !defined(OS_ANDROID)
130 handlers_.push_back(new ProfileImportHandler());
131 #endif
133 #if defined(ENABLE_EXTENSIONS)
134 handlers_.push_back(new extensions::ExtensionsHandler());
135 handlers_.push_back(new image_writer::ImageWriterHandler());
136 #endif
138 #if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN)
139 handlers_.push_back(new PrintingHandler());
140 #endif
142 #if defined(ENABLE_MDNS)
143 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
144 switches::kUtilityProcessEnableMDns)) {
145 handlers_.push_back(new local_discovery::ServiceDiscoveryMessageHandler());
147 #endif
149 #if defined(OS_WIN)
150 handlers_.push_back(new ShellHandler());
151 handlers_.push_back(new FontCacheHandler());
152 #endif
154 handlers_.push_back(new SafeJsonParserHandler());
157 ChromeContentUtilityClient::~ChromeContentUtilityClient() {
160 void ChromeContentUtilityClient::UtilityThreadStarted() {
161 #if defined(ENABLE_EXTENSIONS)
162 extensions::UtilityHandler::UtilityThreadStarted();
163 #endif
165 if (kMessageWhitelistSize > 0) {
166 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
167 if (command_line->HasSwitch(switches::kUtilityProcessRunningElevated)) {
168 message_id_whitelist_.insert(kMessageWhitelist,
169 kMessageWhitelist + kMessageWhitelistSize);
170 filter_messages_ = true;
175 bool ChromeContentUtilityClient::OnMessageReceived(
176 const IPC::Message& message) {
177 if (filter_messages_ && !ContainsKey(message_id_whitelist_, message.type()))
178 return false;
180 bool handled = true;
181 IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient, message)
182 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImage, OnDecodeImage)
183 #if defined(OS_CHROMEOS)
184 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RobustJPEGDecodeImage,
185 OnRobustJPEGDecodeImage)
186 #endif // defined(OS_CHROMEOS)
187 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileBsdiff,
188 OnPatchFileBsdiff)
189 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_PatchFileCourgette,
190 OnPatchFileCourgette)
191 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing, OnStartupPing)
192 #if defined(FULL_SAFE_BROWSING)
193 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection,
194 OnAnalyzeZipFileForDownloadProtection)
195 #endif
196 #if defined(ENABLE_EXTENSIONS)
197 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseMediaMetadata,
198 OnParseMediaMetadata)
199 #endif
200 #if defined(OS_CHROMEOS)
201 IPC_MESSAGE_HANDLER(ChromeUtilityMsg_CreateZipFile, OnCreateZipFile)
202 #endif
203 IPC_MESSAGE_UNHANDLED(handled = false)
204 IPC_END_MESSAGE_MAP()
206 for (Handlers::iterator it = handlers_.begin();
207 !handled && it != handlers_.end(); ++it) {
208 handled = (*it)->OnMessageReceived(message);
211 return handled;
214 void ChromeContentUtilityClient::RegisterMojoServices(
215 content::ServiceRegistry* registry) {
216 #if !defined(OS_ANDROID)
217 registry->AddService<net::interfaces::ProxyResolverFactory>(
218 base::Bind(CreateProxyResolverFactory));
219 registry->AddService<ResourceUsageReporter>(
220 base::Bind(CreateResourceUsageReporter));
221 #endif
224 // static
225 void ChromeContentUtilityClient::PreSandboxStartup() {
226 #if defined(ENABLE_EXTENSIONS)
227 extensions::ExtensionsHandler::PreSandboxStartup();
228 #endif
230 #if defined(ENABLE_MDNS)
231 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
232 switches::kUtilityProcessEnableMDns)) {
233 local_discovery::ServiceDiscoveryMessageHandler::PreSandboxStartup();
235 #endif // ENABLE_MDNS
238 // static
239 SkBitmap ChromeContentUtilityClient::DecodeImage(
240 const std::vector<unsigned char>& encoded_data, bool shrink_to_fit) {
241 SkBitmap decoded_image;
242 if (encoded_data.empty())
243 return decoded_image;
245 decoded_image = content::DecodeImage(&encoded_data[0],
246 gfx::Size(),
247 encoded_data.size());
249 int64_t struct_size = sizeof(ChromeUtilityHostMsg_DecodeImage_Succeeded);
250 int64_t image_size = decoded_image.computeSize64();
251 int halves = 0;
252 while (struct_size + (image_size >> 2*halves) > max_ipc_message_size_)
253 halves++;
254 if (halves) {
255 if (shrink_to_fit) {
256 // If decoded image is too large for IPC message, shrink it by halves.
257 // This prevents quality loss, and should never overshrink on displays
258 // smaller than 3600x2400.
259 // TODO (Issue 416916): Instead of shrinking, return via shared memory
260 decoded_image = skia::ImageOperations::Resize(
261 decoded_image, skia::ImageOperations::RESIZE_LANCZOS3,
262 decoded_image.width() >> halves, decoded_image.height() >> halves);
263 } else {
264 // Image too big for IPC message, but caller didn't request resize;
265 // pre-delete image so DecodeImageAndSend() will send an error.
266 decoded_image.reset();
267 LOG(ERROR) << "Decoded image too large for IPC message";
271 return decoded_image;
274 // static
275 void ChromeContentUtilityClient::DecodeImageAndSend(
276 const std::vector<unsigned char>& encoded_data,
277 bool shrink_to_fit,
278 int request_id) {
279 SkBitmap decoded_image = DecodeImage(encoded_data, shrink_to_fit);
281 if (decoded_image.empty()) {
282 Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
283 } else {
284 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(decoded_image,
285 request_id));
287 ReleaseProcessIfNeeded();
290 void ChromeContentUtilityClient::OnDecodeImage(
291 const std::vector<unsigned char>& encoded_data,
292 bool shrink_to_fit,
293 int request_id) {
294 content::UtilityThread::Get()->EnsureBlinkInitialized();
295 DecodeImageAndSend(encoded_data, shrink_to_fit, request_id);
298 #if defined(OS_CHROMEOS)
299 void ChromeContentUtilityClient::OnCreateZipFile(
300 const base::FilePath& src_dir,
301 const std::vector<base::FilePath>& src_relative_paths,
302 const base::FileDescriptor& dest_fd) {
303 // dest_fd should be closed in the function. See ipc/ipc_message_util.h for
304 // details.
305 base::ScopedFD fd_closer(dest_fd.fd);
306 bool succeeded = true;
308 // Check sanity of source relative paths. Reject if path is absolute or
309 // contains any attempt to reference a parent directory ("../" tricks).
310 for (std::vector<base::FilePath>::const_iterator iter =
311 src_relative_paths.begin(); iter != src_relative_paths.end();
312 ++iter) {
313 if (iter->IsAbsolute() || iter->ReferencesParent()) {
314 succeeded = false;
315 break;
319 if (succeeded)
320 succeeded = zip::ZipFiles(src_dir, src_relative_paths, dest_fd.fd);
322 if (succeeded)
323 Send(new ChromeUtilityHostMsg_CreateZipFile_Succeeded());
324 else
325 Send(new ChromeUtilityHostMsg_CreateZipFile_Failed());
326 ReleaseProcessIfNeeded();
328 #endif // defined(OS_CHROMEOS)
330 #if defined(OS_CHROMEOS)
331 void ChromeContentUtilityClient::OnRobustJPEGDecodeImage(
332 const std::vector<unsigned char>& encoded_data,
333 int request_id) {
334 // Our robust jpeg decoding is using IJG libjpeg.
335 if (!encoded_data.empty()) {
336 scoped_ptr<SkBitmap> decoded_image(gfx::JPEGCodecRobustSlow::Decode(
337 &encoded_data[0], encoded_data.size()));
338 if (!decoded_image.get() || decoded_image->empty()) {
339 Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
340 } else {
341 Send(new ChromeUtilityHostMsg_DecodeImage_Succeeded(*decoded_image,
342 request_id));
344 } else {
345 Send(new ChromeUtilityHostMsg_DecodeImage_Failed(request_id));
347 ReleaseProcessIfNeeded();
349 #endif // defined(OS_CHROMEOS)
351 void ChromeContentUtilityClient::OnPatchFileBsdiff(
352 const base::FilePath& input_file,
353 const base::FilePath& patch_file,
354 const base::FilePath& output_file) {
355 if (input_file.empty() || patch_file.empty() || output_file.empty()) {
356 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
357 } else {
358 const int patch_status = courgette::ApplyBinaryPatch(input_file,
359 patch_file,
360 output_file);
361 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status));
363 ReleaseProcessIfNeeded();
366 void ChromeContentUtilityClient::OnPatchFileCourgette(
367 const base::FilePath& input_file,
368 const base::FilePath& patch_file,
369 const base::FilePath& output_file) {
370 if (input_file.empty() || patch_file.empty() || output_file.empty()) {
371 Send(new ChromeUtilityHostMsg_PatchFile_Finished(-1));
372 } else {
373 const int patch_status = courgette::ApplyEnsemblePatch(
374 input_file.value().c_str(),
375 patch_file.value().c_str(),
376 output_file.value().c_str());
377 Send(new ChromeUtilityHostMsg_PatchFile_Finished(patch_status));
379 ReleaseProcessIfNeeded();
382 void ChromeContentUtilityClient::OnStartupPing() {
383 Send(new ChromeUtilityHostMsg_ProcessStarted);
384 // Don't release the process, we assume further messages are on the way.
387 #if defined(FULL_SAFE_BROWSING)
388 void ChromeContentUtilityClient::OnAnalyzeZipFileForDownloadProtection(
389 const IPC::PlatformFileForTransit& zip_file,
390 const IPC::PlatformFileForTransit& temp_file) {
391 safe_browsing::zip_analyzer::Results results;
392 safe_browsing::zip_analyzer::AnalyzeZipFile(
393 IPC::PlatformFileForTransitToFile(zip_file),
394 IPC::PlatformFileForTransitToFile(temp_file), &results);
395 Send(new ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished(
396 results));
397 ReleaseProcessIfNeeded();
399 #endif
401 #if defined(ENABLE_EXTENSIONS)
402 // TODO(thestig): Try to move this to
403 // chrome/utility/extensions/extensions_handler.cc.
404 void ChromeContentUtilityClient::OnParseMediaMetadata(
405 const std::string& mime_type, int64 total_size, bool get_attached_images) {
406 // Only one IPCDataSource may be created and added to the list of handlers.
407 metadata::IPCDataSource* source = new metadata::IPCDataSource(total_size);
408 handlers_.push_back(source);
410 metadata::MediaMetadataParser* parser = new metadata::MediaMetadataParser(
411 source, mime_type, get_attached_images);
412 parser->Start(base::Bind(&FinishParseMediaMetadata, base::Owned(parser)));
414 #endif