Bug 1069230 - Presentation API implementation. Part 8 - Data transport channel. r=jdm

This commit is contained in:
Sean Lin 2015-04-22 16:01:38 +08:00
parent 905d6d07e3
commit 1efa4382a3
9 changed files with 975 additions and 6 deletions

View File

@ -11,15 +11,119 @@
#include "mozilla/Services.h"
#include "nsIDocShell.h"
#include "nsIFrameLoader.h"
#include "nsIMutableArray.h"
#include "nsINetAddr.h"
#include "nsISocketTransport.h"
#include "nsISupportsPrimitives.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "PresentationService.h"
#include "PresentationSessionInfo.h"
#ifdef MOZ_WIDGET_GONK
#include "nsINetworkInterface.h"
#include "nsINetworkManager.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::services;
/*
* Implementation of PresentationChannelDescription
*/
namespace mozilla {
namespace dom {
class PresentationChannelDescription final : public nsIPresentationChannelDescription
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONCHANNELDESCRIPTION
PresentationChannelDescription(nsACString& aAddress,
uint16_t aPort)
: mAddress(aAddress)
, mPort(aPort)
{
}
private:
~PresentationChannelDescription() {}
nsCString mAddress;
uint16_t mPort;
};
} // namespace dom
} // namespace mozilla
NS_IMPL_ISUPPORTS(PresentationChannelDescription, nsIPresentationChannelDescription)
NS_IMETHODIMP
PresentationChannelDescription::GetType(uint8_t* aRetVal)
{
if (NS_WARN_IF(!aRetVal)) {
return NS_ERROR_INVALID_POINTER;
}
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
// Only support TCP socket for now.
*aRetVal = nsIPresentationChannelDescription::TYPE_TCP;
return NS_OK;
}
NS_IMETHODIMP
PresentationChannelDescription::GetTcpAddress(nsIArray** aRetVal)
{
if (NS_WARN_IF(!aRetVal)) {
return NS_ERROR_INVALID_POINTER;
}
nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (NS_WARN_IF(!array)) {
return NS_ERROR_OUT_OF_MEMORY;
}
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
// Ultimately we may use all the available addresses. DataChannel appears
// more robust upon handling ICE. And at the first stage Presentation API is
// only exposed on Firefox OS where the first IP appears enough for most
// scenarios.
nsCOMPtr<nsISupportsCString> address = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
if (NS_WARN_IF(!address)) {
return NS_ERROR_OUT_OF_MEMORY;
}
address->SetData(mAddress);
array->AppendElement(address, false);
array.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
PresentationChannelDescription::GetTcpPort(uint16_t* aRetVal)
{
if (NS_WARN_IF(!aRetVal)) {
return NS_ERROR_INVALID_POINTER;
}
*aRetVal = mPort;
return NS_OK;
}
NS_IMETHODIMP
PresentationChannelDescription::GetDataChannelSDP(nsAString& aDataChannelSDP)
{
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
// Only support TCP socket for now.
aDataChannelSDP.Truncate();
return NS_OK;
}
/*
* Implementation of PresentationSessionInfo
*/
@ -231,8 +335,42 @@ PresentationRequesterInfo::Init(nsIPresentationControlChannel* aControlChannel)
{
PresentationSessionInfo::Init(aControlChannel);
// TODO Initialize |mServerSocket|, use |this| as the listener, and prepare to
// send offer.
// Initialize |mServerSocket| for bootstrapping the data transport channel and
// use |this| as the listener.
mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
if (NS_WARN_IF(!mServerSocket)) {
return ReplyError(NS_ERROR_NOT_AVAILABLE);
}
nsresult rv = mServerSocket->Init(-1, false, -1);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mServerSocket->AsyncListen(this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Prepare and send the offer.
int32_t port;
rv = mServerSocket->GetPort(&port);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCString address;
rv = GetAddress(address);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsRefPtr<PresentationChannelDescription> description =
new PresentationChannelDescription(address, static_cast<uint16_t>(port));
rv = mControlChannel->SendOffer(description);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
@ -249,6 +387,51 @@ PresentationRequesterInfo::Shutdown(nsresult aReason)
}
}
nsresult
PresentationRequesterInfo::GetAddress(nsACString& aAddress)
{
#ifdef MOZ_WIDGET_GONK
nsCOMPtr<nsINetworkManager> networkManager =
do_GetService("@mozilla.org/network/manager;1");
if (NS_WARN_IF(!networkManager)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsINetworkInfo> activeNetworkInfo;
networkManager->GetActiveNetworkInfo(getter_AddRefs(activeNetworkInfo));
if (NS_WARN_IF(!activeNetworkInfo)) {
return NS_ERROR_FAILURE;
}
char16_t** ips = nullptr;
uint32_t* prefixes = nullptr;
uint32_t count = 0;
activeNetworkInfo->GetAddresses(&ips, &prefixes, &count);
if (NS_WARN_IF(!count)) {
NS_Free(prefixes);
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, ips);
return NS_ERROR_FAILURE;
}
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
// Ultimately we may use all the available addresses. DataChannel appears
// more robust upon handling ICE. And at the first stage Presentation API is
// only exposed on Firefox OS where the first IP appears enough for most
// scenarios.
nsAutoString ip;
ip.Assign(ips[0]);
aAddress = NS_ConvertUTF16toUTF8(ip);
NS_Free(prefixes);
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, ips);
#else
// TODO Get host IP via other platforms.
aAddress.Truncate();
#endif
return NS_OK;
}
// nsIPresentationControlChannelListener
NS_IMETHODIMP
PresentationRequesterInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
@ -425,7 +608,29 @@ PresentationResponderInfo::InitTransportAndSendAnswer()
return rv;
}
// TODO Prepare and send the answer.
// Prepare and send the answer.
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
// In the current implementation of |PresentationSessionTransport|,
// |GetSelfAddress| cannot return the real info when it's initialized via
// |InitWithChannelDescription|. Yet this deficiency only affects the channel
// description for the answer, which is not actually checked at requester side.
nsCOMPtr<nsINetAddr> selfAddr;
rv = mTransport->GetSelfAddress(getter_AddRefs(selfAddr));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCString address;
selfAddr->GetAddress(address);
uint16_t port;
selfAddr->GetPort(&port);
nsCOMPtr<nsIPresentationChannelDescription> description =
new PresentationChannelDescription(address, port);
rv = mControlChannel->SendAnswer(description);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}

View File

@ -145,6 +145,8 @@ private:
void Shutdown(nsresult aReason) override;
nsresult GetAddress(nsACString& aAddress);
nsCOMPtr<nsIServerSocket> mServerSocket;
};

View File

@ -0,0 +1,484 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "nsArrayUtils.h"
#include "nsIAsyncStreamCopier.h"
#include "nsIInputStreamPump.h"
#include "nsIMultiplexInputStream.h"
#include "nsIMutableArray.h"
#include "nsIOutputStream.h"
#include "nsIPresentationControlChannel.h"
#include "nsIScriptableInputStream.h"
#include "nsISocketTransport.h"
#include "nsISocketTransportService.h"
#include "nsISupportsPrimitives.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "PresentationSessionTransport.h"
#define BUFFER_SIZE 65536
using namespace mozilla;
using namespace mozilla::dom;
class CopierCallbacks final : public nsIRequestObserver
{
public:
explicit CopierCallbacks(PresentationSessionTransport* aTransport)
: mOwner(aTransport)
{}
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
private:
~CopierCallbacks() {}
nsRefPtr<PresentationSessionTransport> mOwner;
};
NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)
NS_IMETHODIMP
CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
return NS_OK;
}
NS_IMETHODIMP
CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
{
mOwner->NotifyCopyComplete(aStatus);
return NS_OK;
}
NS_IMPL_ISUPPORTS(PresentationSessionTransport,
nsIPresentationSessionTransport,
nsITransportEventSink,
nsIInputStreamCallback,
nsIStreamListener,
nsIRequestObserver)
PresentationSessionTransport::PresentationSessionTransport()
: mReadyState(CLOSED)
, mAsyncCopierActive(false)
, mCloseStatus(NS_OK)
{
}
PresentationSessionTransport::~PresentationSessionTransport()
{
}
NS_IMETHODIMP
PresentationSessionTransport::InitWithSocketTransport(nsISocketTransport* aTransport,
nsIPresentationSessionTransportCallback* aCallback)
{
if (NS_WARN_IF(!aCallback)) {
return NS_ERROR_INVALID_ARG;
}
mCallback = aCallback;
if (NS_WARN_IF(!aTransport)) {
return NS_ERROR_INVALID_ARG;
}
mTransport = aTransport;
nsresult rv = CreateStream();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
SetReadyState(OPEN);
rv = CreateInputStreamPump();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransport::InitWithChannelDescription(nsIPresentationChannelDescription* aDescription,
nsIPresentationSessionTransportCallback* aCallback)
{
if (NS_WARN_IF(!aCallback)) {
return NS_ERROR_INVALID_ARG;
}
mCallback = aCallback;
if (NS_WARN_IF(!aDescription)) {
return NS_ERROR_INVALID_ARG;
}
uint16_t serverPort;
nsresult rv = aDescription->GetTcpPort(&serverPort);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIArray> serverHosts;
rv = aDescription->GetTcpAddress(getter_AddRefs(serverHosts));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
// Ultimately we may use all the available addresses. DataChannel appears
// more robust upon handling ICE. And at the first stage Presentation API is
// only exposed on Firefox OS where the first IP appears enough for most
// scenarios.
nsCOMPtr<nsISupportsCString> supportStr = do_QueryElementAt(serverHosts, 0);
if (NS_WARN_IF(!supportStr)) {
return NS_ERROR_INVALID_ARG;
}
nsAutoCString serverHost;
supportStr->GetData(serverHost);
if (serverHost.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
SetReadyState(CONNECTING);
nsCOMPtr<nsISocketTransportService> sts =
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
if (NS_WARN_IF(!sts)) {
return NS_ERROR_NOT_AVAILABLE;
}
rv = sts->CreateTransport(nullptr, 0, serverHost, serverPort, nullptr,
getter_AddRefs(mTransport));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
mTransport->SetEventSink(this, mainThread);
rv = CreateStream();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
PresentationSessionTransport::CreateStream()
{
nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// If the other side is not listening, we will get an |onInputStreamReady|
// callback where |available| raises to indicate the connection was refused.
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mSocketInputStream);
if (NS_WARN_IF(!asyncStream)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIThread> mainThread;
NS_GetMainThread(getter_AddRefs(mainThread));
rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainThread);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mInputStreamScriptable->Init(mSocketInputStream);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsISocketTransportService> sts =
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
if (NS_WARN_IF(!sts)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
rv = mMultiplexStreamCopier->Init(mMultiplexStream,
mSocketOutputStream,
target,
true, /* source buffered */
false, /* sink buffered */
BUFFER_SIZE,
false, /* close source */
false); /* close sink */
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
PresentationSessionTransport::CreateInputStreamPump()
{
nsresult rv;
mInputStreamPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mInputStreamPump->Init(mSocketInputStream, -1, -1, 0, 0, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mInputStreamPump->AsyncRead(this, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransport::GetCallback(nsIPresentationSessionTransportCallback** aCallback)
{
nsCOMPtr<nsIPresentationSessionTransportCallback> callback = mCallback;
callback.forget(aCallback);
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransport::SetCallback(nsIPresentationSessionTransportCallback* aCallback)
{
mCallback = aCallback;
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransport::GetSelfAddress(nsINetAddr** aSelfAddress)
{
if (NS_WARN_IF(mReadyState != OPEN)) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
return mTransport->GetScriptableSelfAddr(aSelfAddress);
}
void
PresentationSessionTransport::EnsureCopying()
{
if (mAsyncCopierActive) {
return;
}
mAsyncCopierActive = true;
nsRefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
NS_WARN_IF(NS_FAILED(mMultiplexStreamCopier->AsyncCopy(callbacks, nullptr)));
}
void
PresentationSessionTransport::NotifyCopyComplete(nsresult aStatus)
{
mAsyncCopierActive = false;
mMultiplexStream->RemoveStream(0);
if (NS_WARN_IF(NS_FAILED(aStatus))) {
if (mReadyState != CLOSED) {
mCloseStatus = aStatus;
SetReadyState(CLOSED);
}
return;
}
uint32_t count;
nsresult rv = mMultiplexStream->GetCount(&count);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (count) {
EnsureCopying();
return;
}
if (mReadyState == CLOSING) {
mSocketOutputStream->Close();
mCloseStatus = NS_OK;
SetReadyState(CLOSED);
}
}
NS_IMETHODIMP
PresentationSessionTransport::Send(nsIInputStream* aData)
{
if (NS_WARN_IF(mReadyState != OPEN)) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
mMultiplexStream->AppendStream(aData);
EnsureCopying();
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransport::Close(nsresult aReason)
{
if (mReadyState == CLOSED || mReadyState == CLOSING) {
return NS_OK;
}
mCloseStatus = aReason;
SetReadyState(CLOSING);
uint32_t count = 0;
mMultiplexStream->GetCount(&count);
if (!count) {
mSocketOutputStream->Close();
}
mSocketInputStream->Close();
return NS_OK;
}
void
PresentationSessionTransport::SetReadyState(ReadyState aReadyState)
{
mReadyState = aReadyState;
if (mReadyState == OPEN && mCallback) {
// Notify the transport channel is ready.
NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady()));
} else if (mReadyState == CLOSED && mCallback) {
// Notify the transport channel has been shut down.
NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportClosed(mCloseStatus)));
}
}
// nsITransportEventSink
NS_IMETHODIMP
PresentationSessionTransport::OnTransportStatus(nsITransport* aTransport,
nsresult aStatus,
int64_t aProgress,
int64_t aProgressMax)
{
MOZ_ASSERT(NS_IsMainThread());
if (aStatus != NS_NET_STATUS_CONNECTED_TO) {
return NS_OK;
}
SetReadyState(OPEN);
nsresult rv = CreateInputStreamPump();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// nsIInputStreamCallback
NS_IMETHODIMP
PresentationSessionTransport::OnInputStreamReady(nsIAsyncInputStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
// Only used for detecting if the connection was refused.
uint64_t dummy;
nsresult rv = aStream->Available(&dummy);
if (NS_WARN_IF(NS_FAILED(rv))) {
if (mReadyState != CLOSED) {
mCloseStatus = NS_ERROR_CONNECTION_REFUSED;
SetReadyState(CLOSED);
}
}
return NS_OK;
}
// nsIRequestObserver
NS_IMETHODIMP
PresentationSessionTransport::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
// Do nothing.
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransport::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatusCode)
{
MOZ_ASSERT(NS_IsMainThread());
uint32_t count;
nsresult rv = mMultiplexStream->GetCount(&count);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mInputStreamPump = nullptr;
if (count != 0 && NS_SUCCEEDED(aStatusCode)) {
// If we have some buffered output still, and status is not an error, the
// other side has done a half-close, but we don't want to be in the close
// state until we are done sending everything that was buffered. We also
// don't want to call |NotifyTransportClosed| yet.
return NS_OK;
}
// We call this even if there is no error.
if (mReadyState != CLOSED) {
mCloseStatus = aStatusCode;
SetReadyState(CLOSED);
}
return NS_OK;
}
// nsIStreamListener
NS_IMETHODIMP
PresentationSessionTransport::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aStream,
uint64_t aOffset,
uint32_t aCount)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!mCallback)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCString data;
nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Pass the incoming data to the listener.
return mCallback->NotifyData(data);
}

View File

@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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 mozilla_dom_PresentationSessionTransport_h
#define mozilla_dom_PresentationSessionTransport_h
#include "mozilla/nsRefPtr.h"
#include "nsCOMPtr.h"
#include "nsIAsyncInputStream.h"
#include "nsIPresentationSessionTransport.h"
#include "nsIStreamListener.h"
#include "nsISupportsImpl.h"
#include "nsITransport.h"
class nsISocketTransport;
class nsIInputStreamPump;
class nsIScriptableInputStream;
class nsIMultiplexInputStream;
class nsIAsyncStreamCopier;
class nsIInputStream;
namespace mozilla {
namespace dom {
/*
* App-to-App transport channel for the presentation session. It's usually
* initialized with an |InitWithSocketTransport| call if at the presenting sender
* side; whereas it's initialized with an |InitWithChannelDescription| if at the
* presenting receiver side. The lifetime is managed in either
* |PresentationRequesterInfo| (sender side) or |PresentationResponderInfo|
* (receiver side) in PresentationSessionInfo.cpp.
*
* TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
* The implementation over the TCP channel is primarily used for the early stage
* of Presentation API (without SSL) and should be migrated to DataChannel with
* full support soon.
*/
class PresentationSessionTransport final : public nsIPresentationSessionTransport
, public nsITransportEventSink
, public nsIInputStreamCallback
, public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT
NS_DECL_NSITRANSPORTEVENTSINK
NS_DECL_NSIINPUTSTREAMCALLBACK
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
PresentationSessionTransport();
void NotifyCopyComplete(nsresult aStatus);
private:
~PresentationSessionTransport();
nsresult CreateStream();
nsresult CreateInputStreamPump();
void EnsureCopying();
enum ReadyState {
CONNECTING,
OPEN,
CLOSING,
CLOSED
};
void SetReadyState(ReadyState aReadyState);
ReadyState mReadyState;
bool mAsyncCopierActive;
nsresult mCloseStatus;
// Raw socket streams
nsCOMPtr<nsISocketTransport> mTransport;
nsCOMPtr<nsIInputStream> mSocketInputStream;
nsCOMPtr<nsIOutputStream> mSocketOutputStream;
// Input stream machinery
nsCOMPtr<nsIInputStreamPump> mInputStreamPump;
nsCOMPtr<nsIScriptableInputStream> mInputStreamScriptable;
// Output stream machinery
nsCOMPtr<nsIMultiplexInputStream> mMultiplexStream;
nsCOMPtr<nsIAsyncStreamCopier> mMultiplexStreamCopier;
nsCOMPtr<nsIPresentationSessionTransportCallback> mCallback;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationSessionTransport_h

View File

@ -10,9 +10,6 @@ interface nsIPresentationChannelDescription;
interface nsISocketTransport;
%{C++
#define PRESENTATION_SESSION_TRANSPORT_CID \
{ 0xc9d023f4, 0x6228, 0x4c07, \
{ 0x8b, 0x1d, 0x9c, 0x19, 0x57, 0x3f, 0xaa, 0x27 } }
#define PRESENTATION_SESSION_TRANSPORT_CONTRACTID \
"@mozilla.org/presentation/presentationsessiontransport;1"
%}

View File

@ -19,6 +19,7 @@ EXPORTS.mozilla.dom += [
'PresentationService.h',
'PresentationSession.h',
'PresentationSessionInfo.h',
'PresentationSessionTransport.h',
]
UNIFIED_SOURCES += [
@ -32,6 +33,7 @@ UNIFIED_SOURCES += [
'PresentationSession.cpp',
'PresentationSessionInfo.cpp',
'PresentationSessionRequest.cpp',
'PresentationSessionTransport.cpp',
]
EXTRA_COMPONENTS += [

View File

@ -0,0 +1,171 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr, Constructor: CC } = Components;
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
"nsIServerSocket",
"init");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
var testServer = null;
var clientTransport = null;
var serverTransport = null;
const clientMessage = "Client Message";
const serverMessage = "Server Message";
const address = Cc["@mozilla.org/supports-cstring;1"]
.createInstance(Ci.nsISupportsCString);
address.data = "127.0.0.1";
const addresses = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
addresses.appendElement(address, false);
const serverChannelDescription = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
type: 1,
tcpAddress: addresses,
};
var isClientReady = false;
var isServerReady = false;
var isClientClosed = false;
var isServerClosed = false;
const clientCallback = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
notifyTransportReady: function () {
Assert.ok(true, "Client transport ready.");
isClientReady = true;
if (isClientReady && isServerReady) {
run_next_test();
}
},
notifyTransportClosed: function (aReason) {
Assert.ok(true, "Client transport is closed.");
isClientClosed = true;
if (isClientClosed && isServerClosed) {
run_next_test();
}
},
notifyData: function(aData) {
Assert.equal(aData, serverMessage, "Client transport receives data.");
run_next_test();
},
};
const serverCallback = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
notifyTransportReady: function () {
Assert.ok(true, "Server transport ready.");
isServerReady = true;
if (isClientReady && isServerReady) {
run_next_test();
}
},
notifyTransportClosed: function (aReason) {
Assert.ok(true, "Server transport is closed.");
isServerClosed = true;
if (isClientClosed && isServerClosed) {
run_next_test();
}
},
notifyData: function(aData) {
Assert.equal(aData, clientMessage, "Server transport receives data.");
run_next_test();
},
};
function TestServer() {
this.serverSocket = ServerSocket(-1, true, -1);
this.serverSocket.asyncListen(this)
}
TestServer.prototype = {
onSocketAccepted: function(aSocket, aTransport) {
print("Test server gets a client connection.");
serverTransport = Cc["@mozilla.org/presentation/presentationsessiontransport;1"]
.createInstance(Ci.nsIPresentationSessionTransport);
serverTransport.initWithSocketTransport(aTransport, serverCallback);
},
onStopListening: function(aSocket) {
print("Test server stops listening.");
},
close: function() {
if (this.serverSocket) {
this.serverSocket.close();
this.serverSocket = null;
}
}
};
// Set up the transport connection and ensure |notifyTransportReady| triggered
// at both sides.
function setup() {
clientTransport = Cc["@mozilla.org/presentation/presentationsessiontransport;1"]
.createInstance(Ci.nsIPresentationSessionTransport);
clientTransport.initWithChannelDescription(serverChannelDescription, clientCallback);
}
// Test |selfAddress| attribute of |nsIPresentationSessionTransport|.
function selfAddress() {
var serverSelfAddress = serverTransport.selfAddress;
Assert.equal(serverSelfAddress.address, address.data, "The self address of server transport should be set.");
Assert.equal(serverSelfAddress.port, testServer.serverSocket.port, "The port of server transport should be set.");
var clientSelfAddress = clientTransport.selfAddress;
Assert.ok(clientSelfAddress.address, "The self address of client transport should be set.");
Assert.ok(clientSelfAddress.port, "The port of client transport should be set.");
run_next_test();
}
// Test the client sends a message and then a corresponding notification gets
// triggered at the server side.
function clientSendMessage() {
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
stream.setData(clientMessage, clientMessage.length);
clientTransport.send(stream);
}
// Test the server sends a message an then a corresponding notification gets
// triggered at the client side.
function serverSendMessage() {
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
stream.setData(serverMessage, serverMessage.length);
serverTransport.send(stream);
}
function transportClose() {
clientTransport.close(Cr.NS_OK);
}
function shutdown() {
testServer.close();
run_next_test();
}
add_test(setup);
add_test(selfAddress);
add_test(clientSendMessage);
add_test(serverSendMessage);
add_test(transportClose);
add_test(shutdown);
function run_test() {
testServer = new TestServer();
// Get the port of the test server.
serverChannelDescription.tcpPort = testServer.serverSocket.port;
run_next_test();
}

View File

@ -4,4 +4,5 @@ tail =
[test_multicast_dns_device_provider.js]
[test_presentation_device_manager.js]
[test_presentation_session_transport.js]
[test_tcp_control_channel.js]

View File

@ -262,6 +262,7 @@ static void Shutdown();
#include "GMPService.h"
#include "mozilla/dom/PresentationDeviceManager.h"
#include "mozilla/dom/PresentationSessionTransport.h"
#include "mozilla/TextInputProcessor.h"
@ -294,6 +295,9 @@ using mozilla::gmp::GeckoMediaPluginService;
#define PRESENTATION_DEVICE_MANAGER_CID \
{ 0xe1e79dec, 0x4085, 0x4994, { 0xac, 0x5b, 0x74, 0x4b, 0x01, 0x66, 0x97, 0xe6 } }
#define PRESENTATION_SESSION_TRANSPORT_CID \
{ 0xc9d023f4, 0x6228, 0x4c07, { 0x8b, 0x1d, 0x9c, 0x19, 0x57, 0x3f, 0xaa, 0x27 } }
already_AddRefed<nsIPresentationService> NS_CreatePresentationService();
// Factory Constructor
@ -406,6 +410,7 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(FakeInputPortService,
NS_GENERIC_FACTORY_CONSTRUCTOR(InputPortData)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPresentationService,
NS_CreatePresentationService)
NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationSessionTransport)
//-----------------------------------------------------------------------------
static bool gInitialized = false;
@ -864,6 +869,7 @@ NS_DEFINE_NAMED_CID(GECKO_MEDIA_PLUGIN_SERVICE_CID);
NS_DEFINE_NAMED_CID(PRESENTATION_SERVICE_CID);
NS_DEFINE_NAMED_CID(PRESENTATION_DEVICE_MANAGER_CID);
NS_DEFINE_NAMED_CID(PRESENTATION_SESSION_TRANSPORT_CID);
NS_DEFINE_NAMED_CID(TEXT_INPUT_PROCESSOR_CID);
@ -1163,6 +1169,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
{ &kTV_PROGRAM_DATA_CID, false, nullptr, TVProgramDataConstructor },
{ &kPRESENTATION_SERVICE_CID, false, nullptr, nsIPresentationServiceConstructor },
{ &kPRESENTATION_DEVICE_MANAGER_CID, false, nullptr, PresentationDeviceManagerConstructor },
{ &kPRESENTATION_SESSION_TRANSPORT_CID, false, nullptr, PresentationSessionTransportConstructor },
{ &kTEXT_INPUT_PROCESSOR_CID, false, nullptr, TextInputProcessorConstructor },
{ &kFAKE_INPUTPORT_SERVICE_CID, false, nullptr, FakeInputPortServiceConstructor },
{ &kINPUTPORT_DATA_CID, false, nullptr, InputPortDataConstructor },
@ -1333,6 +1340,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
{ NS_VOICEMAIL_SERVICE_CONTRACTID, &kNS_VOICEMAIL_SERVICE_CID },
{ PRESENTATION_SERVICE_CONTRACTID, &kPRESENTATION_SERVICE_CID },
{ PRESENTATION_DEVICE_MANAGER_CONTRACTID, &kPRESENTATION_DEVICE_MANAGER_CID },
{ PRESENTATION_SESSION_TRANSPORT_CONTRACTID, &kPRESENTATION_SESSION_TRANSPORT_CID },
{ "@mozilla.org/text-input-processor;1", &kTEXT_INPUT_PROCESSOR_CID },
{ FAKE_INPUTPORT_SERVICE_CONTRACTID, &kFAKE_INPUTPORT_SERVICE_CID },
{ INPUTPORT_DATA_CONTRACTID, &kINPUTPORT_DATA_CID },