Upload upstream chromium 71.0.3578.0
[platform/framework/web/chromium-efl.git] / apps / launcher.cc
1 // Copyright 2013 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 "apps/launcher.h"
6
7 #include <memory>
8 #include <set>
9 #include <utility>
10
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/task/post_task.h"
19 #include "base/task/task_traits.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_task_traits.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/common/content_switches.h"
26 #include "content/public/common/url_constants.h"
27 #include "extensions/browser/api/app_runtime/app_runtime_api.h"
28 #include "extensions/browser/api/file_handlers/app_file_handler_util.h"
29 #include "extensions/browser/api/file_handlers/directory_util.h"
30 #include "extensions/browser/api/file_handlers/mime_util.h"
31 #include "extensions/browser/entry_info.h"
32 #include "extensions/browser/event_router.h"
33 #include "extensions/browser/extension_host.h"
34 #include "extensions/browser/extension_prefs.h"
35 #include "extensions/browser/extension_registry.h"
36 #include "extensions/browser/granted_file_entry.h"
37 #include "extensions/browser/lazy_background_task_queue.h"
38 #include "extensions/browser/process_manager.h"
39 #include "extensions/common/api/app_runtime.h"
40 #include "extensions/common/extension.h"
41 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
42 #include "extensions/common/permissions/api_permission.h"
43 #include "extensions/common/permissions/permissions_data.h"
44 #include "net/base/filename_util.h"
45 #include "url/gurl.h"
46
47 #if defined(OS_CHROMEOS)
48 #include "components/user_manager/user_manager.h"
49 #endif
50
51 namespace app_runtime = extensions::api::app_runtime;
52
53 using content::BrowserThread;
54 using extensions::AppRuntimeEventRouter;
55 using extensions::api::app_runtime::PlayStoreStatus;
56 using extensions::app_file_handler_util::CreateFileEntry;
57 using extensions::app_file_handler_util::FileHandlerCanHandleEntry;
58 using extensions::app_file_handler_util::FileHandlerForId;
59 using extensions::app_file_handler_util::HasFileSystemWritePermission;
60 using extensions::app_file_handler_util::PrepareFilesForWritableApp;
61 using extensions::EventRouter;
62 using extensions::Extension;
63 using extensions::ExtensionHost;
64 using extensions::GrantedFileEntry;
65
66 namespace apps {
67
68 namespace {
69
70 const char kFallbackMimeType[] = "application/octet-stream";
71
72 bool DoMakePathAbsolute(const base::FilePath& current_directory,
73                         base::FilePath* file_path) {
74   DCHECK(file_path);
75   if (file_path->IsAbsolute())
76     return true;
77
78   if (current_directory.empty()) {
79     base::FilePath absolute_path = base::MakeAbsoluteFilePath(*file_path);
80     if (absolute_path.empty())
81       return false;
82     *file_path = absolute_path;
83     return true;
84   }
85
86   if (!current_directory.IsAbsolute())
87     return false;
88
89   *file_path = current_directory.Append(*file_path);
90   return true;
91 }
92
93 // Class to handle launching of platform apps to open specific paths.
94 // An instance of this class is created for each launch. The lifetime of these
95 // instances is managed by reference counted pointers. As long as an instance
96 // has outstanding tasks on a message queue it will be retained; once all
97 // outstanding tasks are completed it will be deleted.
98 class PlatformAppPathLauncher
99     : public base::RefCountedThreadSafe<PlatformAppPathLauncher> {
100  public:
101   PlatformAppPathLauncher(content::BrowserContext* context,
102                           const Extension* app,
103                           const std::vector<base::FilePath>& entry_paths)
104       : context_(context),
105         extension_id(app->id()),
106         entry_paths_(entry_paths),
107         mime_type_collector_(context),
108         is_directory_collector_(context) {}
109
110   PlatformAppPathLauncher(content::BrowserContext* context,
111                           const Extension* app,
112                           const base::FilePath& file_path)
113       : context_(context),
114         extension_id(app->id()),
115         mime_type_collector_(context),
116         is_directory_collector_(context) {
117     if (!file_path.empty())
118       entry_paths_.push_back(file_path);
119   }
120
121   void set_action_data(std::unique_ptr<app_runtime::ActionData> action_data) {
122     action_data_ = std::move(action_data);
123   }
124
125   void set_launch_source(extensions::AppLaunchSource launch_source) {
126     launch_source_ = launch_source;
127   }
128
129   void Launch() {
130     DCHECK_CURRENTLY_ON(BrowserThread::UI);
131
132     const Extension* app = GetExtension();
133     if (!app)
134       return;
135
136     if (entry_paths_.empty()) {
137       LaunchWithNoLaunchData();
138       return;
139     }
140
141     for (size_t i = 0; i < entry_paths_.size(); ++i) {
142       DCHECK(entry_paths_[i].IsAbsolute());
143     }
144
145     is_directory_collector_.CollectForEntriesPaths(
146         entry_paths_,
147         base::Bind(&PlatformAppPathLauncher::OnAreDirectoriesCollected, this,
148                    HasFileSystemWritePermission(app)));
149   }
150
151   void LaunchWithHandler(const std::string& handler_id) {
152     handler_id_ = handler_id;
153     Launch();
154   }
155
156   void LaunchWithRelativePath(const base::FilePath& current_directory) {
157     base::PostTaskWithTraits(
158         FROM_HERE,
159         {base::TaskPriority::USER_VISIBLE, base::MayBlock(),
160          base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
161         base::Bind(&PlatformAppPathLauncher::MakePathAbsolute, this,
162                    current_directory));
163   }
164
165  private:
166   friend class base::RefCountedThreadSafe<PlatformAppPathLauncher>;
167
168   virtual ~PlatformAppPathLauncher() = default;
169
170   void MakePathAbsolute(const base::FilePath& current_directory) {
171     for (std::vector<base::FilePath>::iterator it = entry_paths_.begin();
172          it != entry_paths_.end(); ++it) {
173       if (!DoMakePathAbsolute(current_directory, &*it)) {
174         LOG(WARNING) << "Cannot make absolute path from " << it->value();
175         base::PostTaskWithTraits(
176             FROM_HERE, {BrowserThread::UI},
177             base::Bind(&PlatformAppPathLauncher::LaunchWithNoLaunchData, this));
178         return;
179       }
180     }
181
182     base::PostTaskWithTraits(
183         FROM_HERE, {BrowserThread::UI},
184         base::Bind(&PlatformAppPathLauncher::Launch, this));
185   }
186
187   void OnFilesValid(std::unique_ptr<std::set<base::FilePath>> directory_paths) {
188     mime_type_collector_.CollectForLocalPaths(
189         entry_paths_,
190         base::Bind(
191             &PlatformAppPathLauncher::OnAreDirectoriesAndMimeTypesCollected,
192             this, base::Passed(std::move(directory_paths))));
193   }
194
195   void OnFilesInvalid(const base::FilePath& /* error_path */) {
196     LaunchWithNoLaunchData();
197   }
198
199   void LaunchWithNoLaunchData() {
200     // This method is required as an entry point on the UI thread.
201     DCHECK_CURRENTLY_ON(BrowserThread::UI);
202
203     const Extension* app = GetExtension();
204     if (!app)
205       return;
206
207     std::unique_ptr<app_runtime::LaunchData> launch_data =
208         std::make_unique<app_runtime::LaunchData>();
209     launch_data->action_data = std::move(action_data_);
210
211     AppRuntimeEventRouter::DispatchOnLaunchedEvent(
212         context_, app, launch_source_, std::move(launch_data));
213   }
214
215   void OnAreDirectoriesCollected(
216       bool has_file_system_write_permission,
217       std::unique_ptr<std::set<base::FilePath>> directory_paths) {
218     if (has_file_system_write_permission) {
219       std::set<base::FilePath>* const directory_paths_ptr =
220           directory_paths.get();
221       PrepareFilesForWritableApp(
222           entry_paths_, context_, *directory_paths_ptr,
223           base::Bind(&PlatformAppPathLauncher::OnFilesValid, this,
224                      base::Passed(std::move(directory_paths))),
225           base::Bind(&PlatformAppPathLauncher::OnFilesInvalid, this));
226       return;
227     }
228
229     OnFilesValid(std::move(directory_paths));
230   }
231
232   void OnAreDirectoriesAndMimeTypesCollected(
233       std::unique_ptr<std::set<base::FilePath>> directory_paths,
234       std::unique_ptr<std::vector<std::string>> mime_types) {
235     DCHECK(entry_paths_.size() == mime_types->size());
236     // If fetching a mime type failed, then use a fallback one.
237     for (size_t i = 0; i < entry_paths_.size(); ++i) {
238       const std::string mime_type =
239           !(*mime_types)[i].empty() ? (*mime_types)[i] : kFallbackMimeType;
240       bool is_directory =
241           directory_paths->find(entry_paths_[i]) != directory_paths->end();
242       entries_.push_back(
243           extensions::EntryInfo(entry_paths_[i], mime_type, is_directory));
244     }
245
246     const Extension* app = GetExtension();
247     if (!app)
248       return;
249
250     // Find file handler from the platform app for the file being opened.
251     const extensions::FileHandlerInfo* handler = NULL;
252     if (!handler_id_.empty()) {
253       handler = FileHandlerForId(*app, handler_id_);
254       if (handler) {
255         for (size_t i = 0; i < entry_paths_.size(); ++i) {
256           if (!FileHandlerCanHandleEntry(*handler, entries_[i])) {
257             LOG(WARNING)
258                 << "Extension does not provide a valid file handler for "
259                 << entry_paths_[i].value();
260             handler = NULL;
261             break;
262           }
263         }
264       }
265     } else {
266       const std::vector<const extensions::FileHandlerInfo*>& handlers =
267           extensions::app_file_handler_util::FindFileHandlersForEntries(
268               *app, entries_);
269       if (!handlers.empty())
270         handler = handlers[0];
271     }
272
273     // If this app doesn't have a file handler that supports the file, launch
274     // with no launch data.
275     if (!handler) {
276       LOG(WARNING) << "Extension does not provide a valid file handler.";
277       LaunchWithNoLaunchData();
278       return;
279     }
280
281     if (handler_id_.empty())
282       handler_id_ = handler->id;
283
284     // Access needs to be granted to the file for the process associated with
285     // the extension. To do this the ExtensionHost is needed. This might not be
286     // available, or it might be in the process of being unloaded, in which case
287     // the lazy background task queue is used to load the extension and then
288     // call back to us.
289     extensions::LazyBackgroundTaskQueue* const queue =
290         extensions::LazyBackgroundTaskQueue::Get(context_);
291     if (queue->ShouldEnqueueTask(context_, app)) {
292       queue->AddPendingTask(
293           context_, extension_id,
294           base::Bind(&PlatformAppPathLauncher::GrantAccessToFilesAndLaunch,
295                      this));
296       return;
297     }
298
299     extensions::ProcessManager* const process_manager =
300         extensions::ProcessManager::Get(context_);
301     ExtensionHost* const host =
302         process_manager->GetBackgroundHostForExtension(extension_id);
303     DCHECK(host);
304     GrantAccessToFilesAndLaunch(host);
305   }
306
307   void GrantAccessToFilesAndLaunch(ExtensionHost* host) {
308     const Extension* app = GetExtension();
309     if (!app)
310       return;
311
312     // If there was an error loading the app page, |host| will be NULL.
313     if (!host) {
314       LOG(ERROR) << "Could not load app page for " << extension_id;
315       return;
316     }
317
318     std::vector<GrantedFileEntry> granted_entries;
319     for (size_t i = 0; i < entry_paths_.size(); ++i) {
320       granted_entries.push_back(
321           CreateFileEntry(context_, app, host->render_process_host()->GetID(),
322                           entries_[i].path, entries_[i].is_directory));
323     }
324
325     AppRuntimeEventRouter::DispatchOnLaunchedEventWithFileEntries(
326         context_, app, launch_source_, handler_id_, entries_, granted_entries,
327         std::move(action_data_));
328   }
329
330   const Extension* GetExtension() const {
331     return extensions::ExtensionRegistry::Get(context_)->GetExtensionById(
332         extension_id, extensions::ExtensionRegistry::EVERYTHING);
333   }
334
335   // The browser context the app should be run in.
336   content::BrowserContext* context_;
337   // The id of the extension providing the app. A pointer to the extension is
338   // not kept as the extension may be unloaded and deleted during the course of
339   // the launch.
340   const std::string extension_id;
341   extensions::AppLaunchSource launch_source_ = extensions::SOURCE_FILE_HANDLER;
342   std::unique_ptr<app_runtime::ActionData> action_data_;
343   // A list of files and directories to be passed through to the app.
344   std::vector<base::FilePath> entry_paths_;
345   // A corresponding list with EntryInfo for every base::FilePath in
346   // entry_paths_.
347   std::vector<extensions::EntryInfo> entries_;
348   // The ID of the file handler used to launch the app.
349   std::string handler_id_;
350   extensions::app_file_handler_util::MimeTypeCollector mime_type_collector_;
351   extensions::app_file_handler_util::IsDirectoryCollector
352       is_directory_collector_;
353
354   DISALLOW_COPY_AND_ASSIGN(PlatformAppPathLauncher);
355 };
356
357 }  // namespace
358
359 void LaunchPlatformAppWithCommandLine(content::BrowserContext* context,
360                                       const extensions::Extension* app,
361                                       const base::CommandLine& command_line,
362                                       const base::FilePath& current_directory,
363                                       extensions::AppLaunchSource source,
364                                       PlayStoreStatus play_store_status) {
365   LaunchPlatformAppWithCommandLineAndLaunchId(context, app, "", command_line,
366                                               current_directory, source,
367                                               play_store_status);
368 }
369
370 void LaunchPlatformAppWithCommandLineAndLaunchId(
371     content::BrowserContext* context,
372     const extensions::Extension* app,
373     const std::string& launch_id,
374     const base::CommandLine& command_line,
375     const base::FilePath& current_directory,
376     extensions::AppLaunchSource source,
377     PlayStoreStatus play_store_status) {
378   // An app with "kiosk_only" should not be installed and launched
379   // outside of ChromeOS kiosk mode in the first place. This is a defensive
380   // check in case this scenario does occur.
381   if (extensions::KioskModeInfo::IsKioskOnly(app)) {
382     bool in_kiosk_mode = false;
383 #if defined(OS_CHROMEOS)
384     user_manager::UserManager* user_manager = user_manager::UserManager::Get();
385     in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
386 #endif
387     if (!in_kiosk_mode) {
388       LOG(ERROR) << "App with 'kiosk_only' attribute must be run in "
389                  << " ChromeOS kiosk mode.";
390       NOTREACHED();
391       return;
392     }
393   }
394
395 #if defined(OS_WIN)
396   base::CommandLine::StringType about_blank_url(
397       base::ASCIIToUTF16(url::kAboutBlankURL));
398 #else
399   base::CommandLine::StringType about_blank_url(url::kAboutBlankURL);
400 #endif
401   base::CommandLine::StringVector args = command_line.GetArgs();
402   // Browser tests will add about:blank to the command line. This should
403   // never be interpreted as a file to open, as doing so with an app that
404   // has write access will result in a file 'about' being created, which
405   // causes problems on the bots.
406   if (args.empty() || (command_line.HasSwitch(switches::kTestType) &&
407                        args[0] == about_blank_url)) {
408     std::unique_ptr<app_runtime::LaunchData> launch_data =
409         std::make_unique<app_runtime::LaunchData>();
410     if (play_store_status != PlayStoreStatus::PLAY_STORE_STATUS_UNKNOWN)
411       launch_data->play_store_status = play_store_status;
412     if (!launch_id.empty())
413       launch_data->id.reset(new std::string(launch_id));
414     AppRuntimeEventRouter::DispatchOnLaunchedEvent(context, app, source,
415                                                    std::move(launch_data));
416     return;
417   }
418
419   base::FilePath file_path(command_line.GetArgs()[0]);
420   scoped_refptr<PlatformAppPathLauncher> launcher =
421       new PlatformAppPathLauncher(context, app, file_path);
422   launcher->LaunchWithRelativePath(current_directory);
423 }
424
425 void LaunchPlatformAppWithPath(content::BrowserContext* context,
426                                const Extension* app,
427                                const base::FilePath& file_path) {
428   scoped_refptr<PlatformAppPathLauncher> launcher =
429       new PlatformAppPathLauncher(context, app, file_path);
430   launcher->Launch();
431 }
432
433 void LaunchPlatformAppWithAction(
434     content::BrowserContext* context,
435     const extensions::Extension* app,
436     std::unique_ptr<app_runtime::ActionData> action_data,
437     const base::FilePath& file_path) {
438   CHECK(!action_data || !action_data->is_lock_screen_action ||
439         !*action_data->is_lock_screen_action ||
440         app->permissions_data()->HasAPIPermission(
441             extensions::APIPermission::kLockScreen))
442       << "Launching lock screen action handler requires lockScreen permission.";
443
444   scoped_refptr<PlatformAppPathLauncher> launcher =
445       new PlatformAppPathLauncher(context, app, file_path);
446   launcher->set_action_data(std::move(action_data));
447   launcher->set_launch_source(extensions::AppLaunchSource::SOURCE_UNTRACKED);
448   launcher->Launch();
449 }
450
451 void LaunchPlatformApp(content::BrowserContext* context,
452                        const Extension* app,
453                        extensions::AppLaunchSource source) {
454   LaunchPlatformAppWithCommandLine(
455       context, app, base::CommandLine(base::CommandLine::NO_PROGRAM),
456       base::FilePath(), source);
457 }
458
459 void LaunchPlatformAppWithFileHandler(
460     content::BrowserContext* context,
461     const Extension* app,
462     const std::string& handler_id,
463     const std::vector<base::FilePath>& entry_paths) {
464   scoped_refptr<PlatformAppPathLauncher> launcher =
465       new PlatformAppPathLauncher(context, app, entry_paths);
466   launcher->LaunchWithHandler(handler_id);
467 }
468
469 void RestartPlatformApp(content::BrowserContext* context,
470                         const Extension* app) {
471   EventRouter* event_router = EventRouter::Get(context);
472   bool listening_to_restart = event_router->ExtensionHasEventListener(
473       app->id(), app_runtime::OnRestarted::kEventName);
474
475   if (listening_to_restart) {
476     AppRuntimeEventRouter::DispatchOnRestartedEvent(context, app);
477     return;
478   }
479
480   extensions::ExtensionPrefs* extension_prefs =
481       extensions::ExtensionPrefs::Get(context);
482   bool had_windows = extension_prefs->IsActive(app->id());
483   extension_prefs->SetIsActive(app->id(), false);
484   bool listening_to_launch = event_router->ExtensionHasEventListener(
485       app->id(), app_runtime::OnLaunched::kEventName);
486
487   if (listening_to_launch && had_windows) {
488     AppRuntimeEventRouter::DispatchOnLaunchedEvent(
489         context, app, extensions::SOURCE_RESTART, nullptr);
490   }
491 }
492
493 void LaunchPlatformAppWithUrl(content::BrowserContext* context,
494                               const Extension* app,
495                               const std::string& handler_id,
496                               const GURL& url,
497                               const GURL& referrer_url) {
498   AppRuntimeEventRouter::DispatchOnLaunchedEventWithUrl(
499       context, app, handler_id, url, referrer_url);
500 }
501
502 }  // namespace apps