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