mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 1069230 - Presentation API implementation. Part 8 - Data transport channel. r=jdm
This commit is contained in:
parent
905d6d07e3
commit
1efa4382a3
@ -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;
|
||||
}
|
||||
|
@ -145,6 +145,8 @@ private:
|
||||
|
||||
void Shutdown(nsresult aReason) override;
|
||||
|
||||
nsresult GetAddress(nsACString& aAddress);
|
||||
|
||||
nsCOMPtr<nsIServerSocket> mServerSocket;
|
||||
};
|
||||
|
||||
|
484
dom/presentation/PresentationSessionTransport.cpp
Normal file
484
dom/presentation/PresentationSessionTransport.cpp
Normal 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);
|
||||
}
|
99
dom/presentation/PresentationSessionTransport.h
Normal file
99
dom/presentation/PresentationSessionTransport.h
Normal 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
|
@ -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"
|
||||
%}
|
||||
|
@ -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 += [
|
||||
|
@ -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();
|
||||
}
|
@ -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]
|
||||
|
@ -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 },
|
||||
|
Loading…
Reference in New Issue
Block a user