/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/TCPServerSocketBinding.h" #include "mozilla/dom/TCPServerSocketEvent.h" #include "mozilla/dom/TCPSocketBinding.h" #include "TCPServerSocketParent.h" #include "TCPServerSocketChild.h" #include "mozilla/dom/Event.h" #include "mozilla/ErrorResult.h" #include "TCPServerSocket.h" #include "TCPSocket.h" #include "nsComponentManagerUtils.h" using namespace mozilla::dom; NS_IMPL_CYCLE_COLLECTION_CLASS(TCPServerSocket) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPServerSocket, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPServerSocket, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerSocket) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerBridgeChild) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServerBridgeParent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPServerSocket, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerSocket) NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerBridgeChild) NS_IMPL_CYCLE_COLLECTION_UNLINK(mServerBridgeParent) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ADDREF_INHERITED(TCPServerSocket, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(TCPServerSocket, DOMEventTargetHelper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPServerSocket) NS_INTERFACE_MAP_ENTRY(nsIServerSocketListener) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) TCPServerSocket::TCPServerSocket(nsIGlobalObject* aGlobal, uint16_t aPort, bool aUseArrayBuffers, uint16_t aBacklog) : DOMEventTargetHelper(aGlobal), mPort(aPort), mBacklog(aBacklog), mUseArrayBuffers(aUseArrayBuffers) {} TCPServerSocket::~TCPServerSocket() = default; nsresult TCPServerSocket::Init() { if (mServerSocket || mServerBridgeChild) { NS_WARNING("Child TCPServerSocket is already listening."); return NS_ERROR_FAILURE; } if (XRE_GetProcessType() == GeckoProcessType_Content) { nsCOMPtr target; if (nsCOMPtr global = GetOwnerGlobal()) { target = global->EventTargetFor(TaskCategory::Other); } mServerBridgeChild = new TCPServerSocketChild(this, mPort, mBacklog, mUseArrayBuffers, target); return NS_OK; } nsresult rv; mServerSocket = do_CreateInstance("@mozilla.org/network/server-socket;1", &rv); NS_ENSURE_SUCCESS(rv, rv); rv = mServerSocket->Init(mPort, false, mBacklog); NS_ENSURE_SUCCESS(rv, rv); rv = mServerSocket->GetPort(&mPort); NS_ENSURE_SUCCESS(rv, rv); rv = mServerSocket->AsyncListen(this); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } already_AddRefed TCPServerSocket::Constructor( const GlobalObject& aGlobal, uint16_t aPort, const ServerSocketOptions& aOptions, uint16_t aBacklog, mozilla::ErrorResult& aRv) { nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); if (!global) { aRv = NS_ERROR_FAILURE; return nullptr; } bool useArrayBuffers = aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer; RefPtr socket = new TCPServerSocket(global, aPort, useArrayBuffers, aBacklog); nsresult rv = socket->Init(); if (NS_WARN_IF(NS_FAILED(rv))) { aRv = NS_ERROR_FAILURE; return nullptr; } return socket.forget(); } uint16_t TCPServerSocket::LocalPort() { return mPort; } void TCPServerSocket::Close() { if (mServerBridgeChild) { mServerBridgeChild->Close(); } if (mServerSocket) { mServerSocket->Close(); } } void TCPServerSocket::FireEvent(const nsAString& aType, TCPSocket* aSocket) { TCPServerSocketEventInit init; init.mBubbles = false; init.mCancelable = false; init.mSocket = aSocket; RefPtr event = TCPServerSocketEvent::Constructor(this, aType, init); event->SetTrusted(true); DispatchEvent(*event); if (mServerBridgeParent) { mServerBridgeParent->OnConnect(event); } } NS_IMETHODIMP TCPServerSocket::OnSocketAccepted(nsIServerSocket* aServer, nsISocketTransport* aTransport) { nsCOMPtr global = GetOwnerGlobal(); RefPtr socket = TCPSocket::CreateAcceptedSocket(global, aTransport, mUseArrayBuffers); FireEvent(u"connect"_ns, socket); return NS_OK; } NS_IMETHODIMP TCPServerSocket::OnStopListening(nsIServerSocket* aServer, nsresult aStatus) { if (aStatus != NS_BINDING_ABORTED) { RefPtr event = new Event(GetOwner()); event->InitEvent(u"error"_ns, false, false); event->SetTrusted(true); DispatchEvent(*event); NS_WARNING("Server socket was closed by unexpected reason."); return NS_ERROR_FAILURE; } mServerSocket = nullptr; return NS_OK; } nsresult TCPServerSocket::AcceptChildSocket(TCPSocketChild* aSocketChild) { nsCOMPtr global = GetOwnerGlobal(); NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); RefPtr socket = TCPSocket::CreateAcceptedSocket(global, aSocketChild, mUseArrayBuffers); NS_ENSURE_TRUE(socket, NS_ERROR_FAILURE); FireEvent(u"connect"_ns, socket); return NS_OK; } void TCPServerSocket::SetServerBridgeParent( TCPServerSocketParent* aBridgeParent) { mServerBridgeParent = aBridgeParent; } JSObject* TCPServerSocket::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return TCPServerSocket_Binding::Wrap(aCx, this, aGivenProto); }