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 "chrome/browser/apps/ephemeral_app_launcher.h" 6 7 #include "chrome/browser/extensions/extension_install_prompt.h" 8 #include "chrome/browser/extensions/extension_util.h" 9 #include "chrome/browser/profiles/profile.h" 10 #include "chrome/browser/ui/extensions/application_launch.h" 11 #include "chrome/browser/ui/extensions/extension_enable_flow.h" 12 #include "content/public/browser/web_contents.h" 13 #include "extensions/browser/extension_registry.h" 14 #include "extensions/common/permissions/permissions_data.h" 15 16 using content::WebContents; 17 using extensions::Extension; 18 using extensions::ExtensionRegistry; 19 using extensions::WebstoreInstaller; 20 21 namespace { 22 23 const char kInvalidManifestError[] = "Invalid manifest"; 24 const char kExtensionTypeError[] = "Ephemeral extensions are not permitted"; 25 const char kLaunchAbortedError[] = "Launch aborted"; 26 27 Profile* ProfileForWebContents(content::WebContents* contents) { 28 if (!contents) 29 return NULL; 30 31 return Profile::FromBrowserContext(contents->GetBrowserContext()); 32 } 33 34 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) { 35 if (!contents) 36 return NULL; 37 38 return contents->GetTopLevelNativeWindow(); 39 } 40 41 } // namespace 42 43 // static 44 scoped_refptr<EphemeralAppLauncher> 45 EphemeralAppLauncher::CreateForLauncher( 46 const std::string& webstore_item_id, 47 Profile* profile, 48 gfx::NativeWindow parent_window, 49 const Callback& callback) { 50 scoped_refptr<EphemeralAppLauncher> installer = 51 new EphemeralAppLauncher(webstore_item_id, 52 profile, 53 parent_window, 54 callback); 55 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER); 56 return installer; 57 } 58 59 // static 60 scoped_refptr<EphemeralAppLauncher> 61 EphemeralAppLauncher::CreateForLink( 62 const std::string& webstore_item_id, 63 content::WebContents* web_contents) { 64 scoped_refptr<EphemeralAppLauncher> installer = 65 new EphemeralAppLauncher(webstore_item_id, 66 web_contents, 67 Callback()); 68 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER); 69 return installer; 70 } 71 72 void EphemeralAppLauncher::Start() { 73 const Extension* extension = 74 ExtensionRegistry::Get(profile()) 75 ->GetExtensionById(id(), ExtensionRegistry::EVERYTHING); 76 if (extension) { 77 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(), 78 profile())) { 79 LaunchApp(extension); 80 InvokeCallback(std::string()); 81 return; 82 } 83 84 // The ephemeral app may have been updated and disabled as it requests 85 // more permissions. In this case we should always prompt before 86 // launching. 87 extension_enable_flow_.reset( 88 new ExtensionEnableFlow(profile(), extension->id(), this)); 89 if (web_contents()) 90 extension_enable_flow_->StartForWebContents(web_contents()); 91 else 92 extension_enable_flow_->StartForNativeWindow(parent_window_); 93 94 // Keep this object alive until the enable flow is complete. 95 AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall. 96 return; 97 } 98 99 // Fetch the app from the webstore. 100 BeginInstall(); 101 } 102 103 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id, 104 Profile* profile, 105 gfx::NativeWindow parent_window, 106 const Callback& callback) 107 : WebstoreStandaloneInstaller(webstore_item_id, profile, callback), 108 parent_window_(parent_window), 109 dummy_web_contents_( 110 WebContents::Create(WebContents::CreateParams(profile))) { 111 } 112 113 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id, 114 content::WebContents* web_contents, 115 const Callback& callback) 116 : WebstoreStandaloneInstaller(webstore_item_id, 117 ProfileForWebContents(web_contents), 118 callback), 119 content::WebContentsObserver(web_contents), 120 parent_window_(NativeWindowForWebContents(web_contents)) { 121 } 122 123 EphemeralAppLauncher::~EphemeralAppLauncher() {} 124 125 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const { 126 DCHECK(extension); 127 if (!extension->is_app()) { 128 LOG(ERROR) << "Unable to launch extension " << extension->id() 129 << ". It is not an app."; 130 return; 131 } 132 133 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB); 134 params.desktop_type = 135 chrome::GetHostDesktopTypeForNativeWindow(parent_window_); 136 OpenApplication(params); 137 } 138 139 bool EphemeralAppLauncher::CheckRequestorAlive() const { 140 return dummy_web_contents_.get() != NULL || web_contents() != NULL; 141 } 142 143 const GURL& EphemeralAppLauncher::GetRequestorURL() const { 144 return GURL::EmptyGURL(); 145 } 146 147 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const { 148 return false; 149 } 150 151 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const { 152 return false; 153 } 154 155 WebContents* EphemeralAppLauncher::GetWebContents() const { 156 return web_contents() ? web_contents() : dummy_web_contents_.get(); 157 } 158 159 scoped_refptr<ExtensionInstallPrompt::Prompt> 160 EphemeralAppLauncher::CreateInstallPrompt() const { 161 DCHECK(extension_.get() != NULL); 162 163 // Skip the prompt by returning null if the app does not need to display 164 // permission warnings. 165 extensions::PermissionMessages permissions = 166 extension_->permissions_data()->GetPermissionMessages(); 167 if (permissions.empty()) 168 return NULL; 169 170 return make_scoped_refptr(new ExtensionInstallPrompt::Prompt( 171 ExtensionInstallPrompt::LAUNCH_PROMPT)); 172 } 173 174 bool EphemeralAppLauncher::CheckInlineInstallPermitted( 175 const base::DictionaryValue& webstore_data, 176 std::string* error) const { 177 *error = ""; 178 return true; 179 } 180 181 bool EphemeralAppLauncher::CheckRequestorPermitted( 182 const base::DictionaryValue& webstore_data, 183 std::string* error) const { 184 *error = ""; 185 return true; 186 } 187 188 bool EphemeralAppLauncher::CheckInstallValid( 189 const base::DictionaryValue& manifest, 190 std::string* error) { 191 extension_ = Extension::Create( 192 base::FilePath(), 193 extensions::Manifest::INTERNAL, 194 manifest, 195 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE, 196 id(), 197 error); 198 if (!extension_.get()) { 199 *error = kInvalidManifestError; 200 return false; 201 } 202 203 if (!extension_->is_app()) { 204 *error = kExtensionTypeError; 205 return false; 206 } 207 208 return true; 209 } 210 211 scoped_ptr<ExtensionInstallPrompt> 212 EphemeralAppLauncher::CreateInstallUI() { 213 if (web_contents()) 214 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents())); 215 216 return make_scoped_ptr( 217 new ExtensionInstallPrompt(profile(), parent_window_, NULL)); 218 } 219 220 scoped_ptr<WebstoreInstaller::Approval> 221 EphemeralAppLauncher::CreateApproval() const { 222 scoped_ptr<WebstoreInstaller::Approval> approval = 223 WebstoreStandaloneInstaller::CreateApproval(); 224 approval->is_ephemeral = true; 225 return approval.Pass(); 226 } 227 228 void EphemeralAppLauncher::CompleteInstall(const std::string& error) { 229 if (error.empty()) { 230 const Extension* extension = 231 ExtensionRegistry::Get(profile()) 232 ->GetExtensionById(id(), ExtensionRegistry::ENABLED); 233 if (extension) 234 LaunchApp(extension); 235 } 236 237 WebstoreStandaloneInstaller::CompleteInstall(error); 238 } 239 240 void EphemeralAppLauncher::WebContentsDestroyed() { 241 AbortInstall(); 242 } 243 244 void EphemeralAppLauncher::ExtensionEnableFlowFinished() { 245 CompleteInstall(std::string()); 246 } 247 248 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) { 249 CompleteInstall(kLaunchAbortedError); 250 } 251