launcher.cc 19 KB

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