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