blob: d892da6fbba702942dec552727233a20f54737e5 [file] [log] [blame]
Ben Murdochbb1529c2013-08-08 10:24:53 +01001// Copyright 2013 The Chromium Authors. All rights reserved.
Torne (Richard Coles)58218062012-11-14 11:43:16 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ben Murdochbb1529c2013-08-08 10:24:53 +01005#include "apps/launcher.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00006
7#include "base/command_line.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00008#include "base/file_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00009#include "base/files/file_path.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000010#include "base/logging.h"
11#include "base/memory/ref_counted.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010012#include "base/strings/string_util.h"
13#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014#include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000015#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
16#include "chrome/browser/extensions/api/file_system/file_system_api.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010017#include "chrome/browser/extensions/event_names.h"
18#include "chrome/browser/extensions/event_router.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019#include "chrome/browser/extensions/extension_host.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000020#include "chrome/browser/extensions/extension_prefs.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000021#include "chrome/browser/extensions/extension_process_manager.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000022#include "chrome/browser/extensions/extension_service.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000023#include "chrome/browser/extensions/extension_system.h"
24#include "chrome/browser/extensions/lazy_background_task_queue.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000025#include "chrome/browser/profiles/profile.h"
26#include "chrome/common/extensions/extension.h"
27#include "chrome/common/extensions/extension_messages.h"
28#include "content/public/browser/browser_thread.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000029#include "content/public/browser/render_process_host.h"
30#include "content/public/browser/web_contents.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000031#include "net/base/mime_util.h"
32#include "net/base/net_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000033
34#if defined(OS_CHROMEOS)
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010035#include "chrome/browser/chromeos/drive/drive_integration_service.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010036#include "chrome/browser/chromeos/drive/file_errors.h"
37#include "chrome/browser/chromeos/drive/file_system_interface.h"
38#include "chrome/browser/chromeos/drive/file_system_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000039#endif
40
41#if defined(OS_WIN)
42#include "win8/util/win8_util.h"
43#endif
Torne (Richard Coles)58218062012-11-14 11:43:16 +000044
45using content::BrowserThread;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000046using extensions::app_file_handler_util::FileHandlerForId;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010047using extensions::app_file_handler_util::FileHandlerCanHandleFile;
48using extensions::app_file_handler_util::FirstFileHandlerForFile;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000049using extensions::app_file_handler_util::CreateFileEntry;
50using extensions::app_file_handler_util::GrantedFileEntry;
Ben Murdochbb1529c2013-08-08 10:24:53 +010051using extensions::Extension;
52using extensions::ExtensionHost;
53using extensions::ExtensionSystem;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000054
Ben Murdochbb1529c2013-08-08 10:24:53 +010055namespace apps {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000056
57namespace {
58
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010059const char kFallbackMimeType[] = "application/octet-stream";
60
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000061bool MakePathAbsolute(const base::FilePath& current_directory,
62 base::FilePath* file_path) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000063 DCHECK(file_path);
64 if (file_path->IsAbsolute())
65 return true;
66
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010067 if (current_directory.empty()) {
68 *file_path = base::MakeAbsoluteFilePath(*file_path);
69 return !file_path->empty();
70 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000071
72 if (!current_directory.IsAbsolute())
73 return false;
74
75 *file_path = current_directory.Append(*file_path);
76 return true;
77}
78
79bool GetAbsolutePathFromCommandLine(const CommandLine* command_line,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000080 const base::FilePath& current_directory,
81 base::FilePath* path) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000082 if (!command_line || !command_line->GetArgs().size())
83 return false;
84
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000085 base::FilePath relative_path(command_line->GetArgs()[0]);
86 base::FilePath absolute_path(relative_path);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000087 if (!MakePathAbsolute(current_directory, &absolute_path)) {
88 LOG(WARNING) << "Cannot make absolute path from " << relative_path.value();
89 return false;
90 }
91 *path = absolute_path;
92 return true;
93}
94
95// Helper method to launch the platform app |extension| with no data. This
96// should be called in the fallback case, where it has been impossible to
97// load or obtain file launch data.
98void LaunchPlatformAppWithNoData(Profile* profile, const Extension* extension) {
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
100 extensions::AppEventRouter::DispatchOnLaunchedEvent(profile, extension);
101}
102
103// Class to handle launching of platform apps to open a specific path.
104// An instance of this class is created for each launch. The lifetime of these
105// instances is managed by reference counted pointers. As long as an instance
106// has outstanding tasks on a message queue it will be retained; once all
107// outstanding tasks are completed it will be deleted.
108class PlatformAppPathLauncher
109 : public base::RefCountedThreadSafe<PlatformAppPathLauncher> {
110 public:
111 PlatformAppPathLauncher(Profile* profile,
112 const Extension* extension,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000113 const base::FilePath& file_path)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100114 : profile_(profile), extension_(extension), file_path_(file_path) {}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000115
116 void Launch() {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118 if (file_path_.empty()) {
119 LaunchPlatformAppWithNoData(profile_, extension_);
120 return;
121 }
122
123 DCHECK(file_path_.IsAbsolute());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000124
125#if defined(OS_CHROMEOS)
126 if (drive::util::IsUnderDriveMountPoint(file_path_)) {
127 GetMimeTypeAndLaunchForDriveFile();
128 return;
129 }
130#endif
131
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000132 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
133 &PlatformAppPathLauncher::GetMimeTypeAndLaunch, this));
134 }
135
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000136 void LaunchWithHandler(const std::string& handler_id) {
137 handler_id_ = handler_id;
138 Launch();
139 }
140
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000141 private:
142 friend class base::RefCountedThreadSafe<PlatformAppPathLauncher>;
143
144 virtual ~PlatformAppPathLauncher() {}
145
146 void GetMimeTypeAndLaunch() {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
148
149 // If the file doesn't exist, or is a directory, launch with no launch data.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100150 if (!base::PathExists(file_path_) ||
151 base::DirectoryExists(file_path_)) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000152 LOG(WARNING) << "No file exists with path " << file_path_.value();
153 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
154 &PlatformAppPathLauncher::LaunchWithNoLaunchData, this));
155 return;
156 }
157
158 std::string mime_type;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100159 if (!net::GetMimeTypeFromFile(file_path_, &mime_type))
160 mime_type = kFallbackMimeType;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000161
162 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
163 &PlatformAppPathLauncher::LaunchWithMimeType, this, mime_type));
164 }
165
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000166#if defined(OS_CHROMEOS)
167 void GetMimeTypeAndLaunchForDriveFile() {
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
169
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100170 drive::DriveIntegrationService* service =
171 drive::DriveIntegrationServiceFactory::FindForProfile(profile_);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000172 if (!service) {
173 LaunchWithNoLaunchData();
174 return;
175 }
176
177 service->file_system()->GetFileByPath(
178 drive::util::ExtractDrivePath(file_path_),
179 base::Bind(&PlatformAppPathLauncher::OnGotDriveFile, this));
180 }
181
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100182 void OnGotDriveFile(drive::FileError error,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000183 const base::FilePath& file_path,
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100184 scoped_ptr<drive::ResourceEntry> entry) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100187 if (error != drive::FILE_ERROR_OK ||
188 !entry || entry->file_specific_info().is_hosted_document()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000189 LaunchWithNoLaunchData();
190 return;
191 }
192
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100193 const std::string& mime_type =
194 entry->file_specific_info().content_mime_type();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100195 LaunchWithMimeType(mime_type.empty() ? kFallbackMimeType : mime_type);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000196 }
197#endif // defined(OS_CHROMEOS)
198
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000199 void LaunchWithNoLaunchData() {
200 // This method is required as an entry point on the UI thread.
201 LaunchPlatformAppWithNoData(profile_, extension_);
202 }
203
204 void LaunchWithMimeType(const std::string& mime_type) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000205 // Find file handler from the platform app for the file being opened.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100206 const extensions::FileHandlerInfo* handler = NULL;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000207 if (!handler_id_.empty())
208 handler = FileHandlerForId(*extension_, handler_id_);
209 else
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100210 handler = FirstFileHandlerForFile(*extension_, mime_type, file_path_);
211 if (handler && !FileHandlerCanHandleFile(*handler, mime_type, file_path_)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000212 LOG(WARNING) << "Extension does not provide a valid file handler for "
213 << file_path_.value();
214 LaunchWithNoLaunchData();
215 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000216 }
217
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000218 // If this app doesn't have a file handler that supports the file, launch
219 // with no launch data.
220 if (!handler) {
221 LOG(WARNING) << "Extension does not provide a valid file handler for "
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000222 << file_path_.value();
223 LaunchWithNoLaunchData();
224 return;
225 }
226
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100227 if (handler_id_.empty())
228 handler_id_ = handler->id;
229
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000230 // Access needs to be granted to the file for the process associated with
231 // the extension. To do this the ExtensionHost is needed. This might not be
232 // available, or it might be in the process of being unloaded, in which case
233 // the lazy background task queue is used to load the extension and then
234 // call back to us.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100235 extensions::LazyBackgroundTaskQueue* queue =
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000236 ExtensionSystem::Get(profile_)->lazy_background_task_queue();
237 if (queue->ShouldEnqueueTask(profile_, extension_)) {
238 queue->AddPendingTask(profile_, extension_->id(), base::Bind(
239 &PlatformAppPathLauncher::GrantAccessToFileAndLaunch,
240 this, mime_type));
241 return;
242 }
243
244 ExtensionProcessManager* process_manager =
245 ExtensionSystem::Get(profile_)->process_manager();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000246 ExtensionHost* host =
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000247 process_manager->GetBackgroundHostForExtension(extension_->id());
248 DCHECK(host);
249 GrantAccessToFileAndLaunch(mime_type, host);
250 }
251
252 void GrantAccessToFileAndLaunch(const std::string& mime_type,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000253 ExtensionHost* host) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000254 // If there was an error loading the app page, |host| will be NULL.
255 if (!host) {
256 LOG(ERROR) << "Could not load app page for " << extension_->id();
257 return;
258 }
259
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100260 GrantedFileEntry file_entry = CreateFileEntry(
261 profile_,
262 extension_->id(),
263 host->render_process_host()->GetID(),
264 file_path_,
265 false);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100266 extensions::AppEventRouter::DispatchOnLaunchedEventWithFileEntry(
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100267 profile_, extension_, handler_id_, mime_type, file_entry);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000268 }
269
270 // The profile the app should be run in.
271 Profile* profile_;
272 // The extension providing the app.
273 const Extension* extension_;
274 // The path to be passed through to the app.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000275 const base::FilePath file_path_;
276 // The ID of the file handler used to launch the app.
277 std::string handler_id_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000278
279 DISALLOW_COPY_AND_ASSIGN(PlatformAppPathLauncher);
280};
281
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000282} // namespace
283
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100284void LaunchPlatformAppWithCommandLine(Profile* profile,
285 const Extension* extension,
286 const CommandLine* command_line,
287 const base::FilePath& current_directory) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000288 base::FilePath path;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000289 if (!GetAbsolutePathFromCommandLine(command_line, current_directory, &path)) {
290 LaunchPlatformAppWithNoData(profile, extension);
291 return;
292 }
293
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000294 // TODO(benwells): add a command-line argument to provide a handler ID.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000295 LaunchPlatformAppWithPath(profile, extension, path);
296}
297
298void LaunchPlatformAppWithPath(Profile* profile,
299 const Extension* extension,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000300 const base::FilePath& file_path) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000301 // launcher will be freed when nothing has a reference to it. The message
302 // queue will retain a reference for any outstanding task, so when the
303 // launcher has finished it will be freed.
304 scoped_refptr<PlatformAppPathLauncher> launcher =
305 new PlatformAppPathLauncher(profile, extension, file_path);
306 launcher->Launch();
307}
308
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100309void LaunchPlatformApp(Profile* profile, const Extension* extension) {
310 LaunchPlatformAppWithCommandLine(profile, extension, NULL, base::FilePath());
311}
312
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000313void LaunchPlatformAppWithFileHandler(Profile* profile,
314 const Extension* extension,
315 const std::string& handler_id,
316 const base::FilePath& file_path) {
317 scoped_refptr<PlatformAppPathLauncher> launcher =
318 new PlatformAppPathLauncher(profile, extension, file_path);
319 launcher->LaunchWithHandler(handler_id);
320}
321
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100322void RestartPlatformApp(Profile* profile, const Extension* extension) {
Ben Murdoch32409262013-08-07 11:04:47 +0100323#if defined(OS_WIN)
324 // On Windows 8's single window Metro mode we can not launch platform apps.
325 // In restart we are just making sure launch doesn't slip through.
326 if (win8::IsSingleWindowMetroMode())
327 return;
328#endif
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100329 extensions::EventRouter* event_router =
330 ExtensionSystem::Get(profile)->event_router();
331 bool listening_to_restart = event_router->
Ben Murdochbb1529c2013-08-08 10:24:53 +0100332 ExtensionHasEventListener(extension->id(),
333 extensions::event_names::kOnRestarted);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100334
335 if (listening_to_restart) {
336 extensions::AppEventRouter::DispatchOnRestartedEvent(profile, extension);
337 return;
338 }
339
Ben Murdochbb1529c2013-08-08 10:24:53 +0100340 extensions::ExtensionPrefs* extension_prefs = ExtensionSystem::Get(profile)->
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100341 extension_service()->extension_prefs();
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100342 bool had_windows = extension_prefs->IsActive(extension->id());
343 extension_prefs->SetIsActive(extension->id(), false);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100344 bool listening_to_launch = event_router->
Ben Murdochbb1529c2013-08-08 10:24:53 +0100345 ExtensionHasEventListener(extension->id(),
346 extensions::event_names::kOnLaunched);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100347
348 if (listening_to_launch && had_windows)
349 LaunchPlatformAppWithNoData(profile, extension);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000350}
351
Ben Murdochbb1529c2013-08-08 10:24:53 +0100352} // namespace apps