Commit 4b2dee32 authored by tmdiep@chromium.org's avatar tmdiep@chromium.org

Launch ephemeral app for webstore detail links

Added EphemeralAppThrottle, which will launch ephemeral apps for
links to Chrome Web Store detail pages instead of opening the page in
the browser. This will only occur if the enable-ephemeral-apps switch
is enabled.

For experimental purposes only. The extension will currently be
installed in Chrome.

BUG=312460

Review URL: https://codereview.chromium.org/51943003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233419 0039d316-1c4b-4281-b951-d872f2087c98
parent bf10864d
// Copyright 2013 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/apps/ephemeral_app_throttle.h"
#include "base/command_line.h"
#include "chrome/browser/extensions/extension_info_map.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/webstore_ephemeral_installer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "components/navigation_interception/intercept_navigation_resource_throttle.h"
#include "components/navigation_interception/navigation_params.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_throttle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "net/url_request/url_request.h"
using content::BrowserThread;
using content::WebContents;
using extensions::Extension;
using extensions::WebstoreEphemeralInstaller;
namespace {
bool LaunchEphemeralApp(
const std::string& app_id,
content::RenderViewHost* source,
const navigation_interception::NavigationParams& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Redirect top-level navigations only.
if (source->IsSubframe())
return false;
WebContents* web_contents = WebContents::FromRenderViewHost(source);
if (!web_contents)
return false;
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
if (!profile)
return false;
ExtensionService* service =
extensions::ExtensionSystem::Get(profile)->extension_service();
DCHECK(service);
const Extension* extension = service->GetExtensionById(app_id, false);
if (extension && extension->is_app()) {
// If the app is already installed, launch it.
AppLaunchParams params(profile, extension, NEW_FOREGROUND_TAB);
params.desktop_type = chrome::GetHostDesktopTypeForNativeView(
web_contents->GetView()->GetNativeView());
OpenApplication(params);
return true;
}
if (!extension) {
// Install ephemeral app and launch.
scoped_refptr<WebstoreEphemeralInstaller> installer =
WebstoreEphemeralInstaller::CreateForLink(app_id, web_contents);
installer->BeginInstall();
return true;
}
return false;
}
} // namespace
// static
content::ResourceThrottle*
EphemeralAppThrottle::MaybeCreateThrottleForLaunch(
net::URLRequest* request,
ProfileIOData* profile_io_data) {
if (!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableEphemeralApps))
return NULL;
if (request->method() != "GET" || !request->url().SchemeIsHTTPOrHTTPS())
return NULL;
// Not supported for incognito profiles.
if (profile_io_data->is_incognito())
return NULL;
// Crudely watch for links to Chrome Web Store detail pages and assume that
// the app ID will be after the last slash of the URL. We cannot even
// differentiate between apps and extensions, so attempt to launch both.
// This is obviously for experimental purposes only and will be implemented
// properly in production code - for example, links to ephemeral apps could
// have a new scheme.
if (request->url().spec().find(
extension_urls::GetWebstoreItemDetailURLPrefix()) != 0)
return NULL;
std::string app_id(request->url().ExtractFileName());
if (!Extension::IdIsValid(app_id))
return NULL;
return new navigation_interception::InterceptNavigationResourceThrottle(
request,
base::Bind(&LaunchEphemeralApp, app_id));
}
// Copyright 2013 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.
#ifndef CHROME_BROWSER_APPS_EPHEMERAL_APP_THROTTLE_H_
#define CHROME_BROWSER_APPS_EPHEMERAL_APP_THROTTLE_H_
#include "base/basictypes.h"
namespace content {
class ResourceThrottle;
}
namespace net {
class URLRequest;
}
class ProfileIOData;
// This class creates resource throttles that will launch ephemeral apps for
// links to Chrome Web Store detail pages instead of opening the page in the
// browser. This will only occur if the enable-ephemeral-apps switch is enabled.
class EphemeralAppThrottle {
public:
static content::ResourceThrottle* MaybeCreateThrottleForLaunch(
net::URLRequest* request,
ProfileIOData* profile_io_data);
private:
DISALLOW_COPY_AND_ASSIGN(EphemeralAppThrottle);
};
#endif // CHROME_BROWSER_APPS_EPHEMERAL_APP_THROTTLE_H_
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "chrome/common/extensions/permissions/permissions_data.h" #include "chrome/common/extensions/permissions/permissions_data.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
using content::WebContents; using content::WebContents;
...@@ -22,13 +23,14 @@ namespace { ...@@ -22,13 +23,14 @@ namespace {
// TODO(tmdiep): Launching the app will be moved downstream when an ephemeral // TODO(tmdiep): Launching the app will be moved downstream when an ephemeral
// app service/manager is implemented. // app service/manager is implemented.
void LaunchApp(Profile* profile, const std::string& id) { void LaunchApp(Profile* profile, const std::string& id) {
ExtensionService* service = extensions::ExtensionSystem::Get(profile)-> ExtensionService* service =
extension_service(); ExtensionSystem::Get(profile)->extension_service();
DCHECK(service); DCHECK(service);
const extensions::Extension* extension = service->GetExtensionById(id, false); const Extension* extension = service->GetExtensionById(id, false);
if (extension) { if (extension) {
// TODO(tmdiep): Sort out params.desktop_type when launching is moved
// to the correct location.
AppLaunchParams params(profile, extension, NEW_FOREGROUND_TAB); AppLaunchParams params(profile, extension, NEW_FOREGROUND_TAB);
params.desktop_type = chrome::HOST_DESKTOP_TYPE_NATIVE;
OpenApplication(params); OpenApplication(params);
} }
} }
...@@ -45,7 +47,22 @@ void OnInstallDone(Profile* profile, ...@@ -45,7 +47,22 @@ void OnInstallDone(Profile* profile,
base::Bind(&LaunchApp, profile, id), base::Bind(&LaunchApp, profile, id),
base::TimeDelta()); base::TimeDelta());
callback.Run(success, error); if (!callback.is_null())
callback.Run(success, error);
}
Profile* ProfileForWebContents(content::WebContents* contents) {
if (!contents)
return NULL;
return Profile::FromBrowserContext(contents->GetBrowserContext());
}
gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
if (!contents)
return NULL;
return contents->GetView()->GetTopLevelNativeWindow();
} }
} // namespace } // namespace
...@@ -62,8 +79,20 @@ WebstoreEphemeralInstaller::CreateForLauncher( ...@@ -62,8 +79,20 @@ WebstoreEphemeralInstaller::CreateForLauncher(
profile, profile,
parent_window, parent_window,
callback); callback);
installer->set_install_source( installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
extensions::WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER); return installer;
}
// static
scoped_refptr<WebstoreEphemeralInstaller>
WebstoreEphemeralInstaller::CreateForLink(
const std::string& webstore_item_id,
content::WebContents* web_contents) {
scoped_refptr<WebstoreEphemeralInstaller> installer =
new WebstoreEphemeralInstaller(webstore_item_id,
web_contents,
Callback());
installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
return installer; return installer;
} }
...@@ -80,10 +109,24 @@ WebstoreEphemeralInstaller::WebstoreEphemeralInstaller( ...@@ -80,10 +109,24 @@ WebstoreEphemeralInstaller::WebstoreEphemeralInstaller(
dummy_web_contents_( dummy_web_contents_(
WebContents::Create(WebContents::CreateParams(profile))) {} WebContents::Create(WebContents::CreateParams(profile))) {}
WebstoreEphemeralInstaller::WebstoreEphemeralInstaller(
const std::string& webstore_item_id,
content::WebContents* web_contents,
const Callback& callback)
: WebstoreStandaloneInstaller(
webstore_item_id,
ProfileForWebContents(web_contents),
base::Bind(OnInstallDone,
ProfileForWebContents(web_contents),
webstore_item_id,
callback)),
content::WebContentsObserver(web_contents),
parent_window_(NativeWindowForWebContents(web_contents)) {}
WebstoreEphemeralInstaller::~WebstoreEphemeralInstaller() {} WebstoreEphemeralInstaller::~WebstoreEphemeralInstaller() {}
bool WebstoreEphemeralInstaller::CheckRequestorAlive() const { bool WebstoreEphemeralInstaller::CheckRequestorAlive() const {
return true; return dummy_web_contents_.get() != NULL || web_contents() != NULL;
} }
const GURL& WebstoreEphemeralInstaller::GetRequestorURL() const { const GURL& WebstoreEphemeralInstaller::GetRequestorURL() const {
...@@ -99,7 +142,7 @@ bool WebstoreEphemeralInstaller::ShouldShowAppInstalledBubble() const { ...@@ -99,7 +142,7 @@ bool WebstoreEphemeralInstaller::ShouldShowAppInstalledBubble() const {
} }
WebContents* WebstoreEphemeralInstaller::GetWebContents() const { WebContents* WebstoreEphemeralInstaller::GetWebContents() const {
return dummy_web_contents_.get(); return web_contents() ? web_contents() : dummy_web_contents_.get();
} }
scoped_ptr<ExtensionInstallPrompt::Prompt> scoped_ptr<ExtensionInstallPrompt::Prompt>
...@@ -147,8 +190,16 @@ bool WebstoreEphemeralInstaller::CheckRequestorPermitted( ...@@ -147,8 +190,16 @@ bool WebstoreEphemeralInstaller::CheckRequestorPermitted(
scoped_ptr<ExtensionInstallPrompt> scoped_ptr<ExtensionInstallPrompt>
WebstoreEphemeralInstaller::CreateInstallUI() { WebstoreEphemeralInstaller::CreateInstallUI() {
if (web_contents())
return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
return make_scoped_ptr( return make_scoped_ptr(
new ExtensionInstallPrompt(profile(), parent_window_, NULL)); new ExtensionInstallPrompt(profile(), parent_window_, NULL));
} }
void WebstoreEphemeralInstaller::WebContentsDestroyed(
content::WebContents* web_contents) {
AbortInstall();
}
} // namespace extensions } // namespace extensions
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "chrome/browser/extensions/webstore_standalone_installer.h" #include "chrome/browser/extensions/webstore_standalone_installer.h"
#include "content/public/browser/web_contents_observer.h"
class Profile; class Profile;
...@@ -20,7 +21,8 @@ namespace extensions { ...@@ -20,7 +21,8 @@ namespace extensions {
// WebstoreEphemeralInstaller handles the installation of ephemeral apps. // WebstoreEphemeralInstaller handles the installation of ephemeral apps.
class WebstoreEphemeralInstaller class WebstoreEphemeralInstaller
: public extensions::WebstoreStandaloneInstaller { : public extensions::WebstoreStandaloneInstaller,
public content::WebContentsObserver {
public: public:
typedef WebstoreStandaloneInstaller::Callback Callback; typedef WebstoreStandaloneInstaller::Callback Callback;
...@@ -29,6 +31,9 @@ class WebstoreEphemeralInstaller ...@@ -29,6 +31,9 @@ class WebstoreEphemeralInstaller
Profile* profile, Profile* profile,
gfx::NativeWindow parent_window, gfx::NativeWindow parent_window,
const Callback& callback); const Callback& callback);
static scoped_refptr<WebstoreEphemeralInstaller> CreateForLink(
const std::string& webstore_item_id,
content::WebContents* web_contents);
private: private:
friend class base::RefCountedThreadSafe<WebstoreEphemeralInstaller>; friend class base::RefCountedThreadSafe<WebstoreEphemeralInstaller>;
...@@ -38,6 +43,10 @@ class WebstoreEphemeralInstaller ...@@ -38,6 +43,10 @@ class WebstoreEphemeralInstaller
gfx::NativeWindow parent_window, gfx::NativeWindow parent_window,
const Callback& callback); const Callback& callback);
WebstoreEphemeralInstaller(const std::string& webstore_item_id,
content::WebContents* web_contents,
const Callback& callback);
virtual ~WebstoreEphemeralInstaller(); virtual ~WebstoreEphemeralInstaller();
// WebstoreStandaloneInstaller implementation. // WebstoreStandaloneInstaller implementation.
...@@ -56,6 +65,10 @@ class WebstoreEphemeralInstaller ...@@ -56,6 +65,10 @@ class WebstoreEphemeralInstaller
std::string* error) const OVERRIDE; std::string* error) const OVERRIDE;
virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE; virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI() OVERRIDE;
// content::WebContentsObserver implementation.
virtual void WebContentsDestroyed(
content::WebContents* web_contents) OVERRIDE;
gfx::NativeWindow parent_window_; gfx::NativeWindow parent_window_;
scoped_ptr<content::WebContents> dummy_web_contents_; scoped_ptr<content::WebContents> dummy_web_contents_;
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
#include "components/navigation_interception/intercept_navigation_delegate.h" #include "components/navigation_interception/intercept_navigation_delegate.h"
#else #else
#include "chrome/browser/apps/app_url_redirector.h" #include "chrome/browser/apps/app_url_redirector.h"
#include "chrome/browser/apps/ephemeral_app_throttle.h"
#endif #endif
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
...@@ -311,6 +312,13 @@ void ChromeResourceDispatcherHostDelegate::RequestBeginning( ...@@ -311,6 +312,13 @@ void ChromeResourceDispatcherHostDelegate::RequestBeginning(
AppUrlRedirector::MaybeCreateThrottleFor(request, io_data); AppUrlRedirector::MaybeCreateThrottleFor(request, io_data);
if (url_to_app_throttle) if (url_to_app_throttle)
throttles->push_back(url_to_app_throttle); throttles->push_back(url_to_app_throttle);
// Experimental: Launch ephemeral apps from search results.
content::ResourceThrottle* ephemeral_app_throttle =
EphemeralAppThrottle::MaybeCreateThrottleForLaunch(
request, io_data);
if (ephemeral_app_throttle)
throttles->push_back(ephemeral_app_throttle);
#endif #endif
} }
......
...@@ -65,6 +65,8 @@ ...@@ -65,6 +65,8 @@
'browser/apps/app_url_redirector.h', 'browser/apps/app_url_redirector.h',
'browser/apps/chrome_apps_client.cc', 'browser/apps/chrome_apps_client.cc',
'browser/apps/chrome_apps_client.h', 'browser/apps/chrome_apps_client.h',
'browser/apps/ephemeral_app_throttle.cc',
'browser/apps/ephemeral_app_throttle.h',
'browser/apps/shortcut_manager.cc', 'browser/apps/shortcut_manager.cc',
'browser/apps/shortcut_manager.h', 'browser/apps/shortcut_manager.h',
'browser/apps/shortcut_manager_factory.cc', 'browser/apps/shortcut_manager_factory.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment