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
17namespace {
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.
22bool 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
77namespace content {
78
79TransitionLayerData::TransitionLayerData() {
80}
81
82TransitionLayerData::~TransitionLayerData() {
83}
84
85void 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
106TransitionRequestManager::TransitionRequestData::TransitionRequestData() {
107}
108
109TransitionRequestManager::TransitionRequestData::~TransitionRequestData() {
110}
111
112void 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
121bool 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
139bool 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
153void 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
167void 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
174TransitionRequestManager::TransitionRequestManager() {
175}
176
177TransitionRequestManager::~TransitionRequestManager() {
178}
179
180// static
181TransitionRequestManager* TransitionRequestManager::GetInstance() {
182  return Singleton<TransitionRequestManager>::get();
183}
184
185}  // namespace content
186