File: resource_request_utils.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (323 lines) | stat: -rw-r--r-- 13,825 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/platform/loader/fetch/resource_request_utils.h"

#include "base/feature_list.h"
#include "base/trace_event/common/trace_event_common.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/navigation/preloading_headers.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/weborigin/referrer.h"
#include "third_party/blink/renderer/platform/weborigin/security_policy.h"

namespace blink {
namespace {

ReportingDisposition CalculateReportingDisposition(
    const FetchParameters& params) {
  // No CSP reports are sent for:
  //
  // Speculative preload
  // ===================
  // This avoids sending 2 reports for a single resource (preload + real load).
  // Moreover the speculative preload are 'speculative', it might not even be
  // possible to issue a real request.
  //
  // Stale revalidations
  // ===================
  // Web browser should not send violation reports for stale revalidations. The
  // initial request was allowed. In theory, the revalidation request should be
  // allowed as well. However, some <meta> CSP header might have been added in
  // the meantime. See https://crbug.com/1070117.
  //
  // Note: Ideally, stale revalidations should bypass every checks. In practise,
  // they are run and block the request. Bypassing all security checks could be
  // risky and probably doesn't really worth it. They are very rarely blocked.
  return params.IsSpeculativePreload() || params.IsStaleRevalidation()
             ? ReportingDisposition::kSuppressReporting
             : ReportingDisposition::kReport;
}

}  // namespace

// This function corresponds with step 2 substep 7 of
// https://fetch.spec.whatwg.org/#main-fetch.
void SetReferrer(
    ResourceRequest& request,
    const FetchClientSettingsObject& fetch_client_settings_object) {
  String referrer_to_use = request.ReferrerString();
  network::mojom::ReferrerPolicy referrer_policy_to_use =
      request.GetReferrerPolicy();

  if (referrer_to_use == Referrer::ClientReferrerString()) {
    referrer_to_use = fetch_client_settings_object.GetOutgoingReferrer();
  }

  if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault) {
    referrer_policy_to_use = fetch_client_settings_object.GetReferrerPolicy();
  }

  Referrer generated_referrer = SecurityPolicy::GenerateReferrer(
      referrer_policy_to_use, request.Url(), referrer_to_use);

  request.SetReferrerString(generated_referrer.referrer);
  request.SetReferrerPolicy(generated_referrer.referrer_policy);
}

ResourceLoadPriority AdjustPriorityWithPriorityHintAndRenderBlocking(
    ResourceLoadPriority priority,
    ResourceType type,
    mojom::blink::FetchPriorityHint fetch_priority_hint,
    RenderBlockingBehavior render_blocking_behavior) {
  ResourceLoadPriority new_priority = priority;

  switch (fetch_priority_hint) {
    case mojom::blink::FetchPriorityHint::kAuto:
      break;
    case mojom::blink::FetchPriorityHint::kHigh:
      // Boost priority of any request type that supports priority hints.
      if (new_priority < ResourceLoadPriority::kHigh) {
        new_priority = ResourceLoadPriority::kHigh;
      }
      CHECK_LE(priority, new_priority);
      break;
    case mojom::blink::FetchPriorityHint::kLow:
      // Demote priority of any request type that supports priority hints.
      // Most content types go to kLow. The one exception is early
      // render-blocking CSS which defaults to the highest priority but
      // can be lowered to match the "high" priority of everything else
      // to allow for ordering if necessary without causing too much of a
      // foot-gun.
      if (type == ResourceType::kCSSStyleSheet &&
          new_priority == ResourceLoadPriority::kVeryHigh) {
        new_priority = ResourceLoadPriority::kHigh;
      } else if (new_priority > ResourceLoadPriority::kLow) {
        new_priority = ResourceLoadPriority::kLow;
      }

      CHECK_LE(new_priority, priority);
      break;
  }

  // Render-blocking is a signal that the resource is important, so we bump it
  // to at least kHigh.
  if (render_blocking_behavior == RenderBlockingBehavior::kBlocking &&
      new_priority < ResourceLoadPriority::kHigh) {
    new_priority = ResourceLoadPriority::kHigh;
  }

  return new_priority;
}

// This method simply takes in information about a ResourceRequest, and returns
// if the resource should be loaded in parallel (incremental) or sequentially
// for protocols that support multiplexing and HTTP extensible priorities
// (RFC 9218).
// Most content types can be operated on with partial data (document parsing,
// images, media, etc) but a few need to be complete before they can be
// processed.
bool ShouldLoadIncremental(ResourceType type) {
  switch (type) {
    case ResourceType::kCSSStyleSheet:
    case ResourceType::kScript:
    case ResourceType::kFont:
    case ResourceType::kXSLStyleSheet:
    case ResourceType::kManifest:
      return false;
    case ResourceType::kImage:
    case ResourceType::kRaw:
    case ResourceType::kSVGDocument:
    case ResourceType::kLinkPrefetch:
    case ResourceType::kTextTrack:
    case ResourceType::kAudio:
    case ResourceType::kVideo:
    case ResourceType::kSpeculationRules:
    case ResourceType::kMock:
    case ResourceType::kDictionary:
      return true;
  }
  NOTREACHED();
}

void UpgradeResourceRequestForLoader(
    ResourceType resource_type,
    FetchParameters& params,
    FetchContext& context,
    ResourceRequestContext& resource_request_context,
    WebScopedVirtualTimePauser& virtual_time_pauser) {
  ResourceRequest& resource_request = params.MutableResourceRequest();
  const ResourceLoaderOptions& options = params.Options();

  // Remember the TopFrameOrigin here to ensure the value is not changed while
  // upgrading the request for the network access. This intends to ensure the
  // same value is used for both the cache access and the network access.
  const SecurityOrigin* top_frame_origin = resource_request.TopFrameOrigin();

  resource_request.SetCanChangeUrl(false);

  // Note that resource_request.GetRedirectInfo() may be non-null here since
  // e.g. ThreadableLoader may create a new Resource from a ResourceRequest that
  // originates from the ResourceRequest passed to the redirect handling
  // callback.
  context.UpgradeResourceRequestForLoader(
      resource_type, params.GetResourceWidth(), resource_request, options);

  DCHECK(params.Url().IsValid());
  resource_request.SetPriorityIncremental(ShouldLoadIncremental(resource_type));
  resource_request.SetRenderBlockingBehavior(
      params.GetRenderBlockingBehavior());

  if (resource_type == ResourceType::kLinkPrefetch) {
    // Add the "Purpose: prefetch" header to requests for prefetch.
    resource_request.SetPurposeHeader(kSecPurposePrefetchHeaderValue);
    if (base::FeatureList::IsEnabled(
            blink::features::kSecPurposePrefetchHeaderRelPrefetch)) {
      // Add the "Sec-Purpose: prefetch" header to requests for prefetch.
      resource_request.SetHttpHeaderField(http_names::kSecPurpose,
                                          AtomicString("prefetch"));
    }
  } else if (context.IsPrerendering()) {
    // Add the "Sec-Purpose: prefetch;prerender" header to requests issued from
    // prerendered pages. Add "Purpose: prefetch" as well for compatibility
    // concerns (See https://github.com/WICG/nav-speculation/issues/133).
    resource_request.SetHttpHeaderField(
        http_names::kSecPurpose,
        AtomicString(kSecPurposePrefetchPrerenderHeaderValue));
    resource_request.SetPurposeHeader(kSecPurposePrefetchHeaderValue);
  }

  context.AddAdditionalRequestHeaders(resource_request);

  resource_request_context.RecordTrace();

  if (context.CalculateIfAdSubresource(resource_request,
                                       std::nullopt /* alias_url */,
                                       resource_type, options.initiator_info)) {
    resource_request.SetIsAdResource();
  }

  // For initial requests, call PrepareRequest() here before revalidation
  // policy is determined.
  context.PrepareRequest(resource_request, params.MutableOptions(),
                         virtual_time_pauser, resource_type);
  DCHECK(params.Url().IsValid());

  resource_request.SetCanChangeUrl(true);

  // Ensure that the value wasn't changed during the upgrade in this method.
  CHECK_EQ(top_frame_origin, resource_request.TopFrameOrigin());
}

std::optional<ResourceRequestBlockedReason>
PrepareResourceRequestForCacheAccess(
    ResourceType resource_type,
    const FetchClientSettingsObject& fetch_client_settings_object,
    const KURL& bundle_url_for_uuid_resources,
    ResourceRequestContext& resource_request_context,
    FetchContext& context,
    FetchParameters& params) {
  ResourceRequest& resource_request = params.MutableResourceRequest();
  const ResourceLoaderOptions& options = params.Options();
  const ReportingDisposition reporting_disposition =
      CalculateReportingDisposition(params);

  // Note that resource_request.GetRedirectInfo() may be non-null here since
  // e.g. ThreadableLoader may create a new Resource from a ResourceRequest
  // that originates from the ResourceRequest passed to the redirect handling
  // callback.

  // Before modifying the request for CSP, evaluate report-only headers. This
  // allows site owners to learn about requests that are being modified
  // (e.g. mixed content that is being upgraded by upgrade-insecure-requests).
  const std::optional<ResourceRequest::RedirectInfo>& redirect_info =
      resource_request.GetRedirectInfo();
  const KURL& url_before_redirects =
      redirect_info ? redirect_info->original_url : params.Url();
  const ResourceRequestHead::RedirectStatus redirect_status =
      redirect_info ? ResourceRequestHead::RedirectStatus::kFollowedRedirect
                    : ResourceRequestHead::RedirectStatus::kNoRedirect;
  context.CheckCSPForRequest(
      resource_request.GetRequestContext(),
      resource_request.GetRequestDestination(), resource_request.GetMode(),
      MemoryCache::RemoveFragmentIdentifierIfNeeded(
          bundle_url_for_uuid_resources.IsValid()
              ? bundle_url_for_uuid_resources
              : params.Url()),
      options, reporting_disposition,
      MemoryCache::RemoveFragmentIdentifierIfNeeded(url_before_redirects),
      redirect_status);
  // There's no need to add an integrity policy check here, as CanRequest() will
  // do that below.

  context.PopulateResourceRequestBeforeCacheAccess(options, resource_request);
  if (!resource_request.Url().IsValid()) {
    return ResourceRequestBlockedReason::kOther;
  }

  ResourceLoadPriority computed_load_priority = resource_request.Priority();
  // We should only compute the priority for ResourceRequests whose priority has
  // not already been set.
  if (!resource_request.PriorityHasBeenSet()) {
    computed_load_priority =
        resource_request_context.ComputeLoadPriority(params);
  }
  CHECK_NE(computed_load_priority, ResourceLoadPriority::kUnresolved);
  resource_request.SetPriority(computed_load_priority);

  if (resource_request.GetCacheMode() ==
      mojom::blink::FetchCacheMode::kDefault) {
    resource_request.SetCacheMode(context.ResourceRequestCachePolicy(
        resource_request, resource_type, params.Defer()));
  }

  if (resource_request.GetRequestContext() ==
      mojom::blink::RequestContextType::UNSPECIFIED) {
    resource_request.SetRequestContext(ResourceFetcher::DetermineRequestContext(
        resource_type, ResourceFetcher::kImageNotImageSet));
    resource_request.SetRequestDestination(
        ResourceFetcher::DetermineRequestDestination(resource_type));
  }

  // Indicate whether the network stack can return a stale resource. If a
  // stale resource is returned a StaleRevalidation request will be scheduled.
  // Explicitly disallow stale responses for fetchers that don't have SWR
  // enabled (via origin trial), and non-GET requests.
  resource_request.SetAllowStaleResponse(resource_request.HttpMethod() ==
                                             http_names::kGET &&
                                         !params.IsStaleRevalidation());

  SetReferrer(resource_request, fetch_client_settings_object);

  std::optional<ResourceRequestBlockedReason> blocked_reason =
      context.CanRequest(resource_type, resource_request,
                         MemoryCache::RemoveFragmentIdentifierIfNeeded(
                             bundle_url_for_uuid_resources.IsValid()
                                 ? bundle_url_for_uuid_resources
                                 : params.Url()),
                         options, reporting_disposition,
                         resource_request.GetRedirectInfo());
  if (context.CalculateIfAdSubresource(resource_request,
                                       std::nullopt /* alias_url */,
                                       resource_type, options.initiator_info)) {
    resource_request.SetIsAdResource();
  }
  if (blocked_reason) {
    return blocked_reason;
  }
  if (!resource_request.Url().IsValid()) {
    return ResourceRequestBlockedReason::kOther;
  }
  context.WillSendRequest(resource_request);
  if (!resource_request.Url().IsValid()) {
    return ResourceRequestBlockedReason::kOther;
  }

  return std::nullopt;
}

}  // namespace blink