Backed out 2 changesets (bug 1908320) for causing wr failures on align-items-baseline...
[gecko.git] / dom / network / UDPSocket.cpp
blob4fe771ac1d3df43eb0de88bb76f987b04557445d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "UDPSocket.h"
8 #include "mozilla/AsyncEventDispatcher.h"
9 #include "mozilla/dom/File.h"
10 #include "mozilla/dom/ErrorEvent.h"
11 #include "mozilla/dom/network/UDPSocketChild.h"
12 #include "mozilla/dom/UDPMessageEvent.h"
13 #include "mozilla/dom/UDPSocketBinding.h"
14 #include "mozilla/dom/UnionTypes.h"
15 #include "mozilla/dom/RootedDictionary.h"
16 #include "mozilla/net/DNS.h"
17 #include "nsComponentManagerUtils.h"
18 #include "nsContentUtils.h"
19 #include "nsINetAddr.h"
20 #include "nsStringStream.h"
22 namespace mozilla::dom {
24 NS_IMPL_ISUPPORTS(UDPSocket::ListenerProxy, nsIUDPSocketListener,
25 nsIUDPSocketInternal)
27 NS_IMPL_CYCLE_COLLECTION_CLASS(UDPSocket)
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(UDPSocket,
30 DOMEventTargetHelper)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpened)
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClosed)
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
35 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(UDPSocket, DOMEventTargetHelper)
36 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpened)
37 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClosed)
38 tmp->CloseWithReason(NS_OK);
39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
41 NS_IMPL_ADDREF_INHERITED(UDPSocket, DOMEventTargetHelper)
42 NS_IMPL_RELEASE_INHERITED(UDPSocket, DOMEventTargetHelper)
44 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UDPSocket)
45 NS_INTERFACE_MAP_ENTRY(nsIUDPSocketListener)
46 NS_INTERFACE_MAP_ENTRY(nsIUDPSocketInternal)
47 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
49 /* static */
50 already_AddRefed<UDPSocket> UDPSocket::Constructor(const GlobalObject& aGlobal,
51 const UDPOptions& aOptions,
52 ErrorResult& aRv) {
53 nsCOMPtr<nsPIDOMWindowInner> ownerWindow =
54 do_QueryInterface(aGlobal.GetAsSupports());
55 if (!ownerWindow) {
56 aRv.Throw(NS_ERROR_FAILURE);
57 return nullptr;
60 bool addressReuse = aOptions.mAddressReuse;
61 bool loopback = aOptions.mLoopback;
63 nsCString remoteAddress;
64 if (aOptions.mRemoteAddress.WasPassed()) {
65 CopyUTF16toUTF8(aOptions.mRemoteAddress.Value(), remoteAddress);
66 } else {
67 remoteAddress.SetIsVoid(true);
70 Nullable<uint16_t> remotePort;
71 if (aOptions.mRemotePort.WasPassed()) {
72 remotePort.SetValue(aOptions.mRemotePort.Value());
74 if (remotePort.Value() == 0) {
75 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
76 return nullptr;
80 nsString localAddress;
81 if (aOptions.mLocalAddress.WasPassed()) {
82 localAddress = aOptions.mLocalAddress.Value();
84 // check if localAddress is a valid IPv4/6 address
85 NS_ConvertUTF16toUTF8 address(localAddress);
86 if (!net::HostIsIPLiteral(address)) {
87 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
88 return nullptr;
90 } else {
91 SetDOMStringToNull(localAddress);
94 Nullable<uint16_t> localPort;
95 if (aOptions.mLocalPort.WasPassed()) {
96 localPort.SetValue(aOptions.mLocalPort.Value());
98 if (localPort.Value() == 0) {
99 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
100 return nullptr;
104 RefPtr<UDPSocket> socket =
105 new UDPSocket(ownerWindow, remoteAddress, remotePort);
106 aRv = socket->Init(localAddress, localPort, addressReuse, loopback);
108 if (NS_WARN_IF(aRv.Failed())) {
109 return nullptr;
112 return socket.forget();
115 UDPSocket::UDPSocket(nsPIDOMWindowInner* aOwner,
116 const nsCString& aRemoteAddress,
117 const Nullable<uint16_t>& aRemotePort)
118 : DOMEventTargetHelper(aOwner),
119 mRemoteAddress(aRemoteAddress),
120 mRemotePort(aRemotePort),
121 mAddressReuse(false),
122 mLoopback(false),
123 mReadyState(SocketReadyState::Opening) {
124 MOZ_ASSERT(aOwner);
126 Document* aDoc = aOwner->GetExtantDoc();
127 if (aDoc) {
128 aDoc->DisallowBFCaching();
132 UDPSocket::~UDPSocket() { CloseWithReason(NS_OK); }
134 JSObject* UDPSocket::WrapObject(JSContext* aCx,
135 JS::Handle<JSObject*> aGivenProto) {
136 return UDPSocket_Binding::Wrap(aCx, this, aGivenProto);
139 void UDPSocket::DisconnectFromOwner() {
140 DOMEventTargetHelper::DisconnectFromOwner();
141 CloseWithReason(NS_OK);
144 already_AddRefed<Promise> UDPSocket::Close() {
145 MOZ_ASSERT(mClosed);
147 RefPtr<Promise> promise = mClosed;
149 if (mReadyState == SocketReadyState::Closed) {
150 return promise.forget();
153 CloseWithReason(NS_OK);
154 return promise.forget();
157 void UDPSocket::CloseWithReason(nsresult aReason) {
158 if (mReadyState == SocketReadyState::Closed) {
159 return;
162 if (mOpened) {
163 if (mReadyState == SocketReadyState::Opening) {
164 // reject openedPromise with AbortError if socket is closed without error
165 nsresult openFailedReason =
166 NS_FAILED(aReason) ? aReason : NS_ERROR_DOM_ABORT_ERR;
167 mOpened->MaybeReject(openFailedReason);
171 mReadyState = SocketReadyState::Closed;
173 if (mListenerProxy) {
174 mListenerProxy->Disconnect();
175 mListenerProxy = nullptr;
178 if (mSocket) {
179 mSocket->Close();
180 mSocket = nullptr;
183 if (mSocketChild) {
184 mSocketChild->Close();
185 mSocketChild = nullptr;
188 if (mClosed) {
189 if (NS_SUCCEEDED(aReason)) {
190 mClosed->MaybeResolveWithUndefined();
191 } else {
192 mClosed->MaybeReject(aReason);
196 mPendingMcastCommands.Clear();
199 void UDPSocket::JoinMulticastGroup(const nsAString& aMulticastGroupAddress,
200 ErrorResult& aRv) {
201 if (mReadyState == SocketReadyState::Closed) {
202 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
203 return;
206 if (mReadyState == SocketReadyState::Opening) {
207 MulticastCommand joinCommand(MulticastCommand::Join,
208 aMulticastGroupAddress);
209 mPendingMcastCommands.AppendElement(joinCommand);
210 return;
213 MOZ_ASSERT(mSocket || mSocketChild);
215 NS_ConvertUTF16toUTF8 address(aMulticastGroupAddress);
217 if (mSocket) {
218 MOZ_ASSERT(!mSocketChild);
220 aRv = mSocket->JoinMulticast(address, ""_ns);
221 NS_WARNING_ASSERTION(!aRv.Failed(), "JoinMulticast failed");
223 return;
226 MOZ_ASSERT(mSocketChild);
228 mSocketChild->JoinMulticast(address, ""_ns);
231 void UDPSocket::LeaveMulticastGroup(const nsAString& aMulticastGroupAddress,
232 ErrorResult& aRv) {
233 if (mReadyState == SocketReadyState::Closed) {
234 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
235 return;
238 if (mReadyState == SocketReadyState::Opening) {
239 MulticastCommand leaveCommand(MulticastCommand::Leave,
240 aMulticastGroupAddress);
241 mPendingMcastCommands.AppendElement(leaveCommand);
242 return;
245 MOZ_ASSERT(mSocket || mSocketChild);
247 nsCString address = NS_ConvertUTF16toUTF8(aMulticastGroupAddress);
248 if (mSocket) {
249 MOZ_ASSERT(!mSocketChild);
251 aRv = mSocket->LeaveMulticast(address, ""_ns);
252 NS_WARNING_ASSERTION(!aRv.Failed(), "mSocket->LeaveMulticast failed");
253 return;
256 MOZ_ASSERT(mSocketChild);
258 mSocketChild->LeaveMulticast(address, ""_ns);
261 nsresult UDPSocket::DoPendingMcastCommand() {
262 MOZ_ASSERT(mReadyState == SocketReadyState::Open,
263 "Multicast command can only be executed after socket opened");
265 for (uint32_t i = 0; i < mPendingMcastCommands.Length(); ++i) {
266 MulticastCommand& command = mPendingMcastCommands[i];
267 ErrorResult rv;
269 switch (command.mCommand) {
270 case MulticastCommand::Join: {
271 JoinMulticastGroup(command.mAddress, rv);
272 break;
274 case MulticastCommand::Leave: {
275 LeaveMulticastGroup(command.mAddress, rv);
276 break;
280 if (NS_WARN_IF(rv.Failed())) {
281 return rv.StealNSResult();
285 mPendingMcastCommands.Clear();
286 return NS_OK;
289 bool UDPSocket::Send(const StringOrBlobOrArrayBufferOrArrayBufferView& aData,
290 const Optional<nsAString>& aRemoteAddress,
291 const Optional<Nullable<uint16_t>>& aRemotePort,
292 ErrorResult& aRv) {
293 if (mReadyState != SocketReadyState::Open) {
294 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
295 return false;
298 MOZ_ASSERT(mSocket || mSocketChild);
300 // If the remote address and port were not specified in the constructor or as
301 // arguments, throw InvalidAccessError.
302 nsCString remoteAddress;
303 if (aRemoteAddress.WasPassed()) {
304 CopyUTF16toUTF8(aRemoteAddress.Value(), remoteAddress);
305 UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
306 } else if (!mRemoteAddress.IsVoid()) {
307 remoteAddress = mRemoteAddress;
308 UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
309 } else {
310 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
311 return false;
314 uint16_t remotePort;
315 if (aRemotePort.WasPassed() && !aRemotePort.Value().IsNull()) {
316 remotePort = aRemotePort.Value().Value();
317 } else if (!mRemotePort.IsNull()) {
318 remotePort = mRemotePort.Value();
319 } else {
320 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
321 return false;
324 nsCOMPtr<nsIInputStream> stream;
325 if (aData.IsBlob()) {
326 Blob& blob = aData.GetAsBlob();
328 blob.CreateInputStream(getter_AddRefs(stream), aRv);
329 if (NS_WARN_IF(aRv.Failed())) {
330 return false;
332 } else {
333 nsresult rv;
334 nsCOMPtr<nsIStringInputStream> strStream =
335 do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
336 if (NS_WARN_IF(NS_FAILED(rv))) {
337 aRv.Throw(rv);
338 return false;
341 if (aData.IsString()) {
342 NS_ConvertUTF16toUTF8 data(aData.GetAsString());
343 aRv = strStream->SetData(data.BeginReading(), data.Length());
344 } else {
345 Vector<char> data;
346 if (!AppendTypedArrayDataTo(aData, data)) {
347 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
348 return false;
350 size_t length = data.length();
351 aRv = strStream->AdoptData(data.extractOrCopyRawBuffer(), length);
354 if (NS_WARN_IF(aRv.Failed())) {
355 return false;
358 stream = strStream;
361 if (mSocket) {
362 aRv = mSocket->SendBinaryStream(remoteAddress, remotePort, stream);
363 } else if (mSocketChild) {
364 aRv = mSocketChild->SendBinaryStream(remoteAddress, remotePort, stream);
367 if (NS_WARN_IF(aRv.Failed())) {
368 return false;
371 return true;
374 nsresult UDPSocket::InitLocal(const nsAString& aLocalAddress,
375 const uint16_t& aLocalPort) {
376 nsresult rv;
377 nsCOMPtr<nsIUDPSocket> sock =
378 do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
379 if (NS_FAILED(rv)) {
380 return rv;
383 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
384 if (!global) {
385 return NS_ERROR_FAILURE;
388 nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
389 if (!principal) {
390 return NS_ERROR_FAILURE;
393 if (aLocalAddress.IsEmpty()) {
394 rv = sock->Init(aLocalPort, /* loopback = */ false, principal,
395 mAddressReuse, /* optionalArgc = */ 1);
396 } else {
397 PRNetAddr prAddr;
398 PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr);
399 PR_StringToNetAddr(NS_ConvertUTF16toUTF8(aLocalAddress).BeginReading(),
400 &prAddr);
401 UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__,
402 NS_ConvertUTF16toUTF8(aLocalAddress).get(), aLocalPort));
404 mozilla::net::NetAddr addr(&prAddr);
405 rv = sock->InitWithAddress(&addr, principal, mAddressReuse,
406 /* optionalArgc = */ 1);
408 if (NS_FAILED(rv)) {
409 return rv;
412 rv = sock->SetMulticastLoopback(mLoopback);
413 if (NS_FAILED(rv)) {
414 return rv;
417 mSocket = sock;
419 // Get real local address and local port
420 nsCOMPtr<nsINetAddr> localAddr;
421 rv = mSocket->GetLocalAddr(getter_AddRefs(localAddr));
422 if (NS_FAILED(rv)) {
423 return rv;
426 nsCString localAddress;
427 rv = localAddr->GetAddress(localAddress);
428 if (NS_FAILED(rv)) {
429 return rv;
431 CopyUTF8toUTF16(localAddress, mLocalAddress);
433 uint16_t localPort;
434 rv = localAddr->GetPort(&localPort);
435 if (NS_FAILED(rv)) {
436 return rv;
438 mLocalPort.SetValue(localPort);
440 mListenerProxy = new ListenerProxy(this);
442 rv = mSocket->AsyncListen(mListenerProxy);
443 if (NS_FAILED(rv)) {
444 return rv;
447 mReadyState = SocketReadyState::Open;
448 rv = DoPendingMcastCommand();
449 if (NS_FAILED(rv)) {
450 return rv;
453 mOpened->MaybeResolveWithUndefined();
455 return NS_OK;
458 nsresult UDPSocket::InitRemote(const nsAString& aLocalAddress,
459 const uint16_t& aLocalPort) {
460 RefPtr<UDPSocketChild> sock = new UDPSocketChild();
462 mListenerProxy = new ListenerProxy(this);
464 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
465 if (!global) {
466 return NS_ERROR_FAILURE;
469 nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
470 if (!principal) {
471 return NS_ERROR_FAILURE;
474 nsresult rv = sock->Bind(mListenerProxy, principal,
475 NS_ConvertUTF16toUTF8(aLocalAddress), aLocalPort,
476 mAddressReuse, mLoopback, 0, 0);
478 if (NS_FAILED(rv)) {
479 return rv;
482 mSocketChild = sock;
484 return NS_OK;
487 nsresult UDPSocket::Init(const nsString& aLocalAddress,
488 const Nullable<uint16_t>& aLocalPort,
489 const bool& aAddressReuse, const bool& aLoopback) {
490 MOZ_ASSERT(!mSocket && !mSocketChild);
492 mLocalAddress = aLocalAddress;
493 mLocalPort = aLocalPort;
494 mAddressReuse = aAddressReuse;
495 mLoopback = aLoopback;
497 nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
499 ErrorResult rv;
500 mOpened = Promise::Create(global, rv);
501 if (NS_WARN_IF(rv.Failed())) {
502 return rv.StealNSResult();
505 mClosed = Promise::Create(global, rv);
506 if (NS_WARN_IF(rv.Failed())) {
507 return rv.StealNSResult();
510 class OpenSocketRunnable final : public Runnable {
511 public:
512 explicit OpenSocketRunnable(UDPSocket* aSocket)
513 : mozilla::Runnable("OpenSocketRunnable"), mSocket(aSocket) {}
515 NS_IMETHOD Run() override {
516 MOZ_ASSERT(mSocket);
518 if (mSocket->mReadyState != SocketReadyState::Opening) {
519 return NS_OK;
522 uint16_t localPort = 0;
523 if (!mSocket->mLocalPort.IsNull()) {
524 localPort = mSocket->mLocalPort.Value();
527 nsresult rv;
528 if (!XRE_IsParentProcess()) {
529 rv = mSocket->InitRemote(mSocket->mLocalAddress, localPort);
530 } else {
531 rv = mSocket->InitLocal(mSocket->mLocalAddress, localPort);
534 if (NS_WARN_IF(NS_FAILED(rv))) {
535 mSocket->CloseWithReason(NS_ERROR_DOM_NETWORK_ERR);
538 return NS_OK;
541 private:
542 RefPtr<UDPSocket> mSocket;
545 nsCOMPtr<nsIRunnable> runnable = new OpenSocketRunnable(this);
547 return NS_DispatchToMainThread(runnable);
550 void UDPSocket::HandleReceivedData(const nsACString& aRemoteAddress,
551 const uint16_t& aRemotePort,
552 const nsTArray<uint8_t>& aData) {
553 if (mReadyState != SocketReadyState::Open) {
554 return;
557 if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
558 return;
561 if (NS_FAILED(DispatchReceivedData(aRemoteAddress, aRemotePort, aData))) {
562 CloseWithReason(NS_ERROR_UNEXPECTED);
566 nsresult UDPSocket::DispatchReceivedData(const nsACString& aRemoteAddress,
567 const uint16_t& aRemotePort,
568 const nsTArray<uint8_t>& aData) {
569 AutoJSAPI jsapi;
571 if (NS_WARN_IF(!jsapi.Init(GetOwnerWindow()))) {
572 return NS_ERROR_FAILURE;
575 JSContext* cx = jsapi.cx();
577 // Copy packet data to ArrayBuffer
578 ErrorResult error;
579 JS::Rooted<JSObject*> arrayBuf(cx, ArrayBuffer::Create(cx, aData, error));
581 if (NS_WARN_IF(error.Failed())) {
582 return error.StealNSResult();
585 JS::Rooted<JS::Value> jsData(cx, JS::ObjectValue(*arrayBuf));
587 // Create DOM event
588 RootedDictionary<UDPMessageEventInit> init(cx);
589 CopyUTF8toUTF16(aRemoteAddress, init.mRemoteAddress);
590 init.mRemotePort = aRemotePort;
591 init.mData = jsData;
593 RefPtr<UDPMessageEvent> udpEvent =
594 UDPMessageEvent::Constructor(this, u"message"_ns, init);
596 if (NS_WARN_IF(!udpEvent)) {
597 return NS_ERROR_FAILURE;
600 udpEvent->SetTrusted(true);
602 RefPtr<AsyncEventDispatcher> asyncDispatcher =
603 new AsyncEventDispatcher(this, udpEvent.forget());
605 return asyncDispatcher->PostDOMEvent();
608 // nsIUDPSocketListener
610 NS_IMETHODIMP
611 UDPSocket::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage) {
612 // nsIUDPSocketListener callbacks should be invoked on main thread.
613 MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
615 // Create appropriate JS object for message
616 FallibleTArray<uint8_t>& buffer = aMessage->GetDataAsTArray();
618 nsCOMPtr<nsINetAddr> addr;
619 if (NS_WARN_IF(NS_FAILED(aMessage->GetFromAddr(getter_AddRefs(addr))))) {
620 return NS_OK;
623 nsCString remoteAddress;
624 if (NS_WARN_IF(NS_FAILED(addr->GetAddress(remoteAddress)))) {
625 return NS_OK;
628 uint16_t remotePort;
629 if (NS_WARN_IF(NS_FAILED(addr->GetPort(&remotePort)))) {
630 return NS_OK;
633 HandleReceivedData(remoteAddress, remotePort, buffer);
634 return NS_OK;
637 NS_IMETHODIMP
638 UDPSocket::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) {
639 // nsIUDPSocketListener callbacks should be invoked on main thread.
640 MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
642 CloseWithReason(aStatus);
644 return NS_OK;
647 // nsIUDPSocketInternal
649 NS_IMETHODIMP
650 UDPSocket::CallListenerError(const nsACString& aMessage,
651 const nsACString& aFilename,
652 uint32_t aLineNumber) {
653 CloseWithReason(NS_ERROR_DOM_NETWORK_ERR);
655 return NS_OK;
658 NS_IMETHODIMP
659 UDPSocket::CallListenerReceivedData(const nsACString& aRemoteAddress,
660 uint16_t aRemotePort,
661 const nsTArray<uint8_t>& aData) {
662 HandleReceivedData(aRemoteAddress, aRemotePort, aData);
664 return NS_OK;
667 NS_IMETHODIMP
668 UDPSocket::CallListenerOpened() {
669 if (mReadyState != SocketReadyState::Opening) {
670 return NS_OK;
673 MOZ_ASSERT(mSocketChild);
675 // Get real local address and local port
676 CopyUTF8toUTF16(mSocketChild->LocalAddress(), mLocalAddress);
678 mLocalPort.SetValue(mSocketChild->LocalPort());
680 mReadyState = SocketReadyState::Open;
681 nsresult rv = DoPendingMcastCommand();
683 if (NS_WARN_IF(NS_FAILED(rv))) {
684 CloseWithReason(rv);
685 return NS_OK;
688 mOpened->MaybeResolveWithUndefined();
690 return NS_OK;
693 NS_IMETHODIMP
694 UDPSocket::CallListenerConnected() {
695 // This shouldn't be called here.
696 MOZ_CRASH();
698 return NS_OK;
701 NS_IMETHODIMP
702 UDPSocket::CallListenerClosed() {
703 CloseWithReason(NS_OK);
705 return NS_OK;
708 } // namespace mozilla::dom