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 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* 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