| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/beacon/navigator_beacon.h" |
| |
| #include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview_blob_formdata_readablestream_urlsearchparams_usvstring.h" |
| #include "third_party/blink/renderer/core/execution_context/execution_context.h" |
| #include "third_party/blink/renderer/core/fileapi/blob.h" |
| #include "third_party/blink/renderer/core/frame/local_dom_window.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/frame/web_feature.h" |
| #include "third_party/blink/renderer/core/html/forms/form_data.h" |
| #include "third_party/blink/renderer/core/loader/ping_loader.h" |
| #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/bindings/script_state.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" |
| #include "third_party/blink/renderer/platform/loader/cors/cors.h" |
| |
| namespace blink { |
| |
| NavigatorBeacon::NavigatorBeacon(Navigator& navigator) |
| : Supplement<Navigator>(navigator) {} |
| |
| NavigatorBeacon::~NavigatorBeacon() = default; |
| |
| void NavigatorBeacon::Trace(Visitor* visitor) const { |
| Supplement<Navigator>::Trace(visitor); |
| } |
| |
| const char NavigatorBeacon::kSupplementName[] = "NavigatorBeacon"; |
| |
| NavigatorBeacon& NavigatorBeacon::From(Navigator& navigator) { |
| NavigatorBeacon* supplement = |
| Supplement<Navigator>::From<NavigatorBeacon>(navigator); |
| if (!supplement) { |
| supplement = MakeGarbageCollected<NavigatorBeacon>(navigator); |
| ProvideTo(navigator, supplement); |
| } |
| return *supplement; |
| } |
| |
| bool NavigatorBeacon::CanSendBeacon(ExecutionContext* context, |
| const KURL& url, |
| ExceptionState& exception_state) { |
| if (!url.IsValid()) { |
| exception_state.ThrowTypeError( |
| "The URL argument is ill-formed or unsupported."); |
| return false; |
| } |
| // For now, only support HTTP and related. |
| if (!url.ProtocolIsInHTTPFamily()) { |
| exception_state.ThrowTypeError("Beacons are only supported over HTTP(S)."); |
| return false; |
| } |
| |
| // If detached, do not allow sending a Beacon. |
| return GetSupplementable()->DomWindow(); |
| } |
| |
| bool NavigatorBeacon::sendBeacon( |
| ScriptState* script_state, |
| Navigator& navigator, |
| const String& url_string, |
| const V8UnionReadableStreamOrXMLHttpRequestBodyInit* data, |
| ExceptionState& exception_state) { |
| return NavigatorBeacon::From(navigator).SendBeaconImpl( |
| script_state, url_string, data, exception_state); |
| } |
| |
| bool NavigatorBeacon::SendBeaconImpl( |
| ScriptState* script_state, |
| const String& url_string, |
| const V8UnionReadableStreamOrXMLHttpRequestBodyInit* data, |
| ExceptionState& exception_state) { |
| ExecutionContext* execution_context = ExecutionContext::From(script_state); |
| KURL url = execution_context->CompleteURL(url_string); |
| if (!CanSendBeacon(execution_context, url, exception_state)) { |
| return false; |
| } |
| |
| bool allowed; |
| LocalFrame* frame = GetSupplementable()->DomWindow()->GetFrame(); |
| if (data) { |
| switch (data->GetContentType()) { |
| case V8UnionReadableStreamOrXMLHttpRequestBodyInit::ContentType:: |
| kArrayBuffer: { |
| UseCounter::Count(execution_context, |
| WebFeature::kSendBeaconWithArrayBuffer); |
| auto* data_buffer = data->GetAsArrayBuffer(); |
| if (!base::CheckedNumeric<wtf_size_t>(data_buffer->ByteLength()) |
| .IsValid()) { |
| // At the moment the PingLoader::SendBeacon implementation cannot deal |
| // with huge ArrayBuffers. |
| exception_state.ThrowRangeError( |
| "The data provided to sendBeacon() exceeds the maximally " |
| "possible length, which is 4294967295."); |
| return false; |
| } |
| allowed = |
| PingLoader::SendBeacon(*script_state, frame, url, data_buffer); |
| break; |
| } |
| case V8UnionReadableStreamOrXMLHttpRequestBodyInit::ContentType:: |
| kArrayBufferView: { |
| UseCounter::Count(execution_context, |
| WebFeature::kSendBeaconWithArrayBufferView); |
| auto* data_view = data->GetAsArrayBufferView().Get(); |
| if (!base::CheckedNumeric<wtf_size_t>(data_view->byteLength()) |
| .IsValid()) { |
| // At the moment the PingLoader::SendBeacon implementation cannot deal |
| // with huge ArrayBuffers. |
| exception_state.ThrowRangeError( |
| "The data provided to sendBeacon() exceeds the maximally " |
| "possible length, which is 4294967295."); |
| return false; |
| } |
| allowed = PingLoader::SendBeacon(*script_state, frame, url, data_view); |
| break; |
| } |
| case V8UnionReadableStreamOrXMLHttpRequestBodyInit::ContentType::kBlob: |
| UseCounter::Count(execution_context, WebFeature::kSendBeaconWithBlob); |
| allowed = PingLoader::SendBeacon(*script_state, frame, url, |
| data->GetAsBlob()); |
| break; |
| case V8UnionReadableStreamOrXMLHttpRequestBodyInit::ContentType:: |
| kFormData: |
| UseCounter::Count(execution_context, |
| WebFeature::kSendBeaconWithFormData); |
| allowed = PingLoader::SendBeacon(*script_state, frame, url, |
| data->GetAsFormData()); |
| break; |
| case V8UnionReadableStreamOrXMLHttpRequestBodyInit::ContentType:: |
| kReadableStream: |
| exception_state.ThrowTypeError( |
| "sendBeacon cannot have a ReadableStream body."); |
| return false; |
| case V8UnionReadableStreamOrXMLHttpRequestBodyInit::ContentType:: |
| kURLSearchParams: |
| UseCounter::Count(execution_context, |
| WebFeature::kSendBeaconWithURLSearchParams); |
| allowed = PingLoader::SendBeacon(*script_state, frame, url, |
| data->GetAsURLSearchParams()); |
| break; |
| case V8UnionReadableStreamOrXMLHttpRequestBodyInit::ContentType:: |
| kUSVString: |
| UseCounter::Count(execution_context, |
| WebFeature::kSendBeaconWithUSVString); |
| allowed = PingLoader::SendBeacon(*script_state, frame, url, |
| data->GetAsUSVString()); |
| break; |
| } |
| } else { |
| allowed = PingLoader::SendBeacon(*script_state, frame, url, String()); |
| } |
| |
| if (!allowed) { |
| UseCounter::Count(execution_context, WebFeature::kSendBeaconQuotaExceeded); |
| } |
| |
| return allowed; |
| } |
| |
| } // namespace blink |