Chromium Code Reviews| Index: chrome/browser/extensions/platform_app_launcher.cc |
| diff --git a/chrome/browser/extensions/platform_app_launcher.cc b/chrome/browser/extensions/platform_app_launcher.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..189bcf964b296a1ac09f3e83221e959106f20604 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/platform_app_launcher.cc |
| @@ -0,0 +1,191 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/extensions/platform_app_launcher.h" |
| + |
| +#include "base/command_line.h" |
| +#include "base/file_path.h" |
| +#include "base/file_util.h" |
| +#include "base/logging.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/string_util.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "chrome/browser/extensions/api/app/app_api.h" |
| +#include "chrome/browser/extensions/extension_host.h" |
| +#include "chrome/browser/extensions/extension_process_manager.h" |
| +#include "chrome/browser/extensions/extension_system.h" |
| +#include "chrome/browser/extensions/lazy_background_task_queue.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/common/extensions/extension.h" |
| +#include "chrome/common/extensions/extension_messages.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/child_process_security_policy.h" |
| +#include "content/public/browser/render_process_host.h" |
| +#include "net/base/mime_util.h" |
| +#include "net/base/net_util.h" |
| +#include "webkit/fileapi/isolated_context.h" |
| +#include "webkit/glue/web_intent_service_data.h" |
| + |
| +using content::BrowserThread; |
| + |
| +namespace { |
| + |
| +const char kViewIntent[] = "http://webintents.org/view"; |
|
Mihai Parparita -not on Chrome
2012/05/18 04:26:04
Rather than having this as a a constant here and D
benwells
2012/05/18 04:56:55
How is it error-prone? Do you mean if the code use
benwells
2012/05/22 13:15:03
After talking to Mihai will leave as is.
|
| + |
| +class PlatformAppLauncher |
| + : public base::RefCountedThreadSafe<PlatformAppLauncher> { |
| + public: |
| + PlatformAppLauncher(Profile* profile, |
| + const Extension* extension, |
| + const CommandLine* command_line) |
| + : profile_(profile), |
| + extension_(extension), |
| + command_line_(command_line) {} |
| + |
| + void Launch() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (!command_line_ || !command_line_->GetArgs().size()) { |
| + LaunchWithNoLaunchData(); |
| + return; |
| + } |
| + |
| + FilePath file_path(command_line_->GetArgs()[0]); |
| + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( |
| + &PlatformAppLauncher::GetMimeTypeAndLaunch, this, file_path)); |
| + } |
| + |
| + private: |
| + void LaunchWithNoLaunchData() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + extensions::AppEventRouter::DispatchOnLaunchedEvent(profile_, extension_); |
| + } |
| + |
| + void GetMimeTypeAndLaunch(const FilePath& file_path) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + |
| + // If the file doesn't exist, or is a directory, launch with no launch data. |
| + if (!file_util::PathExists(file_path) || |
| + file_util::DirectoryExists(file_path)) { |
| + LOG(WARNING) << "No file exists with path " << file_path.value(); |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| + &PlatformAppLauncher::LaunchWithNoLaunchData, this)); |
| + return; |
| + } |
| + |
| + std::string mime_type; |
| + // If we cannot obtain the MIME type, launch with no launch data. |
| + if (!net::GetMimeTypeFromFile(file_path, &mime_type)) { |
| + LOG(WARNING) << "Could not obtain MIME type for " << file_path.value(); |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| + &PlatformAppLauncher::LaunchWithNoLaunchData, this)); |
| + return; |
| + } |
| + |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| + &PlatformAppLauncher::LaunchWithMimeTypeAndPath, this, file_path, |
| + mime_type)); |
| + } |
| + |
| + void LaunchWithMimeTypeAndPath(const FilePath& file_path, |
| + const std::string& mime_type) { |
| + // Find the intent service from the platform app for the file being opened. |
| + webkit_glue::WebIntentServiceData service; |
| + bool found_service = false; |
| + |
| + std::vector<webkit_glue::WebIntentServiceData> services = |
| + extension_->intents_services(); |
| + for (size_t i = 0; i < services.size(); i++) { |
| + std::string service_type_ascii = UTF16ToASCII(services[i].type); |
| + if (services[i].action == ASCIIToUTF16(kViewIntent) && |
| + net::MatchesMimeType(service_type_ascii, mime_type)) { |
| + service = services[i]; |
| + found_service = true; |
|
Mihai Parparita -not on Chrome
2012/05/18 04:26:04
Add a break here.
benwells
2012/05/22 13:15:03
Done.
|
| + } |
| + } |
| + |
| + // If this app doesn't have an intent that supports the file, launch with |
| + // no launch data. |
| + if (!found_service) { |
| + LOG(WARNING) << "Extension does not provide a valid intent for " |
| + << file_path.value(); |
| + LaunchWithNoLaunchData(); |
| + return; |
| + } |
| + |
| + // We need to grant access to the file for the process associated with the |
| + // extension. To do this we need the ExtensionHost, so we try and get it. |
| + ExtensionProcessManager* pm = |
| + ExtensionSystem::Get(profile_)->process_manager(); |
|
kinuko
2012/05/18 10:22:17
nit: indent
benwells
2012/05/22 13:15:03
Done.
|
| + ExtensionHost* host = pm->GetBackgroundHostForExtension(extension_->id()); |
| + if (host) { |
| + GrantAccessToFileAndLaunch(file_path, mime_type, host); |
| + return; |
| + } |
| + |
| + // The host isn't loaded yet, so we post a task to the queue for the host. |
|
Mihai Parparita -not on Chrome
2012/05/18 04:26:04
Can this be racy (the host was about to be shut do
benwells
2012/05/18 04:56:55
Good point. I initially used AddPendingTask but it
Matt Perry
2012/05/18 19:02:41
Thanks for checking. Rather than checking if host
Matt Perry
2012/05/18 19:04:38
Sorry, I meant to say "if ShouldEnqueueTask return
benwells
2012/05/22 13:15:03
Done.
|
| + // This will load the background page, and then call our task. |
| + extensions::LazyBackgroundTaskQueue* queue = |
| + ExtensionSystem::Get(profile_)->lazy_background_task_queue(); |
|
Matt Perry
2012/05/18 19:02:41
indent += 2
benwells
2012/05/22 13:15:03
Done.
|
| + queue->AddPendingTask(profile_, extension_->id(), |
| + base::Bind(&PlatformAppLauncher::GrantAccessToFileAndLaunch, |
| + this, file_path, mime_type)); |
| + } |
| + |
| + void GrantAccessToFileAndLaunch(const FilePath& file_path, |
| + const std::string& mime_type, |
| + ExtensionHost* host) { |
| + // If there was an error loading the app page, |host| will be NULL. |
| + if (!host) { |
| + LOG(ERROR) << "Could not load app page for " << extension_->id(); |
| + return; |
| + } |
| + |
| + content::ChildProcessSecurityPolicy* policy = |
| + content::ChildProcessSecurityPolicy::GetInstance(); |
| + int renderer_id = host->render_process_host()->GetID(); |
| + |
| + std::set<FilePath> filesets; |
|
Mihai Parparita -not on Chrome
2012/05/18 04:26:04
Nit: this can be moved a few lines lower, since it
benwells
2012/05/22 13:15:03
Done.
|
| + // If the renderer already has permission to read these paths, we don't |
| + // regrant, as this would overwrite any other permissions which the renderer |
| + // may already have. |
| + if (!policy->CanReadFile(renderer_id, file_path)) |
| + policy->GrantReadFile(renderer_id, file_path); |
| + |
| + filesets.insert(file_path); |
| + |
| + fileapi::IsolatedContext* isolated_context = |
| + fileapi::IsolatedContext::GetInstance(); |
| + DCHECK(isolated_context); |
| + std::string filesystem_id = isolated_context->RegisterIsolatedFileSystem( |
| + filesets); |
| + policy->GrantAccessFileSystem(renderer_id, filesystem_id); |
| + |
| + extensions::AppEventRouter::DispatchOnLaunchedEventWithFileEntry( |
| + profile_, extension_, ASCIIToUTF16(kViewIntent), mime_type, |
| + filesystem_id, file_path.BaseName().value()); |
| + } |
| + |
| + Profile* profile_; |
| + const Extension* extension_; |
| + const CommandLine* command_line_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(PlatformAppLauncher); |
| +}; |
| + |
| +} // namespace |
| + |
| +namespace extensions { |
| + |
| +void LaunchPlatformApp(Profile* profile, |
| + const Extension* extension, |
| + const CommandLine* command_line) { |
| + // launcher will be freed when nothing has a reference to it. The message |
| + // queue will retain a reference for any outstanding task, so when the |
| + // launcher has finished it will be freed. |
| + scoped_refptr<PlatformAppLauncher> launcher = |
| + new PlatformAppLauncher(profile, extension, command_line); |
| + launcher->Launch(); |
| +} |
| + |
| +} // namespace extensions |