Home | History | Annotate | Download | only in browser
      1 // Copyright 2014 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 "content/browser/transition_request_manager.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/memory/singleton.h"
      9 #include "base/metrics/field_trial.h"
     10 #include "base/strings/string_split.h"
     11 #include "base/strings/string_util.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "content/public/common/content_switches.h"
     14 #include "net/http/http_response_headers.h"
     15 #include "net/http/http_util.h"
     16 
     17 namespace {
     18 
     19 // Enumerate all Link: headers with the specified relation in this
     20 // response, and optionally returns the URL and any additional attributes of
     21 // each one. See EnumerateHeaders for |iter| usage.
     22 bool EnumerateLinkHeaders(
     23     const scoped_refptr<net::HttpResponseHeaders>& headers,
     24     void** iter,
     25     const std::string& rel,
     26     std::string* url,
     27     base::StringPairs* attributes) {
     28   std::string header_body;
     29   bool rel_matched = false;
     30   while (!rel_matched && headers->EnumerateHeader(iter, "link", &header_body)) {
     31     const std::string::const_iterator begin = header_body.begin();
     32     size_t url_start = header_body.find_first_of('<');
     33     size_t url_end = header_body.find_first_of('>');
     34     if (url_start == std::string::npos || url_end == std::string::npos ||
     35         url_start > url_end) {
     36       break;
     37     }
     38 
     39     if (attributes)
     40       attributes->clear();
     41 
     42     net::HttpUtil::NameValuePairsIterator param_iter(
     43         begin + url_end + 1, header_body.end(), ';');
     44 
     45     while (param_iter.GetNext()) {
     46       if (LowerCaseEqualsASCII(
     47               param_iter.name_begin(), param_iter.name_end(), "rel")) {
     48         if (LowerCaseEqualsASCII(param_iter.value_begin(),
     49                                  param_iter.value_end(),
     50                                  rel.c_str())) {
     51           if (url) {
     52             url->assign(begin + url_start + 1, begin + url_end);
     53           }
     54           rel_matched = true;
     55         } else {
     56           break;
     57         }
     58       } else if (attributes) {
     59         std::string attribute_name(param_iter.name_begin(),
     60                                    param_iter.name_end());
     61         std::string attribute_value(param_iter.value_begin(),
     62                                     param_iter.value_end());
     63         attributes->push_back(std::make_pair(attribute_name, attribute_value));
     64       }
     65     }
     66   }
     67 
     68   if (!rel_matched && attributes) {
     69     attributes->clear();
     70   }
     71 
     72   return rel_matched;
     73 }
     74 
     75 }  // namespace
     76 
     77 namespace content {
     78 
     79 TransitionLayerData::TransitionLayerData() {
     80 }
     81 
     82 TransitionLayerData::~TransitionLayerData() {
     83 }
     84 
     85 void TransitionRequestManager::ParseTransitionStylesheetsFromHeaders(
     86     const scoped_refptr<net::HttpResponseHeaders>& headers,
     87     std::vector<GURL>& entering_stylesheets,
     88     const GURL& resolve_address) {
     89   if (headers.get() == NULL)
     90     return;
     91 
     92   std::string transition_stylesheet;
     93   base::StringPairs attributes;
     94   void* header_iter = NULL;
     95   while (EnumerateLinkHeaders(headers,
     96                               &header_iter,
     97                               "transition-entering-stylesheet",
     98                               &transition_stylesheet,
     99                               &attributes)) {
    100     GURL stylesheet_url = resolve_address.Resolve(transition_stylesheet);
    101     if (stylesheet_url.is_valid())
    102       entering_stylesheets.push_back(stylesheet_url);
    103   }
    104 }
    105 
    106 TransitionRequestManager::TransitionRequestData::TransitionRequestData() {
    107 }
    108 
    109 TransitionRequestManager::TransitionRequestData::~TransitionRequestData() {
    110 }
    111 
    112 void TransitionRequestManager::TransitionRequestData::AddEntry(
    113     const std::string& allowed_destination_host_pattern,
    114     const std::string& css_selector,
    115     const std::string& markup) {
    116   allowed_entries_.push_back(AllowedEntry(allowed_destination_host_pattern,
    117                                           css_selector,
    118                                           markup));
    119 }
    120 
    121 bool TransitionRequestManager::TransitionRequestData::FindEntry(
    122     const GURL& request_url,
    123     TransitionLayerData* transition_data) {
    124   DCHECK(!allowed_entries_.empty());
    125   CHECK(transition_data);
    126   // TODO(oysteine): Add CSP check to validate the host pattern and the
    127   // request_url. Must be done before this feature is moved out from the flag.
    128   CHECK(CommandLine::ForCurrentProcess()->HasSwitch(
    129             switches::kEnableExperimentalWebPlatformFeatures) ||
    130             base::FieldTrialList::FindFullName("NavigationTransitions") ==
    131                 "Enabled");
    132 
    133   const AllowedEntry& allowed_entry = allowed_entries_[0];
    134   transition_data->markup = allowed_entry.markup;
    135   transition_data->css_selector = allowed_entry.css_selector;
    136   return true;
    137 }
    138 
    139 bool TransitionRequestManager::HasPendingTransitionRequest(
    140     int render_process_id,
    141     int render_frame_id,
    142     const GURL& request_url,
    143     TransitionLayerData* transition_data) {
    144   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    145   DCHECK(transition_data);
    146   std::pair<int, int> key(render_process_id, render_frame_id);
    147   RenderFrameRequestDataMap::iterator iter =
    148       pending_transition_frames_.find(key);
    149   return iter != pending_transition_frames_.end() &&
    150       iter->second.FindEntry(request_url, transition_data);
    151 }
    152 
    153 void TransitionRequestManager::AddPendingTransitionRequestData(
    154     int render_process_id,
    155     int render_frame_id,
    156     const std::string& allowed_destination_host_pattern,
    157     const std::string& css_selector,
    158     const std::string& markup) {
    159   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    160 
    161   std::pair<int, int> key(render_process_id, render_frame_id);
    162   pending_transition_frames_[key].AddEntry(allowed_destination_host_pattern,
    163                                            css_selector,
    164                                            markup);
    165 }
    166 
    167 void TransitionRequestManager::ClearPendingTransitionRequestData(
    168     int render_process_id, int render_frame_id) {
    169   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    170   std::pair<int, int> key(render_process_id, render_frame_id);
    171   pending_transition_frames_.erase(key);
    172 }
    173 
    174 TransitionRequestManager::TransitionRequestManager() {
    175 }
    176 
    177 TransitionRequestManager::~TransitionRequestManager() {
    178 }
    179 
    180 // static
    181 TransitionRequestManager* TransitionRequestManager::GetInstance() {
    182   return Singleton<TransitionRequestManager>::get();
    183 }
    184 
    185 }  // namespace content
    186