summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/loader/subresource_redirect_util.cc
blob: 1aac6488246efe8551829466d0a9ef2550ad5771 (plain)
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
// Copyright 2021 The Chromium Authors. All rights reserved.
// 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/core/loader/subresource_redirect_util.h"

#include "base/metrics/histogram_functions.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/html/cross_origin_attribute.h"
#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"

namespace blink {

namespace {

// Records the ineligibility metrics.
void RecordSubresourceRedirectIneligibility(
    BlinkSubresourceRedirectIneligibility reason) {
  base::UmaHistogramEnumeration("SubresourceRedirect.Blink.Ineligibility",
                                reason);
}

// Returns whether the URL scheme is http or https.
bool IsSchemeHttpOrHttps(const KURL& url) {
  return url.Protocol() == url::kHttpsScheme ||
         url.Protocol() == url::kHttpScheme;
}

}  // namespace

bool ShouldEnableSubresourceRedirect(HTMLImageElement* image_element,
                                     const KURL& url) {
  if (!image_element)
    return false;

  // Allow redirection only when DataSaver is enabled and subresource redirect
  // feature is enabled which allows redirecting to better optimized versions.
  if (!base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) ||
      !GetNetworkStateNotifier().SaveDataEnabled()) {
    return false;
  }

  // Allow redirection only for http(s) subresources on http(s) documents.
  if (!IsSchemeHttpOrHttps(url) ||
      !IsSchemeHttpOrHttps(image_element->GetDocument().Url())) {
    return false;
  }

  // Enable subresource redirect only for <img> elements created by parser.
  // Images created from javascript, fetched via XHR/Fetch API should not be
  // subresource redirected due to the additional CORB/CORS handling needed for
  // them.
  if (!image_element->ElementCreatedByParser()) {
    RecordSubresourceRedirectIneligibility(
        SecurityOrigin::AreSameOrigin(url, image_element->GetDocument().Url())
            ? BlinkSubresourceRedirectIneligibility::
                  kJavascriptCreatedSameOrigin
            : BlinkSubresourceRedirectIneligibility::
                  kJavascriptCreatedCrossOrigin);
    return false;
  }

  // Create a cross origin URL by appending a string to the original host. This
  // is used to find whether CSP is restricting image fetches from other
  // origins.
  KURL cross_origin_url = url;
  cross_origin_url.SetHost(url.Host() + "crossorigin.com");
  auto* content_security_policy =
      image_element->GetExecutionContext()->GetContentSecurityPolicy();
  if (content_security_policy &&
      !content_security_policy->AllowImageFromSource(
          cross_origin_url, cross_origin_url, RedirectStatus::kNoRedirect,
          ReportingDisposition::kSuppressReporting)) {
    // Check if an object is allowed by CSP as a proxy to determine whether the
    // image resource was disallowed by default-src or img-src directives. When
    // an object is allowed it means default-src did not disallow the image.
    RecordSubresourceRedirectIneligibility(
        content_security_policy->AllowRequest(
            mojom::blink::RequestContextType::OBJECT,
            network::mojom::RequestDestination::kObject, cross_origin_url,
            String() /* nonce */, IntegrityMetadataSet(),
            ParserDisposition::kParserInserted, cross_origin_url,
            RedirectStatus::kNoRedirect,
            ReportingDisposition::kSuppressReporting,
            ContentSecurityPolicy::CheckHeaderType::kCheckEnforce)
            ? BlinkSubresourceRedirectIneligibility::
                  kContentSecurityPolicyImgSrcRestricted
            : BlinkSubresourceRedirectIneligibility::
                  kContentSecurityPolicyDefaultSrcRestricted);
    return false;
  }

  // Allow subresource redirect only when cross-origin attribute is not set,
  // which indicates CORS validation is not triggered for the image.
  if (GetCrossOriginAttributeValue(image_element->FastGetAttribute(
          html_names::kCrossoriginAttr)) != kCrossOriginAttributeNotSet) {
    RecordSubresourceRedirectIneligibility(
        BlinkSubresourceRedirectIneligibility::kCrossOriginAttributeSet);
    return false;
  }
  return true;
}

}  // namespace blink