/* -*- 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 <algorithm> #include "TCPSocketChild.h" #include "mozilla/Unused.h" #include "mozilla/UniquePtr.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/dom/PBrowserChild.h" #include "mozilla/dom/TabChild.h" #include "nsITCPSocketCallback.h" #include "TCPSocket.h" #include "nsContentUtils.h" #include "jsapi.h" #include "jsfriendapi.h" using mozilla::net::gNeckoChild; namespace IPC { bool DeserializeArrayBuffer(JSContext* cx, const InfallibleTArray<uint8_t>& aBuffer, JS::MutableHandle<JS::Value> aVal) { mozilla::UniquePtr<uint8_t[], JS::FreePolicy> data(js_pod_malloc<uint8_t>(aBuffer.Length())); if (!data) return false; memcpy(data.get(), aBuffer.Elements(), aBuffer.Length()); JSObject* obj = JS_NewArrayBufferWithContents(cx, aBuffer.Length(), data.get()); if (!obj) return false; // If JS_NewArrayBufferWithContents returns non-null, the ownership of // the data is transfered to obj, so we release the ownership here. mozilla::Unused << data.release(); aVal.setObject(*obj); return true; } } // namespace IPC namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocketChildBase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TCPSocketChildBase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocket) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TCPSocketChildBase) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocket) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TCPSocketChildBase) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketChildBase) NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketChildBase) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketChildBase) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END TCPSocketChildBase::TCPSocketChildBase() : mIPCOpen(false) { mozilla::HoldJSObjects(this); } TCPSocketChildBase::~TCPSocketChildBase() { mozilla::DropJSObjects(this); } NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketChild::Release(void) { nsrefcnt refcnt = TCPSocketChildBase::Release(); if (refcnt == 1 && mIPCOpen) { PTCPSocketChild::SendRequestDelete(); return 1; } return refcnt; } TCPSocketChild::TCPSocketChild(const nsAString& aHost, const uint16_t& aPort) : mHost(aHost) , mPort(aPort) { } void TCPSocketChild::SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers) { mSocket = aSocket; AddIPDLReference(); gNeckoChild->SendPTCPSocketConstructor(this, mHost, mPort); MOZ_ASSERT(mFilterName.IsEmpty()); // Currently nobody should use this PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers); } void TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket, const nsACString& aRemoteHost, uint16_t aRemotePort, const nsACString& aLocalHost, uint16_t aLocalPort, bool aUseSSL) { mSocket = aSocket; AddIPDLReference(); gNeckoChild->SendPTCPSocketConstructor(this, NS_ConvertUTF8toUTF16(aRemoteHost), aRemotePort); PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort, nsCString(aLocalHost), aLocalPort, aUseSSL, true, mFilterName); } void TCPSocketChildBase::ReleaseIPDLReference() { MOZ_ASSERT(mIPCOpen); mIPCOpen = false; mSocket = nullptr; this->Release(); } void TCPSocketChildBase::AddIPDLReference() { MOZ_ASSERT(!mIPCOpen); mIPCOpen = true; this->AddRef(); } TCPSocketChild::~TCPSocketChild() { } bool TCPSocketChild::RecvUpdateBufferedAmount(const uint32_t& aBuffered, const uint32_t& aTrackingNumber) { mSocket->UpdateBufferedAmount(aBuffered, aTrackingNumber); return true; } bool TCPSocketChild::RecvCallback(const nsString& aType, const CallbackData& aData, const uint32_t& aReadyState) { mSocket->UpdateReadyState(aReadyState); if (aData.type() == CallbackData::Tvoid_t) { mSocket->FireEvent(aType); } else if (aData.type() == CallbackData::TTCPError) { const TCPError& err(aData.get_TCPError()); mSocket->FireErrorEvent(err.name(), err.message()); } else if (aData.type() == CallbackData::TSendableData) { const SendableData& data = aData.get_SendableData(); if (data.type() == SendableData::TArrayOfuint8_t) { mSocket->FireDataArrayEvent(aType, data.get_ArrayOfuint8_t()); } else if (data.type() == SendableData::TnsCString) { mSocket->FireDataStringEvent(aType, data.get_nsCString()); } else { MOZ_CRASH("Invalid callback data type!"); } } else { MOZ_CRASH("Invalid callback type!"); } return true; } void TCPSocketChild::SendSend(const nsACString& aData, uint32_t aTrackingNumber) { SendData(nsCString(aData), aTrackingNumber); } nsresult TCPSocketChild::SendSend(const ArrayBuffer& aData, uint32_t aByteOffset, uint32_t aByteLength, uint32_t aTrackingNumber) { uint32_t buflen = aData.Length(); uint32_t offset = std::min(buflen, aByteOffset); uint32_t nbytes = std::min(buflen - aByteOffset, aByteLength); FallibleTArray<uint8_t> fallibleArr; if (!fallibleArr.InsertElementsAt(0, aData.Data() + offset, nbytes, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } InfallibleTArray<uint8_t> arr; arr.SwapElements(fallibleArr); SendData(arr, aTrackingNumber); return NS_OK; } NS_IMETHODIMP TCPSocketChild::SendSendArray(nsTArray<uint8_t>& aArray, uint32_t aTrackingNumber) { SendData(aArray, aTrackingNumber); return NS_OK; } void TCPSocketChild::SetSocket(TCPSocket* aSocket) { mSocket = aSocket; } void TCPSocketChild::GetHost(nsAString& aHost) { aHost = mHost; } void TCPSocketChild::GetPort(uint16_t* aPort) { *aPort = mPort; } nsresult TCPSocketChild::SetFilterName(const nsACString& aFilterName) { if (!mFilterName.IsEmpty()) { // filter name can only be set once. return NS_ERROR_FAILURE; } mFilterName = aFilterName; return NS_OK; } bool TCPSocketChild::RecvRequestDelete() { mozilla::Unused << Send__delete__(this); return true; } } // namespace dom } // namespace mozilla