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 296091 dfaa6b689bc00d216add359d8ca8a8e40c65a109
parent 296090 af45bcfab412d2db786648f281d1e85faf1c09f1
child 296092 8eda15c59d265f755582342dd027e97959b2429b
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs950660
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
+};
+