Give an app the file entries it had back on restart.


BUG=165832


Review URL: https://chromiumcodereview.appspot.com/12391006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187846 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc b/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc
index ff70376..10e29f57 100644
--- a/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc
+++ b/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
 #include "chrome/browser/extensions/event_router.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/profiles/profile.h"
@@ -51,10 +52,28 @@
   DispatchOnLaunchedEventImpl(extension->id(), arguments.Pass(), profile);
 }
 
+DictionaryValue* DictionaryFromSavedFileEntry(
+    const app_file_handler_util::GrantedFileEntry& file_entry) {
+  DictionaryValue* result = new DictionaryValue();
+  result->SetString("id", file_entry.id);
+  result->SetString("fileSystemId", file_entry.filesystem_id);
+  result->SetString("baseName", file_entry.registered_name);
+  return result;
+}
+
 // static.
 void AppEventRouter::DispatchOnRestartedEvent(
-    Profile* profile, const Extension* extension) {
+    Profile* profile,
+    const Extension* extension,
+    const std::vector<app_file_handler_util::GrantedFileEntry>& file_entries) {
+  ListValue* file_entries_list = new ListValue();
+  for (std::vector<extensions::app_file_handler_util::GrantedFileEntry>
+       ::const_iterator it = file_entries.begin(); it != file_entries.end();
+       ++it) {
+    file_entries_list->Append(DictionaryFromSavedFileEntry(*it));
+  }
   scoped_ptr<ListValue> arguments(new ListValue());
+  arguments->Append(file_entries_list);
   scoped_ptr<Event> event(new Event(kOnRestartedEvent, arguments.Pass()));
   event->restrict_to_profile = profile;
   extensions::ExtensionSystem::Get(profile)->event_router()->
diff --git a/chrome/browser/extensions/api/app_runtime/app_runtime_api.h b/chrome/browser/extensions/api/app_runtime/app_runtime_api.h
index 84ade63..f9075b4 100644
--- a/chrome/browser/extensions/api/app_runtime/app_runtime_api.h
+++ b/chrome/browser/extensions/api/app_runtime/app_runtime_api.h
@@ -18,14 +18,20 @@
 
 class Extension;
 
+namespace app_file_handler_util {
+struct GrantedFileEntry;
+}
+
 class AppEventRouter {
  public:
   // Dispatches the onLaunched event to the given app, providing no launch
   // data.
   static void DispatchOnLaunchedEvent(Profile* profile,
                                       const Extension* extension);
-  static void DispatchOnRestartedEvent(Profile* profile,
-                                       const Extension* extension);
+  static void DispatchOnRestartedEvent(
+      Profile* profile,
+      const Extension* extension,
+      const std::vector<app_file_handler_util::GrantedFileEntry>& file_entries);
 
   // TODO(benwells): Update this comment, it is out of date.
   // Dispatches the onLaunched event to the given app, providing launch data of
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 38df2d4..832218d3 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h"
 #include "chrome/browser/extensions/api/declarative/rules_registry_service.h"
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
 #include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
 #include "chrome/browser/extensions/api/runtime/runtime_api.h"
 #include "chrome/browser/extensions/api/storage/settings_frontend.h"
@@ -743,6 +744,11 @@
   }
 
   on_load_events_[extension_id] = events;
+  if (events & EVENT_RESTARTED) {
+    extension_prefs_->GetSavedFileEntries(
+        extension_id, &on_restart_file_entries_[extension_id]);
+  }
+
 
   if (delayed_updates_for_idle_.Contains(extension_id)) {
     FinishDelayedInstallation(extension_id);
@@ -2910,9 +2916,16 @@
     if (events_to_fire & EVENT_LAUNCHED)
       queue->AddPendingTask(profile(), extension->id(),
                             base::Bind(&ExtensionService::LaunchApplication));
-    if (events_to_fire & EVENT_RESTARTED)
+    if (events_to_fire & EVENT_RESTARTED) {
+      SavedFileEntryMap::iterator it =
+          on_restart_file_entries_.find(extension->id());
+      if (it == on_restart_file_entries_.end())
+        NOTREACHED();
       queue->AddPendingTask(profile(), extension->id(),
-                            base::Bind(&ExtensionService::RestartApplication));
+                            base::Bind(&ExtensionService::RestartApplication,
+                                       it->second));
+      on_restart_file_entries_.erase(it);
+    }
   }
 
   on_load_events_.erase(it);
@@ -2933,13 +2946,14 @@
 
 // static
 void ExtensionService::RestartApplication(
+    std::vector<extensions::app_file_handler_util::SavedFileEntry> file_entries,
     extensions::ExtensionHost* extension_host) {
   if (!extension_host)
     return;
 
 #if !defined(OS_ANDROID)
-  extensions::AppEventRouter::DispatchOnRestartedEvent(
-      extension_host->profile(), extension_host->extension());
+  extensions::RestartPlatformAppWithFileEntries(
+      extension_host->profile(), extension_host->extension(), file_entries);
 #endif
 }
 
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 7fb4a13..7a4eaea3 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -796,7 +796,10 @@
 
   // Dispatches a restart event to the platform app associated with
   // |extension_host|.
-  static void RestartApplication(extensions::ExtensionHost* extension_host);
+  static void RestartApplication(
+      std::vector<extensions::app_file_handler_util::SavedFileEntry>
+          file_entries,
+      extensions::ExtensionHost* extension_host);
 
   // Helper to inspect an ExtensionHost after it has been loaded.
   void InspectExtensionHost(extensions::ExtensionHost* host);
@@ -924,6 +927,13 @@
   // dispatched to the extension when it is loaded.
   std::map<std::string, int> on_load_events_;
 
+  // Maps extension ids to vectors of saved file entries that the extension
+  // should be given access to on restart.
+  typedef std::map<std::string,
+      std::vector<extensions::app_file_handler_util::SavedFileEntry> >
+          SavedFileEntryMap;
+  SavedFileEntryMap on_restart_file_entries_;
+
   content::NotificationRegistrar registrar_;
   PrefChangeRegistrar pref_change_registrar_;
 
diff --git a/chrome/browser/extensions/platform_app_launcher.cc b/chrome/browser/extensions/platform_app_launcher.cc
index 78548d843..0d37609 100644
--- a/chrome/browser/extensions/platform_app_launcher.cc
+++ b/chrome/browser/extensions/platform_app_launcher.cc
@@ -13,8 +13,11 @@
 #include "base/utf_string_conversions.h"
 #include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h"
 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
+#include "chrome/browser/extensions/api/file_system/file_system_api.h"
 #include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_prefs.h"
 #include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/lazy_background_task_queue.h"
 #include "chrome/browser/profiles/profile.h"
@@ -33,6 +36,9 @@
 using extensions::app_file_handler_util::FileHandlerForId;
 using extensions::app_file_handler_util::FileHandlerCanHandleFileWithMimeType;
 using extensions::app_file_handler_util::FirstFileHandlerForMimeType;
+using extensions::app_file_handler_util::CreateFileEntry;
+using extensions::app_file_handler_util::GrantedFileEntry;
+using extensions::app_file_handler_util::SavedFileEntry;
 
 namespace extensions {
 
@@ -239,6 +245,70 @@
   DISALLOW_COPY_AND_ASSIGN(PlatformAppPathLauncher);
 };
 
+class SavedFileEntryLauncher
+    : public base::RefCountedThreadSafe<SavedFileEntryLauncher> {
+ public:
+  SavedFileEntryLauncher(
+      Profile* profile,
+      const Extension* extension,
+      const std::vector<SavedFileEntry>& file_entries)
+      : profile_(profile),
+        extension_(extension),
+        file_entries_(file_entries) {}
+
+  void Launch() {
+    // Access needs to be granted to the file or filesystem for the process
+    // associated with the extension. To do this the ExtensionHost is needed.
+    // This might not be available, or it might be in the process of being
+    // unloaded, in which case the lazy background task queue is used to load
+    // he extension and then call back to us.
+    extensions::LazyBackgroundTaskQueue* queue =
+        ExtensionSystem::Get(profile_)->lazy_background_task_queue();
+    if (queue->ShouldEnqueueTask(profile_, extension_)) {
+      queue->AddPendingTask(profile_, extension_->id(), base::Bind(
+              &SavedFileEntryLauncher::GrantAccessToFilesAndLaunch,
+              this));
+      return;
+    }
+    ExtensionProcessManager* process_manager =
+        ExtensionSystem::Get(profile_)->process_manager();
+    extensions::ExtensionHost* host =
+        process_manager->GetBackgroundHostForExtension(extension_->id());
+    DCHECK(host);
+    GrantAccessToFilesAndLaunch(host);
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<SavedFileEntryLauncher>;
+  ~SavedFileEntryLauncher() {}
+  void GrantAccessToFilesAndLaunch(ExtensionHost* host) {
+    int renderer_id = host->render_process_host()->GetID();
+    std::vector<GrantedFileEntry> granted_file_entries;
+    for (std::vector<SavedFileEntry>::const_iterator it =
+         file_entries_.begin(); it != file_entries_.end(); ++it) {
+      GrantedFileEntry file_entry = CreateFileEntry(
+          profile_, extension_->id(), renderer_id, it->path, it->writable);
+      file_entry.id = it->id;
+      granted_file_entries.push_back(file_entry);
+
+      // Record that we have granted this file permission.
+      ExtensionPrefs* extension_prefs = ExtensionSystem::Get(profile_)->
+          extension_service()->extension_prefs();
+      extension_prefs->AddSavedFileEntry(
+          host->extension()->id(), it->id, it->path, it->writable);
+    }
+    extensions::AppEventRouter::DispatchOnRestartedEvent(
+        profile_, extension_, granted_file_entries);
+  }
+
+  // The profile the app should be run in.
+  Profile* profile_;
+  // The extension providing the app.
+  const Extension* extension_;
+
+  std::vector<SavedFileEntry> file_entries_;
+};
+
 }  // namespace
 
 void LaunchPlatformApp(Profile* profile,
@@ -275,4 +345,13 @@
   launcher->LaunchWithHandler(handler_id);
 }
 
+void RestartPlatformAppWithFileEntries(
+    Profile* profile,
+    const Extension* extension,
+    const std::vector<SavedFileEntry>& file_entries) {
+  scoped_refptr<SavedFileEntryLauncher> launcher = new SavedFileEntryLauncher(
+      profile, extension, file_entries);
+  launcher->Launch();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/platform_app_launcher.h b/chrome/browser/extensions/platform_app_launcher.h
index c301c45..fc7965a7 100644
--- a/chrome/browser/extensions/platform_app_launcher.h
+++ b/chrome/browser/extensions/platform_app_launcher.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_EXTENSIONS_PLATFORM_APP_LAUNCHER_H_
 
 #include <string>
+#include <vector>
 
 class CommandLine;
 class Profile;
@@ -22,6 +23,10 @@
 
 class Extension;
 
+namespace app_file_handler_util {
+struct SavedFileEntry;
+}
+
 // Launches the platform app |extension|. Creates appropriate launch data for
 // the |command_line| fields present. |extension| and |profile| must not be
 // NULL. A NULL |command_line| means there is no launch data. If non-empty,
@@ -44,6 +49,12 @@
                                       const std::string& handler_id,
                                       const base::FilePath& file_path);
 
+void RestartPlatformAppWithFileEntries(
+    Profile* profile,
+    const Extension* extension,
+    const std::vector<app_file_handler_util::SavedFileEntry>&
+        saved_file_entries);
+
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_PLATFORM_APP_LAUNCHER_H_