diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/presentation/tests/mochitest | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/presentation/tests/mochitest')
59 files changed, 7770 insertions, 0 deletions
diff --git a/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js b/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js new file mode 100644 index 000000000..2bc069f6b --- /dev/null +++ b/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js @@ -0,0 +1,150 @@ +/* -*- 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 } = Components; + +Cu.import('resource://gre/modules/PresentationDeviceInfoManager.jsm'); + +const { XPCOMUtils } = Cu.import('resource://gre/modules/XPCOMUtils.jsm'); + +const manager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + +var testProvider = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceProvider]), + forceDiscovery: function() { + sendAsyncMessage('force-discovery'); + }, + listener: null, +}; + +var testDevice = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), + establishControlChannel: function() { + return null; + }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, + id: null, + name: null, + type: null, + listener: null, +}; + +var testDevice1 = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), + id: 'dummyid', + name: 'dummyName', + type: 'dummyType', + establishControlChannel: function(url, presentationId) { + return null; + }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, +}; + +var testDevice2 = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), + id: 'dummyid', + name: 'dummyName', + type: 'dummyType', + establishControlChannel: function(url, presentationId) { + return null; + }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, +}; + +var mockedDeviceWithoutSupportedURL = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), + id: 'dummyid', + name: 'dummyName', + type: 'dummyType', + establishControlChannel: function(url, presentationId) { + return null; + }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return false; + }, +}; + +var mockedDeviceSupportHttpsURL = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), + id: 'dummyid', + name: 'dummyName', + type: 'dummyType', + establishControlChannel: function(url, presentationId) { + return null; + }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + if (requestedUrl.indexOf("https://") != -1) { + return true; + } + return false; + }, +}; + +addMessageListener('setup', function() { + manager.addDeviceProvider(testProvider); + + sendAsyncMessage('setup-complete'); +}); + +addMessageListener('trigger-device-add', function(device) { + testDevice.id = device.id; + testDevice.name = device.name; + testDevice.type = device.type; + manager.addDevice(testDevice); +}); + +addMessageListener('trigger-add-unsupport-url-device', function() { + manager.addDevice(mockedDeviceWithoutSupportedURL); +}); + +addMessageListener('trigger-add-multiple-devices', function() { + manager.addDevice(testDevice1); + manager.addDevice(testDevice2); +}); + +addMessageListener('trigger-add-https-devices', function() { + manager.addDevice(mockedDeviceSupportHttpsURL); +}); + + +addMessageListener('trigger-device-update', function(device) { + testDevice.id = device.id; + testDevice.name = device.name; + testDevice.type = device.type; + manager.updateDevice(testDevice); +}); + +addMessageListener('trigger-device-remove', function() { + manager.removeDevice(testDevice); +}); + +addMessageListener('trigger-remove-unsupported-device', function() { + manager.removeDevice(mockedDeviceWithoutSupportedURL); +}); + +addMessageListener('trigger-remove-multiple-devices', function() { + manager.removeDevice(testDevice1); + manager.removeDevice(testDevice2); +}); + +addMessageListener('trigger-remove-https-devices', function() { + manager.removeDevice(mockedDeviceSupportHttpsURL); +}); + +addMessageListener('teardown', function() { + manager.removeDeviceProvider(testProvider); +}); diff --git a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js new file mode 100644 index 000000000..3052bdcb1 --- /dev/null +++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js @@ -0,0 +1,470 @@ +/* -*- 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, manager: Cm, utils: Cu, results: Cr } = Components; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); +Cu.import('resource://gre/modules/Timer.jsm'); + +const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator); + +function registerMockedFactory(contractId, mockedClassId, mockedFactory) { + var originalClassId, originalFactory; + + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + if (!registrar.isCIDRegistered(mockedClassId)) { + try { + originalClassId = registrar.contractIDToCID(contractId); + originalFactory = Cm.getClassObject(Cc[contractId], Ci.nsIFactory); + } catch (ex) { + originalClassId = ""; + originalFactory = null; + } + if (originalFactory) { + registrar.unregisterFactory(originalClassId, originalFactory); + } + registrar.registerFactory(mockedClassId, "", contractId, mockedFactory); + } + + return { contractId: contractId, + mockedClassId: mockedClassId, + mockedFactory: mockedFactory, + originalClassId: originalClassId, + originalFactory: originalFactory }; +} + +function registerOriginalFactory(contractId, mockedClassId, mockedFactory, originalClassId, originalFactory) { + if (originalFactory) { + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + registrar.unregisterFactory(mockedClassId, mockedFactory); + registrar.registerFactory(originalClassId, "", contractId, originalFactory); + } +} + +var sessionId = 'test-session-id-' + uuidGenerator.generateUUID().toString(); + +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 mockedChannelDescription = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]), + get type() { + if (Services.prefs.getBoolPref("dom.presentation.session_transport.data_channel.enable")) { + return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL; + } + return Ci.nsIPresentationChannelDescription.TYPE_TCP; + }, + tcpAddress: addresses, + tcpPort: 1234, +}; + +const mockedServerSocket = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIServerSocket, + Ci.nsIFactory]), + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + get port() { + return this._port; + }, + set listener(listener) { + this._listener = listener; + }, + init: function(port, loopbackOnly, backLog) { + if (port != -1) { + this._port = port; + } else { + this._port = 5678; + } + }, + asyncListen: function(listener) { + this._listener = listener; + }, + close: function() { + this._listener.onStopListening(this, Cr.NS_BINDING_ABORTED); + }, + simulateOnSocketAccepted: function(serverSocket, socketTransport) { + this._listener.onSocketAccepted(serverSocket, socketTransport); + } +}; + +const mockedSocketTransport = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsISocketTransport]), +}; + +const mockedControlChannel = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), + set listener(listener) { + this._listener = listener; + }, + get listener() { + return this._listener; + }, + sendOffer: function(offer) { + sendAsyncMessage('offer-sent', this._isValidSDP(offer)); + }, + sendAnswer: function(answer) { + sendAsyncMessage('answer-sent', this._isValidSDP(answer)); + + if (answer.type == Ci.nsIPresentationChannelDescription.TYPE_TCP) { + this._listener.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady(); + } + }, + _isValidSDP: function(aSDP) { + var isValid = false; + if (aSDP.type == Ci.nsIPresentationChannelDescription.TYPE_TCP) { + try { + var addresses = aSDP.tcpAddress; + if (addresses.length > 0) { + for (var i = 0; i < addresses.length; i++) { + // Ensure CString addresses are used. Otherwise, an error will be thrown. + addresses.queryElementAt(i, Ci.nsISupportsCString); + } + + isValid = true; + } + } catch (e) { + isValid = false; + } + } else if (aSDP.type == Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL) { + isValid = (aSDP.dataChannelSDP == "test-sdp"); + } + return isValid; + }, + launch: function(presentationId, url) { + sessionId = presentationId; + }, + terminate: function(presentationId) { + sendAsyncMessage('sender-terminate', presentationId); + }, + reconnect: function(presentationId, url) { + sendAsyncMessage('start-reconnect', url); + }, + notifyReconnected: function() { + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .notifyReconnected(); + }, + disconnect: function(reason) { + sendAsyncMessage('control-channel-closed', reason); + this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyDisconnected(reason); + }, + simulateReceiverReady: function() { + this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyReceiverReady(); + }, + simulateOnOffer: function() { + sendAsyncMessage('offer-received'); + this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onOffer(mockedChannelDescription); + }, + simulateOnAnswer: function() { + sendAsyncMessage('answer-received'); + this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onAnswer(mockedChannelDescription); + }, + simulateNotifyConnected: function() { + sendAsyncMessage('control-channel-opened'); + this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyConnected(); + }, +}; + +const mockedDevice = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), + id: 'id', + name: 'name', + type: 'type', + establishControlChannel: function(url, presentationId) { + sendAsyncMessage('control-channel-established'); + return mockedControlChannel; + }, + disconnect: function() {}, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, +}; + +const mockedDevicePrompt = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt, + Ci.nsIFactory]), + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + set request(request) { + this._request = request; + }, + get request() { + return this._request; + }, + promptDeviceSelection: function(request) { + this._request = request; + sendAsyncMessage('device-prompt'); + }, + simulateSelect: function() { + this._request.select(mockedDevice); + }, + simulateCancel: function(result) { + this._request.cancel(result); + } +}; + +const mockedSessionTransport = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport, + Ci.nsIPresentationSessionTransportBuilder, + Ci.nsIPresentationTCPSessionTransportBuilder, + Ci.nsIPresentationDataChannelSessionTransportBuilder, + Ci.nsIPresentationControlChannelListener, + Ci.nsIFactory]), + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + set callback(callback) { + this._callback = callback; + }, + get callback() { + return this._callback; + }, + get selfAddress() { + return this._selfAddress; + }, + buildTCPSenderTransport: function(transport, listener) { + this._listener = listener; + this._role = Ci.nsIPresentationService.ROLE_CONTROLLER; + this._listener.onSessionTransport(this); + this._listener = null; + sendAsyncMessage('data-transport-initialized'); + + setTimeout(()=>{ + this.simulateTransportReady(); + }, 0); + }, + buildTCPReceiverTransport: function(description, listener) { + this._listener = listener; + this._role = Ci.nsIPresentationService.ROLE_RECEIVER; + + var addresses = description.QueryInterface(Ci.nsIPresentationChannelDescription).tcpAddress; + this._selfAddress = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetAddr]), + address: (addresses.length > 0) ? + addresses.queryElementAt(0, Ci.nsISupportsCString).data : "", + port: description.QueryInterface(Ci.nsIPresentationChannelDescription).tcpPort, + }; + + setTimeout(()=>{ + this._listener.onSessionTransport(this); + this._listener = null; + }, 0); + }, + // in-process case + buildDataChannelTransport: function(role, window, listener) { + this._listener = listener; + this._role = role; + + var hasNavigator = window ? (typeof window.navigator != "undefined") : false; + sendAsyncMessage('check-navigator', hasNavigator); + + setTimeout(()=>{ + this._listener.onSessionTransport(this); + this._listener = null; + this.simulateTransportReady(); + }, 0); + }, + enableDataNotification: function() { + sendAsyncMessage('data-transport-notification-enabled'); + }, + send: function(data) { + sendAsyncMessage('message-sent', data); + }, + close: function(reason) { + sendAsyncMessage('data-transport-closed', reason); + this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportClosed(reason); + }, + simulateTransportReady: function() { + this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady(); + }, + simulateIncomingMessage: function(message) { + this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message, false); + }, + onOffer: function(aOffer) { + }, + onAnswer: function(aAnswer) { + } +}; + +const mockedNetworkInfo = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInfo]), + getAddresses: function(ips, prefixLengths) { + ips.value = ["127.0.0.1"]; + prefixLengths.value = [0]; + return 1; + }, +}; + +const mockedNetworkManager = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkManager, + Ci.nsIFactory]), + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + get activeNetworkInfo() { + return mockedNetworkInfo; + }, +}; + +var requestPromise = null; + +const mockedRequestUIGlue = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue, + Ci.nsIFactory]), + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + sendRequest: function(aUrl, aSessionId) { + sendAsyncMessage('receiver-launching', aSessionId); + return requestPromise; + }, +}; + +// Register mocked factories. +const originalFactoryData = []; +originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation-device/prompt;1", + uuidGenerator.generateUUID(), + mockedDevicePrompt)); +originalFactoryData.push(registerMockedFactory("@mozilla.org/network/server-socket;1", + uuidGenerator.generateUUID(), + mockedServerSocket)); +originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/presentationtcpsessiontransport;1", + uuidGenerator.generateUUID(), + mockedSessionTransport)); +originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/datachanneltransportbuilder;1", + uuidGenerator.generateUUID(), + mockedSessionTransport)); +originalFactoryData.push(registerMockedFactory("@mozilla.org/network/manager;1", + uuidGenerator.generateUUID(), + mockedNetworkManager)); +originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/requestuiglue;1", + uuidGenerator.generateUUID(), + mockedRequestUIGlue)); + +function tearDown() { + requestPromise = null; + mockedServerSocket.listener = null; + mockedControlChannel.listener = null; + mockedDevice.listener = null; + mockedDevicePrompt.request = null; + mockedSessionTransport.callback = null; + + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener).removeDevice(mockedDevice); + + // Register original factories. + for (var data of originalFactoryData) { + registerOriginalFactory(data.contractId, data.mockedClassId, + data.mockedFactory, data.originalClassId, + data.originalFactory); + } + + sendAsyncMessage('teardown-complete'); +} + +addMessageListener('trigger-device-add', function() { + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener).addDevice(mockedDevice); +}); + +addMessageListener('trigger-device-prompt-select', function() { + mockedDevicePrompt.simulateSelect(); +}); + +addMessageListener('trigger-device-prompt-cancel', function(result) { + mockedDevicePrompt.simulateCancel(result); +}); + +addMessageListener('trigger-incoming-session-request', function(url) { + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) + .onSessionRequest(mockedDevice, url, sessionId, mockedControlChannel); +}); + +addMessageListener('trigger-incoming-terminate-request', function() { + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) + .onTerminateRequest(mockedDevice, sessionId, mockedControlChannel, true); +}); + +addMessageListener('trigger-reconnected-acked', function(url) { + mockedControlChannel.notifyReconnected(); +}); + +addMessageListener('trigger-incoming-offer', function() { + mockedControlChannel.simulateOnOffer(); +}); + +addMessageListener('trigger-incoming-answer', function() { + mockedControlChannel.simulateOnAnswer(); +}); + +addMessageListener('trigger-incoming-transport', function() { + mockedServerSocket.simulateOnSocketAccepted(mockedServerSocket, mockedSocketTransport); +}); + +addMessageListener('trigger-control-channel-open', function(reason) { + mockedControlChannel.simulateNotifyConnected(); +}); + +addMessageListener('trigger-control-channel-close', function(reason) { + mockedControlChannel.disconnect(reason); +}); + +addMessageListener('trigger-data-transport-close', function(reason) { + mockedSessionTransport.close(reason); +}); + +addMessageListener('trigger-incoming-message', function(message) { + mockedSessionTransport.simulateIncomingMessage(message); +}); + +addMessageListener('teardown', function() { + tearDown(); +}); + +var controlChannelListener; +addMessageListener('save-control-channel-listener', function() { + controlChannelListener = mockedControlChannel.listener; +}); + +addMessageListener('restore-control-channel-listener', function(message) { + mockedControlChannel.listener = controlChannelListener; + controlChannelListener = null; +}); + +var obs = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); +obs.addObserver(function observer(aSubject, aTopic, aData) { + obs.removeObserver(observer, aTopic); + + requestPromise = aSubject; +}, 'setup-request-promise', false); diff --git a/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js b/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js new file mode 100644 index 000000000..82d7362b2 --- /dev/null +++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js @@ -0,0 +1,366 @@ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* 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/. */ + +'use strict'; + +const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); + +const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator); + +function debug(str) { + // dump('DEBUG -*- PresentationSessionChromeScript1UA -*-: ' + str + '\n'); +} + +const originalFactoryData = []; +var sessionId; // Store the uuid generated by PresentationRequest. +var triggerControlChannelError = false; // For simulating error during control channel establishment. + +// control channel of sender +const mockControlChannelOfSender = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), + set listener(listener) { + // PresentationControllingInfo::SetControlChannel + if (listener) { + debug('set listener for mockControlChannelOfSender without null'); + } else { + debug('set listener for mockControlChannelOfSender with null'); + } + this._listener = listener; + }, + get listener() { + return this._listener; + }, + notifyConnected: function() { + // send offer after notifyConnected immediately + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .notifyConnected(); + }, + notifyReconnected: function() { + // send offer after notifyOpened immediately + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .notifyReconnected(); + }, + sendOffer: function(offer) { + Services.tm.mainThread.dispatch(() => { + mockControlChannelOfReceiver.onOffer(offer); + }, Ci.nsIThread.DISPATCH_NORMAL); + }, + onAnswer: function(answer) { + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .onAnswer(answer); + }, + launch: function(presentationId, url) { + sessionId = presentationId; + sendAsyncMessage('sender-launch', url); + }, + disconnect: function(reason) { + if (!this._listener) { + return; + } + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .notifyDisconnected(reason); + mockControlChannelOfReceiver.disconnect(); + }, + terminate: function(presentationId) { + sendAsyncMessage('sender-terminate'); + }, + reconnect: function(presentationId, url) { + sendAsyncMessage('start-reconnect', url); + }, + sendIceCandidate: function(candidate) { + mockControlChannelOfReceiver.notifyIceCandidate(candidate); + }, + notifyIceCandidate: function(candidate) { + if (!this._listener) { + return; + } + + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .onIceCandidate(candidate); + }, +}; + +// control channel of receiver +const mockControlChannelOfReceiver = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), + set listener(listener) { + // PresentationPresentingInfo::SetControlChannel + if (listener) { + debug('set listener for mockControlChannelOfReceiver without null'); + } else { + debug('set listener for mockControlChannelOfReceiver with null'); + } + this._listener = listener; + + if (this._pendingOpened) { + this._pendingOpened = false; + this.notifyConnected(); + } + }, + get listener() { + return this._listener; + }, + notifyConnected: function() { + // do nothing + if (!this._listener) { + this._pendingOpened = true; + return; + } + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .notifyConnected(); + }, + onOffer: function(offer) { + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .onOffer(offer); + }, + sendAnswer: function(answer) { + Services.tm.mainThread.dispatch(() => { + mockControlChannelOfSender.onAnswer(answer); + }, Ci.nsIThread.DISPATCH_NORMAL); + }, + disconnect: function(reason) { + if (!this._listener) { + return; + } + + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .notifyDisconnected(reason); + sendAsyncMessage('control-channel-receiver-closed', reason); + }, + terminate: function(presentaionId) { + }, + sendIceCandidate: function(candidate) { + mockControlChannelOfReceiver.notifyIceCandidate(candidate); + }, + notifyIceCandidate: function(candidate) { + if (!this._listener) { + return; + } + + this._listener + .QueryInterface(Ci.nsIPresentationControlChannelListener) + .onIceCandidate(candidate); + }, +}; + +const mockDevice = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), + id: 'id', + name: 'name', + type: 'type', + establishControlChannel: function(url, presentationId) { + if (triggerControlChannelError) { + throw Cr.NS_ERROR_FAILURE; + } + sendAsyncMessage('control-channel-established'); + return mockControlChannelOfSender; + }, + disconnect: function() { + sendAsyncMessage('device-disconnected'); + }, + isRequestedUrlSupported: function(requestedUrl) { + return true; + }, +}; + +const mockDevicePrompt = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt, + Ci.nsIFactory]), + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + set request(request) { + this._request = request; + }, + get request() { + return this._request; + }, + promptDeviceSelection: function(request) { + this._request = request; + sendAsyncMessage('device-prompt'); + }, + simulateSelect: function() { + this._request.select(mockDevice); + }, + simulateCancel: function() { + this._request.cancel(); + } +}; + +const mockRequestUIGlue = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue, + Ci.nsIFactory]), + set promise(aPromise) { + this._promise = aPromise + }, + get promise() { + return this._promise; + }, + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + sendRequest: function(aUrl, aSessionId) { + return this.promise; + }, +}; + +function initMockAndListener() { + + function registerMockFactory(contractId, mockClassId, mockFactory) { + var originalClassId, originalFactory; + + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + if (!registrar.isCIDRegistered(mockClassId)) { + try { + originalClassId = registrar.contractIDToCID(contractId); + originalFactory = Cm.getClassObject(Cc[contractId], Ci.nsIFactory); + } catch (ex) { + originalClassId = ""; + originalFactory = null; + } + if (originalFactory) { + registrar.unregisterFactory(originalClassId, originalFactory); + } + registrar.registerFactory(mockClassId, "", contractId, mockFactory); + } + + return { contractId: contractId, + mockClassId: mockClassId, + mockFactory: mockFactory, + originalClassId: originalClassId, + originalFactory: originalFactory }; + } + // Register mock factories. + const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator); + originalFactoryData.push(registerMockFactory("@mozilla.org/presentation-device/prompt;1", + uuidGenerator.generateUUID(), + mockDevicePrompt)); + originalFactoryData.push(registerMockFactory("@mozilla.org/presentation/requestuiglue;1", + uuidGenerator.generateUUID(), + mockRequestUIGlue)); + + addMessageListener('trigger-device-add', function() { + debug('Got message: trigger-device-add'); + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) + .addDevice(mockDevice); + }); + + addMessageListener('trigger-device-prompt-select', function() { + debug('Got message: trigger-device-prompt-select'); + mockDevicePrompt.simulateSelect(); + }); + + addMessageListener('trigger-on-session-request', function(url) { + debug('Got message: trigger-on-session-request'); + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) + .onSessionRequest(mockDevice, + url, + sessionId, + mockControlChannelOfReceiver); + }); + + addMessageListener('trigger-on-terminate-request', function() { + debug('Got message: trigger-on-terminate-request'); + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) + .onTerminateRequest(mockDevice, + sessionId, + mockControlChannelOfReceiver, + false); + }); + + addMessageListener('trigger-control-channel-open', function(reason) { + debug('Got message: trigger-control-channel-open'); + mockControlChannelOfSender.notifyConnected(); + mockControlChannelOfReceiver.notifyConnected(); + }); + + addMessageListener('trigger-control-channel-error', function(reason) { + debug('Got message: trigger-control-channel-open'); + triggerControlChannelError = true; + }); + + addMessageListener('trigger-reconnected-acked', function(url) { + debug('Got message: trigger-reconnected-acked'); + mockControlChannelOfSender.notifyReconnected(); + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) + .onReconnectRequest(mockDevice, + url, + sessionId, + mockControlChannelOfReceiver); + }); + + // Used to call sendAsyncMessage in chrome script from receiver. + addMessageListener('forward-command', function(command_data) { + let command = JSON.parse(command_data); + sendAsyncMessage(command.name, command.data); + }); + + addMessageListener('teardown', teardown); + + var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); + obs.addObserver(function setupRequestPromiseHandler(aSubject, aTopic, aData) { + debug('Got observer: setup-request-promise'); + obs.removeObserver(setupRequestPromiseHandler, aTopic); + mockRequestUIGlue.promise = aSubject; + sendAsyncMessage('promise-setup-ready'); + }, 'setup-request-promise', false); +} + +function teardown() { + + function registerOriginalFactory(contractId, mockedClassId, mockedFactory, originalClassId, originalFactory) { + if (originalFactory) { + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + registrar.unregisterFactory(mockedClassId, mockedFactory); + registrar.registerFactory(originalClassId, "", contractId, originalFactory); + } + } + + mockRequestUIGlue.promise = null; + mockControlChannelOfSender.listener = null; + mockControlChannelOfReceiver.listener = null; + mockDevicePrompt.request = null; + + var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] + .getService(Ci.nsIPresentationDeviceManager); + deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) + .removeDevice(mockDevice); + // Register original factories. + for (var data of originalFactoryData) { + registerOriginalFactory(data.contractId, data.mockClassId, + data.mockFactory, data.originalClassId, + data.originalFactory); + } + sendAsyncMessage('teardown-complete'); +} + +initMockAndListener(); diff --git a/dom/presentation/tests/mochitest/PresentationSessionFrameScript.js b/dom/presentation/tests/mochitest/PresentationSessionFrameScript.js new file mode 100644 index 000000000..77240ab5f --- /dev/null +++ b/dom/presentation/tests/mochitest/PresentationSessionFrameScript.js @@ -0,0 +1,258 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function loadPrivilegedScriptTest() { + /** + * The script is loaded as + * (a) a privileged script in content process for dc_sender.html + * (b) a frame script in the remote iframe process for dc_receiver_oop.html + * |type port == "undefined"| indicates the script is load by + * |loadPrivilegedScript| which is the first case. + */ + function sendMessage(type, data) { + if (typeof port == "undefined") { + sendAsyncMessage(type, {'data': data}); + } else { + port.postMessage({'type': type, + 'data': data + }); + } + } + + if (typeof port != "undefined") { + /** + * When the script is loaded by |loadPrivilegedScript|, these APIs + * are exposed to this script. + */ + port.onmessage = (e) => { + var type = e.data['type']; + if (!handlers.hasOwnProperty(type)) { + return; + } + var args = [e]; + handlers[type].forEach(handler => handler.apply(null, args)); + }; + var handlers = {}; + addMessageListener = function(message, handler) { + if (handlers.hasOwnProperty(message)) { + handlers[message].push(handler); + } else { + handlers[message] = [handler]; + } + }; + removeMessageListener = function(message, handler) { + if (!handler || !handlers.hasOwnProperty(message)) { + return; + } + var index = handlers[message].indexOf(handler); + if (index != -1) { + handlers[message].splice(index, 1); + } + }; + } + + const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components; + + const mockedChannelDescription = { + QueryInterface : function (iid) { + const interfaces = [Ci.nsIPresentationChannelDescription]; + + if (!interfaces.some(v => iid.equals(v))) { + throw Cr.NS_ERROR_NO_INTERFACE; + } + return this; + }, + get type() { + if (Services.prefs.getBoolPref("dom.presentation.session_transport.data_channel.enable")) { + return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL; + } + return Ci.nsIPresentationChannelDescription.TYPE_TCP; + }, + get dataChannelSDP() { + return "test-sdp"; + } + }; + + function setTimeout(callback, delay) { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback({ notify: callback }, + delay, + Ci.nsITimer.TYPE_ONE_SHOT); + return timer; + } + + const mockedSessionTransport = { + QueryInterface : function (iid) { + const interfaces = [Ci.nsIPresentationSessionTransport, + Ci.nsIPresentationDataChannelSessionTransportBuilder, + Ci.nsIFactory]; + + if (!interfaces.some(v => iid.equals(v))) { + throw Cr.NS_ERROR_NO_INTERFACE; + } + return this; + }, + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return this.QueryInterface(aIID); + }, + set callback(callback) { + this._callback = callback; + }, + get callback() { + return this._callback; + }, + /* OOP case */ + buildDataChannelTransport: function(role, window, listener) { + dump("PresentationSessionFrameScript: build data channel transport\n"); + this._listener = listener; + this._role = role; + + var hasNavigator = window ? (typeof window.navigator != "undefined") : false; + sendMessage('check-navigator', hasNavigator); + + if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) { + this._listener.sendOffer(mockedChannelDescription); + } + }, + + enableDataNotification: function() { + sendMessage('data-transport-notification-enabled'); + }, + send: function(data) { + sendMessage('message-sent', data); + }, + close: function(reason) { + sendMessage('data-transport-closed', reason); + this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportClosed(reason); + this._callback = null; + }, + simulateTransportReady: function() { + this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady(); + }, + simulateIncomingMessage: function(message) { + this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message, false); + }, + onOffer: function(aOffer) { + this._listener.sendAnswer(mockedChannelDescription); + this._onSessionTransport(); + }, + onAnswer: function(aAnswer) { + this._onSessionTransport(); + }, + _onSessionTransport: function() { + setTimeout(()=>{ + this._listener.onSessionTransport(this); + this.simulateTransportReady(); + this._listener = null; + }, 0); + } + }; + + + function tearDown() { + mockedSessionTransport.callback = null; + + /* Register original factories. */ + for (var data of originalFactoryData) { + registerOriginalFactory(data.contractId, data.mockedClassId, + data.mockedFactory, data.originalClassId, + data.originalFactory); + } + sendMessage("teardown-complete"); + } + + + function registerMockedFactory(contractId, mockedClassId, mockedFactory) { + var originalClassId, originalFactory; + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + + if (!registrar.isCIDRegistered(mockedClassId)) { + try { + originalClassId = registrar.contractIDToCID(contractId); + originalFactory = Cm.getClassObject(Cc[contractId], Ci.nsIFactory); + } catch (ex) { + originalClassId = ""; + originalFactory = null; + } + if (originalFactory) { + registrar.unregisterFactory(originalClassId, originalFactory); + } + registrar.registerFactory(mockedClassId, "", contractId, mockedFactory); + } + + return { contractId: contractId, + mockedClassId: mockedClassId, + mockedFactory: mockedFactory, + originalClassId: originalClassId, + originalFactory: originalFactory }; + } + + function registerOriginalFactory(contractId, mockedClassId, mockedFactory, originalClassId, originalFactory) { + if (originalFactory) { + var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); + registrar.unregisterFactory(mockedClassId, mockedFactory); + registrar.registerFactory(originalClassId, "", contractId, originalFactory); + } + } + + /* Register mocked factories. */ + const originalFactoryData = []; + const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator); + originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/datachanneltransportbuilder;1", + uuidGenerator.generateUUID(), + mockedSessionTransport)); + + addMessageListener('trigger-incoming-message', function(event) { + mockedSessionTransport.simulateIncomingMessage(event.data.data); + }); + addMessageListener('teardown', ()=>tearDown()); +} + +// Exposed to the caller of |loadPrivilegedScript| +var contentScript = { + handlers: {}, + addMessageListener: function(message, handler) { + if (this.handlers.hasOwnProperty(message)) { + this.handlers[message].push(handler); + } else { + this.handlers[message] = [handler]; + } + }, + removeMessageListener: function(message, handler) { + if (!handler || !this.handlers.hasOwnProperty(message)) { + return; + } + var index = this.handlers[message].indexOf(handler); + if (index != -1) { + this.handlers[message].splice(index, 1); + } + }, + sendAsyncMessage: function(message, data) { + port.postMessage({'type': message, + 'data': data + }); + } +} + +if (!SpecialPowers.isMainProcess()) { + var port; + try { + port = SpecialPowers.loadPrivilegedScript(loadPrivilegedScriptTest.toSource()); + } catch (e) { + ok(false, "loadPrivilegedScript shoulde not throw" + e); + } + + port.onmessage = (e) => { + var type = e.data['type']; + if (!contentScript.handlers.hasOwnProperty(type)) { + return; + } + var args = [e.data['data']]; + contentScript.handlers[type].forEach(handler => handler.apply(null, args)); + }; +} diff --git a/dom/presentation/tests/mochitest/chrome.ini b/dom/presentation/tests/mochitest/chrome.ini new file mode 100644 index 000000000..83841f4f8 --- /dev/null +++ b/dom/presentation/tests/mochitest/chrome.ini @@ -0,0 +1,14 @@ +[DEFAULT] +support-files = + PresentationDeviceInfoChromeScript.js + PresentationSessionChromeScript.js + +[test_presentation_datachannel_sessiontransport.html] +skip-if = os == 'android' +[test_presentation_device_info.html] +[test_presentation_sender_startWithDevice.html] +skip-if = toolkit == 'android' # Bug 1129785 +[test_presentation_tcp_sender.html] +skip-if = toolkit == 'android' # Bug 1129785 +[test_presentation_tcp_sender_default_request.html] +skip-if = toolkit == 'android' # Bug 1129785 diff --git a/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html b/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html new file mode 100644 index 000000000..cf02d2b2c --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html @@ -0,0 +1,220 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <head> + <meta charset="utf-8"> + <title>Test for B2G PresentationReceiver at receiver side</title> + </head> + <body> + <div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function is(a, b, msg) { + if (a === b) { + alert('OK ' + msg); + } else { + alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); + } +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function command(name, data) { + alert('COMMAND ' + JSON.stringify({name: name, data: data})); +} + +function finish() { + alert('DONE'); +} + +var connection; +const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0]; +const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length); +const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER); +TYPED_DATA_ARRAY.set(DATA_ARRAY); + +function is_same_buffer(recv_data, expect_data) { + let recv_dataview = new Uint8Array(recv_data); + let expected_dataview = new Uint8Array(expect_data); + + if (recv_dataview.length !== expected_dataview.length) { + return false; + } + + for (let i = 0; i < recv_dataview.length; i++) { + if (recv_dataview[i] != expected_dataview[i]) { + info('discover byte differenct at ' + i); + return false; + } + } + return true; +} + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionAvailable ---'); + ok(navigator.presentation, "Receiver: navigator.presentation should be available."); + ok(navigator.presentation.receiver, "Receiver: navigator.presentation.receiver should be available."); + is(navigator.presentation.defaultRequest, null, "Receiver: navigator.presentation.defaultRequest should be null."); + + navigator.presentation.receiver.connectionList + .then((aList) => { + is(aList.connections.length, 1, "Should get one conncetion."); + connection = aList.connections[0]; + ok(connection.id, "Connection ID should be set: " + connection.id); + is(connection.state, "connected", "Connection state at receiver side should be connected."); + aResolve(); + }) + .catch((aError) => { + ok(false, "Receiver: Error occurred when getting the connection: " + aError); + finish(); + aReject(); + }); + }); +} + +function testConnectionReady() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionReady ---'); + connection.onconnect = function() { + connection.onconnect = null; + ok(false, "Should not get |onconnect| event.") + aReject(); + }; + if (connection.state === "connected") { + connection.onconnect = null; + is(connection.state, "connected", "Receiver: Connection state should become connected."); + aResolve(); + } + }); +} + +function testIncomingMessage() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testIncomingMessage ---'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + let msg = evt.data; + is(msg, 'msg-sender-to-receiver', 'Receiver: Receiver should receive message from sender.'); + command('forward-command', JSON.stringify({ name: 'message-from-sender-received' })); + aResolve(); + }); + command('forward-command', JSON.stringify({ name: 'trigger-message-from-sender' })); + }); +} + +function testSendMessage() { + return new Promise(function(aResolve, aReject) { + window.addEventListener('hashchange', function hashchangeHandler(evt) { + var message = JSON.parse(decodeURIComponent(window.location.hash.substring(1))); + if (message.type === 'trigger-message-from-receiver') { + info('Receiver: --- testSendMessage ---'); + connection.send('msg-receiver-to-sender'); + } + if (message.type === 'message-from-receiver-received') { + window.removeEventListener('hashchange', hashchangeHandler); + aResolve(); + } + }); + }); +} + +function testIncomingBlobMessage() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testIncomingBlobMessage ---'); + connection.send('testIncomingBlobMessage'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + let recvData= String.fromCharCode.apply(null, new Uint8Array(evt.data)); + is(recvData, "Hello World", 'expected same string data'); + aResolve(); + }); + }); +} + +function testConnectionClosed() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionClosed ---'); + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Receiver: Connection should be closed."); + command('forward-command', JSON.stringify({ name: 'receiver-closed' })); + aResolve(); + }; + command('forward-command', JSON.stringify({ name: 'ready-to-close' })); + }); +} + +function testReconnectConnection() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testReconnectConnection ---'); + window.addEventListener('hashchange', function hashchangeHandler(evt) { + var message = JSON.parse(decodeURIComponent(window.location.hash.substring(1))); + if (message.type === 'prepare-for-reconnect') { + command('forward-command', JSON.stringify({ name: 'ready-to-reconnect' })); + } + }); + connection.onconnect = function() { + connection.onconnect = null; + ok(true, "The connection is reconnected.") + aResolve(); + }; + }); +} + +function testIncomingArrayBuffer() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testIncomingArrayBuffer ---'); + connection.binaryType = "blob"; + connection.send('testIncomingArrayBuffer'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + var fileReader = new FileReader(); + fileReader.onload = function() { + ok(is_same_buffer(DATA_ARRAY_BUFFER, this.result), "expected same buffer data"); + aResolve(); + }; + fileReader.readAsArrayBuffer(evt.data); + }); + }); +} + +function testIncomingArrayBufferView() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testIncomingArrayBufferView ---'); + connection.binaryType = "arraybuffer"; + connection.send('testIncomingArrayBufferView'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + ok(is_same_buffer(evt.data, TYPED_DATA_ARRAY), "expected same buffer data"); + aResolve(); + }); + }); +} + +function runTests() { + testConnectionAvailable() + .then(testConnectionReady) + .then(testIncomingMessage) + .then(testSendMessage) + .then(testIncomingBlobMessage) + .then(testConnectionClosed) + .then(testReconnectConnection) + .then(testIncomingArrayBuffer) + .then(testIncomingArrayBufferView) + .then(testConnectionClosed); +} + +runTests(); + +</script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_1ua_wentaway.html b/dom/presentation/tests/mochitest/file_presentation_1ua_wentaway.html new file mode 100644 index 000000000..370cb92e1 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_1ua_wentaway.html @@ -0,0 +1,95 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <head> + <meta charset="utf-8"> + <title>Test for B2G PresentationReceiver at receiver side</title> + </head> + <body> + <div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function is(a, b, msg) { + if (a === b) { + alert('OK ' + msg); + } else { + alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); + } +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function command(name, data) { + alert('COMMAND ' + JSON.stringify({name: name, data: data})); +} + +function finish() { + alert('DONE'); +} + +var connection; + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionAvailable ---'); + ok(navigator.presentation, "Receiver: navigator.presentation should be available."); + ok(navigator.presentation.receiver, "Receiver: navigator.presentation.receiver should be available."); + + navigator.presentation.receiver.connectionList + .then((aList) => { + is(aList.connections.length, 1, "Should get one conncetion."); + connection = aList.connections[0]; + ok(connection.id, "Connection ID should be set: " + connection.id); + is(connection.state, "connected", "Connection state at receiver side should be connected."); + aResolve(); + }) + .catch((aError) => { + ok(false, "Receiver: Error occurred when getting the connection: " + aError); + finish(); + aReject(); + }); + }); +} + +function testConnectionReady() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionReady ---'); + connection.onconnect = function() { + connection.onconnect = null; + ok(false, "Should not get |onconnect| event.") + aReject(); + }; + if (connection.state === "connected") { + connection.onconnect = null; + is(connection.state, "connected", "Receiver: Connection state should become connected."); + aResolve(); + } + }); +} + +function testConnectionWentaway() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionWentaway ---\n'); + command('forward-command', JSON.stringify({ name: 'ready-to-remove-receiverFrame' })); + }); +} + +function runTests() { + testConnectionAvailable() + .then(testConnectionReady) + .then(testConnectionWentaway); +} + +runTests(); + +</script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html b/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html new file mode 100644 index 000000000..f042d2994 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html @@ -0,0 +1,159 @@ + +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test allow-presentation sandboxing flag</title> +<script type="application/javascript;version=1.8"> + +"use strict"; + +function is(a, b, msg) { + window.parent.postMessage((a === b ? "OK " : "KO ") + msg, "*"); +} + +function ok(a, msg) { + window.parent.postMessage((a ? "OK " : "KO ") + msg, "*"); +} + +function info(msg) { + window.parent.postMessage("INFO " + msg, "*"); +} + +function command(msg) { + window.parent.postMessage("COMMAND " + JSON.stringify(msg), "*"); +} + +function finish() { + window.parent.postMessage("DONE", "*"); +} + +function testGetAvailability() { + return new Promise(function(aResolve, aReject) { + ok(navigator.presentation, "navigator.presentation should be available."); + var request = new PresentationRequest("http://example.com"); + + request.getAvailability().then( + function(aAvailability) { + ok(false, "Unexpected success, should get a security error."); + aReject(); + }, + function(aError) { + is(aError.name, "SecurityError", "Should get a security error."); + aResolve(); + } + ); + }); +} + +function testStartRequest() { + return new Promise(function(aResolve, aReject) { + var request = new PresentationRequest("http://example.com"); + + request.start().then( + function(aAvailability) { + ok(false, "Unexpected success, should get a security error."); + aReject(); + }, + function(aError) { + is(aError.name, "SecurityError", "Should get a security error."); + aResolve(); + } + ); + }); +} + +function testReconnectRequest() { + return new Promise(function(aResolve, aReject) { + var request = new PresentationRequest("http://example.com"); + + request.reconnect("dummyId").then( + function(aConnection) { + ok(false, "Unexpected success, should get a security error."); + aReject(); + }, + function(aError) { + is(aError.name, "SecurityError", "Should get a security error."); + aResolve(); + } + ); + }); +} + +function testGetAvailabilityForAboutBlank() { + return new Promise(function(aResolve, aReject) { + var request = new PresentationRequest("about:blank"); + + request.getAvailability().then( + function(aAvailability) { + ok(true, "Success due to a priori authenticated URL."); + aResolve(); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + aReject(); + } + ); + }); +} + +function testGetAvailabilityForAboutSrcdoc() { + return new Promise(function(aResolve, aReject) { + var request = new PresentationRequest("about:srcdoc"); + + request.getAvailability().then( + function(aAvailability) { + ok(true, "Success due to a priori authenticated URL."); + aResolve(); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + aReject(); + } + ); + }); +} + +function testGetAvailabilityForDataURL() { + return new Promise(function(aResolve, aReject) { + var request = new PresentationRequest("data:text/html,1"); + + request.getAvailability().then( + function(aAvailability) { + ok(true, "Success due to a priori authenticated URL."); + aResolve(); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + aReject(); + } + ); + }); +} + +function runTest() { + testGetAvailability() + .then(testStartRequest) + .then(testReconnectRequest) + .then(testGetAvailabilityForAboutBlank) + .then(testGetAvailabilityForAboutSrcdoc) + .then(testGetAvailabilityForDataURL) + .then(finish); +} + +window.addEventListener("message", function onMessage(evt) { + window.removeEventListener("message", onMessage); + if (evt.data === "start") { + runTest(); + } +}, false); + +window.setTimeout(function() { + command("ready-to-start"); +}, 3000); + +</script> +</head> +<body> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_non_receiver.html b/dom/presentation/tests/mochitest/file_presentation_non_receiver.html new file mode 100644 index 000000000..1203523ac --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_non_receiver.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationReceiver on a non-receiver page at receiver side</title> +</head> +<body> +<div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function is(a, b, msg) { + alert((a === b ? 'OK ' : 'KO ') + msg); +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function finish() { + alert('DONE'); +} + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + is(navigator.presentation.receiver, null, "navigator.presentation.receiver shouldn't be available in non-receiving pages."); + aResolve(); + }); +} + +testConnectionAvailable(). +then(finish); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html b/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html new file mode 100644 index 000000000..c95eddf57 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationReceiver on a non-receiver inner iframe of the receiver page at receiver side</title> +</head> +<body onload="testConnectionAvailable()"> +<div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + is(navigator.presentation.receiver, null, "navigator.presentation.receiver shouldn't be available in inner iframes with different origins from receiving pages."); + aResolve(); + }); +} + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver.html b/dom/presentation/tests/mochitest/file_presentation_receiver.html new file mode 100644 index 000000000..46a330b5f --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_receiver.html @@ -0,0 +1,140 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationReceiver at receiver side</title> +</head> +<body> +<div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function is(a, b, msg) { + alert((a === b ? 'OK ' : 'KO ') + msg); +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function command(msg) { + alert('COMMAND ' + JSON.stringify(msg)); +} + +function finish() { + alert('DONE'); +} + +var connection; + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + ok(navigator.presentation, "navigator.presentation should be available in receiving pages."); + ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available in receiving pages."); + + navigator.presentation.receiver.connectionList.then( + function(aList) { + is(aList.connections.length, 1, "Should get one conncetion."); + connection = aList.connections[0]; + ok(connection.id, "Connection ID should be set: " + connection.id); + is(connection.state, "connected", "Connection state at receiver side should be connected."); + aResolve(); + }, + function(aError) { + ok(false, "Error occurred when getting the connection list: " + aError); + finish(); + aReject(); + } + ); + command({ name: 'trigger-incoming-offer' }); + }); +} + +function testDefaultRequestIsUndefined() { + return new Promise(function(aResolve, aReject) { + is(navigator.presentation.defaultRequest, undefined, "navigator.presentation.defaultRequest should not be available in receiving UA"); + aResolve(); + }); +} + +function testConnectionAvailableSameOriginInnerIframe() { + return new Promise(function(aResolve, aReject) { + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', './file_presentation_receiver_inner_iframe.html'); + document.body.appendChild(iframe); + + aResolve(); + }); +} + +function testConnectionUnavailableDiffOriginInnerIframe() { + return new Promise(function(aResolve, aReject) { + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', 'http://example.com/tests/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html'); + document.body.appendChild(iframe); + + aResolve(); + }); +} + +function testConnectionListSameObject() { + return new Promise(function(aResolve, aReject) { + is(navigator.presentation.receiver.connectionList, navigator.presentation.receiver.connectionList, "The promise should be the same object."); + var promise = navigator.presentation.receiver.connectionList.then( + function(aList) { + is(connection, aList.connections[0], "The connection from list and the one from |connectionavailable| event should be the same."); + aResolve(); + }, + function(aError) { + ok(false, "Error occurred when getting the connection list: " + aError); + finish(); + aReject(); + } + ); + }); +} + +function testIncomingMessage() { + return new Promise(function(aResolve, aReject) { + const incomingMessage = "test incoming message"; + + connection.addEventListener('message', function messageHandler(aEvent) { + connection.removeEventListener('message', messageHandler); + is(aEvent.data, incomingMessage, "An incoming message should be received."); + aResolve(); + }); + + command({ name: 'trigger-incoming-message', + data: incomingMessage }); + }); +} + +function testCloseConnection() { + return new Promise(function(aResolve, aReject) { + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Connection should be closed."); + aResolve(); + }; + + connection.close(); + }); +} + +testConnectionAvailable(). +then(testDefaultRequestIsUndefined). +then(testConnectionAvailableSameOriginInnerIframe). +then(testConnectionUnavailableDiffOriginInnerIframe). +then(testConnectionListSameObject). +then(testIncomingMessage). +then(testCloseConnection). +then(finish); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver_auxiliary_navigation.html b/dom/presentation/tests/mochitest/file_presentation_receiver_auxiliary_navigation.html new file mode 100644 index 000000000..3a6060310 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_receiver_auxiliary_navigation.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for sandboxed auxiliary navigation flag in receiver page</title> +</head> +<body> +<div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function is(a, b, msg) { + alert((a === b ? 'OK ' : 'KO ') + msg); +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function command(msg) { + alert('COMMAND ' + JSON.stringify(msg)); +} + +function finish() { + alert('DONE'); +} + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + ok(navigator.presentation, "navigator.presentation should be available in OOP receiving pages."); + ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available in receiving pages."); + + aResolve(); + }); +} + +function testOpenWindow() { + return new Promise(function(aResolve, aReject) { + try { + window.open("http://example.com"); + ok(false, "receiver page should not be able to open a new window."); + } catch(e) { + ok(true, "receiver page should not be able to open a new window."); + aResolve(); + } + }); +} + +testConnectionAvailable(). +then(testOpenWindow). +then(finish); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver_establish_connection_error.html b/dom/presentation/tests/mochitest/file_presentation_receiver_establish_connection_error.html new file mode 100644 index 000000000..6b1f2152f --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_receiver_establish_connection_error.html @@ -0,0 +1,79 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for connection establishing errors of B2G Presentation API at receiver side</title> +</head> +<body> +<div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function is(a, b, msg) { + if (a === b) { + alert('OK ' + msg); + } else { + alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); + } +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function command(name, data) { + alert('COMMAND ' + JSON.stringify({name: name, data: data})); +} + +function finish() { + alert('DONE'); +} + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + ok(navigator.presentation, "navigator.presentation should be available."); + ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available."); + aResolve(); + }); +} + +function testUnexpectedControlChannelClose() { + // Trigger the control channel to be closed with error code. + command({ name: 'trigger-control-channel-close', data: 0x80004004 /* NS_ERROR_ABORT */ }); + + return new Promise(function(aResolve, aReject) { + return Promise.race([ + navigator.presentation.receiver.connectionList.then( + (aList) => { + ok(false, "Should not get a connection list.") + aReject(); + }, + (aError) => { + ok(false, "Error occurred when getting the connection list: " + aError); + aReject(); + } + ), + new Promise( + () => { + setTimeout(() => { + ok(true, "Not getting a conenction list."); + aResolve(); + }, 3000); + } + ), + ]); + }); +} + +testConnectionAvailable(). +then(testUnexpectedControlChannelClose). +then(finish); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver_inner_iframe.html b/dom/presentation/tests/mochitest/file_presentation_receiver_inner_iframe.html new file mode 100644 index 000000000..3bd5ac4b1 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_receiver_inner_iframe.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationReceiver in an inner iframe of the receiver page at receiver side</title> +</head> +<body onload="testConnectionAvailable()"> +<div id="content"></div> +<script type="application/javascript;version=1.7"> + +"use strict"; + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available in same-origin inner iframes of receiving pages."); + aResolve(); + }); +} + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_reconnect.html b/dom/presentation/tests/mochitest/file_presentation_reconnect.html new file mode 100644 index 000000000..174ccd3f3 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_reconnect.html @@ -0,0 +1,102 @@ + +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test allow-presentation sandboxing flag</title> +<script type="application/javascript;version=1.8"> + +"use strict"; + +function is(a, b, msg) { + window.parent.postMessage((a === b ? "OK " : "KO ") + msg, "*"); +} + +function ok(a, msg) { + window.parent.postMessage((a ? "OK " : "KO ") + msg, "*"); +} + +function info(msg) { + window.parent.postMessage("INFO " + msg, "*"); +} + +function command(msg) { + window.parent.postMessage("COMMAND " + JSON.stringify(msg), "*"); +} + +function finish() { + window.parent.postMessage("DONE", "*"); +} + +var request; +var connection; + +function testStartRequest() { + return new Promise(function(aResolve, aReject) { + ok(navigator.presentation, "navigator.presentation should be available."); + request = new PresentationRequest("http://example1.com"); + + request.start().then( + function(aConnection) { + connection = aConnection; + ok(connection, "Connection should be available."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + + connection.onclose = function() { + connection.onclose = null; + command({ name: "notify-connection-closed", id: connection.id }); + }; + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testCloseConnection() { + return new Promise(function(aResolve, aReject) { + if (connection.state === "closed") { + aResolve(); + return; + } + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "The connection should be closed."); + aResolve(); + }; + + connection.close(); + }); +} + +window.addEventListener("message", function onMessage(evt) { + if (evt.data === "startConnection") { + testStartRequest().then( + function () { + command({ name: "connection-connected", id: connection.id }); + } + ); + } + else if (evt.data === "closeConnection") { + testCloseConnection().then( + function () { + command({ name: "connection-closed", id: connection.id }); + } + ); + } +}, false); + +</script> +</head> +<body> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_sandboxed_presentation.html b/dom/presentation/tests/mochitest/file_presentation_sandboxed_presentation.html new file mode 100644 index 000000000..369621cee --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_sandboxed_presentation.html @@ -0,0 +1,114 @@ + +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test allow-presentation sandboxing flag</title> +<script type="application/javascript;version=1.8"> + +"use strict"; + +function is(a, b, msg) { + window.parent.postMessage((a === b ? "OK " : "KO ") + msg, "*"); +} + +function ok(a, msg) { + window.parent.postMessage((a ? "OK " : "KO ") + msg, "*"); +} + +function info(msg) { + window.parent.postMessage("INFO " + msg, "*"); +} + +function command(msg) { + window.parent.postMessage("COMMAND " + JSON.stringify(msg), "*"); +} + +function finish() { + window.parent.postMessage("DONE", "*"); +} + +function testGetAvailability() { + return new Promise(function(aResolve, aReject) { + ok(navigator.presentation, "navigator.presentation should be available."); + var request = new PresentationRequest("http://example.com"); + + request.getAvailability().then( + function(aAvailability) { + ok(false, "Unexpected success, should get a security error."); + aReject(); + }, + function(aError) { + is(aError.name, "SecurityError", "Should get a security error."); + aResolve(); + } + ); + }); +} + +function testStartRequest() { + return new Promise(function(aResolve, aReject) { + var request = new PresentationRequest("http://example.com"); + + request.start().then( + function(aAvailability) { + ok(false, "Unexpected success, should get a security error."); + aReject(); + }, + function(aError) { + is(aError.name, "SecurityError", "Should get a security error."); + aResolve(); + } + ); + }); +} + +function testDefaultRequest() { + return new Promise(function(aResolve, aReject) { + navigator.presentation.defaultRequest = new PresentationRequest("http://example.com"); + is(navigator.presentation.defaultRequest, null, "DefaultRequest shoud be null."); + aResolve(); + }); +} + +function testReconnectRequest() { + return new Promise(function(aResolve, aReject) { + var request = new PresentationRequest("http://example.com"); + + request.reconnect("dummyId").then( + function(aConnection) { + ok(false, "Unexpected success, should get a security error."); + aReject(); + }, + function(aError) { + is(aError.name, "SecurityError", "Should get a security error."); + aResolve(); + } + ); + }); +} + +function runTest() { + testGetAvailability() + .then(testStartRequest) + .then(testDefaultRequest) + .then(testReconnectRequest) + .then(finish); +} + +window.addEventListener("message", function onMessage(evt) { + window.removeEventListener("message", onMessage); + if (evt.data === "start") { + runTest(); + } +}, false); + +window.setTimeout(function() { + command("ready-to-start"); +}, 3000); + +</script> +</head> +<body> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_terminate.html b/dom/presentation/tests/mochitest/file_presentation_terminate.html new file mode 100644 index 000000000..a26a44b90 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_terminate.html @@ -0,0 +1,104 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <head> + <meta charset='utf-8'> + <title>Test for B2G PresentationReceiver at receiver side</title> + </head> + <body> + <div id='content'></div> +<script type='application/javascript;version=1.7'> + +'use strict'; + +function is(a, b, msg) { + if (a === b) { + alert('OK ' + msg); + } else { + alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); + } +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function command(name, data) { + alert('COMMAND ' + JSON.stringify({name: name, data: data})); +} + +function finish() { + alert('DONE'); +} + +var connection; + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionAvailable ---'); + ok(navigator.presentation, 'Receiver: navigator.presentation should be available.'); + ok(navigator.presentation.receiver, 'Receiver: navigator.presentation.receiver should be available.'); + + navigator.presentation.receiver.connectionList + .then((aList) => { + is(aList.connections.length, 1, 'Should get one conncetion.'); + connection = aList.connections[0]; + ok(connection.id, 'Connection ID should be set: ' + connection.id); + is(connection.state, 'connected', 'Connection state at receiver side should be connected.'); + aResolve(); + }) + .catch((aError) => { + ok(false, 'Receiver: Error occurred when getting the connection: ' + aError); + finish(); + aReject(); + }); + }); +} + +function testConnectionReady() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionReady ---'); + connection.onconnect = function() { + connection.onconnect = null; + ok(false, 'Should not get |onconnect| event.') + aReject(); + }; + if (connection.state === 'connected') { + connection.onconnect = null; + is(connection.state, 'connected', 'Receiver: Connection state should become connected.'); + aResolve(); + } + }); +} + +function testConnectionTerminate() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionTerminate ---'); + connection.onterminate = function() { + connection.onterminate = null; + // Using window.alert at this stage will cause window.close() fail. + // Only trigger it if verdict fail. + if (connection.state !== 'terminated') { + is(connection.state, 'terminated', 'Receiver: Connection should be terminated.'); + } + aResolve(); + }; + command('forward-command', JSON.stringify({ name: 'ready-to-terminate' })); + }); +} + +function runTests() { + testConnectionAvailable() + .then(testConnectionReady) + .then(testConnectionTerminate) +} + +runTests(); + +</script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_terminate_establish_connection_error.html b/dom/presentation/tests/mochitest/file_presentation_terminate_establish_connection_error.html new file mode 100644 index 000000000..d8df8a1a6 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_terminate_establish_connection_error.html @@ -0,0 +1,114 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <head> + <meta charset='utf-8'> + <title>Test for B2G PresentationReceiver at receiver side</title> + </head> + <body> + <div id='content'></div> +<script type='application/javascript;version=1.7'> + +'use strict'; + +function is(a, b, msg) { + if (a === b) { + alert('OK ' + msg); + } else { + alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); + } +} + +function ok(a, msg) { + alert((a ? 'OK ' : 'KO ') + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function command(name, data) { + alert('COMMAND ' + JSON.stringify({name: name, data: data})); +} + +function finish() { + alert('DONE'); +} + +var connection; + +function testConnectionAvailable() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionAvailable ---'); + ok(navigator.presentation, 'Receiver: navigator.presentation should be available.'); + ok(navigator.presentation.receiver, 'Receiver: navigator.presentation.receiver should be available.'); + + navigator.presentation.receiver.connectionList + .then((aList) => { + is(aList.connections.length, 1, 'Should get one connection.'); + connection = aList.connections[0]; + ok(connection.id, 'Connection ID should be set: ' + connection.id); + is(connection.state, 'connected', 'Connection state at receiver side should be connected.'); + aResolve(); + }) + .catch((aError) => { + ok(false, 'Receiver: Error occurred when getting the connection: ' + aError); + finish(); + aReject(); + }); + }); +} + +function testConnectionReady() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionReady ---'); + connection.onconnect = function() { + connection.onconnect = null; + ok(false, 'Should not get |onconnect| event.') + aReject(); + }; + if (connection.state === 'connected') { + connection.onconnect = null; + is(connection.state, 'connected', 'Receiver: Connection state should become connected.'); + aResolve(); + } + }); +} + +function testConnectionTerminate() { + return new Promise(function(aResolve, aReject) { + info('Receiver: --- testConnectionTerminate ---'); + connection.onterminate = function() { + connection.onterminate = null; + // Using window.alert at this stage will cause window.close() fail. + // Only trigger it if verdict fail. + if (connection.state !== 'terminated') { + is(connection.state, 'terminated', 'Receiver: Connection should be terminated.'); + } + aResolve(); + }; + + window.addEventListener('hashchange', function hashchangeHandler(evt) { + var message = JSON.parse(decodeURIComponent(window.location.hash.substring(1))); + if (message.type === 'ready-to-terminate') { + info('Receiver: --- ready-to-terminate ---'); + connection.terminate(); + } + }); + + + command('forward-command', JSON.stringify({ name: 'prepare-for-terminate' })); + }); +} + +function runTests() { + testConnectionAvailable() + .then(testConnectionReady) + .then(testConnectionTerminate) +} + +runTests(); + +</script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test @@ -0,0 +1 @@ + diff --git a/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test^headers^ b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test^headers^ new file mode 100644 index 000000000..fc044e3c4 --- /dev/null +++ b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test^headers^ @@ -0,0 +1 @@ +Content-Type: application/unknown diff --git a/dom/presentation/tests/mochitest/mochitest.ini b/dom/presentation/tests/mochitest/mochitest.ini new file mode 100644 index 000000000..f96e07f1e --- /dev/null +++ b/dom/presentation/tests/mochitest/mochitest.ini @@ -0,0 +1,77 @@ +[DEFAULT] +support-files = + PresentationDeviceInfoChromeScript.js + PresentationSessionChromeScript.js + PresentationSessionFrameScript.js + PresentationSessionChromeScript1UA.js + file_presentation_1ua_receiver.html + test_presentation_1ua_sender_and_receiver.js + file_presentation_non_receiver_inner_iframe.html + file_presentation_non_receiver.html + file_presentation_receiver.html + file_presentation_receiver_establish_connection_error.html + file_presentation_receiver_inner_iframe.html + file_presentation_1ua_wentaway.html + test_presentation_1ua_connection_wentaway.js + file_presentation_receiver_auxiliary_navigation.html + test_presentation_receiver_auxiliary_navigation.js + file_presentation_sandboxed_presentation.html + file_presentation_terminate.html + test_presentation_terminate.js + file_presentation_terminate_establish_connection_error.html + test_presentation_terminate_establish_connection_error.js + file_presentation_reconnect.html + file_presentation_unknown_content_type.test + file_presentation_unknown_content_type.test^headers^ + test_presentation_tcp_receiver_establish_connection_unknown_content_type.js + file_presentation_mixed_security_contexts.html + +[test_presentation_dc_sender.html] +[test_presentation_dc_receiver.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_dc_receiver_oop.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_1ua_sender_and_receiver_inproc.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_1ua_sender_and_receiver_oop.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_1ua_connection_wentaway_inproc.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_1ua_connection_wentaway_oop.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_device_info_permission.html] +[test_presentation_tcp_sender_disconnect.html] +skip-if = toolkit == 'android' # Bug 1129785 +[test_presentation_tcp_sender_establish_connection_error.html] +skip-if = toolkit == 'android' # Bug 1129785 +[test_presentation_tcp_receiver_establish_connection_error.html] +skip-if = (e10s || toolkit == 'android' || os == 'mac' || os == 'win') # Bug 1129785, Bug 1204709 +[test_presentation_tcp_receiver_establish_connection_timeout.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html] +skip-if = (e10s || toolkit == 'android') +[test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html] +skip-if = (e10s || toolkit == 'android') +[test_presentation_tcp_receiver.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_tcp_receiver_oop.html] +skip-if = (e10s || toolkit == 'android') # Bug 1129785 +[test_presentation_receiver_auxiliary_navigation_inproc.html] +skip-if = e10s +[test_presentation_receiver_auxiliary_navigation_oop.html] +skip-if = e10s +[test_presentation_terminate_inproc.html] +skip-if = (e10s || toolkit == 'android') +[test_presentation_terminate_oop.html] +skip-if = (e10s || toolkit == 'android') +[test_presentation_terminate_establish_connection_error_inproc.html] +skip-if = (e10s || toolkit == 'android') +[test_presentation_terminate_establish_connection_error_oop.html] +skip-if = (e10s || toolkit == 'android') +[test_presentation_sender_on_terminate_request.html] +skip-if = toolkit == 'android' +[test_presentation_sandboxed_presentation.html] +skip-if = true # bug 1315867 +[test_presentation_reconnect.html] +[test_presentation_mixed_security_contexts.html] +[test_presentation_availability.html] diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js new file mode 100644 index 000000000..dbeb4ffcc --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js @@ -0,0 +1,175 @@ +'use strict'; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); + +function debug(str) { + // info(str); +} + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_1ua_wentaway.html'); +var request; +var connection; +var receiverIframe; + +function setup() { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + debug('Got message: device-prompt'); + gScript.removeMessageListener('device-prompt', devicePromptHandler); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + gScript.sendAsyncMessage("trigger-control-channel-open"); + }); + + gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { + debug('Got message: sender-launch'); + gScript.removeMessageListener('sender-launch', senderLaunchHandler); + is(url, receiverUrl, 'Receiver: should receive the same url'); + receiverIframe = document.createElement('iframe'); + receiverIframe.setAttribute("mozbrowser", "true"); + receiverIframe.setAttribute("mozpresentation", receiverUrl); + var oop = location.pathname.indexOf('_inproc') == -1; + receiverIframe.setAttribute("remote", oop); + + receiverIframe.setAttribute('src', receiverUrl); + receiverIframe.addEventListener("mozbrowserloadend", function mozbrowserloadendHander() { + receiverIframe.removeEventListener("mozbrowserloadend", mozbrowserloadendHander); + info("Receiver loaded."); + }); + + // This event is triggered when the iframe calls "alert". + receiverIframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { + var message = evt.detail.message; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, "")); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, "")); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, "")); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + receiverIframe.removeEventListener("mozbrowsershowmodalprompt", + receiverListener); + teardown(); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(receiverIframe); + aResolve(receiverIframe); + }); + + var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + obs.notifyObservers(promise, 'setup-request-promise', null); + }); + + gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { + debug('Got message: promise-setup-ready'); + gScript.removeMessageListener('promise-setup-ready', + promiseSetupReadyHandler); + gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); + }); + + return Promise.resolve(); +} + +function testCreateRequest() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testCreateRequest ---'); + request = new PresentationRequest(receiverUrl); + request.getAvailability().then((aAvailability) => { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Sender: Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }).catch((aError) => { + ok(false, "Sender: Error occurred when getting availability: " + aError); + teardown(); + aReject(); + }); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + request.start().then((aConnection) => { + connection = aConnection; + ok(connection, "Sender: Connection should be available."); + ok(connection.id, "Sender: Connection ID should be set."); + is(connection.state, "connecting", "Sender: The initial state should be connecting."); + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }).catch((aError) => { + ok(false, "Sender: Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + }); + }); +} + +function testConnectionWentaway() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testConnectionWentaway ---'); + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Sender: Connection should be closed."); + receiverIframe.addEventListener('mozbrowserclose', function closeHandler() { + ok(false, 'wentaway should not trigger receiver close'); + aResolve(); + }); + setTimeout(aResolve, 3000); + }; + gScript.addMessageListener('ready-to-remove-receiverFrame', function onReadyToRemove() { + gScript.removeMessageListener('ready-to-remove-receiverFrame', onReadyToRemove); + receiverIframe.src = "http://example.com"; + }); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + debug('Got message: teardown-complete'); + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup().then(testCreateRequest) + .then(testStartConnection) + .then(testConnectionWentaway) + .then(teardown); +} + +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: "browser", allow: true, context: document}, +], () => { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.test.enabled", true], + ["dom.mozBrowserFramesEnabled", true], + ["dom.ipc.tabs.disabled", false], + ["network.disable.ipc.security", true], + ["dom.presentation.test.stage", 0]]}, + runTests); +}); diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_inproc.html b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_inproc.html new file mode 100644 index 000000000..68491d81b --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_inproc.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API when sender and receiver at the same side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1258600"> + Test for PresentationConnectionCloseEvent with wentaway reason</a> + <script type="application/javascript;version=1.8" src="test_presentation_1ua_connection_wentaway.js"> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_oop.html b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_oop.html new file mode 100644 index 000000000..68491d81b --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_oop.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API when sender and receiver at the same side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1258600"> + Test for PresentationConnectionCloseEvent with wentaway reason</a> + <script type="application/javascript;version=1.8" src="test_presentation_1ua_connection_wentaway.js"> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js new file mode 100644 index 000000000..8a7787b40 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js @@ -0,0 +1,370 @@ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* 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/. */ + +'use strict'; + +function debug(str) { + // info(str); +} + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_1ua_receiver.html'); +var request; +var connection; +var receiverIframe; +var presentationId; +const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0]; +const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length); +const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER); +TYPED_DATA_ARRAY.set(DATA_ARRAY); + +function postMessageToIframe(aType) { + receiverIframe.src = receiverUrl + "#" + + encodeURIComponent(JSON.stringify({ type: aType })); +} + +function setup() { + + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + debug('Got message: device-prompt'); + gScript.removeMessageListener('device-prompt', devicePromptHandler); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + gScript.sendAsyncMessage("trigger-control-channel-open"); + }); + + gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { + debug('Got message: sender-launch'); + gScript.removeMessageListener('sender-launch', senderLaunchHandler); + is(url, receiverUrl, 'Receiver: should receive the same url'); + receiverIframe = document.createElement('iframe'); + receiverIframe.setAttribute('src', receiverUrl); + receiverIframe.setAttribute("mozbrowser", "true"); + receiverIframe.setAttribute("mozpresentation", receiverUrl); + var oop = location.pathname.indexOf('_inproc') == -1; + receiverIframe.setAttribute("remote", oop); + + // This event is triggered when the iframe calls "alert". + receiverIframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { + var message = evt.detail.message; + debug('Got iframe message: ' + message); + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, "")); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, "")); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, "")); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + receiverIframe.removeEventListener("mozbrowsershowmodalprompt", + receiverListener); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(receiverIframe); + aResolve(receiverIframe); + }); + + var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + obs.notifyObservers(promise, 'setup-request-promise', null); + }); + + gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { + debug('Got message: promise-setup-ready'); + gScript.removeMessageListener('promise-setup-ready', promiseSetupReadyHandler); + gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); + }); + + return Promise.resolve(); +} + +function testCreateRequest() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testCreateRequest ---'); + request = new PresentationRequest("file_presentation_1ua_receiver.html"); + request.getAvailability().then((aAvailability) => { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Sender: Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }).catch((aError) => { + ok(false, "Sender: Error occurred when getting availability: " + aError); + teardown(); + aReject(); + }); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + request.start().then((aConnection) => { + connection = aConnection; + ok(connection, "Sender: Connection should be available."); + ok(connection.id, "Sender: Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + is(connection.url, receiverUrl, "request URL should be expanded to absolute URL"); + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + presentationId = connection.id; + aResolve(); + }; + }).catch((aError) => { + ok(false, "Sender: Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + }); + + let request2 = new PresentationRequest("/"); + request2.start().then(() => { + ok(false, "Sender: session start should fail while there is an unsettled promise."); + }).catch((aError) => { + is(aError.name, "OperationError", "Expect to get OperationError."); + }); + }); +} + +function testSendMessage() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testSendMessage ---'); + gScript.addMessageListener('trigger-message-from-sender', function triggerMessageFromSenderHandler() { + debug('Got message: trigger-message-from-sender'); + gScript.removeMessageListener('trigger-message-from-sender', triggerMessageFromSenderHandler); + info('Send message to receiver'); + connection.send('msg-sender-to-receiver'); + }); + + gScript.addMessageListener('message-from-sender-received', function messageFromSenderReceivedHandler() { + debug('Got message: message-from-sender-received'); + gScript.removeMessageListener('message-from-sender-received', messageFromSenderReceivedHandler); + aResolve(); + }); + }); +} + +function testIncomingMessage() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testIncomingMessage ---'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + let msg = evt.data; + is(msg, "msg-receiver-to-sender", "Sender: Sender should receive message from Receiver"); + postMessageToIframe('message-from-receiver-received'); + aResolve(); + }); + postMessageToIframe('trigger-message-from-receiver'); + }); +} + +function testSendBlobMessage() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testSendBlobMessage ---'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + let msg = evt.data; + is(msg, "testIncomingBlobMessage", "Sender: Sender should receive message from Receiver"); + let blob = new Blob(["Hello World"], {type : 'text/plain'}); + connection.send(blob); + aResolve(); + }); + }); +} + +function testSendArrayBuffer() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testSendArrayBuffer ---'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + let msg = evt.data; + is(msg, "testIncomingArrayBuffer", "Sender: Sender should receive message from Receiver"); + connection.send(DATA_ARRAY_BUFFER); + aResolve(); + }); + }); +} + +function testSendArrayBufferView() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testSendArrayBufferView ---'); + connection.addEventListener('message', function messageHandler(evt) { + connection.removeEventListener('message', messageHandler); + let msg = evt.data; + is(msg, "testIncomingArrayBufferView", "Sender: Sender should receive message from Receiver"); + connection.send(TYPED_DATA_ARRAY); + aResolve(); + }); + }); +} + +function testCloseConnection() { + info('Sender: --- testCloseConnection ---'); + // Test terminate immediate after close. + function controlChannelEstablishedHandler() + { + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + ok(false, "terminate after close should do nothing"); + } + gScript.addMessageListener('ready-to-close', function onReadyToClose() { + gScript.removeMessageListener('ready-to-close', onReadyToClose); + connection.close(); + + gScript.addMessageListener('control-channel-established', controlChannelEstablishedHandler); + connection.terminate(); + }); + + return Promise.all([ + new Promise(function(aResolve, aReject) { + connection.onclose = function() { + connection.onclose = null; + is(connection.state, 'closed', 'Sender: Connection should be closed.'); + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + aResolve(); + }; + }), + new Promise(function(aResolve, aReject) { + let timeout = setTimeout(function() { + gScript.removeMessageListener('device-disconnected', + deviceDisconnectedHandler); + ok(true, "terminate after close should not trigger device.disconnect"); + aResolve(); + }, 3000); + + function deviceDisconnectedHandler() { + gScript.removeMessageListener('device-disconnected', + deviceDisconnectedHandler); + ok(false, "terminate after close should not trigger device.disconnect"); + clearTimeout(timeout); + aResolve(); + } + + gScript.addMessageListener('device-disconnected', deviceDisconnectedHandler); + }), + new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-closed', function onReceiverClosed() { + gScript.removeMessageListener('receiver-closed', onReceiverClosed); + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + aResolve(); + }); + }), + ]); +} + +function testTerminateAfterClose() { + info('Sender: --- testTerminateAfterClose ---'); + return Promise.race([ + new Promise(function(aResolve, aReject) { + connection.onterminate = function() { + connection.onterminate = null; + ok(false, 'terminate after close should do nothing'); + aResolve(); + }; + connection.terminate(); + }), + new Promise(function(aResolve, aReject) { + setTimeout(function() { + is(connection.state, 'closed', 'Sender: Connection should be closed.'); + aResolve(); + }, 3000); + }), + ]); +} + +function testReconnect() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testReconnect ---'); + gScript.addMessageListener('control-channel-established', function controlChannelEstablished() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablished); + gScript.sendAsyncMessage("trigger-control-channel-open"); + }); + + gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { + debug('Got message: start-reconnect'); + gScript.removeMessageListener('start-reconnect', startReconnectHandler); + is(url, receiverUrl, "URLs should be the same.") + gScript.sendAsyncMessage('trigger-reconnected-acked', url); + }); + + gScript.addMessageListener('ready-to-reconnect', function onReadyToReconnect() { + gScript.removeMessageListener('ready-to-reconnect', onReadyToReconnect); + request.reconnect(presentationId).then((aConnection) => { + connection = aConnection; + ok(connection, "Sender: Connection should be available."); + is(connection.id, presentationId, "The presentationId should be the same."); + is(connection.state, "connecting", "The initial state should be connecting."); + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }).catch((aError) => { + ok(false, "Sender: Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + }); + }); + + postMessageToIframe('prepare-for-reconnect'); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + debug('Got message: teardown-complete'); + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup().then(testCreateRequest) + .then(testStartConnection) + .then(testSendMessage) + .then(testIncomingMessage) + .then(testSendBlobMessage) + .then(testCloseConnection) + .then(testReconnect) + .then(testSendArrayBuffer) + .then(testSendArrayBufferView) + .then(testCloseConnection) + .then(testTerminateAfterClose) + .then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: "browser", allow: true, context: document}, +], () => { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + /* Mocked TCP session transport builder in the test */ + ["dom.presentation.session_transport.data_channel.enable", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.test.enabled", true], + ["dom.presentation.test.stage", 0], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["media.navigator.permission.disabled", true]]}, + runTests); +}); diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_inproc.html b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_inproc.html new file mode 100644 index 000000000..520b1a98c --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_inproc.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API when sender and receiver at the same side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1234492"> + Test for B2G Presentation API when sender and receiver at the same side</a> + <script type="application/javascript;version=1.8" src="test_presentation_1ua_sender_and_receiver.js"> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html new file mode 100644 index 000000000..e744e6802 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API when sender and receiver at the same side (OOP ver.)</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1234492"> + Test for B2G Presentation API when sender and receiver at the same side (OOP ver.)</a> + <script type="application/javascript;version=1.8" src="test_presentation_1ua_sender_and_receiver.js"> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_availability.html b/dom/presentation/tests/mochitest/test_presentation_availability.html new file mode 100644 index 000000000..89f1ad1b7 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_availability.html @@ -0,0 +1,236 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for PresentationAvailability</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1228508">Test PresentationAvailability</a> +<script type="application/javascript;version=1.8"> + +"use strict"; + +var testDevice = { + id: 'id', + name: 'name', + type: 'type', +}; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationDeviceInfoChromeScript.js')); +var request; +var availability; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('setup-complete', function() { + aResolve(); + }); + gScript.sendAsyncMessage('setup'); + }); +} + +function testInitialUnavailable() { + request = new PresentationRequest("https://example.com"); + + return request.getAvailability().then(function(aAvailability) { + is(aAvailability.value, false, "Should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + } + availability = aAvailability; + gScript.sendAsyncMessage('trigger-device-add', testDevice); + }).catch(function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + }); +} + +function testInitialAvailable() { + let anotherRequest = new PresentationRequest("https://example.net"); + return anotherRequest.getAvailability().then(function(aAvailability) { + is(aAvailability.value, true, "Should have available device initially"); + isnot(aAvailability, availability, "Should get different availability object for different request URL"); + }).catch(function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + }); +} + +function testSameObject() { + let sameUrlRequest = new PresentationRequest("https://example.com"); + return sameUrlRequest.getAvailability().then(function(aAvailability) { + is(aAvailability, availability, "Should get same availability object for same request URL"); + }).catch(function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + }); +} + +function testOnChangeEvent() { + return new Promise(function(aResolve, aReject) { + availability.onchange = function() { + availability.onchange = null; + is(availability.value, false, "Should have no available device after device removed"); + aResolve(); + } + gScript.sendAsyncMessage('trigger-device-remove'); + }); +} + +function testConsecutiveGetAvailability() { + let request = new PresentationRequest("https://example.org"); + let firstAvailabilityResolved = false; + return Promise.all([ + request.getAvailability().then(function() { + firstAvailabilityResolved = true; + }), + request.getAvailability().then(function() { + ok(firstAvailabilityResolved, "getAvailability() should be resolved in sequence"); + }) + ]).catch(function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + }); +} + +function testUnsupportedDeviceAvailability() { + return Promise.race([ + new Promise(function(aResolve, aReject) { + let request = new PresentationRequest("https://test.com"); + request.getAvailability().then(function(aAvailability) { + availability = aAvailability; + aAvailability.onchange = function() { + availability.onchange = null; + ok(false, "Should not get onchange event."); + teardown(); + } + }); + gScript.sendAsyncMessage('trigger-add-unsupport-url-device'); + }), + new Promise(function(aResolve, aReject) { + setTimeout(function() { + ok(true, "Should not get onchange event."); + availability.onchange = null; + gScript.sendAsyncMessage('trigger-remove-unsupported-device'); + aResolve(); + }, 3000); + }), + ]); +} + +function testMultipleAvailabilityURLs() { + let request1 = new PresentationRequest(["https://example.com", + "https://example1.com"]); + let request2 = new PresentationRequest(["https://example1.com", + "https://example2.com"]); + return Promise.all([ + request1.getAvailability().then(function(aAvailability) { + return new Promise(function(aResolve) { + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(true, "Should get onchange event."); + aResolve(); + }; + }); + }), + request2.getAvailability().then(function(aAvailability) { + return new Promise(function(aResolve) { + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(true, "Should get onchange event."); + aResolve(); + }; + }); + }), + new Promise(function(aResolve) { + gScript.sendAsyncMessage('trigger-add-multiple-devices'); + aResolve(); + }), + ]).then(new Promise(function(aResolve) { + gScript.sendAsyncMessage('trigger-remove-multiple-devices'); + aResolve(); + })); +} + +function testPartialSupportedDeviceAvailability() { + let request1 = new PresentationRequest(["https://supportedUrl.com"]); + let request2 = new PresentationRequest(["http://notSupportedUrl.com"]); + + return Promise.all([ + request1.getAvailability().then(function(aAvailability) { + return new Promise(function(aResolve) { + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(true, "Should get onchange event."); + aResolve(); + }; + }); + }), + Promise.race([ + request2.getAvailability().then(function(aAvailability) { + return new Promise(function(aResolve) { + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(false, "Should get onchange event."); + aResolve(); + }; + }); + }), + new Promise(function(aResolve) { + setTimeout(function() { + ok(true, "Should not get onchange event."); + availability.onchange = null; + aResolve(); + }, 3000); + }), + ]), + new Promise(function(aResolve) { + gScript.sendAsyncMessage('trigger-add-https-devices'); + aResolve(); + }), + ]).then(new Promise(function(aResolve) { + gScript.sendAsyncMessage('trigger-remove-https-devices'); + aResolve(); + })); +} + +function teardown() { + request = null; + availability = null; + gScript.sendAsyncMessage('teardown'); + gScript.destroy(); + SimpleTest.finish(); +} + +function runTests() { + ok(navigator.presentation, "navigator.presentation should be available."); + testSetup().then(testInitialUnavailable) + .then(testInitialAvailable) + .then(testSameObject) + .then(testOnChangeEvent) + .then(testConsecutiveGetAvailability) + .then(testMultipleAvailabilityURLs) + .then(testUnsupportedDeviceAvailability) + .then(testPartialSupportedDeviceAvailability) + .then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); +SpecialPowers.pushPermissions([ + {type: "presentation-device-manage", allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html b/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html new file mode 100644 index 000000000..89a51afb7 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html @@ -0,0 +1,245 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for data channel as session transport in Presentation API</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1148307">Test for data channel as session transport in Presentation API</a> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +"use strict"; + +SimpleTest.waitForExplicitFinish(); + +const loadingTimeoutPref = "presentation.receiver.loading.timeout"; + +var clientBuilder; +var serverBuilder; +var clientTransport; +var serverTransport; + +const clientMessage = "Client Message"; +const serverMessage = "Server Message"; + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; +const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +const { Services } = Cu.import("resource://gre/modules/Services.jsm"); + +var isClientReady = false; +var isServerReady = false; +var isClientClosed = false; +var isServerClosed = false; + +var gResolve; +var gReject; + +const clientCallback = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]), + notifyTransportReady: function () { + info("Client transport ready."); + + isClientReady = true; + if (isClientReady && isServerReady) { + gResolve(); + } + }, + notifyTransportClosed: function (aReason) { + info("Client transport is closed."); + + isClientClosed = true; + if (isClientClosed && isServerClosed) { + gResolve(); + } + }, + notifyData: function(aData) { + is(aData, serverMessage, "Client transport receives data."); + gResolve(); + }, +}; + +const serverCallback = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]), + notifyTransportReady: function () { + info("Server transport ready."); + + isServerReady = true; + if (isClientReady && isServerReady) { + gResolve(); + } + }, + notifyTransportClosed: function (aReason) { + info("Server transport is closed."); + + isServerClosed = true; + if (isClientClosed && isServerClosed) { + gResolve(); + } + }, + notifyData: function(aData) { + is(aData, clientMessage, "Server transport receives data."); + gResolve() + }, +}; + +const clientListener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]), + onSessionTransport: function(aTransport) { + info("Client Transport is built."); + clientTransport = aTransport; + clientTransport.callback = clientCallback; + }, + onError: function(aError) { + ok(false, "client's builder reports error " + aError); + }, + sendOffer: function(aOffer) { + setTimeout(()=>this._remoteBuilder.onOffer(aOffer), 0); + }, + sendAnswer: function(aAnswer) { + setTimeout(()=>this._remoteBuilder.onAnswer(aAnswer), 0); + }, + sendIceCandidate: function(aCandidate) { + setTimeout(()=>this._remoteBuilder.onIceCandidate(aCandidate), 0); + }, + disconnect: function(aReason) { + setTimeout(()=>this._localBuilder.notifyDisconnected(aReason), 0); + setTimeout(()=>this._remoteBuilder.notifyDisconnected(aReason), 0); + }, + set remoteBuilder(aRemoteBuilder) { + this._remoteBuilder = aRemoteBuilder; + }, + set localBuilder(aLocalBuilder) { + this._localBuilder = aLocalBuilder; + }, +} + +const serverListener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]), + onSessionTransport: function(aTransport) { + info("Server Transport is built."); + serverTransport = aTransport; + serverTransport.callback = serverCallback; + serverTransport.enableDataNotification(); + }, + onError: function(aError) { + ok(false, "server's builder reports error " + aError); + }, + sendOffer: function(aOffer) { + setTimeout(()=>this._remoteBuilder.onOffer(aOffer), 0); + }, + sendAnswer: function(aAnswer) { + setTimeout(()=>this._remoteBuilder.onAnswer(aAnswer), 0); + }, + sendIceCandidate: function(aCandidate) { + setTimeout(()=>this._remoteBuilder.onIceCandidate(aCandidate), 0); + }, + disconnect: function(aReason) { + setTimeout(()=>this._localBuilder.notifyDisconnected(aReason), 0); + setTimeout(()=>this._remoteBuilder.notifyDisconnected(aReason), 0); + }, + set remoteBuilder(aRemoteBuilder) { + this._remoteBuilder = aRemoteBuilder; + }, + set localBuilder(aLocalBuilder) { + this._localBuilder = aLocalBuilder; + }, +} + +function testBuilder() { + return new Promise(function(aResolve, aReject) { + gResolve = aResolve; + gReject = aReject; + + clientBuilder = Cc["@mozilla.org/presentation/datachanneltransportbuilder;1"] + .createInstance(Ci.nsIPresentationDataChannelSessionTransportBuilder); + serverBuilder = Cc["@mozilla.org/presentation/datachanneltransportbuilder;1"] + .createInstance(Ci.nsIPresentationDataChannelSessionTransportBuilder); + + clientListener.localBuilder = clientBuilder; + clientListener.remoteBuilder = serverBuilder; + serverListener.localBuilder = serverBuilder; + serverListener.remoteBuilder = clientBuilder; + + clientBuilder + .buildDataChannelTransport(Ci.nsIPresentationService.ROLE_CONTROLLER, + window, + clientListener); + + serverBuilder + .buildDataChannelTransport(Ci.nsIPresentationService.ROLE_RECEIVER, + window, + serverListener); + }); +} + +function testClientSendMessage() { + return new Promise(function(aResolve, aReject) { + info("client sends message"); + gResolve = aResolve; + gReject = aReject; + + clientTransport.send(clientMessage); + }); +} + +function testServerSendMessage() { + return new Promise(function(aResolve, aReject) { + info("server sends message"); + gResolve = aResolve; + gReject = aReject; + + serverTransport.send(serverMessage); + setTimeout(()=>clientTransport.enableDataNotification(), 0); + }); +} + +function testCloseSessionTransport() { + return new Promise(function(aResolve, aReject) { + info("close session transport"); + gResolve = aResolve; + gReject = aReject; + + serverTransport.close(Cr.NS_OK); + }); +} + +function finish() { + info("test finished, teardown"); + Services.prefs.clearUserPref(loadingTimeoutPref); + + SimpleTest.finish(); +} + +function error(aError) { + ok(false, "report Error " + aError.name + ":" + aError.message); + gReject(); +} + +function runTests() { + Services.prefs.setIntPref(loadingTimeoutPref, 30000); + + testBuilder() + .then(testClientSendMessage) + .then(testServerSendMessage) + .then(testCloseSessionTransport) + .then(finish) + .catch(error); + +} + +window.addEventListener("load", function() { + runTests(); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html b/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html new file mode 100644 index 000000000..a42489bdb --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html @@ -0,0 +1,141 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationConnection API at receiver side</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1148307">Test for B2G PresentationConnection API at receiver side</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script type="application/javascript"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); + +var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage('trigger-device-add'); + + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', receiverUrl); + iframe.setAttribute("mozbrowser", "true"); + iframe.setAttribute("mozpresentation", receiverUrl); + + // This event is triggered when the iframe calls "alert". + iframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { + var message = evt.detail.message; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, "")); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, "")); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, "")); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + iframe.removeEventListener("mozbrowsershowmodalprompt", + receiverListener); + teardown(); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(iframe); + + aResolve(iframe); + }); + obs.notifyObservers(promise, 'setup-request-promise', null); + + gScript.addMessageListener('offer-received', function offerReceivedHandler() { + gScript.removeMessageListener('offer-received', offerReceivedHandler); + info("An offer is received."); + }); + + gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { + gScript.removeMessageListener('answer-sent', answerSentHandler); + ok(aIsValid, "A valid answer is sent."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); + }); + + gScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { + gScript.removeMessageListener('check-navigator', checknavigatorHandler); + ok(aSuccess, "buildDataChannel get correct window object"); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + is(aReason, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); + }); + + aResolve(); + }); +} + +function testIncomingSessionRequest() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { + gScript.removeMessageListener('receiver-launching', launchReceiverHandler); + info("Trying to launch receiver page."); + + ok(navigator.presentation, "navigator.presentation should be available in in-process pages."); + is(navigator.presentation.receiver, null, "Non-receiving in-process pages shouldn't get a presentation receiver instance."); + aResolve(); + }); + + gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup(). + then(testIncomingSessionRequest); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: "browser", allow: true, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", false], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html b/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html new file mode 100644 index 000000000..b289b0be6 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html @@ -0,0 +1,213 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationConnection API at receiver side (OOP)</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="PresentationSessionFrameScript.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1148307">Test B2G PresentationConnection API at receiver side (OOP)</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script type="application/javascript"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); +var nonReceiverUrl = SimpleTest.getTestFileURL('file_presentation_non_receiver.html'); + +var isReceiverFinished = false; +var isNonReceiverFinished = false; + +var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); +var receiverIframe; + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage('trigger-device-add'); + + // Create a receiver OOP iframe. + receiverIframe = document.createElement('iframe'); + receiverIframe.setAttribute('remote', 'true'); + receiverIframe.setAttribute('mozbrowser', 'true'); + receiverIframe.setAttribute('mozpresentation', receiverUrl); + receiverIframe.setAttribute('src', receiverUrl); + + // This event is triggered when the iframe calls "alert". + receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(aEvent) { + var message = aEvent.detail.message; + if (/^OK /.exec(message)) { + ok(true, "Message from iframe: " + message); + } else if (/^KO /.exec(message)) { + ok(false, "Message from iframe: " + message); + } else if (/^INFO /.exec(message)) { + info("Message from iframe: " + message); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, '')); + if (command.name == "trigger-incoming-message") { + var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); + mm.sendAsyncMessage('trigger-incoming-message', {"data": command.data}); + } else { + gScript.sendAsyncMessage(command.name, command.data); + } + } else if (/^DONE$/.exec(message)) { + ok(true, "Messaging from iframe complete."); + receiverIframe.removeEventListener('mozbrowsershowmodalprompt', receiverListener); + + isReceiverFinished = true; + + if (isNonReceiverFinished) { + teardown(); + } + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(receiverIframe); + receiverIframe.addEventListener("mozbrowserloadstart", function onLoadEnd() { + receiverIframe.removeEventListener("mozbrowserloadstart", onLoadEnd); + var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); + mm.loadFrameScript("data:,(" + loadPrivilegedScriptTest.toString() + ")();", false); + }); + + aResolve(receiverIframe); + }); + obs.notifyObservers(promise, 'setup-request-promise', null); + + // Create a non-receiver OOP iframe. + var nonReceiverIframe = document.createElement('iframe'); + nonReceiverIframe.setAttribute('remote', 'true'); + nonReceiverIframe.setAttribute('mozbrowser', 'true'); + nonReceiverIframe.setAttribute('src', nonReceiverUrl); + + // This event is triggered when the iframe calls "alert". + nonReceiverIframe.addEventListener('mozbrowsershowmodalprompt', function nonReceiverListener(aEvent) { + var message = aEvent.detail.message; + if (/^OK /.exec(message)) { + ok(true, "Message from iframe: " + message); + } else if (/^KO /.exec(message)) { + ok(false, "Message from iframe: " + message); + } else if (/^INFO /.exec(message)) { + info("Message from iframe: " + message); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, '')); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + ok(true, "Messaging from iframe complete."); + nonReceiverIframe.removeEventListener('mozbrowsershowmodalprompt', nonReceiverListener); + + isNonReceiverFinished = true; + + if (isReceiverFinished) { + teardown(); + } + } + }, false); + + document.body.appendChild(nonReceiverIframe); + + gScript.addMessageListener('offer-received', function offerReceivedHandler() { + gScript.removeMessageListener('offer-received', offerReceivedHandler); + info("An offer is received."); + }); + + gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { + gScript.removeMessageListener('answer-sent', answerSentHandler); + ok(aIsValid, "A valid answer is sent."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); + }); + + var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); + mm.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { + mm.removeMessageListener('check-navigator', checknavigatorHandler); + ok(aSuccess.data.data, "buildDataChannel get correct window object"); + }); + + mm.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + mm.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + mm.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + mm.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + is(aReason.data.data, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); + }); + + aResolve(); + }); +} + +function testIncomingSessionRequest() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { + gScript.removeMessageListener('receiver-launching', launchReceiverHandler); + info("Trying to launch receiver page."); + + aResolve(); + }); + + gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); + }); +} + +var mmTeardownComplete = false; +var gScriptTeardownComplete = false; +function teardown() { + var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); + mm.addMessageListener('teardown-complete', function teardownCompleteHandler() { + mm.removeMessageListener('teardown-complete', teardownCompleteHandler); + mmTeardownComplete = true; + if (gScriptTeardownComplete) { + SimpleTest.finish(); + } + }); + + mm.sendAsyncMessage('teardown'); + + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + gScriptTeardownComplete = true; + if (mmTeardownComplete) { + SimpleTest.finish(); + } + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup(). + then(testIncomingSessionRequest); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: 'browser', allow: true, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", false], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["dom.ipc.browser_frames.oop_by_default", true], + ["presentation.receiver.loading.timeout", 5000000]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_sender.html b/dom/presentation/tests/mochitest/test_presentation_dc_sender.html new file mode 100644 index 000000000..97e252e84 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_dc_sender.html @@ -0,0 +1,291 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="PresentationSessionFrameScript.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1148307">Test for B2G Presentation API at sender side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var frameScript = SpecialPowers.isMainProcess() ? gScript : contentScript; +var request; +var connection; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + request = new PresentationRequest("http://example.com/"); + + request.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + }); + + frameScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { + frameScript.removeMessageListener('check-navigator', checknavigatorHandler); + ok(aSuccess, "buildDataChannel get correct window object"); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + }); + + frameScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + frameScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + }); + + frameScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + frameScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + var connectionFromEvent; + request.onconnectionavailable = function(aEvent) { + request.onconnectionavailable = null; + connectionFromEvent = aEvent.connection; + ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); + + if (connection) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + }; + + request.start().then( + function(aConnection) { + connection = aConnection; + ok(connection, "Connection should be available."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + + if (connectionFromEvent) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testSend() { + return new Promise(function(aResolve, aReject) { + const outgoingMessage = "test outgoing message"; + + frameScript.addMessageListener('message-sent', function messageSentHandler(aMessage) { + frameScript.removeMessageListener('message-sent', messageSentHandler); + is(aMessage, outgoingMessage, "The message is sent out."); + aResolve(); + }); + + connection.send(outgoingMessage); + }); +} + +function testIncomingMessage() { + return new Promise(function(aResolve, aReject) { + const incomingMessage = "test incoming message"; + + connection.addEventListener('message', function messageHandler(aEvent) { + connection.removeEventListener('message', messageHandler); + is(aEvent.data, incomingMessage, "An incoming message should be received."); + aResolve(); + }); + + frameScript.sendAsyncMessage('trigger-incoming-message', incomingMessage); + }); +} + +function testCloseConnection() { + return new Promise(function(aResolve, aReject) { + frameScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + frameScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + }); + + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Connection should be closed."); + aResolve(); + }; + + connection.close(); + }); +} + +function testReconnect() { + return new Promise(function(aResolve, aReject) { + info('--- testReconnect ---'); + gScript.addMessageListener('control-channel-established', function controlChannelEstablished() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablished); + gScript.sendAsyncMessage("trigger-control-channel-open"); + }); + + gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { + gScript.removeMessageListener('start-reconnect', startReconnectHandler); + is(url, "http://example.com/", "URLs should be the same.") + gScript.sendAsyncMessage('trigger-reconnected-acked', url); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + }); + + frameScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { + frameScript.removeMessageListener('check-navigator', checknavigatorHandler); + ok(aSuccess, "buildDataChannel get correct window object"); + }); + + request.reconnect(connection.id).then( + function(aConnection) { + ok(aConnection, "Connection should be available."); + ok(aConnection.id, "Connection ID should be set."); + is(aConnection.state, "connecting", "The initial state should be connecting."); + is(aConnection, connection, "The reconnected connection should be the same."); + + aConnection.onconnect = function() { + aConnection.onconnect = null; + is(aConnection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + info('teardown-complete'); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function testConstructRequestError() { + return Promise.all([ + // XXX: Bug 1305204 - uncomment when bug 1275746 is fixed again. + // new Promise(function(aResolve, aReject) { + // try { + // request = new PresentationRequest("\\\\\\"); + // } + // catch(e) { + // is(e.name, "SyntaxError", "Expect to get SyntaxError."); + // aResolve(); + // } + // }), + new Promise(function(aResolve, aReject) { + try { + request = new PresentationRequest([]); + } + catch(e) { + is(e.name, "NotSupportedError", "Expect to get NotSupportedError."); + aResolve(); + } + }), + ]); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + + testSetup(). + then(testStartConnection). + then(testSend). + then(testIncomingMessage). + then(testCloseConnection). + then(testReconnect). + then(testCloseConnection). + then(testConstructRequestError). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", true]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_device_info.html b/dom/presentation/tests/mochitest/test_presentation_device_info.html new file mode 100644 index 000000000..77253e41d --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_device_info.html @@ -0,0 +1,144 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G Presentation Device Info API</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1080474">Test for B2G Presentation Device Info API</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +SimpleTest.waitForExplicitFinish(); + +var testDevice = { + id: 'id', + name: 'name', + type: 'type', +}; + +var gUrl = SimpleTest.getTestFileURL('PresentationDeviceInfoChromeScript.js'); +var gScript = SpecialPowers.loadChromeScript(gUrl); + +function testSetup() { + return new Promise(function(resolve, reject) { + gScript.addMessageListener('setup-complete', function() { + resolve(); + }); + gScript.sendAsyncMessage('setup'); + }); +} + +function testForceDiscovery() { + info('test force discovery'); + return new Promise(function(resolve, reject) { + gScript.addMessageListener('force-discovery', function() { + ok(true, 'nsIPresentationDeviceProvider.forceDiscovery is invoked'); + resolve(); + }); + navigator.mozPresentationDeviceInfo.forceDiscovery(); + }); +} + +function testDeviceAdd() { + info('test device add'); + return new Promise(function(resolve, reject) { + navigator.mozPresentationDeviceInfo.addEventListener('devicechange', function deviceChangeHandler(e) { + navigator.mozPresentationDeviceInfo.removeEventListener('devicechange', deviceChangeHandler); + let detail = e.detail; + is(detail.type, 'add', 'expected update type'); + is(detail.deviceInfo.id, testDevice.id, 'expected device id'); + is(detail.deviceInfo.name, testDevice.name, 'expected device name'); + is(detail.deviceInfo.type, testDevice.type, 'expected device type'); + + navigator.mozPresentationDeviceInfo.getAll() + .then(function(devices) { + is(devices.length, 1, 'expected 1 available device'); + is(devices[0].id, testDevice.id, 'expected device id'); + is(devices[0].name, testDevice.name, 'expected device name'); + is(devices[0].type, testDevice.type, 'expected device type'); + resolve(); + }); + }); + gScript.sendAsyncMessage('trigger-device-add', testDevice); + }); +} + +function testDeviceUpdate() { + info('test device update'); + return new Promise(function(resolve, reject) { + testDevice.name = 'name-update'; + + navigator.mozPresentationDeviceInfo.addEventListener('devicechange', function deviceChangeHandler(e) { + navigator.mozPresentationDeviceInfo.removeEventListener('devicechange', deviceChangeHandler); + let detail = e.detail; + is(detail.type, 'update', 'expected update type'); + is(detail.deviceInfo.id, testDevice.id, 'expected device id'); + is(detail.deviceInfo.name, testDevice.name, 'expected device name'); + is(detail.deviceInfo.type, testDevice.type, 'expected device type'); + + navigator.mozPresentationDeviceInfo.getAll() + .then(function(devices) { + is(devices.length, 1, 'expected 1 available device'); + is(devices[0].id, testDevice.id, 'expected device id'); + is(devices[0].name, testDevice.name, 'expected device name'); + is(devices[0].type, testDevice.type, 'expected device type'); + resolve(); + }); + }); + gScript.sendAsyncMessage('trigger-device-update', testDevice); + }); +} + +function testDeviceRemove() { + info('test device remove'); + return new Promise(function(resolve, reject) { + navigator.mozPresentationDeviceInfo.addEventListener('devicechange', function deviceChangeHandler(e) { + navigator.mozPresentationDeviceInfo.removeEventListener('devicechange', deviceChangeHandler); + let detail = e.detail; + is(detail.type, 'remove', 'expected update type'); + is(detail.deviceInfo.id, testDevice.id, 'expected device id'); + is(detail.deviceInfo.name, testDevice.name, 'expected device name'); + is(detail.deviceInfo.type, testDevice.type, 'expected device type'); + + navigator.mozPresentationDeviceInfo.getAll() + .then(function(devices) { + is(devices.length, 0, 'expected 0 available device'); + resolve(); + }); + }); + gScript.sendAsyncMessage('trigger-device-remove'); + }); +} + +function runTests() { + testSetup() + .then(testForceDiscovery) + .then(testDeviceAdd) + .then(testDeviceUpdate) + .then(testDeviceRemove) + .then(function() { + info('test finished, teardown'); + gScript.sendAsyncMessage('teardown', ''); + gScript.destroy(); + SimpleTest.finish(); + }); +} + +window.addEventListener('load', function() { + SpecialPowers.pushPrefEnv({ + 'set': [ + ['dom.presentation.enabled', true], + ] + }, runTests); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_device_info_permission.html b/dom/presentation/tests/mochitest/test_presentation_device_info_permission.html new file mode 100644 index 000000000..c7f7ac96d --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_device_info_permission.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G Presentation Device Info API Permission</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1080474">Test for B2G Presentation Device Info API Permission</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +SimpleTest.waitForExplicitFinish(); + +function runTests() { + is(navigator.mozPresentationDeviceInfo, undefined, 'navigator.mozPresentationDeviceInfo is undefined'); + SimpleTest.finish(); +} + +window.addEventListener('load', function() { + SpecialPowers.pushPrefEnv({ + 'set': [ + ['dom.presentation.enabled', true], + ] + }, runTests); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_mixed_security_contexts.html b/dom/presentation/tests/mochitest/test_presentation_mixed_security_contexts.html new file mode 100644 index 000000000..31918a2c4 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_mixed_security_contexts.html @@ -0,0 +1,81 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test default request for B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268758">Test allow-presentation sandboxing flag</a> +<iframe id="iframe" src="https://example.com/tests/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html"></iframe> +<script type="application/javascript;version=1.8"> + +"use strict"; + +var iframe = document.getElementById("iframe"); +var readyToStart = false; +var testSetuped = false; + +function setup() { + SpecialPowers.addPermission("presentation", + true, { url: "https://example.com/tests/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html", + originAttributes: { + appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID, + inIsolatedMozBrowser: false }}); + + return new Promise(function(aResolve, aReject) { + addEventListener("message", function listener(event) { + var message = event.data; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, "")); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, "")); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, "")); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + if (command === "ready-to-start") { + readyToStart = true; + startTest(); + } + } else if (/^DONE$/.exec(message)) { + window.removeEventListener('message', listener); + SimpleTest.finish(); + } + }, false); + + testSetuped = true; + aResolve(); + }); +} + +iframe.onload = startTest(); + +function startTest() { + if (!(testSetuped && readyToStart)) { + return; + } + iframe.contentWindow.postMessage("start", "*"); +} + +function runTests() { + ok(navigator.presentation, "navigator.presentation should be available."); + setup().then(startTest); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: "presentation-device-manage", allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation.js b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation.js new file mode 100644 index 000000000..0647bff3a --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation.js @@ -0,0 +1,77 @@ +"use strict"; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("PresentationSessionChromeScript.js")); +var receiverUrl = SimpleTest.getTestFileURL("file_presentation_receiver_auxiliary_navigation.html"); + +var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage("trigger-device-add"); + + var iframe = document.createElement("iframe"); + iframe.setAttribute("mozbrowser", "true"); + iframe.setAttribute("mozpresentation", receiverUrl); + var oop = location.pathname.indexOf('_inproc') == -1; + iframe.setAttribute("remote", oop); + iframe.setAttribute("src", receiverUrl); + + // This event is triggered when the iframe calls "postMessage". + iframe.addEventListener("mozbrowsershowmodalprompt", function listener(aEvent) { + var message = aEvent.detail.message; + if (/^OK /.exec(message)) { + ok(true, "Message from iframe: " + message); + } else if (/^KO /.exec(message)) { + ok(false, "Message from iframe: " + message); + } else if (/^INFO /.exec(message)) { + info("Message from iframe: " + message); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + ok(true, "Messaging from iframe complete."); + iframe.removeEventListener("mozbrowsershowmodalprompt", listener); + + teardown(); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(iframe); + + aResolve(iframe); + }); + obs.notifyObservers(promise, "setup-request-promise", null); + + aResolve(); + }); +} + +function teardown() { + gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() { + gScript.removeMessageListener("teardown-complete", teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage("teardown"); +} + +function runTests() { + setup().then(); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: "presentation-device-manage", allow: false, context: document}, + {type: "browser", allow: true, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_inproc.html b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_inproc.html new file mode 100644 index 000000000..f873fa3da --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_inproc.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API when sender and receiver at the same side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268810"> + Test for receiver page with sandboxed auxiliary navigation browsing context flag.</a> + <script type="application/javascript;version=1.8" src="test_presentation_receiver_auxiliary_navigation.js"> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_oop.html b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_oop.html new file mode 100644 index 000000000..f873fa3da --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_oop.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API when sender and receiver at the same side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + <body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268810"> + Test for receiver page with sandboxed auxiliary navigation browsing context flag.</a> + <script type="application/javascript;version=1.8" src="test_presentation_receiver_auxiliary_navigation.js"> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_reconnect.html b/dom/presentation/tests/mochitest/test_presentation_reconnect.html new file mode 100644 index 000000000..079b7f5c5 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_reconnect.html @@ -0,0 +1,379 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="PresentationSessionFrameScript.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1197690">Test for Presentation API at sender side</a> +<iframe id="iframe" src="file_presentation_reconnect.html"></iframe> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var iframe = document.getElementById("iframe"); +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var frameScript = SpecialPowers.isMainProcess() ? gScript : contentScript; +var request; +var connection; +var commandHandler = {}; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + addEventListener("message", function listener(event) { + var message = event.data; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, "")); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, "")); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, "")); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + if (command.name in commandHandler) { + commandHandler[command.name](command); + } + } else if (/^DONE$/.exec(message)) { + window.removeEventListener('message', listener); + SimpleTest.finish(); + } + }, false); + + request = new PresentationRequest("http://example.com/"); + + request.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + info("The control channel is closed. " + aReason); + }); + + frameScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { + ok(aSuccess, "buildDataChannel get correct window object"); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + info("An answer is received."); + }); + + frameScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + info("Data transport channel is initialized."); + }); + + frameScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + info("Data notification is enabled for data transport channel."); + }); + + var connectionFromEvent; + request.onconnectionavailable = function(aEvent) { + request.onconnectionavailable = null; + connectionFromEvent = aEvent.connection; + ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); + + if (connection) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + }; + + request.start().then( + function(aConnection) { + connection = aConnection; + ok(connection, "Connection should be available."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + + if (connectionFromEvent) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testCloseConnection() { + return new Promise(function(aResolve, aReject) { + frameScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + frameScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + }); + + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Connection should be closed."); + aResolve(); + }; + + connection.close(); + }); +} + +function testReconnectAConnectedConnection() { + return new Promise(function(aResolve, aReject) { + info('--- testReconnectAConnectedConnection ---'); + ok(connection.state, "connected", "Make sure the state is connected."); + + request.reconnect(connection.id).then( + function(aConnection) { + ok(aConnection, "Connection should be available."); + is(aConnection.id, connection.id, "Connection ID should be the same."); + is(aConnection.state, "connected", "The state should be connected."); + is(aConnection, connection, "The connection should be the same."); + + aResolve(); + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testReconnectInvalidID() { + return new Promise(function(aResolve, aReject) { + info('--- testReconnectInvalidID ---'); + + request.reconnect("dummyID").then( + function(aConnection) { + ok(false, "Unexpected success."); + teardown(); + aReject(); + }, + function(aError) { + is(aError.name, "NotFoundError", "Should get NotFoundError."); + aResolve(); + } + ); + }); +} + +function testReconnectInvalidURL() { + return new Promise(function(aResolve, aReject) { + info('--- testReconnectInvalidURL ---'); + + var request1 = new PresentationRequest("http://invalidURL"); + request1.reconnect(connection.id).then( + function(aConnection) { + ok(false, "Unexpected success."); + teardown(); + aReject(); + }, + function(aError) { + is(aError.name, "NotFoundError", "Should get NotFoundError."); + aResolve(); + } + ); + }); +} + +function testReconnectIframeConnectedConnection() { + info('--- testReconnectIframeConnectedConnection ---'); + gScript.sendAsyncMessage('save-control-channel-listener'); + return Promise.all([ + new Promise(function(aResolve, aReject) { + commandHandler["connection-connected"] = function(command) { + gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { + gScript.removeMessageListener('start-reconnect', startReconnectHandler); + gScript.sendAsyncMessage('trigger-reconnected-acked', url); + }); + + var request1 = new PresentationRequest("http://example1.com"); + request1.reconnect(command.id).then( + function(aConnection) { + is(aConnection.state, "connecting", "The state should be connecting."); + aConnection.onclose = function() { + delete commandHandler["connection-connected"]; + gScript.sendAsyncMessage('restore-control-channel-listener'); + aResolve(); + }; + aConnection.close(); + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }; + iframe.contentWindow.postMessage("startConnection", "*"); + }), + new Promise(function(aResolve, aReject) { + commandHandler["notify-connection-closed"] = function(command) { + delete commandHandler["notify-connection-closed"]; + aResolve(); + }; + }), + ]); +} + +function testReconnectIframeClosedConnection() { + return new Promise(function(aResolve, aReject) { + info('--- testReconnectIframeClosedConnection ---'); + gScript.sendAsyncMessage('save-control-channel-listener'); + commandHandler["connection-closed"] = function(command) { + gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { + gScript.removeMessageListener('start-reconnect', startReconnectHandler); + gScript.sendAsyncMessage('trigger-reconnected-acked', url); + }); + + var request1 = new PresentationRequest("http://example1.com"); + request1.reconnect(command.id).then( + function(aConnection) { + aConnection.onconnect = function() { + aConnection.onconnect = null; + is(aConnection.state, "connected", "The connection should be connected."); + aConnection.onclose = function() { + aConnection.onclose = null; + ok(true, "The connection is closed."); + delete commandHandler["connection-closed"]; + aResolve(); + }; + aConnection.close(); + gScript.sendAsyncMessage('restore-control-channel-listener'); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }; + iframe.contentWindow.postMessage("closeConnection", "*"); + }); +} + +function testReconnect() { + return new Promise(function(aResolve, aReject) { + info('--- testReconnect ---'); + gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { + gScript.removeMessageListener('start-reconnect', startReconnectHandler); + is(url, "http://example.com/", "URLs should be the same."); + gScript.sendAsyncMessage('trigger-reconnected-acked', url); + }); + + request.reconnect(connection.id).then( + function(aConnection) { + ok(aConnection, "Connection should be available."); + ok(aConnection.id, "Connection ID should be set."); + is(aConnection.state, "connecting", "The initial state should be connecting."); + is(aConnection, connection, "The reconnected connection should be the same."); + + aConnection.onconnect = function() { + aConnection.onconnect = null; + is(aConnection.state, "connected", "Connection should be connected."); + + const incomingMessage = "test incoming message"; + aConnection.addEventListener('message', function messageHandler(aEvent) { + aConnection.removeEventListener('message', messageHandler); + is(aEvent.data, incomingMessage, "An incoming message should be received."); + aResolve(); + }); + + frameScript.sendAsyncMessage('trigger-incoming-message', incomingMessage); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + info('teardown-complete'); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + + testSetup(). + then(testStartConnection). + then(testReconnectInvalidID). + then(testReconnectInvalidURL). + then(testReconnectAConnectedConnection). + then(testReconnectIframeConnectedConnection). + then(testReconnectIframeClosedConnection). + then(testCloseConnection). + then(testReconnect). + then(testCloseConnection). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", true]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_sandboxed_presentation.html b/dom/presentation/tests/mochitest/test_presentation_sandboxed_presentation.html new file mode 100644 index 000000000..dc17209c5 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_sandboxed_presentation.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test default request for B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268758">Test allow-presentation sandboxing flag</a> +<iframe sandbox="allow-popups allow-scripts allow-same-origin" id="iframe" src="file_presentation_sandboxed_presentation.html"></iframe> +<script type="application/javascript;version=1.8"> + +"use strict"; + +var iframe = document.getElementById("iframe"); +var readyToStart = false; +var testSetuped = false; +function setup() { + return new Promise(function(aResolve, aReject) { + addEventListener("message", function listener(event) { + var message = event.data; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, "")); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, "")); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, "")); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + if (command === "ready-to-start") { + readyToStart = true; + startTest(); + } + } else if (/^DONE$/.exec(message)) { + window.removeEventListener('message', listener); + SimpleTest.finish(); + } + }, false); + + testSetuped = true; + aResolve(); + }); +} + +iframe.onload = startTest(); + +function startTest() { + if (!(testSetuped && readyToStart)) { + return; + } + iframe.contentWindow.postMessage("start", "*"); +} + +function runTests() { + ok(navigator.presentation, "navigator.presentation should be available."); + setup().then(startTest); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: "presentation-device-manage", allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", false], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_on_terminate_request.html b/dom/presentation/tests/mochitest/test_presentation_sender_on_terminate_request.html new file mode 100644 index 000000000..d0c8af0ad --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_sender_on_terminate_request.html @@ -0,0 +1,187 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test onTerminateRequest at sender side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1276378">Test onTerminateRequest at sender side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var request; +var connection; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + request = new PresentationRequest("http://example.com"); + + request.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + }); + + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + var connectionFromEvent; + request.onconnectionavailable = function(aEvent) { + request.onconnectionavailable = null; + connectionFromEvent = aEvent.connection; + ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); + + if (connection) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + }; + + request.start().then( + function(aConnection) { + connection = aConnection; + ok(connection, "Connection should be available."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + + if (connectionFromEvent) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testOnTerminateRequest() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + }); + + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + }); + + connection.onterminate = function() { + connection.onterminate = null; + is(connection.state, "terminated", "Connection should be closed."); + aResolve(); + }; + + gScript.sendAsyncMessage('trigger-incoming-terminate-request'); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + + testSetup(). + then(testStartConnection). + then(testOnTerminateRequest). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", false], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html b/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html new file mode 100644 index 000000000..27b17bb32 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html @@ -0,0 +1,173 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test startWithDevice for B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1239242">Test startWithDevice for B2G Presentation API at sender side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var request; +var connection; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + request = new PresentationRequest("https://example.com"); + + request.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnectionWithDevice() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + ok(false, "Device prompt should not be triggered."); + teardown(); + aReject(); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + }); + + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + var connectionFromEvent; + request.onconnectionavailable = function(aEvent) { + request.onconnectionavailable = null; + connectionFromEvent = aEvent.connection; + ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); + + if (connection) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + }; + + request.startWithDevice('id').then( + function(aConnection) { + connection = aConnection; + ok(connection, "Connection should be available."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + + if (connectionFromEvent) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnectionWithDeviceNotFoundError() { + return new Promise(function(aResolve, aReject) { + request.startWithDevice('').then( + function(aConnection) { + ok(false, "Should not establish connection to an unknown device"); + teardown(); + aReject(); + }, + function(aError) { + is(aError.name, 'NotFoundError', "Expect NotFoundError occurred when establishing a connection"); + aResolve(); + } + ); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + + testSetup(). + then(testStartConnectionWithDevice). + then(testStartConnectionWithDeviceNotFoundError). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.test.enabled", true], + ["dom.presentation.test.stage", 0]]}, + runTests); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html new file mode 100644 index 000000000..f26184f0b --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html @@ -0,0 +1,137 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationConnection API at receiver side</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for B2G PresentationConnection API at receiver side</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script type="application/javascript"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); + +var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage('trigger-device-add'); + + var iframe = document.createElement('iframe'); + iframe.setAttribute('mozbrowser', 'true'); + iframe.setAttribute('mozpresentation', receiverUrl); + iframe.setAttribute('src', receiverUrl); + + // This event is triggered when the iframe calls "postMessage". + iframe.addEventListener('mozbrowsershowmodalprompt', function listener(aEvent) { + var message = aEvent.detail.message; + if (/^OK /.exec(message)) { + ok(true, "Message from iframe: " + message); + } else if (/^KO /.exec(message)) { + ok(false, "Message from iframe: " + message); + } else if (/^INFO /.exec(message)) { + info("Message from iframe: " + message); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, '')); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + ok(true, "Messaging from iframe complete."); + iframe.removeEventListener('mozbrowsershowmodalprompt', listener); + + teardown(); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(iframe); + + aResolve(iframe); + }); + obs.notifyObservers(promise, 'setup-request-promise', null); + + gScript.addMessageListener('offer-received', function offerReceivedHandler() { + gScript.removeMessageListener('offer-received', offerReceivedHandler); + info("An offer is received."); + }); + + gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { + gScript.removeMessageListener('answer-sent', answerSentHandler); + ok(aIsValid, "A valid answer is sent."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + is(aReason, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); + }); + + aResolve(); + }); +} + +function testIncomingSessionRequest() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { + gScript.removeMessageListener('receiver-launching', launchReceiverHandler); + info("Trying to launch receiver page."); + + ok(navigator.presentation, "navigator.presentation should be available in in-process pages."); + is(navigator.presentation.receiver, null, "Non-receiving in-process pages shouldn't get a presentation receiver instance."); + aResolve(); + }); + + gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup(). + then(testIncomingSessionRequest); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: 'browser', allow: true, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", false], + ["dom.presentation.receiver.enabled", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html new file mode 100644 index 000000000..0935aaaf9 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html @@ -0,0 +1,110 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for connection establishing errors of B2G Presentation API at receiver side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing errors of B2G Presentation API at receiver side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver_establish_connection_error.html'); + +var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage('trigger-device-add'); + + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', receiverUrl); + iframe.setAttribute("mozbrowser", "true"); + iframe.setAttribute("mozpresentation", receiverUrl); + + // This event is triggered when the iframe calls "alert". + iframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { + var message = evt.detail.message; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, "")); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, "")); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, "")); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, "")); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + iframe.removeEventListener("mozbrowsershowmodalprompt", + receiverListener); + teardown(); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(iframe); + + aResolve(iframe); + }); + obs.notifyObservers(promise, 'setup-request-promise', null); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, 0x80004004 /* NS_ERROR_ABORT */, "The control channel is closed abnormally."); + }); + + aResolve(); + }); +} + +function testIncomingSessionRequest() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { + gScript.removeMessageListener('receiver-launching', launchReceiverHandler); + info("Trying to launch receiver page."); + + aResolve(); + }); + + gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup(). + then(testIncomingSessionRequest); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: "browser", allow: true, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html new file mode 100644 index 000000000..1dc002644 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html @@ -0,0 +1,81 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for connection establishing timeout of B2G Presentation API at receiver side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing timeout of B2G Presentation API at receiver side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); + +var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage('trigger-device-add'); + + var promise = new Promise(function(aResolve, aReject) { + // In order to trigger timeout, do not resolve the promise. + }); + obs.notifyObservers(promise, 'setup-request-promise', null); + + aResolve(); + }); +} + +function testIncomingSessionRequestReceiverLaunchTimeout() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { + gScript.removeMessageListener('receiver-launching', launchReceiverHandler); + info("Trying to launch receiver page."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, 0x80530017 /* NS_ERROR_DOM_TIMEOUT_ERR */, "The control channel is closed due to timeout."); + aResolve(); + }); + + gScript.sendAsyncMessage('trigger-incoming-session-request', 'http://example.com'); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup(). + then(testIncomingSessionRequestReceiverLaunchTimeout). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false], + ["presentation.receiver.loading.timeout", 10]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type.js b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type.js new file mode 100644 index 000000000..d73f84cf8 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type.js @@ -0,0 +1,88 @@ +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_unknown_content_type.test'); + +var obs = SpecialPowers.Cc['@mozilla.org/observer-service;1'] + .getService(SpecialPowers.Ci.nsIObserverService); + +var receiverIframe; + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage('trigger-device-add'); + + receiverIframe = document.createElement('iframe'); + receiverIframe.setAttribute('mozbrowser', 'true'); + receiverIframe.setAttribute('mozpresentation', receiverUrl); + receiverIframe.setAttribute('src', receiverUrl); + var oop = location.pathname.indexOf('_inproc') == -1; + receiverIframe.setAttribute("remote", oop); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(receiverIframe); + + aResolve(receiverIframe); + }); + obs.notifyObservers(promise, 'setup-request-promise', null); + + aResolve(); + }); +} + +function testIncomingSessionRequestReceiverLaunchUnknownContentType() { + let promise = Promise.all([ + new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { + gScript.removeMessageListener('receiver-launching', launchReceiverHandler); + info('Trying to launch receiver page.'); + + receiverIframe.addEventListener('mozbrowserclose', function() { + ok(true, 'observe receiver window closed'); + aResolve(); + }); + }); + }), + new Promise(function(aResolve, aReject) { + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, 0x80530020 /* NS_ERROR_DOM_OPERATION_ERR */, 'The control channel is closed due to load failure.'); + aResolve(); + }); + }) + ]); + + gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); + return promise; +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup(). + then(testIncomingSessionRequestReceiverLaunchUnknownContentType). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: 'browser', allow: true, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [['dom.presentation.enabled', true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", true], + ['dom.presentation.session_transport.data_channel.enable', false], + ['dom.mozBrowserFramesEnabled', true], + ["network.disable.ipc.security", true], + ['dom.ipc.tabs.disabled', false]]}, + runTests); +}); diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html new file mode 100644 index 000000000..8ade1d72d --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for unknown content type of B2G Presentation API at receiver side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287717">Test for unknown content type of B2G Presentation API at receiver side</a> + <script type="application/javascript;version=1.8" src="test_presentation_tcp_receiver_establish_connection_unknown_content_type.js"> + </script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html new file mode 100644 index 000000000..b2d2d3c6e --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for unknown content type of B2G Presentation API at receiver side (OOP)</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287717">Test for unknown content type of B2G Presentation API at receiver side (OOP)</a> + <script type="application/javascript;version=1.8" src="test_presentation_tcp_receiver_establish_connection_unknown_content_type.js"> + </script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html new file mode 100644 index 000000000..bfbc7947a --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html @@ -0,0 +1,178 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G PresentationConnection API at receiver side (OOP)</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test B2G PresentationConnection API at receiver side (OOP)</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script type="application/javascript"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); +var nonReceiverUrl = SimpleTest.getTestFileURL('file_presentation_non_receiver.html'); + +var isReceiverFinished = false; +var isNonReceiverFinished = false; + +var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] + .getService(SpecialPowers.Ci.nsIObserverService); + +function setup() { + return new Promise(function(aResolve, aReject) { + gScript.sendAsyncMessage('trigger-device-add'); + + // Create a receiver OOP iframe. + var receiverIframe = document.createElement('iframe'); + receiverIframe.setAttribute('remote', 'true'); + receiverIframe.setAttribute('mozbrowser', 'true'); + receiverIframe.setAttribute('mozpresentation', receiverUrl); + receiverIframe.setAttribute('src', receiverUrl); + + // This event is triggered when the iframe calls "alert". + receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(aEvent) { + var message = aEvent.detail.message; + if (/^OK /.exec(message)) { + ok(true, "Message from iframe: " + message); + } else if (/^KO /.exec(message)) { + ok(false, "Message from iframe: " + message); + } else if (/^INFO /.exec(message)) { + info("Message from iframe: " + message); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, '')); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + ok(true, "Messaging from iframe complete."); + receiverIframe.removeEventListener('mozbrowsershowmodalprompt', receiverListener); + + isReceiverFinished = true; + + if (isNonReceiverFinished) { + teardown(); + } + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(receiverIframe); + + aResolve(receiverIframe); + }); + obs.notifyObservers(promise, 'setup-request-promise', null); + + // Create a non-receiver OOP iframe. + var nonReceiverIframe = document.createElement('iframe'); + nonReceiverIframe.setAttribute('remote', 'true'); + nonReceiverIframe.setAttribute('mozbrowser', 'true'); + nonReceiverIframe.setAttribute('src', nonReceiverUrl); + + // This event is triggered when the iframe calls "alert". + nonReceiverIframe.addEventListener('mozbrowsershowmodalprompt', function nonReceiverListener(aEvent) { + var message = aEvent.detail.message; + if (/^OK /.exec(message)) { + ok(true, "Message from iframe: " + message); + } else if (/^KO /.exec(message)) { + ok(false, "Message from iframe: " + message); + } else if (/^INFO /.exec(message)) { + info("Message from iframe: " + message); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, '')); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + ok(true, "Messaging from iframe complete."); + nonReceiverIframe.removeEventListener('mozbrowsershowmodalprompt', nonReceiverListener); + + isNonReceiverFinished = true; + + if (isReceiverFinished) { + teardown(); + } + } + }, false); + + document.body.appendChild(nonReceiverIframe); + + gScript.addMessageListener('offer-received', function offerReceivedHandler() { + gScript.removeMessageListener('offer-received', offerReceivedHandler); + info("An offer is received."); + }); + + gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { + gScript.removeMessageListener('answer-sent', answerSentHandler); + ok(aIsValid, "A valid answer is sent."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + is(aReason, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); + }); + + aResolve(); + }); +} + +function testIncomingSessionRequest() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { + gScript.removeMessageListener('receiver-launching', launchReceiverHandler); + info("Trying to launch receiver page."); + + aResolve(); + }); + + gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup(). + then(testIncomingSessionRequest); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: 'browser', allow: true, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", false], + ["dom.presentation.receiver.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["dom.ipc.browser_frames.oop_by_default", true]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html new file mode 100644 index 000000000..8df34c884 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html @@ -0,0 +1,260 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for B2G Presentation API at sender side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var request; +var connection; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + request = new PresentationRequest("https://example.com"); + + request.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + }); + + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + var connectionFromEvent; + request.onconnectionavailable = function(aEvent) { + request.onconnectionavailable = null; + connectionFromEvent = aEvent.connection; + ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); + + if (connection) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + }; + + request.start().then( + function(aConnection) { + connection = aConnection; + ok(connection, "Connection should be available."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + + if (connectionFromEvent) { + is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); + } + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testSend() { + return new Promise(function(aResolve, aReject) { + const outgoingMessage = "test outgoing message"; + + gScript.addMessageListener('message-sent', function messageSentHandler(aMessage) { + gScript.removeMessageListener('message-sent', messageSentHandler); + is(aMessage, outgoingMessage, "The message is sent out."); + aResolve(); + }); + + connection.send(outgoingMessage); + }); +} + +function testIncomingMessage() { + return new Promise(function(aResolve, aReject) { + const incomingMessage = "test incoming message"; + + connection.addEventListener('message', function messageHandler(aEvent) { + connection.removeEventListener('message', messageHandler); + is(aEvent.data, incomingMessage, "An incoming message should be received."); + aResolve(); + }); + + gScript.sendAsyncMessage('trigger-incoming-message', incomingMessage); + }); +} + +function testCloseConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + }); + + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Connection should be closed."); + aResolve(); + }; + + connection.close(); + }); +} + +function testReconnect() { + return new Promise(function(aResolve, aReject) { + info('--- testReconnect ---'); + gScript.addMessageListener('control-channel-established', function controlChannelEstablished() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablished); + gScript.sendAsyncMessage("trigger-control-channel-open"); + }); + + gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { + gScript.removeMessageListener('start-reconnect', startReconnectHandler); + is(url, "https://example.com/", "URLs should be the same.") + gScript.sendAsyncMessage('trigger-reconnected-acked', url); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler() { + gScript.removeMessageListener('offer-sent', offerSentHandler); + gScript.sendAsyncMessage('trigger-incoming-transport'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + }); + + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + request.reconnect(connection.id).then( + function(aConnection) { + ok(aConnection, "Connection should be available."); + ok(aConnection.id, "Connection ID should be set."); + is(aConnection.state, "connecting", "The initial state should be connecting."); + is(aConnection, connection, "The reconnected connection should be the same."); + + aConnection.onconnect = function() { + aConnection.onconnect = null; + is(aConnection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + + testSetup(). + then(testStartConnection). + then(testSend). + then(testIncomingMessage). + then(testCloseConnection). + then(testReconnect). + then(testCloseConnection). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html new file mode 100644 index 000000000..60247ec98 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html @@ -0,0 +1,151 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test default request for B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test default request for B2G Presentation API at sender side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var connection; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + navigator.presentation.defaultRequest = new PresentationRequest("https://example.com"); + + navigator.presentation.defaultRequest.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler() { + gScript.removeMessageListener('offer-sent', offerSentHandler); + info("An offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + }); + + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + is(navigator.presentation.receiver, undefined, "Sender shouldn't get a presentation receiver instance."); + + navigator.presentation.defaultRequest.onconnectionavailable = function(aEvent) { + navigator.presentation.defaultRequest.onconnectionavailable = null; + connection = aEvent.connection; + ok(connection, "|connectionavailable| event is fired with a connection."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }; + + // Simulate the UA triggers |start()| of the default request. + navigator.presentation.defaultRequest.start(); + }); +} + +function testCloseConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + }); + + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Connection should be closed."); + aResolve(); + }; + + connection.close(); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + ok(navigator.presentation, "navigator.presentation should be available."); + + testSetup(). + then(testStartConnection). + then(testCloseConnection). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", false], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html new file mode 100644 index 000000000..a95da104f --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html @@ -0,0 +1,160 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for disconnection of B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for disconnection of B2G Presentation API at sender side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var request; +var connection; + +function testSetup() { + return new Promise(function(aResolve, aReject) { + request = new PresentationRequest("http://example.com"); + + request.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + }); + + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + }); + + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-answer'); + }); + + gScript.addMessageListener('answer-received', function answerReceivedHandler() { + gScript.removeMessageListener('answer-received', answerReceivedHandler); + info("An answer is received."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + }); + + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + }); + + gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { + gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); + info("Data notification is enabled for data transport channel."); + }); + + request.start().then( + function(aConnection) { + connection = aConnection; + ok(connection, "Connection should be available."); + ok(connection.id, "Connection ID should be set."); + is(connection.state, "connecting", "The initial state should be connecting."); + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, "connected", "Connection should be connected."); + aResolve(); + }; + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testDisconnection() { + return new Promise(function(aResolve, aReject) { + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + }); + + connection.onclose = function() { + connection.onclose = null; + is(connection.state, "closed", "Connection should be closed."); + aResolve(); + }; + + gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_FAILURE); + }); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + + testSetup(). + then(testStartConnection). + then(testDisconnection). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html new file mode 100644 index 000000000..557ae71a6 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html @@ -0,0 +1,514 @@ +<!DOCTYPE HTML> +<html> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<head> + <meta charset="utf-8"> + <title>Test for connection establishing errors of B2G Presentation API at sender side</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing errors of B2G Presentation API at sender side</a> +<script type="application/javascript;version=1.8"> + +'use strict'; + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); +var request; + +function setup() { + return new Promise(function(aResolve, aReject) { + request = new PresentationRequest("http://example.com"); + + request.getAvailability().then( + function(aAvailability) { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, "Device should be available."); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }, + function(aError) { + ok(false, "Error occurred when getting availability: " + aError); + teardown(); + aReject(); + } + ); + }); +} + +function testStartConnectionCancelPrompt() { + info('--- testStartConnectionCancelPrompt ---'); + return Promise.all([ + new Promise((resolve) => { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR); + resolve(); + }); + }), + request.start().then( + function(aConnection) { + ok(false, "|start| shouldn't succeed in this case."); + }, + function(aError) { + is(aError.name, "NotAllowedError", "NotAllowedError is expected when the prompt is canceled."); + } + ), + ]); +} + +function testStartConnectionNoDevice() { + info('--- testStartConnectionNoDevice ---'); + return Promise.all([ + new Promise((resolve) => { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_FOUND_ERR); + resolve(); + }); + }), + request.start().then( + function(aConnection) { + ok(false, "|start| shouldn't succeed in this case."); + }, + function(aError) { + is(aError.name, "NotFoundError", "NotFoundError is expected when no available device."); + } + ), + ]); +} + +function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit() { + info('--- testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit ---'); + return Promise.all([ + + new Promise((resolve) => { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + is(aReason, SpecialPowers.Cr.NS_ERROR_FAILURE, "The control channel is closed with NS_ERROR_FAILURE"); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE); + resolve(); + }); + }), + + request.start().then( + function(aConnection) { + is(aConnection.state, "connecting", "The initial state should be connecting."); + return new Promise((resolve) => { + aConnection.onclose = function() { + aConnection.onclose = null; + is(aConnection.state, "closed", "Connection should be closed."); + resolve(); + }; + }); + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + } + ), + + ]); +} + +function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit() { + info('--- testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit ---'); + return Promise.all([ + + new Promise((resolve) => { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed with NS_OK"); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK); + resolve(); + }); + }), + + request.start().then( + function(aConnection) { + is(aConnection.state, "connecting", "The initial state should be connecting."); + return new Promise((resolve) => { + aConnection.onclose = function() { + aConnection.onclose = null; + is(aConnection.state, "closed", "Connection should be closed."); + resolve(); + }; + }); + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + } + ), + + ]); +} + +function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady() { + info('--- testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady ---'); + return Promise.all([ + + new Promise((resolve) => { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + is(aReason, SpecialPowers.Cr.NS_ERROR_ABORT, "The control channel is closed with NS_ERROR_ABORT"); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_ABORT); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + resolve(); + }); + }), + + request.start().then( + function(aConnection) { + is(aConnection.state, "connecting", "The initial state should be connecting."); + return new Promise((resolve) => { + aConnection.onclose = function() { + aConnection.onclose = null; + is(aConnection.state, "closed", "Connection should be closed."); + resolve(); + }; + }); + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + } + ), + + ]); +} + +function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady() { + info('--- testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady -- '); + return Promise.all([ + + new Promise((resolve) => { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed with NS_OK"); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + resolve(); + }); + }), + + request.start().then( + function(aConnection) { + is(aConnection.state, "connecting", "The initial state should be connecting."); + return new Promise((resolve) => { + aConnection.onclose = function() { + aConnection.onclose = null; + is(aConnection.state, "closed", "Connection should be closed."); + resolve(); + }; + }); + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + } + ), + + ]); +} + +function testStartConnectionUnexpectedDataTransportClose() { + info('--- testStartConnectionUnexpectedDataTransportClose ---'); + return Promise.all([ + + new Promise((resolve) => { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + gScript.removeMessageListener('device-prompt', devicePromptHandler); + info("Device prompt is triggered."); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); + info("A control channel is established."); + gScript.sendAsyncMessage('trigger-control-channel-open'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { + gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); + info("The control channel is opened."); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { + gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); + info("The control channel is closed. " + aReason); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { + gScript.removeMessageListener('offer-sent', offerSentHandler); + ok(aIsValid, "A valid offer is sent out."); + info("recv offer-sent."); + gScript.sendAsyncMessage('trigger-incoming-transport'); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { + gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); + info("Data transport channel is initialized."); + gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_UNEXPECTED); + resolve(); + }); + }), + + new Promise((resolve) => { + gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { + gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); + info("The data transport is closed. " + aReason); + resolve(); + }); + }), + + request.start().then( + function(aConnection) { + is(aConnection.state, "connecting", "The initial state should be connecting."); + return new Promise((resolve) => { + aConnection.onclose = function() { + aConnection.onclose = null; + is(aConnection.state, "closed", "Connection should be closed."); + resolve(); + }; + }); + }, + function(aError) { + ok(false, "Error occurred when establishing a connection: " + aError); + teardown(); + } + ), + + ]); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + ok(window.PresentationRequest, "PresentationRequest should be available."); + + setup(). + then(testStartConnectionCancelPrompt). + then(testStartConnectionNoDevice). + then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit). + then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit). + then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady). + then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady). + then(testStartConnectionUnexpectedDataTransportClose). + then(teardown); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, +], function() { + SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.session_transport.data_channel.enable", false]]}, + runTests); +}); + +</script> +</body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate.js b/dom/presentation/tests/mochitest/test_presentation_terminate.js new file mode 100644 index 000000000..8ebfd9d64 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_terminate.js @@ -0,0 +1,243 @@ +'use strict'; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); + +function debug(str) { + // info(str); +} + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_terminate.html'); +var request; +var connection; +var receiverIframe; + +function setup() { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + debug('Got message: device-prompt'); + gScript.removeMessageListener('device-prompt', devicePromptHandler); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { + debug('Got message: sender-launch'); + gScript.removeMessageListener('sender-launch', senderLaunchHandler); + is(url, receiverUrl, 'Receiver: should receive the same url'); + receiverIframe = document.createElement('iframe'); + receiverIframe.setAttribute('mozbrowser', 'true'); + receiverIframe.setAttribute('mozpresentation', receiverUrl); + var oop = location.pathname.indexOf('_inproc') == -1; + receiverIframe.setAttribute('remote', oop); + + receiverIframe.setAttribute('src', receiverUrl); + receiverIframe.addEventListener('mozbrowserloadend', function mozbrowserloadendHander() { + receiverIframe.removeEventListener('mozbrowserloadend', mozbrowserloadendHander); + info('Receiver loaded.'); + }); + + // This event is triggered when the iframe calls 'alert'. + receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(evt) { + var message = evt.detail.message; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, '')); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, '')); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, '')); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, '')); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + ok(true, 'Messaging from iframe complete.'); + receiverIframe.removeEventListener('mozbrowsershowmodalprompt', + receiverListener); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(receiverIframe); + aResolve(receiverIframe); + }); + + var obs = SpecialPowers.Cc['@mozilla.org/observer-service;1'] + .getService(SpecialPowers.Ci.nsIObserverService); + obs.notifyObservers(promise, 'setup-request-promise', null); + }); + + gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { + debug('Got message: promise-setup-ready'); + gScript.removeMessageListener('promise-setup-ready', + promiseSetupReadyHandler); + gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); + }); + + return Promise.resolve(); +} + +function testCreateRequest() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testCreateRequest ---'); + request = new PresentationRequest(receiverUrl); + request.getAvailability().then((aAvailability) => { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, 'Sender: Device should be available.'); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }).catch((aError) => { + ok(false, 'Sender: Error occurred when getting availability: ' + aError); + teardown(); + aReject(); + }); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + request.start().then((aConnection) => { + connection = aConnection; + ok(connection, 'Sender: Connection should be available.'); + ok(connection.id, 'Sender: Connection ID should be set.'); + is(connection.state, 'connecting', 'Sender: The initial state should be connecting.'); + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, 'connected', 'Connection should be connected.'); + aResolve(); + }; + + info('Sender: test terminate at connecting state'); + connection.onterminate = function() { + connection.onterminate = null; + ok(false, 'Should not be able to terminate at connecting state'); + aReject(); + } + connection.terminate(); + }).catch((aError) => { + ok(false, 'Sender: Error occurred when establishing a connection: ' + aError); + teardown(); + aReject(); + }); + }); +} + +function testConnectionTerminate() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testConnectionTerminate---'); + connection.onterminate = function() { + connection.onterminate = null; + is(connection.state, 'terminated', 'Sender: Connection should be terminated.'); + }; + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + gScript.addMessageListener('sender-terminate', function senderTerminateHandler() { + gScript.removeMessageListener('sender-terminate', + senderTerminateHandler); + + Promise.all([ + new Promise((resolve) => { + gScript.addMessageListener('device-disconnected', function deviceDisconnectedHandler() { + gScript.removeMessageListener('device-disconnected', deviceDisconnectedHandler); + ok(true, 'observe device disconnect'); + resolve(); + }); + }), + new Promise((resolve) => { + receiverIframe.addEventListener('mozbrowserclose', function() { + ok(true, 'observe receiver page closing'); + resolve(); + }); + }), + ]).then(aResolve); + + gScript.sendAsyncMessage('trigger-on-terminate-request'); + }); + gScript.addMessageListener('ready-to-terminate', function onReadyToTerminate() { + gScript.removeMessageListener('ready-to-terminate', onReadyToTerminate); + connection.terminate(); + + // test unexpected close right after terminate + connection.onclose = function() { + ok(false, 'close after terminate should do nothing'); + }; + connection.close(); + }); + }); +} + +function testSendAfterTerminate() { + return new Promise(function(aResolve, aReject) { + try { + connection.send('something'); + ok(false, 'PresentationConnection.send should be failed'); + } catch (e) { + is(e.name, 'InvalidStateError', 'Must throw InvalidStateError'); + } + aResolve(); + }); +} + +function testCloseAfterTerminate() { + return Promise.race([ + new Promise(function(aResolve, aReject) { + connection.onclose = function() { + connection.onclose = null; + ok(false, 'close at terminated state should do nothing'); + aResolve(); + }; + connection.close(); + }), + new Promise(function(aResolve, aReject) { + setTimeout(function() { + is(connection.state, 'terminated', 'Sender: Connection should be terminated.'); + aResolve(); + }, 3000); + }), + ]); +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + debug('Got message: teardown-complete'); + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup().then(testCreateRequest) + .then(testStartConnection) + .then(testConnectionTerminate) + .then(testSendAfterTerminate) + .then(testCloseAfterTerminate) + .then(teardown); +} + +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: 'browser', allow: true, context: document}, +], () => { + SpecialPowers.pushPrefEnv({ 'set': [['dom.presentation.enabled', true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", true], + ['dom.presentation.test.enabled', true], + ['dom.mozBrowserFramesEnabled', true], + ["network.disable.ipc.security", true], + ['dom.ipc.tabs.disabled', false], + ['dom.presentation.test.stage', 0]]}, + runTests); +}); diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error.js b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error.js new file mode 100644 index 000000000..a1d477aab --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error.js @@ -0,0 +1,197 @@ +'use strict'; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); + +function debug(str) { + // info(str); +} + +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); +var receiverUrl = SimpleTest.getTestFileURL('file_presentation_terminate_establish_connection_error.html'); +var request; +var connection; +var receiverIframe; + +function postMessageToIframe(aType) { + receiverIframe.src = receiverUrl + "#" + + encodeURIComponent(JSON.stringify({ type: aType })); +} + +function setup() { + gScript.addMessageListener('device-prompt', function devicePromptHandler() { + debug('Got message: device-prompt'); + gScript.removeMessageListener('device-prompt', devicePromptHandler); + gScript.sendAsyncMessage('trigger-device-prompt-select'); + }); + + gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { + gScript.removeMessageListener('control-channel-established', + controlChannelEstablishedHandler); + gScript.sendAsyncMessage('trigger-control-channel-open'); + }); + + gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { + debug('Got message: sender-launch'); + gScript.removeMessageListener('sender-launch', senderLaunchHandler); + is(url, receiverUrl, 'Receiver: should receive the same url'); + receiverIframe = document.createElement('iframe'); + receiverIframe.setAttribute('mozbrowser', 'true'); + receiverIframe.setAttribute('mozpresentation', receiverUrl); + var oop = location.pathname.indexOf('_inproc') == -1; + receiverIframe.setAttribute('remote', oop); + + receiverIframe.setAttribute('src', receiverUrl); + receiverIframe.addEventListener('mozbrowserloadend', function mozbrowserloadendHander() { + receiverIframe.removeEventListener('mozbrowserloadend', mozbrowserloadendHander); + info('Receiver loaded.'); + }); + + // This event is triggered when the iframe calls 'alert'. + receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(evt) { + var message = evt.detail.message; + if (/^OK /.exec(message)) { + ok(true, message.replace(/^OK /, '')); + } else if (/^KO /.exec(message)) { + ok(false, message.replace(/^KO /, '')); + } else if (/^INFO /.exec(message)) { + info(message.replace(/^INFO /, '')); + } else if (/^COMMAND /.exec(message)) { + var command = JSON.parse(message.replace(/^COMMAND /, '')); + gScript.sendAsyncMessage(command.name, command.data); + } else if (/^DONE$/.exec(message)) { + ok(true, 'Messaging from iframe complete.'); + receiverIframe.removeEventListener('mozbrowsershowmodalprompt', + receiverListener); + } + }, false); + + var promise = new Promise(function(aResolve, aReject) { + document.body.appendChild(receiverIframe); + aResolve(receiverIframe); + }); + + var obs = SpecialPowers.Cc['@mozilla.org/observer-service;1'] + .getService(SpecialPowers.Ci.nsIObserverService); + obs.notifyObservers(promise, 'setup-request-promise', null); + }); + + gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { + debug('Got message: promise-setup-ready'); + gScript.removeMessageListener('promise-setup-ready', + promiseSetupReadyHandler); + gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); + }); + + return Promise.resolve(); +} + +function testCreateRequest() { + return new Promise(function(aResolve, aReject) { + info('Sender: --- testCreateRequest ---'); + request = new PresentationRequest(receiverUrl); + request.getAvailability().then((aAvailability) => { + is(aAvailability.value, false, "Sender: should have no available device after setup"); + aAvailability.onchange = function() { + aAvailability.onchange = null; + ok(aAvailability.value, 'Sender: Device should be available.'); + aResolve(); + } + + gScript.sendAsyncMessage('trigger-device-add'); + }).catch((aError) => { + ok(false, 'Sender: Error occurred when getting availability: ' + aError); + teardown(); + aReject(); + }); + }); +} + +function testStartConnection() { + return new Promise(function(aResolve, aReject) { + request.start().then((aConnection) => { + connection = aConnection; + ok(connection, 'Sender: Connection should be available.'); + ok(connection.id, 'Sender: Connection ID should be set.'); + is(connection.state, 'connecting', 'Sender: The initial state should be connecting.'); + connection.onconnect = function() { + connection.onconnect = null; + is(connection.state, 'connected', 'Connection should be connected.'); + aResolve(); + }; + }).catch((aError) => { + ok(false, 'Sender: Error occurred when establishing a connection: ' + aError); + teardown(); + aReject(); + }); + }); +} + +function testConnectionTerminate() { + info('Sender: --- testConnectionTerminate---'); + let promise = Promise.all([ + new Promise(function(aResolve, aReject) { + connection.onclose = function() { + connection.onclose = null; + is(connection.state, 'closed', 'Sender: Connection should be closed.'); + aResolve(); + }; + }), + new Promise(function(aResolve, aReject) { + function deviceDisconnectedHandler() { + gScript.removeMessageListener('device-disconnected', deviceDisconnectedHandler); + ok(true, 'should not receive device disconnect'); + aResolve(); + } + + gScript.addMessageListener('device-disconnected', deviceDisconnectedHandler); + }), + new Promise(function(aResolve, aReject) { + receiverIframe.addEventListener('mozbrowserclose', function() { + ok(true, 'observe receiver page closing'); + aResolve(); + }); + }) + ]); + + gScript.addMessageListener('prepare-for-terminate', function prepareForTerminateHandler() { + debug('Got message: prepare-for-terminate'); + gScript.removeMessageListener('prepare-for-terminate', prepareForTerminateHandler); + gScript.sendAsyncMessage('trigger-control-channel-error'); + postMessageToIframe('ready-to-terminate'); + }); + + return promise; +} + +function teardown() { + gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { + debug('Got message: teardown-complete'); + gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); + gScript.destroy(); + SimpleTest.finish(); + }); + gScript.sendAsyncMessage('teardown'); +} + +function runTests() { + setup().then(testCreateRequest) + .then(testStartConnection) + .then(testConnectionTerminate) + .then(teardown); +} + +SpecialPowers.pushPermissions([ + {type: 'presentation-device-manage', allow: false, context: document}, + {type: 'browser', allow: true, context: document}, +], () => { + SpecialPowers.pushPrefEnv({ 'set': [['dom.presentation.enabled', true], + ["dom.presentation.controller.enabled", true], + ["dom.presentation.receiver.enabled", true], + ['dom.presentation.test.enabled', true], + ['dom.mozBrowserFramesEnabled', true], + ["network.disable.ipc.security", true], + ['dom.ipc.tabs.disabled', false], + ['dom.presentation.test.stage', 0]]}, + runTests); +}); diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_inproc.html b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_inproc.html new file mode 100644 index 000000000..ccf0767f1 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_inproc.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset='utf-8'> + <title>Test for control channel establish error during PresentationConnection.terminate()</title> + <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> + <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> + </head> + <body> + <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1289292'> + Test for constrol channel establish error during PresentationConnection.terminate()</a> + <script type='application/javascript;version=1.8' src='test_presentation_terminate_establish_connection_error.js'> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_oop.html b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_oop.html new file mode 100644 index 000000000..ccf0767f1 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_oop.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset='utf-8'> + <title>Test for control channel establish error during PresentationConnection.terminate()</title> + <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> + <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> + </head> + <body> + <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1289292'> + Test for constrol channel establish error during PresentationConnection.terminate()</a> + <script type='application/javascript;version=1.8' src='test_presentation_terminate_establish_connection_error.js'> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_inproc.html b/dom/presentation/tests/mochitest/test_presentation_terminate_inproc.html new file mode 100644 index 000000000..33bbcda57 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_terminate_inproc.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset='utf-8'> + <title>Test for PresentationConnection.terminate()</title> + <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> + <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> + </head> + <body> + <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1276378'> + Test for PresentationConnection.terminate()</a> + <script type='application/javascript;version=1.8' src='test_presentation_terminate.js'> + </script> + </body> +</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_oop.html b/dom/presentation/tests/mochitest/test_presentation_terminate_oop.html new file mode 100644 index 000000000..33bbcda57 --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_terminate_oop.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> +<html> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset='utf-8'> + <title>Test for PresentationConnection.terminate()</title> + <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> + <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> + </head> + <body> + <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1276378'> + Test for PresentationConnection.terminate()</a> + <script type='application/javascript;version=1.8' src='test_presentation_terminate.js'> + </script> + </body> +</html> |