Bug 1276378 - Part 1: Add terminate command in control protocol. r=junior

MozReview-Commit-ID: BwfJKcXmN07

--HG--
extra : rebase_source : 7df8c968def5fa9640499257ce8b8814fe884388
This commit is contained in:
Shih-Chiang Chien 2016-05-26 16:22:27 -07:00
Родитель faec6da232
Коммит c8b67767f3
19 изменённых файлов: 627 добавлений и 0 удалений

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

@ -14,6 +14,7 @@
#include "nsIObserverService.h"
#include "nsXULAppAPI.h"
#include "PresentationSessionRequest.h"
#include "PresentationTerminateRequest.h"
namespace mozilla {
namespace dom {
@ -239,6 +240,28 @@ PresentationDeviceManager::OnSessionRequest(nsIPresentationDevice* aDevice,
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::OnTerminateRequest(nsIPresentationDevice* aDevice,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver)
{
NS_ENSURE_ARG(aDevice);
NS_ENSURE_ARG(aControlChannel);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
RefPtr<PresentationTerminateRequest> request =
new PresentationTerminateRequest(aDevice, aPresentationId,
aControlChannel, aIsFromReceiver);
obs->NotifyObservers(request,
PRESENTATION_TERMINATE_REQUEST_TOPIC,
nullptr);
return NS_OK;
}
// nsIObserver
NS_IMETHODIMP
PresentationDeviceManager::Observe(nsISupports *aSubject,

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

@ -0,0 +1,73 @@
/* -*- 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/. */
#include "PresentationTerminateRequest.h"
#include "nsIPresentationControlChannel.h"
#include "nsIPresentationDevice.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(PresentationTerminateRequest, nsIPresentationTerminateRequest)
PresentationTerminateRequest::PresentationTerminateRequest(
nsIPresentationDevice* aDevice,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver)
: mPresentationId(aPresentationId)
, mDevice(aDevice)
, mControlChannel(aControlChannel)
, mIsFromReceiver(aIsFromReceiver)
{
}
PresentationTerminateRequest::~PresentationTerminateRequest()
{
}
// nsIPresentationTerminateRequest
NS_IMETHODIMP
PresentationTerminateRequest::GetDevice(nsIPresentationDevice** aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
nsCOMPtr<nsIPresentationDevice> device = mDevice;
device.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
PresentationTerminateRequest::GetPresentationId(nsAString& aRetVal)
{
aRetVal = mPresentationId;
return NS_OK;
}
NS_IMETHODIMP
PresentationTerminateRequest::GetControlChannel(
nsIPresentationControlChannel** aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
nsCOMPtr<nsIPresentationControlChannel> controlChannel = mControlChannel;
controlChannel.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
PresentationTerminateRequest::GetIsFromReceiver(bool* aRetVal)
{
*aRetVal = mIsFromReceiver;
return NS_OK;
}
} // namespace dom
} // namespace mozilla

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

@ -0,0 +1,41 @@
/* -*- 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_PresentationTerminateRequest_h__
#define mozilla_dom_PresentationTerminateRequest_h__
#include "nsIPresentationTerminateRequest.h"
#include "nsCOMPtr.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
class PresentationTerminateRequest final : public nsIPresentationTerminateRequest
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONTERMINATEREQUEST
PresentationTerminateRequest(nsIPresentationDevice* aDevice,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver);
private:
virtual ~PresentationTerminateRequest();
nsString mPresentationId;
nsCOMPtr<nsIPresentationDevice> mDevice;
nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
bool mIsFromReceiver;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PresentationTerminateRequest_h__ */

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

@ -18,6 +18,7 @@ XPIDL_SOURCES += [
'nsIPresentationSessionRequest.idl',
'nsIPresentationSessionTransport.idl',
'nsIPresentationSessionTransportBuilder.idl',
'nsIPresentationTerminateRequest.idl',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':

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

@ -110,6 +110,13 @@ interface nsIPresentationControlChannel: nsISupports
*/
void launch(in DOMString presentationId, in DOMString url);
/*
* Terminate a presentation on remote endpoint.
* @param presentationId The Id for representing this session.
* @throws NS_ERROR_FAILURE on failure
*/
void terminate(in DOMString presentationId);
/*
* Disconnect the control channel.
* @param reason The reason of disconnecting channel; NS_OK represents normal.

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

@ -45,6 +45,18 @@ interface nsIPresentationControlServerListener: nsISupports
in DOMString aUrl,
in DOMString aPresentationId,
in nsIPresentationControlChannel aControlChannel);
/**
* Callback while the remote host is requesting to terminate a presentation session.
* @param aDeviceInfo The device information related to the remote host.
* @param aPresentationId The Id for representing this session.
* @param aControlChannel The control channel for this session.
* @param aIsFromReceiver true if termination is initiated by receiver.
*/
void onTerminateRequest(in nsITCPDeviceInfo aDeviceInfo,
in DOMString aPresentationId,
in nsIPresentationControlChannel aControlChannel,
in boolean aIsFromReceiver);
};
/**

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

@ -32,6 +32,18 @@ interface nsIPresentationDeviceListener: nsISupports
in DOMString url,
in DOMString presentationId,
in nsIPresentationControlChannel controlChannel);
/*
* Callback while the remote device is requesting to terminate a presentation session.
* @param device The remote device that sent session request.
* @param presentationId The Id for representing this session.
* @param controlChannel The control channel for this session.
* @param aIsFromReceiver true if termination is initiated by receiver.
*/
void onTerminateRequest(in nsIPresentationDevice device,
in DOMString presentationId,
in nsIPresentationControlChannel controlChannel,
in boolean aIsFromReceiver);
};
/*

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

@ -0,0 +1,33 @@
/* 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 "nsISupports.idl"
interface nsIPresentationDevice;
interface nsIPresentationControlChannel;
%{C++
#define PRESENTATION_TERMINATE_REQUEST_TOPIC "presentation-terminate-request"
%}
/*
* The event of a device requesting for terminating a presentation session. User can
* monitor the terminate request on every device by observing "presentation-terminate-request".
*/
[scriptable, uuid(3ddbf3a4-53ee-4b70-9bbc-58ac90dce6b5)]
interface nsIPresentationTerminateRequest: nsISupports
{
// The device which requesting to terminate presentation session.
readonly attribute nsIPresentationDevice device;
// The Id for representing this session.
readonly attribute DOMString presentationId;
// The control channel for this session.
// Should only use this channel to complete session termination.
readonly attribute nsIPresentationControlChannel controlChannel;
// True if termination is initiated by receiver.
readonly attribute boolean isFromReceiver;
};

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

@ -52,6 +52,7 @@ UNIFIED_SOURCES += [
'PresentationSessionInfo.cpp',
'PresentationSessionRequest.cpp',
'PresentationTCPSessionTransport.cpp',
'PresentationTerminateRequest.cpp',
]
EXTRA_COMPONENTS += [

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

@ -50,6 +50,12 @@ var handlers = [
case CommandType.LAUNCH_ACK:
stateMachine._notifyLaunch(command.presentationId);
break;
case CommandType.TERMINATE:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.TERMINATE_ACK:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.ANSWER:
case CommandType.ICE_CANDIDATE:
stateMachine._notifyChannelDescriptor(command);
@ -87,6 +93,24 @@ ControllerStateMachine.prototype = {
}
},
terminate: function _terminate(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE,
presentationId: presentationId,
});
}
},
terminateAck: function _terminateAck(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE_ACK,
presentationId: presentationId,
});
}
},
sendOffer: function _sendOffer(offer) {
if (this.state === State.CONNECTED) {
this._sendCommand({
@ -172,6 +196,10 @@ ControllerStateMachine.prototype = {
this._channel.notifyLaunch(presentationId);
},
_notifyTerminate: function _notifyTerminate(presentationId) {
this._channel.notifyTerminate(presentationId);
},
_notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
switch (command.type) {
case CommandType.ANSWER:

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

@ -405,6 +405,37 @@ DisplayDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo,
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aDeviceInfo);
MOZ_ASSERT(aControlChannel);
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener;
rv = GetListener(getter_AddRefs(listener));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(!listener);
rv = listener->OnTerminateRequest(mDevice,
aPresentationId,
aControlChannel,
aIsFromReceiver);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// nsIObserver
NS_IMETHODIMP
DisplayDeviceProvider::Observe(nsISupports* aSubject,

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

@ -231,6 +231,12 @@ LegacyTCPControlChannel.prototype = {
this._sendInit();
},
terminate: function() {
// Legacy protocol doesn't support extra terminate protocol.
// Trigger error handling for browser to shutdown all the resource locally.
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
sendOffer: function(aOffer) {
let msg = {
type: "requestSession:Offer",

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

@ -917,6 +917,50 @@ MulticastDNSDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo,
return NS_OK;
}
NS_IMETHODIMP
MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver)
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoCString address;
Unused << aDeviceInfo->GetAddress(address);
LOG_I("OnTerminateRequest: %s", address.get());
RefPtr<Device> device;
uint32_t index;
if (FindDeviceByAddress(address, index)) {
device = mDevices[index];
} else {
// Create a one-time device object for non-discoverable controller.
// This device will not be in the list of available devices and cannot
// be used for requesting session.
nsAutoCString id;
Unused << aDeviceInfo->GetId(id);
uint16_t port;
Unused << aDeviceInfo->GetPort(&port);
device = new Device(id,
/* aName = */ id,
/* aType = */ EmptyCString(),
address,
port,
DeviceState::eActive,
/* aProvider = */ nullptr);
}
nsCOMPtr<nsIPresentationDeviceListener> listener;
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
Unused << listener->OnTerminateRequest(device, aPresentationId,
aControlChannel, aIsFromReceiver);
}
return NS_OK;
}
// nsIObserver
NS_IMETHODIMP
MulticastDNSDeviceProvider::Observe(nsISupports* aSubject,

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

@ -166,6 +166,11 @@ PresentationControlService.prototype = {
onSessionRequest: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) {
DEBUG && log("PresentationControlService - onSessionRequest: " +
aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line
if (!this.listener) {
this.releaseControlChannel(aControlChannel);
return;
}
this.listener.onSessionRequest(aDeviceInfo,
aUrl,
aPresentationId,
@ -173,6 +178,21 @@ PresentationControlService.prototype = {
this.releaseControlChannel(aControlChannel);
},
onSessionTerminate: function(aDeviceInfo, aPresentationId, aControlChannel, aIsFromReceiver) {
DEBUG && log("TCPPresentationServer - onSessionTerminate: " +
aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line
if (!this.listener) {
this.releaseControlChannel(aControlChannel);
return;
}
this.listener.onTerminateRequest(aDeviceInfo,
aPresentationId,
aControlChannel,
aIsFromReceiver);
this.releaseControlChannel(aControlChannel);
},
// nsIServerSocketListener (Triggered by nsIServerSocket.init)
onSocketAccepted: function(aServerSocket, aClientSocket) {
DEBUG && log("PresentationControlService - onSocketAccepted: " +
@ -390,6 +410,16 @@ TCPControlChannel.prototype = {
this._stateMachine.launch(aPresentationId, aUrl);
},
terminate: function(aPresentationId) {
if (!this._terminatingId) {
this._terminatingId = aPresentationId;
this._stateMachine.terminate(aPresentationId);
} else {
this._stateMachine.terminateAck(aPresentationId);
delete this._terminatingId;
}
},
// may throw an exception
_send: function(aMsg) {
DEBUG && log("TCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2)); // jshint ignore:line
@ -650,6 +680,26 @@ TCPControlChannel.prototype = {
}
},
notifyTerminate: function(presentationId) {
if (!this._terminatingId) {
this._terminatingId = presentationId;
this._presentationService.onSessionTerminate(this._deviceInfo,
presentationId,
this,
this._direction === "sender");
return;
}
if (this._terminatingId !== presentationId) {
// Requested presentation Id doesn't matched with the one in ACK.
// Disconnect the control channel with error.
DEBUG && log("TCPControlChannel - unmatched terminatingId: " + presentationId); // jshint ignore:line
this.disconnect(Cr.NS_ERROR_FAILURE);
}
delete this._terminatingId;
},
notifyOffer: function(offer) {
this._onOffer(offer);
},

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

@ -58,6 +58,12 @@ var handlers = [
presentationId: command.presentationId
});
break;
case CommandType.TERMINATE:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.TERMINATE_ACK:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.OFFER:
case CommandType.ICE_CANDIDATE:
stateMachine._notifyChannelDescriptor(command);
@ -89,6 +95,24 @@ ReceiverStateMachine.prototype = {
debug("receiver shouldn't trigger launch");
},
terminate: function _terminate(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE,
presentationId: presentationId,
});
}
},
terminateAck: function _terminateAck(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE_ACK,
presentationId: presentationId,
});
}
},
sendOffer: function _sendOffer() {
// offer can only be sent by controlling UA.
debug("receiver shouldn't generate offer");
@ -171,6 +195,10 @@ ReceiverStateMachine.prototype = {
this._channel.notifyLaunch(presentationId, url);
},
_notifyTerminate: function _notifyTerminate(presentationId) {
this._channel.notifyTerminate(presentationId);
},
_notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
switch (command.type) {
case CommandType.OFFER:

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

@ -25,6 +25,8 @@ const CommandType = Object.freeze({
// presentation session life cycle
LAUNCH: "launch", // { presentationId: <string>, url: <string> }
LAUNCH_ACK: "launch-ack", // { presentationId: <string> }
TERMINATE: "terminate", // { presentationId: <string> }
TERMINATE_ACK: "terminate-ack", // { presentationId: <string> }
// session transport establishment
OFFER: "offer", // { offer: <json> }
ANSWER: "answer", // { answer: <json> }

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

@ -23,6 +23,7 @@ TestPresentationControlChannel.prototype = {
sendAnswer: function(answer) {},
disconnect: function() {},
launch: function() {},
terminate: function() {},
set listener(listener) {},
get listener() {},
};
@ -139,6 +140,27 @@ function sessionRequest() {
.onSessionRequest(testDevice, testUrl, testPresentationId, testControlChannel);
}
function terminateRequest() {
let testUrl = 'http://www.example.org/';
let testPresentationId = 'test-presentation-id';
let testControlChannel = new TestPresentationControlChannel();
let testIsFromReceiver = true;
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
let request = subject.QueryInterface(Ci.nsIPresentationTerminateRequest);
Assert.equal(request.device.id, testDevice.id, 'expected device');
Assert.equal(request.presentationId, testPresentationId, 'expected presentation Id');
Assert.equal(request.isFromReceiver, testIsFromReceiver, 'expected isFromReceiver');
run_next_test();
}, 'presentation-terminate-request', false);
manager.QueryInterface(Ci.nsIPresentationDeviceListener)
.onTerminateRequest(testDevice, testPresentationId,
testControlChannel, testIsFromReceiver);
}
function removeDevice() {
Services.obs.addObserver(function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
@ -176,6 +198,7 @@ add_test(forceDiscovery);
add_test(addDevice);
add_test(updateDevice);
add_test(sessionRequest);
add_test(terminateRequest);
add_test(removeDevice);
add_test(removeProvider);

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

@ -78,6 +78,45 @@ function launch() {
};
}
function terminateByController() {
Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
controllerState.terminate(testPresentationId);
mockReceiverChannel.notifyTerminate = function(presentationId) {
Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
Assert.equal(presentationId, testPresentationId, 'expected presentationId received');
mockControllerChannel.notifyTerminate = function(presentationId) {
Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
Assert.equal(presentationId, testPresentationId, 'expected presentationId received from ack');
run_next_test();
};
receiverState.terminateAck(presentationId);
};
}
function terminateByReceiver() {
Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
receiverState.terminate(testPresentationId);
mockControllerChannel.notifyTerminate = function(presentationId) {
Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
Assert.equal(presentationId, testPresentationId, 'expected presentationId received');
mockReceiverChannel.notifyTerminate = function(presentationId) {
Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
Assert.equal(presentationId, testPresentationId, 'expected presentationId received from ack');
run_next_test();
};
controllerState.terminateAck(presentationId);
};
}
function exchangeSDP() {
Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
@ -185,6 +224,8 @@ function abnormalDisconnect() {
add_test(connect);
add_test(launch);
add_test(terminateByController);
add_test(terminateByReceiver);
add_test(exchangeSDP);
add_test(disconnect);
add_test(receiverDisconnect);

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

@ -181,6 +181,175 @@ function testPresentationServer() {
};
}
function terminateRequest() {
let yayFuncs = makeJointSuccess(['controllerControlChannelConnected',
'controllerControlChannelDisconnected',
'presenterControlChannelDisconnected']);
let controllerControlChannel;
pcs.listener = {
onTerminateRequest: function(deviceInfo, presentationId, controlChannel, isFromReceiverj) {
controllerControlChannel = controlChannel;
Assert.equal(deviceInfo.id, pcs.id, 'expected device id');
Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address');
Assert.equal(presentationId, 'testPresentationId', 'expected presentation id');
Assert.equal(isFromReceiver, false, 'expected request from controller');
controllerControlChannel.listener = {
notifyConnected: function() {
Assert.ok(true, 'control channel notify connected');
yayFuncs.controllerControlChannelConnected();
},
notifyDisconnected: function(aReason) {
Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'controllerControlChannel notify disconncted');
yayFuncs.controllerControlChannelDisconnected();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]),
};
let presenterDeviceInfo = {
id: 'presentatorID',
address: '127.0.0.1',
port: PRESENTER_CONTROL_CHANNEL_PORT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
};
let presenterControlChannel = pcs.connect(presenterDeviceInfo);
presenterControlChannel.listener = {
notifyConnected: function() {
presenterControlChannel.terminate('testPresentationId', 'http://example.com');
presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON);
},
notifyDisconnected: function(aReason) {
Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. presenterControlChannel notify disconnected');
yayFuncs.presenterControlChannelDisconnected();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
}
function terminateRequest() {
let yayFuncs = makeJointSuccess(['controllerControlChannelConnected',
'controllerControlChannelDisconnected',
'presenterControlChannelDisconnected',
'terminatedByController',
'terminatedByReceiver']);
let controllerControlChannel;
let terminatePhase = 'controller';
pcs.listener = {
onTerminateRequest: function(deviceInfo, presentationId, controlChannel, isFromReceiver) {
Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address');
Assert.equal(presentationId, 'testPresentationId', 'expected presentation id');
controlChannel.terminate(presentationId); // Reply terminate ack.
if (terminatePhase === 'controller') {
controllerControlChannel = controlChannel;
Assert.equal(deviceInfo.id, pcs.id, 'expected controller device id');
Assert.equal(isFromReceiver, false, 'expected request from controller');
yayFuncs.terminatedByController();
controllerControlChannel.listener = {
notifyConnected: function() {
Assert.ok(true, 'control channel notify connected');
yayFuncs.controllerControlChannelConnected();
terminatePhase = 'receiver';
controllerControlChannel.terminate('testPresentationId');
},
notifyDisconnected: function(aReason) {
Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'controllerControlChannel notify disconncted');
yayFuncs.controllerControlChannelDisconnected();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
} else {
Assert.equal(deviceInfo.id, presenterDeviceInfo.id, 'expected presenter device id');
Assert.equal(isFromReceiver, true, 'expected request from receiver');
yayFuncs.terminatedByReceiver();
presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON);
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]),
};
let presenterDeviceInfo = {
id: 'presentatorID',
address: '127.0.0.1',
port: PRESENTER_CONTROL_CHANNEL_PORT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
};
let presenterControlChannel = pcs.connect(presenterDeviceInfo);
presenterControlChannel.listener = {
notifyConnected: function() {
presenterControlChannel.terminate('testPresentationId');
},
notifyDisconnected: function(aReason) {
Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. presenterControlChannel notify disconnected');
yayFuncs.presenterControlChannelDisconnected();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
}
function terminateRequestAbnormal() {
let yayFuncs = makeJointSuccess(['controllerControlChannelConnected',
'controllerControlChannelDisconnected',
'presenterControlChannelDisconnected']);
let controllerControlChannel;
pcs.listener = {
onTerminateRequest: function(deviceInfo, presentationId, controlChannel, isFromReceiver) {
Assert.equal(deviceInfo.id, pcs.id, 'expected controller device id');
Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address');
Assert.equal(presentationId, 'testPresentationId', 'expected presentation id');
Assert.equal(isFromReceiver, false, 'expected request from controller');
controlChannel.terminate('unmatched-presentationId'); // Reply abnormal terminate ack.
controllerControlChannel = controlChannel;
controllerControlChannel.listener = {
notifyConnected: function() {
Assert.ok(true, 'control channel notify connected');
yayFuncs.controllerControlChannelConnected();
},
notifyDisconnected: function(aReason) {
Assert.equal(aReason, Cr.NS_ERROR_FAILURE, 'controllerControlChannel notify disconncted with error');
yayFuncs.controllerControlChannelDisconnected();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]),
};
let presenterDeviceInfo = {
id: 'presentatorID',
address: '127.0.0.1',
port: PRESENTER_CONTROL_CHANNEL_PORT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
};
let presenterControlChannel = pcs.connect(presenterDeviceInfo);
presenterControlChannel.listener = {
notifyConnected: function() {
presenterControlChannel.terminate('testPresentationId');
},
notifyDisconnected: function(aReason) {
Assert.equal(aReason, Cr.NS_ERROR_FAILURE, '4. presenterControlChannel notify disconnected with error');
yayFuncs.presenterControlChannelDisconnected();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
}
function setOffline() {
pcs.listener = {
onPortChange: function(aPort) {
@ -225,6 +394,8 @@ function changeCloseReason() {
}
add_test(loopOfferAnser);
add_test(terminateRequest);
add_test(terminateRequestAbnormal);
add_test(setOffline);
add_test(changeCloseReason);
add_test(oneMoreLoop);