mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 18:32:00 +00:00

This will mean that in places like the tight loop in GetTypeIndex() we would no longer require calling strlen() on the input type argument once per loop iteration. Depends on D20236 Differential Revision: https://phabricator.services.mozilla.com/D20237 --HG-- extra : moz-landing-system : lando
571 lines
16 KiB
C++
571 lines
16 KiB
C++
/* -*- 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 "nsIServiceManager.h"
|
|
#include "UDPSocketParent.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsIUDPSocket.h"
|
|
#include "nsINetAddr.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/ipc/InputStreamUtils.h"
|
|
#include "mozilla/net/DNS.h"
|
|
#include "mozilla/net/NeckoCommon.h"
|
|
#include "mozilla/net/PNeckoParent.h"
|
|
#include "nsIPermissionManager.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "mozilla/ipc/PBackgroundParent.h"
|
|
#include "mtransport/runnable_utils.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace net;
|
|
|
|
namespace dom {
|
|
|
|
NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener)
|
|
|
|
UDPSocketParent::UDPSocketParent(PBackgroundParent* aManager)
|
|
: mBackgroundManager(aManager), mIPCOpen(true) {}
|
|
|
|
UDPSocketParent::UDPSocketParent(PNeckoParent* aManager)
|
|
: mBackgroundManager(nullptr), mIPCOpen(true) {}
|
|
|
|
UDPSocketParent::~UDPSocketParent() {}
|
|
|
|
bool UDPSocketParent::Init(const IPC::Principal& aPrincipal,
|
|
const nsACString& aFilter) {
|
|
MOZ_ASSERT_IF(mBackgroundManager, !aPrincipal);
|
|
// will be used once we move all UDPSocket to PBackground, or
|
|
// if we add in Principal checking for mtransport
|
|
Unused << mBackgroundManager;
|
|
|
|
mPrincipal = aPrincipal;
|
|
if (net::UsingNeckoIPCSecurity() && mPrincipal &&
|
|
!ContentParent::IgnoreIPCPrincipal()) {
|
|
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
|
|
if (!permMgr) {
|
|
NS_WARNING("No PermissionManager available!");
|
|
return false;
|
|
}
|
|
|
|
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
|
permMgr->TestExactPermissionFromPrincipal(
|
|
mPrincipal, NS_LITERAL_CSTRING("udp-socket"), &permission);
|
|
if (permission != nsIPermissionManager::ALLOW_ACTION) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!aFilter.IsEmpty()) {
|
|
nsAutoCString contractId(NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX);
|
|
contractId.Append(aFilter);
|
|
nsCOMPtr<nsISocketFilterHandler> filterHandler =
|
|
do_GetService(contractId.get());
|
|
if (filterHandler) {
|
|
nsresult rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
|
|
if (NS_FAILED(rv)) {
|
|
printf_stderr(
|
|
"Cannot create filter that content specified. "
|
|
"filter name: %s, error code: %u.",
|
|
aFilter.BeginReading(), static_cast<uint32_t>(rv));
|
|
return false;
|
|
}
|
|
} else {
|
|
printf_stderr(
|
|
"Content doesn't have a valid filter. "
|
|
"filter name: %s.",
|
|
aFilter.BeginReading());
|
|
return false;
|
|
}
|
|
}
|
|
// We don't have browser actors in xpcshell, and hence can't run automated
|
|
// tests without this loophole.
|
|
if (net::UsingNeckoIPCSecurity() && !mFilter &&
|
|
(!mPrincipal || ContentParent::IgnoreIPCPrincipal())) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// PUDPSocketParent methods
|
|
|
|
mozilla::ipc::IPCResult UDPSocketParent::RecvBind(
|
|
const UDPAddressInfo& aAddressInfo, const bool& aAddressReuse,
|
|
const bool& aLoopback, const uint32_t& recvBufferSize,
|
|
const uint32_t& sendBufferSize) {
|
|
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(),
|
|
aAddressInfo.port()));
|
|
|
|
if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(),
|
|
aAddressReuse, aLoopback, recvBufferSize,
|
|
sendBufferSize))) {
|
|
FireInternalError(__LINE__);
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsCOMPtr<nsINetAddr> localAddr;
|
|
mSocket->GetLocalAddr(getter_AddRefs(localAddr));
|
|
|
|
nsCString addr;
|
|
if (NS_FAILED(localAddr->GetAddress(addr))) {
|
|
FireInternalError(__LINE__);
|
|
return IPC_OK();
|
|
}
|
|
|
|
uint16_t port;
|
|
if (NS_FAILED(localAddr->GetPort(&port))) {
|
|
FireInternalError(__LINE__);
|
|
return IPC_OK();
|
|
}
|
|
|
|
UDPSOCKET_LOG(
|
|
("%s: SendCallbackOpened: %s:%u", __FUNCTION__, addr.get(), port));
|
|
mozilla::Unused << SendCallbackOpened(UDPAddressInfo(addr, port));
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsresult UDPSocketParent::BindInternal(const nsCString& aHost,
|
|
const uint16_t& aPort,
|
|
const bool& aAddressReuse,
|
|
const bool& aLoopback,
|
|
const uint32_t& recvBufferSize,
|
|
const uint32_t& sendBufferSize) {
|
|
nsresult rv;
|
|
|
|
UDPSOCKET_LOG(
|
|
("%s: [this=%p] %s:%u addressReuse: %d loopback: %d recvBufferSize: "
|
|
"%" PRIu32 ", sendBufferSize: %" PRIu32,
|
|
__FUNCTION__, this, nsCString(aHost).get(), aPort, aAddressReuse,
|
|
aLoopback, recvBufferSize, sendBufferSize));
|
|
|
|
nsCOMPtr<nsIUDPSocket> sock =
|
|
do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (aHost.IsEmpty()) {
|
|
rv = sock->Init(aPort, false, mPrincipal, aAddressReuse,
|
|
/* optional_argc = */ 1);
|
|
} else {
|
|
PRNetAddr prAddr;
|
|
PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
|
|
PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
|
|
if (status != PR_SUCCESS) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mozilla::net::NetAddr addr;
|
|
PRNetAddrToNetAddr(&prAddr, &addr);
|
|
rv = sock->InitWithAddress(&addr, mPrincipal, aAddressReuse,
|
|
/* optional_argc = */ 1);
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsINetAddr> laddr;
|
|
rv = sock->GetLocalAddr(getter_AddRefs(laddr));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
uint16_t family;
|
|
rv = laddr->GetFamily(&family);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
if (family == nsINetAddr::FAMILY_INET) {
|
|
rv = sock->SetMulticastLoopback(aLoopback);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
// TODO: once bug 1252759 is fixed query buffer first and only increase
|
|
if (recvBufferSize != 0) {
|
|
rv = sock->SetRecvBufferSize(recvBufferSize);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
UDPSOCKET_LOG(
|
|
("%s: [this=%p] %s:%u failed to set recv buffer size to: %" PRIu32,
|
|
__FUNCTION__, this, nsCString(aHost).get(), aPort, recvBufferSize));
|
|
}
|
|
}
|
|
if (sendBufferSize != 0) {
|
|
rv = sock->SetSendBufferSize(sendBufferSize);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
UDPSOCKET_LOG(
|
|
("%s: [this=%p] %s:%u failed to set send buffer size to: %" PRIu32,
|
|
__FUNCTION__, this, nsCString(aHost).get(), aPort, sendBufferSize));
|
|
}
|
|
}
|
|
|
|
// register listener
|
|
rv = sock->AsyncListen(this);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
mSocket = sock;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static nsCOMPtr<nsIEventTarget> GetSTSThread() {
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIEventTarget> sts_thread;
|
|
|
|
sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
return sts_thread;
|
|
}
|
|
|
|
static void CheckSTSThread() {
|
|
DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread();
|
|
|
|
ASSERT_ON_THREAD(sts_thread.value);
|
|
}
|
|
|
|
// Proxy the Connect() request to the STS thread, since it may block and
|
|
// should be done there.
|
|
mozilla::ipc::IPCResult UDPSocketParent::RecvConnect(
|
|
const UDPAddressInfo& aAddressInfo) {
|
|
nsCOMPtr<nsIEventTarget> target = GetCurrentThreadEventTarget();
|
|
Unused << NS_WARN_IF(NS_FAILED(GetSTSThread()->Dispatch(
|
|
WrapRunnable(RefPtr<UDPSocketParent>(this), &UDPSocketParent::DoConnect,
|
|
mSocket, target, aAddressInfo),
|
|
NS_DISPATCH_NORMAL)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
void UDPSocketParent::DoSendConnectResponse(
|
|
const UDPAddressInfo& aAddressInfo) {
|
|
// can't use directly with WrapRunnable due to warnings
|
|
mozilla::Unused << SendCallbackConnected(aAddressInfo);
|
|
}
|
|
|
|
void UDPSocketParent::SendConnectResponse(nsIEventTarget* aThread,
|
|
const UDPAddressInfo& aAddressInfo) {
|
|
Unused << NS_WARN_IF(NS_FAILED(aThread->Dispatch(
|
|
WrapRunnable(RefPtr<UDPSocketParent>(this),
|
|
&UDPSocketParent::DoSendConnectResponse, aAddressInfo),
|
|
NS_DISPATCH_NORMAL)));
|
|
}
|
|
|
|
// Runs on STS thread
|
|
void UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
|
|
nsCOMPtr<nsIEventTarget>& aReturnThread,
|
|
const UDPAddressInfo& aAddressInfo) {
|
|
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(),
|
|
aAddressInfo.port()));
|
|
if (NS_FAILED(ConnectInternal(aAddressInfo.addr(), aAddressInfo.port()))) {
|
|
SendInternalError(aReturnThread, __LINE__);
|
|
return;
|
|
}
|
|
CheckSTSThread();
|
|
|
|
nsCOMPtr<nsINetAddr> localAddr;
|
|
aSocket->GetLocalAddr(getter_AddRefs(localAddr));
|
|
|
|
nsCString addr;
|
|
if (NS_FAILED(localAddr->GetAddress(addr))) {
|
|
SendInternalError(aReturnThread, __LINE__);
|
|
return;
|
|
}
|
|
|
|
uint16_t port;
|
|
if (NS_FAILED(localAddr->GetPort(&port))) {
|
|
SendInternalError(aReturnThread, __LINE__);
|
|
return;
|
|
}
|
|
|
|
UDPSOCKET_LOG(
|
|
("%s: SendConnectResponse: %s:%u", __FUNCTION__, addr.get(), port));
|
|
SendConnectResponse(aReturnThread, UDPAddressInfo(addr, port));
|
|
}
|
|
|
|
nsresult UDPSocketParent::ConnectInternal(const nsCString& aHost,
|
|
const uint16_t& aPort) {
|
|
nsresult rv;
|
|
|
|
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort));
|
|
|
|
if (!mSocket) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
PRNetAddr prAddr;
|
|
memset(&prAddr, 0, sizeof(prAddr));
|
|
PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
|
|
PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
|
|
if (status != PR_SUCCESS) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mozilla::net::NetAddr addr;
|
|
PRNetAddrToNetAddr(&prAddr, &addr);
|
|
|
|
rv = mSocket->Connect(&addr);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UDPSocketParent::RecvOutgoingData(
|
|
const UDPData& aData, const UDPSocketAddr& aAddr) {
|
|
if (!mSocket) {
|
|
NS_WARNING("sending socket is closed");
|
|
FireInternalError(__LINE__);
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsresult rv;
|
|
if (mFilter) {
|
|
if (aAddr.type() != UDPSocketAddr::TNetAddr) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
// TODO, Packet filter doesn't support input stream yet.
|
|
if (aData.type() != UDPData::TArrayOfuint8_t) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool allowed;
|
|
const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
|
|
rv = mFilter->FilterPacket(&aAddr.get_NetAddr(), data.Elements(),
|
|
data.Length(), nsISocketFilter::SF_OUTGOING,
|
|
&allowed);
|
|
|
|
// Sending unallowed data, kill content.
|
|
if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
|
|
return IPC_FAIL(this, "Content tried to send non STUN packet");
|
|
}
|
|
}
|
|
|
|
switch (aData.type()) {
|
|
case UDPData::TArrayOfuint8_t:
|
|
Send(aData.get_ArrayOfuint8_t(), aAddr);
|
|
break;
|
|
case UDPData::TIPCStream:
|
|
Send(aData.get_IPCStream(), aAddr);
|
|
break;
|
|
default:
|
|
MOZ_ASSERT(false, "Invalid data type!");
|
|
return IPC_OK();
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
void UDPSocketParent::Send(const InfallibleTArray<uint8_t>& aData,
|
|
const UDPSocketAddr& aAddr) {
|
|
nsresult rv;
|
|
uint32_t count;
|
|
switch (aAddr.type()) {
|
|
case UDPSocketAddr::TUDPAddressInfo: {
|
|
const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
|
|
rv = mSocket->Send(addrInfo.addr(), addrInfo.port(), aData.Elements(),
|
|
aData.Length(), &count);
|
|
break;
|
|
}
|
|
case UDPSocketAddr::TNetAddr: {
|
|
const NetAddr& addr(aAddr.get_NetAddr());
|
|
rv = mSocket->SendWithAddress(&addr, aData.Elements(), aData.Length(),
|
|
&count);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_ASSERT(false, "Invalid address type!");
|
|
return;
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv)) || count == 0) {
|
|
FireInternalError(__LINE__);
|
|
}
|
|
}
|
|
|
|
void UDPSocketParent::Send(const IPCStream& aStream,
|
|
const UDPSocketAddr& aAddr) {
|
|
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStream);
|
|
|
|
if (NS_WARN_IF(!stream)) {
|
|
return;
|
|
}
|
|
|
|
nsresult rv;
|
|
switch (aAddr.type()) {
|
|
case UDPSocketAddr::TUDPAddressInfo: {
|
|
const UDPAddressInfo& addrInfo(aAddr.get_UDPAddressInfo());
|
|
rv = mSocket->SendBinaryStream(addrInfo.addr(), addrInfo.port(), stream);
|
|
break;
|
|
}
|
|
case UDPSocketAddr::TNetAddr: {
|
|
const NetAddr& addr(aAddr.get_NetAddr());
|
|
rv = mSocket->SendBinaryStreamWithAddress(&addr, stream);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_ASSERT(false, "Invalid address type!");
|
|
return;
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
FireInternalError(__LINE__);
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UDPSocketParent::RecvJoinMulticast(
|
|
const nsCString& aMulticastAddress, const nsCString& aInterface) {
|
|
if (!mSocket) {
|
|
NS_WARNING("multicast socket is closed");
|
|
FireInternalError(__LINE__);
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsresult rv = mSocket->JoinMulticast(aMulticastAddress, aInterface);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
FireInternalError(__LINE__);
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UDPSocketParent::RecvLeaveMulticast(
|
|
const nsCString& aMulticastAddress, const nsCString& aInterface) {
|
|
if (!mSocket) {
|
|
NS_WARNING("multicast socket is closed");
|
|
FireInternalError(__LINE__);
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsresult rv = mSocket->LeaveMulticast(aMulticastAddress, aInterface);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
FireInternalError(__LINE__);
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UDPSocketParent::RecvClose() {
|
|
if (!mSocket) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsresult rv = mSocket->Close();
|
|
mSocket = nullptr;
|
|
|
|
mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UDPSocketParent::RecvRequestDelete() {
|
|
mozilla::Unused << Send__delete__(this);
|
|
return IPC_OK();
|
|
}
|
|
|
|
void UDPSocketParent::ActorDestroy(ActorDestroyReason why) {
|
|
MOZ_ASSERT(mIPCOpen);
|
|
mIPCOpen = false;
|
|
if (mSocket) {
|
|
mSocket->Close();
|
|
}
|
|
mSocket = nullptr;
|
|
}
|
|
|
|
// nsIUDPSocketListener
|
|
|
|
NS_IMETHODIMP
|
|
UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket,
|
|
nsIUDPMessage* aMessage) {
|
|
// receiving packet from remote host, forward the message content to child
|
|
// process
|
|
if (!mIPCOpen) {
|
|
return NS_OK;
|
|
}
|
|
|
|
uint16_t port;
|
|
nsCString ip;
|
|
nsCOMPtr<nsINetAddr> fromAddr;
|
|
aMessage->GetFromAddr(getter_AddRefs(fromAddr));
|
|
fromAddr->GetPort(&port);
|
|
fromAddr->GetAddress(ip);
|
|
|
|
nsCString data;
|
|
aMessage->GetData(data);
|
|
|
|
const char* buffer = data.get();
|
|
uint32_t len = data.Length();
|
|
UDPSOCKET_LOG(("%s: %s:%u, length %u", __FUNCTION__, ip.get(), port, len));
|
|
|
|
if (mFilter) {
|
|
bool allowed;
|
|
mozilla::net::NetAddr addr;
|
|
fromAddr->GetNetAddr(&addr);
|
|
nsresult rv = mFilter->FilterPacket(&addr, (const uint8_t*)buffer, len,
|
|
nsISocketFilter::SF_INCOMING, &allowed);
|
|
// Receiving unallowed data, drop.
|
|
if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
|
|
if (!allowed) {
|
|
UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
FallibleTArray<uint8_t> fallibleArray;
|
|
if (!fallibleArray.InsertElementsAt(0, buffer, len, fallible)) {
|
|
FireInternalError(__LINE__);
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
InfallibleTArray<uint8_t> infallibleArray;
|
|
infallibleArray.SwapElements(fallibleArray);
|
|
|
|
// compose callback
|
|
mozilla::Unused << SendCallbackReceivedData(UDPAddressInfo(ip, port),
|
|
infallibleArray);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
UDPSocketParent::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) {
|
|
// underlying socket is dead, send state update to child process
|
|
if (mIPCOpen) {
|
|
mozilla::Unused << SendCallbackClosed();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void UDPSocketParent::FireInternalError(uint32_t aLineNo) {
|
|
if (!mIPCOpen) {
|
|
return;
|
|
}
|
|
|
|
mozilla::Unused << SendCallbackError(NS_LITERAL_CSTRING("Internal error"),
|
|
NS_LITERAL_CSTRING(__FILE__), aLineNo);
|
|
}
|
|
|
|
void UDPSocketParent::SendInternalError(nsIEventTarget* aThread,
|
|
uint32_t aLineNo) {
|
|
UDPSOCKET_LOG(("SendInternalError: %u", aLineNo));
|
|
Unused << NS_WARN_IF(NS_FAILED(aThread->Dispatch(
|
|
WrapRunnable(RefPtr<UDPSocketParent>(this),
|
|
&UDPSocketParent::FireInternalError, aLineNo),
|
|
NS_DISPATCH_NORMAL)));
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|