Bug 1228474 - Support sending blob/arraybuffer for data channel. r=smaug

This commit is contained in:
Kershaw Chang 2016-09-17 19:42:00 -04:00
Родитель e2d5d5425f
Коммит 6c373070a1
24 изменённых файлов: 625 добавлений и 38 удалений

Просмотреть файл

@ -9,6 +9,7 @@
#include "ControllerConnectionCollection.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/PresentationConnectionClosedEvent.h"
@ -54,6 +55,7 @@ PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
, mUrl(aUrl)
, mState(PresentationConnectionState::Connecting)
, mOwningConnectionList(aList)
, mBinaryType(PresentationConnectionBinaryType::Arraybuffer)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
@ -169,6 +171,18 @@ PresentationConnection::State() const
return mState;
}
PresentationConnectionBinaryType
PresentationConnection::BinaryType() const
{
return mBinaryType;
}
void
PresentationConnection::SetBinaryType(PresentationConnectionBinaryType aType)
{
mBinaryType = aType;
}
void
PresentationConnection::Send(const nsAString& aData,
ErrorResult& aRv)
@ -182,13 +196,107 @@ PresentationConnection::Send(const nsAString& aData,
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
nsresult rv = service->SendSessionMessage(mId, mRole, aData);
if(NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
const uint32_t kMaxMessageLength = 256;
nsAutoString data(Substring(aData, 0, kMaxMessageLength));
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message: \"") + data +
NS_LITERAL_STRING("\""));
}
}
void
PresentationConnection::Send(Blob& aData,
ErrorResult& aRv)
{
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
nsresult rv = service->SendSessionBlob(mId, mRole, &aData);
if(NS_WARN_IF(NS_FAILED(rv))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send binary message for Blob message."));
}
}
void
PresentationConnection::Send(const ArrayBuffer& aData,
ErrorResult& aRv)
{
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t length = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());
nsDependentCSubstring msgString(data, length);
nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString);
if(NS_WARN_IF(NS_FAILED(rv))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send binary message for ArrayBuffer message."));
}
}
void
PresentationConnection::Send(const ArrayBufferView& aData,
ErrorResult& aRv)
{
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t length = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());
nsDependentCSubstring msgString(data, length);
nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString);
if(NS_WARN_IF(NS_FAILED(rv))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send binary message for ArrayBufferView message."));
}
}
@ -352,7 +460,8 @@ PresentationConnection::ProcessStateChanged(nsresult aReason)
NS_IMETHODIMP
PresentationConnection::NotifyMessage(const nsAString& aSessionId,
const nsACString& aData)
const nsACString& aData,
bool aIsBinary)
{
PRES_DEBUG("connection %s:id[%s], data[%s], role[%d]\n", __func__,
NS_ConvertUTF16toUTF8(aSessionId).get(),
@ -367,6 +476,18 @@ PresentationConnection::NotifyMessage(const nsAString& aSessionId,
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (NS_WARN_IF(NS_FAILED(DoReceiveMessage(aData, aIsBinary)))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to receive a message."));
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
PresentationConnection::DoReceiveMessage(const nsACString& aData, bool aIsBinary)
{
// Transform the data.
AutoJSAPI jsapi;
if (!jsapi.Init(GetOwner())) {
@ -374,9 +495,30 @@ PresentationConnection::NotifyMessage(const nsAString& aSessionId,
}
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> jsData(cx);
NS_ConvertUTF8toUTF16 utf16Data(aData);
if(NS_WARN_IF(!ToJSValue(cx, utf16Data, &jsData))) {
return NS_ERROR_FAILURE;
nsresult rv;
if (aIsBinary) {
if (mBinaryType == PresentationConnectionBinaryType::Blob) {
rv = nsContentUtils::CreateBlobBuffer(cx, GetOwner(), aData, &jsData);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else if (mBinaryType == PresentationConnectionBinaryType::Arraybuffer) {
JS::Rooted<JSObject*> arrayBuf(cx);
rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
jsData.setObject(*arrayBuf);
} else {
NS_RUNTIMEABORT("Unknown binary type!");
return NS_ERROR_UNEXPECTED;
}
} else {
NS_ConvertUTF8toUTF16 utf16Data(aData);
if(NS_WARN_IF(!ToJSValue(cx, utf16Data, &jsData))) {
return NS_ERROR_FAILURE;
}
}
return DispatchMessageEvent(jsData);
@ -396,7 +538,8 @@ PresentationConnection::NotifyReplaced()
nsresult
PresentationConnection::DispatchConnectionClosedEvent(
PresentationConnectionClosedReason aReason,
const nsAString& aMessage)
const nsAString& aMessage,
bool aDispatchNow)
{
if (mState != PresentationConnectionState::Closed) {
MOZ_ASSERT(false, "The connection state should be closed.");
@ -413,6 +556,11 @@ PresentationConnection::DispatchConnectionClosedEvent(
init);
closedEvent->SetTrusted(true);
if (aDispatchNow) {
bool ignore;
return DOMEventTargetHelper::DispatchEvent(closedEvent, &ignore);
}
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, static_cast<Event*>(closedEvent));
return asyncDispatcher->PostDOMEvent();
@ -579,3 +727,39 @@ PresentationConnection::RemoveFromLoadGroup()
return NS_OK;
}
void
PresentationConnection::AsyncCloseConnectionWithErrorMsg(const nsAString& aMessage)
{
if (mState == PresentationConnectionState::Terminated) {
return;
}
nsString message = nsString(aMessage);
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([this, message]() -> void {
// Set |mState| to |PresentationConnectionState::Closed| here to avoid
// calling |ProcessStateChanged|.
mState = PresentationConnectionState::Closed;
// Make sure dispatching the event and closing the connection are invoked
// at the same time by setting |aDispatchNow| to true.
Unused << NS_WARN_IF(NS_FAILED(
DispatchConnectionClosedEvent(PresentationConnectionClosedReason::Error,
message,
true)));
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
return;
}
Unused << NS_WARN_IF(NS_FAILED(
service->CloseSession(mId,
mRole,
nsIPresentationService::CLOSED_REASON_ERROR)));
});
Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)));
}

Просмотреть файл

@ -8,6 +8,7 @@
#define mozilla_dom_PresentationConnection_h
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/PresentationConnectionBinding.h"
#include "mozilla/dom/PresentationConnectionClosedEventBinding.h"
@ -18,6 +19,7 @@
namespace mozilla {
namespace dom {
class Blob;
class PresentationConnectionList;
class PresentationConnection final : public DOMEventTargetHelper
@ -52,9 +54,22 @@ public:
PresentationConnectionState State() const;
PresentationConnectionBinaryType BinaryType() const;
void SetBinaryType(PresentationConnectionBinaryType aType);
void Send(const nsAString& aData,
ErrorResult& aRv);
void Send(Blob& aData,
ErrorResult& aRv);
void Send(const ArrayBuffer& aData,
ErrorResult& aRv);
void Send(const ArrayBufferView& aData,
ErrorResult& aRv);
void Close(ErrorResult& aRv);
void Terminate(ErrorResult& aRv);
@ -83,7 +98,8 @@ private:
nsresult ProcessStateChanged(nsresult aReason);
nsresult DispatchConnectionClosedEvent(PresentationConnectionClosedReason aReason,
const nsAString& aMessage);
const nsAString& aMessage,
bool aDispatchNow = false);
nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
@ -93,12 +109,17 @@ private:
nsresult RemoveFromLoadGroup();
void AsyncCloseConnectionWithErrorMsg(const nsAString& aMessage);
nsresult DoReceiveMessage(const nsACString& aData, bool aIsBinary);
nsString mId;
nsString mUrl;
uint8_t mRole;
PresentationConnectionState mState;
RefPtr<PresentationConnectionList> mOwningConnectionList;
nsWeakPtr mWeakLoadGroup;;
nsWeakPtr mWeakLoadGroup;
PresentationConnectionBinaryType mBinaryType;
};
} // namespace dom

Просмотреть файл

@ -27,7 +27,7 @@ function PresentationDataChannelDescription(aDataChannelSDP) {
PresentationDataChannelDescription.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
get type() {
return nsIPresentationChannelDescription.TYPE_DATACHANNEL;
return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL;
},
get tcpAddress() {
return null;
@ -94,6 +94,8 @@ PresentationTransportBuilder.prototype = {
case Ci.nsIPresentationService.ROLE_RECEIVER:
this._peerConnection.ondatachannel = aEvent => {
this._dataChannel = aEvent.channel;
// Ensure the binaryType of dataChannel is blob.
this._dataChannel.binaryType = "blob";
this._setDataChannel();
}
break;
@ -134,7 +136,7 @@ PresentationTransportBuilder.prototype = {
// Handoff the ownership of _peerConnection and _dataChannel to
// _sessionTransport
this._sessionTransport = new PresentationTransport();
this._sessionTransport.init(this._peerConnection, this._dataChannel);
this._sessionTransport.init(this._peerConnection, this._dataChannel, this._window);
this._peerConnection = this._dataChannel = null;
this._listener.onSessionTransport(this._sessionTransport);
@ -243,11 +245,12 @@ PresentationTransport.prototype = {
contractID: PRESENTATIONTRANSPORT_CONTRACTID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport]),
init: function(aPeerConnection, aDataChannel) {
init: function(aPeerConnection, aDataChannel, aWindow) {
log("initWithDataChannel");
this._enableDataNotification = false;
this._dataChannel = aDataChannel;
this._peerConnection = aPeerConnection;
this._window = aWindow;
this._dataChannel.onopen = () => {
log("data channel reopen. Should never touch here");
@ -269,7 +272,7 @@ PresentationTransport.prototype = {
this._messageQueue.push(aEvent.data);
return;
}
this._callback.notifyData(aEvent.data);
this._doNotifyData(aEvent.data);
};
this._dataChannel.onerror = aError => {
@ -299,6 +302,23 @@ PresentationTransport.prototype = {
this._dataChannel.send(aData);
},
sendBinaryMsg: function(aData) {
log("sendBinaryMsg");
let array = new Uint8Array(aData.length);
for (let i = 0; i < aData.length; i++) {
array[i] = aData.charCodeAt(i);
}
this._dataChannel.send(array);
},
sendBlob: function(aBlob) {
log("sendBlob");
this._dataChannel.send(aBlob);
},
enableDataNotification: function() {
log("enableDataNotification");
if (this._enableDataNotification) {
@ -311,7 +331,7 @@ PresentationTransport.prototype = {
this._enableDataNotification = true;
this._messageQueue.forEach(aData => this._callback.notifyData(aData));
this._messageQueue.forEach(aData => this._doNotifyData(aData));
this._messageQueue = [];
},
@ -330,6 +350,23 @@ PresentationTransport.prototype = {
}
this._callback = null;
this._messageQueue = [];
this._window = null;
},
_doNotifyData: function(aData) {
if (!this._callback) {
throw NS_ERROR_NOT_AVAILABLE;
}
if (aData instanceof this._window.Blob) {
let reader = new this._window.FileReader();
reader.addEventListener("load", (aEvent) => {
this._callback.notifyData(aEvent.target.result, true);
});
reader.readAsBinaryString(aData);
} else {
this._callback.notifyData(aData, false);
}
},
};

Просмотреть файл

@ -776,6 +776,44 @@ PresentationService::SendSessionMessage(const nsAString& aSessionId,
return info->Send(aData);
}
NS_IMETHODIMP
PresentationService::SendSessionBinaryMsg(const nsAString& aSessionId,
uint8_t aRole,
const nsACString &aData)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aData.IsEmpty());
MOZ_ASSERT(!aSessionId.IsEmpty());
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
if (NS_WARN_IF(!info)) {
return NS_ERROR_NOT_AVAILABLE;
}
return info->SendBinaryMsg(aData);
}
NS_IMETHODIMP
PresentationService::SendSessionBlob(const nsAString& aSessionId,
uint8_t aRole,
nsIDOMBlob* aBlob)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aSessionId.IsEmpty());
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
MOZ_ASSERT(aBlob);
RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
if (NS_WARN_IF(!info)) {
return NS_ERROR_NOT_AVAILABLE;
}
return info->SendBlob(aBlob);
}
NS_IMETHODIMP
PresentationService::CloseSession(const nsAString& aSessionId,
uint8_t aRole,

Просмотреть файл

@ -284,6 +284,34 @@ PresentationSessionInfo::Send(const nsAString& aData)
return mTransport->Send(aData);
}
nsresult
PresentationSessionInfo::SendBinaryMsg(const nsACString& aData)
{
if (NS_WARN_IF(!IsSessionReady())) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (NS_WARN_IF(!mTransport)) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->SendBinaryMsg(aData);
}
nsresult
PresentationSessionInfo::SendBlob(nsIDOMBlob* aBlob)
{
if (NS_WARN_IF(!IsSessionReady())) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (NS_WARN_IF(!mTransport)) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->SendBlob(aBlob);
}
nsresult
PresentationSessionInfo::Close(nsresult aReason,
uint32_t aState)
@ -475,7 +503,7 @@ PresentationSessionInfo::NotifyTransportClosed(nsresult aReason)
}
NS_IMETHODIMP
PresentationSessionInfo::NotifyData(const nsACString& aData)
PresentationSessionInfo::NotifyData(const nsACString& aData, bool aIsBinary)
{
MOZ_ASSERT(NS_IsMainThread());
@ -487,7 +515,7 @@ PresentationSessionInfo::NotifyData(const nsACString& aData)
return NS_ERROR_NOT_AVAILABLE;
}
return mListener->NotifyMessage(mSessionId, aData);
return mListener->NotifyMessage(mSessionId, aData, aIsBinary);
}
// nsIPresentationSessionTransportBuilderListener

Просмотреть файл

@ -99,6 +99,10 @@ public:
nsresult Send(const nsAString& aData);
nsresult SendBinaryMsg(const nsACString& aData);
nsresult SendBlob(nsIDOMBlob* aBlob);
nsresult Close(nsresult aReason,
uint32_t aState);

Просмотреть файл

@ -417,6 +417,18 @@ PresentationTCPSessionTransport::Send(const nsAString& aData)
return NS_OK;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::SendBinaryMsg(const nsACString& aData)
{
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::SendBlob(nsIDOMBlob* aBlob)
{
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::Close(nsresult aReason)
{
@ -573,5 +585,5 @@ PresentationTCPSessionTransport::OnDataAvailable(nsIRequest* aRequest,
}
// Pass the incoming data to the listener.
return mCallback->NotifyData(data);
return mCallback->NotifyData(data, false);
}

Просмотреть файл

@ -32,7 +32,8 @@ interface nsIPresentationSessionListener : nsISupports
* Called when receive messages.
*/
void notifyMessage(in DOMString sessionId,
in ACString data);
in ACString data,
in boolean isBinary);
/*
* Called when this listener is replaced by another one.

Просмотреть файл

@ -4,6 +4,7 @@
#include "nsISupports.idl"
interface nsIDOMBlob;
interface nsIDOMEventTarget;
interface nsIInputStream;
interface nsIPresentationAvailabilityListener;
@ -95,6 +96,28 @@ interface nsIPresentationService : nsISupports
in uint8_t role,
in DOMString data);
/*
* Send the binary message to the session.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param data: the message being sent out.
*/
void sendSessionBinaryMsg(in DOMString sessionId,
in uint8_t role,
in ACString data);
/*
* Send the blob to the session.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param blob: The input blob to be sent.
*/
void sendSessionBlob(in DOMString sessionId,
in uint8_t role,
in nsIDOMBlob blob);
/*
* Close the session.
*

Просмотреть файл

@ -4,6 +4,7 @@
#include "nsISupports.idl"
interface nsIDOMBlob;
interface nsIInputStream;
interface nsINetAddr;
@ -20,7 +21,7 @@ interface nsIPresentationSessionTransportCallback : nsISupports
{
void notifyTransportReady();
void notifyTransportClosed(in nsresult reason);
void notifyData(in ACString data);
void notifyData(in ACString data, in boolean isBinary);
};
/*
@ -48,6 +49,18 @@ interface nsIPresentationSessionTransport : nsISupports
*/
void send(in DOMString data);
/*
* Send the binary message to the remote endpoint.
* @param data: the message being sent out.
*/
void sendBinaryMsg(in ACString data);
/*
* Send the blob to the remote endpoint.
* @param blob: The input blob to be sent.
*/
void sendBlob(in nsIDOMBlob blob);
/*
* Close this session transport.
* @param reason The reason for closing this session transport.

Просмотреть файл

@ -79,7 +79,7 @@ child:
async NotifySessionStateChange(nsString aSessionId,
uint16_t aState,
nsresult aReason);
async NotifyMessage(nsString aSessionId, nsCString aData);
async NotifyMessage(nsString aSessionId, nsCString aData, bool aIsBinary);
async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId);
async PPresentationBuilder(nsString aSessionId, uint8_t aRole);

Просмотреть файл

@ -112,10 +112,13 @@ PresentationChild::RecvNotifySessionStateChange(const nsString& aSessionId,
bool
PresentationChild::RecvNotifyMessage(const nsString& aSessionId,
const nsCString& aData)
const nsCString& aData,
const bool& aIsBinary)
{
if (mService) {
Unused << NS_WARN_IF(NS_FAILED(mService->NotifyMessage(aSessionId, aData)));
Unused << NS_WARN_IF(NS_FAILED(mService->NotifyMessage(aSessionId,
aData,
aIsBinary)));
}
return true;
}

Просмотреть файл

@ -52,7 +52,8 @@ public:
virtual bool
RecvNotifyMessage(const nsString& aSessionId,
const nsCString& aData) override;
const nsCString& aData,
const bool& aIsBinary) override;
virtual bool
RecvNotifySessionConnect(const uint64_t& aWindowId,

Просмотреть файл

@ -35,6 +35,26 @@ PresentationContentSessionInfo::Send(const nsAString& aData)
return mTransport->Send(aData);
}
nsresult
PresentationContentSessionInfo::SendBinaryMsg(const nsACString& aData)
{
if (NS_WARN_IF(!mTransport)) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->SendBinaryMsg(aData);
}
nsresult
PresentationContentSessionInfo::SendBlob(nsIDOMBlob* aBlob)
{
if (NS_WARN_IF(!mTransport)) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->SendBlob(aBlob);
}
nsresult
PresentationContentSessionInfo::Close(nsresult aReason)
{
@ -71,7 +91,8 @@ PresentationContentSessionInfo::NotifyTransportClosed(nsresult aReason)
}
NS_IMETHODIMP
PresentationContentSessionInfo::NotifyData(const nsACString& aData)
PresentationContentSessionInfo::NotifyData(const nsACString& aData,
bool aIsBinary)
{
MOZ_ASSERT(NS_IsMainThread());
@ -81,7 +102,7 @@ PresentationContentSessionInfo::NotifyData(const nsACString& aData)
return NS_ERROR_NOT_AVAILABLE;
}
return static_cast<PresentationIPCService*>(service.get())->
NotifyMessage(mSessionId, aData);
NotifyMessage(mSessionId, aData, aIsBinary);
}
} // namespace dom

Просмотреть файл

@ -42,6 +42,10 @@ public:
nsresult Send(const nsAString& aData);
nsresult SendBinaryMsg(const nsACString& aData);
nsresult SendBlob(nsIDOMBlob* aBlob);
nsresult Close(nsresult aReason);
private:

Просмотреть файл

@ -101,6 +101,46 @@ PresentationIPCService::SendSessionMessage(const nsAString& aSessionId,
nsString(aData)));
}
NS_IMETHODIMP
PresentationIPCService::SendSessionBinaryMsg(const nsAString& aSessionId,
uint8_t aRole,
const nsACString &aData)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aData.IsEmpty());
MOZ_ASSERT(!aSessionId.IsEmpty());
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
RefPtr<PresentationContentSessionInfo> info;
// data channel session transport is maintained by content process
if (mSessionInfos.Get(aSessionId, getter_AddRefs(info))) {
return info->SendBinaryMsg(aData);
}
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
PresentationIPCService::SendSessionBlob(const nsAString& aSessionId,
uint8_t aRole,
nsIDOMBlob* aBlob)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aSessionId.IsEmpty());
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
MOZ_ASSERT(aBlob);
RefPtr<PresentationContentSessionInfo> info;
// data channel session transport is maintained by content process
if (mSessionInfos.Get(aSessionId, getter_AddRefs(info))) {
return info->SendBlob(aBlob);
}
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
PresentationIPCService::CloseSession(const nsAString& aSessionId,
uint8_t aRole,
@ -332,14 +372,15 @@ PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId,
// Only used for OOP RTCDataChannel session transport case.
nsresult
PresentationIPCService::NotifyMessage(const nsAString& aSessionId,
const nsACString& aData)
const nsACString& aData,
const bool& aIsBinary)
{
nsCOMPtr<nsIPresentationSessionListener> listener;
if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
return NS_OK;
}
return listener->NotifyMessage(aSessionId, aData);
return listener->NotifyMessage(aSessionId, aData, aIsBinary);
}
// Only used for OOP RTCDataChannel session transport case.

Просмотреть файл

@ -37,7 +37,8 @@ public:
nsresult aReason);
nsresult NotifyMessage(const nsAString& aSessionId,
const nsACString& aData);
const nsACString& aData,
const bool& aIsBinary);
nsresult NotifySessionConnect(uint64_t aWindowId,
const nsAString& aSessionId);

Просмотреть файл

@ -315,10 +315,13 @@ PresentationParent::NotifyReplaced()
NS_IMETHODIMP
PresentationParent::NotifyMessage(const nsAString& aSessionId,
const nsACString& aData)
const nsACString& aData,
bool aIsBinary)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifyMessage(nsString(aSessionId), nsCString(aData)))) {
!SendNotifyMessage(nsString(aSessionId),
nsCString(aData),
aIsBinary))) {
return NS_ERROR_FAILURE;
}
return NS_OK;

Просмотреть файл

@ -296,7 +296,7 @@ const mockedSessionTransport = {
this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady();
},
simulateIncomingMessage: function(message) {
this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message);
this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message, false);
},
onOffer: function(aOffer) {
},

Просмотреть файл

@ -77,6 +77,18 @@ const mockControlChannelOfSender = {
reconnect: function(presentationId, url) {
sendAsyncMessage('start-reconnect', url);
},
sendIceCandidate: function(candidate) {
mockControlChannelOfReceiver.notifyIceCandidate(candidate);
},
notifyIceCandidate: function(candidate) {
if (!this._listener) {
return;
}
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
.onIceCandidate(candidate);
},
};
// control channel of receiver
@ -130,7 +142,19 @@ const mockControlChannelOfReceiver = {
sendAsyncMessage('control-channel-receiver-closed', reason);
},
terminate: function(presentaionId) {
}
},
sendIceCandidate: function(candidate) {
mockControlChannelOfReceiver.notifyIceCandidate(candidate);
},
notifyIceCandidate: function(candidate) {
if (!this._listener) {
return;
}
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
.onIceCandidate(candidate);
},
};
const mockDevice = {

Просмотреть файл

@ -134,7 +134,7 @@ function loadPrivilegedScriptTest() {
this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady();
},
simulateIncomingMessage: function(message) {
this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message);
this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message, false);
},
onOffer: function(aOffer) {
this._listener.sendAnswer(mockedChannelDescription);

Просмотреть файл

@ -36,6 +36,27 @@ function finish() {
}
var connection;
const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length);
const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER);
TYPED_DATA_ARRAY.set(DATA_ARRAY);
function is_same_buffer(recv_data, expect_data) {
let recv_dataview = new Uint8Array(recv_data);
let expected_dataview = new Uint8Array(expect_data);
if (recv_dataview.length !== expected_dataview.length) {
return false;
}
for (let i = 0; i < recv_dataview.length; i++) {
if (recv_dataview[i] != expected_dataview[i]) {
info('discover byte differenct at ' + i);
return false;
}
}
return true;
}
function testConnectionAvailable() {
return new Promise(function(aResolve, aReject) {
@ -105,6 +126,19 @@ function testSendMessage() {
});
}
function testIncomingBlobMessage() {
return new Promise(function(aResolve, aReject) {
info('Receiver: --- testIncomingBlobMessage ---');
connection.send('testIncomingBlobMessage');
connection.addEventListener('message', function messageHandler(evt) {
connection.removeEventListener('message', messageHandler);
let recvData= String.fromCharCode.apply(null, new Uint8Array(evt.data));
is(recvData, "Hello World", 'expected same string data');
aResolve();
});
});
}
function testConnectionClosed() {
return new Promise(function(aResolve, aReject) {
info('Receiver: --- testConnectionClosed ---');
@ -130,13 +164,46 @@ function testReconnectConnection() {
});
}
function testIncomingArrayBuffer() {
return new Promise(function(aResolve, aReject) {
info('Receiver: --- testIncomingArrayBuffer ---');
connection.binaryType = "blob";
connection.send('testIncomingArrayBuffer');
connection.addEventListener('message', function messageHandler(evt) {
connection.removeEventListener('message', messageHandler);
var fileReader = new FileReader();
fileReader.onload = function() {
ok(is_same_buffer(DATA_ARRAY_BUFFER, this.result), "expected same buffer data");
aResolve();
};
fileReader.readAsArrayBuffer(evt.data);
});
});
}
function testIncomingArrayBufferView() {
return new Promise(function(aResolve, aReject) {
info('Receiver: --- testIncomingArrayBufferView ---');
connection.binaryType = "arraybuffer";
connection.send('testIncomingArrayBufferView');
connection.addEventListener('message', function messageHandler(evt) {
connection.removeEventListener('message', messageHandler);
ok(is_same_buffer(evt.data, TYPED_DATA_ARRAY), "expected same buffer data");
aResolve();
});
});
}
function runTests() {
testConnectionAvailable()
.then(testConnectionReady)
.then(testIncomingMessage)
.then(testSendMessage)
.then(testIncomingBlobMessage)
.then(testConnectionClosed)
.then(testReconnectConnection)
.then(testIncomingArrayBuffer)
.then(testIncomingArrayBufferView)
.then(testConnectionClosed);
}

Просмотреть файл

@ -15,6 +15,10 @@ var request;
var connection;
var receiverIframe;
var presentationId;
const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length);
const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER);
TYPED_DATA_ARRAY.set(DATA_ARRAY);
function postMessageToIframe(aType) {
receiverIframe.src = receiverUrl + "#" +
@ -159,6 +163,46 @@ function testIncomingMessage() {
});
}
function testSendBlobMessage() {
return new Promise(function(aResolve, aReject) {
info('Sender: --- testSendBlobMessage ---');
connection.addEventListener('message', function messageHandler(evt) {
connection.removeEventListener('message', messageHandler);
let msg = evt.data;
is(msg, "testIncomingBlobMessage", "Sender: Sender should receive message from Receiver");
let blob = new Blob(["Hello World"], {type : 'text/plain'});
connection.send(blob);
aResolve();
});
});
}
function testSendArrayBuffer() {
return new Promise(function(aResolve, aReject) {
info('Sender: --- testSendArrayBuffer ---');
connection.addEventListener('message', function messageHandler(evt) {
connection.removeEventListener('message', messageHandler);
let msg = evt.data;
is(msg, "testIncomingArrayBuffer", "Sender: Sender should receive message from Receiver");
connection.send(DATA_ARRAY_BUFFER);
aResolve();
});
});
}
function testSendArrayBufferView() {
return new Promise(function(aResolve, aReject) {
info('Sender: --- testSendArrayBufferView ---');
connection.addEventListener('message', function messageHandler(evt) {
connection.removeEventListener('message', messageHandler);
let msg = evt.data;
is(msg, "testIncomingArrayBufferView", "Sender: Sender should receive message from Receiver");
connection.send(TYPED_DATA_ARRAY);
aResolve();
});
});
}
function testCloseConnection() {
info('Sender: --- testCloseConnection ---');
// Test terminate immediate after close.
@ -269,8 +313,11 @@ function runTests() {
.then(testStartConnection)
.then(testSendMessage)
.then(testIncomingMessage)
.then(testSendBlobMessage)
.then(testCloseConnection)
.then(testReconnect)
.then(testSendArrayBuffer)
.then(testSendArrayBufferView)
.then(testCloseConnection)
.then(testTerminateAfterClose)
.then(teardown);
@ -284,11 +331,12 @@ SpecialPowers.pushPermissions([
], () => {
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
/* Mocked TCP session transport builder in the test */
["dom.presentation.session_transport.data_channel.enable", false],
["dom.presentation.session_transport.data_channel.enable", true],
["dom.presentation.controller.enabled", true],
["dom.presentation.receiver.enabled", true],
["dom.presentation.test.enabled", true],
["dom.presentation.test.stage", 0],
["dom.mozBrowserFramesEnabled", true]]},
["dom.mozBrowserFramesEnabled", true],
["media.navigator.permission.disabled", true]]},
runTests);
});

Просмотреть файл

@ -20,6 +20,12 @@ enum PresentationConnectionState
"terminated"
};
enum PresentationConnectionBinaryType
{
"blob",
"arraybuffer"
};
[Pref="dom.presentation.enabled"]
interface PresentationConnection : EventTarget {
/*
@ -41,6 +47,7 @@ interface PresentationConnection : EventTarget {
attribute EventHandler onconnect;
attribute EventHandler onclose;
attribute EventHandler onterminate;
attribute PresentationConnectionBinaryType binaryType;
/*
* After a communication channel has been established between the controlling
@ -48,13 +55,19 @@ interface PresentationConnection : EventTarget {
* event handler "onmessage" will be invoked at the remote side.
*
* This function only works when the state is "connected".
*
* TODO bug 1228474 Implement PresentationSessionTransport with DataChannel to
* support other binary types.
*/
[Throws]
void send(DOMString data);
[Throws]
void send(Blob data);
[Throws]
void send(ArrayBuffer data);
[Throws]
void send(ArrayBufferView data);
/*
* It is triggered when receiving messages.
*/