| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/platform_app_launcher.h" | 5 #include "chrome/browser/extensions/platform_app_launcher.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/file_path.h" | 8 #include "base/file_path.h" |
| 9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
| 12 #include "base/string_util.h" | 12 #include "base/string_util.h" |
| 13 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 14 #include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h" | 14 #include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h" |
| 15 #include "chrome/browser/extensions/extension_host.h" | 15 #include "chrome/browser/extensions/extension_host.h" |
| 16 #include "chrome/browser/extensions/extension_process_manager.h" | 16 #include "chrome/browser/extensions/extension_process_manager.h" |
| 17 #include "chrome/browser/extensions/extension_system.h" | 17 #include "chrome/browser/extensions/extension_system.h" |
| 18 #include "chrome/browser/extensions/lazy_background_task_queue.h" | 18 #include "chrome/browser/extensions/lazy_background_task_queue.h" |
| 19 #include "chrome/browser/intents/web_intents_util.h" |
| 19 #include "chrome/browser/profiles/profile.h" | 20 #include "chrome/browser/profiles/profile.h" |
| 20 #include "chrome/common/extensions/extension.h" | 21 #include "chrome/common/extensions/extension.h" |
| 21 #include "chrome/common/extensions/extension_messages.h" | 22 #include "chrome/common/extensions/extension_messages.h" |
| 22 #include "content/public/browser/browser_thread.h" | 23 #include "content/public/browser/browser_thread.h" |
| 23 #include "content/public/browser/child_process_security_policy.h" | 24 #include "content/public/browser/child_process_security_policy.h" |
| 24 #include "content/public/browser/render_process_host.h" | 25 #include "content/public/browser/render_process_host.h" |
| 25 #include "content/public/browser/web_contents.h" | 26 #include "content/public/browser/web_contents.h" |
| 26 #include "content/public/browser/web_intents_dispatcher.h" | 27 #include "content/public/browser/web_intents_dispatcher.h" |
| 27 #include "net/base/mime_util.h" | 28 #include "net/base/mime_util.h" |
| 28 #include "net/base/net_util.h" | 29 #include "net/base/net_util.h" |
| 29 #include "webkit/fileapi/file_system_types.h" | 30 #include "webkit/fileapi/file_system_types.h" |
| 30 #include "webkit/fileapi/isolated_context.h" | 31 #include "webkit/fileapi/isolated_context.h" |
| 31 #include "webkit/glue/web_intent_data.h" | 32 #include "webkit/glue/web_intent_data.h" |
| 32 #include "webkit/glue/web_intent_service_data.h" | 33 #include "webkit/glue/web_intent_service_data.h" |
| 33 | 34 |
| 34 using content::BrowserThread; | 35 using content::BrowserThread; |
| 35 | 36 |
| 36 namespace extensions { | 37 namespace extensions { |
| 37 | 38 |
| 38 namespace { | 39 namespace { |
| 39 | 40 |
| 40 const char kViewIntent[] = "http://webintents.org/view"; | |
| 41 | |
| 42 bool MakePathAbsolute(const FilePath& current_directory, | 41 bool MakePathAbsolute(const FilePath& current_directory, |
| 43 FilePath* file_path) { | 42 FilePath* file_path) { |
| 44 DCHECK(file_path); | 43 DCHECK(file_path); |
| 45 if (file_path->IsAbsolute()) | 44 if (file_path->IsAbsolute()) |
| 46 return true; | 45 return true; |
| 47 | 46 |
| 48 if (current_directory.empty()) | 47 if (current_directory.empty()) |
| 49 return file_util::AbsolutePath(file_path); | 48 return file_util::AbsolutePath(file_path); |
| 50 | 49 |
| 51 if (!current_directory.IsAbsolute()) | 50 if (!current_directory.IsAbsolute()) |
| 52 return false; | 51 return false; |
| 53 | 52 |
| 54 *file_path = current_directory.Append(*file_path); | 53 *file_path = current_directory.Append(*file_path); |
| 55 return true; | 54 return true; |
| 56 } | 55 } |
| 57 | 56 |
| 58 // Class to handle launching of platform apps with command line information. | 57 bool GetAbsolutePathFromCommandLine(const CommandLine* command_line, |
| 58 const FilePath& current_directory, |
| 59 FilePath* path) { |
| 60 if (!command_line || !command_line->GetArgs().size()) |
| 61 return false; |
| 62 |
| 63 FilePath relative_path(command_line->GetArgs()[0]); |
| 64 FilePath absolute_path(relative_path); |
| 65 if (!MakePathAbsolute(current_directory, &absolute_path)) { |
| 66 LOG(WARNING) << "Cannot make absolute path from " << relative_path.value(); |
| 67 return false; |
| 68 } |
| 69 *path = absolute_path; |
| 70 return true; |
| 71 } |
| 72 |
| 73 // Helper method to launch the platform app |extension| with no data. This |
| 74 // should be called in the fallback case, where it has been impossible to |
| 75 // load or obtain file launch data. |
| 76 void LaunchPlatformAppWithNoData(Profile* profile, const Extension* extension) { |
| 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 78 extensions::AppEventRouter::DispatchOnLaunchedEvent(profile, extension); |
| 79 } |
| 80 |
| 81 // Class to handle launching of platform apps to open a specific path. |
| 59 // An instance of this class is created for each launch. The lifetime of these | 82 // An instance of this class is created for each launch. The lifetime of these |
| 60 // instances is managed by reference counted pointers. As long as an instance | 83 // instances is managed by reference counted pointers. As long as an instance |
| 61 // has outstanding tasks on a message queue it will be retained; once all | 84 // has outstanding tasks on a message queue it will be retained; once all |
| 62 // outstanding tasks are completed it will be deleted. | 85 // outstanding tasks are completed it will be deleted. |
| 63 class PlatformAppCommandLineLauncher | 86 class PlatformAppPathLauncher |
| 64 : public base::RefCountedThreadSafe<PlatformAppCommandLineLauncher> { | 87 : public base::RefCountedThreadSafe<PlatformAppPathLauncher> { |
| 65 public: | 88 public: |
| 66 PlatformAppCommandLineLauncher(Profile* profile, | 89 PlatformAppPathLauncher(Profile* profile, |
| 67 const Extension* extension, | 90 const Extension* extension, |
| 68 const CommandLine* command_line, | 91 const FilePath& file_path) |
| 69 const FilePath& current_directory) | |
| 70 : profile_(profile), | 92 : profile_(profile), |
| 71 extension_(extension), | 93 extension_(extension), |
| 72 command_line_(command_line), | 94 file_path_(file_path) {} |
| 73 current_directory_(current_directory) {} | |
| 74 | 95 |
| 75 void Launch() { | 96 void Launch() { |
| 76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 77 if (!command_line_ || !command_line_->GetArgs().size()) { | 98 if (file_path_.empty()) { |
| 78 LaunchWithNoLaunchData(); | 99 LaunchPlatformAppWithNoData(profile_, extension_); |
| 79 return; | 100 return; |
| 80 } | 101 } |
| 81 | 102 |
| 82 FilePath file_path(command_line_->GetArgs()[0]); | 103 DCHECK(file_path_.IsAbsolute()); |
| 83 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | 104 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( |
| 84 &PlatformAppCommandLineLauncher::GetMimeTypeAndLaunch, | 105 &PlatformAppPathLauncher::GetMimeTypeAndLaunch, this)); |
| 85 this, file_path)); | |
| 86 } | 106 } |
| 87 | 107 |
| 88 private: | 108 private: |
| 89 friend class base::RefCountedThreadSafe<PlatformAppCommandLineLauncher>; | 109 friend class base::RefCountedThreadSafe<PlatformAppPathLauncher>; |
| 90 | 110 |
| 91 virtual ~PlatformAppCommandLineLauncher() {} | 111 virtual ~PlatformAppPathLauncher() {} |
| 92 | 112 |
| 93 void LaunchWithNoLaunchData() { | 113 void GetMimeTypeAndLaunch() { |
| 94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 95 extensions::AppEventRouter::DispatchOnLaunchedEvent(profile_, extension_); | |
| 96 } | |
| 97 | |
| 98 void GetMimeTypeAndLaunch(const FilePath& file_path) { | |
| 99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 100 | 115 |
| 101 // If we cannot construct an absolute path, launch with no launch data. | 116 // If the file doesn't exist, or is a directory, launch with no launch data. |
| 102 FilePath absolute_path(file_path); | 117 if (!file_util::PathExists(file_path_) || |
| 103 if (!MakePathAbsolute(current_directory_, &absolute_path)) { | 118 file_util::DirectoryExists(file_path_)) { |
| 104 LOG(WARNING) << "Cannot make absolute path from " << file_path.value(); | 119 LOG(WARNING) << "No file exists with path " << file_path_.value(); |
| 105 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 120 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 106 &PlatformAppCommandLineLauncher::LaunchWithNoLaunchData, this)); | 121 &PlatformAppPathLauncher::LaunchWithNoLaunchData, this)); |
| 107 return; | |
| 108 } | |
| 109 | |
| 110 // If the file doesn't exist, or is a directory, launch with no launch data. | |
| 111 if (!file_util::PathExists(absolute_path) || | |
| 112 file_util::DirectoryExists(absolute_path)) { | |
| 113 LOG(WARNING) << "No file exists with path " << absolute_path.value(); | |
| 114 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | |
| 115 &PlatformAppCommandLineLauncher::LaunchWithNoLaunchData, this)); | |
| 116 return; | 122 return; |
| 117 } | 123 } |
| 118 | 124 |
| 119 std::string mime_type; | 125 std::string mime_type; |
| 120 // If we cannot obtain the MIME type, launch with no launch data. | 126 // If we cannot obtain the MIME type, launch with no launch data. |
| 121 if (!net::GetMimeTypeFromFile(absolute_path, &mime_type)) { | 127 if (!net::GetMimeTypeFromFile(file_path_, &mime_type)) { |
| 122 LOG(WARNING) << "Could not obtain MIME type for " | 128 LOG(WARNING) << "Could not obtain MIME type for " |
| 123 << absolute_path.value(); | 129 << file_path_.value(); |
| 124 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 130 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 125 &PlatformAppCommandLineLauncher::LaunchWithNoLaunchData, this)); | 131 &PlatformAppPathLauncher::LaunchWithNoLaunchData, this)); |
| 126 return; | 132 return; |
| 127 } | 133 } |
| 128 | 134 |
| 129 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 135 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 130 &PlatformAppCommandLineLauncher::LaunchWithMimeTypeAndPath, | 136 &PlatformAppPathLauncher::LaunchWithMimeType, this, mime_type)); |
| 131 this, absolute_path, mime_type)); | |
| 132 } | 137 } |
| 133 | 138 |
| 134 void LaunchWithMimeTypeAndPath(const FilePath& file_path, | 139 void LaunchWithNoLaunchData() { |
| 135 const std::string& mime_type) { | 140 // This method is required as an entry point on the UI thread. |
| 141 LaunchPlatformAppWithNoData(profile_, extension_); |
| 142 } |
| 143 |
| 144 void LaunchWithMimeType(const std::string& mime_type) { |
| 136 // Find the intent service from the platform app for the file being opened. | 145 // Find the intent service from the platform app for the file being opened. |
| 137 webkit_glue::WebIntentServiceData service; | 146 webkit_glue::WebIntentServiceData service; |
| 138 bool found_service = false; | 147 bool found_service = false; |
| 139 | 148 |
| 140 std::vector<webkit_glue::WebIntentServiceData> services = | 149 std::vector<webkit_glue::WebIntentServiceData> services = |
| 141 extension_->intents_services(); | 150 extension_->intents_services(); |
| 142 for (size_t i = 0; i < services.size(); i++) { | 151 for (size_t i = 0; i < services.size(); i++) { |
| 143 std::string service_type_ascii = UTF16ToASCII(services[i].type); | 152 std::string service_type_ascii = UTF16ToASCII(services[i].type); |
| 144 if (services[i].action == ASCIIToUTF16(kViewIntent) && | 153 if (services[i].action == ASCIIToUTF16(web_intents::kActionView) && |
| 145 net::MatchesMimeType(service_type_ascii, mime_type)) { | 154 net::MatchesMimeType(service_type_ascii, mime_type)) { |
| 146 service = services[i]; | 155 service = services[i]; |
| 147 found_service = true; | 156 found_service = true; |
| 148 break; | 157 break; |
| 149 } | 158 } |
| 150 } | 159 } |
| 151 | 160 |
| 152 // If this app doesn't have an intent that supports the file, launch with | 161 // If this app doesn't have an intent that supports the file, launch with |
| 153 // no launch data. | 162 // no launch data. |
| 154 if (!found_service) { | 163 if (!found_service) { |
| 155 LOG(WARNING) << "Extension does not provide a valid intent for " | 164 LOG(WARNING) << "Extension does not provide a valid intent for " |
| 156 << file_path.value(); | 165 << file_path_.value(); |
| 157 LaunchWithNoLaunchData(); | 166 LaunchWithNoLaunchData(); |
| 158 return; | 167 return; |
| 159 } | 168 } |
| 160 | 169 |
| 161 // Access needs to be granted to the file for the process associated with | 170 // Access needs to be granted to the file for the process associated with |
| 162 // the extension. To do this the ExtensionHost is needed. This might not be | 171 // the extension. To do this the ExtensionHost is needed. This might not be |
| 163 // available, or it might be in the process of being unloaded, in which case | 172 // available, or it might be in the process of being unloaded, in which case |
| 164 // the lazy background task queue is used to load the extension and then | 173 // the lazy background task queue is used to load the extension and then |
| 165 // call back to us. | 174 // call back to us. |
| 166 extensions::LazyBackgroundTaskQueue* queue = | 175 extensions::LazyBackgroundTaskQueue* queue = |
| 167 ExtensionSystem::Get(profile_)->lazy_background_task_queue(); | 176 ExtensionSystem::Get(profile_)->lazy_background_task_queue(); |
| 168 if (queue->ShouldEnqueueTask(profile_, extension_)) { | 177 if (queue->ShouldEnqueueTask(profile_, extension_)) { |
| 169 queue->AddPendingTask(profile_, extension_->id(), base::Bind( | 178 queue->AddPendingTask(profile_, extension_->id(), base::Bind( |
| 170 &PlatformAppCommandLineLauncher::GrantAccessToFileAndLaunch, | 179 &PlatformAppPathLauncher::GrantAccessToFileAndLaunch, |
| 171 this, file_path, mime_type)); | 180 this, mime_type)); |
| 172 return; | 181 return; |
| 173 } | 182 } |
| 174 | 183 |
| 175 ExtensionProcessManager* process_manager = | 184 ExtensionProcessManager* process_manager = |
| 176 ExtensionSystem::Get(profile_)->process_manager(); | 185 ExtensionSystem::Get(profile_)->process_manager(); |
| 177 extensions::ExtensionHost* host = | 186 extensions::ExtensionHost* host = |
| 178 process_manager->GetBackgroundHostForExtension(extension_->id()); | 187 process_manager->GetBackgroundHostForExtension(extension_->id()); |
| 179 DCHECK(host); | 188 DCHECK(host); |
| 180 GrantAccessToFileAndLaunch(file_path, mime_type, host); | 189 GrantAccessToFileAndLaunch(mime_type, host); |
| 181 } | 190 } |
| 182 | 191 |
| 183 void GrantAccessToFileAndLaunch(const FilePath& file_path, | 192 void GrantAccessToFileAndLaunch(const std::string& mime_type, |
| 184 const std::string& mime_type, | |
| 185 extensions::ExtensionHost* host) { | 193 extensions::ExtensionHost* host) { |
| 186 // If there was an error loading the app page, |host| will be NULL. | 194 // If there was an error loading the app page, |host| will be NULL. |
| 187 if (!host) { | 195 if (!host) { |
| 188 LOG(ERROR) << "Could not load app page for " << extension_->id(); | 196 LOG(ERROR) << "Could not load app page for " << extension_->id(); |
| 189 return; | 197 return; |
| 190 } | 198 } |
| 191 | 199 |
| 192 content::ChildProcessSecurityPolicy* policy = | 200 content::ChildProcessSecurityPolicy* policy = |
| 193 content::ChildProcessSecurityPolicy::GetInstance(); | 201 content::ChildProcessSecurityPolicy::GetInstance(); |
| 194 int renderer_id = host->render_process_host()->GetID(); | 202 int renderer_id = host->render_process_host()->GetID(); |
| 195 | 203 |
| 196 // Granting read file permission to allow reading file content. | 204 // Granting read file permission to allow reading file content. |
| 197 // If the renderer already has permission to read these paths, it is not | 205 // If the renderer already has permission to read these paths, it is not |
| 198 // regranted, as this would overwrite any other permissions which the | 206 // regranted, as this would overwrite any other permissions which the |
| 199 // renderer may already have. | 207 // renderer may already have. |
| 200 if (!policy->CanReadFile(renderer_id, file_path)) | 208 if (!policy->CanReadFile(renderer_id, file_path_)) |
| 201 policy->GrantReadFile(renderer_id, file_path); | 209 policy->GrantReadFile(renderer_id, file_path_); |
| 202 | 210 |
| 203 std::string registered_name; | 211 std::string registered_name; |
| 204 fileapi::IsolatedContext* isolated_context = | 212 fileapi::IsolatedContext* isolated_context = |
| 205 fileapi::IsolatedContext::GetInstance(); | 213 fileapi::IsolatedContext::GetInstance(); |
| 206 DCHECK(isolated_context); | 214 DCHECK(isolated_context); |
| 207 std::string filesystem_id = isolated_context->RegisterFileSystemForPath( | 215 std::string filesystem_id = isolated_context->RegisterFileSystemForPath( |
| 208 fileapi::kFileSystemTypeNativeLocal, file_path, ®istered_name); | 216 fileapi::kFileSystemTypeNativeLocal, file_path_, ®istered_name); |
| 209 // Granting read file system permission as well to allow file-system | 217 // Granting read file system permission as well to allow file-system |
| 210 // read operations. | 218 // read operations. |
| 211 policy->GrantReadFileSystem(renderer_id, filesystem_id); | 219 policy->GrantReadFileSystem(renderer_id, filesystem_id); |
| 212 | 220 |
| 213 extensions::AppEventRouter::DispatchOnLaunchedEventWithFileEntry( | 221 extensions::AppEventRouter::DispatchOnLaunchedEventWithFileEntry( |
| 214 profile_, extension_, ASCIIToUTF16(kViewIntent), filesystem_id, | 222 profile_, extension_, ASCIIToUTF16(web_intents::kActionView), |
| 215 registered_name); | 223 filesystem_id, registered_name); |
| 216 } | 224 } |
| 217 | 225 |
| 218 // The profile the app should be run in. | 226 // The profile the app should be run in. |
| 219 Profile* profile_; | 227 Profile* profile_; |
| 220 // The extension providing the app. | 228 // The extension providing the app. |
| 221 const Extension* extension_; | 229 const Extension* extension_; |
| 222 // The command line to be passed through to the app, or NULL. | 230 // The path to be passed through to the app. |
| 223 const CommandLine* command_line_; | 231 const FilePath file_path_; |
| 224 // If non-empty, this is used to expand relative paths. | |
| 225 const FilePath current_directory_; | |
| 226 | 232 |
| 227 DISALLOW_COPY_AND_ASSIGN(PlatformAppCommandLineLauncher); | 233 DISALLOW_COPY_AND_ASSIGN(PlatformAppPathLauncher); |
| 228 }; | 234 }; |
| 229 | 235 |
| 230 // Class to handle launching of platform apps with WebIntent data. | 236 // Class to handle launching of platform apps with WebIntent data. |
| 231 // An instance of this class is created for each launch. The lifetime of these | 237 // An instance of this class is created for each launch. The lifetime of these |
| 232 // instances is managed by reference counted pointers. As long as an instance | 238 // instances is managed by reference counted pointers. As long as an instance |
| 233 // has outstanding tasks on a message queue it will be retained; once all | 239 // has outstanding tasks on a message queue it will be retained; once all |
| 234 // outstanding tasks are completed it will be deleted. | 240 // outstanding tasks are completed it will be deleted. |
| 235 class PlatformAppWebIntentLauncher | 241 class PlatformAppWebIntentLauncher |
| 236 : public base::RefCountedThreadSafe<PlatformAppWebIntentLauncher> { | 242 : public base::RefCountedThreadSafe<PlatformAppWebIntentLauncher> { |
| 237 public: | 243 public: |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 | 339 |
| 334 DISALLOW_COPY_AND_ASSIGN(PlatformAppWebIntentLauncher); | 340 DISALLOW_COPY_AND_ASSIGN(PlatformAppWebIntentLauncher); |
| 335 }; | 341 }; |
| 336 | 342 |
| 337 } // namespace | 343 } // namespace |
| 338 | 344 |
| 339 void LaunchPlatformApp(Profile* profile, | 345 void LaunchPlatformApp(Profile* profile, |
| 340 const Extension* extension, | 346 const Extension* extension, |
| 341 const CommandLine* command_line, | 347 const CommandLine* command_line, |
| 342 const FilePath& current_directory) { | 348 const FilePath& current_directory) { |
| 349 FilePath path; |
| 350 if (!GetAbsolutePathFromCommandLine(command_line, current_directory, &path)) { |
| 351 LaunchPlatformAppWithNoData(profile, extension); |
| 352 return; |
| 353 } |
| 354 |
| 355 LaunchPlatformAppWithPath(profile, extension, path); |
| 356 } |
| 357 |
| 358 void LaunchPlatformAppWithPath(Profile* profile, |
| 359 const Extension* extension, |
| 360 const FilePath& file_path) { |
| 343 // launcher will be freed when nothing has a reference to it. The message | 361 // launcher will be freed when nothing has a reference to it. The message |
| 344 // queue will retain a reference for any outstanding task, so when the | 362 // queue will retain a reference for any outstanding task, so when the |
| 345 // launcher has finished it will be freed. | 363 // launcher has finished it will be freed. |
| 346 scoped_refptr<PlatformAppCommandLineLauncher> launcher = | 364 scoped_refptr<PlatformAppPathLauncher> launcher = |
| 347 new PlatformAppCommandLineLauncher(profile, extension, command_line, | 365 new PlatformAppPathLauncher(profile, extension, file_path); |
| 348 current_directory); | |
| 349 launcher->Launch(); | 366 launcher->Launch(); |
| 350 } | 367 } |
| 351 | 368 |
| 352 void LaunchPlatformAppWithWebIntent( | 369 void LaunchPlatformAppWithWebIntent( |
| 353 Profile* profile, | 370 Profile* profile, |
| 354 const Extension* extension, | 371 const Extension* extension, |
| 355 content::WebIntentsDispatcher* intents_dispatcher, | 372 content::WebIntentsDispatcher* intents_dispatcher, |
| 356 content::WebContents* source) { | 373 content::WebContents* source) { |
| 357 scoped_refptr<PlatformAppWebIntentLauncher> launcher = | 374 scoped_refptr<PlatformAppWebIntentLauncher> launcher = |
| 358 new PlatformAppWebIntentLauncher( | 375 new PlatformAppWebIntentLauncher( |
| 359 profile, extension, intents_dispatcher, source); | 376 profile, extension, intents_dispatcher, source); |
| 360 launcher->Launch(); | 377 launcher->Launch(); |
| 361 } | 378 } |
| 362 | 379 |
| 363 } // namespace extensions | 380 } // namespace extensions |
| OLD | NEW |