Home | History | Annotate | Download | only in utility
      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