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/app_url_redirector.h"
6
7 #include "apps/launcher.h"
8 #include "base/bind.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_messages.h"
24 #include "extensions/common/extension_set.h"
25 #include "net/url_request/url_request.h"
26
27 using content::BrowserThread;
28 using content::ResourceRequestInfo;
29 using content::WebContents;
30 using extensions::Extension;
31 using extensions::UrlHandlers;
32 using extensions::UrlHandlerInfo;
33
34 namespace {
35
LaunchAppWithUrl(const scoped_refptr<const Extension> app,const std::string & handler_id,content::WebContents * source,const navigation_interception::NavigationParams & params)36 bool LaunchAppWithUrl(
37 const scoped_refptr<const Extension> app,
38 const std::string& handler_id,
39 content::WebContents* source,
40 const navigation_interception::NavigationParams& params) {
41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
42
43 // Redirect top-level navigations only. This excludes iframes and webviews
44 // in particular.
45 if (source->IsSubframe()) {
46 DVLOG(1) << "Cancel redirection: source is a subframe";
47 return false;
48 }
49
50 // If prerendering, don't launch the app but abort the navigation.
51 prerender::PrerenderContents* prerender_contents =
52 prerender::PrerenderContents::FromWebContents(source);
53 if (prerender_contents) {
54 prerender_contents->Destroy(prerender::FINAL_STATUS_NAVIGATION_INTERCEPTED);
55 return true;
56 }
57
58 // These are guaranteed by CreateThrottleFor below.
59 DCHECK(!params.is_post());
60 DCHECK(UrlHandlers::CanExtensionHandleUrl(app, params.url()));
61
62 Profile* profile =
63 Profile::FromBrowserContext(source->GetBrowserContext());
64
65 DVLOG(1) << "Launching app handler with URL: "
66 << params.url().spec() << " -> "
67 << app->name() << "(" << app->id() << "):" << handler_id;
68 apps::LaunchPlatformAppWithUrl(
69 profile, app, handler_id, params.url(), params.referrer().url);
70
71 return true;
72 }
73
74 } // namespace
75
76 // static
77 content::ResourceThrottle*
MaybeCreateThrottleFor(net::URLRequest * request,ProfileIOData * profile_io_data)78 AppUrlRedirector::MaybeCreateThrottleFor(net::URLRequest* request,
79 ProfileIOData* profile_io_data) {
80 DVLOG(1) << "Considering URL for redirection: "
81 << request->method() << " " << request->url().spec();
82
83 // Support only GET for now.
84 if (request->method() != "GET") {
85 DVLOG(1) << "Skip redirection: method is not GET";
86 return NULL;
87 }
88
89 if (!request->url().SchemeIsHTTPOrHTTPS()) {
90 DVLOG(1) << "Skip redirection: scheme is not HTTP or HTTPS";
91 return NULL;
92 }
93
94 // The user has indicated that a URL should be force downloaded. Turn off
95 // URL redirection in this case.
96 if (ResourceRequestInfo::ForRequest(request)->IsDownload()) {
97 DVLOG(1) << "Skip redirection: request is a forced download";
98 return NULL;
99 }
100
101 // Never redirect URLs to apps in incognito. Technically, apps are not
102 // supported in incognito, but that may change in future.
103 // See crbug.com/240879, which tracks incognito support for v2 apps.
104 if (profile_io_data->IsOffTheRecord()) {
105 DVLOG(1) << "Skip redirection: unsupported in incognito";
106 return NULL;
107 }
108
109 const extensions::ExtensionSet& extensions =
110 profile_io_data->GetExtensionInfoMap()->extensions();
111 for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
112 iter != extensions.end();
113 ++iter) {
114 const UrlHandlerInfo* handler =
115 UrlHandlers::FindMatchingUrlHandler(*iter, request->url());
116 if (handler) {
117 DVLOG(1) << "Found matching app handler for redirection: "
118 << (*iter)->name() << "(" << (*iter)->id() << "):"
119 << handler->id;
120 return new navigation_interception::InterceptNavigationResourceThrottle(
121 request,
122 base::Bind(&LaunchAppWithUrl,
123 scoped_refptr<const Extension>(*iter),
124 handler->id));
125 }
126 }
127
128 DVLOG(1) << "Skipping redirection: no matching app handler found";
129 return NULL;
130 }
131