blob: 9756ddf84d487342e3af1b1ef46e563cf1c65048 [file] [log] [blame]
// Copyright 2022 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/direct_sockets/socket.h"
#include <utility>
#include "net/base/net_errors.h"
#include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace blink {
namespace {
std::pair<DOMExceptionCode, String>
CreateDOMExceptionCodeAndMessageFromNetErrorCode(int32_t net_error) {
switch (net_error) {
case net::ERR_NAME_NOT_RESOLVED:
return {DOMExceptionCode::kNetworkError,
"Hostname couldn't be resolved."};
case net::ERR_INVALID_URL:
return {DOMExceptionCode::kDataError, "Supplied url is not valid."};
case net::ERR_UNEXPECTED:
return {DOMExceptionCode::kUnknownError, "Unexpected error occured."};
case net::ERR_ACCESS_DENIED:
return {DOMExceptionCode::kInvalidAccessError,
"Access to the requested host is blocked."};
case net::ERR_NETWORK_ACCESS_DENIED:
return {DOMExceptionCode::kInvalidAccessError, "Firewall error."};
default:
return {DOMExceptionCode::kNetworkError, "Network Error."};
}
}
} // namespace
ScriptPromise Socket::opened(ScriptState* script_state) const {
return ScriptPromise(script_state, opened_.Get(script_state->GetIsolate()));
}
ScriptPromise Socket::closed(ScriptState* script_state) const {
return ScriptPromise(script_state, closed_.Get(script_state->GetIsolate()));
}
Socket::Socket(ScriptState* script_state)
: ExecutionContextLifecycleStateObserver(
ExecutionContext::From(script_state)),
script_state_(script_state),
service_(GetExecutionContext()),
feature_handle_for_scheduler_(
GetExecutionContext()->GetScheduler()->RegisterFeature(
SchedulingPolicy::Feature::kOutstandingNetworkRequestDirectSocket,
{SchedulingPolicy::DisableBackForwardCache()})),
opened_resolver_(
MakeGarbageCollected<ScriptPromiseResolver>(script_state)),
opened_(script_state->GetIsolate(),
opened_resolver_->Promise().V8Promise()),
closed_resolver_(
MakeGarbageCollected<ScriptPromiseResolver>(script_state)),
closed_(script_state->GetIsolate(),
closed_resolver_->Promise().V8Promise()) {
UpdateStateIfNeeded();
GetExecutionContext()->GetBrowserInterfaceBroker().GetInterface(
service_.BindNewPipeAndPassReceiver(
GetExecutionContext()->GetTaskRunner(TaskType::kNetworking)));
service_.set_disconnect_handler(
WTF::BindOnce(&Socket::OnServiceConnectionError, WrapPersistent(this)));
// |closed| promise is just one of the ways to learn that the socket state has
// changed. Therefore it's not necessary to force developers to handle
// rejections.
closed_resolver_->Promise().MarkAsHandled();
}
Socket::~Socket() = default;
// static
bool Socket::CheckContextAndPermissions(ScriptState* script_state,
ExceptionState& exception_state) {
if (!script_state->ContextIsValid()) {
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
"Current context is detached.");
return false;
}
if (!ExecutionContext::From(script_state)
->IsFeatureEnabled(
mojom::blink::PermissionsPolicyFeature::kDirectSockets)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotAllowedError,
"Permissions-Policy: direct-sockets are disabled.");
return false;
}
return true;
}
// static
DOMException* Socket::CreateDOMExceptionFromNetErrorCode(int32_t net_error) {
auto [code, message] =
CreateDOMExceptionCodeAndMessageFromNetErrorCode(net_error);
return MakeGarbageCollected<DOMException>(code, std::move(message));
}
void Socket::Trace(Visitor* visitor) const {
visitor->Trace(script_state_);
visitor->Trace(service_);
visitor->Trace(opened_resolver_);
visitor->Trace(opened_);
visitor->Trace(closed_resolver_);
visitor->Trace(closed_);
ExecutionContextLifecycleStateObserver::Trace(visitor);
}
void Socket::ResetServiceAndFeatureHandle() {
feature_handle_for_scheduler_.reset();
service_.reset();
}
} // namespace blink