1 // Copyright 2019 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/renderer/loader/resource_load_stats.h"
6 
7 #include "base/bind.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "content/common/net/record_load_histograms.h"
10 #include "content/renderer/render_frame_impl.h"
11 #include "content/renderer/render_thread_impl.h"
12 #include "net/base/ip_endpoint.h"
13 #include "net/url_request/redirect_info.h"
14 #include "services/network/public/cpp/url_loader_completion_status.h"
15 #include "services/network/public/mojom/fetch_api.mojom.h"
16 #include "services/network/public/mojom/url_response_head.mojom.h"
17 #include "third_party/blink/public/common/loader/resource_type_util.h"
18 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
19 
20 namespace content {
21 
22 namespace {
23 
24 // Returns true if the headers indicate that this resource should always be
25 // revalidated or not cached.
AlwaysAccessNetwork(const scoped_refptr<net::HttpResponseHeaders> & headers)26 bool AlwaysAccessNetwork(
27     const scoped_refptr<net::HttpResponseHeaders>& headers) {
28   if (!headers)
29     return false;
30 
31   // RFC 2616, section 14.9.
32   return headers->HasHeaderValue("cache-control", "no-cache") ||
33          headers->HasHeaderValue("cache-control", "no-store") ||
34          headers->HasHeaderValue("pragma", "no-cache") ||
35          headers->HasHeaderValue("vary", "*");
36 }
37 
38 #if defined(OS_ANDROID)
UpdateUserGestureCarryoverInfo(int render_frame_id)39 void UpdateUserGestureCarryoverInfo(int render_frame_id) {
40   RenderFrameImpl* frame = RenderFrameImpl::FromRoutingID(render_frame_id);
41   if (frame)
42     frame->GetFrameHost()->UpdateUserGestureCarryoverInfo();
43 }
44 #endif
45 
ResourceResponseReceived(int render_frame_id,int request_id,const GURL & response_url,network::mojom::URLResponseHeadPtr response_head,network::mojom::RequestDestination destination,PreviewsState previews_state)46 void ResourceResponseReceived(int render_frame_id,
47                               int request_id,
48                               const GURL& response_url,
49                               network::mojom::URLResponseHeadPtr response_head,
50                               network::mojom::RequestDestination destination,
51                               PreviewsState previews_state) {
52   RenderFrameImpl* frame = RenderFrameImpl::FromRoutingID(render_frame_id);
53   if (!frame)
54     return;
55   if (!blink::IsRequestDestinationFrame(destination)) {
56     frame->GetFrameHost()->SubresourceResponseStarted(
57         response_url, response_head->cert_status);
58   }
59   frame->DidStartResponse(response_url, request_id, std::move(response_head),
60                           destination, previews_state);
61 }
62 
ResourceTransferSizeUpdated(int render_frame_id,int request_id,int transfer_size_diff)63 void ResourceTransferSizeUpdated(int render_frame_id,
64                                  int request_id,
65                                  int transfer_size_diff) {
66   RenderFrameImpl* frame = RenderFrameImpl::FromRoutingID(render_frame_id);
67   if (frame)
68     frame->DidReceiveTransferSizeUpdate(request_id, transfer_size_diff);
69 }
70 
ResourceLoadCompleted(int render_frame_id,blink::mojom::ResourceLoadInfoPtr resource_load_info,const network::URLLoaderCompletionStatus & status)71 void ResourceLoadCompleted(int render_frame_id,
72                            blink::mojom::ResourceLoadInfoPtr resource_load_info,
73                            const network::URLLoaderCompletionStatus& status) {
74   RenderFrameImpl* frame = RenderFrameImpl::FromRoutingID(render_frame_id);
75   if (!frame)
76     return;
77   frame->DidCompleteResponse(resource_load_info->request_id, status);
78   frame->GetFrameHost()->ResourceLoadComplete(std::move(resource_load_info));
79 }
80 
ResourceLoadCanceled(int render_frame_id,int request_id)81 void ResourceLoadCanceled(int render_frame_id, int request_id) {
82   RenderFrameImpl* frame = RenderFrameImpl::FromRoutingID(render_frame_id);
83   if (frame)
84     frame->DidCancelResponse(request_id);
85 }
86 
87 }  // namespace
88 
89 #if defined(OS_ANDROID)
NotifyUpdateUserGestureCarryoverInfo(int render_frame_id)90 void NotifyUpdateUserGestureCarryoverInfo(int render_frame_id) {
91   auto task_runner = RenderThreadImpl::DeprecatedGetMainTaskRunner();
92   if (!task_runner)
93     return;
94   if (task_runner->BelongsToCurrentThread()) {
95     UpdateUserGestureCarryoverInfo(render_frame_id);
96     return;
97   }
98   task_runner->PostTask(
99       FROM_HERE,
100       base::BindOnce(UpdateUserGestureCarryoverInfo, render_frame_id));
101 }
102 #endif
103 
NotifyResourceLoadInitiated(int render_frame_id,int request_id,const GURL & request_url,const std::string & http_method,const GURL & referrer,network::mojom::RequestDestination request_destination,net::RequestPriority request_priority)104 blink::mojom::ResourceLoadInfoPtr NotifyResourceLoadInitiated(
105     int render_frame_id,
106     int request_id,
107     const GURL& request_url,
108     const std::string& http_method,
109     const GURL& referrer,
110     network::mojom::RequestDestination request_destination,
111     net::RequestPriority request_priority) {
112   auto resource_load_info = blink::mojom::ResourceLoadInfo::New();
113   resource_load_info->method = http_method;
114   resource_load_info->original_url = request_url;
115   resource_load_info->final_url = request_url;
116   resource_load_info->request_destination = request_destination;
117   resource_load_info->request_id = request_id;
118   resource_load_info->referrer = referrer;
119   resource_load_info->network_info = blink::mojom::CommonNetworkInfo::New();
120   resource_load_info->request_priority = request_priority;
121   return resource_load_info;
122 }
123 
NotifyResourceRedirectReceived(int render_frame_id,blink::mojom::ResourceLoadInfo * resource_load_info,const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr redirect_response)124 void NotifyResourceRedirectReceived(
125     int render_frame_id,
126     blink::mojom::ResourceLoadInfo* resource_load_info,
127     const net::RedirectInfo& redirect_info,
128     network::mojom::URLResponseHeadPtr redirect_response) {
129   resource_load_info->final_url = redirect_info.new_url;
130   resource_load_info->method = redirect_info.new_method;
131   resource_load_info->referrer = GURL(redirect_info.new_referrer);
132   blink::mojom::RedirectInfoPtr net_redirect_info =
133       blink::mojom::RedirectInfo::New();
134   net_redirect_info->origin_of_new_url =
135       url::Origin::Create(redirect_info.new_url);
136   net_redirect_info->network_info = blink::mojom::CommonNetworkInfo::New();
137   net_redirect_info->network_info->network_accessed =
138       redirect_response->network_accessed;
139   net_redirect_info->network_info->always_access_network =
140       AlwaysAccessNetwork(redirect_response->headers);
141   net_redirect_info->network_info->remote_endpoint =
142       redirect_response->remote_endpoint;
143   resource_load_info->redirect_info_chain.push_back(
144       std::move(net_redirect_info));
145 }
146 
NotifyResourceResponseReceived(int render_frame_id,blink::mojom::ResourceLoadInfo * resource_load_info,network::mojom::URLResponseHeadPtr response_head,PreviewsState previews_state)147 void NotifyResourceResponseReceived(
148     int render_frame_id,
149     blink::mojom::ResourceLoadInfo* resource_load_info,
150     network::mojom::URLResponseHeadPtr response_head,
151     PreviewsState previews_state) {
152   if (response_head->network_accessed) {
153     if (resource_load_info->request_destination ==
154         network::mojom::RequestDestination::kDocument) {
155       UMA_HISTOGRAM_ENUMERATION("Net.ConnectionInfo.MainFrame",
156                                 response_head->connection_info,
157                                 net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS);
158     } else {
159       UMA_HISTOGRAM_ENUMERATION("Net.ConnectionInfo.SubResource",
160                                 response_head->connection_info,
161                                 net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS);
162     }
163   }
164 
165   resource_load_info->mime_type = response_head->mime_type;
166   resource_load_info->load_timing_info = response_head->load_timing;
167   resource_load_info->network_info->network_accessed =
168       response_head->network_accessed;
169   resource_load_info->network_info->always_access_network =
170       AlwaysAccessNetwork(response_head->headers);
171   resource_load_info->network_info->remote_endpoint =
172       response_head->remote_endpoint;
173 
174   auto task_runner = RenderThreadImpl::DeprecatedGetMainTaskRunner();
175   if (!task_runner)
176     return;
177   if (task_runner->BelongsToCurrentThread()) {
178     ResourceResponseReceived(
179         render_frame_id, resource_load_info->request_id,
180         resource_load_info->final_url, std::move(response_head),
181         resource_load_info->request_destination, previews_state);
182     return;
183   }
184 
185   // Make a deep copy of URLResponseHead before passing it cross-thread.
186   if (response_head->headers) {
187     response_head->headers =
188         new net::HttpResponseHeaders(response_head->headers->raw_headers());
189   }
190   task_runner->PostTask(
191       FROM_HERE,
192       base::BindOnce(ResourceResponseReceived, render_frame_id,
193                      resource_load_info->request_id,
194                      resource_load_info->final_url, std::move(response_head),
195                      resource_load_info->request_destination, previews_state));
196 }
197 
NotifyResourceTransferSizeUpdated(int render_frame_id,blink::mojom::ResourceLoadInfo * resource_load_info,int transfer_size_diff)198 void NotifyResourceTransferSizeUpdated(
199     int render_frame_id,
200     blink::mojom::ResourceLoadInfo* resource_load_info,
201     int transfer_size_diff) {
202   auto task_runner = RenderThreadImpl::DeprecatedGetMainTaskRunner();
203   if (!task_runner)
204     return;
205   if (task_runner->BelongsToCurrentThread()) {
206     ResourceTransferSizeUpdated(render_frame_id, resource_load_info->request_id,
207                                 transfer_size_diff);
208     return;
209   }
210   task_runner->PostTask(
211       FROM_HERE,
212       base::BindOnce(ResourceTransferSizeUpdated, render_frame_id,
213                      resource_load_info->request_id, transfer_size_diff));
214 }
215 
NotifyResourceLoadCompleted(int render_frame_id,blink::mojom::ResourceLoadInfoPtr resource_load_info,const network::URLLoaderCompletionStatus & status)216 void NotifyResourceLoadCompleted(
217     int render_frame_id,
218     blink::mojom::ResourceLoadInfoPtr resource_load_info,
219     const network::URLLoaderCompletionStatus& status) {
220   RecordLoadHistograms(url::Origin::Create(resource_load_info->final_url),
221                        resource_load_info->request_destination,
222                        status.error_code);
223 
224   resource_load_info->was_cached = status.exists_in_cache;
225   resource_load_info->net_error = status.error_code;
226   resource_load_info->total_received_bytes = status.encoded_data_length;
227   resource_load_info->raw_body_bytes = status.encoded_body_length;
228 
229   auto task_runner = RenderThreadImpl::DeprecatedGetMainTaskRunner();
230   if (!task_runner)
231     return;
232   if (task_runner->BelongsToCurrentThread()) {
233     ResourceLoadCompleted(render_frame_id, std::move(resource_load_info),
234                           status);
235     return;
236   }
237   task_runner->PostTask(FROM_HERE,
238                         base::BindOnce(ResourceLoadCompleted, render_frame_id,
239                                        std::move(resource_load_info), status));
240 }
241 
NotifyResourceLoadCanceled(int render_frame_id,blink::mojom::ResourceLoadInfoPtr resource_load_info,int net_error)242 void NotifyResourceLoadCanceled(
243     int render_frame_id,
244     blink::mojom::ResourceLoadInfoPtr resource_load_info,
245     int net_error) {
246   RecordLoadHistograms(url::Origin::Create(resource_load_info->final_url),
247                        resource_load_info->request_destination, net_error);
248 
249   auto task_runner = RenderThreadImpl::DeprecatedGetMainTaskRunner();
250   if (!task_runner)
251     return;
252   if (task_runner->BelongsToCurrentThread()) {
253     ResourceLoadCanceled(render_frame_id, resource_load_info->request_id);
254     return;
255   }
256   task_runner->PostTask(FROM_HERE,
257                         base::BindOnce(ResourceLoadCanceled, render_frame_id,
258                                        resource_load_info->request_id));
259 }
260 
261 }  // namespace content
262