michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TCPSocketParent.h" michael@0: #include "jsapi.h" michael@0: #include "jsfriendapi.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsIDOMTCPSocket.h" michael@0: #include "nsCxPusher.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/AppProcessChecker.h" michael@0: #include "mozilla/net/NeckoCommon.h" michael@0: #include "mozilla/net/PNeckoParent.h" michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/TabParent.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: michael@0: namespace IPC { michael@0: michael@0: //Defined in TCPSocketChild.cpp michael@0: extern bool michael@0: DeserializeArrayBuffer(JS::Handle aObj, michael@0: const InfallibleTArray& aBuffer, michael@0: JS::MutableHandle aVal); michael@0: michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: static void michael@0: FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo) michael@0: { michael@0: mozilla::unused << michael@0: aActor->SendCallback(NS_LITERAL_STRING("onerror"), michael@0: TCPError(NS_LITERAL_STRING("InvalidStateError")), michael@0: NS_LITERAL_STRING("connecting")); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(TCPSocketParentBase, mSocket, mIntermediary) michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketParentBase) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketParentBase) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketParentBase) michael@0: NS_INTERFACE_MAP_ENTRY(nsITCPSocketParent) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: TCPSocketParentBase::TCPSocketParentBase() michael@0: : mIPCOpen(false) michael@0: { michael@0: } michael@0: michael@0: TCPSocketParentBase::~TCPSocketParentBase() michael@0: { michael@0: } michael@0: michael@0: void michael@0: TCPSocketParentBase::ReleaseIPDLReference() michael@0: { michael@0: MOZ_ASSERT(mIPCOpen); michael@0: mIPCOpen = false; michael@0: this->Release(); michael@0: } michael@0: michael@0: void michael@0: TCPSocketParentBase::AddIPDLReference() michael@0: { michael@0: MOZ_ASSERT(!mIPCOpen); michael@0: mIPCOpen = true; michael@0: this->AddRef(); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketParent::Release(void) michael@0: { michael@0: nsrefcnt refcnt = TCPSocketParentBase::Release(); michael@0: if (refcnt == 1 && mIPCOpen) { michael@0: mozilla::unused << PTCPSocketParent::SendRequestDelete(); michael@0: return 1; michael@0: } michael@0: return refcnt; michael@0: } michael@0: michael@0: bool michael@0: TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bool& aUseSSL, michael@0: const nsString& aBinaryType) michael@0: { michael@0: // We don't have browser actors in xpcshell, and hence can't run automated michael@0: // tests without this loophole. michael@0: if (net::UsingNeckoIPCSecurity() && michael@0: !AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) { michael@0: FireInteralError(this, __LINE__); michael@0: return true; michael@0: } michael@0: michael@0: // Obtain App ID michael@0: uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; michael@0: const PContentParent *content = Manager()->Manager(); michael@0: const InfallibleTArray& browsers = content->ManagedPBrowserParent(); michael@0: if (browsers.Length() > 0) { michael@0: TabParent *tab = static_cast(browsers[0]); michael@0: appId = tab->OwnAppId(); michael@0: } michael@0: michael@0: nsresult rv; michael@0: mIntermediary = do_CreateInstance("@mozilla.org/tcp-socket-intermediary;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: FireInteralError(this, __LINE__); michael@0: return true; michael@0: } michael@0: michael@0: rv = mIntermediary->Open(this, aHost, aPort, aUseSSL, aBinaryType, appId, michael@0: getter_AddRefs(mSocket)); michael@0: if (NS_FAILED(rv) || !mSocket) { michael@0: FireInteralError(this, __LINE__); michael@0: return true; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TCPSocketParent::InitJS(JS::Handle aIntermediary, JSContext* aCx) michael@0: { michael@0: MOZ_ASSERT(aIntermediary.isObject()); michael@0: mIntermediaryObj = &aIntermediary.toObject(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: TCPSocketParent::RecvStartTLS() michael@0: { michael@0: NS_ENSURE_TRUE(mSocket, true); michael@0: nsresult rv = mSocket->UpgradeToSecure(); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TCPSocketParent::RecvSuspend() michael@0: { michael@0: NS_ENSURE_TRUE(mSocket, true); michael@0: nsresult rv = mSocket->Suspend(); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TCPSocketParent::RecvResume() michael@0: { michael@0: NS_ENSURE_TRUE(mSocket, true); michael@0: nsresult rv = mSocket->Resume(); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TCPSocketParent::RecvData(const SendableData& aData, michael@0: const uint32_t& aTrackingNumber) michael@0: { michael@0: NS_ENSURE_TRUE(mIntermediary, true); michael@0: michael@0: nsresult rv; michael@0: switch (aData.type()) { michael@0: case SendableData::TArrayOfuint8_t: { michael@0: AutoSafeJSContext cx; michael@0: JSAutoRequest ar(cx); michael@0: JS::Rooted val(cx); michael@0: JS::Rooted obj(cx, mIntermediaryObj); michael@0: IPC::DeserializeArrayBuffer(obj, aData.get_ArrayOfuint8_t(), &val); michael@0: rv = mIntermediary->OnRecvSendArrayBuffer(val, aTrackingNumber); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: break; michael@0: } michael@0: michael@0: case SendableData::TnsString: michael@0: rv = mIntermediary->OnRecvSendString(aData.get_nsString(), aTrackingNumber); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: break; michael@0: michael@0: default: michael@0: MOZ_CRASH("unexpected SendableData type"); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TCPSocketParent::RecvClose() michael@0: { michael@0: NS_ENSURE_TRUE(mSocket, true); michael@0: nsresult rv = mSocket->Close(); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TCPSocketParent::SendEvent(const nsAString& aType, JS::Handle aDataVal, michael@0: const nsAString& aReadyState, JSContext* aCx) michael@0: { michael@0: if (!mIPCOpen) { michael@0: NS_WARNING("Dropping callback due to no IPC connection"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: CallbackData data; michael@0: if (aDataVal.isString()) { michael@0: JSString* jsstr = aDataVal.toString(); michael@0: nsDependentJSString str; michael@0: if (!str.init(aCx, jsstr)) { michael@0: FireInteralError(this, __LINE__); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: data = SendableData(str); michael@0: michael@0: } else if (aDataVal.isUndefined() || aDataVal.isNull()) { michael@0: data = mozilla::void_t(); michael@0: michael@0: } else if (aDataVal.isObject()) { michael@0: JS::Rooted obj(aCx, &aDataVal.toObject()); michael@0: if (JS_IsArrayBufferObject(obj)) { michael@0: uint32_t nbytes = JS_GetArrayBufferByteLength(obj); michael@0: uint8_t* buffer = JS_GetArrayBufferData(obj); michael@0: if (!buffer) { michael@0: FireInteralError(this, __LINE__); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: FallibleTArray fallibleArr; michael@0: if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) { michael@0: FireInteralError(this, __LINE__); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: InfallibleTArray arr; michael@0: arr.SwapElements(fallibleArr); michael@0: data = SendableData(arr); michael@0: michael@0: } else { michael@0: nsDependentJSString name; michael@0: michael@0: JS::Rooted val(aCx); michael@0: if (!JS_GetProperty(aCx, obj, "name", &val)) { michael@0: NS_ERROR("No name property on supposed error object"); michael@0: } else if (JSVAL_IS_STRING(val)) { michael@0: if (!name.init(aCx, JSVAL_TO_STRING(val))) { michael@0: NS_WARNING("couldn't initialize string"); michael@0: } michael@0: } michael@0: michael@0: data = TCPError(name); michael@0: } michael@0: } else { michael@0: NS_ERROR("Unexpected JS value encountered"); michael@0: FireInteralError(this, __LINE__); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: mozilla::unused << michael@0: PTCPSocketParent::SendCallback(nsString(aType), data, michael@0: nsString(aReadyState)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TCPSocketParent::SetSocketAndIntermediary(nsIDOMTCPSocket *socket, michael@0: nsITCPSocketIntermediary *intermediary, michael@0: JSContext* cx) michael@0: { michael@0: mSocket = socket; michael@0: mIntermediary = intermediary; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TCPSocketParent::SendUpdateBufferedAmount(uint32_t aBufferedAmount, michael@0: uint32_t aTrackingNumber) michael@0: { michael@0: mozilla::unused << PTCPSocketParent::SendUpdateBufferedAmount(aBufferedAmount, michael@0: aTrackingNumber); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: TCPSocketParent::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: if (mSocket) { michael@0: mSocket->Close(); michael@0: } michael@0: mSocket = nullptr; michael@0: mIntermediaryObj = nullptr; michael@0: mIntermediary = nullptr; michael@0: } michael@0: michael@0: bool michael@0: TCPSocketParent::RecvRequestDelete() michael@0: { michael@0: mozilla::unused << Send__delete__(this); michael@0: return true; michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla