Convert utility process extension Unpacker IPC to mojo

Add ExtensionUnpacker mojo, used to (unzip and) unpack a chrome
extension, and expose it to the browser via the utility process
policy file. Make callers use a utility process mojo client.

Add [Native] IPC enum param traits via a mojo typemap to handle
extensions::Manifest::Location. Set the wire limit for the enum
value to one less than Manifest::NUM_LOCATIONS (for compat with
the pre-existing custom traits removed in this patch since this
patch auto-generates them: manifest_location_param_traits.cc).

In the extensions/utility/utility_handler.cc, retain the CHECKs
on the Manifest::Location enum limits while bug 692069 is still
being investigated.

Move DecodeImages declarations from the extension messages-file
into its own file (extension_utility_types.h) to decouple those
declarations from this messages-file. A follow-up patch deletes
the messages-file so DecodeImages needs a new home.

Update all BUILD.gn for using clients to directly depend on the
//extensions/common:common target (and hence, its :mojo) to fix
post-compile step failures (warning about under-specified BUILD
files via "targets is_dirty" errors, see review comment #169).

Add TestContentBrowserClient to the extension test harness, and
also an overlay file in test/data (needed to expose the mojo in
the extension test harness to the unit tests listed below).

Covered by existing unit tests:
  {sandboxed_unpacker, zipfile_installer}_unittest.cc

BUG=691410

Review-Url: https://codereview.chromium.org/2697463002
Cr-Commit-Position: refs/heads/master@{#457374}
diff --git a/extensions/utility/unpacker.cc b/extensions/utility/unpacker.cc
index bab2f98..e946013 100644
--- a/extensions/utility/unpacker.cc
+++ b/extensions/utility/unpacker.cc
@@ -25,7 +25,7 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_l10n_util.h"
-#include "extensions/common/extension_utility_messages.h"
+#include "extensions/common/extension_utility_types.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/file_util.h"
 #include "extensions/common/manifest.h"
diff --git a/extensions/utility/unpacker.h b/extensions/utility/unpacker.h
index fa77b69f..63b20be 100644
--- a/extensions/utility/unpacker.h
+++ b/extensions/utility/unpacker.h
@@ -53,6 +53,10 @@
   base::DictionaryValue* parsed_manifest() { return parsed_manifest_.get(); }
   base::DictionaryValue* parsed_catalogs() { return parsed_catalogs_.get(); }
 
+  std::unique_ptr<base::DictionaryValue> TakeParsedManifest() {
+    return std::move(parsed_manifest_);
+  }
+
  private:
   // Write the decoded images to kDecodedImagesFilename.  We do this instead
   // of sending them over IPC, since they are so large.  Returns true on
diff --git a/extensions/utility/utility_handler.cc b/extensions/utility/utility_handler.cc
index 59fcdfe..96a4445 100644
--- a/extensions/utility/utility_handler.cc
+++ b/extensions/utility/utility_handler.cc
@@ -6,12 +6,10 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/i18n/rtl.h"
 #include "content/public/utility/utility_thread.h"
 #include "extensions/common/constants.h"
-#include "extensions/common/extension.h"
 #include "extensions/common/extension_l10n_util.h"
+#include "extensions/common/extension_unpacker.mojom.h"
 #include "extensions/common/extension_utility_messages.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/manifest.h"
@@ -20,6 +18,8 @@
 #include "extensions/utility/unpacker.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_message_macros.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
 #include "third_party/zlib/google/zip.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_switches.h"
@@ -28,24 +28,84 @@
 
 namespace {
 
-bool Send(IPC::Message* message) {
-  return content::UtilityThread::Get()->Send(message);
-}
+class ExtensionUnpackerImpl : public extensions::mojom::ExtensionUnpacker {
+ public:
+  ExtensionUnpackerImpl() = default;
+  ~ExtensionUnpackerImpl() override = default;
 
-void ReleaseProcessIfNeeded() {
-  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
-}
+  static void Create(extensions::mojom::ExtensionUnpackerRequest request) {
+    mojo::MakeStrongBinding(base::MakeUnique<ExtensionUnpackerImpl>(),
+                            std::move(request));
+  }
 
-const char kExtensionHandlerUnzipError[] =
-    "Could not unzip extension for install.";
+ private:
+  // extensions::mojom::ExtensionUnpacker:
+  void Unzip(const base::FilePath& file,
+             const base::FilePath& path,
+             const UnzipCallback& callback) override {
+    std::unique_ptr<base::DictionaryValue> manifest;
+    if (UnzipFileManifestIntoPath(file, path, &manifest)) {
+      callback.Run(UnzipFileIntoPath(file, path, std::move(manifest)));
+    } else {
+      callback.Run(false);
+    }
+  }
+
+  void Unpack(const base::FilePath& path,
+              const std::string& extension_id,
+              Manifest::Location location,
+              int32_t creation_flags,
+              const UnpackCallback& callback) override {
+    CHECK_GT(location, Manifest::INVALID_LOCATION);
+    CHECK_LT(location, Manifest::NUM_LOCATIONS);
+    DCHECK(ExtensionsClient::Get());
+
+    content::UtilityThread::Get()->EnsureBlinkInitialized();
+
+    Unpacker unpacker(path.DirName(), path, extension_id, location,
+                      creation_flags);
+    if (unpacker.Run()) {
+      callback.Run(base::string16(), unpacker.TakeParsedManifest());
+    } else {
+      callback.Run(unpacker.error_message(), nullptr);
+    }
+  }
+
+  static bool UnzipFileManifestIntoPath(
+      const base::FilePath& file,
+      const base::FilePath& path,
+      std::unique_ptr<base::DictionaryValue>* manifest) {
+    if (zip::UnzipWithFilterCallback(
+            file, path, base::Bind(&Unpacker::IsManifestFile), false)) {
+      std::string error;
+      *manifest = Unpacker::ReadManifest(path, &error);
+      return error.empty() && manifest->get();
+    }
+
+    return false;
+  }
+
+  static bool UnzipFileIntoPath(
+      const base::FilePath& file,
+      const base::FilePath& path,
+      std::unique_ptr<base::DictionaryValue> manifest) {
+    Manifest internal(Manifest::INTERNAL, std::move(manifest));
+    // TODO(crbug.com/645263): This silently ignores blocked file types.
+    //                         Add install warnings.
+    return zip::UnzipWithFilterCallback(
+        file, path,
+        base::Bind(&Unpacker::ShouldExtractFile, internal.is_theme()),
+        true /* log_skipped_files */);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionUnpackerImpl);
+};
 
 }  // namespace
 
-UtilityHandler::UtilityHandler() {
-}
+UtilityHandler::UtilityHandler() = default;
 
-UtilityHandler::~UtilityHandler() {
-}
+UtilityHandler::~UtilityHandler() = default;
 
 // static
 void UtilityHandler::UtilityThreadStarted() {
@@ -55,13 +115,23 @@
     extension_l10n_util::SetProcessLocale(lang);
 }
 
+// static
+void UtilityHandler::ExposeInterfacesToBrowser(
+    service_manager::InterfaceRegistry* registry,
+    bool running_elevated) {
+  // If our process runs with elevated privileges, only add elevated Mojo
+  // services to the interface registry.
+  if (running_elevated)
+    return;
+
+  registry->AddInterface(base::Bind(&ExtensionUnpackerImpl::Create));
+}
+
 bool UtilityHandler::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(UtilityHandler, message)
     IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_ParseUpdateManifest,
                         OnParseUpdateManifest)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_UnzipToDir, OnUnzipToDir)
-    IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_UnpackExtension, OnUnpackExtension)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -70,73 +140,15 @@
 void UtilityHandler::OnParseUpdateManifest(const std::string& xml) {
   UpdateManifest manifest;
   if (!manifest.Parse(xml)) {
-    Send(new ExtensionUtilityHostMsg_ParseUpdateManifest_Failed(
-        manifest.errors()));
+    content::UtilityThread::Get()->Send(
+        new ExtensionUtilityHostMsg_ParseUpdateManifest_Failed(
+            manifest.errors()));
   } else {
-    Send(new ExtensionUtilityHostMsg_ParseUpdateManifest_Succeeded(
-        manifest.results()));
+    content::UtilityThread::Get()->Send(
+        new ExtensionUtilityHostMsg_ParseUpdateManifest_Succeeded(
+            manifest.results()));
   }
-  ReleaseProcessIfNeeded();
+  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
 }
 
-void UtilityHandler::OnUnzipToDir(const base::FilePath& zip_path,
-                                  const base::FilePath& dir) {
-  // First extract only the manifest to determine the extension type.
-  if (!zip::UnzipWithFilterCallback(zip_path, dir,
-                                    base::Bind(&Unpacker::IsManifestFile),
-                                    false /* log_skipped_files */)) {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed(
-        std::string(kExtensionHandlerUnzipError)));
-    ReleaseProcessIfNeeded();
-    return;
-  }
-
-  // Load the manifest.
-  std::string error;
-  std::unique_ptr<base::DictionaryValue> dict =
-      Unpacker::ReadManifest(dir, &error);
-  if (!dict.get()) {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed(
-        std::string(kExtensionHandlerUnzipError)));
-    ReleaseProcessIfNeeded();
-    return;
-  }
-
-  Manifest manifest(Manifest::INTERNAL, std::move(dict));
-  base::Callback<bool(const base::FilePath&)> filetype_filter_cb =
-      base::Bind(&Unpacker::ShouldExtractFile, manifest.is_theme());
-
-  // TODO(crbug.com/645263): This silently ignores blocked file types.
-  //                         Add install warnings.
-  if (!zip::UnzipWithFilterCallback(zip_path, dir, filetype_filter_cb,
-                                    true /* log_skipped_files */)) {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed(
-        std::string(kExtensionHandlerUnzipError)));
-  } else {
-    Send(new ExtensionUtilityHostMsg_UnzipToDir_Succeeded(dir));
-  }
-  ReleaseProcessIfNeeded();
-}
-
-void UtilityHandler::OnUnpackExtension(const base::FilePath& directory_path,
-                                       const std::string& extension_id,
-                                       int location,
-                                       int creation_flags) {
-  CHECK_GT(location, Manifest::INVALID_LOCATION);
-  CHECK_LT(location, Manifest::NUM_LOCATIONS);
-  DCHECK(ExtensionsClient::Get());
-  content::UtilityThread::Get()->EnsureBlinkInitialized();
-  Unpacker unpacker(directory_path.DirName(), directory_path, extension_id,
-                    static_cast<Manifest::Location>(location), creation_flags);
-  if (unpacker.Run()) {
-    Send(new ExtensionUtilityHostMsg_UnpackExtension_Succeeded(
-        *unpacker.parsed_manifest()));
-  } else {
-    Send(new ExtensionUtilityHostMsg_UnpackExtension_Failed(
-        unpacker.error_message()));
-  }
-  ReleaseProcessIfNeeded();
-}
-
-
 }  // namespace extensions
diff --git a/extensions/utility/utility_handler.h b/extensions/utility/utility_handler.h
index 2bc12f4..2378dcd 100644
--- a/extensions/utility/utility_handler.h
+++ b/extensions/utility/utility_handler.h
@@ -7,17 +7,16 @@
 
 #include <string>
 
-#include "base/callback.h"
 #include "base/macros.h"
 
-namespace base {
-class FilePath;
-}
-
 namespace IPC {
 class Message;
 }
 
+namespace service_manager {
+class InterfaceRegistry;
+}
+
 namespace extensions {
 
 // A handler for extensions-related IPC from within utility processes.
@@ -28,16 +27,15 @@
 
   static void UtilityThreadStarted();
 
+  static void ExposeInterfacesToBrowser(
+      service_manager::InterfaceRegistry* registry,
+      bool running_elevated);
+
   bool OnMessageReceived(const IPC::Message& message);
 
  private:
   // IPC message handlers.
   void OnParseUpdateManifest(const std::string& xml);
-  void OnUnzipToDir(const base::FilePath& zip_path, const base::FilePath& dir);
-  void OnUnpackExtension(const base::FilePath& directory_path,
-                         const std::string& extension_id,
-                         int location,
-                         int creation_flags);
 
   DISALLOW_COPY_AND_ASSIGN(UtilityHandler);
 };