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 54 bool Send(IPC::Message* message) { 55 return content::UtilityThread::Get()->Send(message); 56 } 57 58 void ReleaseProcessIfNeeded() { 59 content::UtilityThread::Get()->ReleaseProcessIfNeeded(); 60 } 61 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(); 70 } 71 #endif 72 73 } // namespace 74 75 int64_t ChromeContentUtilityClient::max_ipc_message_size_ = 76 IPC::Channel::kMaximumMessageSize; 77 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 105 ChromeContentUtilityClient::~ChromeContentUtilityClient() { 106 } 107 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 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 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 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 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 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 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) 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 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 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 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 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) 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. 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