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.
5 #include "chrome/browser/apps/app_url_redirector.h"
7 #include "apps/launcher.h"
9 #include "base/logging.h"
10 #include "chrome/browser/prerender/prerender_contents.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/profiles/profile_io_data.h"
13 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
14 #include "components/navigation_interception/intercept_navigation_resource_throttle.h"
15 #include "components/navigation_interception/navigation_params.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/resource_request_info.h"
19 #include "content/public/browser/resource_throttle.h"
20 #include "content/public/browser/web_contents.h"
21 #include "extensions/browser/info_map.h"
22 #include "extensions/common/extension.h"
23 #include "extensions/common/extension_set.h"
24 #include "net/url_request/url_request.h"
26 using content::BrowserThread;
27 using content::ResourceRequestInfo;
28 using content::WebContents;
29 using extensions::Extension;
30 using extensions::UrlHandlers;
31 using extensions::UrlHandlerInfo;
35 bool LaunchAppWithUrl(
36 const scoped_refptr<const Extension> app,
37 const std::string& handler_id,
38 content::WebContents* source,
39 const navigation_interception::NavigationParams& params) {
40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
42 // Redirect top-level navigations only. This excludes iframes and webviews
44 if (source->IsSubframe()) {
45 DVLOG(1) << "Cancel redirection: source is a subframe";
49 // If prerendering, don't launch the app but abort the navigation.
50 prerender::PrerenderContents* prerender_contents =
51 prerender::PrerenderContents::FromWebContents(source);
52 if (prerender_contents) {
53 prerender_contents->Destroy(prerender::FINAL_STATUS_NAVIGATION_INTERCEPTED);
57 // These are guaranteed by CreateThrottleFor below.
58 DCHECK(!params.is_post());
59 DCHECK(UrlHandlers::CanExtensionHandleUrl(app.get(), params.url()));
62 Profile::FromBrowserContext(source->GetBrowserContext());
64 DVLOG(1) << "Launching app handler with URL: "
65 << params.url().spec() << " -> "
66 << app->name() << "(" << app->id() << "):" << handler_id;
67 apps::LaunchPlatformAppWithUrl(
68 profile, app.get(), handler_id, params.url(), params.referrer().url);
76 content::ResourceThrottle*
77 AppUrlRedirector::MaybeCreateThrottleFor(net::URLRequest* request,
78 ProfileIOData* profile_io_data) {
79 DVLOG(1) << "Considering URL for redirection: "
80 << request->method() << " " << request->url().spec();
82 // Support only GET for now.
83 if (request->method() != "GET") {
84 DVLOG(1) << "Skip redirection: method is not GET";
88 if (!request->url().SchemeIsHTTPOrHTTPS()) {
89 DVLOG(1) << "Skip redirection: scheme is not HTTP or HTTPS";
93 // The user has indicated that a URL should be force downloaded. Turn off
94 // URL redirection in this case.
95 if (ResourceRequestInfo::ForRequest(request)->IsDownload()) {
96 DVLOG(1) << "Skip redirection: request is a forced download";
100 // Never redirect URLs to apps in incognito. Technically, apps are not
101 // supported in incognito, but that may change in future.
102 // See crbug.com/240879, which tracks incognito support for v2 apps.
103 if (profile_io_data->IsOffTheRecord()) {
104 DVLOG(1) << "Skip redirection: unsupported in incognito";
108 const extensions::ExtensionSet& extensions =
109 profile_io_data->GetExtensionInfoMap()->extensions();
110 for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
111 iter != extensions.end();
113 const UrlHandlerInfo* handler =
114 UrlHandlers::FindMatchingUrlHandler(iter->get(), request->url());
116 DVLOG(1) << "Found matching app handler for redirection: "
117 << (*iter)->name() << "(" << (*iter)->id() << "):"
119 return new navigation_interception::InterceptNavigationResourceThrottle(
121 base::Bind(&LaunchAppWithUrl,
122 scoped_refptr<const Extension>(*iter),
127 DVLOG(1) << "Skipping redirection: no matching app handler found";