Bug 950660: Part 3 - make TCPSocket/TCPSocketChild interface an IDL interface r=jdm
authorRandell Jesup <rjesup@jesup.org>
Sun, 20 Sep 2015 23:05:20 -0400
changeset 294588 dfaa6b689bc00d216add359d8ca8a8e40c65a109
parent 294587 af45bcfab412d2db786648f281d1e85faf1c09f1
child 294589 8eda15c59d265f755582342dd027e97959b2429b
push id5619
push userj.parkouss@gmail.com
push dateMon, 21 Sep 2015 17:26:04 +0000
reviewersjdm
bugs950660
milestone43.0a1
Bug 950660: Part 3 - make TCPSocket/TCPSocketChild interface an IDL interface r=jdm
dom/network/TCPSocket.cpp
dom/network/TCPSocket.h
dom/network/TCPSocketChild.cpp
dom/network/TCPSocketChild.h
dom/network/interfaces/moz.build
dom/network/interfaces/nsITCPSocketCallback.idl
--- a/dom/network/TCPSocket.cpp
+++ b/dom/network/TCPSocket.cpp
@@ -138,16 +138,17 @@ NS_IMPL_RELEASE_INHERITED(TCPSocket, DOM
 
 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)
@@ -455,102 +456,107 @@ TCPSocket::ActivateTLS()
   nsCOMPtr<nsISupports> securityInfo;
   mTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
   nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(securityInfo);
   if (socketControl) {
     socketControl->StartTLS();
   }
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType)
 {
   if (mSocketBridgeParent) {
     mSocketBridgeParent->FireErrorEvent(aName, aType, mReadyState);
-    return;
+    return NS_OK;
   }
 
   TCPSocketErrorEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
   init.mName = aName;
   init.mMessage = aType;
 
   nsRefPtr<TCPSocketErrorEvent> event =
     TCPSocketErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init);
+  MOZ_ASSERT(event);
   event->SetTrusted(true);
   bool dummy;
   DispatchEvent(event, &dummy);
+  return NS_OK;
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::FireEvent(const nsAString& aType)
 {
   if (mSocketBridgeParent) {
     mSocketBridgeParent->FireEvent(aType, mReadyState);
-    return;
+    return NS_OK;
   }
 
   AutoJSAPI api;
   if (NS_WARN_IF(!api.Init(GetOwner()))) {
-    return;
+    return NS_ERROR_FAILURE;
   }
   JS::Rooted<JS::Value> val(api.cx());
-  FireDataEvent(api.cx(), aType, val);
+  return FireDataEvent(api.cx(), aType, val);
 }
 
-void
-TCPSocket::FireDataEvent(const nsAString& aType,
-                         const InfallibleTArray<uint8_t>& buffer)
+NS_IMETHODIMP
+TCPSocket::FireDataArrayEvent(const nsAString& aType,
+                              const InfallibleTArray<uint8_t>& buffer)
 {
   AutoJSAPI api;
   if (NS_WARN_IF(!api.Init(GetOwner()))) {
     return NS_ERROR_FAILURE;
   }
   JSContext* cx = api.cx();
   JS::Rooted<JS::Value> val(cx);
 
   bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val);
   if (ok) {
-    FireDataEvent(aType, val);
+    return FireDataEvent(cx, aType, val);
   }
+  return NS_ERROR_FAILURE;
 }
 
-void
-TCPSocket::FireDataEvent(const nsAString& aType,
-                         const nsACString& aString)
+NS_IMETHODIMP
+TCPSocket::FireDataStringEvent(const nsAString& aType,
+                               const nsACString& aString)
 {
   AutoJSAPI api;
   if (NS_WARN_IF(!api.Init(GetOwner()))) {
     return NS_ERROR_FAILURE;
   }
   JSContext* cx = api.cx();
   JS::Rooted<JS::Value> val(cx);
 
   bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val);
   if (ok) {
-    FireDataEvent(aType, val);
+    return FireDataEvent(cx, aType, val);
   }
+  return NS_ERROR_FAILURE;
 }
 
-void
+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;
 
   nsRefPtr<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);
 }
 
@@ -728,21 +734,20 @@ TCPSocket::MaybeReportErrorAndCloseIfOpe
           errName.AssignLiteral("NetworkInterruptError");
           break;
         default:
           errName.AssignLiteral("NetworkError");
           break;
       }
     }
 
-    FireErrorEvent(errName, errorType);
+    NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType)));
   }
 
-  FireEvent(NS_LITERAL_STRING("close"));
-  return NS_OK;
+  return FireEvent(NS_LITERAL_STRING("close"));
 }
 
 void
 TCPSocket::Close()
 {
   if (mReadyState == TCPReadyState::Closed || mReadyState == TCPReadyState::Closing) {
     return;
   }
@@ -1100,36 +1105,38 @@ void
 TCPSocket::SetAppIdAndBrowser(uint32_t aAppId, bool aInBrowser)
 {
 #ifdef MOZ_WIDGET_GONK
   mAppId = aAppId;
   mInBrowser = aInBrowser;
 #endif
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::UpdateReadyState(uint32_t aReadyState)
 {
   MOZ_ASSERT(mSocketBridgeChild);
   mReadyState = static_cast<TCPReadyState>(aReadyState);
+  return NS_OK;
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount, uint32_t aTrackingNumber)
 {
   if (aTrackingNumber != mTrackingNumber) {
-    return;
+    return NS_OK;
   }
   mBufferedAmount = aBufferedAmount;
   if (!mBufferedAmount) {
     if (mWaitingForDrain) {
       mWaitingForDrain = false;
-      FireEvent(NS_LITERAL_STRING("drain"));
+      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.
--- a/dom/network/TCPSocket.h
+++ b/dom/network/TCPSocket.h
@@ -9,16 +9,17 @@
 #include "mozilla/dom/TCPSocketBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsITransport.h"
 #include "nsIStreamListener.h"
 #include "nsIAsyncInputStream.h"
 #include "nsISupportsImpl.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
+#include "nsITCPSocketCallback.h"
 #include "js/RootingAPI.h"
 
 class nsISocketTransport;
 class nsIInputStreamPump;
 class nsIScriptableInputStream;
 class nsIBinaryInputStream;
 class nsIMultiplexInputStream;
 class nsIAsyncStreamCopier;
@@ -67,28 +68,30 @@ private:
 };
 
 class TCPSocket final : public DOMEventTargetHelper
                       , public nsIStreamListener
                       , public nsITransportEventSink
                       , public nsIInputStreamCallback
                       , public nsIObserver
                       , public nsSupportsWeakReference
+                      , public nsITCPSocketCallback
 {
 public:
   TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
             bool aSsl, bool aUseArrayBuffers);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TCPSocket, DOMEventTargetHelper)
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSITRANSPORTEVENTSINK
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIOBSERVER
+  NS_DECL_NSITCPSOCKETCALLBACK
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetOwner();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
@@ -149,30 +152,16 @@ public:
   IMPL_EVENT_HANDLER(error);
   IMPL_EVENT_HANDLER(close);
 
   nsresult Init();
 
   // Inform this socket that a buffered send() has completed sending.
   void NotifyCopyComplete(nsresult aStatus);
 
-  // Set this child socket's number of buffered bytes, based on the count from the parent
-  // process associated with the given sequence id.
-  void UpdateBufferedAmount(uint32_t aAmount, uint32_t aTrackingNumber);
-  // Set this child socket's ready state, based on the state in the parent process.
-  void UpdateReadyState(uint32_t aReadyState);
-  // Dispatch an "error" event at this object.
-  void FireErrorEvent(const nsAString& aName, const nsAString& aMessage);
-  // Dispatch an event of the given type at this object.
-  void FireEvent(const nsAString& aType);
-  // Dispatch a "data" event at this object.
-  void FireDataEvent(const nsAString& aType, const InfallibleTArray<uint8_t>& buffer);
-  void FireDataEvent(const nsAString& aType, const nsAString& aString);
-  void FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData);
-
   // Initialize this socket from a low-level connection that hasn't connected yet
   // (called from RecvOpenBind() in TCPSocketParent).
   nsresult InitWithUnconnectedTransport(nsISocketTransport* aTransport);
 
 private:
   ~TCPSocket();
 
   // Initialize this socket with an existing IPC actor.
--- a/dom/network/TCPSocketChild.cpp
+++ b/dom/network/TCPSocketChild.cpp
@@ -6,16 +6,17 @@
 
 #include <algorithm>
 #include "TCPSocketChild.h"
 #include "mozilla/unused.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
+#include "nsITCPSocketCallback.h"
 #include "TCPSocket.h"
 #include "nsContentUtils.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 using mozilla::net::gNeckoChild;
 
 namespace IPC {
@@ -88,27 +89,27 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
 
 TCPSocketChild::TCPSocketChild(const nsAString& aHost, const uint16_t& aPort)
 : mHost(aHost)
 , mPort(aPort)
 {
 }
 
 void
-TCPSocketChild::SendOpen(TCPSocket* aSocket, bool aUseSSL, bool aUseArrayBuffers)
+TCPSocketChild::SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers)
 {
   mSocket = aSocket;
 
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this, mHost, mPort);
   PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers);
 }
 
 void
-TCPSocketChild::SendWindowlessOpenBind(TCPSocket* aSocket,
+TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
                                        const nsACString& aRemoteHost, uint16_t aRemotePort,
                                        const nsACString& aLocalHost, uint16_t aLocalPort,
                                        bool aUseSSL)
 {
   mSocket = aSocket;
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this,
                                          NS_ConvertUTF8toUTF16(aRemoteHost),
@@ -159,19 +160,19 @@ TCPSocketChild::RecvCallback(const nsStr
   } else if (aData.type() == CallbackData::TTCPError) {
     const TCPError& err(aData.get_TCPError());
     mSocket->FireErrorEvent(err.name(), err.message());
 
   } else if (aData.type() == CallbackData::TSendableData) {
     const SendableData& data = aData.get_SendableData();
 
     if (data.type() == SendableData::TArrayOfuint8_t) {
-      mSocket->FireDataEvent(cx, aType, data.get_ArrayOfuint8_t());
+      mSocket->FireDataArrayEvent(aType, data.get_ArrayOfuint8_t());
     } else if (data.type() == SendableData::TnsCString) {
-      mSocket->FireDataEvent(aType, data.get_nsCString());
+      mSocket->FireDataStringEvent(aType, data.get_nsCString());
     } else {
       MOZ_CRASH("Invalid callback data type!");
     }
   } else {
     MOZ_CRASH("Invalid callback type!");
   }
   return true;
 }
--- a/dom/network/TCPSocketChild.h
+++ b/dom/network/TCPSocketChild.h
@@ -7,16 +7,18 @@
 #ifndef mozilla_dom_TCPSocketChild_h
 #define mozilla_dom_TCPSocketChild_h
 
 #include "mozilla/net/PTCPSocketChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 #include "js/TypeDecls.h"
 
+class nsITCPSocketCallback;
+
 namespace IPC {
 bool
 DeserializeArrayBuffer(JSContext* cx,
                        const InfallibleTArray<uint8_t>& aBuffer,
                        JS::MutableHandle<JS::Value> aVal);
 }
 
 namespace mozilla {
@@ -31,31 +33,31 @@ public:
 
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
 protected:
   TCPSocketChildBase();
   virtual ~TCPSocketChildBase();
 
-  nsRefPtr<TCPSocket> mSocket;
+  nsCOMPtr<nsITCPSocketCallback> mSocket;
   bool mIPCOpen;
 };
 
 class TCPSocketChild : public mozilla::net::PTCPSocketChild
                      , public TCPSocketChildBase
 {
 public:
   NS_IMETHOD_(MozExternalRefCountType) Release() override;
 
   TCPSocketChild(const nsAString& aHost, const uint16_t& aPort);
   ~TCPSocketChild();
 
-  void SendOpen(TCPSocket* aSocket, bool aUseSSL, bool aUseArrayBuffers);
-  void SendWindowlessOpenBind(TCPSocket* aSocket,
+  void SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers);
+  void SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
                               const nsACString& aRemoteHost, uint16_t aRemotePort,
                               const nsACString& aLocalHost, uint16_t aLocalPort,
                               bool aUseSSL);
   NS_IMETHOD SendSendArray(nsTArray<uint8_t>& aArray,
                            uint32_t aTrackingNumber);
   void SendSend(const nsACString& aData, uint32_t aTrackingNumber);
   nsresult SendSend(const ArrayBuffer& aData,
                     uint32_t aByteOffset,
--- a/dom/network/interfaces/moz.build
+++ b/dom/network/interfaces/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIMozNavigatorNetwork.idl',
+    'nsITCPSocketCallback.idl',
     'nsIUDPSocketChild.idl',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     XPIDL_SOURCES += [
         'nsIDOMNetworkStatsManager.idl',
         'nsINetworkStatsServiceProxy.idl',
     ]
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsITCPSocketCallback.idl
@@ -0,0 +1,62 @@
+/* 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/. */
+
+/**
+ * MozTCPSocket exposes a TCP client and server sockets
+ * to highly privileged apps. It provides a buffered, non-blocking
+ * interface for sending. For receiving, it uses an asynchronous,
+ * event handler based interface.
+ */
+
+#include "domstubs.idl"
+
+%{C++
+template<class T> class InfallibleTArray;
+%}
+[ref] native nsUint8TArrayRef(InfallibleTArray<uint8_t>);
+[ptr] native JSContextPtr(JSContext);
+
+
+/*
+ * This interface is implemented in TCPSocket.cpp as an internal interface
+ * for use in cross-process socket implementation.
+ * Needed to account for multiple possible types that can be provided to
+ * the socket callbacks as arguments.
+ */
+[scriptable, uuid(ac2c4b69-cb79-4767-b1ce-bcf62945cd39)]
+interface nsITCPSocketCallback : nsISupports {
+  // Limitation of TCPSocket's buffer size.
+  const unsigned long BUFFER_SIZE = 65536;
+
+  // Dispatch an "error" event at this object with the given name and type.
+  void fireErrorEvent(in AString name, in AString type);
+
+  // Dispatch a "data" event at this object with a string
+  void fireDataStringEvent(in DOMString type, in ACString data);
+
+  // Dispatch a "data" event at this object with an Array
+  void fireDataArrayEvent(in DOMString type, [const] in nsUint8TArrayRef data);
+
+  // Dispatch a "data" event at this object with an ArrayBuffer argument
+  void fireDataEvent(in JSContextPtr cx, in DOMString type, in jsval data);
+
+  // Dispatch an event of the given type at this object.
+  void fireEvent(in DOMString type);
+
+  // Update the DOM object's readyState.
+  // @param readyState
+  //        new ready state
+  void updateReadyState(in unsigned long readystate);
+
+  // Update the DOM object's bufferedAmount value with a tracking number to
+  // to allow tracking of which writes are "in-flight"
+  // @param bufferedAmount
+  //        TCPSocket parent's bufferedAmount.
+  // @param trackingNumber
+  //        A number to ensure the bufferedAmount is updated after data
+  //        from child are sent to parent.
+  void updateBufferedAmount(in uint32_t bufferedAmount,
+                            in uint32_t trackingNumber);
+};
+