mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-27 12:15:33 +00:00
aefa2aa5da
For the Quatum DOM project, it's better to work in terms of event targets than threads. This patch converts DOM code to operate on event targets rather than threads, when possible. MozReview-Commit-ID: 5FgvpKadUA2
1203 lines
34 KiB
C++
1203 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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/ErrorResult.h"
|
|
#include "TCPSocket.h"
|
|
#include "TCPServerSocket.h"
|
|
#include "TCPSocketChild.h"
|
|
#include "mozilla/dom/DOMError.h"
|
|
#include "mozilla/dom/TCPSocketBinding.h"
|
|
#include "mozilla/dom/TCPSocketErrorEvent.h"
|
|
#include "mozilla/dom/TCPSocketErrorEventBinding.h"
|
|
#include "mozilla/dom/TCPSocketEvent.h"
|
|
#include "mozilla/dom/TCPSocketEventBinding.h"
|
|
#include "mozilla/dom/ToJSValue.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIArrayBufferInputStream.h"
|
|
#include "nsISocketTransportService.h"
|
|
#include "nsISocketTransport.h"
|
|
#include "nsIMultiplexInputStream.h"
|
|
#include "nsIAsyncStreamCopier.h"
|
|
#include "nsIInputStream.h"
|
|
#include "nsIBinaryInputStream.h"
|
|
#include "nsIScriptableInputStream.h"
|
|
#include "nsIInputStreamPump.h"
|
|
#include "nsIAsyncInputStream.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "nsITransport.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsINSSErrorsService.h"
|
|
#include "nsISSLSocketControl.h"
|
|
#include "nsStringStream.h"
|
|
#include "secerr.h"
|
|
#include "sslerr.h"
|
|
#ifdef MOZ_WIDGET_GONK
|
|
#include "nsINetworkManager.h"
|
|
#include "nsINetworkInterface.h"
|
|
#endif
|
|
|
|
#define BUFFER_SIZE 65536
|
|
#define NETWORK_STATS_THRESHOLD 65536
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(LegacyMozTCPSocket, mGlobal)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(LegacyMozTCPSocket)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(LegacyMozTCPSocket)
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LegacyMozTCPSocket)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
LegacyMozTCPSocket::LegacyMozTCPSocket(nsPIDOMWindowInner* aWindow)
|
|
: mGlobal(do_QueryInterface(aWindow))
|
|
{
|
|
}
|
|
|
|
LegacyMozTCPSocket::~LegacyMozTCPSocket()
|
|
{
|
|
}
|
|
|
|
already_AddRefed<TCPSocket>
|
|
LegacyMozTCPSocket::Open(const nsAString& aHost,
|
|
uint16_t aPort,
|
|
const SocketOptions& aOptions,
|
|
mozilla::ErrorResult& aRv)
|
|
{
|
|
AutoJSAPI api;
|
|
if (NS_WARN_IF(!api.Init(mGlobal))) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return nullptr;
|
|
}
|
|
GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
|
|
return TCPSocket::Constructor(globalObj, aHost, aPort, aOptions, aRv);
|
|
}
|
|
|
|
already_AddRefed<TCPServerSocket>
|
|
LegacyMozTCPSocket::Listen(uint16_t aPort,
|
|
const ServerSocketOptions& aOptions,
|
|
uint16_t aBacklog,
|
|
mozilla::ErrorResult& aRv)
|
|
{
|
|
AutoJSAPI api;
|
|
if (NS_WARN_IF(!api.Init(mGlobal))) {
|
|
return nullptr;
|
|
}
|
|
GlobalObject globalObj(api.cx(), mGlobal->GetGlobalJSObject());
|
|
return TCPServerSocket::Constructor(globalObj, aPort, aOptions, aBacklog, aRv);
|
|
}
|
|
|
|
bool
|
|
LegacyMozTCPSocket::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto,
|
|
JS::MutableHandle<JSObject*> aReflector)
|
|
{
|
|
return LegacyMozTCPSocketBinding::Wrap(aCx, this, aGivenProto, aReflector);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocket)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TCPSocket,
|
|
DOMEventTargetHelper)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TCPSocket,
|
|
DOMEventTargetHelper)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransport)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketInputStream)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketOutputStream)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamPump)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamScriptable)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStreamBinary)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMultiplexStream)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMultiplexStreamCopier)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingDataAfterStartTLS)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeChild)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocketBridgeParent)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TCPSocket,
|
|
DOMEventTargetHelper)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransport)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketInputStream)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketOutputStream)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamPump)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamScriptable)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStreamBinary)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMultiplexStream)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMultiplexStreamCopier)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingDataAfterStartTLS)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeChild)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocketBridgeParent)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_ADDREF_INHERITED(TCPSocket, DOMEventTargetHelper)
|
|
NS_IMPL_RELEASE_INHERITED(TCPSocket, DOMEventTargetHelper)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TCPSocket)
|
|
NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
|
|
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
|
|
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|
|
|
TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
|
|
bool aSsl, bool aUseArrayBuffers)
|
|
: DOMEventTargetHelper(aGlobal)
|
|
, mReadyState(TCPReadyState::Closed)
|
|
, mUseArrayBuffers(aUseArrayBuffers)
|
|
, mHost(aHost)
|
|
, mPort(aPort)
|
|
, mSsl(aSsl)
|
|
, mAsyncCopierActive(false)
|
|
, mWaitingForDrain(false)
|
|
, mInnerWindowID(0)
|
|
, mBufferedAmount(0)
|
|
, mSuspendCount(0)
|
|
, mTrackingNumber(0)
|
|
, mWaitingForStartTLS(false)
|
|
, mObserversActive(false)
|
|
#ifdef MOZ_WIDGET_GONK
|
|
, mAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID)
|
|
, mInIsolatedMozBrowser(false)
|
|
#endif
|
|
{
|
|
if (aGlobal) {
|
|
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
|
|
if (window) {
|
|
mInnerWindowID = window->WindowID();
|
|
}
|
|
}
|
|
}
|
|
|
|
TCPSocket::~TCPSocket()
|
|
{
|
|
if (mObserversActive) {
|
|
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
|
|
if (obs) {
|
|
obs->RemoveObserver(this, "inner-window-destroyed");
|
|
obs->RemoveObserver(this, "profile-change-net-teardown");
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
TCPSocket::CreateStream()
|
|
{
|
|
nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// If the other side is not listening, we will
|
|
// get an onInputStreamReady callback where available
|
|
// raises to indicate the connection was refused.
|
|
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mSocketInputStream);
|
|
NS_ENSURE_TRUE(asyncStream, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
|
|
rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainTarget);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (mUseArrayBuffers) {
|
|
mInputStreamBinary = do_CreateInstance("@mozilla.org/binaryinputstream;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = mInputStreamBinary->SetInputStream(mSocketInputStream);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
} else {
|
|
mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = mInputStreamScriptable->Init(mSocketInputStream);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsISocketTransportService> sts =
|
|
do_GetService("@mozilla.org/network/socket-transport-service;1");
|
|
|
|
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
|
|
rv = mMultiplexStreamCopier->Init(mMultiplexStream,
|
|
mSocketOutputStream,
|
|
target,
|
|
true, /* source buffered */
|
|
false, /* sink buffered */
|
|
BUFFER_SIZE,
|
|
false, /* close source */
|
|
false); /* close sink */
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
TCPSocket::InitWithUnconnectedTransport(nsISocketTransport* aTransport)
|
|
{
|
|
mReadyState = TCPReadyState::Connecting;
|
|
mTransport = aTransport;
|
|
|
|
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);
|
|
|
|
nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
|
|
mTransport->SetEventSink(this, mainTarget);
|
|
|
|
nsresult rv = CreateStream();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
TCPSocket::Init()
|
|
{
|
|
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
|
|
if (obs) {
|
|
mObserversActive = true;
|
|
obs->AddObserver(this, "inner-window-destroyed", true); // weak reference
|
|
obs->AddObserver(this, "profile-change-net-teardown", true); // weak ref
|
|
}
|
|
|
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
|
mReadyState = TCPReadyState::Connecting;
|
|
|
|
nsCOMPtr<nsIEventTarget> target;
|
|
if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
|
|
target = global->EventTargetFor(TaskCategory::Other);
|
|
}
|
|
mSocketBridgeChild = new TCPSocketChild(mHost, mPort, target);
|
|
mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsISocketTransportService> sts =
|
|
do_GetService("@mozilla.org/network/socket-transport-service;1");
|
|
|
|
const char* socketTypes[1];
|
|
if (mSsl) {
|
|
socketTypes[0] = "ssl";
|
|
} else {
|
|
socketTypes[0] = "starttls";
|
|
}
|
|
nsCOMPtr<nsISocketTransport> transport;
|
|
nsresult rv = sts->CreateTransport(socketTypes, 1, NS_ConvertUTF16toUTF8(mHost), mPort,
|
|
nullptr, getter_AddRefs(transport));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return InitWithUnconnectedTransport(transport);
|
|
}
|
|
|
|
void
|
|
TCPSocket::InitWithSocketChild(TCPSocketChild* aSocketBridge)
|
|
{
|
|
mSocketBridgeChild = aSocketBridge;
|
|
mReadyState = TCPReadyState::Open;
|
|
mSocketBridgeChild->SetSocket(this);
|
|
mSocketBridgeChild->GetHost(mHost);
|
|
mSocketBridgeChild->GetPort(&mPort);
|
|
}
|
|
|
|
nsresult
|
|
TCPSocket::InitWithTransport(nsISocketTransport* aTransport)
|
|
{
|
|
mTransport = aTransport;
|
|
nsresult rv = CreateStream();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mReadyState = TCPReadyState::Open;
|
|
rv = CreateInputStreamPump();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoCString host;
|
|
mTransport->GetHost(host);
|
|
mHost = NS_ConvertUTF8toUTF16(host);
|
|
int32_t port;
|
|
mTransport->GetPort(&port);
|
|
mPort = port;
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
nsCOMPtr<nsINetworkManager> networkManager = do_GetService("@mozilla.org/network/manager;1");
|
|
if (networkManager) {
|
|
networkManager->GetActiveNetworkInfo(getter_AddRefs(mActiveNetworkInfo));
|
|
}
|
|
#endif
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
TCPSocket::UpgradeToSecure(mozilla::ErrorResult& aRv)
|
|
{
|
|
if (mReadyState != TCPReadyState::Open) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
if (mSsl) {
|
|
return;
|
|
}
|
|
|
|
mSsl = true;
|
|
|
|
if (mSocketBridgeChild) {
|
|
mSocketBridgeChild->SendStartTLS();
|
|
return;
|
|
}
|
|
|
|
uint32_t count = 0;
|
|
mMultiplexStream->GetCount(&count);
|
|
if (!count) {
|
|
ActivateTLS();
|
|
} else {
|
|
mWaitingForStartTLS = true;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class CopierCallbacks final : public nsIRequestObserver
|
|
{
|
|
RefPtr<TCPSocket> mOwner;
|
|
public:
|
|
explicit CopierCallbacks(TCPSocket* aSocket) : mOwner(aSocket) {}
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIREQUESTOBSERVER
|
|
private:
|
|
~CopierCallbacks() {}
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)
|
|
|
|
NS_IMETHODIMP
|
|
CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
|
|
{
|
|
mOwner->NotifyCopyComplete(aStatus);
|
|
mOwner = nullptr;
|
|
return NS_OK;
|
|
}
|
|
} // unnamed namespace
|
|
|
|
nsresult
|
|
TCPSocket::EnsureCopying()
|
|
{
|
|
if (mAsyncCopierActive) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mAsyncCopierActive = true;
|
|
RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
|
|
return mMultiplexStreamCopier->AsyncCopy(callbacks, nullptr);
|
|
}
|
|
|
|
void
|
|
TCPSocket::NotifyCopyComplete(nsresult aStatus)
|
|
{
|
|
mAsyncCopierActive = false;
|
|
|
|
uint32_t countRemaining;
|
|
nsresult rvRemaining = mMultiplexStream->GetCount(&countRemaining);
|
|
NS_ENSURE_SUCCESS_VOID(rvRemaining);
|
|
|
|
while (countRemaining--) {
|
|
mMultiplexStream->RemoveStream(0);
|
|
}
|
|
|
|
while (!mPendingDataWhileCopierActive.IsEmpty()) {
|
|
nsCOMPtr<nsIInputStream> stream = mPendingDataWhileCopierActive[0];
|
|
mMultiplexStream->AppendStream(stream);
|
|
mPendingDataWhileCopierActive.RemoveElementAt(0);
|
|
}
|
|
|
|
if (mSocketBridgeParent) {
|
|
mozilla::Unused << mSocketBridgeParent->SendUpdateBufferedAmount(BufferedAmount(),
|
|
mTrackingNumber);
|
|
}
|
|
|
|
if (NS_FAILED(aStatus)) {
|
|
MaybeReportErrorAndCloseIfOpen(aStatus);
|
|
return;
|
|
}
|
|
|
|
uint32_t count;
|
|
nsresult rv = mMultiplexStream->GetCount(&count);
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
|
|
if (count) {
|
|
EnsureCopying();
|
|
return;
|
|
}
|
|
|
|
// If we are waiting for initiating starttls, we can begin to
|
|
// activate tls now.
|
|
if (mWaitingForStartTLS && mReadyState == TCPReadyState::Open) {
|
|
ActivateTLS();
|
|
mWaitingForStartTLS = false;
|
|
// If we have pending data, we should send them, or fire
|
|
// a drain event if we are waiting for it.
|
|
if (!mPendingDataAfterStartTLS.IsEmpty()) {
|
|
while (!mPendingDataAfterStartTLS.IsEmpty()) {
|
|
nsCOMPtr<nsIInputStream> stream = mPendingDataAfterStartTLS[0];
|
|
mMultiplexStream->AppendStream(stream);
|
|
mPendingDataAfterStartTLS.RemoveElementAt(0);
|
|
}
|
|
EnsureCopying();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If we have a connected child, we let the child decide whether
|
|
// ondrain should be dispatched.
|
|
if (mWaitingForDrain && !mSocketBridgeParent) {
|
|
mWaitingForDrain = false;
|
|
FireEvent(NS_LITERAL_STRING("drain"));
|
|
}
|
|
|
|
if (mReadyState == TCPReadyState::Closing) {
|
|
if (mSocketOutputStream) {
|
|
mSocketOutputStream->Close();
|
|
mSocketOutputStream = nullptr;
|
|
}
|
|
mReadyState = TCPReadyState::Closed;
|
|
FireEvent(NS_LITERAL_STRING("close"));
|
|
}
|
|
}
|
|
|
|
void
|
|
TCPSocket::ActivateTLS()
|
|
{
|
|
nsCOMPtr<nsISupports> securityInfo;
|
|
mTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
|
|
nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(securityInfo);
|
|
if (socketControl) {
|
|
socketControl->StartTLS();
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType)
|
|
{
|
|
if (mSocketBridgeParent) {
|
|
mSocketBridgeParent->FireErrorEvent(aName, aType, mReadyState);
|
|
return NS_OK;
|
|
}
|
|
|
|
TCPSocketErrorEventInit init;
|
|
init.mBubbles = false;
|
|
init.mCancelable = false;
|
|
init.mName = aName;
|
|
init.mMessage = aType;
|
|
|
|
RefPtr<TCPSocketErrorEvent> event =
|
|
TCPSocketErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init);
|
|
MOZ_ASSERT(event);
|
|
event->SetTrusted(true);
|
|
bool dummy;
|
|
DispatchEvent(event, &dummy);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::FireEvent(const nsAString& aType)
|
|
{
|
|
if (mSocketBridgeParent) {
|
|
mSocketBridgeParent->FireEvent(aType, mReadyState);
|
|
return NS_OK;
|
|
}
|
|
|
|
AutoJSAPI api;
|
|
if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
JS::Rooted<JS::Value> val(api.cx());
|
|
return FireDataEvent(api.cx(), aType, val);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::FireDataArrayEvent(const nsAString& aType,
|
|
const InfallibleTArray<uint8_t>& buffer)
|
|
{
|
|
AutoJSAPI api;
|
|
if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
JSContext* cx = api.cx();
|
|
JS::Rooted<JS::Value> val(cx);
|
|
|
|
bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val);
|
|
if (ok) {
|
|
return FireDataEvent(cx, aType, val);
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::FireDataStringEvent(const nsAString& aType,
|
|
const nsACString& aString)
|
|
{
|
|
AutoJSAPI api;
|
|
if (NS_WARN_IF(!api.Init(GetOwnerGlobal()))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
JSContext* cx = api.cx();
|
|
JS::Rooted<JS::Value> val(cx);
|
|
|
|
bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val);
|
|
if (ok) {
|
|
return FireDataEvent(cx, aType, val);
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult
|
|
TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData)
|
|
{
|
|
MOZ_ASSERT(!mSocketBridgeParent);
|
|
|
|
RootedDictionary<TCPSocketEventInit> init(aCx);
|
|
init.mBubbles = false;
|
|
init.mCancelable = false;
|
|
init.mData = aData;
|
|
|
|
RefPtr<TCPSocketEvent> event =
|
|
TCPSocketEvent::Constructor(this, aType, init);
|
|
event->SetTrusted(true);
|
|
bool dummy;
|
|
DispatchEvent(event, &dummy);
|
|
return NS_OK;
|
|
}
|
|
|
|
JSObject*
|
|
TCPSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return TCPSocketBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
void
|
|
TCPSocket::GetHost(nsAString& aHost)
|
|
{
|
|
aHost.Assign(mHost);
|
|
}
|
|
|
|
uint32_t
|
|
TCPSocket::Port()
|
|
{
|
|
return mPort;
|
|
}
|
|
|
|
bool
|
|
TCPSocket::Ssl()
|
|
{
|
|
return mSsl;
|
|
}
|
|
|
|
uint64_t
|
|
TCPSocket::BufferedAmount()
|
|
{
|
|
if (mSocketBridgeChild) {
|
|
return mBufferedAmount;
|
|
}
|
|
if (mMultiplexStream) {
|
|
uint64_t available = 0;
|
|
mMultiplexStream->Available(&available);
|
|
return available;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
TCPSocket::Suspend()
|
|
{
|
|
if (mSocketBridgeChild) {
|
|
mSocketBridgeChild->SendSuspend();
|
|
return;
|
|
}
|
|
if (mInputStreamPump) {
|
|
mInputStreamPump->Suspend();
|
|
}
|
|
mSuspendCount++;
|
|
}
|
|
|
|
void
|
|
TCPSocket::Resume(mozilla::ErrorResult& aRv)
|
|
{
|
|
if (mSocketBridgeChild) {
|
|
mSocketBridgeChild->SendResume();
|
|
return;
|
|
}
|
|
if (!mSuspendCount) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
if (mInputStreamPump) {
|
|
mInputStreamPump->Resume();
|
|
}
|
|
mSuspendCount--;
|
|
}
|
|
|
|
nsresult
|
|
TCPSocket::MaybeReportErrorAndCloseIfOpen(nsresult status) {
|
|
// If we're closed, we've already reported the error or just don't need to
|
|
// report the error.
|
|
if (mReadyState == TCPReadyState::Closed) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// go through ::Closing state and then mark ::Closed
|
|
Close();
|
|
mReadyState = TCPReadyState::Closed;
|
|
|
|
if (NS_FAILED(status)) {
|
|
// Convert the status code to an appropriate error message.
|
|
|
|
nsString errorType, errName;
|
|
|
|
// security module? (and this is an error)
|
|
if ((static_cast<uint32_t>(status) & 0xFF0000) == 0x5a0000) {
|
|
nsCOMPtr<nsINSSErrorsService> errSvc = do_GetService("@mozilla.org/nss_errors_service;1");
|
|
// getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is
|
|
// somehow not in the set of covered errors.
|
|
uint32_t errorClass;
|
|
nsresult rv = errSvc->GetErrorClass(status, &errorClass);
|
|
if (NS_FAILED(rv)) {
|
|
errorType.AssignLiteral("SecurityProtocol");
|
|
} else {
|
|
switch (errorClass) {
|
|
case nsINSSErrorsService::ERROR_CLASS_BAD_CERT:
|
|
errorType.AssignLiteral("SecurityCertificate");
|
|
break;
|
|
default:
|
|
errorType.AssignLiteral("SecurityProtocol");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// NSS_SEC errors (happen below the base value because of negative vals)
|
|
if ((static_cast<int32_t>(status) & 0xFFFF) < abs(nsINSSErrorsService::NSS_SEC_ERROR_BASE)) {
|
|
switch (static_cast<SECErrorCodes>(status)) {
|
|
case SEC_ERROR_EXPIRED_CERTIFICATE:
|
|
errName.AssignLiteral("SecurityExpiredCertificateError");
|
|
break;
|
|
case SEC_ERROR_REVOKED_CERTIFICATE:
|
|
errName.AssignLiteral("SecurityRevokedCertificateError");
|
|
break;
|
|
// per bsmith, we will be unable to tell these errors apart very soon,
|
|
// so it makes sense to just folder them all together already.
|
|
case SEC_ERROR_UNKNOWN_ISSUER:
|
|
case SEC_ERROR_UNTRUSTED_ISSUER:
|
|
case SEC_ERROR_UNTRUSTED_CERT:
|
|
case SEC_ERROR_CA_CERT_INVALID:
|
|
errName.AssignLiteral("SecurityUntrustedCertificateIssuerError");
|
|
break;
|
|
case SEC_ERROR_INADEQUATE_KEY_USAGE:
|
|
errName.AssignLiteral("SecurityInadequateKeyUsageError");
|
|
break;
|
|
case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
|
|
errName.AssignLiteral("SecurityCertificateSignatureAlgorithmDisabledError");
|
|
break;
|
|
default:
|
|
errName.AssignLiteral("SecurityError");
|
|
break;
|
|
}
|
|
} else {
|
|
// NSS_SSL errors
|
|
switch (static_cast<SSLErrorCodes>(status)) {
|
|
case SSL_ERROR_NO_CERTIFICATE:
|
|
errName.AssignLiteral("SecurityNoCertificateError");
|
|
break;
|
|
case SSL_ERROR_BAD_CERTIFICATE:
|
|
errName.AssignLiteral("SecurityBadCertificateError");
|
|
break;
|
|
case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:
|
|
errName.AssignLiteral("SecurityUnsupportedCertificateTypeError");
|
|
break;
|
|
case SSL_ERROR_UNSUPPORTED_VERSION:
|
|
errName.AssignLiteral("SecurityUnsupportedTLSVersionError");
|
|
break;
|
|
case SSL_ERROR_BAD_CERT_DOMAIN:
|
|
errName.AssignLiteral("SecurityCertificateDomainMismatchError");
|
|
break;
|
|
default:
|
|
errName.AssignLiteral("SecurityError");
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// must be network
|
|
errorType.AssignLiteral("Network");
|
|
|
|
switch (status) {
|
|
// connect to host:port failed
|
|
case NS_ERROR_CONNECTION_REFUSED:
|
|
errName.AssignLiteral("ConnectionRefusedError");
|
|
break;
|
|
// network timeout error
|
|
case NS_ERROR_NET_TIMEOUT:
|
|
errName.AssignLiteral("NetworkTimeoutError");
|
|
break;
|
|
// hostname lookup failed
|
|
case NS_ERROR_UNKNOWN_HOST:
|
|
errName.AssignLiteral("DomainNotFoundError");
|
|
break;
|
|
case NS_ERROR_NET_INTERRUPT:
|
|
errName.AssignLiteral("NetworkInterruptError");
|
|
break;
|
|
default:
|
|
errName.AssignLiteral("NetworkError");
|
|
break;
|
|
}
|
|
}
|
|
|
|
Unused << NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType)));
|
|
}
|
|
|
|
return FireEvent(NS_LITERAL_STRING("close"));
|
|
}
|
|
|
|
void
|
|
TCPSocket::Close()
|
|
{
|
|
CloseHelper(true);
|
|
}
|
|
|
|
void
|
|
TCPSocket::CloseImmediately()
|
|
{
|
|
CloseHelper(false);
|
|
}
|
|
|
|
void
|
|
TCPSocket::CloseHelper(bool waitForUnsentData)
|
|
{
|
|
if (mReadyState == TCPReadyState::Closed || mReadyState == TCPReadyState::Closing) {
|
|
return;
|
|
}
|
|
|
|
mReadyState = TCPReadyState::Closing;
|
|
|
|
if (mSocketBridgeChild) {
|
|
mSocketBridgeChild->SendClose();
|
|
return;
|
|
}
|
|
|
|
uint32_t count = 0;
|
|
if (mMultiplexStream) {
|
|
mMultiplexStream->GetCount(&count);
|
|
}
|
|
if (!count || !waitForUnsentData) {
|
|
if (mSocketOutputStream) {
|
|
mSocketOutputStream->Close();
|
|
mSocketOutputStream = nullptr;
|
|
}
|
|
}
|
|
|
|
if (mSocketInputStream) {
|
|
mSocketInputStream->Close();
|
|
mSocketInputStream = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
TCPSocket::SendWithTrackingNumber(const nsACString& aData,
|
|
const uint32_t& aTrackingNumber,
|
|
mozilla::ErrorResult& aRv)
|
|
{
|
|
MOZ_ASSERT(mSocketBridgeParent);
|
|
mTrackingNumber = aTrackingNumber;
|
|
// The JSContext isn't necessary for string values; it's a codegen limitation.
|
|
Send(nullptr, aData, aRv);
|
|
}
|
|
|
|
bool
|
|
TCPSocket::Send(JSContext* aCx, const nsACString& aData, mozilla::ErrorResult& aRv)
|
|
{
|
|
if (mReadyState != TCPReadyState::Open) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return false;
|
|
}
|
|
|
|
uint64_t byteLength;
|
|
nsCOMPtr<nsIInputStream> stream;
|
|
if (mSocketBridgeChild) {
|
|
mSocketBridgeChild->SendSend(aData, ++mTrackingNumber);
|
|
byteLength = aData.Length();
|
|
} else {
|
|
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(stream), aData);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
return false;
|
|
}
|
|
rv = stream->Available(&byteLength);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
return false;
|
|
}
|
|
}
|
|
return Send(stream, byteLength);
|
|
}
|
|
|
|
void
|
|
TCPSocket::SendWithTrackingNumber(JSContext* aCx,
|
|
const ArrayBuffer& aData,
|
|
uint32_t aByteOffset,
|
|
const Optional<uint32_t>& aByteLength,
|
|
const uint32_t& aTrackingNumber,
|
|
mozilla::ErrorResult& aRv)
|
|
{
|
|
MOZ_ASSERT(mSocketBridgeParent);
|
|
mTrackingNumber = aTrackingNumber;
|
|
Send(aCx, aData, aByteOffset, aByteLength, aRv);
|
|
}
|
|
|
|
bool
|
|
TCPSocket::Send(JSContext* aCx,
|
|
const ArrayBuffer& aData,
|
|
uint32_t aByteOffset,
|
|
const Optional<uint32_t>& aByteLength,
|
|
mozilla::ErrorResult& aRv)
|
|
{
|
|
if (mReadyState != TCPReadyState::Open) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return false;
|
|
}
|
|
|
|
nsCOMPtr<nsIArrayBufferInputStream> stream;
|
|
|
|
aData.ComputeLengthAndData();
|
|
uint32_t byteLength = aByteLength.WasPassed() ? aByteLength.Value() : aData.Length();
|
|
|
|
if (mSocketBridgeChild) {
|
|
nsresult rv = mSocketBridgeChild->SendSend(aData, aByteOffset, byteLength, ++mTrackingNumber);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return false;
|
|
}
|
|
} else {
|
|
JS::Rooted<JSObject*> obj(aCx, aData.Obj());
|
|
JSAutoCompartment ac(aCx, obj);
|
|
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*obj));
|
|
|
|
stream = do_CreateInstance("@mozilla.org/io/arraybuffer-input-stream;1");
|
|
nsresult rv = stream->SetData(value, aByteOffset, byteLength, aCx);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return false;
|
|
}
|
|
}
|
|
return Send(stream, byteLength);
|
|
}
|
|
|
|
bool
|
|
TCPSocket::Send(nsIInputStream* aStream, uint32_t aByteLength)
|
|
{
|
|
uint64_t newBufferedAmount = BufferedAmount() + aByteLength;
|
|
bool bufferFull = newBufferedAmount > BUFFER_SIZE;
|
|
if (bufferFull) {
|
|
// If we buffered more than some arbitrary amount of data,
|
|
// (65535 right now) we should tell the caller so they can
|
|
// wait until ondrain is called if they so desire. Once all the
|
|
// buffered data has been written to the socket, ondrain is
|
|
// called.
|
|
mWaitingForDrain = true;
|
|
}
|
|
|
|
if (mSocketBridgeChild) {
|
|
// In the child, we just add the buffer length to our bufferedAmount and let
|
|
// the parent update our bufferedAmount when the data have been sent.
|
|
mBufferedAmount = newBufferedAmount;
|
|
return !bufferFull;
|
|
}
|
|
|
|
if (mWaitingForStartTLS) {
|
|
// When we are waiting for starttls, newStream is added to pendingData
|
|
// and will be appended to multiplexStream after tls had been set up.
|
|
mPendingDataAfterStartTLS.AppendElement(aStream);
|
|
} else if (mAsyncCopierActive) {
|
|
// While the AsyncCopier is still active..
|
|
mPendingDataWhileCopierActive.AppendElement(aStream);
|
|
} else {
|
|
mMultiplexStream->AppendStream(aStream);
|
|
}
|
|
|
|
EnsureCopying();
|
|
|
|
return !bufferFull;
|
|
}
|
|
|
|
TCPReadyState
|
|
TCPSocket::ReadyState()
|
|
{
|
|
return mReadyState;
|
|
}
|
|
|
|
TCPSocketBinaryType
|
|
TCPSocket::BinaryType()
|
|
{
|
|
if (mUseArrayBuffers) {
|
|
return TCPSocketBinaryType::Arraybuffer;
|
|
} else {
|
|
return TCPSocketBinaryType::String;
|
|
}
|
|
}
|
|
|
|
already_AddRefed<TCPSocket>
|
|
TCPSocket::CreateAcceptedSocket(nsIGlobalObject* aGlobal,
|
|
nsISocketTransport* aTransport,
|
|
bool aUseArrayBuffers)
|
|
{
|
|
RefPtr<TCPSocket> socket = new TCPSocket(aGlobal, EmptyString(), 0, false, aUseArrayBuffers);
|
|
nsresult rv = socket->InitWithTransport(aTransport);
|
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
|
return socket.forget();
|
|
}
|
|
|
|
already_AddRefed<TCPSocket>
|
|
TCPSocket::CreateAcceptedSocket(nsIGlobalObject* aGlobal,
|
|
TCPSocketChild* aBridge,
|
|
bool aUseArrayBuffers)
|
|
{
|
|
RefPtr<TCPSocket> socket = new TCPSocket(aGlobal, EmptyString(), 0, false, aUseArrayBuffers);
|
|
socket->InitWithSocketChild(aBridge);
|
|
return socket.forget();
|
|
}
|
|
|
|
already_AddRefed<TCPSocket>
|
|
TCPSocket::Constructor(const GlobalObject& aGlobal,
|
|
const nsAString& aHost,
|
|
uint16_t aPort,
|
|
const SocketOptions& aOptions,
|
|
mozilla::ErrorResult& aRv)
|
|
{
|
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
|
RefPtr<TCPSocket> socket =
|
|
new TCPSocket(global, aHost, aPort, aOptions.mUseSecureTransport,
|
|
aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer);
|
|
nsresult rv = socket->Init();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aRv.Throw(rv);
|
|
return nullptr;
|
|
}
|
|
|
|
return socket.forget();
|
|
}
|
|
|
|
nsresult
|
|
TCPSocket::CreateInputStreamPump()
|
|
{
|
|
if (!mSocketInputStream) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
nsresult rv;
|
|
mInputStreamPump = do_CreateInstance("@mozilla.org/network/input-stream-pump;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = mInputStreamPump->Init(mSocketInputStream, -1, -1, 0, 0, false, nullptr);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
uint64_t suspendCount = mSuspendCount;
|
|
while (suspendCount--) {
|
|
mInputStreamPump->Suspend();
|
|
}
|
|
|
|
rv = mInputStreamPump->AsyncRead(this, nullptr);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
|
|
int64_t aProgress, int64_t aProgressMax)
|
|
{
|
|
if (static_cast<uint32_t>(aStatus) != nsISocketTransport::STATUS_CONNECTED_TO) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mReadyState = TCPReadyState::Open;
|
|
FireEvent(NS_LITERAL_STRING("open"));
|
|
|
|
nsresult rv = CreateInputStreamPump();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
|
{
|
|
// Only used for detecting if the connection was refused.
|
|
|
|
uint64_t dummy;
|
|
nsresult rv = aStream->Available(&dummy);
|
|
if (NS_FAILED(rv)) {
|
|
MaybeReportErrorAndCloseIfOpen(NS_ERROR_CONNECTION_REFUSED);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
|
|
uint64_t aOffset, uint32_t aCount)
|
|
{
|
|
if (mUseArrayBuffers) {
|
|
nsTArray<uint8_t> buffer;
|
|
buffer.SetCapacity(aCount);
|
|
uint32_t actual;
|
|
nsresult rv = aStream->Read(reinterpret_cast<char*>(buffer.Elements()), aCount, &actual);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
MOZ_ASSERT(actual == aCount);
|
|
buffer.SetLength(actual);
|
|
|
|
if (mSocketBridgeParent) {
|
|
mSocketBridgeParent->FireArrayBufferDataEvent(buffer, mReadyState);
|
|
return NS_OK;
|
|
}
|
|
|
|
AutoJSAPI api;
|
|
if (!api.Init(GetOwnerGlobal())) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
JSContext* cx = api.cx();
|
|
|
|
JS::Rooted<JS::Value> value(cx);
|
|
if (!ToJSValue(cx, TypedArrayCreator<ArrayBuffer>(buffer), &value)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
FireDataEvent(cx, NS_LITERAL_STRING("data"), value);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCString data;
|
|
nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (mSocketBridgeParent) {
|
|
mSocketBridgeParent->FireStringDataEvent(data, mReadyState);
|
|
return NS_OK;
|
|
}
|
|
|
|
AutoJSAPI api;
|
|
if (!api.Init(GetOwnerGlobal())) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
JSContext* cx = api.cx();
|
|
|
|
JS::Rooted<JS::Value> value(cx);
|
|
if (!ToJSValue(cx, NS_ConvertASCIItoUTF16(data), &value)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
FireDataEvent(cx, NS_LITERAL_STRING("data"), value);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
|
|
{
|
|
uint32_t count;
|
|
nsresult rv = mMultiplexStream->GetCount(&count);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
bool bufferedOutput = count != 0;
|
|
|
|
mInputStreamPump = nullptr;
|
|
|
|
if (bufferedOutput && NS_SUCCEEDED(aStatus)) {
|
|
// If we have some buffered output still, and status is not an
|
|
// error, the other side has done a half-close, but we don't
|
|
// want to be in the close state until we are done sending
|
|
// everything that was buffered. We also don't want to call onclose
|
|
// yet.
|
|
return NS_OK;
|
|
}
|
|
|
|
// We call this even if there is no error.
|
|
MaybeReportErrorAndCloseIfOpen(aStatus);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
TCPSocket::SetSocketBridgeParent(TCPSocketParent* aBridgeParent)
|
|
{
|
|
mSocketBridgeParent = aBridgeParent;
|
|
}
|
|
|
|
void
|
|
TCPSocket::SetAppIdAndBrowser(uint32_t aAppId, bool aInIsolatedMozBrowser)
|
|
{
|
|
#ifdef MOZ_WIDGET_GONK
|
|
mAppId = aAppId;
|
|
mInIsolatedMozBrowser = aInIsolatedMozBrowser;
|
|
#endif
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::UpdateReadyState(uint32_t aReadyState)
|
|
{
|
|
MOZ_ASSERT(mSocketBridgeChild);
|
|
mReadyState = static_cast<TCPReadyState>(aReadyState);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount, uint32_t aTrackingNumber)
|
|
{
|
|
if (aTrackingNumber != mTrackingNumber) {
|
|
return NS_OK;
|
|
}
|
|
mBufferedAmount = aBufferedAmount;
|
|
if (!mBufferedAmount) {
|
|
if (mWaitingForDrain) {
|
|
mWaitingForDrain = false;
|
|
return FireEvent(NS_LITERAL_STRING("drain"));
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
TCPSocket::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
|
|
{
|
|
if (!strcmp(aTopic, "inner-window-destroyed")) {
|
|
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
|
|
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
|
|
uint64_t innerID;
|
|
nsresult rv = wrapper->GetData(&innerID);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (innerID == mInnerWindowID) {
|
|
Close();
|
|
}
|
|
} else if (!strcmp(aTopic, "profile-change-net-teardown")) {
|
|
Close();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/* static */
|
|
bool
|
|
TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal)
|
|
{
|
|
JS::Rooted<JSObject*> global(aCx, aGlobal);
|
|
return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
|
|
}
|