Home | History | Annotate | Download | only in apps
      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