1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2009 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
28 
29 #include "base/strings/string_number_conversions.h"
30 #include "net/base/net_errors.h"
31 #include "services/network/public/mojom/trust_tokens.mojom-blink-forward.h"
32 #include "third_party/blink/public/platform/platform.h"
33 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
34 #include "third_party/blink/public/platform/web_string.h"
35 #include "third_party/blink/public/platform/web_url.h"
36 #include "third_party/blink/public/platform/web_url_error.h"
37 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
38 #include "third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h"
39 
40 namespace blink {
41 
42 namespace {
43 constexpr char kThrottledErrorDescription[] =
44     "Request throttled. Visit https://dev.chromium.org/throttling for more "
45     "information.";
46 }  // namespace
47 
CancelledError(const KURL & url)48 ResourceError ResourceError::CancelledError(const KURL& url) {
49   return ResourceError(net::ERR_ABORTED, url, base::nullopt);
50 }
51 
CancelledDueToAccessCheckError(const KURL & url,ResourceRequestBlockedReason blocked_reason)52 ResourceError ResourceError::CancelledDueToAccessCheckError(
53     const KURL& url,
54     ResourceRequestBlockedReason blocked_reason) {
55   ResourceError error = CancelledError(url);
56   error.is_access_check_ = true;
57   error.blocked_by_subresource_filter_ =
58       blocked_reason == ResourceRequestBlockedReason::kSubresourceFilter;
59   return error;
60 }
61 
CancelledDueToAccessCheckError(const KURL & url,ResourceRequestBlockedReason blocked_reason,const String & localized_description)62 ResourceError ResourceError::CancelledDueToAccessCheckError(
63     const KURL& url,
64     ResourceRequestBlockedReason blocked_reason,
65     const String& localized_description) {
66   ResourceError error = CancelledDueToAccessCheckError(url, blocked_reason);
67   error.localized_description_ = localized_description;
68   return error;
69 }
70 
BlockedByResponse(const KURL & url,network::mojom::BlockedByResponseReason blocked_by_response_reason)71 ResourceError ResourceError::BlockedByResponse(
72     const KURL& url,
73     network::mojom::BlockedByResponseReason blocked_by_response_reason) {
74   ResourceError error(net::ERR_BLOCKED_BY_RESPONSE, url, base::nullopt);
75   error.blocked_by_response_reason_ = blocked_by_response_reason;
76   return error;
77 }
78 
CacheMissError(const KURL & url)79 ResourceError ResourceError::CacheMissError(const KURL& url) {
80   return ResourceError(net::ERR_CACHE_MISS, url, base::nullopt);
81 }
82 
TimeoutError(const KURL & url)83 ResourceError ResourceError::TimeoutError(const KURL& url) {
84   return ResourceError(net::ERR_TIMED_OUT, url, base::nullopt);
85 }
86 
Failure(const KURL & url)87 ResourceError ResourceError::Failure(const KURL& url) {
88   return ResourceError(net::ERR_FAILED, url, base::nullopt);
89 }
90 
ResourceError(int error_code,const KURL & url,base::Optional<network::CorsErrorStatus> cors_error_status)91 ResourceError::ResourceError(
92     int error_code,
93     const KURL& url,
94     base::Optional<network::CorsErrorStatus> cors_error_status)
95     : error_code_(error_code),
96       failing_url_(url),
97       is_access_check_(cors_error_status.has_value()),
98       cors_error_status_(cors_error_status) {
99   DCHECK_NE(error_code_, 0);
100   InitializeDescription();
101 }
102 
ResourceError(const KURL & url,const network::CorsErrorStatus & cors_error_status)103 ResourceError::ResourceError(const KURL& url,
104                              const network::CorsErrorStatus& cors_error_status)
105     : ResourceError(net::ERR_FAILED, url, cors_error_status) {}
106 
ResourceError(const WebURLError & error)107 ResourceError::ResourceError(const WebURLError& error)
108     : error_code_(error.reason()),
109       extended_error_code_(error.extended_reason()),
110       resolve_error_info_(error.resolve_error_info()),
111       failing_url_(error.url()),
112       is_access_check_(error.is_web_security_violation()),
113       has_copy_in_cache_(error.has_copy_in_cache()),
114       cors_error_status_(error.cors_error_status()),
115       blocked_by_response_reason_(error.blocked_by_response_reason()),
116       trust_token_operation_error_(error.trust_token_operation_error()) {
117   DCHECK_NE(error_code_, 0);
118   InitializeDescription();
119 }
120 
operator WebURLError() const121 ResourceError::operator WebURLError() const {
122   WebURLError::HasCopyInCache has_copy_in_cache =
123       has_copy_in_cache_ ? WebURLError::HasCopyInCache::kTrue
124                          : WebURLError::HasCopyInCache::kFalse;
125 
126   if (cors_error_status_) {
127     DCHECK_EQ(net::ERR_FAILED, error_code_);
128     return WebURLError(*cors_error_status_, has_copy_in_cache, failing_url_);
129   }
130 
131   if (trust_token_operation_error_ !=
132       network::mojom::blink::TrustTokenOperationStatus::kOk) {
133     return WebURLError(error_code_, trust_token_operation_error_, failing_url_);
134   }
135 
136   return WebURLError(
137       error_code_, extended_error_code_, resolve_error_info_, has_copy_in_cache,
138       is_access_check_ ? WebURLError::IsWebSecurityViolation::kTrue
139                        : WebURLError::IsWebSecurityViolation::kFalse,
140       failing_url_);
141 }
142 
Compare(const ResourceError & a,const ResourceError & b)143 bool ResourceError::Compare(const ResourceError& a, const ResourceError& b) {
144   if (a.ErrorCode() != b.ErrorCode())
145     return false;
146 
147   if (a.FailingURL() != b.FailingURL())
148     return false;
149 
150   if (a.LocalizedDescription() != b.LocalizedDescription())
151     return false;
152 
153   if (a.IsAccessCheck() != b.IsAccessCheck())
154     return false;
155 
156   if (a.HasCopyInCache() != b.HasCopyInCache())
157     return false;
158 
159   if (a.CorsErrorStatus() != b.CorsErrorStatus())
160     return false;
161 
162   if (a.extended_error_code_ != b.extended_error_code_)
163     return false;
164 
165   if (a.resolve_error_info_ != b.resolve_error_info_)
166     return false;
167 
168   if (a.trust_token_operation_error_ != b.trust_token_operation_error_)
169     return false;
170 
171   return true;
172 }
173 
IsTimeout() const174 bool ResourceError::IsTimeout() const {
175   return error_code_ == net::ERR_TIMED_OUT;
176 }
177 
IsCancellation() const178 bool ResourceError::IsCancellation() const {
179   return error_code_ == net::ERR_ABORTED;
180 }
181 
IsTrustTokenCacheHit() const182 bool ResourceError::IsTrustTokenCacheHit() const {
183   return error_code_ == net::ERR_TRUST_TOKEN_OPERATION_CACHE_HIT;
184 }
185 
IsUnactionableTrustTokensStatus() const186 bool ResourceError::IsUnactionableTrustTokensStatus() const {
187   return IsTrustTokenCacheHit() ||
188          (error_code_ == net::ERR_TRUST_TOKEN_OPERATION_FAILED &&
189           trust_token_operation_error_ ==
190               network::mojom::TrustTokenOperationStatus::kUnavailable);
191 }
192 
IsCacheMiss() const193 bool ResourceError::IsCacheMiss() const {
194   return error_code_ == net::ERR_CACHE_MISS;
195 }
196 
WasBlockedByResponse() const197 bool ResourceError::WasBlockedByResponse() const {
198   return error_code_ == net::ERR_BLOCKED_BY_RESPONSE;
199 }
200 
ShouldCollapseInitiator() const201 bool ResourceError::ShouldCollapseInitiator() const {
202   return blocked_by_subresource_filter_ ||
203          GetResourceRequestBlockedReason() ==
204              ResourceRequestBlockedReason::kCollapsedByClient;
205 }
206 
207 namespace {
208 blink::ResourceRequestBlockedReason
BlockedByResponseReasonToResourceRequestBlockedReason(network::mojom::BlockedByResponseReason reason)209 BlockedByResponseReasonToResourceRequestBlockedReason(
210     network::mojom::BlockedByResponseReason reason) {
211   switch (reason) {
212     case network::mojom::BlockedByResponseReason::
213         kCoepFrameResourceNeedsCoepHeader:
214       return blink::ResourceRequestBlockedReason::
215           kCoepFrameResourceNeedsCoepHeader;
216     case network::mojom::BlockedByResponseReason::
217         kCoopSandboxedIFrameCannotNavigateToCoopPage:
218       return blink::ResourceRequestBlockedReason::
219           kCoopSandboxedIFrameCannotNavigateToCoopPage;
220     case network::mojom::BlockedByResponseReason::kCorpNotSameOrigin:
221       return blink::ResourceRequestBlockedReason::kCorpNotSameOrigin;
222     case network::mojom::BlockedByResponseReason::
223         kCorpNotSameOriginAfterDefaultedToSameOriginByCoep:
224       return blink::ResourceRequestBlockedReason::
225           kCorpNotSameOriginAfterDefaultedToSameOriginByCoep;
226     case network::mojom::BlockedByResponseReason::kCorpNotSameSite:
227       return blink::ResourceRequestBlockedReason::kCorpNotSameSite;
228   }
229   NOTREACHED();
230   return blink::ResourceRequestBlockedReason::kOther;
231 }
232 }  // namespace
233 
234 base::Optional<ResourceRequestBlockedReason>
GetResourceRequestBlockedReason() const235 ResourceError::GetResourceRequestBlockedReason() const {
236   if (error_code_ != net::ERR_BLOCKED_BY_CLIENT &&
237       error_code_ != net::ERR_BLOCKED_BY_RESPONSE) {
238     return base::nullopt;
239   }
240   if (blocked_by_response_reason_) {
241     return BlockedByResponseReasonToResourceRequestBlockedReason(
242         *blocked_by_response_reason_);
243   }
244   return static_cast<ResourceRequestBlockedReason>(extended_error_code_);
245 }
246 
247 base::Optional<network::mojom::BlockedByResponseReason>
GetBlockedByResponseReason() const248 ResourceError::GetBlockedByResponseReason() const {
249   if (error_code_ != net::ERR_BLOCKED_BY_CLIENT &&
250       error_code_ != net::ERR_BLOCKED_BY_RESPONSE) {
251     return base::nullopt;
252   }
253   return blocked_by_response_reason_;
254 }
255 
256 namespace {
DescriptionForBlockedByClientOrResponse(int error,int extended_error)257 String DescriptionForBlockedByClientOrResponse(int error, int extended_error) {
258   if (extended_error == 0)
259     return WebString::FromASCII(net::ErrorToString(error));
260   std::string detail;
261   switch (static_cast<ResourceRequestBlockedReason>(extended_error)) {
262     case ResourceRequestBlockedReason::kOther:
263       NOTREACHED();  // extended_error == 0, handled above
264       break;
265     case ResourceRequestBlockedReason::kCSP:
266       detail = "CSP";
267       break;
268     case ResourceRequestBlockedReason::kMixedContent:
269       detail = "MixedContent";
270       break;
271     case ResourceRequestBlockedReason::kOrigin:
272       detail = "Origin";
273       break;
274     case ResourceRequestBlockedReason::kInspector:
275       detail = "Inspector";
276       break;
277     case ResourceRequestBlockedReason::kSubresourceFilter:
278       detail = "SubresourceFilter";
279       break;
280     case ResourceRequestBlockedReason::kContentType:
281       detail = "ContentType";
282       break;
283     case ResourceRequestBlockedReason::kCollapsedByClient:
284       detail = "Collapsed";
285       break;
286     case ResourceRequestBlockedReason::kCoepFrameResourceNeedsCoepHeader:
287       detail = "ResponseNeedsCrossOriginEmbedderPolicy";
288       break;
289     case ResourceRequestBlockedReason::
290         kCoopSandboxedIFrameCannotNavigateToCoopPage:
291       detail = "SandboxedIFrameCannotNavigateToOriginIsolatedPage";
292       break;
293     case ResourceRequestBlockedReason::kCorpNotSameOrigin:
294       detail = "NotSameOrigin";
295       break;
296     case ResourceRequestBlockedReason::
297         kCorpNotSameOriginAfterDefaultedToSameOriginByCoep:
298       detail = "NotSameOriginAfterDefaultedToSameOriginByCoep";
299       break;
300     case ResourceRequestBlockedReason::kCorpNotSameSite:
301       detail = "NotSameSite";
302       break;
303     case ResourceRequestBlockedReason::
304         kBlockedByExtensionCrbug1128174Investigation:
305       detail = "BlockedByExtensionCrbug1128174Investigation";
306       break;
307   }
308   return WebString::FromASCII(net::ErrorToString(error) + "." + detail);
309 }
310 }  // namespace
311 
InitializeDescription()312 void ResourceError::InitializeDescription() {
313   if (error_code_ == net::ERR_TEMPORARILY_THROTTLED) {
314     localized_description_ = WebString::FromASCII(kThrottledErrorDescription);
315   } else if (error_code_ == net::ERR_BLOCKED_BY_CLIENT ||
316              error_code_ == net::ERR_BLOCKED_BY_RESPONSE) {
317     localized_description_ = DescriptionForBlockedByClientOrResponse(
318         error_code_, extended_error_code_);
319   } else {
320     localized_description_ = WebString::FromASCII(
321         net::ExtendedErrorToString(error_code_, extended_error_code_));
322   }
323 }
324 
operator <<(std::ostream & os,const ResourceError & error)325 std::ostream& operator<<(std::ostream& os, const ResourceError& error) {
326   return os << ", ErrorCode = " << error.ErrorCode()
327             << ", FailingURL = " << error.FailingURL()
328             << ", LocalizedDescription = " << error.LocalizedDescription()
329             << ", IsCancellation = " << error.IsCancellation()
330             << ", IsAccessCheck = " << error.IsAccessCheck()
331             << ", IsTimeout = " << error.IsTimeout()
332             << ", HasCopyInCache = " << error.HasCopyInCache()
333             << ", IsCacheMiss = " << error.IsCacheMiss()
334             << ", TrustTokenOperationError = "
335             << String::FromUTF8(base::NumberToString(
336                    static_cast<int32_t>(error.TrustTokenOperationError())));
337 }
338 
339 }  // namespace blink
340