1 /*
2 * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org>
4 * Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org>
5 * Copyright (C) 2008, 2011 Google Inc. All rights reserved.
6 * Copyright (C) 2012 Intel Corporation
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA
22 */
23
24 #include "third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h"
25
26 #include <memory>
27 #include <utility>
28
29 #include "base/auto_reset.h"
30 #include "base/feature_list.h"
31 #include "base/metrics/histogram_macros.h"
32 #include "base/time/time.h"
33 #include "services/network/public/mojom/trust_tokens.mojom-shared.h"
34 #include "third_party/blink/public/common/blob/blob_utils.h"
35 #include "third_party/blink/public/common/features.h"
36 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink-forward.h"
37 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
38 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
39 #include "third_party/blink/public/platform/web_url_request.h"
40 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
41 #include "third_party/blink/renderer/bindings/core/v8/document_or_xml_http_request_body_init.h"
42 #include "third_party/blink/renderer/core/dom/document_init.h"
43 #include "third_party/blink/renderer/core/dom/document_parser.h"
44 #include "third_party/blink/renderer/core/dom/dom_exception.h"
45 #include "third_party/blink/renderer/core/dom/events/event.h"
46 #include "third_party/blink/renderer/core/dom/xml_document.h"
47 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
48 #include "third_party/blink/renderer/core/events/progress_event.h"
49 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
50 #include "third_party/blink/renderer/core/fetch/trust_token_issuance_authorization.h"
51 #include "third_party/blink/renderer/core/fetch/trust_token_to_mojom.h"
52 #include "third_party/blink/renderer/core/fileapi/blob.h"
53 #include "third_party/blink/renderer/core/fileapi/file.h"
54 #include "third_party/blink/renderer/core/fileapi/file_reader_loader.h"
55 #include "third_party/blink/renderer/core/fileapi/file_reader_loader_client.h"
56 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
57 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
58 #include "third_party/blink/renderer/core/frame/deprecation.h"
59 #include "third_party/blink/renderer/core/frame/frame.h"
60 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
61 #include "third_party/blink/renderer/core/frame/page_dismissal_scope.h"
62 #include "third_party/blink/renderer/core/frame/settings.h"
63 #include "third_party/blink/renderer/core/html/forms/form_data.h"
64 #include "third_party/blink/renderer/core/html/html_document.h"
65 #include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
66 #include "third_party/blink/renderer/core/inspector/console_message.h"
67 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
68 #include "third_party/blink/renderer/core/loader/threadable_loader.h"
69 #include "third_party/blink/renderer/core/page/chrome_client.h"
70 #include "third_party/blink/renderer/core/page/page.h"
71 #include "third_party/blink/renderer/core/probe/core_probes.h"
72 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
73 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
74 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
75 #include "third_party/blink/renderer/core/url/url_search_params.h"
76 #include "third_party/blink/renderer/core/xmlhttprequest/xml_http_request_upload.h"
77 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
78 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
79 #include "third_party/blink/renderer/platform/bindings/script_state.h"
80 #include "third_party/blink/renderer/platform/blob/blob_data.h"
81 #include "third_party/blink/renderer/platform/file_metadata.h"
82 #include "third_party/blink/renderer/platform/heap/heap.h"
83 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
84 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
85 #include "third_party/blink/renderer/platform/loader/cors/cors.h"
86 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
87 #include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
88 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
89 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
90 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
91 #include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
92 #include "third_party/blink/renderer/platform/network/http_names.h"
93 #include "third_party/blink/renderer/platform/network/http_parsers.h"
94 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
95 #include "third_party/blink/renderer/platform/network/network_log.h"
96 #include "third_party/blink/renderer/platform/network/parsed_content_type.h"
97 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
98 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
99 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
100 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
101 #include "third_party/blink/renderer/platform/wtf/assertions.h"
102 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
103 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
104 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
105
106 namespace blink {
107
108 namespace {
109
110 // This class protects the wrapper of the associated XMLHttpRequest object
111 // via hasPendingActivity method which returns true if
112 // m_eventDispatchRecursionLevel is positive.
113 class ScopedEventDispatchProtect final {
114 STACK_ALLOCATED();
115
116 public:
ScopedEventDispatchProtect(int * level)117 explicit ScopedEventDispatchProtect(int* level) : level_(level) { ++*level_; }
~ScopedEventDispatchProtect()118 ~ScopedEventDispatchProtect() {
119 DCHECK_GT(*level_, 0);
120 --*level_;
121 }
122
123 private:
124 int* const level_;
125 };
126
127 // These methods were placed in HTTPParsers.h. Since these methods don't
128 // perform ABNF validation but loosely look for the part that is likely to be
129 // indicating the charset parameter, new code should use
130 // HttpUtil::ParseContentType() than these. To discourage use of these methods,
131 // moved from HTTPParser.h to the only user XMLHttpRequest.cpp.
132 //
133 // TODO(tyoshino): Switch XHR to use HttpUtil. See crbug.com/743311.
FindCharsetInMediaType(const String & media_type,unsigned & charset_pos,unsigned & charset_len)134 void FindCharsetInMediaType(const String& media_type,
135 unsigned& charset_pos,
136 unsigned& charset_len) {
137 charset_len = 0;
138
139 size_t pos = charset_pos;
140 unsigned length = media_type.length();
141
142 while (pos < length) {
143 pos = media_type.FindIgnoringASCIICase("charset", pos);
144
145 if (pos == kNotFound)
146 return;
147
148 // Give up if we find "charset" at the head.
149 if (!pos)
150 return;
151
152 // Now check that "charset" is not a substring of some longer word.
153 if (media_type[pos - 1] > ' ' && media_type[pos - 1] != ';') {
154 pos += 7;
155 continue;
156 }
157
158 pos += 7;
159
160 while (pos < length && media_type[pos] <= ' ')
161 ++pos;
162
163 // Treat this as a charset parameter.
164 if (media_type[pos++] == '=')
165 break;
166 }
167
168 while (pos < length && (media_type[pos] <= ' ' || media_type[pos] == '"' ||
169 media_type[pos] == '\''))
170 ++pos;
171
172 charset_pos = pos;
173
174 // we don't handle spaces within quoted parameter values, because charset
175 // names cannot have any
176 while (pos < length && media_type[pos] > ' ' && media_type[pos] != '"' &&
177 media_type[pos] != '\'' && media_type[pos] != ';')
178 ++pos;
179
180 charset_len = pos - charset_pos;
181 }
ExtractCharsetFromMediaType(const String & media_type)182 String ExtractCharsetFromMediaType(const String& media_type) {
183 unsigned pos = 0;
184 unsigned len = 0;
185 FindCharsetInMediaType(media_type, pos, len);
186 return media_type.Substring(pos, len);
187 }
188
ReplaceCharsetInMediaType(String & media_type,const String & charset_value)189 void ReplaceCharsetInMediaType(String& media_type,
190 const String& charset_value) {
191 unsigned pos = 0;
192
193 while (true) {
194 unsigned len = 0;
195 FindCharsetInMediaType(media_type, pos, len);
196 if (!len)
197 return;
198 media_type.replace(pos, len, charset_value);
199 pos += charset_value.length();
200 }
201 }
202
LogConsoleError(ExecutionContext * context,const String & message)203 void LogConsoleError(ExecutionContext* context, const String& message) {
204 if (!context)
205 return;
206 // FIXME: It's not good to report the bad usage without indicating what source
207 // line it came from. We should pass additional parameters so we can tell the
208 // console where the mistake occurred.
209 auto* console_message = MakeGarbageCollected<ConsoleMessage>(
210 mojom::ConsoleMessageSource::kJavaScript,
211 mojom::ConsoleMessageLevel::kError, message);
212 context->AddConsoleMessage(console_message);
213 }
214
ValidateOpenArguments(const AtomicString & method,const KURL & url,ExceptionState & exception_state)215 bool ValidateOpenArguments(const AtomicString& method,
216 const KURL& url,
217 ExceptionState& exception_state) {
218 if (!IsValidHTTPToken(method)) {
219 exception_state.ThrowDOMException(
220 DOMExceptionCode::kSyntaxError,
221 "'" + method + "' is not a valid HTTP method.");
222 return false;
223 }
224
225 if (FetchUtils::IsForbiddenMethod(method)) {
226 exception_state.ThrowSecurityError("'" + method +
227 "' HTTP method is unsupported.");
228 return false;
229 }
230
231 if (!url.IsValid()) {
232 exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
233 "Invalid URL");
234 return false;
235 }
236
237 return true;
238 }
239
240 } // namespace
241
242 class XMLHttpRequest::BlobLoader final
243 : public GarbageCollected<XMLHttpRequest::BlobLoader>,
244 public FileReaderLoaderClient {
245 public:
BlobLoader(XMLHttpRequest * xhr,scoped_refptr<BlobDataHandle> handle)246 BlobLoader(XMLHttpRequest* xhr, scoped_refptr<BlobDataHandle> handle)
247 : xhr_(xhr),
248 loader_(std::make_unique<FileReaderLoader>(
249 FileReaderLoader::kReadByClient,
250 this,
251 xhr->GetExecutionContext()->GetTaskRunner(
252 TaskType::kFileReading))) {
253 loader_->Start(std::move(handle));
254 }
255
256 // FileReaderLoaderClient functions.
DidStartLoading()257 void DidStartLoading() override {}
DidReceiveDataForClient(const char * data,unsigned length)258 void DidReceiveDataForClient(const char* data, unsigned length) override {
259 DCHECK_LE(length, static_cast<unsigned>(INT_MAX));
260 xhr_->DidReceiveData(data, length);
261 }
DidFinishLoading()262 void DidFinishLoading() override { xhr_->DidFinishLoadingFromBlob(); }
DidFail(FileErrorCode error)263 void DidFail(FileErrorCode error) override { xhr_->DidFailLoadingFromBlob(); }
264
Cancel()265 void Cancel() { loader_->Cancel(); }
266
Trace(Visitor * visitor) const267 void Trace(Visitor* visitor) const { visitor->Trace(xhr_); }
268
269 private:
270 Member<XMLHttpRequest> xhr_;
271 std::unique_ptr<FileReaderLoader> loader_;
272 };
273
Create(ScriptState * script_state)274 XMLHttpRequest* XMLHttpRequest::Create(ScriptState* script_state) {
275 return MakeGarbageCollected<XMLHttpRequest>(
276 ExecutionContext::From(script_state), script_state->GetIsolate(),
277 &script_state->World());
278 }
279
Create(ExecutionContext * context)280 XMLHttpRequest* XMLHttpRequest::Create(ExecutionContext* context) {
281 v8::Isolate* isolate = context->GetIsolate();
282 CHECK(isolate);
283
284 return MakeGarbageCollected<XMLHttpRequest>(context, isolate, nullptr);
285 }
286
XMLHttpRequest(ExecutionContext * context,v8::Isolate * isolate,scoped_refptr<const DOMWrapperWorld> world)287 XMLHttpRequest::XMLHttpRequest(ExecutionContext* context,
288 v8::Isolate* isolate,
289 scoped_refptr<const DOMWrapperWorld> world)
290 : ExecutionContextLifecycleObserver(context),
291 progress_event_throttle_(
292 MakeGarbageCollected<XMLHttpRequestProgressEventThrottle>(this)),
293 isolate_(isolate),
294 world_(std::move(world)),
295 isolated_world_security_origin_(world_ && world_->IsIsolatedWorld()
296 ? world_->IsolatedWorldSecurityOrigin(
297 context->GetAgentClusterID())
298 : nullptr) {}
299
~XMLHttpRequest()300 XMLHttpRequest::~XMLHttpRequest() {
301 binary_response_builder_ = nullptr;
302 length_downloaded_to_blob_ = 0;
303 ReportMemoryUsageToV8();
304 }
305
readyState() const306 XMLHttpRequest::State XMLHttpRequest::readyState() const {
307 return state_;
308 }
309
responseText(ExceptionState & exception_state)310 v8::Local<v8::String> XMLHttpRequest::responseText(
311 ExceptionState& exception_state) {
312 if (response_type_code_ != kResponseTypeDefault &&
313 response_type_code_ != kResponseTypeText) {
314 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
315 "The value is only accessible if the "
316 "object's 'responseType' is '' or 'text' "
317 "(was '" +
318 responseType() + "').");
319 return v8::Local<v8::String>();
320 }
321 if (error_ || (state_ != kLoading && state_ != kDone))
322 return v8::Local<v8::String>();
323 return response_text_.V8Value(isolate_);
324 }
325
ResponseJSONSource()326 v8::Local<v8::String> XMLHttpRequest::ResponseJSONSource() {
327 DCHECK_EQ(response_type_code_, kResponseTypeJSON);
328
329 if (error_ || state_ != kDone)
330 return v8::Local<v8::String>();
331 return response_text_.V8Value(isolate_);
332 }
333
InitResponseDocument()334 void XMLHttpRequest::InitResponseDocument() {
335 // The W3C spec requires the final MIME type to be some valid XML type, or
336 // text/html. If it is text/html, then the responseType of "document" must
337 // have been supplied explicitly.
338 bool is_html = ResponseIsHTML();
339 if ((response_.IsHTTP() && !ResponseIsXML() && !is_html) ||
340 (is_html && response_type_code_ == kResponseTypeDefault) ||
341 !GetExecutionContext() || GetExecutionContext()->IsWorkerGlobalScope()) {
342 response_document_ = nullptr;
343 return;
344 }
345
346 auto* document = To<LocalDOMWindow>(GetExecutionContext())->document();
347 DocumentInit init = DocumentInit::Create()
348 .WithExecutionContext(GetExecutionContext())
349 .WithURL(response_.ResponseUrl());
350 if (is_html) {
351 response_document_ = MakeGarbageCollected<HTMLDocument>(init);
352 response_document_->setAllowDeclarativeShadowRoot(false);
353 } else
354 response_document_ = MakeGarbageCollected<XMLDocument>(init);
355
356 // FIXME: Set Last-Modified.
357 response_document_->SetContextFeatures(document->GetContextFeatures());
358 response_document_->SetMimeType(FinalResponseMIMETypeWithFallback());
359 }
360
responseXML(ExceptionState & exception_state)361 Document* XMLHttpRequest::responseXML(ExceptionState& exception_state) {
362 if (response_type_code_ != kResponseTypeDefault &&
363 response_type_code_ != kResponseTypeDocument) {
364 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
365 "The value is only accessible if the "
366 "object's 'responseType' is '' or "
367 "'document' (was '" +
368 responseType() + "').");
369 return nullptr;
370 }
371
372 if (error_ || state_ != kDone)
373 return nullptr;
374
375 if (!parsed_response_) {
376 InitResponseDocument();
377 if (!response_document_)
378 return nullptr;
379
380 response_document_->SetContent(response_text_.Flatten(isolate_));
381 if (!response_document_->WellFormed()) {
382 response_document_ = nullptr;
383 } else {
384 response_document_->OverrideLastModified(
385 response_.HttpHeaderField(http_names::kLastModified));
386 }
387
388 parsed_response_ = true;
389 }
390
391 return response_document_;
392 }
393
ResponseBlob()394 Blob* XMLHttpRequest::ResponseBlob() {
395 DCHECK_EQ(response_type_code_, kResponseTypeBlob);
396
397 // We always return null before kDone.
398 if (error_ || state_ != kDone)
399 return nullptr;
400
401 if (!response_blob_) {
402 auto blob_data = std::make_unique<BlobData>();
403 blob_data->SetContentType(FinalResponseMIMETypeWithFallback().LowerASCII());
404 size_t size = 0;
405 if (binary_response_builder_ && binary_response_builder_->size()) {
406 for (const auto& span : *binary_response_builder_)
407 blob_data->AppendBytes(span.data(), span.size());
408 size = binary_response_builder_->size();
409 binary_response_builder_ = nullptr;
410 ReportMemoryUsageToV8();
411 }
412 response_blob_ = MakeGarbageCollected<Blob>(
413 BlobDataHandle::Create(std::move(blob_data), size));
414 }
415
416 return response_blob_;
417 }
418
ResponseArrayBuffer()419 DOMArrayBuffer* XMLHttpRequest::ResponseArrayBuffer() {
420 DCHECK_EQ(response_type_code_, kResponseTypeArrayBuffer);
421
422 if (error_ || state_ != kDone)
423 return nullptr;
424
425 if (!response_array_buffer_ && !response_array_buffer_failure_) {
426 if (binary_response_builder_ && binary_response_builder_->size()) {
427 DOMArrayBuffer* buffer = DOMArrayBuffer::CreateUninitializedOrNull(
428 binary_response_builder_->size(), 1);
429 if (buffer) {
430 bool result = binary_response_builder_->GetBytes(buffer->Data(),
431 buffer->ByteLength());
432 DCHECK(result);
433 response_array_buffer_ = buffer;
434 }
435 // https://xhr.spec.whatwg.org/#arraybuffer-response allows clearing
436 // of the 'received bytes' payload when the response buffer allocation
437 // fails.
438 binary_response_builder_ = nullptr;
439 ReportMemoryUsageToV8();
440 // Mark allocation as failed; subsequent calls to the accessor must
441 // continue to report |null|.
442 //
443 response_array_buffer_failure_ = !buffer;
444 } else {
445 response_array_buffer_ = DOMArrayBuffer::Create(nullptr, 0);
446 }
447 }
448
449 return response_array_buffer_;
450 }
451
setTimeout(unsigned timeout,ExceptionState & exception_state)452 void XMLHttpRequest::setTimeout(unsigned timeout,
453 ExceptionState& exception_state) {
454 // FIXME: Need to trigger or update the timeout Timer here, if needed.
455 // http://webkit.org/b/98156
456 // XHR2 spec, 4.7.3. "This implies that the timeout attribute can be set while
457 // fetching is in progress. If that occurs it will still be measured relative
458 // to the start of fetching."
459 if (GetExecutionContext() && GetExecutionContext()->IsWindow() && !async_) {
460 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
461 "Timeouts cannot be set for synchronous "
462 "requests made from a document.");
463 return;
464 }
465
466 timeout_ = base::TimeDelta::FromMilliseconds(timeout);
467
468 // From http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute:
469 // Note: This implies that the timeout attribute can be set while fetching is
470 // in progress. If that occurs it will still be measured relative to the start
471 // of fetching.
472 //
473 // The timeout may be overridden after send.
474 if (loader_)
475 loader_->SetTimeout(timeout_);
476 }
477
setResponseType(const String & response_type,ExceptionState & exception_state)478 void XMLHttpRequest::setResponseType(const String& response_type,
479 ExceptionState& exception_state) {
480 if (state_ >= kLoading) {
481 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
482 "The response type cannot be set if the "
483 "object's state is LOADING or DONE.");
484 return;
485 }
486
487 // Newer functionality is not available to synchronous requests in window
488 // contexts, as a spec-mandated attempt to discourage synchronous XHR use.
489 // responseType is one such piece of functionality.
490 if (GetExecutionContext() && GetExecutionContext()->IsWindow() && !async_) {
491 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
492 "The response type cannot be changed for "
493 "synchronous requests made from a "
494 "document.");
495 return;
496 }
497
498 if (response_type == "") {
499 response_type_code_ = kResponseTypeDefault;
500 } else if (response_type == "text") {
501 response_type_code_ = kResponseTypeText;
502 } else if (response_type == "json") {
503 response_type_code_ = kResponseTypeJSON;
504 } else if (response_type == "document") {
505 response_type_code_ = kResponseTypeDocument;
506 } else if (response_type == "blob") {
507 response_type_code_ = kResponseTypeBlob;
508 } else if (response_type == "arraybuffer") {
509 response_type_code_ = kResponseTypeArrayBuffer;
510 } else {
511 NOTREACHED();
512 }
513 }
514
responseType()515 String XMLHttpRequest::responseType() {
516 switch (response_type_code_) {
517 case kResponseTypeDefault:
518 return "";
519 case kResponseTypeText:
520 return "text";
521 case kResponseTypeJSON:
522 return "json";
523 case kResponseTypeDocument:
524 return "document";
525 case kResponseTypeBlob:
526 return "blob";
527 case kResponseTypeArrayBuffer:
528 return "arraybuffer";
529 }
530 return "";
531 }
532
responseURL()533 String XMLHttpRequest::responseURL() {
534 KURL response_url(response_.ResponseUrl());
535 if (!response_url.IsNull())
536 response_url.RemoveFragmentIdentifier();
537 return response_url.GetString();
538 }
539
upload()540 XMLHttpRequestUpload* XMLHttpRequest::upload() {
541 if (!upload_)
542 upload_ = MakeGarbageCollected<XMLHttpRequestUpload>(this);
543 return upload_;
544 }
545
TrackProgress(uint64_t length)546 void XMLHttpRequest::TrackProgress(uint64_t length) {
547 received_length_ += length;
548
549 ChangeState(kLoading);
550 if (async_) {
551 // readyStateChange event is fired as well.
552 DispatchProgressEventFromSnapshot(event_type_names::kProgress);
553 }
554 }
555
ChangeState(State new_state)556 void XMLHttpRequest::ChangeState(State new_state) {
557 if (state_ != new_state) {
558 state_ = new_state;
559 DispatchReadyStateChangeEvent();
560 }
561 }
562
DispatchReadyStateChangeEvent()563 void XMLHttpRequest::DispatchReadyStateChangeEvent() {
564 if (!GetExecutionContext())
565 return;
566
567 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
568 if (async_ || (state_ <= kOpened || state_ == kDone)) {
569 TRACE_EVENT1("devtools.timeline", "XHRReadyStateChange", "data",
570 inspector_xhr_ready_state_change_event::Data(
571 GetExecutionContext(), this));
572 XMLHttpRequestProgressEventThrottle::DeferredEventAction action =
573 XMLHttpRequestProgressEventThrottle::kIgnore;
574 if (state_ == kDone) {
575 if (error_)
576 action = XMLHttpRequestProgressEventThrottle::kClear;
577 else
578 action = XMLHttpRequestProgressEventThrottle::kFlush;
579 }
580 progress_event_throttle_->DispatchReadyStateChangeEvent(
581 Event::Create(event_type_names::kReadystatechange), action);
582 }
583
584 if (state_ == kDone && !error_) {
585 TRACE_EVENT1("devtools.timeline", "XHRLoad", "data",
586 inspector_xhr_load_event::Data(GetExecutionContext(), this));
587 DispatchProgressEventFromSnapshot(event_type_names::kLoad);
588 DispatchProgressEventFromSnapshot(event_type_names::kLoadend);
589 }
590 }
591
setWithCredentials(bool value,ExceptionState & exception_state)592 void XMLHttpRequest::setWithCredentials(bool value,
593 ExceptionState& exception_state) {
594 if (state_ > kOpened || send_flag_) {
595 exception_state.ThrowDOMException(
596 DOMExceptionCode::kInvalidStateError,
597 "The value may only be set if the object's state is UNSENT or OPENED.");
598 return;
599 }
600
601 with_credentials_ = value;
602 }
603
open(const AtomicString & method,const String & url_string,ExceptionState & exception_state)604 void XMLHttpRequest::open(const AtomicString& method,
605 const String& url_string,
606 ExceptionState& exception_state) {
607 if (!GetExecutionContext())
608 return;
609
610 KURL url(GetExecutionContext()->CompleteURL(url_string));
611 if (!ValidateOpenArguments(method, url, exception_state))
612 return;
613
614 open(method, url, true, exception_state);
615 }
616
open(const AtomicString & method,const String & url_string,bool async,const String & username,const String & password,ExceptionState & exception_state)617 void XMLHttpRequest::open(const AtomicString& method,
618 const String& url_string,
619 bool async,
620 const String& username,
621 const String& password,
622 ExceptionState& exception_state) {
623 if (!GetExecutionContext())
624 return;
625
626 KURL url(GetExecutionContext()->CompleteURL(url_string));
627 if (!ValidateOpenArguments(method, url, exception_state))
628 return;
629
630 if (!username.IsNull())
631 url.SetUser(username);
632 if (!password.IsNull())
633 url.SetPass(password);
634
635 open(method, url, async, exception_state);
636 }
637
open(const AtomicString & method,const KURL & url,bool async,ExceptionState & exception_state)638 void XMLHttpRequest::open(const AtomicString& method,
639 const KURL& url,
640 bool async,
641 ExceptionState& exception_state) {
642 NETWORK_DVLOG(1) << this << " open(" << method << ", " << url.ElidedString()
643 << ", " << async << ")";
644
645 DCHECK(ValidateOpenArguments(method, url, exception_state));
646
647 InternalAbort();
648
649 State previous_state = state_;
650 state_ = kUnsent;
651 error_ = false;
652 upload_complete_ = false;
653
654 auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext());
655 if (!async && window) {
656 if (window->GetFrame() &&
657 !window->GetFrame()->GetSettings()->GetSyncXHRInDocumentsEnabled()) {
658 exception_state.ThrowDOMException(
659 DOMExceptionCode::kInvalidAccessError,
660 "Synchronous requests are disabled for this page.");
661 return;
662 }
663
664 // Newer functionality is not available to synchronous requests in window
665 // contexts, as a spec-mandated attempt to discourage synchronous XHR use.
666 // responseType is one such piece of functionality.
667 if (response_type_code_ != kResponseTypeDefault) {
668 exception_state.ThrowDOMException(
669 DOMExceptionCode::kInvalidAccessError,
670 "Synchronous requests from a document must not set a response type.");
671 return;
672 }
673
674 // Similarly, timeouts are disabled for synchronous requests as well.
675 if (!timeout_.is_zero()) {
676 exception_state.ThrowDOMException(
677 DOMExceptionCode::kInvalidAccessError,
678 "Synchronous requests must not set a timeout.");
679 return;
680 }
681
682 // Here we just warn that firing sync XHR's may affect responsiveness.
683 // Eventually sync xhr will be deprecated and an "InvalidAccessError"
684 // exception thrown.
685 // Refer : https://xhr.spec.whatwg.org/#sync-warning
686 // Use count for XHR synchronous requests on main thread only.
687 if (!window->document()->ProcessingBeforeUnload()) {
688 Deprecation::CountDeprecation(
689 GetExecutionContext(),
690 WebFeature::kXMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload);
691 }
692 }
693
694 method_ = FetchUtils::NormalizeMethod(method);
695
696 url_ = url;
697
698 if (url_.ProtocolIs("blob")) {
699 GetExecutionContext()->GetPublicURLManager().Resolve(
700 url_, blob_url_loader_factory_.InitWithNewPipeAndPassReceiver());
701 }
702
703 async_ = async;
704
705 DCHECK(!loader_);
706 send_flag_ = false;
707
708 // Check previous state to avoid dispatching readyState event
709 // when calling open several times in a row.
710 if (previous_state != kOpened)
711 ChangeState(kOpened);
712 else
713 state_ = kOpened;
714 }
715
InitSend(ExceptionState & exception_state)716 bool XMLHttpRequest::InitSend(ExceptionState& exception_state) {
717 // We need to check ContextDestroyed because it is possible to create a
718 // XMLHttpRequest with already detached document.
719 // TODO(yhirano): Fix this.
720 if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
721 HandleNetworkError();
722 ThrowForLoadFailureIfNeeded(exception_state,
723 "Document is already detached.");
724 return false;
725 }
726
727 if (state_ != kOpened || send_flag_) {
728 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
729 "The object's state must be OPENED.");
730 return false;
731 }
732
733 if (!async_) {
734 if (GetExecutionContext()->IsWindow() &&
735 !GetExecutionContext()->IsFeatureEnabled(
736 mojom::blink::FeaturePolicyFeature::kSyncXHR,
737 ReportOptions::kReportOnFailure,
738 "Synchronous requests are disabled by Feature Policy.")) {
739 HandleNetworkError();
740 ThrowForLoadFailureIfNeeded(exception_state, String());
741 return false;
742 }
743 v8::Isolate* isolate = v8::Isolate::GetCurrent();
744 if (isolate && v8::MicrotasksScope::IsRunningMicrotasks(isolate)) {
745 UseCounter::Count(GetExecutionContext(),
746 WebFeature::kDuring_Microtask_SyncXHR);
747 }
748 }
749
750 error_ = false;
751 return true;
752 }
753
send(const DocumentOrBlobOrArrayBufferOrArrayBufferViewOrFormDataOrURLSearchParamsOrUSVString & body,ExceptionState & exception_state)754 void XMLHttpRequest::send(
755 const DocumentOrBlobOrArrayBufferOrArrayBufferViewOrFormDataOrURLSearchParamsOrUSVString&
756 body,
757 ExceptionState& exception_state) {
758 probe::WillSendXMLHttpOrFetchNetworkRequest(GetExecutionContext(), Url());
759
760 if (body.IsNull()) {
761 send(String(), exception_state);
762 return;
763 }
764
765 if (body.IsDocument()) {
766 send(body.GetAsDocument(), exception_state);
767 return;
768 }
769
770 if (body.IsBlob()) {
771 send(body.GetAsBlob(), exception_state);
772 return;
773 }
774
775 if (body.IsArrayBuffer()) {
776 send(body.GetAsArrayBuffer(), exception_state);
777 return;
778 }
779
780 if (body.IsArrayBufferView()) {
781 send(body.GetAsArrayBufferView().View(), exception_state);
782 return;
783 }
784
785 if (body.IsFormData()) {
786 send(body.GetAsFormData(), exception_state);
787 return;
788 }
789
790 if (body.IsURLSearchParams()) {
791 send(body.GetAsURLSearchParams(), exception_state);
792 return;
793 }
794
795 DCHECK(body.IsUSVString());
796 send(body.GetAsUSVString(), exception_state);
797 }
798
AreMethodAndURLValidForSend()799 bool XMLHttpRequest::AreMethodAndURLValidForSend() {
800 return method_ != http_names::kGET && method_ != http_names::kHEAD &&
801 url_.ProtocolIsInHTTPFamily();
802 }
803
send(Document * document,ExceptionState & exception_state)804 void XMLHttpRequest::send(Document* document, ExceptionState& exception_state) {
805 NETWORK_DVLOG(1) << this << " send() Document "
806 << static_cast<void*>(document);
807
808 DCHECK(document);
809
810 if (!InitSend(exception_state))
811 return;
812
813 scoped_refptr<EncodedFormData> http_body;
814
815 if (AreMethodAndURLValidForSend()) {
816 if (IsA<HTMLDocument>(document))
817 UpdateContentTypeAndCharset("text/html;charset=UTF-8", "UTF-8");
818 else if (IsA<XMLDocument>(document))
819 UpdateContentTypeAndCharset("application/xml;charset=UTF-8", "UTF-8");
820
821 String body = CreateMarkup(document);
822
823 http_body = EncodedFormData::Create(
824 UTF8Encoding().Encode(body, WTF::kNoUnencodables));
825 }
826
827 CreateRequest(std::move(http_body), exception_state);
828 }
829
send(const String & body,ExceptionState & exception_state)830 void XMLHttpRequest::send(const String& body, ExceptionState& exception_state) {
831 NETWORK_DVLOG(1) << this << " send() String " << body;
832
833 if (!InitSend(exception_state))
834 return;
835
836 scoped_refptr<EncodedFormData> http_body;
837
838 if (!body.IsNull() && AreMethodAndURLValidForSend()) {
839 http_body = EncodedFormData::Create(
840 UTF8Encoding().Encode(body, WTF::kNoUnencodables));
841 UpdateContentTypeAndCharset("text/plain;charset=UTF-8", "UTF-8");
842 }
843
844 CreateRequest(std::move(http_body), exception_state);
845 }
846
send(Blob * body,ExceptionState & exception_state)847 void XMLHttpRequest::send(Blob* body, ExceptionState& exception_state) {
848 NETWORK_DVLOG(1) << this << " send() Blob " << body->Uuid();
849
850 if (!InitSend(exception_state))
851 return;
852
853 scoped_refptr<EncodedFormData> http_body;
854
855 if (AreMethodAndURLValidForSend()) {
856 if (!HasContentTypeRequestHeader()) {
857 const String& blob_type = FetchUtils::NormalizeHeaderValue(body->type());
858 if (!blob_type.IsEmpty() && ParsedContentType(blob_type).IsValid()) {
859 SetRequestHeaderInternal(http_names::kContentType,
860 AtomicString(blob_type));
861 }
862 }
863
864 // FIXME: add support for uploading bundles.
865 http_body = EncodedFormData::Create();
866 if (body->HasBackingFile()) {
867 auto* file = To<File>(body);
868 if (!file->GetPath().IsEmpty())
869 http_body->AppendFile(file->GetPath(), file->LastModifiedTime());
870 else
871 NOTREACHED();
872 } else {
873 http_body->AppendBlob(body->Uuid(), body->GetBlobDataHandle());
874 }
875 }
876
877 CreateRequest(std::move(http_body), exception_state);
878 }
879
send(FormData * body,ExceptionState & exception_state)880 void XMLHttpRequest::send(FormData* body, ExceptionState& exception_state) {
881 NETWORK_DVLOG(1) << this << " send() FormData " << body;
882
883 if (!InitSend(exception_state))
884 return;
885
886 scoped_refptr<EncodedFormData> http_body;
887
888 if (AreMethodAndURLValidForSend()) {
889 http_body = body->EncodeMultiPartFormData();
890
891 // TODO (sof): override any author-provided charset= in the
892 // content type value to UTF-8 ?
893 if (!HasContentTypeRequestHeader()) {
894 AtomicString content_type =
895 AtomicString("multipart/form-data; boundary=") +
896 FetchUtils::NormalizeHeaderValue(http_body->Boundary().data());
897 SetRequestHeaderInternal(http_names::kContentType, content_type);
898 }
899 }
900
901 CreateRequest(std::move(http_body), exception_state);
902 }
903
send(URLSearchParams * body,ExceptionState & exception_state)904 void XMLHttpRequest::send(URLSearchParams* body,
905 ExceptionState& exception_state) {
906 NETWORK_DVLOG(1) << this << " send() URLSearchParams " << body;
907
908 if (!InitSend(exception_state))
909 return;
910
911 scoped_refptr<EncodedFormData> http_body;
912
913 if (AreMethodAndURLValidForSend()) {
914 http_body = body->ToEncodedFormData();
915 UpdateContentTypeAndCharset(
916 "application/x-www-form-urlencoded;charset=UTF-8", "UTF-8");
917 }
918
919 CreateRequest(std::move(http_body), exception_state);
920 }
921
send(DOMArrayBuffer * body,ExceptionState & exception_state)922 void XMLHttpRequest::send(DOMArrayBuffer* body,
923 ExceptionState& exception_state) {
924 NETWORK_DVLOG(1) << this << " send() ArrayBuffer " << body;
925
926 SendBytesData(body->Data(), body->ByteLength(), exception_state);
927 }
928
send(DOMArrayBufferView * body,ExceptionState & exception_state)929 void XMLHttpRequest::send(DOMArrayBufferView* body,
930 ExceptionState& exception_state) {
931 NETWORK_DVLOG(1) << this << " send() ArrayBufferView " << body;
932
933 SendBytesData(body->BaseAddress(), body->byteLength(), exception_state);
934 }
935
SendBytesData(const void * data,size_t length,ExceptionState & exception_state)936 void XMLHttpRequest::SendBytesData(const void* data,
937 size_t length,
938 ExceptionState& exception_state) {
939 if (!InitSend(exception_state))
940 return;
941
942 scoped_refptr<EncodedFormData> http_body;
943
944 if (AreMethodAndURLValidForSend()) {
945 http_body = EncodedFormData::Create(data, length);
946 }
947
948 CreateRequest(std::move(http_body), exception_state);
949 }
950
SendForInspectorXHRReplay(scoped_refptr<EncodedFormData> form_data,ExceptionState & exception_state)951 void XMLHttpRequest::SendForInspectorXHRReplay(
952 scoped_refptr<EncodedFormData> form_data,
953 ExceptionState& exception_state) {
954 CreateRequest(form_data ? form_data->DeepCopy() : nullptr, exception_state);
955 if (exception_state.HadException()) {
956 CHECK(IsDOMExceptionCode(exception_state.Code()));
957 exception_code_ = exception_state.CodeAs<DOMExceptionCode>();
958 }
959 }
960
ThrowForLoadFailureIfNeeded(ExceptionState & exception_state,const String & reason)961 void XMLHttpRequest::ThrowForLoadFailureIfNeeded(
962 ExceptionState& exception_state,
963 const String& reason) {
964 if (error_ && exception_code_ == DOMExceptionCode::kNoError)
965 exception_code_ = DOMExceptionCode::kNetworkError;
966
967 if (exception_code_ == DOMExceptionCode::kNoError)
968 return;
969
970 StringBuilder message;
971 message.Append("Failed to load '");
972 message.Append(url_.ElidedString());
973 message.Append('\'');
974 if (reason.IsNull()) {
975 message.Append('.');
976 } else {
977 message.Append(": ");
978 message.Append(reason);
979 }
980
981 exception_state.ThrowDOMException(exception_code_, message.ToString());
982 }
983
CreateRequest(scoped_refptr<EncodedFormData> http_body,ExceptionState & exception_state)984 void XMLHttpRequest::CreateRequest(scoped_refptr<EncodedFormData> http_body,
985 ExceptionState& exception_state) {
986 // Only GET request is supported for blob URL.
987 if (url_.ProtocolIs("blob") && method_ != http_names::kGET) {
988 HandleNetworkError();
989
990 if (!async_) {
991 ThrowForLoadFailureIfNeeded(
992 exception_state,
993 "'GET' is the only method allowed for 'blob:' URLs.");
994 }
995 return;
996 }
997
998 if (url_.ProtocolIs("ftp")) {
999 LogConsoleError(GetExecutionContext(), "FTP is not supported.");
1000 HandleNetworkError();
1001 if (!async_) {
1002 ThrowForLoadFailureIfNeeded(
1003 exception_state, "Making a request to a FTP URL is not supported.");
1004 }
1005 return;
1006 }
1007
1008 DCHECK(GetExecutionContext());
1009 ExecutionContext& execution_context = *GetExecutionContext();
1010
1011 send_flag_ = true;
1012 // The presence of upload event listeners forces us to use preflighting
1013 // because POSTing to an URL that does not permit cross origin requests should
1014 // look exactly like POSTing to an URL that does not respond at all.
1015 // Also, only async requests support upload progress events.
1016 bool upload_events = false;
1017 if (async_) {
1018 probe::AsyncTaskScheduled(&execution_context, "XMLHttpRequest.send",
1019 &async_task_id_);
1020 DispatchProgressEvent(event_type_names::kLoadstart, 0, 0);
1021 // Event handler could have invalidated this send operation,
1022 // (re)setting the send flag and/or initiating another send
1023 // operation; leave quietly if so.
1024 if (!send_flag_ || loader_)
1025 return;
1026 if (http_body && upload_) {
1027 upload_events = upload_->HasEventListeners();
1028 upload_->DispatchEvent(*ProgressEvent::Create(
1029 event_type_names::kLoadstart, true, 0, http_body->SizeInBytes()));
1030 // See above.
1031 if (!send_flag_ || loader_)
1032 return;
1033 }
1034 }
1035
1036 // We also remember whether upload events should be allowed for this request
1037 // in case the upload listeners are added after the request is started.
1038 upload_events_allowed_ =
1039 GetExecutionContext()->GetSecurityOrigin()->CanRequest(url_) ||
1040 (isolated_world_security_origin_ &&
1041 isolated_world_security_origin_->CanRequest(url_)) ||
1042 upload_events || !cors::IsCorsSafelistedMethod(method_) ||
1043 !cors::ContainsOnlyCorsSafelistedHeaders(request_headers_);
1044
1045 ResourceRequest request(url_);
1046 request.SetRequestorOrigin(GetExecutionContext()->GetSecurityOrigin());
1047 request.SetIsolatedWorldOrigin(isolated_world_security_origin_);
1048 request.SetHttpMethod(method_);
1049 request.SetRequestContext(mojom::blink::RequestContextType::XML_HTTP_REQUEST);
1050 request.SetFetchLikeAPI(true);
1051 request.SetMode(upload_events
1052 ? network::mojom::RequestMode::kCorsWithForcedPreflight
1053 : network::mojom::RequestMode::kCors);
1054 request.SetCredentialsMode(
1055 with_credentials_ ? network::mojom::CredentialsMode::kInclude
1056 : network::mojom::CredentialsMode::kSameOrigin);
1057 request.SetSkipServiceWorker(world_ && world_->IsIsolatedWorld());
1058 request.SetExternalRequestStateFromRequestorAddressSpace(
1059 execution_context.AddressSpace());
1060 if (trust_token_params_)
1061 request.SetTrustTokenParams(*trust_token_params_);
1062
1063 probe::WillLoadXHR(&execution_context, method_, url_, async_,
1064 request_headers_, with_credentials_);
1065
1066 if (http_body) {
1067 DCHECK_NE(method_, http_names::kGET);
1068 DCHECK_NE(method_, http_names::kHEAD);
1069 request.SetHttpBody(std::move(http_body));
1070 }
1071
1072 if (request_headers_.size() > 0)
1073 request.AddHTTPHeaderFields(request_headers_);
1074
1075 ResourceLoaderOptions resource_loader_options(world_);
1076 resource_loader_options.initiator_info.name =
1077 fetch_initiator_type_names::kXmlhttprequest;
1078 if (blob_url_loader_factory_) {
1079 resource_loader_options.url_loader_factory =
1080 base::MakeRefCounted<base::RefCountedData<
1081 mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>>>(
1082 std::move(blob_url_loader_factory_));
1083 }
1084
1085 // When responseType is set to "blob", we redirect the downloaded data to a
1086 // blob directly, except for data: URLs, since those are loaded by
1087 // renderer side code, and don't support being downloaded to a blob.
1088 downloading_to_blob_ =
1089 GetResponseTypeCode() == kResponseTypeBlob && !url_.ProtocolIsData();
1090 if (downloading_to_blob_) {
1091 request.SetDownloadToBlob(true);
1092 resource_loader_options.data_buffering_policy = kDoNotBufferData;
1093 }
1094
1095 if (async_) {
1096 resource_loader_options.data_buffering_policy = kDoNotBufferData;
1097 }
1098
1099 if (async_) {
1100 UseCounter::Count(&execution_context,
1101 WebFeature::kXMLHttpRequestAsynchronous);
1102 if (upload_)
1103 request.SetReportUploadProgress(true);
1104
1105 // TODO(yhirano): Turn this CHECK into DCHECK: see https://crbug.com/570946.
1106 CHECK(!loader_);
1107 DCHECK(send_flag_);
1108 } else {
1109 // Use count for XHR synchronous requests.
1110 UseCounter::Count(&execution_context, WebFeature::kXMLHttpRequestSynchronous);
1111 if (auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext())) {
1112 if (Frame* frame = window->GetFrame()) {
1113 if (frame->IsMainFrame()) {
1114 UseCounter::Count(&execution_context,
1115 WebFeature::kXMLHttpRequestSynchronousInMainFrame);
1116 } else if (frame->IsCrossOriginToMainFrame()) {
1117 UseCounter::Count(
1118 &execution_context,
1119 WebFeature::kXMLHttpRequestSynchronousInCrossOriginSubframe);
1120 } else {
1121 UseCounter::Count(
1122 &execution_context,
1123 WebFeature::kXMLHttpRequestSynchronousInSameOriginSubframe);
1124 }
1125 }
1126 if (PageDismissalScope::IsActive() &&
1127 !RuntimeEnabledFeatures::AllowSyncXHRInPageDismissalEnabled(
1128 &execution_context)) {
1129 HandleNetworkError();
1130 ThrowForLoadFailureIfNeeded(exception_state,
1131 "Synchronous XHR in page dismissal. See "
1132 "https://www.chromestatus.com/feature/"
1133 "4664843055398912 for more details.");
1134 return;
1135 }
1136 } else {
1137 DCHECK(execution_context.IsWorkerGlobalScope());
1138 UseCounter::Count(&execution_context,
1139 WebFeature::kXMLHttpRequestSynchronousInWorker);
1140 }
1141 resource_loader_options.synchronous_policy = kRequestSynchronously;
1142 }
1143
1144 exception_code_ = DOMExceptionCode::kNoError;
1145 error_ = false;
1146
1147 loader_ = MakeGarbageCollected<ThreadableLoader>(execution_context, this,
1148 resource_loader_options);
1149 loader_->SetTimeout(timeout_);
1150 base::TimeTicks start_time = base::TimeTicks::Now();
1151 loader_->Start(std::move(request));
1152
1153 if (!async_) {
1154 base::TimeDelta blocking_time = base::TimeTicks::Now() - start_time;
1155 if (execution_context.IsWindow()) {
1156 UMA_HISTOGRAM_MEDIUM_TIMES("XHR.Sync.BlockingTime.MainThread",
1157 blocking_time);
1158 } else {
1159 UMA_HISTOGRAM_MEDIUM_TIMES("XHR.Sync.BlockingTime.WorkerThread",
1160 blocking_time);
1161 }
1162
1163 ThrowForLoadFailureIfNeeded(exception_state, String());
1164 }
1165 }
1166
abort()1167 void XMLHttpRequest::abort() {
1168 NETWORK_DVLOG(1) << this << " abort()";
1169
1170 // internalAbort() clears the response. Save the data needed for
1171 // dispatching ProgressEvents.
1172 int64_t expected_length = response_.ExpectedContentLength();
1173 int64_t received_length = received_length_;
1174
1175 InternalAbort();
1176
1177 // The script never gets any chance to call abort() on a sync XHR between
1178 // send() call and transition to the DONE state. It's because a sync XHR
1179 // doesn't dispatch any event between them. So, if |m_async| is false, we
1180 // can skip the "request error steps" (defined in the XHR spec) without any
1181 // state check.
1182 //
1183 // FIXME: It's possible open() is invoked in internalAbort() and |m_async|
1184 // becomes true by that. We should implement more reliable treatment for
1185 // nested method invocations at some point.
1186 if (async_) {
1187 if ((state_ == kOpened && send_flag_) || state_ == kHeadersReceived ||
1188 state_ == kLoading) {
1189 DCHECK(!loader_);
1190 HandleRequestError(DOMExceptionCode::kNoError, event_type_names::kAbort,
1191 received_length, expected_length);
1192 }
1193 }
1194 if (state_ == kDone)
1195 state_ = kUnsent;
1196 }
1197
Dispose()1198 void XMLHttpRequest::Dispose() {
1199 progress_event_throttle_->Stop();
1200 InternalAbort();
1201 // TODO(yhirano): Remove this CHECK: see https://crbug.com/570946.
1202 CHECK(!loader_);
1203 }
1204
ClearVariablesForLoading()1205 void XMLHttpRequest::ClearVariablesForLoading() {
1206 if (blob_loader_) {
1207 blob_loader_->Cancel();
1208 blob_loader_ = nullptr;
1209 }
1210
1211 decoder_.reset();
1212
1213 if (response_document_parser_) {
1214 response_document_parser_->RemoveClient(this);
1215 response_document_parser_->Detach();
1216 response_document_parser_ = nullptr;
1217 }
1218 }
1219
InternalAbort()1220 void XMLHttpRequest::InternalAbort() {
1221 // If there is an existing pending abort event, cancel it. The caller of this
1222 // function is responsible for firing any events on XMLHttpRequest, if
1223 // needed.
1224 pending_abort_event_.Cancel();
1225
1226 // Fast path for repeated internalAbort()s; this
1227 // will happen if an XHR object is notified of context
1228 // destruction followed by finalization.
1229 if (error_ && !loader_)
1230 return;
1231
1232 error_ = true;
1233
1234 if (response_document_parser_ && !response_document_parser_->IsStopped())
1235 response_document_parser_->StopParsing();
1236
1237 ClearVariablesForLoading();
1238
1239 ClearResponse();
1240 ClearRequest();
1241
1242 if (!loader_)
1243 return;
1244
1245 ThreadableLoader* loader = loader_.Release();
1246 loader->Cancel();
1247
1248 DCHECK(!loader_);
1249 }
1250
ClearResponse()1251 void XMLHttpRequest::ClearResponse() {
1252 // FIXME: when we add the support for multi-part XHR, we will have to
1253 // be careful with this initialization.
1254 received_length_ = 0;
1255
1256 response_ = ResourceResponse();
1257
1258 response_text_.Clear();
1259
1260 parsed_response_ = false;
1261 response_document_ = nullptr;
1262
1263 response_blob_ = nullptr;
1264
1265 length_downloaded_to_blob_ = 0;
1266 downloading_to_blob_ = false;
1267
1268 // These variables may referred by the response accessors. So, we can clear
1269 // this only when we clear the response holder variables above.
1270 binary_response_builder_ = nullptr;
1271 response_array_buffer_.Clear();
1272 response_array_buffer_failure_ = false;
1273
1274 ReportMemoryUsageToV8();
1275 }
1276
ClearRequest()1277 void XMLHttpRequest::ClearRequest() {
1278 request_headers_.Clear();
1279 }
1280
DispatchProgressEvent(const AtomicString & type,int64_t received_length,int64_t expected_length)1281 void XMLHttpRequest::DispatchProgressEvent(const AtomicString& type,
1282 int64_t received_length,
1283 int64_t expected_length) {
1284 bool length_computable =
1285 expected_length > 0 && received_length <= expected_length;
1286 uint64_t loaded =
1287 received_length >= 0 ? static_cast<uint64_t>(received_length) : 0;
1288 uint64_t total =
1289 length_computable ? static_cast<uint64_t>(expected_length) : 0;
1290
1291 ExecutionContext* context = GetExecutionContext();
1292 probe::AsyncTask async_task(
1293 context, &async_task_id_,
1294 type == event_type_names::kLoadend ? nullptr : "progress", async_);
1295 progress_event_throttle_->DispatchProgressEvent(type, length_computable,
1296 loaded, total);
1297 }
1298
DispatchProgressEventFromSnapshot(const AtomicString & type)1299 void XMLHttpRequest::DispatchProgressEventFromSnapshot(
1300 const AtomicString& type) {
1301 DispatchProgressEvent(type, received_length_,
1302 response_.ExpectedContentLength());
1303 }
1304
HandleNetworkError()1305 void XMLHttpRequest::HandleNetworkError() {
1306 NETWORK_DVLOG(1) << this << " handleNetworkError()";
1307
1308 // Response is cleared next, save needed progress event data.
1309 int64_t expected_length = response_.ExpectedContentLength();
1310 int64_t received_length = received_length_;
1311
1312 InternalAbort();
1313
1314 HandleRequestError(DOMExceptionCode::kNetworkError, event_type_names::kError,
1315 received_length, expected_length);
1316 }
1317
HandleDidCancel()1318 void XMLHttpRequest::HandleDidCancel() {
1319 NETWORK_DVLOG(1) << this << " handleDidCancel()";
1320
1321 // Response is cleared next, save needed progress event data.
1322 int64_t expected_length = response_.ExpectedContentLength();
1323 int64_t received_length = received_length_;
1324
1325 InternalAbort();
1326
1327 pending_abort_event_ = PostCancellableTask(
1328 *GetExecutionContext()->GetTaskRunner(TaskType::kNetworking), FROM_HERE,
1329 WTF::Bind(&XMLHttpRequest::HandleRequestError, WrapPersistent(this),
1330 DOMExceptionCode::kAbortError, event_type_names::kAbort,
1331 received_length, expected_length));
1332 }
1333
HandleRequestError(DOMExceptionCode exception_code,const AtomicString & type,int64_t received_length,int64_t expected_length)1334 void XMLHttpRequest::HandleRequestError(DOMExceptionCode exception_code,
1335 const AtomicString& type,
1336 int64_t received_length,
1337 int64_t expected_length) {
1338 NETWORK_DVLOG(1) << this << " handleRequestError()";
1339
1340 probe::DidFinishXHR(GetExecutionContext(), this);
1341
1342 send_flag_ = false;
1343 if (!async_) {
1344 DCHECK_NE(exception_code, DOMExceptionCode::kNoError);
1345 state_ = kDone;
1346 exception_code_ = exception_code;
1347 return;
1348 }
1349
1350 // With m_error set, the state change steps are minimal: any pending
1351 // progress event is flushed + a readystatechange is dispatched.
1352 // No new progress events dispatched; as required, that happens at
1353 // the end here.
1354 DCHECK(error_);
1355 ChangeState(kDone);
1356
1357 if (!upload_complete_) {
1358 upload_complete_ = true;
1359 if (upload_ && upload_events_allowed_)
1360 upload_->HandleRequestError(type);
1361 }
1362
1363 // Note: The below event dispatch may be called while |hasPendingActivity() ==
1364 // false|, when |handleRequestError| is called after |internalAbort()|. This
1365 // is safe, however, as |this| will be kept alive from a strong ref
1366 // |Event::m_target|.
1367 DispatchProgressEvent(type, received_length, expected_length);
1368 DispatchProgressEvent(event_type_names::kLoadend, received_length,
1369 expected_length);
1370 }
1371
1372 // https://xhr.spec.whatwg.org/#the-overridemimetype()-method
overrideMimeType(const AtomicString & mime_type,ExceptionState & exception_state)1373 void XMLHttpRequest::overrideMimeType(const AtomicString& mime_type,
1374 ExceptionState& exception_state) {
1375 if (state_ == kLoading || state_ == kDone) {
1376 exception_state.ThrowDOMException(
1377 DOMExceptionCode::kInvalidStateError,
1378 "MimeType cannot be overridden when the state is LOADING or DONE.");
1379 return;
1380 }
1381
1382 mime_type_override_ = "application/octet-stream";
1383 if (ParsedContentType(mime_type).IsValid())
1384 mime_type_override_ = mime_type;
1385 }
1386
1387 // https://xhr.spec.whatwg.org/#the-setrequestheader()-method
setRequestHeader(const AtomicString & name,const AtomicString & value,ExceptionState & exception_state)1388 void XMLHttpRequest::setRequestHeader(const AtomicString& name,
1389 const AtomicString& value,
1390 ExceptionState& exception_state) {
1391 // "1. If |state| is not "opened", throw an InvalidStateError exception.
1392 // 2. If the send() flag is set, throw an InvalidStateError exception."
1393 if (state_ != kOpened || send_flag_) {
1394 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
1395 "The object's state must be OPENED.");
1396 return;
1397 }
1398
1399 // "3. Normalize |value|."
1400 const String normalized_value = FetchUtils::NormalizeHeaderValue(value);
1401
1402 // "4. If |name| is not a name or |value| is not a value, throw a SyntaxError
1403 // exception."
1404 if (!IsValidHTTPToken(name)) {
1405 exception_state.ThrowDOMException(
1406 DOMExceptionCode::kSyntaxError,
1407 "'" + name + "' is not a valid HTTP header field name.");
1408 return;
1409 }
1410 if (!IsValidHTTPHeaderValue(normalized_value)) {
1411 exception_state.ThrowDOMException(
1412 DOMExceptionCode::kSyntaxError,
1413 "'" + normalized_value + "' is not a valid HTTP header field value.");
1414 return;
1415 }
1416
1417 // "5. Terminate these steps if |name| is a forbidden header name."
1418 // No script (privileged or not) can set unsafe headers.
1419 if (cors::IsForbiddenHeaderName(name)) {
1420 LogConsoleError(GetExecutionContext(),
1421 "Refused to set unsafe header \"" + name + "\"");
1422 return;
1423 }
1424
1425 // "6. Combine |name|/|value| in author request headers."
1426 SetRequestHeaderInternal(name, AtomicString(normalized_value));
1427 }
1428
SetRequestHeaderInternal(const AtomicString & name,const AtomicString & value)1429 void XMLHttpRequest::SetRequestHeaderInternal(const AtomicString& name,
1430 const AtomicString& value) {
1431 DCHECK_EQ(value, FetchUtils::NormalizeHeaderValue(value))
1432 << "Header values must be normalized";
1433 HTTPHeaderMap::AddResult result = request_headers_.Add(name, value);
1434 if (!result.is_new_entry) {
1435 AtomicString new_value = result.stored_value->value + ", " + value;
1436 result.stored_value->value = new_value;
1437 }
1438 }
1439
setTrustToken(const TrustToken * trust_token,ExceptionState & exception_state)1440 void XMLHttpRequest::setTrustToken(const TrustToken* trust_token,
1441 ExceptionState& exception_state) {
1442 // These precondition checks are copied from |setRequestHeader|.
1443 if (state_ != kOpened || send_flag_) {
1444 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
1445 "The object's state must be OPENED.");
1446 return;
1447 }
1448
1449 auto params = network::mojom::blink::TrustTokenParams::New();
1450 if (!ConvertTrustTokenToMojom(*trust_token, &exception_state, params.get())) {
1451 DCHECK(exception_state.HadException());
1452 return;
1453 }
1454
1455 bool operation_requires_feature_policy =
1456 params->type ==
1457 network::mojom::blink::TrustTokenOperationType::kRedemption ||
1458 params->type == network::mojom::blink::TrustTokenOperationType::kSigning;
1459 if (operation_requires_feature_policy &&
1460 !GetExecutionContext()->IsFeatureEnabled(
1461 mojom::blink::FeaturePolicyFeature::kTrustTokenRedemption)) {
1462 exception_state.ThrowDOMException(
1463 DOMExceptionCode::kNotAllowedError,
1464 "Trust Tokens redemption and signing require the "
1465 "trust-token-redemption Feature Policy feature.");
1466 return;
1467 }
1468
1469 if (params->type ==
1470 network::mojom::blink::TrustTokenOperationType::kIssuance &&
1471 !IsTrustTokenIssuanceAvailableInExecutionContext(
1472 *GetExecutionContext())) {
1473 exception_state.ThrowDOMException(
1474 DOMExceptionCode::kNotAllowedError,
1475 "Trust Tokens issuance is disabled except in "
1476 "contexts with the TrustTokens Origin Trial enabled.");
1477 return;
1478 }
1479
1480 trust_token_params_ = std::move(params);
1481 }
1482
HasContentTypeRequestHeader() const1483 bool XMLHttpRequest::HasContentTypeRequestHeader() const {
1484 return request_headers_.Find(http_names::kContentType) !=
1485 request_headers_.end();
1486 }
1487
getAllResponseHeaders() const1488 String XMLHttpRequest::getAllResponseHeaders() const {
1489 if (state_ < kHeadersReceived || error_)
1490 return "";
1491
1492 StringBuilder string_builder;
1493
1494 HTTPHeaderSet access_control_expose_header_set =
1495 cors::ExtractCorsExposedHeaderNamesList(
1496 with_credentials_ ? network::mojom::CredentialsMode::kInclude
1497 : network::mojom::CredentialsMode::kSameOrigin,
1498 response_);
1499
1500 // "Let |headers| be the result of sorting |initialHeaders| in ascending
1501 // order, with |a| being less than |b| if |a|’s name is legacy-uppercased-byte
1502 // less than |b|’s name."
1503 Vector<std::pair<String, String>> headers;
1504 // Although we omit some headers in |response_.HttpHeaderFields()| below,
1505 // we pre-allocate the buffer for performance.
1506 headers.ReserveInitialCapacity(response_.HttpHeaderFields().size());
1507 for (auto it = response_.HttpHeaderFields().begin();
1508 it != response_.HttpHeaderFields().end(); ++it) {
1509 // Hide any headers whose name is a forbidden response-header name.
1510 // This is required for all kinds of filtered responses.
1511 //
1512 // TODO: Consider removing canLoadLocalResources() call.
1513 // crbug.com/567527
1514 if (FetchUtils::IsForbiddenResponseHeaderName(it->key) &&
1515 !GetExecutionContext()->GetSecurityOrigin()->CanLoadLocalResources()) {
1516 continue;
1517 }
1518
1519 if (response_.GetType() == network::mojom::FetchResponseType::kCors &&
1520 !cors::IsCorsSafelistedResponseHeader(it->key) &&
1521 access_control_expose_header_set.find(it->key.Ascii()) ==
1522 access_control_expose_header_set.end()) {
1523 continue;
1524 }
1525
1526 headers.push_back(std::make_pair(it->key.UpperASCII(), it->value));
1527 }
1528 std::sort(headers.begin(), headers.end(),
1529 [](const std::pair<String, String>& x,
1530 const std::pair<String, String>& y) {
1531 return CodeUnitCompareLessThan(x.first, y.first);
1532 });
1533 for (const auto& header : headers) {
1534 string_builder.Append(header.first.LowerASCII());
1535 string_builder.Append(':');
1536 string_builder.Append(' ');
1537 string_builder.Append(header.second);
1538 string_builder.Append('\r');
1539 string_builder.Append('\n');
1540 }
1541
1542 return string_builder.ToString();
1543 }
1544
getResponseHeader(const AtomicString & name) const1545 const AtomicString& XMLHttpRequest::getResponseHeader(
1546 const AtomicString& name) const {
1547 if (state_ < kHeadersReceived || error_)
1548 return g_null_atom;
1549
1550 // See comment in getAllResponseHeaders above.
1551 if (FetchUtils::IsForbiddenResponseHeaderName(name) &&
1552 !GetExecutionContext()->GetSecurityOrigin()->CanLoadLocalResources()) {
1553 LogConsoleError(GetExecutionContext(),
1554 "Refused to get unsafe header \"" + name + "\"");
1555 return g_null_atom;
1556 }
1557
1558 HTTPHeaderSet access_control_expose_header_set =
1559 cors::ExtractCorsExposedHeaderNamesList(
1560 with_credentials_ ? network::mojom::CredentialsMode::kInclude
1561 : network::mojom::CredentialsMode::kSameOrigin,
1562 response_);
1563
1564 if (response_.GetType() == network::mojom::FetchResponseType::kCors &&
1565 !cors::IsCorsSafelistedResponseHeader(name) &&
1566 access_control_expose_header_set.find(name.Ascii()) ==
1567 access_control_expose_header_set.end()) {
1568 LogConsoleError(GetExecutionContext(),
1569 "Refused to get unsafe header \"" + name + "\"");
1570 return g_null_atom;
1571 }
1572 return response_.HttpHeaderField(name);
1573 }
1574
FinalResponseMIMEType() const1575 AtomicString XMLHttpRequest::FinalResponseMIMEType() const {
1576 AtomicString overridden_type =
1577 ExtractMIMETypeFromMediaType(mime_type_override_);
1578 if (!overridden_type.IsEmpty())
1579 return overridden_type;
1580
1581 if (response_.IsHTTP()) {
1582 return ExtractMIMETypeFromMediaType(
1583 response_.HttpHeaderField(http_names::kContentType));
1584 }
1585
1586 return response_.MimeType();
1587 }
1588
FinalResponseMIMETypeWithFallback() const1589 AtomicString XMLHttpRequest::FinalResponseMIMETypeWithFallback() const {
1590 AtomicString final_type = FinalResponseMIMEType();
1591 if (!final_type.IsEmpty())
1592 return final_type;
1593
1594 return AtomicString("text/xml");
1595 }
1596
1597 // https://xhr.spec.whatwg.org/#final-charset
FinalResponseCharset() const1598 WTF::TextEncoding XMLHttpRequest::FinalResponseCharset() const {
1599 // 1. Let label be null. [spec text]
1600 //
1601 // 2. If response MIME type's parameters["charset"] exists, then set label to
1602 // it. [spec text]
1603 String label = response_.TextEncodingName();
1604
1605 // 3. If override MIME type's parameters["charset"] exists, then set label to
1606 // it. [spec text]
1607 String override_response_charset =
1608 ExtractCharsetFromMediaType(mime_type_override_);
1609 if (!override_response_charset.IsEmpty())
1610 label = override_response_charset;
1611
1612 // 4. If label is null, then return null. [spec text]
1613 //
1614 // 5. Let encoding be the result of getting an encoding from label. [spec
1615 // text]
1616 //
1617 // 6. If encoding is failure, then return null. [spec text]
1618 //
1619 // 7. Return encoding. [spec text]
1620 //
1621 // We rely on WTF::TextEncoding() to return invalid TextEncoding for
1622 // null, empty, or invalid/unsupported |label|.
1623 return WTF::TextEncoding(label);
1624 }
1625
UpdateContentTypeAndCharset(const AtomicString & default_content_type,const String & charset)1626 void XMLHttpRequest::UpdateContentTypeAndCharset(
1627 const AtomicString& default_content_type,
1628 const String& charset) {
1629 // http://xhr.spec.whatwg.org/#the-send()-method step 4's concilliation of
1630 // "charset=" in any author-provided Content-Type: request header.
1631 String content_type = request_headers_.Get(http_names::kContentType);
1632 if (content_type.IsNull()) {
1633 SetRequestHeaderInternal(http_names::kContentType, default_content_type);
1634 return;
1635 }
1636 String original_content_type = content_type;
1637 ReplaceCharsetInMediaType(content_type, charset);
1638 request_headers_.Set(http_names::kContentType, AtomicString(content_type));
1639
1640 if (original_content_type != content_type) {
1641 UseCounter::Count(GetExecutionContext(), WebFeature::kReplaceCharsetInXHR);
1642 if (!EqualIgnoringASCIICase(original_content_type, content_type)) {
1643 UseCounter::Count(GetExecutionContext(),
1644 WebFeature::kReplaceCharsetInXHRIgnoringCase);
1645 }
1646 }
1647 }
1648
ResponseIsXML() const1649 bool XMLHttpRequest::ResponseIsXML() const {
1650 return MIMETypeRegistry::IsXMLMIMEType(FinalResponseMIMETypeWithFallback());
1651 }
1652
ResponseIsHTML() const1653 bool XMLHttpRequest::ResponseIsHTML() const {
1654 return EqualIgnoringASCIICase(FinalResponseMIMEType(), "text/html");
1655 }
1656
status() const1657 int XMLHttpRequest::status() const {
1658 if (state_ == kUnsent || state_ == kOpened || error_)
1659 return 0;
1660
1661 if (response_.HttpStatusCode())
1662 return response_.HttpStatusCode();
1663
1664 return 0;
1665 }
1666
statusText() const1667 String XMLHttpRequest::statusText() const {
1668 if (state_ == kUnsent || state_ == kOpened || error_)
1669 return String();
1670
1671 if (!response_.HttpStatusText().IsNull())
1672 return response_.HttpStatusText();
1673
1674 return String();
1675 }
1676
DidFail(const ResourceError & error)1677 void XMLHttpRequest::DidFail(const ResourceError& error) {
1678 NETWORK_DVLOG(1) << this << " didFail()";
1679 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1680
1681 // If we are already in an error state, for instance we called abort(), bail
1682 // out early.
1683 if (error_)
1684 return;
1685
1686 // Internally, access check violations are considered `cancellations`, but
1687 // at least the mixed-content and CSP specs require them to be surfaced as
1688 // network errors to the page. See:
1689 // [1] https://www.w3.org/TR/mixed-content/#algorithms,
1690 // [2] https://www.w3.org/TR/CSP3/#fetch-integration.
1691 if (error.IsCancellation() && !error.IsAccessCheck()) {
1692 HandleDidCancel();
1693 return;
1694 }
1695
1696 if (error.IsTimeout()) {
1697 HandleDidTimeout();
1698 return;
1699 }
1700
1701 if (error.TrustTokenOperationError() !=
1702 network::mojom::TrustTokenOperationStatus::kOk) {
1703 trust_token_operation_error_ =
1704 TrustTokenErrorToDOMException(error.TrustTokenOperationError());
1705 }
1706
1707 HandleNetworkError();
1708 }
1709
DidFailRedirectCheck()1710 void XMLHttpRequest::DidFailRedirectCheck() {
1711 NETWORK_DVLOG(1) << this << " didFailRedirectCheck()";
1712 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1713
1714 HandleNetworkError();
1715 }
1716
DidFinishLoading(uint64_t identifier)1717 void XMLHttpRequest::DidFinishLoading(uint64_t identifier) {
1718 NETWORK_DVLOG(1) << this << " didFinishLoading(" << identifier << ")";
1719 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1720
1721 if (error_)
1722 return;
1723
1724 if (state_ < kHeadersReceived)
1725 ChangeState(kHeadersReceived);
1726
1727 if (downloading_to_blob_ && response_type_code_ != kResponseTypeBlob &&
1728 response_blob_) {
1729 // In this case, we have sent the request with DownloadToBlob true,
1730 // but the user changed the response type after that. Hence we need to
1731 // read the response data and provide it to this object.
1732 blob_loader_ = MakeGarbageCollected<BlobLoader>(
1733 this, response_blob_->GetBlobDataHandle());
1734 } else {
1735 DidFinishLoadingInternal();
1736 }
1737 }
1738
DidFinishLoadingInternal()1739 void XMLHttpRequest::DidFinishLoadingInternal() {
1740 if (response_document_parser_) {
1741 // |DocumentParser::finish()| tells the parser that we have reached end of
1742 // the data. When using |HTMLDocumentParser|, which works asynchronously,
1743 // we do not have the complete document just after the
1744 // |DocumentParser::finish()| call. Wait for the parser to call us back in
1745 // |notifyParserStopped| to progress state.
1746 response_document_parser_->Finish();
1747 DCHECK(response_document_);
1748 return;
1749 }
1750
1751 if (decoder_) {
1752 auto text = decoder_->Flush();
1753 if (!text.IsEmpty() && !response_text_overflow_) {
1754 response_text_.Concat(isolate_, text);
1755 response_text_overflow_ = response_text_.IsEmpty();
1756 }
1757 }
1758
1759 ClearVariablesForLoading();
1760 EndLoading();
1761 }
1762
DidFinishLoadingFromBlob()1763 void XMLHttpRequest::DidFinishLoadingFromBlob() {
1764 NETWORK_DVLOG(1) << this << " didFinishLoadingFromBlob";
1765 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1766
1767 DidFinishLoadingInternal();
1768 }
1769
DidFailLoadingFromBlob()1770 void XMLHttpRequest::DidFailLoadingFromBlob() {
1771 NETWORK_DVLOG(1) << this << " didFailLoadingFromBlob()";
1772 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1773
1774 if (error_)
1775 return;
1776 HandleNetworkError();
1777 }
1778
NotifyParserStopped()1779 void XMLHttpRequest::NotifyParserStopped() {
1780 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1781
1782 // This should only be called when response document is parsed asynchronously.
1783 DCHECK(response_document_parser_);
1784 DCHECK(!response_document_parser_->IsParsing());
1785
1786 // Do nothing if we are called from |internalAbort()|.
1787 if (error_)
1788 return;
1789
1790 ClearVariablesForLoading();
1791
1792 if (!response_document_->WellFormed())
1793 response_document_ = nullptr;
1794
1795 parsed_response_ = true;
1796
1797 EndLoading();
1798 }
1799
EndLoading()1800 void XMLHttpRequest::EndLoading() {
1801 probe::DidFinishXHR(GetExecutionContext(), this);
1802
1803 if (loader_) {
1804 // Set |m_error| in order to suppress the cancel notification (see
1805 // XMLHttpRequest::didFail).
1806 base::AutoReset<bool> scope(&error_, true);
1807 loader_.Release()->Cancel();
1808 }
1809
1810 send_flag_ = false;
1811 ChangeState(kDone);
1812
1813 if (auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext())) {
1814 LocalFrame* frame = window->GetFrame();
1815 if (frame && cors::IsOkStatus(status()))
1816 frame->GetPage()->GetChromeClient().AjaxSucceeded(frame);
1817 }
1818 }
1819
DidSendData(uint64_t bytes_sent,uint64_t total_bytes_to_be_sent)1820 void XMLHttpRequest::DidSendData(uint64_t bytes_sent,
1821 uint64_t total_bytes_to_be_sent) {
1822 NETWORK_DVLOG(1) << this << " didSendData(" << bytes_sent << ", "
1823 << total_bytes_to_be_sent << ")";
1824 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1825
1826 if (!upload_)
1827 return;
1828
1829 if (upload_events_allowed_)
1830 upload_->DispatchProgressEvent(bytes_sent, total_bytes_to_be_sent);
1831
1832 if (bytes_sent == total_bytes_to_be_sent && !upload_complete_) {
1833 upload_complete_ = true;
1834 if (upload_events_allowed_) {
1835 upload_->DispatchEventAndLoadEnd(event_type_names::kLoad, true,
1836 bytes_sent, total_bytes_to_be_sent);
1837 }
1838 }
1839 }
1840
DidReceiveResponse(uint64_t identifier,const ResourceResponse & response)1841 void XMLHttpRequest::DidReceiveResponse(uint64_t identifier,
1842 const ResourceResponse& response) {
1843 // TODO(yhirano): Remove this CHECK: see https://crbug.com/570946.
1844 CHECK(&response);
1845
1846 NETWORK_DVLOG(1) << this << " didReceiveResponse(" << identifier << ")";
1847 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1848
1849 response_ = response;
1850 }
1851
ParseDocumentChunk(const char * data,unsigned len)1852 void XMLHttpRequest::ParseDocumentChunk(const char* data, unsigned len) {
1853 if (!response_document_parser_) {
1854 DCHECK(!response_document_);
1855 InitResponseDocument();
1856 if (!response_document_)
1857 return;
1858
1859 response_document_parser_ = response_document_->ImplicitOpen(
1860 RuntimeEnabledFeatures::ForceSynchronousHTMLParsingEnabled()
1861 ? kAllowDeferredParsing
1862 : kAllowAsynchronousParsing);
1863 response_document_parser_->AddClient(this);
1864 }
1865 DCHECK(response_document_parser_);
1866
1867 if (response_document_parser_->NeedsDecoder())
1868 response_document_parser_->SetDecoder(CreateDecoder());
1869
1870 response_document_parser_->AppendBytes(data, len);
1871 }
1872
CreateDecoder() const1873 std::unique_ptr<TextResourceDecoder> XMLHttpRequest::CreateDecoder() const {
1874 const TextResourceDecoderOptions decoder_options_for_utf8_plain_text(
1875 TextResourceDecoderOptions::kPlainTextContent, UTF8Encoding());
1876 if (response_type_code_ == kResponseTypeJSON) {
1877 return std::make_unique<TextResourceDecoder>(
1878 decoder_options_for_utf8_plain_text);
1879 }
1880
1881 WTF::TextEncoding final_response_charset = FinalResponseCharset();
1882 if (final_response_charset.IsValid()) {
1883 // If the final charset is given and valid, use the charset without
1884 // sniffing the content.
1885 return std::make_unique<TextResourceDecoder>(TextResourceDecoderOptions(
1886 TextResourceDecoderOptions::kPlainTextContent, final_response_charset));
1887 }
1888
1889 TextResourceDecoderOptions decoder_options_for_xml(
1890 TextResourceDecoderOptions::kXMLContent);
1891 // Don't stop on encoding errors, unlike it is done for other kinds
1892 // of XML resources. This matches the behavior of previous WebKit
1893 // versions, Firefox and Opera.
1894 decoder_options_for_xml.SetUseLenientXMLDecoding();
1895
1896 switch (response_type_code_) {
1897 case kResponseTypeDefault:
1898 if (ResponseIsXML())
1899 return std::make_unique<TextResourceDecoder>(decoder_options_for_xml);
1900 FALLTHROUGH;
1901 case kResponseTypeText:
1902 return std::make_unique<TextResourceDecoder>(
1903 decoder_options_for_utf8_plain_text);
1904 case kResponseTypeDocument:
1905 if (ResponseIsHTML()) {
1906 return std::make_unique<TextResourceDecoder>(TextResourceDecoderOptions(
1907 TextResourceDecoderOptions::kHTMLContent, UTF8Encoding()));
1908 }
1909 return std::make_unique<TextResourceDecoder>(decoder_options_for_xml);
1910 case kResponseTypeJSON:
1911 case kResponseTypeBlob:
1912 case kResponseTypeArrayBuffer:
1913 NOTREACHED();
1914 break;
1915 }
1916 NOTREACHED();
1917 return nullptr;
1918 }
1919
DidReceiveData(const char * data,unsigned len)1920 void XMLHttpRequest::DidReceiveData(const char* data, unsigned len) {
1921 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1922 if (error_)
1923 return;
1924
1925 DCHECK(!downloading_to_blob_ || blob_loader_);
1926
1927 if (state_ < kHeadersReceived)
1928 ChangeState(kHeadersReceived);
1929
1930 // We need to check for |m_error| again, because |changeState| may trigger
1931 // readystatechange, and user javascript can cause |abort()|.
1932 if (error_)
1933 return;
1934
1935 if (!len)
1936 return;
1937
1938 if (response_type_code_ == kResponseTypeDocument && ResponseIsHTML()) {
1939 ParseDocumentChunk(data, len);
1940 } else if (response_type_code_ == kResponseTypeDefault ||
1941 response_type_code_ == kResponseTypeText ||
1942 response_type_code_ == kResponseTypeJSON ||
1943 response_type_code_ == kResponseTypeDocument) {
1944 if (!decoder_)
1945 decoder_ = CreateDecoder();
1946
1947 auto text = decoder_->Decode(data, len);
1948 if (!text.IsEmpty() && !response_text_overflow_) {
1949 response_text_.Concat(isolate_, text);
1950 response_text_overflow_ = response_text_.IsEmpty();
1951 }
1952 } else if (response_type_code_ == kResponseTypeArrayBuffer ||
1953 response_type_code_ == kResponseTypeBlob) {
1954 // Buffer binary data.
1955 if (!binary_response_builder_)
1956 binary_response_builder_ = SharedBuffer::Create();
1957 binary_response_builder_->Append(data, len);
1958 ReportMemoryUsageToV8();
1959 }
1960
1961 if (blob_loader_) {
1962 // In this case, the data is provided by m_blobLoader. As progress
1963 // events are already fired, we should return here.
1964 return;
1965 }
1966 TrackProgress(len);
1967 }
1968
DidDownloadData(uint64_t data_length)1969 void XMLHttpRequest::DidDownloadData(uint64_t data_length) {
1970 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1971 if (error_)
1972 return;
1973
1974 DCHECK(downloading_to_blob_);
1975
1976 if (state_ < kHeadersReceived)
1977 ChangeState(kHeadersReceived);
1978
1979 if (!data_length)
1980 return;
1981
1982 // readystatechange event handler may do something to put this XHR in error
1983 // state. We need to check m_error again here.
1984 if (error_)
1985 return;
1986
1987 length_downloaded_to_blob_ += data_length;
1988 ReportMemoryUsageToV8();
1989
1990 TrackProgress(data_length);
1991 }
1992
DidDownloadToBlob(scoped_refptr<BlobDataHandle> blob)1993 void XMLHttpRequest::DidDownloadToBlob(scoped_refptr<BlobDataHandle> blob) {
1994 ScopedEventDispatchProtect protect(&event_dispatch_recursion_level_);
1995 if (error_)
1996 return;
1997
1998 DCHECK(downloading_to_blob_);
1999
2000 if (!blob) {
2001 // This generally indicates not enough quota for the blob, or somehow
2002 // failing to write the blob to disk. Treat this as a network error.
2003 // TODO(mek): Maybe print a more helpful/specific error message to the
2004 // console, to distinguish this from true network errors?
2005 // TODO(mek): This would best be treated as a network error, but for sync
2006 // requests this could also just mean succesfully reading a zero-byte blob
2007 // from a misbehaving URLLoader, so for now just ignore this and don't do
2008 // anything, which will result in an empty blob being returned by XHR.
2009 // HandleNetworkError();
2010 } else {
2011 // Fix content type if overrides or fallbacks are in effect.
2012 String mime_type = FinalResponseMIMETypeWithFallback().LowerASCII();
2013 if (blob->GetType() != mime_type) {
2014 auto blob_size = blob->size();
2015 auto blob_data = std::make_unique<BlobData>();
2016 blob_data->SetContentType(mime_type);
2017 blob_data->AppendBlob(std::move(blob), 0, blob_size);
2018 response_blob_ = MakeGarbageCollected<Blob>(
2019 BlobDataHandle::Create(std::move(blob_data), blob_size));
2020 } else {
2021 response_blob_ = MakeGarbageCollected<Blob>(std::move(blob));
2022 }
2023 }
2024 }
2025
HandleDidTimeout()2026 void XMLHttpRequest::HandleDidTimeout() {
2027 NETWORK_DVLOG(1) << this << " handleDidTimeout()";
2028
2029 // Response is cleared next, save needed progress event data.
2030 int64_t expected_length = response_.ExpectedContentLength();
2031 int64_t received_length = received_length_;
2032
2033 InternalAbort();
2034
2035 HandleRequestError(DOMExceptionCode::kTimeoutError,
2036 event_type_names::kTimeout, received_length,
2037 expected_length);
2038 }
2039
ContextDestroyed()2040 void XMLHttpRequest::ContextDestroyed() {
2041 Dispose();
2042
2043 // In case we are in the middle of send() function, unset the send flag to
2044 // stop the operation.
2045 send_flag_ = false;
2046 }
2047
HasPendingActivity() const2048 bool XMLHttpRequest::HasPendingActivity() const {
2049 // Neither this object nor the JavaScript wrapper should be deleted while
2050 // a request is in progress because we need to keep the listeners alive,
2051 // and they are referenced by the JavaScript wrapper.
2052 // |m_loader| is non-null while request is active and ThreadableLoaderClient
2053 // callbacks may be called, and |m_responseDocumentParser| is non-null while
2054 // DocumentParserClient callbacks may be called.
2055 if (loader_ || response_document_parser_)
2056 return true;
2057 return event_dispatch_recursion_level_ > 0;
2058 }
2059
InterfaceName() const2060 const AtomicString& XMLHttpRequest::InterfaceName() const {
2061 return event_target_names::kXMLHttpRequest;
2062 }
2063
GetExecutionContext() const2064 ExecutionContext* XMLHttpRequest::GetExecutionContext() const {
2065 return ExecutionContextLifecycleObserver::GetExecutionContext();
2066 }
2067
ReportMemoryUsageToV8()2068 void XMLHttpRequest::ReportMemoryUsageToV8() {
2069 // binary_response_builder_
2070 size_t size = binary_response_builder_ ? binary_response_builder_->size() : 0;
2071 int64_t diff =
2072 static_cast<int64_t>(size) -
2073 static_cast<int64_t>(binary_response_builder_last_reported_size_);
2074 binary_response_builder_last_reported_size_ = size;
2075
2076 // Blob (downloading_to_blob_, length_downloaded_to_blob_)
2077 diff += static_cast<int64_t>(length_downloaded_to_blob_) -
2078 static_cast<int64_t>(length_downloaded_to_blob_last_reported_);
2079 length_downloaded_to_blob_last_reported_ = length_downloaded_to_blob_;
2080
2081 if (diff)
2082 isolate_->AdjustAmountOfExternalAllocatedMemory(diff);
2083 }
2084
Trace(Visitor * visitor) const2085 void XMLHttpRequest::Trace(Visitor* visitor) const {
2086 visitor->Trace(response_blob_);
2087 visitor->Trace(loader_);
2088 visitor->Trace(response_document_);
2089 visitor->Trace(response_document_parser_);
2090 visitor->Trace(response_array_buffer_);
2091 visitor->Trace(progress_event_throttle_);
2092 visitor->Trace(upload_);
2093 visitor->Trace(blob_loader_);
2094 visitor->Trace(response_text_);
2095 visitor->Trace(trust_token_operation_error_);
2096 XMLHttpRequestEventTarget::Trace(visitor);
2097 ThreadableLoaderClient::Trace(visitor);
2098 DocumentParserClient::Trace(visitor);
2099 ExecutionContextLifecycleObserver::Trace(visitor);
2100 }
2101
operator <<(std::ostream & ostream,const XMLHttpRequest * xhr)2102 std::ostream& operator<<(std::ostream& ostream, const XMLHttpRequest* xhr) {
2103 return ostream << "XMLHttpRequest " << static_cast<const void*>(xhr);
2104 }
2105
2106 } // namespace blink
2107