Files
palemoon27/dom/network/TCPSocket.cpp
T
roytam1 f5a7b27f3d import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1211204 - Remove possible false-negative with D3D9 texture memory reporting. r=mattwoodrow (74adbcad19)
- Bug 1200595 - D3D9 TextureData implementation. r=Bas (2624bd59a3)
- Bug 1200595 - MacIOSurface TextureData implementation. r=mattwoodrow (98130b5d6b)
- Bug 1200595 - EGLImage TextureData implementation. r=mattwoodrow (8b732af2da)
- Bug 1200595 - AndroidSurface TextureData implementation. r=mattwoodrow (79443ff5d7)
- Bug 1200595 - SharedSurface TextureData implementation. r=jgilbert (aadc8c5601)
- Bug 1200595 - DIB TextureData implementation. r=Bas (d7bae178ad)
- Bug 1200595 - Merge TextureClient and ClientTexture back into TextureClient. r=mattwoodrow (890d76ff9b)
- Bug 1200595 - Consolidate the TextureClient's destruction logic. r=mattwoodrow (fa9ac2e414)
- Allow asynchronous D3D11 TextureClients on the main thread. (bug 1217665 part 5, r=nical) (cb77fa88cc)
- Implement the direct bitmap drawing model for plugins. (bug 1217665 part 6, r=mattwoodrow) (82f9015e5a)
- Implement the direct DXGI drawing model for plugins. (bug 1217665 part 7, r=mattwoodrow) (cdeeb52a91)
- Implement the DidComposite NPAPI callback. (bug 1217665 part 8, r=mattwoodrow) (4eb2a790ae)
- Add an NPN_GetValue query to find the browser's DXGI adapter. (bug 1217665 part 10, r=aklotz,mattwoodrow) (84af4e525a)
- Create a D3D11 content device even if D2D is blocked. (bug 1217665 part 11, r=jrmuizel) (142441808e)
- Bug 1229665 - Convert widget clip regions to LayoutDevicePixels. r=botond. (edf746b278)
- Bug 1204715 - Move browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd} to moz.build. r=mshal (8c6b4709bc)
- Bug 1212773 - Pass a base directory to jar maker, instead of a chrome directory. r=gps (1f644706c6)
- Bustage fix for bug 1210687 on a CLOSED TREE. r=me (4cd7000101)
- Bug 1212773 - Extend jar maker syntax for jar file location. r=gps (8119aa2b98)
- Bug 1164039 - moved TelemetryTimestamps.jsm and tests to toolkit/componets/telemetry/. r=dexter (942145656e)
- Bug 1184705 - Search A/B testing cohort identifier should be recorded in FHR, r=rnewman. (b5cf397cf0)
- Bug 1230047 (part 2) - Make several PaintWindow() functions use LayoutDevice coordinates. r=kats. (246e1321ca)
- Bug 1218454 - part 1 - move DialogValueHolder::Get out-of-line; r=bz (2d65438b37)
- Bug 1218454 - part 2 - don't #include nsContentUtils.h from CallbackObject.h; r=bz (59e3b60749)
- Bug 1205945 part.1 Remove unnecessary member of IMEInputHandler, mLastDispatchedCompositionString r=smichaud (2223df8aec)
- Bug 1205945 part.2 Add DispatchCompositionStartEvent() and move the code of OnStartIMEComposition() into it r=smichaud (25449c2bca)
- Bug 1205945 part.3 Move the code of OnUpdateIMEComposition() into DispatchCompositionChangeEvent() r=smichaud (e85a65f2f7)
- Bug 1205945 part.4 Move the code of OnEndIMEComposition() into DispatchCompositionCommitEvent() r=smichaud (21e6218c06)
- Bug 1205945 part.5 Emulate mSelectedRange at dispatching compositionchange or compositioncommit event until OnSelectionChange() is called r=smichaud (f82baaea8e)
- Bug 1205945 part.6 IMEInputHandler::GetAttributedSubstringFromRange() should return stored composition string if the range is in the composition string r=smichaud (53eabe028b)
- Bug 1205399 - Follow-up to fix build bustage for platforms without OS X 10.10 and -Wswitch enabled. r=mstange (c2e8eda9b5)
- Bug 1220337 - Don't show alternate notification actions on OS X 10.8. r=MattN (5ef8449dcc)
- Bug 1224738 - Fix alternate action index getter name on OS X. r=MattN (a6f83ce706)
- Bug 1225908 - AsmSimdTypeToLaneType. r=bbouvier (f850c10bff)
- Bug 1224389 - Odin: refactor types/signatures/values (r=bbouvier) (31bc615781)
- Bug 1224389 - Odin: simplify AsmJSModule global data allocation (r=bbouvier) (3ba518183a)
- Bug 1222684 - IonMonkey: MIPS: Implement callWithPatch and patchCall. r=luke (0ede7a61c3)
- Bug 1224814 - "TraceLogger: Remove redundant checks in BaselineJIT". r=hv1989 (b3865fbdf0)
- Bug 1228369: Rename CodeOffsetLabel into CodeOffset; r=luke (1d0aba3710)
- Bug 1224389 - Odin: refactor stubs, func-ptrs, and masm use (r=bbouvier) (2834725644)
- Bug 1228340: Get rid of the js_ prefix for CodeSpec, CodeName, NumCodeSpecs; r=jorendorff (8ff2b3bcd5)
- Bug 1176214 - Part 2: Preliminary adjustments. r=waldo (f9e2adca8a)
- Bug 1176214 - Part 3: VM core changes. r=waldo (cf5b24fe18)
- Bug 1176214 - Part 4: VM built-in lib changes. r=waldo (603335d2aa)
- Bug 1176214 - Part 5: Ion changes. r=h4writer (b589713e1c)
- Bug 1176214 - Part 6: Odin changes. r=luke (f1dcb025d2)
- Bug 1176214 - Part 7: Ctypes, shell, xpconnect, etc. r=waldo (bdc78e0558)
- Bug 1199578 - test case. r=waldo (6a94fd455a)
- Bug 1211409 - load/store exclusive for ARM-32. r=jolesen (38efc4882e)
- Bug 1205390 - guard against asm.js compilation not being available. r=me (aeefe98cfd)
- Make test runnable on non-Nightly (no bug) r=me (27930210ed)
- Bug 1176214 - Part 8: jit-test changes. r=bbouvier (31c4f42eea)
- Bug 1176214 - Part 9: tests changes. r=bbouvier (a245687f43)
- Bug 1176214 - Part 10: jsapi-tests changes. r=bbouvier (610e0002a2)
- bug 1198656 remove unnecessary reinterpret_casts r=padenot (bc5a67d521)
- bug 1198656 refactor acquiring the content into an object method r=padenot (b7b062fbf1)
- bug 1198656 clear references in mJSChannels on successful content acquire r=padenot (65e5ee1856)
- bug 1198656 delay AudioBuffer allocation until required r=padenot (57d0fd0d01)
- bug 1199559 remove now unused SetRawChannelContents r=padenot (40685ef783)
- Bug 1203616 - Properly scale the input buffer of a WaveShaperNode before processing it with the curve. r=karlt (12e9592a9e)
- Bug 1186343: Throw an InvalidStateError when we set the curve attribute of a WaveShaperNode with a Float32Array of length less than 2; r=padenot,smaug (df83b21fd0)
- bug 1188244 throw in SetCurve() on OOM r=padenot (17967b7b58)
- Bug 1176214 - Part 11: Changes to DOM, except for WebGL. r=bz, r=clb (c40e5c2a68)
- Bug 1176214 - Part 12: Changes to WebGL. r=bz, r=clb (228c90da3c)
- Bug 1176214 - Part 13: Changes to ipc. r=mrbkap (4b11d4e509)
- Bug 1176214 - Part 14: Changes to netwerk. r=jduell (91568c8444)
- Bug 1176214 - Part 15: Changes to xpcom. r=nfroyd (df8d080070)
- Revert "Bug 1176214 - Part 14: Changes to netwerk. r=jduell" (d0aedbac94)
- improved backport of PM because of newer JS_GetArrayBufferData (9d8188ff5e)
- bug 1199559 write decodeAudioData buffer in a format suitable for direct use by AudioBuffer r=padenot (da00bab1a1)
- bug 1225003 null-check mBuffer in SizeOfExcludingThis() r=padenot (c01d389f10)
- Bug 1225365 - Fix assertion in the nsScriptNameSpaceManager memory reporter. r=bz. (a5605a8923)
- Bug 1229458 - Remove SizeOfIncludingThisMustBeUnshared() from string classes. r=mccr8. (26abcea276)
- Bug 1214506. Ensure OggReader sets proper IDs for its tracks. r=jya (a270b02301)
- bits of Bug 1188812 - Obtain CDM can render capability and store into MediaInfo (321388180e)
- Bug 1226450 - Report audio/video codecs used in HTMLMediaElement and WebAudio via telemetry. r=jya (e722b409b7)
2023-04-11 11:15:41 +08:00

1207 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 "nsINetworkStatsServiceProxy.h"
#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(nsPIDOMWindow* 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)
#ifdef MOZ_WIDGET_GONK
, mTxBytes(0)
, mRxBytes(0)
, mAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID)
, mInBrowser(false)
#endif
{
if (aGlobal) {
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
if (window && window->IsOuterWindow()) {
window = window->GetCurrentInnerWindow();
}
if (window) {
mInnerWindowID = window->WindowID();
}
}
}
TCPSocket::~TCPSocket()
{
}
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<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainThread);
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<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
mTransport->SetEventSink(this, mainThread);
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) {
obs->AddObserver(this, "inner-window-destroyed", true);
}
if (XRE_GetProcessType() == GeckoProcessType_Content) {
mReadyState = TCPReadyState::Connecting;
mSocketBridgeChild = new TCPSocketChild(mHost, mPort);
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);
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;
mMultiplexStream->RemoveStream(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) {
mSocketOutputStream->Close();
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;
}
NS_IMETHODIMP
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) {
#ifdef MOZ_WIDGET_GONK
// Save network statistics once the connection is closed.
// For now this function is Gonk-specific.
SaveNetworkStats(true);
#endif
// 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;
}
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;
}
}
NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType)));
}
return FireEvent(NS_LITERAL_STRING("close"));
}
void
TCPSocket::Close()
{
if (mReadyState == TCPReadyState::Closed || mReadyState == TCPReadyState::Closing) {
return;
}
mReadyState = TCPReadyState::Closing;
if (mSocketBridgeChild) {
mSocketBridgeChild->SendClose();
return;
}
uint32_t count = 0;
mMultiplexStream->GetCount(&count);
if (!count) {
mSocketOutputStream->Close();
}
mSocketInputStream->Close();
}
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 {
mMultiplexStream->AppendStream(aStream);
}
EnsureCopying();
#ifdef MOZ_WIDGET_GONK
// Collect transmitted amount for network statistics.
mTxBytes += aByteLength;
SaveNetworkStats(false);
#endif
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()
{
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);
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)
{
#ifdef MOZ_WIDGET_GONK
// Collect received amount for network statistics.
mRxBytes += aCount;
SaveNetworkStats(false);
#endif
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 aInBrowser)
{
#ifdef MOZ_WIDGET_GONK
mAppId = aAppId;
mInBrowser = aInBrowser;
#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;
}
#ifdef MOZ_WIDGET_GONK
void
TCPSocket::SaveNetworkStats(bool aEnforce)
{
if (!mTxBytes && !mRxBytes) {
// There is no traffic at all. No need to save statistics.
return;
}
// If "enforce" is false, the traffic amount is saved to NetworkStatsServiceProxy
// only when the total amount exceeds the predefined threshold value.
// The purpose is to avoid too much overhead for collecting statistics.
uint32_t totalBytes = mTxBytes + mRxBytes;
if (!aEnforce && totalBytes < NETWORK_STATS_THRESHOLD) {
return;
}
nsCOMPtr<nsINetworkStatsServiceProxy> nssProxy =
do_GetService("@mozilla.org/networkstatsServiceProxy;1");
if (!nssProxy) {
return;
}
nssProxy->SaveAppStats(mAppId, mInBrowser, mActiveNetworkInfo, PR_Now(),
mRxBytes, mTxBytes, false, nullptr);
// Reset the counters once the statistics is saved to NetworkStatsServiceProxy.
mTxBytes = mRxBytes = 0;
}
#endif
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) {
nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
if (obs) {
obs->RemoveObserver(this, "inner-window-destroyed");
}
Close();
}
}
return NS_OK;
}
/* static */
bool
TCPSocket::ShouldTCPSocketExist(JSContext* aCx, JSObject* aGlobal)
{
JS::Rooted<JSObject*> global(aCx, aGlobal);
if (nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global))) {
return true;
}
const char* const perms[] = { "tcp-socket", nullptr };
return Preferences::GetBool("dom.mozTCPSocket.enabled") &&
CheckAnyPermissions(aCx, global, perms);
}