diff --git a/dom/presentation/PresentationDeviceManager.cpp b/dom/presentation/PresentationDeviceManager.cpp index 2e4735e619c9..2c2b2c8298f6 100644 --- a/dom/presentation/PresentationDeviceManager.cpp +++ b/dom/presentation/PresentationDeviceManager.cpp @@ -262,6 +262,27 @@ PresentationDeviceManager::OnTerminateRequest(nsIPresentationDevice* aDevice, return NS_OK; } +NS_IMETHODIMP +PresentationDeviceManager::OnReconnectRequest(nsIPresentationDevice* aDevice, + const nsAString& aUrl, + const nsAString& aPresentationId, + nsIPresentationControlChannel* aControlChannel) +{ + NS_ENSURE_ARG(aDevice); + NS_ENSURE_ARG(aControlChannel); + + nsCOMPtr obs = services::GetObserverService(); + NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); + + RefPtr request = + new PresentationSessionRequest(aDevice, aUrl, aPresentationId, aControlChannel); + obs->NotifyObservers(request, + PRESENTATION_RECONNECT_REQUEST_TOPIC, + nullptr); + + return NS_OK; +} + // nsIObserver NS_IMETHODIMP PresentationDeviceManager::Observe(nsISupports *aSubject, diff --git a/dom/presentation/interfaces/nsIPresentationControlChannel.idl b/dom/presentation/interfaces/nsIPresentationControlChannel.idl index ddc715dff003..669e4088ee78 100644 --- a/dom/presentation/interfaces/nsIPresentationControlChannel.idl +++ b/dom/presentation/interfaces/nsIPresentationControlChannel.idl @@ -64,6 +64,11 @@ interface nsIPresentationControlChannelListener: nsISupports * @param reason The reason of channel close, NS_OK represents normal close. */ void notifyDisconnected(in nsresult reason); + + /* + * The callback for notifying the reconnect command is acknowledged. + */ + void notifyReconnected(); }; /* @@ -122,4 +127,13 @@ interface nsIPresentationControlChannel: nsISupports * @param reason The reason of disconnecting channel; NS_OK represents normal. */ void disconnect(in nsresult reason); + + /* + * Reconnect a presentation on remote endpoint. + * Note that only controller is allowed to reconnect a session. + * @param presentationId The Id for representing this session. + * @param url The URL requested to open by remote device. + * @throws NS_ERROR_FAILURE on failure + */ + void reconnect(in DOMString presentationId, in DOMString url); }; diff --git a/dom/presentation/interfaces/nsIPresentationControlService.idl b/dom/presentation/interfaces/nsIPresentationControlService.idl index e056633de9a0..4fe988a3d524 100644 --- a/dom/presentation/interfaces/nsIPresentationControlService.idl +++ b/dom/presentation/interfaces/nsIPresentationControlService.idl @@ -57,6 +57,18 @@ interface nsIPresentationControlServerListener: nsISupports in DOMString aPresentationId, in nsIPresentationControlChannel aControlChannel, in boolean aIsFromReceiver); + + /** + * Callback while the remote host is requesting to reconnect a presentation session. + * @param aDeviceInfo The device information related to the remote host. + * @param aUrl The URL requested to open by remote device. + * @param aPresentationId The Id for representing this session. + * @param aControlChannel The control channel for this session. + */ + void onReconnectRequest(in nsITCPDeviceInfo aDeviceInfo, + in DOMString url, + in DOMString aPresentationId, + in nsIPresentationControlChannel aControlChannel); }; /** diff --git a/dom/presentation/interfaces/nsIPresentationDeviceProvider.idl b/dom/presentation/interfaces/nsIPresentationDeviceProvider.idl index 3002b03398a1..b2c5e530cd0c 100644 --- a/dom/presentation/interfaces/nsIPresentationDeviceProvider.idl +++ b/dom/presentation/interfaces/nsIPresentationDeviceProvider.idl @@ -44,6 +44,18 @@ interface nsIPresentationDeviceListener: nsISupports in DOMString presentationId, in nsIPresentationControlChannel controlChannel, in boolean aIsFromReceiver); + + /* + * Callback while the remote device is requesting to reconnect a presentation session. + * @param device The remote device that sent session request. + * @param aUrl The URL requested to open by remote device. + * @param presentationId The Id for representing this session. + * @param controlChannel The control channel for this session. + */ + void onReconnectRequest(in nsIPresentationDevice device, + in DOMString url, + in DOMString presentationId, + in nsIPresentationControlChannel controlChannel); }; /* diff --git a/dom/presentation/interfaces/nsIPresentationSessionRequest.idl b/dom/presentation/interfaces/nsIPresentationSessionRequest.idl index 36f323a6c3f2..473b260a1af8 100644 --- a/dom/presentation/interfaces/nsIPresentationSessionRequest.idl +++ b/dom/presentation/interfaces/nsIPresentationSessionRequest.idl @@ -12,8 +12,10 @@ interface nsIPresentationControlChannel; %} /* - * The event of a device requesting for a presentation session. User can - * monitor the session request on every device by observing "presentation-sesion-request". + * The event of a device requesting for starting or reconnecting + * a presentation session. User can monitor the session request + * on every device by observing "presentation-sesion-request" for a + * new session and "presentation-reconnect-request" for reconnecting. */ [scriptable, uuid(d808a084-d0f8-455a-a8df-5879e05a755b)] interface nsIPresentationSessionRequest: nsISupports diff --git a/dom/presentation/provider/ControllerStateMachine.jsm b/dom/presentation/provider/ControllerStateMachine.jsm index 5db792db3b52..e4b6f767222a 100644 --- a/dom/presentation/provider/ControllerStateMachine.jsm +++ b/dom/presentation/provider/ControllerStateMachine.jsm @@ -60,6 +60,9 @@ var handlers = [ case CommandType.ICE_CANDIDATE: stateMachine._notifyChannelDescriptor(command); break; + case CommandType.RECONNECT_ACK: + stateMachine._notifyReconnect(command.presentationId); + break; default: debug("unexpected command: " + JSON.stringify(command)); // ignore unexpected command. @@ -111,6 +114,16 @@ ControllerStateMachine.prototype = { } }, + reconnect: function _reconnect(presentationId, url) { + if (this.state === State.CONNECTED) { + this._sendCommand({ + type: CommandType.RECONNECT, + presentationId: presentationId, + url: url, + }); + } + }, + sendOffer: function _sendOffer(offer) { if (this.state === State.CONNECTED) { this._sendCommand({ @@ -200,6 +213,10 @@ ControllerStateMachine.prototype = { this._channel.notifyTerminate(presentationId); }, + _notifyReconnect: function _notifyReconnect(presentationId) { + this._channel.notifyReconnect(presentationId); + }, + _notifyChannelDescriptor: function _notifyChannelDescriptor(command) { switch (command.type) { case CommandType.ANSWER: diff --git a/dom/presentation/provider/DisplayDeviceProvider.cpp b/dom/presentation/provider/DisplayDeviceProvider.cpp index d2756bec92be..695c041f37ab 100644 --- a/dom/presentation/provider/DisplayDeviceProvider.cpp +++ b/dom/presentation/provider/DisplayDeviceProvider.cpp @@ -436,6 +436,37 @@ DisplayDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo, return NS_OK; } +NS_IMETHODIMP +DisplayDeviceProvider::OnReconnectRequest(nsITCPDeviceInfo* aDeviceInfo, + const nsAString& aUrl, + const nsAString& aPresentationId, + nsIPresentationControlChannel* aControlChannel) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aDeviceInfo); + MOZ_ASSERT(aControlChannel); + + nsresult rv; + + nsCOMPtr listener; + rv = GetListener(getter_AddRefs(listener)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(!listener); + + rv = listener->OnReconnectRequest(mDevice, + aUrl, + aPresentationId, + aControlChannel); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + // nsIObserver NS_IMETHODIMP DisplayDeviceProvider::Observe(nsISupports* aSubject, diff --git a/dom/presentation/provider/LegacyPresentationControlService.js b/dom/presentation/provider/LegacyPresentationControlService.js index 41c84e090eeb..b27177b635bc 100644 --- a/dom/presentation/provider/LegacyPresentationControlService.js +++ b/dom/presentation/provider/LegacyPresentationControlService.js @@ -474,6 +474,12 @@ LegacyTCPControlChannel.prototype = { } }, + reconnect: function() { + // Legacy protocol doesn't support extra reconnect protocol. + // Trigger error handling for browser to shutdown all the resource locally. + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + classID: Components.ID("{4027ce3d-06e3-4d06-a235-df329cb0d411}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel, Ci.nsIStreamListener]), diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp index e878b57b86ff..a47afe99ffef 100644 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp +++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp @@ -873,63 +873,13 @@ MulticastDNSDeviceProvider::OnPortChange(uint16_t aPort) return NS_OK; } -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) +// Create a new device if we were unable to find one with the address. +already_AddRefed +MulticastDNSDeviceProvider::GetOrCreateDevice(nsITCPDeviceInfo* aDeviceInfo) { - MOZ_ASSERT(NS_IsMainThread()); - nsAutoCString address; Unused << aDeviceInfo->GetAddress(address); - LOG_I("OnSessionRequest: %s", address.get()); - - RefPtr 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 listed in available device list 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 listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->OnSessionRequest(device, aUrl, aPresentationId, - aControlChannel); - } - - 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; uint32_t index; if (FindDeviceByAddress(address, index)) { @@ -952,6 +902,46 @@ MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo, /* aProvider = */ nullptr); } + return device.forget(); +} + +NS_IMETHODIMP +MulticastDNSDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo, + const nsAString& aUrl, + const nsAString& aPresentationId, + nsIPresentationControlChannel* aControlChannel) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoCString address; + Unused << aDeviceInfo->GetAddress(address); + + LOG_I("OnSessionRequest: %s", address.get()); + + RefPtr device = GetOrCreateDevice(aDeviceInfo); + nsCOMPtr listener; + if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { + Unused << listener->OnSessionRequest(device, aUrl, aPresentationId, + aControlChannel); + } + + 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 = GetOrCreateDevice(aDeviceInfo); nsCOMPtr listener; if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { Unused << listener->OnTerminateRequest(device, aPresentationId, @@ -961,6 +951,29 @@ MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo, return NS_OK; } +NS_IMETHODIMP +MulticastDNSDeviceProvider::OnReconnectRequest(nsITCPDeviceInfo* aDeviceInfo, + const nsAString& aUrl, + const nsAString& aPresentationId, + nsIPresentationControlChannel* aControlChannel) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoCString address; + Unused << aDeviceInfo->GetAddress(address); + + LOG_I("OnReconnectRequest: %s", address.get()); + + RefPtr device = GetOrCreateDevice(aDeviceInfo); + nsCOMPtr listener; + if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { + Unused << listener->OnReconnectRequest(device, aUrl, aPresentationId, + aControlChannel); + } + + return NS_OK; +} + // nsIObserver NS_IMETHODIMP MulticastDNSDeviceProvider::Observe(nsISupports* aSubject, diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.h b/dom/presentation/provider/MulticastDNSDeviceProvider.h index ab5b988559ed..22d0bd2a3819 100644 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.h +++ b/dom/presentation/provider/MulticastDNSDeviceProvider.h @@ -19,6 +19,8 @@ #include "nsTArray.h" #include "nsWeakPtr.h" +class nsITCPDeviceInfo; + namespace mozilla { namespace dom { namespace presentation { @@ -162,6 +164,9 @@ private: bool FindDeviceByAddress(const nsACString& aAddress, uint32_t& aIndex); + already_AddRefed + GetOrCreateDevice(nsITCPDeviceInfo* aDeviceInfo); + void MarkAllDevicesUnknown(); void ClearUnknownDevices(); void ClearDevices(); diff --git a/dom/presentation/provider/PresentationControlService.js b/dom/presentation/provider/PresentationControlService.js index a41b58ed914d..069c84df0061 100644 --- a/dom/presentation/provider/PresentationControlService.js +++ b/dom/presentation/provider/PresentationControlService.js @@ -193,6 +193,21 @@ PresentationControlService.prototype = { this.releaseControlChannel(aControlChannel); }, + onSessionReconnect: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) { + DEBUG && log("TCPPresentationServer - onSessionReconnect: " + + aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line + if (!this.listener) { + this.releaseControlChannel(aControlChannel); + return; + } + + this.listener.onReconnectRequest(aDeviceInfo, + aUrl, + aPresentationId, + aControlChannel); + this.releaseControlChannel(aControlChannel); + }, + // nsIServerSocketListener (Triggered by nsIServerSocket.init) onSocketAccepted: function(aServerSocket, aClientSocket) { DEBUG && log("PresentationControlService - onSocketAccepted: " + @@ -393,6 +408,7 @@ TCPControlChannel.prototype = { _pendingAnswer: null, _pendingClose: null, _pendingCloseReason: null, + _pendingReconnect: false, sendOffer: function(aOffer) { this._stateMachine.sendOffer(discriptionAsJson(aOffer)); @@ -555,6 +571,12 @@ TCPControlChannel.prototype = { this._notifyDisconnected(this._pendingCloseReason); this._pendingClose = null; } + + if (this._pendingReconnect) { + DEBUG && log("TCPControlChannel - notify pending reconnected"); // jshint ignore:line + this._notifyReconnected(); + this._pendingReconnect = false; + } }, /** @@ -624,6 +646,17 @@ TCPControlChannel.prototype = { this._listener.notifyDisconnected(aReason); }, + _notifyReconnected: function() { + if (!this._listener) { + this._pendingReconnect = true; + return; + } + + DEBUG && log("TCPControlChannel - notify reconnected with role: " + + this._direction); // jshint ignore:line + this._listener.notifyReconnected(); + }, + _closeTransport: function() { if (this._connected) { this._transport.setEventSink(null, null); @@ -649,6 +682,16 @@ TCPControlChannel.prototype = { } }, + reconnect: function(aPresentationId, aUrl) { + DEBUG && log("TCPControlChannel - reconnect with role: " + + this._direction); // jshint ignore:line + if (this._direction != "sender") { + return Cr.NS_ERROR_FAILURE; + } + + this._stateMachine.reconnect(aPresentationId, aUrl); + }, + // callback from state machine sendCommand: function(command) { this._send(command); @@ -684,9 +727,9 @@ TCPControlChannel.prototype = { if (!this._terminatingId) { this._terminatingId = presentationId; this._presentationService.onSessionTerminate(this._deviceInfo, - presentationId, - this, - this._direction === "sender"); + presentationId, + this, + this._direction === "sender"); return; } @@ -700,6 +743,20 @@ TCPControlChannel.prototype = { delete this._terminatingId; }, + notifyReconnect: function(presentationId, url) { + switch (this._direction) { + case "receiver": + this._presentationService.onSessionReconnect(this._deviceInfo, + url, + presentationId, + this); + break; + case "sender": + this._notifyReconnected(); + break; + } + }, + notifyOffer: function(offer) { this._onOffer(offer); }, diff --git a/dom/presentation/provider/ReceiverStateMachine.jsm b/dom/presentation/provider/ReceiverStateMachine.jsm index cb6ffddfc935..5e6fe3756829 100644 --- a/dom/presentation/provider/ReceiverStateMachine.jsm +++ b/dom/presentation/provider/ReceiverStateMachine.jsm @@ -68,6 +68,14 @@ var handlers = [ case CommandType.ICE_CANDIDATE: stateMachine._notifyChannelDescriptor(command); break; + case CommandType.RECONNECT: + stateMachine._notifyReconnect(command.presentationId, + command.url); + stateMachine._sendCommand({ + type: CommandType.RECONNECT_ACK, + presentationId: command.presentationId + }); + break; default: debug("unexpected command: " + JSON.stringify(command)); // ignore unexpected command @@ -113,6 +121,10 @@ ReceiverStateMachine.prototype = { } }, + reconnect: function _reconnect() { + debug("receiver shouldn't trigger reconnect"); + }, + sendOffer: function _sendOffer() { // offer can only be sent by controlling UA. debug("receiver shouldn't generate offer"); @@ -199,6 +211,10 @@ ReceiverStateMachine.prototype = { this._channel.notifyTerminate(presentationId); }, + _notifyReconnect: function _notifyReconnect(presentationId, url) { + this._channel.notifyReconnect(presentationId, url); + }, + _notifyChannelDescriptor: function _notifyChannelDescriptor(command) { switch (command.type) { case CommandType.OFFER: diff --git a/dom/presentation/provider/StateMachineHelper.jsm b/dom/presentation/provider/StateMachineHelper.jsm index f35868d6ce78..6e07863d4173 100644 --- a/dom/presentation/provider/StateMachineHelper.jsm +++ b/dom/presentation/provider/StateMachineHelper.jsm @@ -27,6 +27,8 @@ const CommandType = Object.freeze({ LAUNCH_ACK: "launch-ack", // { presentationId: } TERMINATE: "terminate", // { presentationId: } TERMINATE_ACK: "terminate-ack", // { presentationId: } + RECONNECT: "reconnect", // { presentationId: } + RECONNECT_ACK: "reconnect-ack", // { presentationId: } // session transport establishment OFFER: "offer", // { offer: } ANSWER: "answer", // { answer: } diff --git a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js b/dom/presentation/tests/xpcshell/test_presentation_device_manager.js index 948b392b2f35..95067658b9a5 100644 --- a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js +++ b/dom/presentation/tests/xpcshell/test_presentation_device_manager.js @@ -24,6 +24,7 @@ TestPresentationControlChannel.prototype = { disconnect: function() {}, launch: function() {}, terminate: function() {}, + reconnect: function() {}, set listener(listener) {}, get listener() {}, }; @@ -161,6 +162,25 @@ function terminateRequest() { testControlChannel, testIsFromReceiver); } +function reconnectRequest() { + let testUrl = 'http://www.example.org/'; + let testPresentationId = 'test-presentation-id'; + let testControlChannel = new TestPresentationControlChannel(); + Services.obs.addObserver(function observer(subject, topic, data) { + Services.obs.removeObserver(observer, topic); + + let request = subject.QueryInterface(Ci.nsIPresentationSessionRequest); + + Assert.equal(request.device.id, testDevice.id, 'expected device'); + Assert.equal(request.url, testUrl, 'expected requesting URL'); + Assert.equal(request.presentationId, testPresentationId, 'expected presentation Id'); + + run_next_test(); + }, 'presentation-reconnect-request', false); + manager.QueryInterface(Ci.nsIPresentationDeviceListener) + .onReconnectRequest(testDevice, testUrl, testPresentationId, testControlChannel); +} + function removeDevice() { Services.obs.addObserver(function observer(subject, topic, data) { Services.obs.removeObserver(observer, topic); @@ -199,6 +219,7 @@ add_test(addDevice); add_test(updateDevice); add_test(sessionRequest); add_test(terminateRequest); +add_test(reconnectRequest); add_test(removeDevice); add_test(removeProvider); diff --git a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js index cc014b52e4b3..1467b2bffa14 100644 --- a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js +++ b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js @@ -67,22 +67,24 @@ function loopOfferAnser() { function testPresentationServer() { let yayFuncs = makeJointSuccess(['controllerControlChannelClose', - 'presenterControlChannelClose']); - let controllerControlChannel; + 'presenterControlChannelClose', + 'controllerControlChannelReconnect', + 'presenterControlChannelReconnect']); + let presenterControlChannel; pcs.listener = { onSessionRequest: function(deviceInfo, url, presentationId, controlChannel) { - controllerControlChannel = controlChannel; + presenterControlChannel = controlChannel; Assert.equal(deviceInfo.id, pcs.id, 'expected device id'); Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address'); Assert.equal(url, 'http://example.com', 'expected url'); Assert.equal(presentationId, 'testPresentationId', 'expected presentation id'); - controllerControlChannel.listener = { + presenterControlChannel.listener = { status: 'created', onOffer: function(aOffer) { - Assert.equal(this.status, 'opened', '1. controllerControlChannel: get offer, send answer'); + Assert.equal(this.status, 'opened', '1. presenterControlChannel: get offer, send answer'); this.status = 'onOffer'; let offer = aOffer.QueryInterface(Ci.nsIPresentationChannelDescription); @@ -93,7 +95,7 @@ function testPresentationServer() { try { let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP; let answer = new TestDescription(tcpType, [ANSWER_ADDRESS], ANSWER_PORT); - controllerControlChannel.sendAnswer(answer); + presenterControlChannel.sendAnswer(answer); } catch (e) { Assert.ok(false, 'sending answer fails' + e); } @@ -102,28 +104,33 @@ function testPresentationServer() { Assert.ok(false, 'get answer'); }, onIceCandidate: function(aCandidate) { - Assert.ok(true, '3. controllerControlChannel: get ice candidate, close channel'); + Assert.ok(true, '3. presenterControlChannel: get ice candidate, close channel'); let recvCandidate = JSON.parse(aCandidate); for (let key in recvCandidate) { if (typeof(recvCandidate[key]) !== "function") { Assert.equal(recvCandidate[key], candidate[key], "key " + key + " should match."); } } - controllerControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON); + presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON); }, notifyConnected: function() { - Assert.equal(this.status, 'created', '0. controllerControlChannel: opened'); + Assert.equal(this.status, 'created', '0. presenterControlChannel: opened'); this.status = 'opened'; }, notifyDisconnected: function(aReason) { - Assert.equal(this.status, 'onOffer', '4. controllerControlChannel: closed'); - Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'controllerControlChannel notify closed'); + Assert.equal(this.status, 'onOffer', '4. presenterControlChannel: closed'); + Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'presenterControlChannel notify closed'); this.status = 'closed'; yayFuncs.controllerControlChannelClose(); }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), }; }, + onReconnectRequest: function(deviceInfo, url, presentationId, controlChannel) { + Assert.equal(url, 'http://example.com', 'expected url'); + Assert.equal(presentationId, 'testPresentationId', 'expected presentation id'); + yayFuncs.presenterControlChannelReconnect(); + }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlServerListener]), }; @@ -135,15 +142,15 @@ function testPresentationServer() { QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), }; - let presenterControlChannel = pcs.connect(presenterDeviceInfo); + let controllerControlChannel = pcs.connect(presenterDeviceInfo); - presenterControlChannel.listener = { + controllerControlChannel.listener = { status: 'created', onOffer: function(offer) { Assert.ok(false, 'get offer'); }, onAnswer: function(aAnswer) { - Assert.equal(this.status, 'opened', '2. presenterControlChannel: get answer, send ICE candidate'); + Assert.equal(this.status, 'opened', '2. controllerControlChannel: get answer, send ICE candidate'); let answer = aAnswer.QueryInterface(Ci.nsIPresentationChannelDescription); Assert.strictEqual(answer.tcpAddress.queryElementAt(0,Ci.nsISupportsCString).data, @@ -155,27 +162,38 @@ function testPresentationServer() { sdpMid: "helloworld", sdpMLineIndex: 1 }; - presenterControlChannel.sendIceCandidate(JSON.stringify(candidate)); + controllerControlChannel.sendIceCandidate(JSON.stringify(candidate)); }, onIceCandidate: function(aCandidate) { Assert.ok(false, 'get ICE candidate'); }, notifyConnected: function() { - Assert.equal(this.status, 'created', '0. presenterControlChannel: opened, send offer'); - presenterControlChannel.launch('testPresentationId', 'http://example.com'); + Assert.equal(this.status, 'created', '0. controllerControlChannel: opened, send offer'); + controllerControlChannel.launch('testPresentationId', 'http://example.com'); this.status = 'opened'; try { let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP; let offer = new TestDescription(tcpType, [OFFER_ADDRESS], OFFER_PORT) - presenterControlChannel.sendOffer(offer); + controllerControlChannel.sendOffer(offer); } catch (e) { Assert.ok(false, 'sending offer fails:' + e); } }, notifyDisconnected: function(aReason) { this.status = 'closed'; - Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. presenterControlChannel notify closed'); + Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. controllerControlChannel notify closed'); yayFuncs.presenterControlChannelClose(); + + let reconnectControllerControlChannel = pcs.connect(presenterDeviceInfo); + reconnectControllerControlChannel.listener = { + notifyConnected: function() { + reconnectControllerControlChannel.reconnect('testPresentationId', 'http://example.com'); + }, + notifyReconnected: function() { + yayFuncs.controllerControlChannelReconnect(); + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), + }; }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), };