Bug 733002: DataChannel DOM implementation w/ m3 fix r=smaug
authorRandell Jesup <rjesup@jesup.org>
Wed, 03 Oct 2012 19:51:47 -0400
changeset 115478 de48716d5b48f1179407c3413c0c4c0bcdabb056
parent 115477 37ac46f7dd404f375d11a7fd3ac6d937ed315878
child 115479 de67d5a36369f34338ddb9d844849f47ed1bc0f6
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewerssmaug
bugs733002
milestone18.0a1
Bug 733002: DataChannel DOM implementation w/ m3 fix r=smaug
content/base/public/Makefile.in
content/base/public/nsContentUtils.h
content/base/public/nsIDOMDataChannel.idl
content/base/src/Makefile.in
content/base/src/WebSocket.cpp
content/base/src/WebSocket.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMDataChannel.cpp
content/base/src/nsDOMDataChannel.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/tests/mochitest/general/test_interfaces.html
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -60,16 +60,17 @@ EXPORTS_mozilla = \
 
 SDK_XPIDLSRCS   = \
 		nsISelection.idl  \
 		$(NULL)
 
 XPIDLSRCS	= \
 		nsIContentPolicy.idl        \
 		nsIDocumentEncoder.idl      \
+	        nsIDOMDataChannel.idl \
 		nsIDOMFile.idl \
 		nsIDOMFileReader.idl \
 		nsIDOMFileList.idl \
 		nsIDOMFormData.idl \
 		nsIDOMParser.idl \
 		nsIDOMSerializer.idl \
 		nsISelectionController.idl  \
 		nsISelectionDisplay.idl  \
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1760,16 +1760,20 @@ public:
   }
 
   /**
    * Creates an arraybuffer from a binary string.
    */
   static nsresult CreateArrayBuffer(JSContext *aCx, const nsACString& aData,
                                     JSObject** aResult);
 
+  static nsresult CreateBlobBuffer(JSContext* aCx,
+                                   const nsACString& aData,
+                                   jsval& aBlob);
+
   static void StripNullChars(const nsAString& aInStr, nsAString& aOutStr);
 
   /**
    * Strip all \n, \r and nulls from the given string
    * @param aString the string to remove newlines from [in/out]
    */
   static void RemoveNewlines(nsString &aString);
 
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsIDOMDataChannel.idl
@@ -0,0 +1,35 @@
+#include "domstubs.idl"
+
+#include "nsIDOMEventTarget.idl"
+
+interface nsIVariant;
+
+[scriptable, builtinclass, uuid(fb7a8ec4-c1eb-4d9f-b927-fbb8b4493e6d)]
+interface nsIDOMDataChannel : nsIDOMEventTarget
+{
+  readonly attribute DOMString label;
+  readonly attribute boolean reliable;
+  readonly attribute boolean ordered;
+
+  readonly attribute DOMString readyState;
+  readonly attribute unsigned long bufferedAmount;
+
+  [implicit_jscontext] attribute jsval onopen;
+  [implicit_jscontext] attribute jsval onerror;
+  [implicit_jscontext] attribute jsval onclose;
+  [implicit_jscontext] attribute jsval onmessage;
+
+  attribute DOMString binaryType;
+
+  void close();
+
+  /**
+   * Transmits data to other end of the connection.
+   * @param data The data to be transmitted.  Arraybuffers and Blobs are sent as
+   * binary data.  Strings are sent as UTF-8 text data.  Other types are
+   * converted to a String and sent as a String.
+   * @return if the connection is still established and the data was queued or
+   *         sent successfully.
+   */
+  [implicit_jscontext] void send(in nsIVariant data);
+};
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -132,16 +132,24 @@ CPPSRCS		= \
 		nsInProcessTabChildGlobal.cpp \
 		ThirdPartyUtil.cpp \
 		nsEventSource.cpp \
 		FileIOObject.cpp \
 		nsDOMMutationObserver.cpp \
 		nsMixedContentBlocker.cpp \
 		$(NULL)
 
+ifdef MOZ_WEBRTC
+EXPORTS += nsDOMDataChannel.h
+CPPSRCS += nsDOMDataChannel.cpp
+LOCAL_INCLUDES += \
+		-I$(topsrcdir)/netwerk/sctp/datachannel \
+		$(NULL)
+endif
+
 # Are we targeting x86-32 or x86-64?  If so, we want to include SSE2 code for
 # nsTextFragment.cpp
 ifneq (,$(INTEL_ARCHITECTURE))
 CPPSRCS += nsTextFragmentSSE2.cpp
 endif
 
 GQI_SRCS = contentbase.gqi
 
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -895,17 +895,17 @@ WebSocket::CreateAndDispatchMessageEvent
   NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
 
   // Create appropriate JS object for message
   jsval jsData;
   {
     JSAutoRequest ar(cx);
     if (isBinary) {
       if (mBinaryType == BinaryTypeValues::Blob) {
-        rv = CreateResponseBlob(aData, cx, jsData);
+        rv = nsContentUtils::CreateBlobBuffer(cx, aData, jsData);
         NS_ENSURE_SUCCESS(rv, rv);
       } else if (mBinaryType == BinaryTypeValues::Arraybuffer) {
         JSObject* arrayBuf;
         rv = nsContentUtils::CreateArrayBuffer(cx, aData, &arrayBuf);
         NS_ENSURE_SUCCESS(rv, rv);
         jsData = OBJECT_TO_JSVAL(arrayBuf);
       } else {
         NS_RUNTIMEABORT("Unknown binary type!");
@@ -938,36 +938,16 @@ WebSocket::CreateAndDispatchMessageEvent
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = event->SetTrusted(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 }
 
-// Initial implementation: only stores to RAM, not file
-// TODO: bug 704447: large file support
-nsresult
-WebSocket::CreateResponseBlob(const nsACString& aData,
-                              JSContext *aCx,
-                              jsval &jsData)
-{
-  uint32_t blobLen = aData.Length();
-  void* blobData = PR_Malloc(blobLen);
-  nsCOMPtr<nsIDOMBlob> blob;
-  if (blobData) {
-    memcpy(blobData, aData.BeginReading(), blobLen);
-    blob = new nsDOMMemoryFile(blobData, blobLen, EmptyString());
-  } else {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  JSObject* scope = JS_GetGlobalForScopeChain(aCx);
-  return nsContentUtils::WrapNative(aCx, scope, blob, &jsData, nullptr, true);
-}
-
 nsresult
 WebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
                                        uint16_t aCode,
                                        const nsString &aReason)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   nsresult rv = CheckInnerWindowCorrectness();
--- a/content/base/src/WebSocket.h
+++ b/content/base/src/WebSocket.h
@@ -223,19 +223,16 @@ protected:
 
   // These methods actually do the dispatch for various events.
   nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
   nsresult CreateAndDispatchMessageEvent(const nsACString& aData,
                                          bool isBinary);
   nsresult CreateAndDispatchCloseEvent(bool aWasClean,
                                        uint16_t aCode,
                                        const nsString& aReason);
-  nsresult CreateResponseBlob(const nsACString& aData,
-                              JSContext* aCx,
-                              jsval& jsData);
 
   // if there are "strong event listeners" (see comment in WebSocket.cpp) or
   // outgoing not sent messages then this method keeps the object alive
   // when js doesn't have strong references to it.
   void UpdateMustKeepAlive();
   // ATTENTION, when calling this method the object can be released
   // (and possibly collected).
   void DontKeepAliveAnyMore();
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6198,16 +6198,36 @@ nsContentUtils::CreateArrayBuffer(JSCont
   if (dataLen > 0) {
     NS_ASSERTION(JS_IsArrayBufferObject(*aResult, aCx), "What happened?");
     memcpy(JS_GetArrayBufferData(*aResult, aCx), aData.BeginReading(), dataLen);
   }
 
   return NS_OK;
 }
 
+// Initial implementation: only stores to RAM, not file
+// TODO: bug 704447: large file support
+nsresult
+nsContentUtils::CreateBlobBuffer(JSContext* aCx,
+                                 const nsACString& aData,
+                                 jsval& aBlob)
+{
+  uint32_t blobLen = aData.Length();
+  void* blobData = PR_Malloc(blobLen);
+  nsCOMPtr<nsIDOMBlob> blob;
+  if (blobData) {
+    memcpy(blobData, aData.BeginReading(), blobLen);
+    blob = new nsDOMMemoryFile(blobData, blobLen, EmptyString());
+  } else {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  JSObject* scope = JS_GetGlobalForScopeChain(aCx);
+  return nsContentUtils::WrapNative(aCx, scope, blob, &aBlob, nullptr, true);
+}
+
 void
 nsContentUtils::StripNullChars(const nsAString& aInStr, nsAString& aOutStr)
 {
   // In common cases where we don't have nulls in the
   // string we can simple simply bypass the checking code.
   int32_t firstNullPos = aInStr.FindChar('\0');
   if (firstNullPos == kNotFound) {
     aOutStr.Assign(aInStr);
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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/. */
+
+#ifdef MOZ_LOGGING
+#define FORCE_PR_LOG
+#endif
+
+#include "base/basictypes.h"
+#include "prlog.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* dataChannelLog;
+#endif
+#undef LOG
+#define LOG(args) PR_LOG(dataChannelLog, PR_LOG_DEBUG, args)
+
+
+#include "nsDOMDataChannel.h"
+#include "nsIDOMFile.h"
+#include "nsIJSNativeInitializer.h"
+#include "nsIDOMDataChannel.h"
+#include "nsIDOMMessageEvent.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMEventTargetHelper.h"
+
+#include "jsval.h"
+
+#include "nsError.h"
+#include "nsAutoPtr.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsJSUtils.h"
+#include "nsNetUtil.h"
+#include "nsDOMFile.h"
+
+#include "DataChannel.h"
+
+#ifdef GetBinaryType
+// Windows apparently has a #define for GetBinaryType...
+#undef GetBinaryType
+#endif
+
+class nsDOMDataChannel : public nsDOMEventTargetHelper,
+                         public nsIDOMDataChannel,
+                         public mozilla::DataChannelListener
+{
+public:
+  nsDOMDataChannel(mozilla::DataChannel* aDataChannel)
+    : mDataChannel(aDataChannel)
+    , mBinaryType(DC_BINARY_TYPE_BLOB)
+  {}
+
+  nsresult Init(nsPIDOMWindow* aDOMWindow);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMDATACHANNEL
+
+  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMDataChannel,
+                                           nsDOMEventTargetHelper)
+
+  nsresult
+  DoOnMessageAvailable(const nsACString& aMessage, bool aBinary);
+
+  virtual nsresult
+  OnMessageAvailable(nsISupports* aContext, const nsACString& aMessage);
+
+  virtual nsresult
+  OnBinaryMessageAvailable(nsISupports* aContext, const nsACString& aMessage);
+
+  virtual nsresult OnSimpleEvent(nsISupports* aContext, const nsAString& aName);
+
+  virtual nsresult
+  OnChannelConnected(nsISupports* aContext);
+
+  virtual nsresult
+  OnChannelClosed(nsISupports* aContext);
+
+  virtual void
+  AppReady();
+
+private:
+  // Get msg info out of JS variable being sent (string, arraybuffer, blob)
+  nsresult GetSendParams(nsIVariant *aData, nsCString &aStringOut,
+                         nsCOMPtr<nsIInputStream> &aStreamOut,
+                         bool &aIsBinary, uint32_t &aOutgoingLength,
+                         JSContext *aCx);
+
+  // Owning reference
+  nsAutoPtr<mozilla::DataChannel> mDataChannel;
+  nsString  mOrigin;
+  enum
+  {
+    DC_BINARY_TYPE_ARRAYBUFFER,
+    DC_BINARY_TYPE_BLOB,
+  } mBinaryType;
+};
+
+DOMCI_DATA(DataChannel, nsDOMDataChannel)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDataChannel)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDataChannel,
+                                                  nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDataChannel,
+                                                nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_ADDREF_INHERITED(nsDOMDataChannel, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(nsDOMDataChannel, nsDOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDataChannel)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMDataChannel)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DataChannel)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+nsresult
+nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow)
+{
+  nsresult rv;
+  nsAutoString urlParam;
+
+  nsDOMEventTargetHelper::Init();
+
+  MOZ_ASSERT(mDataChannel);
+  mDataChannel->SetListener(this, nullptr);
+
+  // Now grovel through the objects to get a usable origin for onMessage
+  nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDOMWindow);
+  NS_ENSURE_STATE(sgo);
+  nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
+  NS_ENSURE_STATE(scriptContext);
+
+  nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aDOMWindow));
+  NS_ENSURE_STATE(scriptPrincipal);
+  nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
+  NS_ENSURE_STATE(principal);
+
+  if (aDOMWindow) {
+    BindToOwner(aDOMWindow->IsOuterWindow() ?
+                aDOMWindow->GetCurrentInnerWindow() : aDOMWindow);
+  } else {
+    BindToOwner(aDOMWindow);
+  }
+
+  // Attempt to kill "ghost" DataChannel (if one can happen): but usually too early for check to fail
+  rv = CheckInnerWindowCorrectness();
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  // See bug 696085
+  // We don't need to observe for window destroyed or frozen; but PeerConnection needs
+  // to not allow itself to be bfcached (and get destroyed on navigation).
+
+  rv = nsContentUtils::GetUTFOrigin(principal,mOrigin);
+  LOG(("%s: origin = %s\n",__FUNCTION__,NS_LossyConvertUTF16toASCII(mOrigin).get()));
+  return rv;
+}
+
+NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, open)
+NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, error)
+NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, close)
+NS_IMPL_EVENT_HANDLER(nsDOMDataChannel, message)
+
+NS_IMETHODIMP
+nsDOMDataChannel::GetLabel(nsAString& aLabel)
+{
+  mDataChannel->GetLabel(aLabel);
+  return NS_OK;
+}
+
+// XXX should be GetType()?  Open question for the spec
+NS_IMETHODIMP
+nsDOMDataChannel::GetReliable(bool* aReliable)
+{
+  *aReliable = (mDataChannel->GetType() == mozilla::DataChannelConnection::RELIABLE);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::GetOrdered(bool* aOrdered)
+{
+  *aOrdered = mDataChannel->GetOrdered();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::GetReadyState(nsAString& aReadyState)
+{
+  uint16_t readyState = mDataChannel->GetReadyState();
+  const char * stateName[] = {
+    "Connecting",
+    "Open",
+    "Closing",
+    "Closed"
+  };
+  MOZ_ASSERT(/*readyState >= mozilla::DataChannel::CONNECTING && */ // Always true due to datatypes
+             readyState <= mozilla::DataChannel::CLOSED);
+  aReadyState.AssignASCII(stateName[readyState]);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount)
+{
+  *aBufferedAmount = mDataChannel->GetBufferedAmount();
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsDOMDataChannel::GetBinaryType(nsAString & aBinaryType)
+{
+  switch (mBinaryType) {
+  case DC_BINARY_TYPE_ARRAYBUFFER:
+    aBinaryType.AssignLiteral("arraybuffer");
+    break;
+  case DC_BINARY_TYPE_BLOB:
+    aBinaryType.AssignLiteral("blob");
+    break;
+  default:
+    NS_ERROR("Should not happen");
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::SetBinaryType(const nsAString& aBinaryType)
+{
+  if (aBinaryType.EqualsLiteral("arraybuffer")) {
+    mBinaryType = DC_BINARY_TYPE_ARRAYBUFFER;
+  } else if (aBinaryType.EqualsLiteral("blob")) {
+    mBinaryType = DC_BINARY_TYPE_BLOB;
+  } else  {
+    return NS_ERROR_INVALID_ARG;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::Close()
+{
+  mDataChannel->Close();
+  return NS_OK;
+}
+
+// Almost a clone of nsWebSocketChannel::Send()
+NS_IMETHODIMP
+nsDOMDataChannel::Send(nsIVariant* aData, JSContext* aCx)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  uint16_t state = mDataChannel->GetReadyState();
+
+  // In reality, the DataChannel protocol allows this, but we want it to
+  // look like WebSockets
+  if (state == mozilla::DataChannel::CONNECTING) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
+  nsCString msgString;
+  nsCOMPtr<nsIInputStream> msgStream;
+  bool isBinary;
+  uint32_t msgLen;
+  nsresult rv = GetSendParams(aData, msgString, msgStream, isBinary, msgLen, aCx);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (state == mozilla::DataChannel::CLOSING ||
+      state == mozilla::DataChannel::CLOSED) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(state == mozilla::DataChannel::OPEN,
+             "Unknown state in nsWebSocket::Send");
+
+  int32_t sent;
+  if (msgStream) {
+    sent = mDataChannel->SendBinaryStream(msgStream, msgLen);
+  } else {
+    if (isBinary) {
+      sent = mDataChannel->SendBinaryMsg(msgString);
+    } else {
+      sent = mDataChannel->SendMsg(msgString);
+    }
+  }
+  return sent >= 0 ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// XXX Exact clone of nsWebSocketChannel::GetSendParams() - find a way to share!
+nsresult
+nsDOMDataChannel::GetSendParams(nsIVariant* aData, nsCString& aStringOut,
+                                nsCOMPtr<nsIInputStream>& aStreamOut,
+                                bool& aIsBinary, uint32_t& aOutgoingLength,
+                                JSContext* aCx)
+{
+  // Get type of data (arraybuffer, blob, or string)
+  uint16_t dataType;
+  nsresult rv = aData->GetDataType(&dataType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (dataType == nsIDataType::VTYPE_INTERFACE ||
+      dataType == nsIDataType::VTYPE_INTERFACE_IS) {
+    nsCOMPtr<nsISupports> supports;
+    nsID* iid;
+    rv = aData->GetAsInterface(&iid, getter_AddRefs(supports));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsMemory::Free(iid);
+
+    // ArrayBuffer?
+    jsval realVal;
+    JSObject* obj;
+    nsresult rv = aData->GetAsJSVal(&realVal);
+    if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal) &&
+        (obj = JSVAL_TO_OBJECT(realVal)) &&
+        (JS_IsArrayBufferObject(obj, aCx))) {
+      int32_t len = JS_GetArrayBufferByteLength(obj, aCx);
+      char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(obj, aCx));
+
+      aStringOut.Assign(data, len);
+      aIsBinary = true;
+      aOutgoingLength = len;
+      return NS_OK;
+    }
+
+    // Blob?
+    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
+    if (blob) {
+      rv = blob->GetInternalStream(getter_AddRefs(aStreamOut));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // GetSize() should not perform blocking I/O (unlike Available())
+      uint64_t blobLen;
+      rv = blob->GetSize(&blobLen);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (blobLen > PR_UINT32_MAX) {
+        return NS_ERROR_FILE_TOO_BIG;
+      }
+      aOutgoingLength = static_cast<uint32_t>(blobLen);
+
+      aIsBinary = true;
+      return NS_OK;
+    }
+  }
+
+  // Text message: if not already a string, turn it into one.
+  // TODO: bug 704444: Correctly coerce any JS type to string
+  //
+  PRUnichar* data = nullptr;
+  uint32_t len = 0;
+  rv = aData->GetAsWStringWithSize(&len, &data);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString text;
+  text.Adopt(data, len);
+
+  CopyUTF16toUTF8(text, aStringOut);
+
+  aIsBinary = false;
+  aOutgoingLength = aStringOut.Length();
+  return NS_OK;
+}
+
+nsresult
+nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData,
+                                       bool aBinary)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  LOG(("DoOnMessageAvailable%s\n",aBinary ? ((mBinaryType == DC_BINARY_TYPE_BLOB) ? " (blob)" : " (binary)") : ""));
+
+  nsresult rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv)) {
+    return NS_OK;
+  }
+  nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
+  NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
+
+  nsIScriptContext* sc = sgo->GetContext();
+  NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE);
+
+  JSContext* cx = sc->GetNativeContext();
+  NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+
+  JSAutoRequest ar(cx);
+  jsval jsData;
+
+  if (aBinary) {
+    if (mBinaryType == DC_BINARY_TYPE_BLOB) {
+      rv = nsContentUtils::CreateBlobBuffer(cx, aData, jsData);
+      NS_ENSURE_SUCCESS(rv, rv);
+    } else if (mBinaryType == DC_BINARY_TYPE_ARRAYBUFFER) {
+      JSObject* arrayBuf;
+      rv = nsContentUtils::CreateArrayBuffer(cx, aData, &arrayBuf);
+      NS_ENSURE_SUCCESS(rv, rv);
+      jsData = OBJECT_TO_JSVAL(arrayBuf);
+    } else {
+      NS_RUNTIMEABORT("Unknown binary type!");
+      return NS_ERROR_UNEXPECTED;
+    }
+  } else {
+    NS_ConvertUTF8toUTF16 utf16data(aData);
+    JSString* jsString = JS_NewUCStringCopyN(cx, utf16data.get(), utf16data.Length());
+    NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
+
+    jsData = STRING_TO_JSVAL(jsString);
+  }
+
+  nsCOMPtr<nsIDOMEvent> event;
+  rv = NS_NewDOMMessageEvent(getter_AddRefs(event), nullptr, nullptr);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
+  rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
+                                      false, false,
+                                      jsData, mOrigin, EmptyString(),
+                                      nullptr);
+  NS_ENSURE_SUCCESS(rv,rv);
+  event->SetTrusted(true);
+
+  LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
+  rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch the message event!!!");
+  }
+  return rv;
+}
+
+nsresult
+nsDOMDataChannel::OnMessageAvailable(nsISupports* aContext,
+                                     const nsACString& aMessage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return DoOnMessageAvailable(aMessage, false);
+}
+
+nsresult
+nsDOMDataChannel::OnBinaryMessageAvailable(nsISupports* aContext,
+                                           const nsACString& aMessage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return DoOnMessageAvailable(aMessage, true);
+}
+
+nsresult
+nsDOMDataChannel::OnSimpleEvent(nsISupports* aContext, const nsAString& aName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv)) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMEvent> event;
+  rv = NS_NewDOMEvent(getter_AddRefs(event), nullptr, nullptr);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  rv = event->InitEvent(aName, false, false);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  event->SetTrusted(true);
+
+  return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+}
+
+nsresult
+nsDOMDataChannel::OnChannelConnected(nsISupports* aContext)
+{
+  LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
+
+  return OnSimpleEvent(aContext, NS_LITERAL_STRING("open"));
+}
+
+nsresult
+nsDOMDataChannel::OnChannelClosed(nsISupports* aContext)
+{
+  LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
+
+  return OnSimpleEvent(aContext, NS_LITERAL_STRING("close"));
+}
+
+void
+nsDOMDataChannel::AppReady()
+{
+  mDataChannel->AppReady();
+}
+
+/* static */
+nsresult
+NS_NewDOMDataChannel(mozilla::DataChannel* aDataChannel,
+                     nsPIDOMWindow* aWindow,
+                     nsIDOMDataChannel** aDomDataChannel)
+{
+  nsRefPtr<nsDOMDataChannel> domdc = new nsDOMDataChannel(aDataChannel);
+
+  nsresult rv = domdc->Init(aWindow);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  return CallQueryInterface(domdc, aDomDataChannel);
+}
+
+/* static */
+void
+NS_DataChannelAppReady(nsIDOMDataChannel* aDomDataChannel)
+{
+  ((nsDOMDataChannel *)aDomDataChannel)->AppReady();
+}
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsDOMDataChannel.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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/. */
+
+#ifndef nsDOMDataChannel_h__
+#define nsDOMDataChannel_h__
+
+// This defines only what's necessary to create nsDOMDataChannels, since this
+// gets used with MOZ_INTERNAL_API not set for media/webrtc/signaling/testing
+
+#include "nsIDOMDataChannel.h"
+
+namespace mozilla {
+   class DataChannel;
+}
+
+class nsPIDOMWindow;
+
+nsresult
+NS_NewDOMDataChannel(mozilla::DataChannel* dataChannel,
+                     nsPIDOMWindow* aWindow,
+                     nsIDOMDataChannel** domDataChannel);
+
+// Tell DataChannel it's ok to deliver open and message events
+void NS_DataChannelAppReady(nsIDOMDataChannel* domDataChannel);
+
+#endif
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -545,16 +545,20 @@ using mozilla::dom::indexedDB::IDBWrappe
 
 #include "DOMFileHandle.h"
 #include "FileRequest.h"
 #include "LockedFile.h"
 #include "GeneratedEvents.h"
 #include "mozilla/Likely.h"
 #include "nsDebug.h"
 
+#ifdef MOZ_WEBRTC
+#include "nsIDOMDataChannel.h"
+#endif
+
 #undef None // something included above defines this preprocessor symbol, maybe Xlib headers
 #include "WebGLContext.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/HTMLCollectionBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -1702,16 +1706,21 @@ static nsDOMClassInfoData sClassInfoData
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozActivity, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozTimeManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+#ifdef MOZ_WEBRTC
+  NS_DEFINE_CLASSINFO_DATA(DataChannel, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
+#endif
 };
 
 // Objects that should be constructable through |new Name();|
 struct nsContractIDMapData
 {
   int32_t mDOMClassInfoID;
   const char *mContractID;
 };
@@ -4505,16 +4514,23 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozTimeManager, nsIDOMMozTimeManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozTimeManager)
   DOM_CLASSINFO_MAP_END
 
+#ifdef MOZ_WEBRTC
+  DOM_CLASSINFO_MAP_BEGIN(DataChannel, nsIDOMDataChannel)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataChannel)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+  DOM_CLASSINFO_MAP_END
+#endif
+
 #ifdef DEBUG
   {
     uint32_t i = ArrayLength(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -532,8 +532,12 @@ DOMCI_CLASS(OpenWindowEventDetail)
 
 DOMCI_CLASS(DOMFileHandle)
 DOMCI_CLASS(FileRequest)
 DOMCI_CLASS(LockedFile)
 
 DOMCI_CLASS(MozActivity)
 
 DOMCI_CLASS(MozTimeManager)
+
+#ifdef MOZ_WEBRTC
+DOMCI_CLASS(DataChannel)
+#endif
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -532,17 +532,18 @@ var interfaceNamesInGlobalScope =
     "CameraManager",
     "CSSSupportsRule",
     "MozMobileCellInfo",
     "MozCanvasPrintState",
     "TCPSocket",
     "MozTimeManager",
     "MozNavigatorTime",
     "PermissionSettings",
-    "DataErrorEvent"
+    "DataErrorEvent",
+    "DataChannel"
   ]
 
 for (var i in SpecialPowers.Components.interfaces) {
   var s = i.toString();
   var name = null;
   if (s.indexOf("nsIDOM") == 0) {
     name = s.substring("nsIDOM".length);
   } else if (s.indexOf("nsI") == 0) {