mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1069230 - Presentation API implementation. Part 1 - WebIDL Bindings. r=smaug (180d2e23a) - Bug 1142770 - part 1 - Use telephony service creator for telephony IPC service. r=hsinyi (447da4e64) - Bug 1142770 - part 2 - disable telephonyservice and voicemailservice. r=fabrice (5e0592769) - Bug 1069230 - Presentation API implementation. Part 2 - Presentation service and listeners. r=smaug (4be1d0628) - Bug 1069230 - Presentation API implementation. Part 3 - IPC. r=smaug (10db1c229) - Bug 1069230 - Presentation API implementation. Part 4 - Establish session (sender) & available changes. r=smaug (d7b358f74) - Bug 1069230 - Presentation API implementation. Part 5 - Establish session (receiver). r=smaug (e60709725) - Bug 1020179 - ContentPermissionPrompt change in b2g. r=fabrice (7aab4449e) - Bug 1020179 - Test case for visibilitychange. r=fabrice (d404f25e6) - Bug 1069230 - Presentation API implementation. Part 6 - mozChromeEvent for app launch. r=fabrice r=smaug (17081096c) - Bug 1069230 - Presentation API implementation. Part 7 - Presentation session. r=smaug (29227f2cf) - Bug 1069230 - Presentation API implementation. Part 8 - Data transport channel. r=jdm (2eb3a49ca) - Bug 1069230 - Presentation API implementation. Part 9 - Tests. r=kikuo (3cb72b71e) - Bug 1162700 - Split the AppInfo into initial setting of the values and the further initialization. r=smaug (d0b8d1470)
This commit is contained in:
@@ -118,3 +118,6 @@ contract @mozilla.org/app-migrator;1 {7211ece0-b458-4635-9afc-f8d7f376ee95}
|
||||
component {4a300c26-e99b-4018-ab9b-c48cf9bc4de1} B2GPresentationDevicePrompt.js
|
||||
contract @mozilla.org/presentation-device/prompt;1 {4a300c26-e99b-4018-ab9b-c48cf9bc4de1}
|
||||
|
||||
# PresentationRequestUIGlue.js
|
||||
component {ccc8a839-0b64-422b-8a60-fb2af0e376d0} PresentationRequestUIGlue.js
|
||||
contract @mozilla.org/presentation/requestuiglue;1 {ccc8a839-0b64-422b-8a60-fb2af0e376d0}
|
||||
|
||||
@@ -292,50 +292,45 @@ ContentPermissionPrompt.prototype = {
|
||||
return !type.deny && (type.action == Ci.nsIPermissionManager.PROMPT_ACTION || type.options.length > 0) ;
|
||||
});
|
||||
|
||||
let frame = request.element;
|
||||
|
||||
if (!frame) {
|
||||
if (!request.element) {
|
||||
this.delegatePrompt(request, typesInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
frame = frame.wrappedJSObject;
|
||||
var cancelRequest = function() {
|
||||
frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange);
|
||||
request.requester.onVisibilityChange = null;
|
||||
request.cancel();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var onVisibilityChange = function(evt) {
|
||||
if (evt.detail.visible === true)
|
||||
return;
|
||||
|
||||
self.cancelPrompt(request, typesInfo);
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
// If the request was initiated from a hidden iframe
|
||||
// we don't forward it to content and cancel it right away
|
||||
let domRequest = frame.getVisible();
|
||||
domRequest.onsuccess = function gv_success(evt) {
|
||||
if (!evt.target.result) {
|
||||
cancelRequest();
|
||||
return;
|
||||
request.requester.getVisibility( {
|
||||
notifyVisibility: function(isVisible) {
|
||||
if (!isVisible) {
|
||||
cancelRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
// Monitor the frame visibility and cancel the request if the frame goes
|
||||
// away but the request is still here.
|
||||
request.requester.onVisibilityChange = {
|
||||
notifyVisibility: function(isVisible) {
|
||||
if (isVisible)
|
||||
return;
|
||||
|
||||
self.cancelPrompt(request, typesInfo);
|
||||
cancelRequest();
|
||||
}
|
||||
}
|
||||
|
||||
self.delegatePrompt(request, typesInfo, function onCallback() {
|
||||
request.requester.onVisibilityChange = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Monitor the frame visibility and cancel the request if the frame goes
|
||||
// away but the request is still here.
|
||||
frame.addEventListener("mozbrowservisibilitychange", onVisibilityChange);
|
||||
|
||||
self.delegatePrompt(request, typesInfo, function onCallback() {
|
||||
frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange);
|
||||
});
|
||||
};
|
||||
|
||||
// Something went wrong. Let's cancel the request just in case.
|
||||
domRequest.onerror = function gv_error() {
|
||||
cancelRequest();
|
||||
}
|
||||
},
|
||||
|
||||
cancelPrompt: function(request, typesInfo) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* 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 { interfaces: Ci, utils: Cu, classes: Cc } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
|
||||
"resource://gre/modules/SystemAppProxy.jsm");
|
||||
|
||||
function PresentationRequestUIGlue() {
|
||||
// This is to store the session ID / resolver binding.
|
||||
// An example of the object literal is shown below:
|
||||
//
|
||||
// {
|
||||
// "sessionId1" : resolver1,
|
||||
// ...
|
||||
// }
|
||||
this._resolvers = {};
|
||||
|
||||
// Listen to the result for the opened iframe from front-end.
|
||||
SystemAppProxy.addEventListener("mozPresentationContentEvent", aEvent => {
|
||||
let detail = aEvent.detail;
|
||||
|
||||
if (detail.type != "presentation-receiver-launched") {
|
||||
return;
|
||||
}
|
||||
|
||||
let sessionId = detail.sessionId;
|
||||
let resolver = this._resolvers[sessionId];
|
||||
if (!resolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete this._resolvers[sessionId];
|
||||
resolver(detail.frame);
|
||||
});
|
||||
}
|
||||
|
||||
PresentationRequestUIGlue.prototype = {
|
||||
|
||||
sendRequest: function(aUrl, aSessionId) {
|
||||
SystemAppProxy._sendCustomEvent("mozPresentationChromeEvent",
|
||||
{ type: "presentation-launch-receiver",
|
||||
url: aUrl,
|
||||
id: aSessionId });
|
||||
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
this._resolvers[aSessionId] = aResolve;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
classID: Components.ID("{ccc8a839-0b64-422b-8a60-fb2af0e376d0}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationRequestUIGlue]);
|
||||
@@ -23,6 +23,7 @@ EXTRA_COMPONENTS += [
|
||||
'OMAContentHandler.js',
|
||||
'PaymentGlue.js',
|
||||
'PaymentProviderStrategy.js',
|
||||
'PresentationRequestUIGlue.js',
|
||||
'ProcessGlobal.js',
|
||||
'SmsProtocolHandler.js',
|
||||
'SystemMessageGlue.js',
|
||||
|
||||
@@ -7,6 +7,7 @@ support-files =
|
||||
screenshot_helper.js
|
||||
systemapp_helper.js
|
||||
presentation_prompt_handler_chrome.js
|
||||
presentation_ui_glue_handler_chrome.js
|
||||
|
||||
[test_filepicker_path.html]
|
||||
[test_permission_deny.html]
|
||||
@@ -16,3 +17,5 @@ skip-if = true # Bug 1019572 - frequent timeouts
|
||||
[test_screenshot.html]
|
||||
[test_systemapp.html]
|
||||
[test_presentation_device_prompt.html]
|
||||
[test_permission_visibilitychange.html]
|
||||
[test_presentation_request_ui_glue.html]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* -*- 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;
|
||||
const { XPCOMUtils } = Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
const { SystemAppProxy } = Cu.import('resource://gre/modules/SystemAppProxy.jsm');
|
||||
|
||||
const glue = Cc["@mozilla.org/presentation/requestuiglue;1"]
|
||||
.createInstance(Ci.nsIPresentationRequestUIGlue);
|
||||
|
||||
SystemAppProxy.addEventListener('mozPresentationChromeEvent', function(aEvent) {
|
||||
if (!aEvent.detail || aEvent.detail.type !== 'presentation-launch-receiver') {
|
||||
return;
|
||||
}
|
||||
sendAsyncMessage('presentation-launch-receiver', aEvent.detail);
|
||||
});
|
||||
|
||||
addMessageListener('trigger-ui-glue', function(aData) {
|
||||
var promise = glue.sendRequest(aData.url, aData.sessionId);
|
||||
promise.then(function(aFrame){
|
||||
sendAsyncMessage('iframe-resolved', aFrame);
|
||||
});
|
||||
});
|
||||
|
||||
addMessageListener('trigger-presentation-content-event', function(aData) {
|
||||
var detail = {
|
||||
type: 'presentation-receiver-launched',
|
||||
sessionId: aData.sessionId,
|
||||
frame: aData.frame
|
||||
};
|
||||
SystemAppProxy._sendCustomEvent('mozPresentationContentEvent', detail);
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=951997
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Permission Prompt Test</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=1020179">Permission prompt visibilitychange test</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
"use strict";
|
||||
|
||||
var gUrl = SimpleTest.getTestFileURL("permission_handler_chrome.js");
|
||||
var gScript = SpecialPowers.loadChromeScript(gUrl);
|
||||
|
||||
function testDone() {
|
||||
gScript.sendAsyncMessage("teardown", "");
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
alert("setVisible::true");
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
function (pos) {
|
||||
ok(false, "unexpected success, permission request should be canceled");
|
||||
testDone();
|
||||
}, function (err) {
|
||||
ok(true, "success, permission request is canceled");
|
||||
testDone();
|
||||
});
|
||||
}
|
||||
|
||||
gScript.addMessageListener("permission-request", function (detail) {
|
||||
info("got permission-request!!!!\n");
|
||||
alert("setVisible::false");
|
||||
});
|
||||
|
||||
// Add permissions to this app. We use ALLOW_ACTION here. The ContentPermissionPrompt
|
||||
// should prompt for permission, not allow it without prompt.
|
||||
SpecialPowers.pushPrefEnv({"set": [["media.navigator.permission.disabled", false]]},
|
||||
function() {
|
||||
SpecialPowers.addPermission("geolocation",
|
||||
SpecialPowers.Ci.nsIPermissionManager.PROMPT_ACTION, document);
|
||||
runTest();
|
||||
});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
<!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 Presentation Device Selection</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=">Test for Presentation UI Glue</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('presentation_ui_glue_handler_chrome.js'));
|
||||
|
||||
var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIObserverService);
|
||||
|
||||
var url = 'http://example.com';
|
||||
var sessionId = 'sessionId';
|
||||
|
||||
function testLaunchReceiver() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('presentation-launch-receiver', function launchReceiverHandler(aDetail) {
|
||||
gScript.removeMessageListener('presentation-launch-receiver', launchReceiverHandler);
|
||||
ok(true, "A presentation-launch-receiver mozPresentationChromeEvent should be received.");
|
||||
is(aDetail.url, url, "Url should be the same.");
|
||||
is(aDetail.id, sessionId, "Session ID should be the same.");
|
||||
|
||||
aResolve();
|
||||
});
|
||||
|
||||
gScript.sendAsyncMessage('trigger-ui-glue',
|
||||
{ url: url,
|
||||
sessionId : sessionId });
|
||||
});
|
||||
}
|
||||
|
||||
function testReceiverLaunched() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('iframe-resolved', function iframeResolvedHandler(aFrame) {
|
||||
gScript.removeMessageListener('iframe-resolved', iframeResolvedHandler);
|
||||
ok(true, "The promise should be resolved.");
|
||||
|
||||
aResolve();
|
||||
});
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('remote', 'true');
|
||||
iframe.setAttribute('mozbrowser', 'true');
|
||||
iframe.setAttribute('src', 'http://example.com');
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
gScript.sendAsyncMessage('trigger-presentation-content-event',
|
||||
{ sessionId : sessionId,
|
||||
frame: iframe });
|
||||
});
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
testLaunchReceiver()
|
||||
.then(testReceiverLaunched)
|
||||
.then(function() {
|
||||
info('test finished, teardown');
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('load', runTests);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -908,6 +908,7 @@ bin/libfreebl_32int64_3.so
|
||||
@RESPATH@/components/SystemMessageGlue.js
|
||||
@RESPATH@/components/B2GAppMigrator.js
|
||||
@RESPATH@/components/B2GPresentationDevicePrompt.js
|
||||
@RESPATH@/components/PresentationRequestUIGlue.js
|
||||
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
@RESPATH@/components/SimulatorScreen.js
|
||||
|
||||
@@ -560,7 +560,13 @@ this.PermissionsTable = { geolocation: {
|
||||
trusted: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
app: DENY_ACTION,
|
||||
trusted: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "mozilla/dom/InputPortManager.h"
|
||||
#include "mozilla/dom/MobileMessageManager.h"
|
||||
#include "mozilla/dom/Permissions.h"
|
||||
#include "mozilla/dom/Presentation.h"
|
||||
#include "mozilla/dom/ServiceWorkerContainer.h"
|
||||
#include "mozilla/dom/Telephony.h"
|
||||
#include "mozilla/dom/Voicemail.h"
|
||||
@@ -207,6 +208,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedResolveResults)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@@ -326,6 +328,10 @@ Navigator::Invalidate()
|
||||
mTimeManager = nullptr;
|
||||
}
|
||||
|
||||
if (mPresentation) {
|
||||
mPresentation = nullptr;
|
||||
}
|
||||
|
||||
mServiceWorkerContainer = nullptr;
|
||||
|
||||
if (mDeviceStorageAreaListener) {
|
||||
@@ -2692,5 +2698,19 @@ Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
|
||||
return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
|
||||
}
|
||||
|
||||
Presentation*
|
||||
Navigator::GetPresentation(ErrorResult& aRv)
|
||||
{
|
||||
if (!mPresentation) {
|
||||
if (!mWindow) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
mPresentation = Presentation::Create(mWindow);
|
||||
}
|
||||
|
||||
return mPresentation;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -94,6 +94,7 @@ class Voicemail;
|
||||
class TVManager;
|
||||
class InputPortManager;
|
||||
class DeviceStorageAreaListener;
|
||||
class Presentation;
|
||||
|
||||
namespace time {
|
||||
class TimeManager;
|
||||
@@ -265,6 +266,8 @@ public:
|
||||
system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
|
||||
#endif // MOZ_AUDIO_CHANNEL_MANAGER
|
||||
|
||||
Presentation* GetPresentation(ErrorResult& aRv);
|
||||
|
||||
bool SendBeacon(const nsAString& aUrl,
|
||||
const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData,
|
||||
ErrorResult& aRv);
|
||||
@@ -377,6 +380,7 @@ private:
|
||||
nsRefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsRefPtr<DeviceStorageAreaListener> mDeviceStorageAreaListener;
|
||||
nsRefPtr<Presentation> mPresentation;
|
||||
|
||||
// Hashtable for saving cached objects DoResolve created, so we don't create
|
||||
// the object twice if asked for it twice, whether due to use of "delete" or
|
||||
|
||||
@@ -683,6 +683,7 @@ GK_ATOM(onantennaavailablechange, "onantennaavailablechange")
|
||||
GK_ATOM(onAppCommand, "onAppCommand")
|
||||
GK_ATOM(onattributechanged, "onattributechanged")
|
||||
GK_ATOM(onaudioprocess, "onaudioprocess")
|
||||
GK_ATOM(onavailablechange, "onavailablechange")
|
||||
GK_ATOM(onbeforecopy, "onbeforecopy")
|
||||
GK_ATOM(onbeforecut, "onbeforecut")
|
||||
GK_ATOM(onbeforepaste, "onbeforepaste")
|
||||
|
||||
@@ -167,6 +167,8 @@
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "mozilla/dom/bluetooth/PBluetoothChild.h"
|
||||
#include "mozilla/dom/PFMRadioChild.h"
|
||||
#include "mozilla/dom/PPresentationChild.h"
|
||||
#include "mozilla/dom/PresentationIPCService.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
@@ -1375,6 +1377,37 @@ ContentChild::SendPBlobConstructor(PBlobChild* aActor,
|
||||
return PContentChild::SendPBlobConstructor(aActor, aParams);
|
||||
}
|
||||
|
||||
PPresentationChild*
|
||||
ContentChild::AllocPPresentationChild()
|
||||
{
|
||||
NS_NOTREACHED("We should never be manually allocating PPresentationChild actors");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPPresentationChild(PPresentationChild* aActor)
|
||||
{
|
||||
delete aActor;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
|
||||
const nsString& aSessionId)
|
||||
{
|
||||
nsCOMPtr<nsIDocShell> docShell =
|
||||
do_GetInterface(static_cast<TabChild*>(aIframe)->WebNavigation());
|
||||
NS_WARN_IF(!docShell);
|
||||
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
NS_WARN_IF(!service);
|
||||
|
||||
NS_WARN_IF(NS_FAILED(static_cast<PresentationIPCService*>(service.get())->MonitorResponderLoading(aSessionId, docShell)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PHalChild*
|
||||
ContentChild::AllocPHalChild()
|
||||
{
|
||||
@@ -2178,6 +2211,12 @@ ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
|
||||
mAppInfo.ID.Assign(ID);
|
||||
mAppInfo.vendor.Assign(vendor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvAppInit()
|
||||
{
|
||||
if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -262,6 +262,11 @@ public:
|
||||
virtual PFMRadioChild* AllocPFMRadioChild() override;
|
||||
virtual bool DeallocPFMRadioChild(PFMRadioChild* aActor) override;
|
||||
|
||||
virtual PPresentationChild* AllocPPresentationChild() override;
|
||||
virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override;
|
||||
virtual bool RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
|
||||
const nsString& aSessionId) override;
|
||||
|
||||
virtual PAsmJSCacheEntryChild* AllocPAsmJSCacheEntryChild(
|
||||
const asmjscache::OpenMode& aOpenMode,
|
||||
const asmjscache::WriteParams& aWriteParams,
|
||||
@@ -332,6 +337,7 @@ public:
|
||||
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID,
|
||||
const nsCString& name, const nsCString& UAName,
|
||||
const nsCString& ID, const nsCString& vendor) override;
|
||||
virtual bool RecvAppInit() override;
|
||||
|
||||
virtual bool RecvLastPrivateDocShellDestroyed() override;
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
#include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
|
||||
#include "mozilla/dom/mobilemessage/SmsParent.h"
|
||||
#include "mozilla/dom/power/PowerManagerService.h"
|
||||
#include "mozilla/dom/PresentationParent.h"
|
||||
#include "mozilla/dom/PPresentationParent.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/telephony/TelephonyParent.h"
|
||||
#include "mozilla/dom/time/DateCacheCleaner.h"
|
||||
@@ -2342,6 +2344,18 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
|
||||
// must come after the Open() call above.
|
||||
ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
|
||||
|
||||
if (gAppData) {
|
||||
nsCString version(gAppData->version);
|
||||
nsCString buildID(gAppData->buildID);
|
||||
nsCString name(gAppData->name);
|
||||
nsCString UAName(gAppData->UAName);
|
||||
nsCString ID(gAppData->ID);
|
||||
nsCString vendor(gAppData->vendor);
|
||||
|
||||
// Sending all information to content process.
|
||||
unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
|
||||
}
|
||||
|
||||
if (aSetupOffMainThreadCompositing) {
|
||||
// NB: internally, this will send an IPC message to the child
|
||||
// process to get it to create the CompositorChild. This
|
||||
@@ -2380,15 +2394,8 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
|
||||
}
|
||||
|
||||
if (gAppData) {
|
||||
nsCString version(gAppData->version);
|
||||
nsCString buildID(gAppData->buildID);
|
||||
nsCString name(gAppData->name);
|
||||
nsCString UAName(gAppData->UAName);
|
||||
nsCString ID(gAppData->ID);
|
||||
nsCString vendor(gAppData->vendor);
|
||||
|
||||
// Sending all information to content process.
|
||||
unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
|
||||
unused << SendAppInit();
|
||||
}
|
||||
|
||||
nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
|
||||
@@ -3738,6 +3745,27 @@ ContentParent::DeallocPFMRadioParent(PFMRadioParent* aActor)
|
||||
#endif
|
||||
}
|
||||
|
||||
PPresentationParent*
|
||||
ContentParent::AllocPPresentationParent()
|
||||
{
|
||||
nsRefPtr<PresentationParent> actor = new PresentationParent();
|
||||
return actor.forget().take();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPPresentationParent(PPresentationParent* aActor)
|
||||
{
|
||||
nsRefPtr<PresentationParent> actor =
|
||||
dont_AddRef(static_cast<PresentationParent*>(aActor));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor)
|
||||
{
|
||||
return static_cast<PresentationParent*>(aActor)->Init();
|
||||
}
|
||||
|
||||
asmjscache::PAsmJSCacheEntryParent*
|
||||
ContentParent::AllocPAsmJSCacheEntryParent(
|
||||
const asmjscache::OpenMode& aOpenMode,
|
||||
|
||||
@@ -642,6 +642,10 @@ private:
|
||||
virtual PFMRadioParent* AllocPFMRadioParent() override;
|
||||
virtual bool DeallocPFMRadioParent(PFMRadioParent* aActor) override;
|
||||
|
||||
virtual PPresentationParent* AllocPPresentationParent() override;
|
||||
virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override;
|
||||
virtual bool RecvPPresentationConstructor(PPresentationParent* aActor) override;
|
||||
|
||||
virtual PAsmJSCacheEntryParent* AllocPAsmJSCacheEntryParent(
|
||||
const asmjscache::OpenMode& aOpenMode,
|
||||
const asmjscache::WriteParams& aWriteParams,
|
||||
|
||||
@@ -49,6 +49,7 @@ include protocol PVoicemail;
|
||||
include protocol PJavaScript;
|
||||
include protocol PRemoteSpellcheckEngine;
|
||||
include protocol PWebrtcGlobal;
|
||||
include protocol PPresentation;
|
||||
include DOMTypes;
|
||||
include JavaScriptTypes;
|
||||
include InputStreamParams;
|
||||
@@ -447,6 +448,7 @@ prio(normal upto urgent) sync protocol PContent
|
||||
manages PJavaScript;
|
||||
manages PRemoteSpellcheckEngine;
|
||||
manages PWebrtcGlobal;
|
||||
manages PPresentation;
|
||||
|
||||
both:
|
||||
// Depending on exactly how the new browser is being created, it might be
|
||||
@@ -557,6 +559,7 @@ child:
|
||||
|
||||
AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName,
|
||||
nsCString ID, nsCString vendor);
|
||||
AppInit();
|
||||
|
||||
// Notify child that last-pb-context-exited notification was observed
|
||||
LastPrivateDocShellDestroyed();
|
||||
@@ -644,6 +647,13 @@ child:
|
||||
* Send gamepad status update to child.
|
||||
*/
|
||||
GamepadUpdate(GamepadChangeEvent aGamepadEvent);
|
||||
|
||||
/**
|
||||
* Notify the child that presentation receiver has been launched with the
|
||||
* correspondent iframe.
|
||||
*/
|
||||
async NotifyPresentationReceiverLaunched(PBrowser aIframe, nsString aSessionId);
|
||||
|
||||
parent:
|
||||
/**
|
||||
* Tell the content process some attributes of itself. This is
|
||||
@@ -763,6 +773,8 @@ parent:
|
||||
PAsmJSCacheEntry(OpenMode openMode, WriteParams write, Principal principal);
|
||||
|
||||
PWebrtcGlobal();
|
||||
|
||||
PPresentation();
|
||||
|
||||
// Services remoting
|
||||
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/dom/PresentationAvailableEvent.h"
|
||||
#include "mozilla/dom/PresentationBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIPresentationDeviceManager.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "Presentation.h"
|
||||
#include "PresentationCallbacks.h"
|
||||
#include "PresentationSession.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@@ -30,6 +35,7 @@ NS_IMPL_ADDREF_INHERITED(Presentation, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(Presentation, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Presentation)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPresentationListener)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
/* static */ already_AddRefed<Presentation>
|
||||
@@ -53,7 +59,39 @@ Presentation::~Presentation()
|
||||
bool
|
||||
Presentation::Init()
|
||||
{
|
||||
// TODO: Register listener for |mAvailable| changes.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = service->RegisterListener(this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
|
||||
do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
|
||||
if (NS_WARN_IF(!deviceManager)) {
|
||||
return false;
|
||||
}
|
||||
deviceManager->GetDeviceAvailable(&mAvailable);
|
||||
|
||||
// Check if a session instance is required now. The receiver requires a
|
||||
// session instance is ready at beginning because the web content may access
|
||||
// it right away; whereas the sender doesn't until |startSession| succeeds.
|
||||
nsAutoString sessionId;
|
||||
rv = service->GetExistentSessionIdAtLaunch(sessionId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
if (!sessionId.IsEmpty()) {
|
||||
mSession = PresentationSession::Create(GetOwner(), sessionId,
|
||||
PresentationSessionState::Disconnected);
|
||||
if (NS_WARN_IF(!mSession)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -62,7 +100,14 @@ void Presentation::Shutdown()
|
||||
{
|
||||
mSession = nullptr;
|
||||
|
||||
// TODO: Unregister listener for |mAvailable| changes.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = service->UnregisterListener(this);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
@@ -82,12 +127,63 @@ Presentation::StartSession(const nsAString& aUrl,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the origin.
|
||||
nsAutoString origin;
|
||||
nsresult rv = nsContentUtils::GetUTFOrigin(global->PrincipalOrNull(), origin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: Resolve/reject the promise.
|
||||
// Ensure there's something to select.
|
||||
if (NS_WARN_IF(!mAvailable)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Ensure the URL is not empty.
|
||||
if (NS_WARN_IF(aUrl.IsEmpty())) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Generate an ID if it's not assigned.
|
||||
nsAutoString id;
|
||||
if (aId.WasPassed()) {
|
||||
id = aId.Value();
|
||||
} else {
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
||||
do_GetService("@mozilla.org/uuid-generator;1");
|
||||
if(NS_WARN_IF(!uuidgen)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsID uuid;
|
||||
uuidgen->GenerateUUIDInPlace(&uuid);
|
||||
char buffer[NSID_LENGTH];
|
||||
uuid.ToProvidedString(buffer);
|
||||
CopyASCIItoUTF16(buffer, id);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if(NS_WARN_IF(!service)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationServiceCallback> callback =
|
||||
new PresentationRequesterCallback(GetOwner(), aUrl, id, promise);
|
||||
rv = service->StartSession(aUrl, id, origin, callback);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@@ -104,3 +200,21 @@ Presentation::CachedAvailable() const
|
||||
{
|
||||
return mAvailable;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Presentation::NotifyAvailableChange(bool aAvailable)
|
||||
{
|
||||
mAvailable = aAvailable;
|
||||
|
||||
PresentationAvailableEventInit init;
|
||||
init.mAvailable = mAvailable;
|
||||
nsRefPtr<PresentationAvailableEvent> event =
|
||||
PresentationAvailableEvent::Constructor(this,
|
||||
NS_LITERAL_STRING("availablechange"),
|
||||
init);
|
||||
event->SetTrusted(true);
|
||||
|
||||
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, event);
|
||||
return asyncDispatcher->PostDOMEvent();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_Presentation_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -16,11 +17,13 @@ class Promise;
|
||||
class PresentationSession;
|
||||
|
||||
class Presentation final : public DOMEventTargetHelper
|
||||
, public nsIPresentationListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Presentation,
|
||||
DOMEventTargetHelper)
|
||||
NS_DECL_NSIPRESENTATIONLISTENER
|
||||
|
||||
static already_AddRefed<Presentation> Create(nsPIDOMWindow* aWindow);
|
||||
virtual JSObject*
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsIWebProgress.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "PresentationCallbacks.h"
|
||||
#include "PresentationSession.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
/*
|
||||
* Implementation of PresentationRequesterCallback
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationRequesterCallback, nsIPresentationServiceCallback)
|
||||
|
||||
PresentationRequesterCallback::PresentationRequesterCallback(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
Promise* aPromise)
|
||||
: mWindow(aWindow)
|
||||
, mSessionId(aSessionId)
|
||||
, mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(mWindow);
|
||||
MOZ_ASSERT(mPromise);
|
||||
MOZ_ASSERT(!mSessionId.IsEmpty());
|
||||
}
|
||||
|
||||
PresentationRequesterCallback::~PresentationRequesterCallback()
|
||||
{
|
||||
}
|
||||
|
||||
// nsIPresentationServiceCallback
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterCallback::NotifySuccess()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// At the sender side, this function must get called after the transport
|
||||
// channel is ready. So we simply set the session state as connected.
|
||||
nsRefPtr<PresentationSession> session =
|
||||
PresentationSession::Create(mWindow, mSessionId, PresentationSessionState::Connected);
|
||||
if (!session) {
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mPromise->MaybeResolve(session);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterCallback::NotifyError(nsresult aError)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mPromise->MaybeReject(aError);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of PresentationRequesterCallback
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationResponderLoadingCallback,
|
||||
nsIWebProgressListener,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
PresentationResponderLoadingCallback::PresentationResponderLoadingCallback(const nsAString& aSessionId)
|
||||
: mSessionId(aSessionId)
|
||||
{
|
||||
}
|
||||
|
||||
PresentationResponderLoadingCallback::~PresentationResponderLoadingCallback()
|
||||
{
|
||||
if (mProgress) {
|
||||
mProgress->RemoveProgressListener(this);
|
||||
mProgress = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationResponderLoadingCallback::Init(nsIDocShell* aDocShell)
|
||||
{
|
||||
mProgress = do_GetInterface(aDocShell);
|
||||
if (NS_WARN_IF(!mProgress)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
|
||||
nsresult rv = aDocShell->GetBusyFlags(&busyFlags);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if ((busyFlags & nsIDocShell::BUSY_FLAGS_NONE) ||
|
||||
(busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) {
|
||||
// The docshell has finished loading or is receiving data (|STATE_TRANSFERRING|
|
||||
// has already been fired), so the page is ready for presentation use.
|
||||
return NotifyReceiverReady();
|
||||
}
|
||||
|
||||
// Start to listen to document state change event |STATE_TRANSFERRING|.
|
||||
return mProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationResponderLoadingCallback::NotifyReceiverReady()
|
||||
{
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return service->NotifyReceiverReady(mSessionId);
|
||||
}
|
||||
|
||||
// nsIWebProgressListener
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderLoadingCallback::OnStateChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
uint32_t aStateFlags,
|
||||
nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING) {
|
||||
mProgress->RemoveProgressListener(this);
|
||||
|
||||
return NotifyReceiverReady();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderLoadingCallback::OnProgressChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
int32_t aCurSelfProgress,
|
||||
int32_t aMaxSelfProgress,
|
||||
int32_t aCurTotalProgress,
|
||||
int32_t aMaxTotalProgress)
|
||||
{
|
||||
// Do nothing.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderLoadingCallback::OnLocationChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
nsIURI* aURI,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
// Do nothing.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderLoadingCallback::OnStatusChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
nsresult aStatus,
|
||||
const char16_t* aMessage)
|
||||
{
|
||||
// Do nothing.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderLoadingCallback::OnSecurityChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
uint32_t state)
|
||||
{
|
||||
// Do nothing.
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationCallbacks_h
|
||||
#define mozilla_dom_PresentationCallbacks_h
|
||||
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
class nsIDocShell;
|
||||
class nsIWebProgress;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
class PresentationRequesterCallback final : public nsIPresentationServiceCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONSERVICECALLBACK
|
||||
|
||||
PresentationRequesterCallback(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
Promise* aPromise);
|
||||
|
||||
private:
|
||||
~PresentationRequesterCallback();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsString mSessionId;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
class PresentationResponderLoadingCallback final : public nsIWebProgressListener
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIWEBPROGRESSLISTENER
|
||||
|
||||
explicit PresentationResponderLoadingCallback(const nsAString& aSessionId);
|
||||
|
||||
nsresult Init(nsIDocShell* aDocShell);
|
||||
|
||||
private:
|
||||
~PresentationResponderLoadingCallback();
|
||||
|
||||
nsresult NotifyReceiverReady();
|
||||
|
||||
nsString mSessionId;
|
||||
nsCOMPtr<nsIWebProgress> mProgress;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationCallbacks_h
|
||||
@@ -0,0 +1,555 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ipc/PresentationIPCService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozIApplication.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPresentationControlChannel.h"
|
||||
#include "nsIPresentationDeviceManager.h"
|
||||
#include "nsIPresentationDevicePrompt.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
#include "nsIPresentationRequestUIGlue.h"
|
||||
#include "nsIPresentationSessionRequest.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "PresentationService.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* Implementation of PresentationDeviceRequest
|
||||
*/
|
||||
|
||||
class PresentationDeviceRequest final : public nsIPresentationDeviceRequest
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONDEVICEREQUEST
|
||||
|
||||
PresentationDeviceRequest(const nsAString& aRequestUrl,
|
||||
const nsAString& aId,
|
||||
const nsAString& aOrigin);
|
||||
|
||||
private:
|
||||
virtual ~PresentationDeviceRequest();
|
||||
|
||||
nsString mRequestUrl;
|
||||
nsString mId;
|
||||
nsString mOrigin;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationDeviceRequest, nsIPresentationDeviceRequest)
|
||||
|
||||
PresentationDeviceRequest::PresentationDeviceRequest(const nsAString& aRequestUrl,
|
||||
const nsAString& aId,
|
||||
const nsAString& aOrigin)
|
||||
: mRequestUrl(aRequestUrl)
|
||||
, mId(aId)
|
||||
, mOrigin(aOrigin)
|
||||
{
|
||||
MOZ_ASSERT(!mRequestUrl.IsEmpty());
|
||||
MOZ_ASSERT(!mId.IsEmpty());
|
||||
MOZ_ASSERT(!mOrigin.IsEmpty());
|
||||
}
|
||||
|
||||
PresentationDeviceRequest::~PresentationDeviceRequest()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationDeviceRequest::GetOrigin(nsAString& aOrigin)
|
||||
{
|
||||
aOrigin = mOrigin;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationDeviceRequest::GetRequestURL(nsAString& aRequestUrl)
|
||||
{
|
||||
aRequestUrl = mRequestUrl;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationDeviceRequest::Select(nsIPresentationDevice* aDevice)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aDevice);
|
||||
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Update device in the session info.
|
||||
nsRefPtr<PresentationSessionInfo> info =
|
||||
static_cast<PresentationService*>(service.get())->GetSessionInfo(mId);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
info->SetDevice(aDevice);
|
||||
|
||||
// Establish a control channel. If we failed to do so, the callback is called
|
||||
// with an error message.
|
||||
nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
|
||||
nsresult rv = aDevice->EstablishControlChannel(mRequestUrl, mId, getter_AddRefs(ctrlChannel));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return info->ReplyError(NS_ERROR_DOM_NETWORK_ERR);
|
||||
}
|
||||
|
||||
// Initialize the session info with the control channel.
|
||||
rv = info->Init(ctrlChannel);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return info->ReplyError(NS_ERROR_DOM_NETWORK_ERR);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationDeviceRequest::Cancel()
|
||||
{
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsRefPtr<PresentationSessionInfo> info =
|
||||
static_cast<PresentationService*>(service.get())->GetSessionInfo(mId);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return info->ReplyError(NS_ERROR_DOM_PROP_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of PresentationService
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationService, nsIPresentationService, nsIObserver)
|
||||
|
||||
PresentationService::PresentationService()
|
||||
: mIsAvailable(false)
|
||||
{
|
||||
}
|
||||
|
||||
PresentationService::~PresentationService()
|
||||
{
|
||||
HandleShutdown();
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationService::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (NS_WARN_IF(!obs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
rv = obs->AddObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
rv = obs->AddObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
|
||||
do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
|
||||
if (NS_WARN_IF(!deviceManager)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rv = deviceManager->GetDeviceAvailable(&mIsAvailable);
|
||||
return !NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
HandleShutdown();
|
||||
return NS_OK;
|
||||
} else if (!strcmp(aTopic, PRESENTATION_DEVICE_CHANGE_TOPIC)) {
|
||||
return HandleDeviceChange();
|
||||
} else if (!strcmp(aTopic, PRESENTATION_SESSION_REQUEST_TOPIC)) {
|
||||
nsCOMPtr<nsIPresentationSessionRequest> request(do_QueryInterface(aSubject));
|
||||
if (NS_WARN_IF(!request)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return HandleSessionRequest(request);
|
||||
} else if (!strcmp(aTopic, "profile-after-change")) {
|
||||
// It's expected since we add and entry to |kLayoutCategories| in
|
||||
// |nsLayoutModule.cpp| to launch this service earlier.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "Unexpected topic for PresentationService");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationService::HandleShutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mListeners.Clear();
|
||||
mSessionInfo.Clear();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
obs->RemoveObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC);
|
||||
obs->RemoveObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationService::HandleDeviceChange()
|
||||
{
|
||||
nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
|
||||
do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
|
||||
if (NS_WARN_IF(!deviceManager)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
bool isAvailable;
|
||||
nsresult rv = deviceManager->GetDeviceAvailable(&isAvailable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (isAvailable != mIsAvailable) {
|
||||
mIsAvailable = isAvailable;
|
||||
NotifyAvailableChange(mIsAvailable);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationService::HandleSessionRequest(nsIPresentationSessionRequest* aRequest)
|
||||
{
|
||||
nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
|
||||
nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
|
||||
if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoString url;
|
||||
rv = aRequest->GetUrl(url);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoString sessionId;
|
||||
rv = aRequest->GetPresentationId(sessionId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationDevice> device;
|
||||
rv = aRequest->GetDevice(getter_AddRefs(device));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Verify the existence of the app if necessary.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), url);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_BAD_URI);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool isApp;
|
||||
rv = uri->SchemeIs("app", &isApp);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(isApp && !IsAppInstalled(uri))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Make sure the service is not handling another session request.
|
||||
if (NS_WARN_IF(!mRespondingSessionId.IsEmpty())) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Set |mRespondingSessionId| to indicate the service is handling a session
|
||||
// request. Then a session instance will be prepared while instantiating
|
||||
// |navigator.presentation| at receiver side. This variable will be reset when
|
||||
// registering the session listener.
|
||||
mRespondingSessionId = sessionId;
|
||||
|
||||
// Create or reuse session info.
|
||||
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(sessionId);
|
||||
if (NS_WARN_IF(info)) {
|
||||
// TODO Update here after session resumption becomes supported.
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
mRespondingSessionId.Truncate();
|
||||
return NS_ERROR_DOM_ABORT_ERR;
|
||||
}
|
||||
|
||||
info = new PresentationResponderInfo(url, sessionId, device);
|
||||
rv = info->Init(ctrlChannel);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
mRespondingSessionId.Truncate();
|
||||
return rv;
|
||||
}
|
||||
|
||||
mSessionInfo.Put(sessionId, info);
|
||||
|
||||
// Notify the receiver to launch.
|
||||
nsCOMPtr<nsIPresentationRequestUIGlue> glue =
|
||||
do_CreateInstance(PRESENTATION_REQUEST_UI_GLUE_CONTRACTID);
|
||||
if (NS_WARN_IF(!glue)) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
return info->ReplyError(NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
rv = glue->SendRequest(url, sessionId, getter_AddRefs(promise));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
|
||||
return info->ReplyError(rv);
|
||||
}
|
||||
nsCOMPtr<Promise> realPromise = do_QueryInterface(promise);
|
||||
static_cast<PresentationResponderInfo*>(info.get())->SetPromise(realPromise);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationService::NotifyAvailableChange(bool aIsAvailable)
|
||||
{
|
||||
nsTObserverArray<nsCOMPtr<nsIPresentationListener> >::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsCOMPtr<nsIPresentationListener> listener = iter.GetNext();
|
||||
NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(aIsAvailable)));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationService::IsAppInstalled(nsIURI* aUri)
|
||||
{
|
||||
nsAutoCString prePath;
|
||||
nsresult rv = aUri->GetPrePath(prePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString manifestUrl;
|
||||
AppendUTF8toUTF16(prePath, manifestUrl);
|
||||
manifestUrl.AppendLiteral("/manifest.webapp");
|
||||
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!appsService)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
appsService->GetAppByManifestURL(manifestUrl, getter_AddRefs(app));
|
||||
if (NS_WARN_IF(!app)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::StartSession(const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
const nsAString& aOrigin,
|
||||
nsIPresentationServiceCallback* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
|
||||
// Create session info and set the callback. The callback is called when the
|
||||
// request is finished.
|
||||
nsRefPtr<PresentationRequesterInfo> info =
|
||||
new PresentationRequesterInfo(aUrl, aSessionId, aCallback);
|
||||
mSessionInfo.Put(aSessionId, info);
|
||||
|
||||
// Pop up a prompt and ask user to select a device.
|
||||
nsCOMPtr<nsIPresentationDevicePrompt> prompt =
|
||||
do_GetService(PRESENTATION_DEVICE_PROMPT_CONTRACTID);
|
||||
if (NS_WARN_IF(!prompt)) {
|
||||
return info->ReplyError(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
nsCOMPtr<nsIPresentationDeviceRequest> request =
|
||||
new PresentationDeviceRequest(aUrl, aSessionId, aOrigin);
|
||||
nsresult rv = prompt->PromptDeviceSelection(request);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return info->ReplyError(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::SendSessionMessage(const nsAString& aSessionId,
|
||||
nsIInputStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aStream);
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
|
||||
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return info->Send(aStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::Terminate(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
|
||||
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return info->Close(NS_OK);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::RegisterListener(nsIPresentationListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_WARN_IF(mListeners.Contains(aListener))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mListeners.AppendElement(aListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::UnregisterListener(nsIPresentationListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mListeners.RemoveElement(aListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::RegisterSessionListener(const nsAString& aSessionId,
|
||||
nsIPresentationSessionListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
|
||||
if (mRespondingSessionId.Equals(aSessionId)) {
|
||||
mRespondingSessionId.Truncate();
|
||||
}
|
||||
|
||||
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
// Notify the listener of TERMINATED since no correspondent session info is
|
||||
// available possibly due to establishment failure. This would be useful at
|
||||
// the receiver side, since a presentation session is created at beginning
|
||||
// and here is the place to realize the underlying establishment fails.
|
||||
nsresult rv = aListener->NotifyStateChange(aSessionId,
|
||||
nsIPresentationSessionListener::STATE_TERMINATED);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return info->SetListener(aListener);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::UnregisterSessionListener(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
|
||||
if (info) {
|
||||
NS_WARN_IF(NS_FAILED(info->Close(NS_OK)));
|
||||
RemoveSessionInfo(aSessionId);
|
||||
return info->SetListener(nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::GetExistentSessionIdAtLaunch(nsAString& aSessionId)
|
||||
{
|
||||
aSessionId = mRespondingSessionId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::NotifyReceiverReady(const nsAString& aSessionId)
|
||||
{
|
||||
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return static_cast<PresentationResponderInfo*>(info.get())->NotifyResponderReady();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPresentationService>
|
||||
NS_CreatePresentationService()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIPresentationService> service;
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
service = new mozilla::dom::PresentationIPCService();
|
||||
} else {
|
||||
service = new PresentationService();
|
||||
if (NS_WARN_IF(!static_cast<PresentationService*>(service.get())->Init())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return service.forget();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationService_h
|
||||
#define mozilla_dom_PresentationService_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "PresentationSessionInfo.h"
|
||||
|
||||
class nsIPresentationSessionRequest;
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationService final : public nsIPresentationService
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIPRESENTATIONSERVICE
|
||||
|
||||
PresentationService();
|
||||
bool Init();
|
||||
|
||||
already_AddRefed<PresentationSessionInfo>
|
||||
GetSessionInfo(const nsAString& aSessionId)
|
||||
{
|
||||
nsRefPtr<PresentationSessionInfo> info;
|
||||
return mSessionInfo.Get(aSessionId, getter_AddRefs(info)) ?
|
||||
info.forget() : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
RemoveSessionInfo(const nsAString& aSessionId)
|
||||
{
|
||||
if (mRespondingSessionId.Equals(aSessionId)) {
|
||||
mRespondingSessionId.Truncate();
|
||||
}
|
||||
|
||||
mSessionInfo.Remove(aSessionId);
|
||||
}
|
||||
|
||||
private:
|
||||
~PresentationService();
|
||||
void HandleShutdown();
|
||||
nsresult HandleDeviceChange();
|
||||
nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
|
||||
void NotifyAvailableChange(bool aIsAvailable);
|
||||
bool IsAppInstalled(nsIURI* aUri);
|
||||
|
||||
bool mIsAvailable;
|
||||
nsString mRespondingSessionId;
|
||||
nsRefPtrHashtable<nsStringHashKey, PresentationSessionInfo> mSessionInfo;
|
||||
nsTObserverArray<nsCOMPtr<nsIPresentationListener>> mListeners;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationService_h
|
||||
@@ -4,7 +4,10 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIDOMMessageEvent.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "PresentationSession.h"
|
||||
@@ -25,6 +28,7 @@ NS_IMPL_ADDREF_INHERITED(PresentationSession, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(PresentationSession, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationSession)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
PresentationSession::PresentationSession(nsPIDOMWindow* aWindow,
|
||||
@@ -57,7 +61,16 @@ PresentationSession::Init()
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Register listener for session state changes.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if(NS_WARN_IF(!service)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = service->RegisterSessionListener(mId, this);
|
||||
if(NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -65,7 +78,14 @@ PresentationSession::Init()
|
||||
void
|
||||
PresentationSession::Shutdown()
|
||||
{
|
||||
// TODO: Unregister listener for session state changes.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = service->UnregisterSessionListener(mId);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
@@ -84,7 +104,6 @@ PresentationSession::GetId(nsAString& aId) const
|
||||
PresentationSessionState
|
||||
PresentationSession::State() const
|
||||
{
|
||||
// TODO: Dispatch event when the value of |mState| is changed.
|
||||
return mState;
|
||||
}
|
||||
|
||||
@@ -102,18 +121,28 @@ PresentationSession::Send(const nsAString& aData,
|
||||
nsCOMPtr<nsIStringInputStream> stream =
|
||||
do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
|
||||
if(NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 msgString(aData);
|
||||
rv = stream->SetData(msgString.BeginReading(), msgString.Length());
|
||||
if(NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Send the message to the stream.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if(NS_WARN_IF(!service)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
rv = service->SendSessionMessage(mId, stream);
|
||||
if(NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -124,5 +153,132 @@ PresentationSession::Close()
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Terminate the socket.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if(NS_WARN_IF(!service)) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_WARN_IF(NS_FAILED(service->Terminate(mId)));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSession::NotifyStateChange(const nsAString& aSessionId,
|
||||
uint16_t aState)
|
||||
{
|
||||
if (!aSessionId.Equals(mId)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
PresentationSessionState state;
|
||||
switch (aState) {
|
||||
case nsIPresentationSessionListener::STATE_CONNECTED:
|
||||
state = PresentationSessionState::Connected;
|
||||
break;
|
||||
case nsIPresentationSessionListener::STATE_DISCONNECTED:
|
||||
state = PresentationSessionState::Disconnected;
|
||||
break;
|
||||
case nsIPresentationSessionListener::STATE_TERMINATED:
|
||||
state = PresentationSessionState::Terminated;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Unknown presentation session state.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (mState == state) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mState = state;
|
||||
|
||||
// Unregister session listener if the session is no longer connected.
|
||||
if (mState == PresentationSessionState::Terminated) {
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv = service->UnregisterSessionListener(mId);
|
||||
if(NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return DispatchStateChangeEvent();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSession::NotifyMessage(const nsAString& aSessionId,
|
||||
const nsACString& aData)
|
||||
{
|
||||
if (!aSessionId.Equals(mId)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// No message should be expected when the session is not connected.
|
||||
if (NS_WARN_IF(mState != PresentationSessionState::Connected)) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
// Transform the data.
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(GetOwner())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JS::Value> jsData(cx);
|
||||
NS_ConvertUTF8toUTF16 utf16Data(aData);
|
||||
if(NS_WARN_IF(!ToJSValue(cx, utf16Data, &jsData))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return DispatchMessageEvent(jsData);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSession::DispatchStateChangeEvent()
|
||||
{
|
||||
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("statechange"), false);
|
||||
return asyncDispatcher->PostDOMEvent();
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSession::DispatchMessageEvent(JS::Handle<JS::Value> aData)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if (NS_WARN_IF(!global)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Get the origin.
|
||||
nsAutoString origin;
|
||||
nsresult rv = nsContentUtils::GetUTFOrigin(global->PrincipalOrNull(), origin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
|
||||
rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false, false,
|
||||
aData,
|
||||
origin,
|
||||
EmptyString(), nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
|
||||
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, event);
|
||||
return asyncDispatcher->PostDOMEvent();
|
||||
}
|
||||
|
||||
@@ -9,16 +9,19 @@
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/PresentationSessionBinding.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationSession final : public DOMEventTargetHelper
|
||||
, public nsIPresentationSessionListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationSession,
|
||||
DOMEventTargetHelper)
|
||||
NS_DECL_NSIPRESENTATIONSESSIONLISTENER
|
||||
|
||||
static already_AddRefed<PresentationSession>
|
||||
Create(nsPIDOMWindow* aWindow,
|
||||
@@ -44,6 +47,8 @@ private:
|
||||
|
||||
bool Init();
|
||||
void Shutdown();
|
||||
nsresult DispatchStateChangeEvent();
|
||||
nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
|
||||
|
||||
nsString mId;
|
||||
PresentationSessionState mState;
|
||||
|
||||
@@ -0,0 +1,810 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/HTMLIFrameElementBinding.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIFrameLoader.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsINetAddr.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "PresentationService.h"
|
||||
#include "PresentationSessionInfo.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsINetworkInterface.h"
|
||||
#include "nsINetworkManager.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::services;
|
||||
|
||||
/*
|
||||
* Implementation of PresentationChannelDescription
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationChannelDescription final : public nsIPresentationChannelDescription
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONCHANNELDESCRIPTION
|
||||
|
||||
PresentationChannelDescription(nsACString& aAddress,
|
||||
uint16_t aPort)
|
||||
: mAddress(aAddress)
|
||||
, mPort(aPort)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
~PresentationChannelDescription() {}
|
||||
|
||||
nsCString mAddress;
|
||||
uint16_t mPort;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationChannelDescription, nsIPresentationChannelDescription)
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationChannelDescription::GetType(uint8_t* aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!aRetVal)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
|
||||
// Only support TCP socket for now.
|
||||
*aRetVal = nsIPresentationChannelDescription::TYPE_TCP;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationChannelDescription::GetTcpAddress(nsIArray** aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!aRetVal)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (NS_WARN_IF(!array)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
|
||||
// Ultimately we may use all the available addresses. DataChannel appears
|
||||
// more robust upon handling ICE. And at the first stage Presentation API is
|
||||
// only exposed on Firefox OS where the first IP appears enough for most
|
||||
// scenarios.
|
||||
nsCOMPtr<nsISupportsCString> address = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
|
||||
if (NS_WARN_IF(!address)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
address->SetData(mAddress);
|
||||
|
||||
array->AppendElement(address, false);
|
||||
array.forget(aRetVal);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationChannelDescription::GetTcpPort(uint16_t* aRetVal)
|
||||
{
|
||||
if (NS_WARN_IF(!aRetVal)) {
|
||||
return NS_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
*aRetVal = mPort;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationChannelDescription::GetDataChannelSDP(nsAString& aDataChannelSDP)
|
||||
{
|
||||
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
|
||||
// Only support TCP socket for now.
|
||||
aDataChannelSDP.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of PresentationSessionInfo
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationSessionInfo,
|
||||
nsIPresentationSessionTransportCallback,
|
||||
nsIPresentationControlChannelListener);
|
||||
|
||||
/* virtual */ nsresult
|
||||
PresentationSessionInfo::Init(nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
SetControlChannel(aControlChannel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
PresentationSessionInfo::Shutdown(nsresult aReason)
|
||||
{
|
||||
// Close the control channel if any.
|
||||
if (mControlChannel) {
|
||||
NS_WARN_IF(NS_FAILED(mControlChannel->Close(aReason)));
|
||||
}
|
||||
|
||||
// Close the data transport channel if any.
|
||||
if (mTransport) {
|
||||
// |mIsTransportReady| will be unset once |NotifyTransportClosed| is called.
|
||||
NS_WARN_IF(NS_FAILED(mTransport->Close(aReason)));
|
||||
}
|
||||
|
||||
mIsResponderReady = false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSessionInfo::SetListener(nsIPresentationSessionListener* aListener)
|
||||
{
|
||||
mListener = aListener;
|
||||
|
||||
if (mListener) {
|
||||
// The transport might become ready, or might become un-ready again, before
|
||||
// the listener has registered. So notify the listener of the state change.
|
||||
uint16_t state = IsSessionReady() ?
|
||||
nsIPresentationSessionListener::STATE_CONNECTED :
|
||||
nsIPresentationSessionListener::STATE_DISCONNECTED;
|
||||
return mListener->NotifyStateChange(mSessionId, state);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSessionInfo::Send(nsIInputStream* aData)
|
||||
{
|
||||
if (NS_WARN_IF(!IsSessionReady())) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mTransport)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return mTransport->Send(aData);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSessionInfo::Close(nsresult aReason)
|
||||
{
|
||||
// The session is disconnected and it's a normal close. Simply change the
|
||||
// state to TERMINATED.
|
||||
if (!IsSessionReady() && NS_SUCCEEDED(aReason) && mListener) {
|
||||
nsresult rv = mListener->NotifyStateChange(mSessionId,
|
||||
nsIPresentationSessionListener::STATE_TERMINATED);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
Shutdown(aReason);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSessionInfo::ReplySuccess()
|
||||
{
|
||||
if (mListener) {
|
||||
// Notify session state change.
|
||||
nsresult rv = mListener->NotifyStateChange(mSessionId,
|
||||
nsIPresentationSessionListener::STATE_CONNECTED);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
if (mCallback) {
|
||||
NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess()));
|
||||
SetCallback(nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSessionInfo::ReplyError(nsresult aError)
|
||||
{
|
||||
Shutdown(aError);
|
||||
|
||||
if (mCallback) {
|
||||
NS_WARN_IF(NS_FAILED(mCallback->NotifyError(aError)));
|
||||
SetCallback(nullptr);
|
||||
}
|
||||
|
||||
// Remove itself since it never succeeds.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
static_cast<PresentationService*>(service.get())->RemoveSessionInfo(mSessionId);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIPresentationSessionTransportCallback
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionInfo::NotifyTransportReady()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mIsTransportReady = true;
|
||||
|
||||
// At sender side, session might not be ready at this point (waiting for
|
||||
// receiver's answer). Yet at receiver side, session must be ready at this
|
||||
// point since the data transport channel is created after the receiver page
|
||||
// is ready for presentation use.
|
||||
if (IsSessionReady()) {
|
||||
return ReplySuccess();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionInfo::NotifyTransportClosed(nsresult aReason)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Nullify |mTransport| here so it won't try to re-close |mTransport| in
|
||||
// potential subsequent |Shutdown| calls.
|
||||
mTransport->SetCallback(nullptr);
|
||||
mTransport = nullptr;
|
||||
|
||||
if (!IsSessionReady()) {
|
||||
// It happens before the session is ready. Reply the callback.
|
||||
return ReplyError(aReason);
|
||||
}
|
||||
|
||||
// Unset |mIsTransportReady| here so it won't affect |IsSessionReady()| above.
|
||||
mIsTransportReady = false;
|
||||
|
||||
Shutdown(aReason);
|
||||
|
||||
if (mListener) {
|
||||
// It happens after the session is ready. Notify session state change.
|
||||
uint16_t state = (NS_WARN_IF(NS_FAILED(aReason))) ?
|
||||
nsIPresentationSessionListener::STATE_DISCONNECTED :
|
||||
nsIPresentationSessionListener::STATE_TERMINATED;
|
||||
return mListener->NotifyStateChange(mSessionId, state);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionInfo::NotifyData(const nsACString& aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_WARN_IF(!IsSessionReady())) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mListener)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return mListener->NotifyMessage(mSessionId, aData);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of PresentationRequesterInfo
|
||||
*
|
||||
* During presentation session establishment, the sender expects the following
|
||||
* after trying to establish the control channel: (The order between step 2 and
|
||||
* 3 is not guaranteed.)
|
||||
* 1. |Init| is called to open a socket |mServerSocket| for data transport
|
||||
* channel and send the offer to the receiver via the control channel.
|
||||
* 2.1 |OnSocketAccepted| of |nsIServerSocketListener| is called to indicate the
|
||||
* data transport channel is connected. Then initialize |mTransport|.
|
||||
* 2.2 |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is
|
||||
* called.
|
||||
* 3. |OnAnswer| of |nsIPresentationControlChannelListener| is called to
|
||||
* indicate the receiver is ready. Close the control channel since it's no
|
||||
* longer needed.
|
||||
* 4. Once both step 2 and 3 are done, the presentation session is ready to use.
|
||||
* So notify the listener of CONNECTED state.
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(PresentationRequesterInfo,
|
||||
PresentationSessionInfo,
|
||||
nsIServerSocketListener)
|
||||
|
||||
nsresult
|
||||
PresentationRequesterInfo::Init(nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
PresentationSessionInfo::Init(aControlChannel);
|
||||
|
||||
// Initialize |mServerSocket| for bootstrapping the data transport channel and
|
||||
// use |this| as the listener.
|
||||
mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
|
||||
if (NS_WARN_IF(!mServerSocket)) {
|
||||
return ReplyError(NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
nsresult rv = mServerSocket->Init(-1, false, -1);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mServerSocket->AsyncListen(this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Prepare and send the offer.
|
||||
int32_t port;
|
||||
rv = mServerSocket->GetPort(&port);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCString address;
|
||||
rv = GetAddress(address);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsRefPtr<PresentationChannelDescription> description =
|
||||
new PresentationChannelDescription(address, static_cast<uint16_t>(port));
|
||||
rv = mControlChannel->SendOffer(description);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationRequesterInfo::Shutdown(nsresult aReason)
|
||||
{
|
||||
PresentationSessionInfo::Shutdown(aReason);
|
||||
|
||||
// Close the server socket if any.
|
||||
if (mServerSocket) {
|
||||
NS_WARN_IF(NS_FAILED(mServerSocket->Close()));
|
||||
mServerSocket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequesterInfo::GetAddress(nsACString& aAddress)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsCOMPtr<nsINetworkManager> networkManager =
|
||||
do_GetService("@mozilla.org/network/manager;1");
|
||||
if (NS_WARN_IF(!networkManager)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINetworkInfo> activeNetworkInfo;
|
||||
networkManager->GetActiveNetworkInfo(getter_AddRefs(activeNetworkInfo));
|
||||
if (NS_WARN_IF(!activeNetworkInfo)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
char16_t** ips = nullptr;
|
||||
uint32_t* prefixes = nullptr;
|
||||
uint32_t count = 0;
|
||||
activeNetworkInfo->GetAddresses(&ips, &prefixes, &count);
|
||||
if (NS_WARN_IF(!count)) {
|
||||
NS_Free(prefixes);
|
||||
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, ips);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
|
||||
// Ultimately we may use all the available addresses. DataChannel appears
|
||||
// more robust upon handling ICE. And at the first stage Presentation API is
|
||||
// only exposed on Firefox OS where the first IP appears enough for most
|
||||
// scenarios.
|
||||
nsAutoString ip;
|
||||
ip.Assign(ips[0]);
|
||||
aAddress = NS_ConvertUTF16toUTF8(ip);
|
||||
|
||||
NS_Free(prefixes);
|
||||
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, ips);
|
||||
#else
|
||||
// TODO Get host IP via other platforms.
|
||||
aAddress.Truncate();
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIPresentationControlChannelListener
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
|
||||
{
|
||||
MOZ_ASSERT(false, "Sender side should not receive offer.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterInfo::OnAnswer(nsIPresentationChannelDescription* aDescription)
|
||||
{
|
||||
mIsResponderReady = true;
|
||||
|
||||
// Close the control channel since it's no longer needed.
|
||||
nsresult rv = mControlChannel->Close(NS_OK);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return ReplyError(rv);
|
||||
}
|
||||
|
||||
// Session might not be ready at this moment (waiting for the establishment of
|
||||
// the data transport channel).
|
||||
if (IsSessionReady()){
|
||||
return ReplySuccess();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterInfo::NotifyOpened()
|
||||
{
|
||||
// Do nothing and wait for receiver to be ready.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterInfo::NotifyClosed(nsresult aReason)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Unset control channel here so it won't try to re-close it in potential
|
||||
// subsequent |Shutdown| calls.
|
||||
SetControlChannel(nullptr);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(aReason))) {
|
||||
if (mListener) {
|
||||
// The presentation session instance at receiver side may already exist.
|
||||
// Change the state to TERMINATED since it never succeeds.
|
||||
return mListener->NotifyStateChange(mSessionId,
|
||||
nsIPresentationSessionListener::STATE_TERMINATED);
|
||||
}
|
||||
|
||||
// Reply error for an abnormal close.
|
||||
return ReplyError(aReason);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIServerSocketListener
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterInfo::OnSocketAccepted(nsIServerSocket* aServerSocket,
|
||||
nsISocketTransport* aTransport)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Initialize |mTransport| and use |this| as the callback.
|
||||
mTransport = do_CreateInstance(PRESENTATION_SESSION_TRANSPORT_CONTRACTID);
|
||||
if (NS_WARN_IF(!mTransport)) {
|
||||
return ReplyError(NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
nsresult rv = mTransport->InitWithSocketTransport(aTransport, this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequesterInfo::OnStopListening(nsIServerSocket* aServerSocket,
|
||||
nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (aStatus == NS_BINDING_ABORTED) { // The server socket was manually closed.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Shutdown(aStatus);
|
||||
|
||||
if (!IsSessionReady()) {
|
||||
// It happens before the session is ready. Reply the callback.
|
||||
return ReplyError(aStatus);
|
||||
}
|
||||
|
||||
// It happens after the session is ready. Notify session state change.
|
||||
if (mListener) {
|
||||
return mListener->NotifyStateChange(mSessionId,
|
||||
nsIPresentationSessionListener::STATE_DISCONNECTED);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of PresentationResponderInfo
|
||||
*
|
||||
* During presentation session establishment, the receiver expects the following
|
||||
* after trying to launch the app by notifying "presentation-launch-receiver":
|
||||
* (The order between step 2 and 3 is not guaranteed.)
|
||||
* 1. |Observe| of |nsIObserver| is called with "presentation-receiver-launched".
|
||||
* Then start listen to document |STATE_TRANSFERRING| event.
|
||||
* 2. |NotifyResponderReady| is called to indicate the receiver page is ready
|
||||
* for presentation use.
|
||||
* 3. |OnOffer| of |nsIPresentationControlChannelListener| is called.
|
||||
* 4. Once both step 2 and 3 are done, establish the data transport channel and
|
||||
* send the answer. (The control channel will be closed by the sender once it
|
||||
* receives the answer.)
|
||||
* 5. |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is
|
||||
* called. The presentation session is ready to use, so notify the listener
|
||||
* of CONNECTED state.
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(PresentationResponderInfo,
|
||||
PresentationSessionInfo,
|
||||
nsITimerCallback)
|
||||
|
||||
nsresult
|
||||
PresentationResponderInfo::Init(nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
PresentationSessionInfo::Init(aControlChannel);
|
||||
|
||||
// Add a timer to prevent waiting indefinitely in case the receiver page fails
|
||||
// to become ready.
|
||||
nsresult rv;
|
||||
int32_t timeout =
|
||||
Preferences::GetInt("presentation.receiver.loading.timeout", 10000);
|
||||
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = mTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationResponderInfo::Shutdown(nsresult aReason)
|
||||
{
|
||||
PresentationSessionInfo::Shutdown(aReason);
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
mLoadingCallback = nullptr;
|
||||
mRequesterDescription = nullptr;
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationResponderInfo::InitTransportAndSendAnswer()
|
||||
{
|
||||
// Establish a data transport channel |mTransport| to the sender and use
|
||||
// |this| as the callback.
|
||||
mTransport = do_CreateInstance(PRESENTATION_SESSION_TRANSPORT_CONTRACTID);
|
||||
if (NS_WARN_IF(!mTransport)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv = mTransport->InitWithChannelDescription(mRequesterDescription, this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Prepare and send the answer.
|
||||
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
|
||||
// In the current implementation of |PresentationSessionTransport|,
|
||||
// |GetSelfAddress| cannot return the real info when it's initialized via
|
||||
// |InitWithChannelDescription|. Yet this deficiency only affects the channel
|
||||
// description for the answer, which is not actually checked at requester side.
|
||||
nsCOMPtr<nsINetAddr> selfAddr;
|
||||
rv = mTransport->GetSelfAddress(getter_AddRefs(selfAddr));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCString address;
|
||||
selfAddr->GetAddress(address);
|
||||
uint16_t port;
|
||||
selfAddr->GetPort(&port);
|
||||
nsCOMPtr<nsIPresentationChannelDescription> description =
|
||||
new PresentationChannelDescription(address, port);
|
||||
|
||||
rv = mControlChannel->SendAnswer(description);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationResponderInfo::NotifyResponderReady()
|
||||
{
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
|
||||
mIsResponderReady = true;
|
||||
|
||||
// Initialize |mTransport| and send the answer to the sender if sender's
|
||||
// description is already offered.
|
||||
if (mRequesterDescription) {
|
||||
nsresult rv = InitTransportAndSendAnswer();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return ReplyError(rv);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIPresentationControlChannelListener
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderInfo::OnOffer(nsIPresentationChannelDescription* aDescription)
|
||||
{
|
||||
if (NS_WARN_IF(!aDescription)) {
|
||||
return ReplyError(NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
mRequesterDescription = aDescription;
|
||||
|
||||
// Initialize |mTransport| and send the answer to the sender if the receiver
|
||||
// page is ready for presentation use.
|
||||
if (mIsResponderReady) {
|
||||
nsresult rv = InitTransportAndSendAnswer();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return ReplyError(rv);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderInfo::OnAnswer(nsIPresentationChannelDescription* aDescription)
|
||||
{
|
||||
MOZ_ASSERT(false, "Receiver side should not receive answer.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderInfo::NotifyOpened()
|
||||
{
|
||||
// Do nothing.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderInfo::NotifyClosed(nsresult aReason)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Unset control channel here so it won't try to re-close it in potential
|
||||
// subsequent |Shutdown| calls.
|
||||
SetControlChannel(nullptr);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(aReason))) {
|
||||
if (mListener) {
|
||||
// The presentation session instance at receiver side may already exist.
|
||||
// Change the state to TERMINATED since it never succeeds.
|
||||
return mListener->NotifyStateChange(mSessionId,
|
||||
nsIPresentationSessionListener::STATE_TERMINATED);
|
||||
}
|
||||
|
||||
// Reply error for an abnormal close.
|
||||
return ReplyError(aReason);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsITimerCallback
|
||||
NS_IMETHODIMP
|
||||
PresentationResponderInfo::Notify(nsITimer* aTimer)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("The receiver page fails to become ready before timeout.");
|
||||
|
||||
mTimer = nullptr;
|
||||
return ReplyError(NS_ERROR_DOM_TIMEOUT_ERR);
|
||||
}
|
||||
|
||||
// PromiseNativeHandler
|
||||
void
|
||||
PresentationResponderInfo::ResolvedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_WARN_IF(!aValue.isObject())) {
|
||||
ReplyError(NS_ERROR_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
|
||||
if (NS_WARN_IF(!obj)) {
|
||||
ReplyError(NS_ERROR_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start to listen to document state change event |STATE_TRANSFERRING|.
|
||||
HTMLIFrameElement* frame = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(HTMLIFrameElement, obj, frame);
|
||||
if (NS_WARN_IF(!frame)) {
|
||||
ReplyError(NS_ERROR_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface((nsIFrameLoaderOwner*) frame);
|
||||
if (NS_WARN_IF(!owner)) {
|
||||
ReplyError(NS_ERROR_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFrameLoader> frameLoader;
|
||||
rv = owner->GetFrameLoader(getter_AddRefs(frameLoader));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ReplyError(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<TabParent> tabParent = TabParent::GetFrom(frameLoader);
|
||||
if (tabParent) {
|
||||
// OOP frame
|
||||
nsCOMPtr<nsIContentParent> cp = tabParent->Manager();
|
||||
NS_WARN_IF(!static_cast<ContentParent*>(cp.get())->SendNotifyPresentationReceiverLaunched(tabParent, mSessionId));
|
||||
} else {
|
||||
// In-process frame
|
||||
nsCOMPtr<nsIDocShell> docShell;
|
||||
rv = frameLoader->GetDocShell(getter_AddRefs(docShell));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ReplyError(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
mLoadingCallback = new PresentationResponderLoadingCallback(mSessionId);
|
||||
rv = mLoadingCallback->Init(docShell);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ReplyError(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresentationResponderInfo::RejectedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("Launching the receiver page has been rejected.");
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
|
||||
ReplyError(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationSessionInfo_h
|
||||
#define mozilla_dom_PresentationSessionInfo_h
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIPresentationControlChannel.h"
|
||||
#include "nsIPresentationDevice.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsIPresentationSessionTransport.h"
|
||||
#include "nsIServerSocket.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsString.h"
|
||||
#include "PresentationCallbacks.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationSessionInfo : public nsIPresentationSessionTransportCallback
|
||||
, public nsIPresentationControlChannelListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK
|
||||
|
||||
PresentationSessionInfo(const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
nsIPresentationServiceCallback* aCallback)
|
||||
: mUrl(aUrl)
|
||||
, mSessionId(aSessionId)
|
||||
, mIsResponderReady(false)
|
||||
, mIsTransportReady(false)
|
||||
, mCallback(aCallback)
|
||||
{
|
||||
MOZ_ASSERT(!mUrl.IsEmpty());
|
||||
MOZ_ASSERT(!mSessionId.IsEmpty());
|
||||
}
|
||||
|
||||
virtual nsresult Init(nsIPresentationControlChannel* aControlChannel);
|
||||
|
||||
const nsAString& GetUrl() const
|
||||
{
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
const nsAString& GetSessionId() const
|
||||
{
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
void SetCallback(nsIPresentationServiceCallback* aCallback)
|
||||
{
|
||||
mCallback = aCallback;
|
||||
}
|
||||
|
||||
nsresult SetListener(nsIPresentationSessionListener* aListener);
|
||||
|
||||
void SetDevice(nsIPresentationDevice* aDevice)
|
||||
{
|
||||
mDevice = aDevice;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPresentationDevice> GetDevice() const
|
||||
{
|
||||
nsCOMPtr<nsIPresentationDevice> device = mDevice;
|
||||
return device.forget();
|
||||
}
|
||||
|
||||
void SetControlChannel(nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
if (mControlChannel) {
|
||||
mControlChannel->SetListener(nullptr);
|
||||
}
|
||||
|
||||
mControlChannel = aControlChannel;
|
||||
if (mControlChannel) {
|
||||
mControlChannel->SetListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult Send(nsIInputStream* aData);
|
||||
|
||||
nsresult Close(nsresult aReason);
|
||||
|
||||
nsresult ReplyError(nsresult aReason);
|
||||
|
||||
protected:
|
||||
virtual ~PresentationSessionInfo()
|
||||
{
|
||||
Shutdown(NS_OK);
|
||||
}
|
||||
|
||||
virtual void Shutdown(nsresult aReason);
|
||||
|
||||
nsresult ReplySuccess();
|
||||
|
||||
bool IsSessionReady()
|
||||
{
|
||||
return mIsResponderReady && mIsTransportReady;
|
||||
}
|
||||
|
||||
nsString mUrl;
|
||||
nsString mSessionId;
|
||||
bool mIsResponderReady;
|
||||
bool mIsTransportReady;
|
||||
nsCOMPtr<nsIPresentationServiceCallback> mCallback;
|
||||
nsCOMPtr<nsIPresentationSessionListener> mListener;
|
||||
nsCOMPtr<nsIPresentationDevice> mDevice;
|
||||
nsCOMPtr<nsIPresentationSessionTransport> mTransport;
|
||||
nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
|
||||
};
|
||||
|
||||
// Session info with sender side behaviors.
|
||||
class PresentationRequesterInfo final : public PresentationSessionInfo
|
||||
, public nsIServerSocketListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
|
||||
NS_DECL_NSISERVERSOCKETLISTENER
|
||||
|
||||
PresentationRequesterInfo(const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
nsIPresentationServiceCallback* aCallback)
|
||||
: PresentationSessionInfo(aUrl, aSessionId, aCallback)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
}
|
||||
|
||||
nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
|
||||
|
||||
private:
|
||||
~PresentationRequesterInfo()
|
||||
{
|
||||
Shutdown(NS_OK);
|
||||
}
|
||||
|
||||
void Shutdown(nsresult aReason) override;
|
||||
|
||||
nsresult GetAddress(nsACString& aAddress);
|
||||
|
||||
nsCOMPtr<nsIServerSocket> mServerSocket;
|
||||
};
|
||||
|
||||
// Session info with receiver side behaviors.
|
||||
class PresentationResponderInfo final : public PresentationSessionInfo
|
||||
, public PromiseNativeHandler
|
||||
, public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
PresentationResponderInfo(const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
nsIPresentationDevice* aDevice)
|
||||
: PresentationSessionInfo(aUrl, aSessionId, nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aDevice);
|
||||
|
||||
SetDevice(aDevice);
|
||||
}
|
||||
|
||||
nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
|
||||
|
||||
nsresult NotifyResponderReady();
|
||||
|
||||
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
|
||||
|
||||
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
|
||||
|
||||
void SetPromise(Promise* aPromise)
|
||||
{
|
||||
mPromise = aPromise;
|
||||
mPromise->AppendNativeHandler(this);
|
||||
}
|
||||
|
||||
private:
|
||||
~PresentationResponderInfo()
|
||||
{
|
||||
Shutdown(NS_OK);
|
||||
}
|
||||
|
||||
void Shutdown(nsresult aReason) override;
|
||||
|
||||
nsresult InitTransportAndSendAnswer();
|
||||
|
||||
nsRefPtr<PresentationResponderLoadingCallback> mLoadingCallback;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCOMPtr<nsIPresentationChannelDescription> mRequesterDescription;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationSessionInfo_h
|
||||
@@ -0,0 +1,484 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsIAsyncStreamCopier.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIMultiplexInputStream.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIPresentationControlChannel.h"
|
||||
#include "nsIScriptableInputStream.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "PresentationSessionTransport.h"
|
||||
|
||||
#define BUFFER_SIZE 65536
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
class CopierCallbacks final : public nsIRequestObserver
|
||||
{
|
||||
public:
|
||||
explicit CopierCallbacks(PresentationSessionTransport* aTransport)
|
||||
: mOwner(aTransport)
|
||||
{}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
private:
|
||||
~CopierCallbacks() {}
|
||||
|
||||
nsRefPtr<PresentationSessionTransport> mOwner;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
|
||||
{
|
||||
mOwner->NotifyCopyComplete(aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationSessionTransport,
|
||||
nsIPresentationSessionTransport,
|
||||
nsITransportEventSink,
|
||||
nsIInputStreamCallback,
|
||||
nsIStreamListener,
|
||||
nsIRequestObserver)
|
||||
|
||||
PresentationSessionTransport::PresentationSessionTransport()
|
||||
: mReadyState(CLOSED)
|
||||
, mAsyncCopierActive(false)
|
||||
, mCloseStatus(NS_OK)
|
||||
{
|
||||
}
|
||||
|
||||
PresentationSessionTransport::~PresentationSessionTransport()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::InitWithSocketTransport(nsISocketTransport* aTransport,
|
||||
nsIPresentationSessionTransportCallback* aCallback)
|
||||
{
|
||||
if (NS_WARN_IF(!aCallback)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
mCallback = aCallback;
|
||||
|
||||
if (NS_WARN_IF(!aTransport)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
mTransport = aTransport;
|
||||
|
||||
nsresult rv = CreateStream();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
SetReadyState(OPEN);
|
||||
|
||||
rv = CreateInputStreamPump();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::InitWithChannelDescription(nsIPresentationChannelDescription* aDescription,
|
||||
nsIPresentationSessionTransportCallback* aCallback)
|
||||
{
|
||||
if (NS_WARN_IF(!aCallback)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
mCallback = aCallback;
|
||||
|
||||
if (NS_WARN_IF(!aDescription)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint16_t serverPort;
|
||||
nsresult rv = aDescription->GetTcpPort(&serverPort);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIArray> serverHosts;
|
||||
rv = aDescription->GetTcpAddress(getter_AddRefs(serverHosts));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
|
||||
// Ultimately we may use all the available addresses. DataChannel appears
|
||||
// more robust upon handling ICE. And at the first stage Presentation API is
|
||||
// only exposed on Firefox OS where the first IP appears enough for most
|
||||
// scenarios.
|
||||
nsCOMPtr<nsISupportsCString> supportStr = do_QueryElementAt(serverHosts, 0);
|
||||
if (NS_WARN_IF(!supportStr)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoCString serverHost;
|
||||
supportStr->GetData(serverHost);
|
||||
if (serverHost.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
SetReadyState(CONNECTING);
|
||||
|
||||
nsCOMPtr<nsISocketTransportService> sts =
|
||||
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!sts)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
rv = sts->CreateTransport(nullptr, 0, serverHost, serverPort, nullptr,
|
||||
getter_AddRefs(mTransport));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
|
||||
mTransport->SetEventSink(this, mainThread);
|
||||
|
||||
rv = CreateStream();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSessionTransport::CreateStream()
|
||||
{
|
||||
nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If the other side is not listening, we will get an |onInputStreamReady|
|
||||
// callback where |available| raises to indicate the connection was refused.
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mSocketInputStream);
|
||||
if (NS_WARN_IF(!asyncStream)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
|
||||
rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainThread);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = mInputStreamScriptable->Init(mSocketInputStream);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISocketTransportService> sts =
|
||||
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!sts)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
|
||||
rv = mMultiplexStreamCopier->Init(mMultiplexStream,
|
||||
mSocketOutputStream,
|
||||
target,
|
||||
true, /* source buffered */
|
||||
false, /* sink buffered */
|
||||
BUFFER_SIZE,
|
||||
false, /* close source */
|
||||
false); /* close sink */
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationSessionTransport::CreateInputStreamPump()
|
||||
{
|
||||
nsresult rv;
|
||||
mInputStreamPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mInputStreamPump->Init(mSocketInputStream, -1, -1, 0, 0, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mInputStreamPump->AsyncRead(this, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::GetCallback(nsIPresentationSessionTransportCallback** aCallback)
|
||||
{
|
||||
nsCOMPtr<nsIPresentationSessionTransportCallback> callback = mCallback;
|
||||
callback.forget(aCallback);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::SetCallback(nsIPresentationSessionTransportCallback* aCallback)
|
||||
{
|
||||
mCallback = aCallback;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::GetSelfAddress(nsINetAddr** aSelfAddress)
|
||||
{
|
||||
if (NS_WARN_IF(mReadyState != OPEN)) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
return mTransport->GetScriptableSelfAddr(aSelfAddress);
|
||||
}
|
||||
|
||||
void
|
||||
PresentationSessionTransport::EnsureCopying()
|
||||
{
|
||||
if (mAsyncCopierActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
mAsyncCopierActive = true;
|
||||
nsRefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
|
||||
NS_WARN_IF(NS_FAILED(mMultiplexStreamCopier->AsyncCopy(callbacks, nullptr)));
|
||||
}
|
||||
|
||||
void
|
||||
PresentationSessionTransport::NotifyCopyComplete(nsresult aStatus)
|
||||
{
|
||||
mAsyncCopierActive = false;
|
||||
mMultiplexStream->RemoveStream(0);
|
||||
if (NS_WARN_IF(NS_FAILED(aStatus))) {
|
||||
if (mReadyState != CLOSED) {
|
||||
mCloseStatus = aStatus;
|
||||
SetReadyState(CLOSED);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t count;
|
||||
nsresult rv = mMultiplexStream->GetCount(&count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
EnsureCopying();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReadyState == CLOSING) {
|
||||
mSocketOutputStream->Close();
|
||||
mCloseStatus = NS_OK;
|
||||
SetReadyState(CLOSED);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::Send(nsIInputStream* aData)
|
||||
{
|
||||
if (NS_WARN_IF(mReadyState != OPEN)) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
mMultiplexStream->AppendStream(aData);
|
||||
|
||||
EnsureCopying();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::Close(nsresult aReason)
|
||||
{
|
||||
if (mReadyState == CLOSED || mReadyState == CLOSING) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mCloseStatus = aReason;
|
||||
SetReadyState(CLOSING);
|
||||
|
||||
uint32_t count = 0;
|
||||
mMultiplexStream->GetCount(&count);
|
||||
if (!count) {
|
||||
mSocketOutputStream->Close();
|
||||
}
|
||||
|
||||
mSocketInputStream->Close();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationSessionTransport::SetReadyState(ReadyState aReadyState)
|
||||
{
|
||||
mReadyState = aReadyState;
|
||||
|
||||
if (mReadyState == OPEN && mCallback) {
|
||||
// Notify the transport channel is ready.
|
||||
NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady()));
|
||||
} else if (mReadyState == CLOSED && mCallback) {
|
||||
// Notify the transport channel has been shut down.
|
||||
NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportClosed(mCloseStatus)));
|
||||
}
|
||||
}
|
||||
|
||||
// nsITransportEventSink
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::OnTransportStatus(nsITransport* aTransport,
|
||||
nsresult aStatus,
|
||||
int64_t aProgress,
|
||||
int64_t aProgressMax)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (aStatus != NS_NET_STATUS_CONNECTED_TO) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SetReadyState(OPEN);
|
||||
|
||||
nsresult rv = CreateInputStreamPump();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIInputStreamCallback
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Only used for detecting if the connection was refused.
|
||||
uint64_t dummy;
|
||||
nsresult rv = aStream->Available(&dummy);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
if (mReadyState != CLOSED) {
|
||||
mCloseStatus = NS_ERROR_CONNECTION_REFUSED;
|
||||
SetReadyState(CLOSED);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIRequestObserver
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::OnStartRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext)
|
||||
{
|
||||
// Do nothing.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
uint32_t count;
|
||||
nsresult rv = mMultiplexStream->GetCount(&count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mInputStreamPump = nullptr;
|
||||
|
||||
if (count != 0 && NS_SUCCEEDED(aStatusCode)) {
|
||||
// If we have some buffered output still, and status is not an error, the
|
||||
// other side has done a half-close, but we don't want to be in the close
|
||||
// state until we are done sending everything that was buffered. We also
|
||||
// don't want to call |NotifyTransportClosed| yet.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We call this even if there is no error.
|
||||
if (mReadyState != CLOSED) {
|
||||
mCloseStatus = aStatusCode;
|
||||
SetReadyState(CLOSED);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIStreamListener
|
||||
NS_IMETHODIMP
|
||||
PresentationSessionTransport::OnDataAvailable(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsIInputStream* aStream,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_WARN_IF(!mCallback)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCString data;
|
||||
nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Pass the incoming data to the listener.
|
||||
return mCallback->NotifyData(data);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationSessionTransport_h
|
||||
#define mozilla_dom_PresentationSessionTransport_h
|
||||
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIPresentationSessionTransport.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsITransport.h"
|
||||
|
||||
class nsISocketTransport;
|
||||
class nsIInputStreamPump;
|
||||
class nsIScriptableInputStream;
|
||||
class nsIMultiplexInputStream;
|
||||
class nsIAsyncStreamCopier;
|
||||
class nsIInputStream;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* App-to-App transport channel for the presentation session. It's usually
|
||||
* initialized with an |InitWithSocketTransport| call if at the presenting sender
|
||||
* side; whereas it's initialized with an |InitWithChannelDescription| if at the
|
||||
* presenting receiver side. The lifetime is managed in either
|
||||
* |PresentationRequesterInfo| (sender side) or |PresentationResponderInfo|
|
||||
* (receiver side) in PresentationSessionInfo.cpp.
|
||||
*
|
||||
* TODO bug 1148307 Implement PresentationSessionTransport with DataChannel.
|
||||
* The implementation over the TCP channel is primarily used for the early stage
|
||||
* of Presentation API (without SSL) and should be migrated to DataChannel with
|
||||
* full support soon.
|
||||
*/
|
||||
class PresentationSessionTransport final : public nsIPresentationSessionTransport
|
||||
, public nsITransportEventSink
|
||||
, public nsIInputStreamCallback
|
||||
, public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT
|
||||
NS_DECL_NSITRANSPORTEVENTSINK
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
PresentationSessionTransport();
|
||||
|
||||
void NotifyCopyComplete(nsresult aStatus);
|
||||
|
||||
private:
|
||||
~PresentationSessionTransport();
|
||||
|
||||
nsresult CreateStream();
|
||||
|
||||
nsresult CreateInputStreamPump();
|
||||
|
||||
void EnsureCopying();
|
||||
|
||||
enum ReadyState {
|
||||
CONNECTING,
|
||||
OPEN,
|
||||
CLOSING,
|
||||
CLOSED
|
||||
};
|
||||
|
||||
void SetReadyState(ReadyState aReadyState);
|
||||
|
||||
ReadyState mReadyState;
|
||||
bool mAsyncCopierActive;
|
||||
nsresult mCloseStatus;
|
||||
|
||||
// Raw socket streams
|
||||
nsCOMPtr<nsISocketTransport> mTransport;
|
||||
nsCOMPtr<nsIInputStream> mSocketInputStream;
|
||||
nsCOMPtr<nsIOutputStream> mSocketOutputStream;
|
||||
|
||||
// Input stream machinery
|
||||
nsCOMPtr<nsIInputStreamPump> mInputStreamPump;
|
||||
nsCOMPtr<nsIScriptableInputStream> mInputStreamScriptable;
|
||||
|
||||
// Output stream machinery
|
||||
nsCOMPtr<nsIMultiplexInputStream> mMultiplexStream;
|
||||
nsCOMPtr<nsIAsyncStreamCopier> mMultiplexStreamCopier;
|
||||
|
||||
nsCOMPtr<nsIPresentationSessionTransportCallback> mCallback;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationSessionTransport_h
|
||||
@@ -10,7 +10,11 @@ XPIDL_SOURCES += [
|
||||
'nsIPresentationDeviceManager.idl',
|
||||
'nsIPresentationDevicePrompt.idl',
|
||||
'nsIPresentationDeviceProvider.idl',
|
||||
'nsIPresentationListener.idl',
|
||||
'nsIPresentationRequestUIGlue.idl',
|
||||
'nsIPresentationService.idl',
|
||||
'nsIPresentationSessionRequest.idl',
|
||||
'nsIPresentationSessionTransport.idl',
|
||||
'nsITCPPresentationServer.idl',
|
||||
]
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ interface nsIPresentationControlChannelListener: nsISupports
|
||||
* The control channel for establishing RTCPeerConnection for a presentation
|
||||
* session. SDP Offer/Answer will be exchanged through this interface.
|
||||
*/
|
||||
[scriptable, uuid(6bff04b9-8e79-466f-9446-f969de646fd3)]
|
||||
[scriptable, uuid(2c8ec493-4e5b-4df7-bedc-7ab25af323f0)]
|
||||
interface nsIPresentationControlChannel: nsISupports
|
||||
{
|
||||
// The listener for handling events of this control channel.
|
||||
@@ -87,7 +87,14 @@ interface nsIPresentationControlChannel: nsISupports
|
||||
void sendAnswer(in nsIPresentationChannelDescription answer);
|
||||
|
||||
/*
|
||||
* Close the transport channel.
|
||||
* Notify the app-to-app connection is fully established. (Only used at the
|
||||
* receiver side.)
|
||||
*/
|
||||
void close();
|
||||
void sendReceiverReady();
|
||||
|
||||
/*
|
||||
* Close the transport channel.
|
||||
* @param reason The reason of channel close; NS_OK represents normal.
|
||||
*/
|
||||
void close(in nsresult reason);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(0105f837-4279-4715-9d5b-2dc3f8b65353)]
|
||||
interface nsIPresentationListener : nsISupports
|
||||
{
|
||||
/*
|
||||
* Called when device availability changes.
|
||||
*/
|
||||
void notifyAvailableChange(in bool available);
|
||||
};
|
||||
|
||||
[scriptable, uuid(3b9ae71f-2905-4969-9117-101627c1c2ea)]
|
||||
interface nsIPresentationSessionListener : nsISupports
|
||||
{
|
||||
const unsigned short STATE_CONNECTED = 0;
|
||||
const unsigned short STATE_DISCONNECTED = 1;
|
||||
const unsigned short STATE_TERMINATED = 2;
|
||||
|
||||
/*
|
||||
* Called when session state changes.
|
||||
*/
|
||||
void notifyStateChange(in DOMString sessionId,
|
||||
in unsigned short state);
|
||||
|
||||
/*
|
||||
* Called when receive messages.
|
||||
*/
|
||||
void notifyMessage(in DOMString sessionId,
|
||||
in ACString data);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
%{C++
|
||||
#define PRESENTATION_REQUEST_UI_GLUE_CONTRACTID \
|
||||
"@mozilla.org/presentation/requestuiglue;1"
|
||||
%}
|
||||
|
||||
[scriptable, uuid(faa45119-6fb5-496c-aa4c-f740177a38b5)]
|
||||
interface nsIPresentationRequestUIGlue : nsISupports
|
||||
{
|
||||
/*
|
||||
* This method is called to open the responding app/page when a presentation
|
||||
* request comes in at receiver side.
|
||||
*
|
||||
* @param url The url of the request.
|
||||
* @param sessionId The session ID of the request.
|
||||
*
|
||||
* @return A promise that resolves to the opening frame.
|
||||
*/
|
||||
nsISupports sendRequest(in DOMString url, in DOMString sessionId);
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIInputStream;
|
||||
interface nsIPresentationListener;
|
||||
interface nsIPresentationSessionListener;
|
||||
|
||||
%{C++
|
||||
#define PRESENTATION_SERVICE_CID \
|
||||
{ 0x1d9bb10c, 0xc0ab, 0x4fe8, \
|
||||
{ 0x9e, 0x4f, 0x40, 0x58, 0xb8, 0x51, 0x98, 0x32 } }
|
||||
#define PRESENTATION_SERVICE_CONTRACTID \
|
||||
"@mozilla.org/presentation/presentationservice;1"
|
||||
%}
|
||||
|
||||
[scriptable, uuid(12073206-0065-4b10-9488-a6eb9b23e65b)]
|
||||
interface nsIPresentationServiceCallback : nsISupports
|
||||
{
|
||||
/*
|
||||
* Called when the operation succeeds.
|
||||
*/
|
||||
void notifySuccess();
|
||||
|
||||
/*
|
||||
* Called when the operation fails.
|
||||
*
|
||||
* @param error: error message.
|
||||
*/
|
||||
void notifyError(in nsresult error);
|
||||
};
|
||||
|
||||
[scriptable, uuid(5801efd9-9dba-4af7-8be9-8fc97c2d54a6)]
|
||||
interface nsIPresentationService : nsISupports
|
||||
{
|
||||
/*
|
||||
* Start a new presentation session and display a prompt box which asks users
|
||||
* to select a device.
|
||||
*
|
||||
* @param url: The url of presenting page.
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
* @param origin: The url of requesting page.
|
||||
* @param callback: Invoke the callback when the operation is completed.
|
||||
* NotifySuccess() is called with |id| if a session is
|
||||
* established successfully with the selected device.
|
||||
* Otherwise, NotifyError() is called with a error message.
|
||||
*/
|
||||
void startSession(in DOMString url,
|
||||
in DOMString sessionId,
|
||||
in DOMString origin,
|
||||
in nsIPresentationServiceCallback callback);
|
||||
|
||||
/*
|
||||
* Send the message wrapped with an input stream to the session.
|
||||
*
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
* @param stream: The message is converted to an input stream.
|
||||
*/
|
||||
void sendSessionMessage(in DOMString sessionId,
|
||||
in nsIInputStream stream);
|
||||
|
||||
/*
|
||||
* Terminate the session.
|
||||
*
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
*/
|
||||
void terminate(in DOMString sessionId);
|
||||
|
||||
/*
|
||||
* Register a listener. Must be called from the main thread.
|
||||
*
|
||||
* @param listener: The listener to register.
|
||||
*/
|
||||
void registerListener(in nsIPresentationListener listener);
|
||||
|
||||
/*
|
||||
* Unregister a listener. Must be called from the main thread.
|
||||
* @param listener: The listener to unregister.
|
||||
*/
|
||||
void unregisterListener(in nsIPresentationListener listener);
|
||||
|
||||
/*
|
||||
* Register a session listener. Must be called from the main thread.
|
||||
*
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
* @param listener: The listener to register.
|
||||
*/
|
||||
void registerSessionListener(in DOMString sessionId,
|
||||
in nsIPresentationSessionListener listener);
|
||||
|
||||
/*
|
||||
* Unregister a session listener. Must be called from the main thread.
|
||||
*
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
*/
|
||||
void unregisterSessionListener(in DOMString sessionId);
|
||||
|
||||
/*
|
||||
* Check if the presentation instance has an existent session ID at launch.
|
||||
* An empty string is returned at sender side; non-empty at receiver side.
|
||||
*/
|
||||
DOMString getExistentSessionIdAtLaunch();
|
||||
|
||||
/*
|
||||
* Notify the receiver page is ready for presentation use.
|
||||
*
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
*/
|
||||
void notifyReceiverReady(in DOMString sessionId);
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIInputStream;
|
||||
interface nsINetAddr;
|
||||
interface nsIPresentationChannelDescription;
|
||||
interface nsISocketTransport;
|
||||
|
||||
%{C++
|
||||
#define PRESENTATION_SESSION_TRANSPORT_CONTRACTID \
|
||||
"@mozilla.org/presentation/presentationsessiontransport;1"
|
||||
%}
|
||||
|
||||
/*
|
||||
* The callback for session transport events.
|
||||
*/
|
||||
[scriptable, uuid(9f158786-41a6-4a10-b29b-9497f25d4b67)]
|
||||
interface nsIPresentationSessionTransportCallback : nsISupports
|
||||
{
|
||||
void notifyTransportReady();
|
||||
void notifyTransportClosed(in nsresult reason);
|
||||
void notifyData(in ACString data);
|
||||
};
|
||||
|
||||
/*
|
||||
* App-to-App transport channel for the presentation session.
|
||||
*/
|
||||
[scriptable, uuid(5a9fb9e9-b846-4c49-ad57-20ed88457295)]
|
||||
interface nsIPresentationSessionTransport : nsISupports
|
||||
{
|
||||
attribute nsIPresentationSessionTransportCallback callback;
|
||||
readonly attribute nsINetAddr selfAddress;
|
||||
|
||||
/*
|
||||
* Initialize the transport channel with an existent socket transport. (This
|
||||
* is primarily used at the sender side.)
|
||||
* @param transport The socket transport.
|
||||
* @param callback The callback for followup notifications.
|
||||
*/
|
||||
void initWithSocketTransport(in nsISocketTransport transport,
|
||||
in nsIPresentationSessionTransportCallback callback);
|
||||
|
||||
/*
|
||||
* Initialize the transport channel with the channel description. (This is
|
||||
* primarily used at the receiver side.)
|
||||
* @param description The channel description.
|
||||
* @param callback The callback for followup notifications.
|
||||
*/
|
||||
void initWithChannelDescription(in nsIPresentationChannelDescription description,
|
||||
in nsIPresentationSessionTransportCallback callback);
|
||||
|
||||
/*
|
||||
* Send message to the remote endpoint.
|
||||
* @param data The message to send.
|
||||
*/
|
||||
void send(in nsIInputStream data);
|
||||
|
||||
/*
|
||||
* Close this session transport.
|
||||
* @param reason The reason for closing this session transport.
|
||||
*/
|
||||
void close(in nsresult reason);
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PContent;
|
||||
include protocol PPresentationRequest;
|
||||
|
||||
include InputStreamParams;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct StartSessionRequest
|
||||
{
|
||||
nsString url;
|
||||
nsString sessionId;
|
||||
nsString origin;
|
||||
};
|
||||
|
||||
struct SendSessionMessageRequest
|
||||
{
|
||||
nsString sessionId;
|
||||
InputStreamParams data;
|
||||
};
|
||||
|
||||
struct TerminateRequest
|
||||
{
|
||||
nsString sessionId;
|
||||
};
|
||||
|
||||
union PresentationRequest
|
||||
{
|
||||
StartSessionRequest;
|
||||
SendSessionMessageRequest;
|
||||
TerminateRequest;
|
||||
};
|
||||
|
||||
sync protocol PPresentation
|
||||
{
|
||||
manager PContent;
|
||||
manages PPresentationRequest;
|
||||
|
||||
child:
|
||||
NotifyAvailableChange(bool aAvailable);
|
||||
NotifySessionStateChange(nsString aSessionId, uint16_t aState);
|
||||
NotifyMessage(nsString aSessionId, nsCString aData);
|
||||
|
||||
parent:
|
||||
__delete__();
|
||||
|
||||
RegisterHandler();
|
||||
UnregisterHandler();
|
||||
|
||||
RegisterSessionHandler(nsString aSessionId);
|
||||
UnregisterSessionHandler(nsString aSessionId);
|
||||
|
||||
PPresentationRequest(PresentationRequest aRequest);
|
||||
|
||||
sync GetExistentSessionIdAtLaunch()
|
||||
returns (nsString aSessionId);
|
||||
|
||||
NotifyReceiverReady(nsString aSessionId);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,21 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PPresentation;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
sync protocol PPresentationRequest
|
||||
{
|
||||
manager PPresentation;
|
||||
|
||||
child:
|
||||
__delete__(nsresult result);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,130 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "PresentationChild.h"
|
||||
#include "PresentationIPCService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
/*
|
||||
* Implementation of PresentationChild
|
||||
*/
|
||||
|
||||
PresentationChild::PresentationChild(PresentationIPCService* aService)
|
||||
: mActorDestroyed(false)
|
||||
, mService(aService)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
|
||||
MOZ_COUNT_CTOR(PresentationChild);
|
||||
}
|
||||
|
||||
PresentationChild::~PresentationChild()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PresentationChild);
|
||||
|
||||
if (!mActorDestroyed) {
|
||||
Send__delete__(this);
|
||||
}
|
||||
mService = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
mActorDestroyed = true;
|
||||
mService->NotifyPresentationChildDestroyed();
|
||||
mService = nullptr;
|
||||
}
|
||||
|
||||
PPresentationRequestChild*
|
||||
PresentationChild::AllocPPresentationRequestChild(const PresentationRequest& aRequest)
|
||||
{
|
||||
NS_NOTREACHED("We should never be manually allocating PPresentationRequestChild actors");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationChild::DeallocPPresentationRequestChild(PPresentationRequestChild* aActor)
|
||||
{
|
||||
delete aActor;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationChild::RecvNotifyAvailableChange(const bool& aAvailable)
|
||||
{
|
||||
if (mService) {
|
||||
NS_WARN_IF(NS_FAILED(mService->NotifyAvailableChange(aAvailable)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationChild::RecvNotifySessionStateChange(const nsString& aSessionId,
|
||||
const uint16_t& aState)
|
||||
{
|
||||
if (mService) {
|
||||
NS_WARN_IF(NS_FAILED(mService->NotifySessionStateChange(aSessionId, aState)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationChild::RecvNotifyMessage(const nsString& aSessionId,
|
||||
const nsCString& aData)
|
||||
{
|
||||
if (mService) {
|
||||
NS_WARN_IF(NS_FAILED(mService->NotifyMessage(aSessionId, aData)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of PresentationRequestChild
|
||||
*/
|
||||
|
||||
PresentationRequestChild::PresentationRequestChild(nsIPresentationServiceCallback* aCallback)
|
||||
: mActorDestroyed(false)
|
||||
, mCallback(aCallback)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PresentationRequestChild);
|
||||
}
|
||||
|
||||
PresentationRequestChild::~PresentationRequestChild()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PresentationRequestChild);
|
||||
|
||||
mCallback = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationRequestChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
mActorDestroyed = true;
|
||||
mCallback = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationRequestChild::Recv__delete__(const nsresult& aResult)
|
||||
{
|
||||
if (mActorDestroyed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mCallback) {
|
||||
if (NS_SUCCEEDED(aResult)) {
|
||||
NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess()));
|
||||
} else {
|
||||
NS_WARN_IF(NS_FAILED(mCallback->NotifyError(aResult)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationChild_h
|
||||
#define mozilla_dom_PresentationChild_h
|
||||
|
||||
#include "mozilla/dom/PPresentationChild.h"
|
||||
#include "mozilla/dom/PPresentationRequestChild.h"
|
||||
|
||||
class nsIPresentationServiceCallback;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationIPCService;
|
||||
|
||||
class PresentationChild final : public PPresentationChild
|
||||
{
|
||||
public:
|
||||
explicit PresentationChild(PresentationIPCService* aService);
|
||||
|
||||
virtual void
|
||||
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
virtual PPresentationRequestChild*
|
||||
AllocPPresentationRequestChild(const PresentationRequest& aRequest) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPPresentationRequestChild(PPresentationRequestChild* aActor) override;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyAvailableChange(const bool& aAvailable) override;
|
||||
|
||||
virtual bool
|
||||
RecvNotifySessionStateChange(const nsString& aSessionId,
|
||||
const uint16_t& aState) override;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyMessage(const nsString& aSessionId,
|
||||
const nsCString& aData) override;
|
||||
|
||||
private:
|
||||
virtual ~PresentationChild();
|
||||
|
||||
bool mActorDestroyed;
|
||||
nsRefPtr<PresentationIPCService> mService;
|
||||
};
|
||||
|
||||
class PresentationRequestChild final : public PPresentationRequestChild
|
||||
{
|
||||
friend class PresentationChild;
|
||||
|
||||
public:
|
||||
explicit PresentationRequestChild(nsIPresentationServiceCallback* aCallback);
|
||||
|
||||
virtual void
|
||||
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
virtual bool
|
||||
Recv__delete__(const nsresult& aResult) override;
|
||||
|
||||
private:
|
||||
virtual ~PresentationRequestChild();
|
||||
|
||||
bool mActorDestroyed;
|
||||
nsCOMPtr<nsIPresentationServiceCallback> mCallback;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationChild_h
|
||||
@@ -0,0 +1,209 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/PPresentation.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
#include "PresentationCallbacks.h"
|
||||
#include "PresentationChild.h"
|
||||
#include "PresentationIPCService.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace {
|
||||
|
||||
PresentationChild* sPresentationChild;
|
||||
|
||||
} // anonymous
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationIPCService, nsIPresentationService)
|
||||
|
||||
PresentationIPCService::PresentationIPCService()
|
||||
{
|
||||
ContentChild* contentChild = ContentChild::GetSingleton();
|
||||
if (NS_WARN_IF(!contentChild)) {
|
||||
return;
|
||||
}
|
||||
sPresentationChild = new PresentationChild(this);
|
||||
NS_WARN_IF(!contentChild->SendPPresentationConstructor(sPresentationChild));
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
PresentationIPCService::~PresentationIPCService()
|
||||
{
|
||||
mListeners.Clear();
|
||||
mSessionListeners.Clear();
|
||||
sPresentationChild = nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::StartSession(const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
const nsAString& aOrigin,
|
||||
nsIPresentationServiceCallback* aCallback)
|
||||
{
|
||||
return SendRequest(aCallback,
|
||||
StartSessionRequest(nsAutoString(aUrl), nsAutoString(aSessionId), nsAutoString(aOrigin)));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::SendSessionMessage(const nsAString& aSessionId,
|
||||
nsIInputStream* aStream)
|
||||
{
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
MOZ_ASSERT(aStream);
|
||||
|
||||
mozilla::ipc::OptionalInputStreamParams stream;
|
||||
nsTArray<mozilla::ipc::FileDescriptor> fds;
|
||||
SerializeInputStream(aStream, stream, fds);
|
||||
MOZ_ASSERT(fds.IsEmpty());
|
||||
|
||||
return SendRequest(nullptr, SendSessionMessageRequest(nsAutoString(aSessionId), stream));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::Terminate(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
|
||||
return SendRequest(nullptr, TerminateRequest(nsAutoString(aSessionId)));
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::SendRequest(nsIPresentationServiceCallback* aCallback,
|
||||
const PresentationRequest& aRequest)
|
||||
{
|
||||
if (sPresentationChild) {
|
||||
PresentationRequestChild* actor = new PresentationRequestChild(aCallback);
|
||||
NS_WARN_IF(!sPresentationChild->SendPPresentationRequestConstructor(actor, aRequest));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::RegisterListener(nsIPresentationListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
|
||||
mListeners.AppendElement(aListener);
|
||||
if (sPresentationChild) {
|
||||
NS_WARN_IF(!sPresentationChild->SendRegisterHandler());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::UnregisterListener(nsIPresentationListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
|
||||
mListeners.RemoveElement(aListener);
|
||||
if (sPresentationChild) {
|
||||
NS_WARN_IF(!sPresentationChild->SendUnregisterHandler());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::RegisterSessionListener(const nsAString& aSessionId,
|
||||
nsIPresentationSessionListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
|
||||
mSessionListeners.Put(aSessionId, aListener);
|
||||
if (sPresentationChild) {
|
||||
NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler(nsAutoString(aSessionId)));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mSessionListeners.Remove(aSessionId);
|
||||
if (sPresentationChild) {
|
||||
NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler(nsAutoString(aSessionId)));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId,
|
||||
uint16_t aState)
|
||||
{
|
||||
nsCOMPtr<nsIPresentationSessionListener> listener;
|
||||
if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return listener->NotifyStateChange(aSessionId, aState);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::NotifyMessage(const nsAString& aSessionId,
|
||||
const nsACString& aData)
|
||||
{
|
||||
nsCOMPtr<nsIPresentationSessionListener> listener;
|
||||
if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return listener->NotifyMessage(aSessionId, aData);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::NotifyAvailableChange(bool aAvailable)
|
||||
{
|
||||
nsTObserverArray<nsCOMPtr<nsIPresentationListener> >::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsIPresentationListener* listener = iter.GetNext();
|
||||
NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(aAvailable)));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::GetExistentSessionIdAtLaunch(nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsAutoString sessionId(aSessionId);
|
||||
NS_WARN_IF(!sPresentationChild->SendGetExistentSessionIdAtLaunch(&sessionId));
|
||||
aSessionId = sessionId;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::NotifyReceiverReady(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsAutoString(aSessionId)));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationIPCService::NotifyPresentationChildDestroyed()
|
||||
{
|
||||
sPresentationChild = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::MonitorResponderLoading(const nsAString& aSessionId,
|
||||
nsIDocShell* aDocShell)
|
||||
{
|
||||
mCallback = new PresentationResponderLoadingCallback(aSessionId);
|
||||
return mCallback->Init(aDocShell);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationIPCService_h
|
||||
#define mozilla_dom_PresentationIPCService_h
|
||||
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
class nsIDocShell;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationRequest;
|
||||
class PresentationResponderLoadingCallback;
|
||||
|
||||
class PresentationIPCService final : public nsIPresentationService
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONSERVICE
|
||||
|
||||
PresentationIPCService();
|
||||
|
||||
nsresult NotifyAvailableChange(bool aAvailable);
|
||||
|
||||
nsresult NotifySessionStateChange(const nsAString& aSessionId,
|
||||
uint16_t aState);
|
||||
|
||||
nsresult NotifyMessage(const nsAString& aSessionId,
|
||||
const nsACString& aData);
|
||||
|
||||
void NotifyPresentationChildDestroyed();
|
||||
|
||||
nsresult MonitorResponderLoading(const nsAString& aSessionId,
|
||||
nsIDocShell* aDocShell);
|
||||
|
||||
private:
|
||||
virtual ~PresentationIPCService();
|
||||
nsresult SendRequest(nsIPresentationServiceCallback* aCallback,
|
||||
const PresentationRequest& aRequest);
|
||||
|
||||
nsTObserverArray<nsCOMPtr<nsIPresentationListener> > mListeners;
|
||||
nsRefPtrHashtable<nsStringHashKey, nsIPresentationSessionListener> mSessionListeners;
|
||||
nsRefPtr<PresentationResponderLoadingCallback> mCallback;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationIPCService_h
|
||||
@@ -0,0 +1,265 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "nsIPresentationDeviceManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "PresentationParent.h"
|
||||
#include "PresentationService.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
/*
|
||||
* Implementation of PresentationParent
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationParent, nsIPresentationListener, nsIPresentationSessionListener)
|
||||
|
||||
PresentationParent::PresentationParent()
|
||||
: mActorDestroyed(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PresentationParent);
|
||||
}
|
||||
|
||||
/* virtual */ PresentationParent::~PresentationParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PresentationParent);
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::Init()
|
||||
{
|
||||
MOZ_ASSERT(!mService);
|
||||
mService = do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
return NS_WARN_IF(!mService) ? false : true;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
mActorDestroyed = true;
|
||||
|
||||
for (uint32_t i = 0; i < mSessionIds.Length(); i++) {
|
||||
NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(mSessionIds[i])));
|
||||
}
|
||||
mSessionIds.Clear();
|
||||
|
||||
mService->UnregisterListener(this);
|
||||
mService = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::RecvPPresentationRequestConstructor(
|
||||
PPresentationRequestParent* aActor,
|
||||
const PresentationRequest& aRequest)
|
||||
{
|
||||
PresentationRequestParent* actor = static_cast<PresentationRequestParent*>(aActor);
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
switch (aRequest.type()) {
|
||||
case PresentationRequest::TStartSessionRequest:
|
||||
rv = actor->DoRequest(aRequest.get_StartSessionRequest());
|
||||
break;
|
||||
case PresentationRequest::TSendSessionMessageRequest:
|
||||
rv = actor->DoRequest(aRequest.get_SendSessionMessageRequest());
|
||||
break;
|
||||
case PresentationRequest::TTerminateRequest:
|
||||
rv = actor->DoRequest(aRequest.get_TerminateRequest());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown PresentationRequest type");
|
||||
}
|
||||
|
||||
return NS_WARN_IF(NS_FAILED(rv)) ? false : true;
|
||||
}
|
||||
|
||||
PPresentationRequestParent*
|
||||
PresentationParent::AllocPPresentationRequestParent(
|
||||
const PresentationRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
nsRefPtr<PresentationRequestParent> actor = new PresentationRequestParent(mService);
|
||||
return actor.forget().take();
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::DeallocPPresentationRequestParent(
|
||||
PPresentationRequestParent* aActor)
|
||||
{
|
||||
nsRefPtr<PresentationRequestParent> actor =
|
||||
dont_AddRef(static_cast<PresentationRequestParent*>(aActor));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::Recv__delete__()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::RecvRegisterHandler()
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
NS_WARN_IF(NS_FAILED(mService->RegisterListener(this)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::RecvUnregisterHandler()
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
NS_WARN_IF(NS_FAILED(mService->UnregisterListener(this)));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
mSessionIds.AppendElement(aSessionId);
|
||||
NS_WARN_IF(NS_FAILED(mService->RegisterSessionListener(aSessionId, this)));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
mSessionIds.RemoveElement(aSessionId);
|
||||
NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(aSessionId)));
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationParent::NotifyAvailableChange(bool aAvailable)
|
||||
{
|
||||
if (NS_WARN_IF(mActorDestroyed || !SendNotifyAvailableChange(aAvailable))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationParent::NotifyStateChange(const nsAString& aSessionId,
|
||||
uint16_t aState)
|
||||
{
|
||||
if (NS_WARN_IF(mActorDestroyed ||
|
||||
!SendNotifySessionStateChange(nsString(aSessionId), aState))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationParent::NotifyMessage(const nsAString& aSessionId,
|
||||
const nsACString& aData)
|
||||
{
|
||||
if (NS_WARN_IF(mActorDestroyed ||
|
||||
!SendNotifyMessage(nsString(aSessionId), nsCString(aData)))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::RecvGetExistentSessionIdAtLaunch(nsString* aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
NS_WARN_IF(NS_FAILED(mService->GetExistentSessionIdAtLaunch(*aSessionId)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationParent::RecvNotifyReceiverReady(const nsString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId)));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of PresentationRequestParent
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationRequestParent, nsIPresentationServiceCallback)
|
||||
|
||||
PresentationRequestParent::PresentationRequestParent(nsIPresentationService* aService)
|
||||
: mActorDestroyed(false)
|
||||
, mService(aService)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PresentationRequestParent);
|
||||
}
|
||||
|
||||
PresentationRequestParent::~PresentationRequestParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PresentationRequestParent);
|
||||
}
|
||||
|
||||
void
|
||||
PresentationRequestParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
mActorDestroyed = true;
|
||||
mService = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequestParent::DoRequest(const StartSessionRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
return mService->StartSession(aRequest.url(), aRequest.sessionId(),
|
||||
aRequest.origin(), this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequestParent::DoRequest(const SendSessionMessageRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
nsTArray<mozilla::ipc::FileDescriptor> fds;
|
||||
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aRequest.data(), fds);
|
||||
if(NS_WARN_IF(!stream)) {
|
||||
return NotifyError(NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
nsresult rv = mService->SendSessionMessage(aRequest.sessionId(), stream);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NotifyError(rv);
|
||||
}
|
||||
return NotifySuccess();
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequestParent::DoRequest(const TerminateRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
nsresult rv = mService->Terminate(aRequest.sessionId());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NotifyError(rv);
|
||||
}
|
||||
return NotifySuccess();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequestParent::NotifySuccess()
|
||||
{
|
||||
return SendResponse(NS_OK);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequestParent::NotifyError(nsresult aError)
|
||||
{
|
||||
return SendResponse(aError);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequestParent::SendResponse(nsresult aResult)
|
||||
{
|
||||
if (NS_WARN_IF(mActorDestroyed || !Send__delete__(this, aResult))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationParent_h__
|
||||
#define mozilla_dom_PresentationParent_h__
|
||||
|
||||
#include "mozilla/dom/PPresentationParent.h"
|
||||
#include "mozilla/dom/PPresentationRequestParent.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
#include "nsIPresentationService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationParent final : public PPresentationParent
|
||||
, public nsIPresentationListener
|
||||
, public nsIPresentationSessionListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONLISTENER
|
||||
NS_DECL_NSIPRESENTATIONSESSIONLISTENER
|
||||
|
||||
PresentationParent();
|
||||
|
||||
bool Init();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
virtual bool
|
||||
RecvPPresentationRequestConstructor(PPresentationRequestParent* aActor,
|
||||
const PresentationRequest& aRequest) override;
|
||||
|
||||
virtual PPresentationRequestParent*
|
||||
AllocPPresentationRequestParent(const PresentationRequest& aRequest) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPPresentationRequestParent(PPresentationRequestParent* aActor) override;
|
||||
|
||||
virtual bool Recv__delete__() override;
|
||||
|
||||
virtual bool RecvRegisterHandler() override;
|
||||
|
||||
virtual bool RecvUnregisterHandler() override;
|
||||
|
||||
virtual bool RecvRegisterSessionHandler(const nsString& aSessionId) override;
|
||||
|
||||
virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId) override;
|
||||
|
||||
virtual bool RecvGetExistentSessionIdAtLaunch(nsString* aSessionId) override;
|
||||
|
||||
virtual bool RecvNotifyReceiverReady(const nsString& aSessionId) override;
|
||||
|
||||
private:
|
||||
virtual ~PresentationParent();
|
||||
|
||||
bool mActorDestroyed;
|
||||
nsCOMPtr<nsIPresentationService> mService;
|
||||
nsTArray<nsString> mSessionIds;
|
||||
};
|
||||
|
||||
class PresentationRequestParent final : public PPresentationRequestParent
|
||||
, public nsIPresentationServiceCallback
|
||||
{
|
||||
friend class PresentationParent;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPRESENTATIONSERVICECALLBACK
|
||||
|
||||
explicit PresentationRequestParent(nsIPresentationService* aService);
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
virtual ~PresentationRequestParent();
|
||||
|
||||
nsresult SendResponse(nsresult aResult);
|
||||
|
||||
nsresult DoRequest(const StartSessionRequest& aRequest);
|
||||
|
||||
nsresult DoRequest(const SendSessionMessageRequest& aRequest);
|
||||
|
||||
nsresult DoRequest(const TerminateRequest& aRequest);
|
||||
|
||||
bool mActorDestroyed;
|
||||
nsCOMPtr<nsIPresentationService> mService;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationParent_h__
|
||||
@@ -9,13 +9,31 @@ DIRS += ['interfaces', 'provider']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
|
||||
|
||||
EXPORTS.mozilla.dom.presentation += [
|
||||
EXPORTS.mozilla.dom += [
|
||||
'ipc/PresentationChild.h',
|
||||
'ipc/PresentationIPCService.h',
|
||||
'ipc/PresentationParent.h',
|
||||
'Presentation.h',
|
||||
'PresentationCallbacks.h',
|
||||
'PresentationDeviceManager.h',
|
||||
'PresentationService.h',
|
||||
'PresentationSession.h',
|
||||
'PresentationSessionInfo.h',
|
||||
'PresentationSessionTransport.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'ipc/PresentationChild.cpp',
|
||||
'ipc/PresentationIPCService.cpp',
|
||||
'ipc/PresentationParent.cpp',
|
||||
'Presentation.cpp',
|
||||
'PresentationCallbacks.cpp',
|
||||
'PresentationDeviceManager.cpp',
|
||||
'PresentationService.cpp',
|
||||
'PresentationSession.cpp',
|
||||
'PresentationSessionInfo.cpp',
|
||||
'PresentationSessionRequest.cpp',
|
||||
'PresentationSessionTransport.cpp',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
@@ -27,6 +45,11 @@ EXTRA_JS_MODULES += [
|
||||
'PresentationDeviceInfoManager.jsm',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'ipc/PPresentation.ipdl',
|
||||
'ipc/PPresentationRequest.ipdl'
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
/* -*- 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');
|
||||
|
||||
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) {
|
||||
registrar.unregisterFactory(mockedClassId, mockedFactory);
|
||||
registrar.registerFactory(originalClassId, "", contractId, originalFactory);
|
||||
}
|
||||
}
|
||||
|
||||
const sessionId = 'test-session-id';
|
||||
|
||||
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]),
|
||||
type: 1,
|
||||
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');
|
||||
},
|
||||
sendAnswer: function(answer) {
|
||||
this._listener.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady();
|
||||
},
|
||||
close: function(reason) {
|
||||
sendAsyncMessage('control-channel-closed', reason);
|
||||
this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyClosed(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);
|
||||
},
|
||||
};
|
||||
|
||||
const mockedDevice = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
type: 'type',
|
||||
establishControlChannel: function(url, presentationId) {
|
||||
sendAsyncMessage('control-channel-established');
|
||||
return mockedControlChannel;
|
||||
},
|
||||
set listener(listener) {
|
||||
this._listener = listener;
|
||||
},
|
||||
get listener() {
|
||||
return this._listener;
|
||||
},
|
||||
simulateSessionRequest: function(url, presentationId, controlChannel) {
|
||||
this._listener.onSessionRequest(this, url, presentationId, controlChannel);
|
||||
},
|
||||
};
|
||||
|
||||
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() {
|
||||
this._request.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
const mockedSessionTransport = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport,
|
||||
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;
|
||||
},
|
||||
initWithSocketTransport: function(transport, callback) {
|
||||
sendAsyncMessage('data-transport-initialized');
|
||||
this._callback = callback;
|
||||
this.simulateTransportReady();
|
||||
},
|
||||
initWithChannelDescription: function(description, callback) {
|
||||
this._callback = callback;
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
send: function(data) {
|
||||
var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIBinaryInputStream);
|
||||
binaryStream.setInputStream(data);
|
||||
var message = binaryStream.readBytes(binaryStream.available());
|
||||
sendAsyncMessage('message-sent', message);
|
||||
},
|
||||
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);
|
||||
},
|
||||
};
|
||||
|
||||
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 uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator);
|
||||
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/presentationsessiontransport;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 in 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() {
|
||||
mockedDevicePrompt.simulateCancel();
|
||||
});
|
||||
|
||||
addMessageListener('trigger-incoming-session-request', function(url) {
|
||||
mockedDevice.simulateSessionRequest(url, sessionId, mockedControlChannel);
|
||||
});
|
||||
|
||||
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-close', function(reason) {
|
||||
mockedControlChannel.close(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 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);
|
||||
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for B2G Presentation Session 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) {
|
||||
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 session;
|
||||
|
||||
function testSessionAvailable() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
|
||||
session = navigator.presentation.session;
|
||||
ok(session.id, "Session ID should be set: " + session.id);
|
||||
is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
|
||||
aResolve();
|
||||
});
|
||||
}
|
||||
|
||||
function testSessionReady() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "connected", "Session state should become connected.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
command({ name: 'trigger-incoming-offer' });
|
||||
});
|
||||
}
|
||||
|
||||
function testCloseSession() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.close();
|
||||
});
|
||||
}
|
||||
|
||||
testSessionAvailable().
|
||||
then(testSessionReady).
|
||||
then(testCloseSession).
|
||||
then(finish);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for B2G Presentation Session API at receiver side (OOP)</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 session;
|
||||
|
||||
function testSessionAvailable() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
|
||||
session = navigator.presentation.session;
|
||||
ok(session.id, "Session ID should be set: " + session.id);
|
||||
is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
|
||||
aResolve();
|
||||
});
|
||||
}
|
||||
|
||||
function testSessionReady() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "connected", "Session state should become connected.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
command({ name: 'trigger-incoming-offer' });
|
||||
});
|
||||
}
|
||||
|
||||
function testCloseSession() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.close();
|
||||
});
|
||||
}
|
||||
|
||||
testSessionAvailable().
|
||||
then(testSessionReady).
|
||||
then(testCloseSession).
|
||||
then(finish);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for startSession 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) {
|
||||
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 session;
|
||||
|
||||
function testSessionAvailable() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
|
||||
session = navigator.presentation.session;
|
||||
ok(session.id, "Session ID should be set: " + session.id);
|
||||
is(session.state, "disconnected", "Session state at receiver side should be disconnected by default.");
|
||||
aResolve();
|
||||
});
|
||||
}
|
||||
|
||||
function testUnexpectedControlChannelClose() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session state should become terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
// Trigger the control channel to be closed with error code.
|
||||
command({ name: 'trigger-control-channel-close', data: 0x80004004 /* NS_ERROR_ABORT */ });
|
||||
});
|
||||
}
|
||||
|
||||
testSessionAvailable().
|
||||
then(testUnexpectedControlChannelClose).
|
||||
then(finish);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +1,24 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
PresentationDeviceInfoChromeScript.js
|
||||
PresentationSessionChromeScript.js
|
||||
file_presentation_receiver.html
|
||||
file_presentation_receiver_oop.html
|
||||
file_presentation_receiver_start_session_error.html
|
||||
|
||||
[test_presentation_device_info.html]
|
||||
[test_presentation_device_info_permission.html]
|
||||
[test_presentation_sender_disconnect.html]
|
||||
skip-if = toolkit == 'android' # Bug 1129785
|
||||
[test_presentation_sender_start_session_error.html]
|
||||
skip-if = toolkit == 'android' # Bug 1129785
|
||||
[test_presentation_sender.html]
|
||||
skip-if = toolkit == 'android' # Bug 1129785
|
||||
[test_presentation_receiver_start_session_error.html]
|
||||
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
|
||||
[test_presentation_receiver_start_session_timeout.html]
|
||||
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
|
||||
[test_presentation_receiver.html]
|
||||
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
|
||||
[test_presentation_receiver_oop.html]
|
||||
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<!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 Session 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 Presentation Session 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);
|
||||
|
||||
// This event is triggered when the iframe calls "postMessage".
|
||||
window.addEventListener('message', function listener(aEvent) {
|
||||
var message = aEvent.data;
|
||||
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.");
|
||||
window.removeEventListener('message', 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('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-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.expectAssertions(0, 5);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'presentation-device-manage', allow: false, context: document},
|
||||
{type: 'presentation', allow: true, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.presentation.test.enabled", true],
|
||||
["dom.presentation.test.stage", 0]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,131 @@
|
||||
<!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 Session 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 Presentation Session 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_oop.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');
|
||||
|
||||
SpecialPowers.addPermission('presentation', true, { url: receiverUrl,
|
||||
appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
|
||||
isInBrowserElement: true });
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('remote', 'true');
|
||||
iframe.setAttribute('mozbrowser', 'true');
|
||||
iframe.setAttribute('src', receiverUrl);
|
||||
|
||||
// This event is triggered when the iframe calls "alert".
|
||||
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('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-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.expectAssertions(0, 5);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'presentation-device-manage', allow: false, context: document},
|
||||
{type: 'presentation', allow: true, context: document},
|
||||
{type: 'browser', allow: true, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.presentation.test.enabled", true],
|
||||
["dom.presentation.test.stage", 0],
|
||||
["dom.mozBrowserFramesEnabled", true],
|
||||
["dom.ipc.browser_frames.oop_by_default", true]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</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 startSession 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 startSession 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_start_session_error.html');
|
||||
|
||||
var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIObserverService);
|
||||
var session;
|
||||
|
||||
function setup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('src', receiverUrl);
|
||||
|
||||
// This event is triggered when the iframe calls "postMessage".
|
||||
window.addEventListener('message', function listener(aEvent) {
|
||||
var message = aEvent.data;
|
||||
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.");
|
||||
window.removeEventListener('message', 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('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.expectAssertions(0, 5);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'presentation-device-manage', allow: false, context: document},
|
||||
{type: 'presentation', allow: true, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.presentation.test.enabled", true],
|
||||
["dom.presentation.test.stage", 0]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<!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 startSession 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 startSession 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.expectAssertions(0, 5);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'presentation-device-manage', allow: false, context: document},
|
||||
{type: 'presentation', allow: true, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.presentation.test.enabled", true],
|
||||
["dom.presentation.test.stage", 0],
|
||||
["presentation.receiver.loading.timeout", 10]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,167 @@
|
||||
<!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>
|
||||
</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 presentation;
|
||||
var session;
|
||||
|
||||
function testSetup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
presentation.onavailablechange = function(aIsAvailable) {
|
||||
presentation.onavailablechange = null;
|
||||
ok(aIsAvailable, "Device should be available.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSession() {
|
||||
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.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');
|
||||
});
|
||||
|
||||
presentation.startSession("http://example.com").then(
|
||||
function(aSession) {
|
||||
session = aSession;
|
||||
ok(session, "Session should be availlable.");
|
||||
ok(session.id, "Session ID should be set.");
|
||||
is(session.state, "connected", "Session state at sender side should be connected by default.");
|
||||
aResolve();
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when starting session: " + 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();
|
||||
});
|
||||
|
||||
session.send(outgoingMessage);
|
||||
});
|
||||
}
|
||||
|
||||
function testIncomingMessage() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
const incomingMessage = "test incoming message";
|
||||
|
||||
session.addEventListener('message', function messageHandler(aEvent) {
|
||||
session.removeEventListener('message', messageHandler);
|
||||
is(aEvent.data, incomingMessage, "An incoming message should be received.");
|
||||
aResolve();
|
||||
});
|
||||
|
||||
gScript.sendAsyncMessage('trigger-incoming-message', incomingMessage);
|
||||
});
|
||||
}
|
||||
|
||||
function testCloseSession() {
|
||||
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);
|
||||
});
|
||||
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.close();
|
||||
});
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
|
||||
gScript.removeMessageListener('teardown-complete', teardownCompleteHandler);
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
gScript.sendAsyncMessage('teardown');
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
presentation = navigator.presentation;
|
||||
|
||||
testSetup().
|
||||
then(testStartSession).
|
||||
then(testSend).
|
||||
then(testIncomingMessage).
|
||||
then(testCloseSession).
|
||||
then(teardown);
|
||||
}
|
||||
|
||||
SimpleTest.expectAssertions(0, 5);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'presentation-device-manage', allow: false, context: document},
|
||||
{type: 'presentation', allow: true, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.presentation.test.enabled", true],
|
||||
["dom.presentation.test.stage", 0]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,150 @@
|
||||
<!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 session 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 session 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 presentation;
|
||||
var session;
|
||||
|
||||
function testSetup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
presentation.onavailablechange = function(aIsAvailable) {
|
||||
presentation.onavailablechange = null;
|
||||
ok(aIsAvailable, "Device should be available.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSession() {
|
||||
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.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-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.");
|
||||
});
|
||||
|
||||
presentation.startSession("http://example.com").then(
|
||||
function(aSession) {
|
||||
session = aSession;
|
||||
ok(session, "Session should be availlable.");
|
||||
ok(session.id, "Session ID should be set.");
|
||||
is(session.state, "connected", "Session state at sender side should be connected by default.");
|
||||
aResolve();
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when starting session: " + aError);
|
||||
teardown();
|
||||
aReject();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function testSessionDisconnection() {
|
||||
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);
|
||||
});
|
||||
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "disconnected", "Session should be disconnected.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_FAILURE);
|
||||
});
|
||||
}
|
||||
|
||||
function testCloseSession() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.close();
|
||||
});
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
|
||||
gScript.removeMessageListener('teardown-complete', teardownCompleteHandler);
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
gScript.sendAsyncMessage('teardown');
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
presentation = navigator.presentation;
|
||||
|
||||
testSetup().
|
||||
then(testStartSession).
|
||||
then(testSessionDisconnection).
|
||||
then(testCloseSession).
|
||||
then(teardown);
|
||||
}
|
||||
|
||||
SimpleTest.expectAssertions(0, 5);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'presentation-device-manage', allow: false, context: document},
|
||||
{type: 'presentation', allow: true, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.presentation.test.enabled", true],
|
||||
["dom.presentation.test.stage", 0]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,239 @@
|
||||
<!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 startSession 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 startSession 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 presentation;
|
||||
|
||||
function setup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
presentation.onavailablechange = function(aIsAvailable) {
|
||||
presentation.onavailablechange = null;
|
||||
ok(aIsAvailable, "Device should be available.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSessionNoAvailableDevice() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
presentation.startSession("http://example.com").then(
|
||||
function(aSession) {
|
||||
ok(false, "startSession shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "InvalidStateError", "InvalidStateError is expected when starting session.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSessionCancelPrompt() {
|
||||
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-cancel');
|
||||
});
|
||||
|
||||
presentation.startSession("http://example.com").then(
|
||||
function(aSession) {
|
||||
ok(false, "startSession shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "NS_ERROR_DOM_PROP_ACCESS_DENIED", "NS_ERROR_DOM_PROP_ACCESS_DENIED is expected when starting session.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportInit() {
|
||||
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.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-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE);
|
||||
});
|
||||
|
||||
presentation.startSession("http://example.com").then(
|
||||
function(aSession) {
|
||||
ok(false, "startSession shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "NS_ERROR_FAILURE", "NS_ERROR_FAILURE is expected when starting session.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportReady() {
|
||||
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.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('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);
|
||||
});
|
||||
|
||||
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
|
||||
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
|
||||
info("The data transport is closed. " + aReason);
|
||||
});
|
||||
|
||||
presentation.startSession("http://example.com").then(
|
||||
function(aSession) {
|
||||
ok(false, "startSession shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "NS_ERROR_ABORT", "NS_ERROR_ABORT is expected when starting session.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSessionUnexpectedDataTransportClose() {
|
||||
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.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('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);
|
||||
});
|
||||
|
||||
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
|
||||
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
|
||||
info("The data transport is closed. " + aReason);
|
||||
});
|
||||
|
||||
presentation.startSession("http://example.com").then(
|
||||
function(aSession) {
|
||||
ok(false, "startSession shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "NS_ERROR_UNEXPECTED", "NS_ERROR_UNEXPECTED is expected when starting session.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() {
|
||||
gScript.removeMessageListener('teardown-complete', teardownCompleteHandler);
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
gScript.sendAsyncMessage('teardown');
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
presentation = navigator.presentation;
|
||||
|
||||
testStartSessionNoAvailableDevice().
|
||||
then(setup).
|
||||
then(testStartSessionCancelPrompt).
|
||||
then(testStartSessionUnexpectedControlChannelCloseBeforeDataTransportInit).
|
||||
then(testStartSessionUnexpectedControlChannelCloseBeforeDataTransportReady).
|
||||
then(testStartSessionUnexpectedDataTransportClose).
|
||||
then(teardown);
|
||||
}
|
||||
|
||||
SimpleTest.expectAssertions(0, 5);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'presentation-device-manage', allow: false, context: document},
|
||||
{type: 'presentation', allow: true, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.presentation.test.enabled", true],
|
||||
["dom.presentation.test.stage", 0]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,171 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr, Constructor: CC } = Components;
|
||||
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
|
||||
"nsIServerSocket",
|
||||
"init");
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
var testServer = null;
|
||||
var clientTransport = null;
|
||||
var serverTransport = null;
|
||||
|
||||
const clientMessage = "Client Message";
|
||||
const serverMessage = "Server Message";
|
||||
|
||||
const address = Cc["@mozilla.org/supports-cstring;1"]
|
||||
.createInstance(Ci.nsISupportsCString);
|
||||
address.data = "127.0.0.1";
|
||||
const addresses = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
addresses.appendElement(address, false);
|
||||
|
||||
const serverChannelDescription = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
|
||||
type: 1,
|
||||
tcpAddress: addresses,
|
||||
};
|
||||
|
||||
var isClientReady = false;
|
||||
var isServerReady = false;
|
||||
var isClientClosed = false;
|
||||
var isServerClosed = false;
|
||||
|
||||
const clientCallback = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
|
||||
notifyTransportReady: function () {
|
||||
Assert.ok(true, "Client transport ready.");
|
||||
|
||||
isClientReady = true;
|
||||
if (isClientReady && isServerReady) {
|
||||
run_next_test();
|
||||
}
|
||||
},
|
||||
notifyTransportClosed: function (aReason) {
|
||||
Assert.ok(true, "Client transport is closed.");
|
||||
|
||||
isClientClosed = true;
|
||||
if (isClientClosed && isServerClosed) {
|
||||
run_next_test();
|
||||
}
|
||||
},
|
||||
notifyData: function(aData) {
|
||||
Assert.equal(aData, serverMessage, "Client transport receives data.");
|
||||
run_next_test();
|
||||
},
|
||||
};
|
||||
|
||||
const serverCallback = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]),
|
||||
notifyTransportReady: function () {
|
||||
Assert.ok(true, "Server transport ready.");
|
||||
|
||||
isServerReady = true;
|
||||
if (isClientReady && isServerReady) {
|
||||
run_next_test();
|
||||
}
|
||||
},
|
||||
notifyTransportClosed: function (aReason) {
|
||||
Assert.ok(true, "Server transport is closed.");
|
||||
|
||||
isServerClosed = true;
|
||||
if (isClientClosed && isServerClosed) {
|
||||
run_next_test();
|
||||
}
|
||||
},
|
||||
notifyData: function(aData) {
|
||||
Assert.equal(aData, clientMessage, "Server transport receives data.");
|
||||
run_next_test();
|
||||
},
|
||||
};
|
||||
|
||||
function TestServer() {
|
||||
this.serverSocket = ServerSocket(-1, true, -1);
|
||||
this.serverSocket.asyncListen(this)
|
||||
}
|
||||
|
||||
TestServer.prototype = {
|
||||
onSocketAccepted: function(aSocket, aTransport) {
|
||||
print("Test server gets a client connection.");
|
||||
serverTransport = Cc["@mozilla.org/presentation/presentationsessiontransport;1"]
|
||||
.createInstance(Ci.nsIPresentationSessionTransport);
|
||||
serverTransport.initWithSocketTransport(aTransport, serverCallback);
|
||||
},
|
||||
onStopListening: function(aSocket) {
|
||||
print("Test server stops listening.");
|
||||
},
|
||||
close: function() {
|
||||
if (this.serverSocket) {
|
||||
this.serverSocket.close();
|
||||
this.serverSocket = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Set up the transport connection and ensure |notifyTransportReady| triggered
|
||||
// at both sides.
|
||||
function setup() {
|
||||
clientTransport = Cc["@mozilla.org/presentation/presentationsessiontransport;1"]
|
||||
.createInstance(Ci.nsIPresentationSessionTransport);
|
||||
clientTransport.initWithChannelDescription(serverChannelDescription, clientCallback);
|
||||
}
|
||||
|
||||
// Test |selfAddress| attribute of |nsIPresentationSessionTransport|.
|
||||
function selfAddress() {
|
||||
var serverSelfAddress = serverTransport.selfAddress;
|
||||
Assert.equal(serverSelfAddress.address, address.data, "The self address of server transport should be set.");
|
||||
Assert.equal(serverSelfAddress.port, testServer.serverSocket.port, "The port of server transport should be set.");
|
||||
|
||||
var clientSelfAddress = clientTransport.selfAddress;
|
||||
Assert.ok(clientSelfAddress.address, "The self address of client transport should be set.");
|
||||
Assert.ok(clientSelfAddress.port, "The port of client transport should be set.");
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Test the client sends a message and then a corresponding notification gets
|
||||
// triggered at the server side.
|
||||
function clientSendMessage() {
|
||||
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stream.setData(clientMessage, clientMessage.length);
|
||||
clientTransport.send(stream);
|
||||
}
|
||||
|
||||
// Test the server sends a message an then a corresponding notification gets
|
||||
// triggered at the client side.
|
||||
function serverSendMessage() {
|
||||
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stream.setData(serverMessage, serverMessage.length);
|
||||
serverTransport.send(stream);
|
||||
}
|
||||
|
||||
function transportClose() {
|
||||
clientTransport.close(Cr.NS_OK);
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
testServer.close();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(setup);
|
||||
add_test(selfAddress);
|
||||
add_test(clientSendMessage);
|
||||
add_test(serverSendMessage);
|
||||
add_test(transportClose);
|
||||
add_test(shutdown);
|
||||
|
||||
function run_test() {
|
||||
testServer = new TestServer();
|
||||
// Get the port of the test server.
|
||||
serverChannelDescription.tcpPort = testServer.serverSocket.port;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
@@ -4,4 +4,5 @@ tail =
|
||||
|
||||
[test_multicast_dns_device_provider.js]
|
||||
[test_presentation_device_manager.js]
|
||||
[test_presentation_session_transport.js]
|
||||
[test_tcp_control_channel.js]
|
||||
|
||||
@@ -63,9 +63,12 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
|
||||
EXTRA_COMPONENTS += [
|
||||
'gonk/TelephonyAudioService.js',
|
||||
'gonk/TelephonyAudioService.manifest',
|
||||
'gonk/TelephonyService.js',
|
||||
'gonk/TelephonyService.manifest',
|
||||
]
|
||||
if not CONFIG['DISABLE_MOZ_RIL_GEOLOC']:
|
||||
EXTRA_COMPONENTS += [
|
||||
'gonk/TelephonyService.js',
|
||||
'gonk/TelephonyService.manifest',
|
||||
]
|
||||
EXTRA_JS_MODULES += [
|
||||
'gonk/DialNumberUtils.jsm'
|
||||
]
|
||||
|
||||
@@ -34,11 +34,11 @@ UNIFIED_SOURCES += [
|
||||
'VoicemailStatus.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
XPIDL_SOURCES += [
|
||||
'gonk/nsIGonkVoicemailService.idl',
|
||||
]
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
|
||||
XPIDL_SOURCES += [
|
||||
'gonk/nsIGonkVoicemailService.idl',
|
||||
]
|
||||
if not CONFIG['DISABLE_MOZ_RIL_GEOLOC']:
|
||||
EXTRA_COMPONENTS += [
|
||||
'gonk/VoicemailService.js',
|
||||
'gonk/VoicemailService.manifest',
|
||||
|
||||
@@ -429,6 +429,11 @@ partial interface Navigator {
|
||||
readonly attribute InputPortManager inputPortManager;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="dom.presentation.enabled", CheckAnyPermissions="presentation", AvailableIn="PrivilegedApps"]
|
||||
readonly attribute Presentation? presentation;
|
||||
};
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
partial interface Navigator {
|
||||
[Func="Navigator::IsE10sEnabled"]
|
||||
|
||||
@@ -363,7 +363,9 @@ WEBIDL_FILES = [
|
||||
'PopupBoxObject.webidl',
|
||||
'Position.webidl',
|
||||
'PositionError.webidl',
|
||||
'Presentation.webidl',
|
||||
'PresentationDeviceInfoManager.webidl',
|
||||
'PresentationSession.webidl',
|
||||
'ProcessingInstruction.webidl',
|
||||
'ProfileTimelineMarker.webidl',
|
||||
'Promise.webidl',
|
||||
@@ -774,6 +776,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
||||
'PluginCrashedEvent.webidl',
|
||||
'PopStateEvent.webidl',
|
||||
'PopupBlockedEvent.webidl',
|
||||
'PresentationAvailableEvent.webidl',
|
||||
'ProgressEvent.webidl',
|
||||
'RecordErrorEvent.webidl',
|
||||
'ScrollViewChangeEvent.webidl',
|
||||
|
||||
@@ -235,6 +235,7 @@ static void Shutdown();
|
||||
#include "mozilla/dom/time/TimeService.h"
|
||||
#include "StreamingProtocolService.h"
|
||||
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsITelephonyService.h"
|
||||
#include "nsIVoicemailService.h"
|
||||
|
||||
@@ -255,7 +256,8 @@ static void Shutdown();
|
||||
|
||||
#include "GMPService.h"
|
||||
|
||||
#include "mozilla/dom/presentation/PresentationDeviceManager.h"
|
||||
#include "mozilla/dom/PresentationDeviceManager.h"
|
||||
#include "mozilla/dom/PresentationSessionTransport.h"
|
||||
|
||||
#include "mozilla/TextInputProcessor.h"
|
||||
|
||||
@@ -288,6 +290,11 @@ using mozilla::gmp::GeckoMediaPluginService;
|
||||
#define PRESENTATION_DEVICE_MANAGER_CID \
|
||||
{ 0xe1e79dec, 0x4085, 0x4994, { 0xac, 0x5b, 0x74, 0x4b, 0x01, 0x66, 0x97, 0xe6 } }
|
||||
|
||||
#define PRESENTATION_SESSION_TRANSPORT_CID \
|
||||
{ 0xc9d023f4, 0x6228, 0x4c07, { 0x8b, 0x1d, 0x9c, 0x19, 0x57, 0x3f, 0xaa, 0x27 } }
|
||||
|
||||
already_AddRefed<nsIPresentationService> NS_CreatePresentationService();
|
||||
|
||||
// Factory Constructor
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(txMozillaXSLTProcessor)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(XPathEvaluator)
|
||||
@@ -344,11 +351,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHapticFeedback)
|
||||
#endif
|
||||
#endif
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ThirdPartyUtil, Init)
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsICellBroadcastService,
|
||||
NS_CreateCellBroadcastService)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISmsService, NS_CreateSmsService)
|
||||
#endif
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIIccService, NS_CreateIccService)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMmsService, NS_CreateMmsService)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileMessageService,
|
||||
@@ -382,14 +387,12 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsVolumeService,
|
||||
#endif
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMediaManagerService,
|
||||
MediaManager::GetInstance)
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileConnectionService,
|
||||
NS_CreateMobileConnectionService)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelephonyService,
|
||||
NS_CreateTelephonyService)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIVoicemailService,
|
||||
NS_CreateVoicemailService)
|
||||
#endif
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(FakeTVService,
|
||||
TVServiceFactory::CreateFakeTVService)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TVTunerData)
|
||||
@@ -400,6 +403,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(TextInputProcessor)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(FakeInputPortService,
|
||||
InputPortServiceFactory::CreateFakeInputPortService)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(InputPortData)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPresentationService,
|
||||
NS_CreatePresentationService)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationSessionTransport)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static bool gInitialized = false;
|
||||
@@ -804,15 +810,15 @@ NS_DEFINE_NAMED_CID(NS_HAPTICFEEDBACK_CID);
|
||||
#endif
|
||||
#endif
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
NS_DEFINE_NAMED_CID(CELLBROADCAST_SERVICE_CID);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
NS_DEFINE_NAMED_CID(GONK_GPS_GEOLOCATION_PROVIDER_CID);
|
||||
#endif
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(CELLBROADCAST_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(TELEPHONY_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_VOICEMAIL_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_MOBILE_CONNECTION_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(SMS_SERVICE_CID);
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(ICC_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(MMS_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(MOBILE_MESSAGE_SERVICE_CID);
|
||||
@@ -848,7 +854,9 @@ NS_DEFINE_NAMED_CID(INPUTPORT_DATA_CID);
|
||||
|
||||
NS_DEFINE_NAMED_CID(GECKO_MEDIA_PLUGIN_SERVICE_CID);
|
||||
|
||||
NS_DEFINE_NAMED_CID(PRESENTATION_SERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(PRESENTATION_DEVICE_MANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(PRESENTATION_SESSION_TRANSPORT_CID);
|
||||
|
||||
NS_DEFINE_NAMED_CID(TEXT_INPUT_PROCESSOR_CID);
|
||||
|
||||
@@ -1108,10 +1116,8 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
||||
#endif
|
||||
{ &kTHIRDPARTYUTIL_CID, false, nullptr, ThirdPartyUtilConstructor },
|
||||
{ &kNS_STRUCTUREDCLONECONTAINER_CID, false, nullptr, nsStructuredCloneContainerConstructor },
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
{ &kCELLBROADCAST_SERVICE_CID, false, nullptr, nsICellBroadcastServiceConstructor },
|
||||
{ &kSMS_SERVICE_CID, false, nullptr, nsISmsServiceConstructor },
|
||||
#endif
|
||||
{ &kICC_SERVICE_CID, false, nullptr, nsIIccServiceConstructor },
|
||||
{ &kMMS_SERVICE_CID, false, nullptr, nsIMmsServiceConstructor },
|
||||
{ &kMOBILE_MESSAGE_SERVICE_CID, false, nullptr, nsIMobileMessageServiceConstructor },
|
||||
@@ -1136,16 +1142,16 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
||||
#ifdef ACCESSIBILITY
|
||||
{ &kNS_ACCESSIBILITY_SERVICE_CID, false, nullptr, CreateA11yService },
|
||||
#endif
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
{ &kTELEPHONY_SERVICE_CID, false, nullptr, nsITelephonyServiceConstructor },
|
||||
{ &kNS_MOBILE_CONNECTION_SERVICE_CID, false, NULL, nsIMobileConnectionServiceConstructor },
|
||||
{ &kNS_VOICEMAIL_SERVICE_CID, false, nullptr, nsIVoicemailServiceConstructor },
|
||||
#endif
|
||||
{ &kFAKE_TV_SERVICE_CID, false, nullptr, FakeTVServiceConstructor },
|
||||
{ &kTV_TUNER_DATA_CID, false, nullptr, TVTunerDataConstructor },
|
||||
{ &kTV_CHANNEL_DATA_CID, false, nullptr, TVChannelDataConstructor },
|
||||
{ &kTV_PROGRAM_DATA_CID, false, nullptr, TVProgramDataConstructor },
|
||||
{ &kPRESENTATION_SERVICE_CID, false, nullptr, nsIPresentationServiceConstructor },
|
||||
{ &kPRESENTATION_DEVICE_MANAGER_CID, false, nullptr, PresentationDeviceManagerConstructor },
|
||||
{ &kPRESENTATION_SESSION_TRANSPORT_CID, false, nullptr, PresentationSessionTransportConstructor },
|
||||
{ &kTEXT_INPUT_PROCESSOR_CID, false, nullptr, TextInputProcessorConstructor },
|
||||
{ &kFAKE_INPUTPORT_SERVICE_CID, false, nullptr, FakeInputPortServiceConstructor },
|
||||
{ &kINPUTPORT_DATA_CID, false, nullptr, InputPortDataConstructor },
|
||||
@@ -1275,10 +1281,8 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||
#endif
|
||||
{ THIRDPARTYUTIL_CONTRACTID, &kTHIRDPARTYUTIL_CID },
|
||||
{ NS_STRUCTUREDCLONECONTAINER_CONTRACTID, &kNS_STRUCTUREDCLONECONTAINER_CID },
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
{ CELLBROADCAST_SERVICE_CONTRACTID, &kCELLBROADCAST_SERVICE_CID },
|
||||
{ SMS_SERVICE_CONTRACTID, &kSMS_SERVICE_CID },
|
||||
#endif
|
||||
{ ICC_SERVICE_CONTRACTID, &kICC_SERVICE_CID },
|
||||
{ MMS_SERVICE_CONTRACTID, &kMMS_SERVICE_CID },
|
||||
{ MOBILE_MESSAGE_SERVICE_CONTRACTID, &kMOBILE_MESSAGE_SERVICE_CID },
|
||||
@@ -1303,19 +1307,17 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||
{ "@mozilla.org/accessibilityService;1", &kNS_ACCESSIBILITY_SERVICE_CID },
|
||||
{ "@mozilla.org/accessibleRetrieval;1", &kNS_ACCESSIBILITY_SERVICE_CID },
|
||||
#endif
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
{ TELEPHONY_SERVICE_CONTRACTID, &kTELEPHONY_SERVICE_CID },
|
||||
#endif
|
||||
{ FAKE_TV_SERVICE_CONTRACTID, &kFAKE_TV_SERVICE_CID },
|
||||
{ TV_TUNER_DATA_CONTRACTID, &kTV_TUNER_DATA_CID },
|
||||
{ TV_CHANNEL_DATA_CONTRACTID, &kTV_CHANNEL_DATA_CID },
|
||||
{ TV_PROGRAM_DATA_CONTRACTID, &kTV_PROGRAM_DATA_CID },
|
||||
{ "@mozilla.org/gecko-media-plugin-service;1", &kGECKO_MEDIA_PLUGIN_SERVICE_CID },
|
||||
#ifndef DISABLE_MOZ_RIL_GEOLOC
|
||||
{ NS_MOBILE_CONNECTION_SERVICE_CONTRACTID, &kNS_MOBILE_CONNECTION_SERVICE_CID },
|
||||
{ NS_VOICEMAIL_SERVICE_CONTRACTID, &kNS_VOICEMAIL_SERVICE_CID },
|
||||
#endif
|
||||
{ PRESENTATION_SERVICE_CONTRACTID, &kPRESENTATION_SERVICE_CID },
|
||||
{ PRESENTATION_DEVICE_MANAGER_CONTRACTID, &kPRESENTATION_DEVICE_MANAGER_CID },
|
||||
{ PRESENTATION_SESSION_TRANSPORT_CONTRACTID, &kPRESENTATION_SESSION_TRANSPORT_CID },
|
||||
{ "@mozilla.org/text-input-processor;1", &kTEXT_INPUT_PROCESSOR_CID },
|
||||
{ FAKE_INPUTPORT_SERVICE_CONTRACTID, &kFAKE_INPUTPORT_SERVICE_CID },
|
||||
{ INPUTPORT_DATA_CONTRACTID, &kINPUTPORT_DATA_CID },
|
||||
@@ -1344,6 +1346,7 @@ static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
|
||||
{ "profile-after-change", "Bluetooth Service", BLUETOOTHSERVICE_CONTRACTID },
|
||||
#endif
|
||||
{ "profile-after-change", "PresentationDeviceManager", PRESENTATION_DEVICE_MANAGER_CONTRACTID },
|
||||
{ "profile-after-change", "PresentationService", PRESENTATION_SERVICE_CONTRACTID },
|
||||
{ "idle-daily", "ServiceWorker Periodic Updater", SERVICEWORKERPERIODICUPDATER_CONTRACTID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
@@ -66,6 +66,14 @@ function openWindow(aEvent) {
|
||||
container.parentNode.appendChild(popupIframe);
|
||||
}
|
||||
container.addEventListener('mozbrowseropenwindow', openWindow);
|
||||
container.addEventListener('mozbrowsershowmodalprompt', function (e) {
|
||||
if (e.detail.message == 'setVisible::false') {
|
||||
container.setVisible(false);
|
||||
}
|
||||
else if (e.detail.message == 'setVisible::true') {
|
||||
container.setVisible(true);
|
||||
}
|
||||
});
|
||||
|
||||
if (outOfProcess) {
|
||||
let specialpowers = {};
|
||||
|
||||
Reference in New Issue
Block a user