Bug 1192727 - Improve the way that Presentation receiver gets the ID of the incoming session. r=smaug

--HG--
extra : histedit_source : 9ca69ab5fb5b440050bf6cd9e9e95242d31c2b27
This commit is contained in:
Sean Lin 2015-08-31 13:24:35 +08:00
parent e921df5cdc
commit 16cb2d0274
19 changed files with 357 additions and 81 deletions

View File

@ -1409,6 +1409,18 @@ ContentChild::RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
return true;
}
bool
ContentChild::RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId)
{
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
NS_WARN_IF(!service);
NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(aSessionId)));
return true;
}
PCrashReporterChild*
ContentChild::AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
const uint32_t& processType)

View File

@ -276,6 +276,7 @@ public:
virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override;
virtual bool RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
const nsString& aSessionId) override;
virtual bool RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override;
virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override;
virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override;

View File

@ -659,6 +659,12 @@ child:
*/
async NotifyPresentationReceiverLaunched(PBrowser aIframe, nsString aSessionId);
/**
* Notify the child that the info about a presentation receiver needs to be
* cleaned up.
*/
async NotifyPresentationReceiverCleanUp(nsString aSessionId);
parent:
/**
* Tell the content process some attributes of itself. This is

View File

@ -81,7 +81,7 @@ Presentation::Init()
// 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);
rv = service->GetExistentSessionIdAtLaunch(GetOwner()->WindowID(), sessionId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}

View File

@ -116,13 +116,19 @@ PresentationResponderLoadingCallback::Init(nsIDocShell* aDocShell)
nsresult
PresentationResponderLoadingCallback::NotifyReceiverReady()
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mProgress);
if (NS_WARN_IF(!window || !window->GetCurrentInnerWindow())) {
return NS_ERROR_NOT_AVAILABLE;
}
uint64_t windowId = window->GetCurrentInnerWindow()->WindowID();
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
return service->NotifyReceiverReady(mSessionId);
return service->NotifyReceiverReady(mSessionId, windowId);
}
// nsIWebProgressListener

View File

@ -221,6 +221,7 @@ PresentationService::HandleShutdown()
mListeners.Clear();
mSessionInfo.Clear();
mRespondingSessionIds.Clear();
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
@ -305,24 +306,11 @@ PresentationService::HandleSessionRequest(nsIPresentationSessionRequest* aReques
}
#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;
}
@ -330,7 +318,6 @@ PresentationService::HandleSessionRequest(nsIPresentationSessionRequest* aReques
rv = info->Init(ctrlChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
ctrlChannel->Close(NS_ERROR_DOM_ABORT_ERR);
mRespondingSessionId.Truncate();
return rv;
}
@ -483,10 +470,6 @@ PresentationService::RegisterSessionListener(const nsAString& aSessionId,
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
@ -512,30 +495,73 @@ PresentationService::UnregisterSessionListener(const nsAString& aSessionId)
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
if (info) {
NS_WARN_IF(NS_FAILED(info->Close(NS_OK)));
RemoveSessionInfo(aSessionId);
UntrackSessionInfo(aSessionId);
return info->SetListener(nullptr);
}
return NS_OK;
}
NS_IMETHODIMP
PresentationService::GetExistentSessionIdAtLaunch(nsAString& aSessionId)
PresentationService::GetExistentSessionIdAtLaunch(uint64_t aWindowId,
nsAString& aSessionId)
{
aSessionId = mRespondingSessionId;
MOZ_ASSERT(NS_IsMainThread());
nsString* sessionId = mRespondingSessionIds.Get(aWindowId);
if (sessionId) {
aSessionId.Assign(*sessionId);
} else {
aSessionId.Truncate();
}
return NS_OK;
}
NS_IMETHODIMP
PresentationService::NotifyReceiverReady(const nsAString& aSessionId)
PresentationService::NotifyReceiverReady(const nsAString& aSessionId,
uint64_t aWindowId)
{
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
if (NS_WARN_IF(!info)) {
return NS_ERROR_NOT_AVAILABLE;
}
// Only track the responding info when an actual window ID, which would never
// be 0, is provided (for an in-process receiver page).
if (aWindowId != 0) {
mRespondingSessionIds.Put(aWindowId, new nsAutoString(aSessionId));
mRespondingWindowIds.Put(aSessionId, aWindowId);
}
return static_cast<PresentationResponderInfo*>(info.get())->NotifyResponderReady();
}
NS_IMETHODIMP
PresentationService::UntrackSessionInfo(const nsAString& aSessionId)
{
// Remove the session info.
mSessionInfo.Remove(aSessionId);
// Remove the in-process responding info if there's still any.
uint64_t windowId = 0;
if(mRespondingWindowIds.Get(aSessionId, &windowId)) {
mRespondingWindowIds.Remove(aSessionId);
mRespondingSessionIds.Remove(windowId);
}
return NS_OK;
}
bool
PresentationService::IsSessionAccessible(const nsAString& aSessionId,
base::ProcessId aProcessId)
{
nsRefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId);
if (NS_WARN_IF(!info)) {
return false;
}
return info->IsAccessible(aProcessId);
}
already_AddRefed<nsIPresentationService>
NS_CreatePresentationService()
{

View File

@ -38,15 +38,8 @@ public:
info.forget() : nullptr;
}
void
RemoveSessionInfo(const nsAString& aSessionId)
{
if (mRespondingSessionId.Equals(aSessionId)) {
mRespondingSessionId.Truncate();
}
mSessionInfo.Remove(aSessionId);
}
bool IsSessionAccessible(const nsAString& aSessionId,
base::ProcessId aProcessId);
private:
~PresentationService();
@ -57,9 +50,16 @@ private:
bool IsAppInstalled(nsIURI* aUri);
bool mIsAvailable;
nsString mRespondingSessionId;
nsRefPtrHashtable<nsStringHashKey, PresentationSessionInfo> mSessionInfo;
nsTObserverArray<nsCOMPtr<nsIPresentationListener>> mListeners;
// Store the mapping between the window ID of the in-process page and the ID
// of the responding session. It's used for an in-process receiver page to
// retrieve the correspondent session ID. Besides, also keep the mapping
// between the responding session ID and the window ID to help look up the
// window ID.
nsClassHashtable<nsUint64HashKey, nsString> mRespondingSessionIds;
nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds;
};
} // namespace dom

View File

@ -192,10 +192,17 @@ 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));
if (!IsSessionReady() && NS_SUCCEEDED(aReason)) {
if (mListener) {
// Notify the listener and the service will untrack the session info after
// the listener calls |UnregisterSessionListener|.
nsresult rv = mListener->NotifyStateChange(mSessionId,
nsIPresentationSessionListener::STATE_TERMINATED);
NS_WARN_IF(NS_FAILED(rv));
} else {
// Directly untrack the session info from the service.
NS_WARN_IF(NS_FAILED(UntrackFromService()));
}
}
Shutdown(aReason);
@ -231,16 +238,29 @@ PresentationSessionInfo::ReplyError(nsresult aError)
}
// Remove itself since it never succeeds.
return UntrackFromService();
}
/* virtual */ nsresult
PresentationSessionInfo::UntrackFromService()
{
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);
static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId);
return NS_OK;
}
/* virtual */ bool
PresentationSessionInfo::IsAccessible(base::ProcessId aProcessId)
{
// No restriction by default.
return true;
}
// nsIPresentationSessionTransportCallback
NS_IMETHODIMP
PresentationSessionInfo::NotifyTransportReady()
@ -280,12 +300,17 @@ PresentationSessionInfo::NotifyTransportClosed(nsresult aReason)
Shutdown(aReason);
uint16_t state = (NS_WARN_IF(NS_FAILED(aReason))) ?
nsIPresentationSessionListener::STATE_DISCONNECTED :
nsIPresentationSessionListener::STATE_TERMINATED;
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;
// If the new state is TERMINATED. the service will untrack the session info
// after the listener calls |UnregisterSessionListener|.
return mListener->NotifyStateChange(mSessionId, state);
} else if (state == nsIPresentationSessionListener::STATE_TERMINATED) {
// Directly untrack the session info from the service.
return UntrackFromService();
}
return NS_OK;
@ -639,6 +664,34 @@ PresentationResponderInfo::InitTransportAndSendAnswer()
return NS_OK;
}
nsresult
PresentationResponderInfo::UntrackFromService()
{
// Remove the OOP responding info (if it has never been used).
if (mContentParent) {
NS_WARN_IF(!static_cast<ContentParent*>(mContentParent.get())->SendNotifyPresentationReceiverCleanUp(mSessionId));
}
// Remove the session info (and the in-process responding info if there's any).
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId);
return NS_OK;
}
bool
PresentationResponderInfo::IsAccessible(base::ProcessId aProcessId)
{
// Only the specific content process should access the responder info.
return (mContentParent) ?
aProcessId == static_cast<ContentParent*>(mContentParent.get())->OtherPid() :
false;
}
nsresult
PresentationResponderInfo::NotifyResponderReady()
{
@ -774,8 +827,10 @@ PresentationResponderInfo::ResolvedCallback(JSContext* aCx,
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));
// Notify the content process that a receiver page has launched, so it can
// start monitoring the loading progress.
mContentParent = tabParent->Manager();
NS_WARN_IF(!static_cast<ContentParent*>(mContentParent.get())->SendNotifyPresentationReceiverLaunched(tabParent, mSessionId));
} else {
// In-process frame
nsCOMPtr<nsIDocShell> docShell;
@ -785,6 +840,7 @@ PresentationResponderInfo::ResolvedCallback(JSContext* aCx,
return;
}
// Keep an eye on the loading progress of the receiver page.
mLoadingCallback = new PresentationResponderLoadingCallback(mSessionId);
rv = mLoadingCallback->Init(docShell);
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -7,6 +7,7 @@
#ifndef mozilla_dom_PresentationSessionInfo_h
#define mozilla_dom_PresentationSessionInfo_h
#include "base/process.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/nsRefPtr.h"
@ -92,6 +93,8 @@ public:
nsresult ReplyError(nsresult aReason);
virtual bool IsAccessible(base::ProcessId aProcessId);
protected:
virtual ~PresentationSessionInfo()
{
@ -107,6 +110,8 @@ protected:
return mIsResponderReady && mIsTransportReady;
}
virtual nsresult UntrackFromService();
nsString mUrl;
nsString mSessionId;
bool mIsResponderReady;
@ -184,6 +189,8 @@ public:
mPromise->AppendNativeHandler(this);
}
bool IsAccessible(base::ProcessId aProcessId) override;
private:
~PresentationResponderInfo()
{
@ -194,10 +201,16 @@ private:
nsresult InitTransportAndSendAnswer();
nsresult UntrackFromService() override;
nsRefPtr<PresentationResponderLoadingCallback> mLoadingCallback;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIPresentationChannelDescription> mRequesterDescription;
nsRefPtr<Promise> mPromise;
// The content parent communicating with the content process which the OOP
// receiver page belongs to.
nsCOMPtr<nsIContentParent> mContentParent;
};
} // namespace dom

View File

@ -32,7 +32,7 @@ interface nsIPresentationServiceCallback : nsISupports
void notifyError(in nsresult error);
};
[scriptable, uuid(5801efd9-9dba-4af7-8be9-8fc97c2d54a6)]
[scriptable, uuid(731af98a-2760-4e7f-bb59-c7cf6665f26f)]
interface nsIPresentationService : nsISupports
{
/*
@ -99,14 +99,29 @@ interface nsIPresentationService : nsISupports
/*
* 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.
* An empty string is always returned at sender side. Whereas at receiver side
* the associated session ID is returned if the window ID and URI are matched;
* otherwise an empty string is returned.
*
* @param windowId: The inner window ID used to look up the session ID.
*/
DOMString getExistentSessionIdAtLaunch();
DOMString getExistentSessionIdAtLaunch(in uint64_t windowId);
/*
* Notify the receiver page is ready for presentation use.
*
* @param sessionId: An ID to identify presentation session.
* @param windowId: The inner window ID associated with the presentation
* session. (0 implies no window ID since no actual window
* uses 0 as its ID.)
*/
void notifyReceiverReady(in DOMString sessionId);
void notifyReceiverReady(in DOMString sessionId,
[optional] in uint64_t windowId);
/*
* Untrack the relevant info about the presentation session if there's any.
*
* @param sessionId: An ID to identify presentation session.
*/
void untrackSessionInfo(in DOMString sessionId);
};

View File

@ -58,9 +58,6 @@ parent:
PPresentationRequest(PresentationRequest aRequest);
sync GetExistentSessionIdAtLaunch()
returns (nsString aSessionId);
NotifyReceiverReady(nsString aSessionId);
};

View File

@ -38,6 +38,8 @@ PresentationIPCService::~PresentationIPCService()
{
mListeners.Clear();
mSessionListeners.Clear();
mRespondingSessionIds.Clear();
mRespondingWindowIds.Clear();
sPresentationChild = nullptr;
}
@ -130,6 +132,8 @@ PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
UntrackSessionInfo(aSessionId);
mSessionListeners.Remove(aSessionId);
if (sPresentationChild) {
NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler(nsAutoString(aSessionId)));
@ -174,26 +178,53 @@ PresentationIPCService::NotifyAvailableChange(bool aAvailable)
}
NS_IMETHODIMP
PresentationIPCService::GetExistentSessionIdAtLaunch(nsAString& aSessionId)
PresentationIPCService::GetExistentSessionIdAtLaunch(uint64_t aWindowId,
nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoString sessionId(aSessionId);
NS_WARN_IF(!sPresentationChild->SendGetExistentSessionIdAtLaunch(&sessionId));
aSessionId = sessionId;
nsString* sessionId = mRespondingSessionIds.Get(aWindowId);
if (sessionId) {
aSessionId.Assign(*sessionId);
} else {
aSessionId.Truncate();
}
return NS_OK;
}
nsresult
PresentationIPCService::NotifyReceiverReady(const nsAString& aSessionId)
NS_IMETHODIMP
PresentationIPCService::NotifyReceiverReady(const nsAString& aSessionId,
uint64_t aWindowId)
{
MOZ_ASSERT(NS_IsMainThread());
// No actual window uses 0 as its ID.
if (NS_WARN_IF(aWindowId == 0)) {
return NS_ERROR_NOT_AVAILABLE;
}
// Track the responding info for an OOP receiver page.
mRespondingSessionIds.Put(aWindowId, new nsAutoString(aSessionId));
mRespondingWindowIds.Put(aSessionId, aWindowId);
mCallback = nullptr;
NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsAutoString(aSessionId)));
return NS_OK;
}
NS_IMETHODIMP
PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId)
{
// Remove the OOP responding info (if it has never been used).
uint64_t windowId = 0;
if(mRespondingWindowIds.Get(aSessionId, &windowId)) {
mRespondingWindowIds.Remove(aSessionId);
mRespondingSessionIds.Remove(windowId);
}
return NS_OK;
}
void
PresentationIPCService::NotifyPresentationChildDestroyed()
{
@ -204,6 +235,8 @@ nsresult
PresentationIPCService::MonitorResponderLoading(const nsAString& aSessionId,
nsIDocShell* aDocShell)
{
MOZ_ASSERT(NS_IsMainThread());
mCallback = new PresentationResponderLoadingCallback(aSessionId);
return mCallback->Init(aDocShell);
}

View File

@ -48,6 +48,14 @@ private:
nsTObserverArray<nsCOMPtr<nsIPresentationListener> > mListeners;
nsRefPtrHashtable<nsStringHashKey, nsIPresentationSessionListener> mSessionListeners;
nsRefPtr<PresentationResponderLoadingCallback> mCallback;
// Store the mapping between the window ID of the OOP page (in this process)
// and the ID of the responding session. It's used for an OOP receiver page
// to retrieve the correspondent session ID. Besides, also keep the mapping
// between the responding session ID and the window ID to help look up the
// window ID.
nsClassHashtable<nsUint64HashKey, nsString> mRespondingSessionIds;
nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds;
};
} // namespace dom

View File

@ -120,6 +120,14 @@ PresentationParent::RecvUnregisterHandler()
PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId)
{
MOZ_ASSERT(mService);
// Validate the accessibility (primarily for receiver side) so that a
// compromised child process can't fake the ID.
if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
IsSessionAccessible(aSessionId, OtherPid()))) {
return true;
}
mSessionIds.AppendElement(aSessionId);
NS_WARN_IF(NS_FAILED(mService->RegisterSessionListener(aSessionId, this)));
return true;
@ -148,7 +156,7 @@ PresentationParent::NotifyStateChange(const nsAString& aSessionId,
uint16_t aState)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifySessionStateChange(nsString(aSessionId), aState))) {
!SendNotifySessionStateChange(nsAutoString(aSessionId), aState))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
@ -159,25 +167,17 @@ PresentationParent::NotifyMessage(const nsAString& aSessionId,
const nsACString& aData)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifyMessage(nsString(aSessionId), nsCString(aData)))) {
!SendNotifyMessage(nsAutoString(aSessionId), nsAutoCString(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)));
NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId, 0)));
return true;
}
@ -218,6 +218,14 @@ nsresult
PresentationRequestParent::DoRequest(const SendSessionMessageRequest& aRequest)
{
MOZ_ASSERT(mService);
// Validate the accessibility (primarily for receiver side) so that a
// compromised child process can't fake the ID.
if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
IsSessionAccessible(aRequest.sessionId(), OtherPid()))) {
return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
}
nsTArray<mozilla::ipc::FileDescriptor> fds;
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aRequest.data(), fds);
if(NS_WARN_IF(!stream)) {
@ -235,6 +243,14 @@ nsresult
PresentationRequestParent::DoRequest(const TerminateRequest& aRequest)
{
MOZ_ASSERT(mService);
// Validate the accessibility (primarily for receiver side) so that a
// compromised child process can't fake the ID.
if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
IsSessionAccessible(aRequest.sessionId(), OtherPid()))) {
return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
}
nsresult rv = mService->Terminate(aRequest.sessionId());
if (NS_WARN_IF(NS_FAILED(rv))) {
return NotifyError(rv);

View File

@ -50,8 +50,6 @@ public:
virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId) override;
virtual bool RecvGetExistentSessionIdAtLaunch(nsString* aSessionId) override;
virtual bool RecvNotifyReceiverReady(const nsString& aSessionId) override;
private:

View File

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for B2G Presentation Session API on a non-receiver page 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 finish() {
alert('DONE');
}
function testSessionAvailable() {
return new Promise(function(aResolve, aReject) {
ok(navigator.presentation, "navigator.presentation should be available in OOP pages.");
ok(!navigator.presentation.session, "Non-receiving OOP pages shouldn't get a predefined presentation session instance.");
aResolve();
});
}
testSessionAvailable().
then(finish);
</script>
</body>
</html>

View File

@ -4,6 +4,7 @@ support-files =
PresentationSessionChromeScript.js
file_presentation_receiver.html
file_presentation_receiver_oop.html
file_presentation_non_receiver_oop.html
file_presentation_receiver_start_session_error.html
[test_presentation_device_info.html]

View File

@ -82,6 +82,9 @@ function testIncomingSessionRequest() {
gScript.removeMessageListener('receiver-launching', launchReceiverHandler);
info("Trying to launch receiver page.");
ok(navigator.presentation, "navigator.presentation should be available in in-process pages.");
ok(!navigator.presentation.session, "Non-receiving in-process pages shouldn't get a predefined presentation session instance.");
aResolve();
});

View File

@ -19,6 +19,10 @@
var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver_oop.html');
var nonReceiverUrl = SimpleTest.getTestFileURL('file_presentation_non_receiver_oop.html');
var isReceiverFinished = false;
var isNonReceiverFinished = false;
var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
.getService(SpecialPowers.Ci.nsIObserverService);
@ -27,17 +31,17 @@ function setup() {
return new Promise(function(aResolve, aReject) {
gScript.sendAsyncMessage('trigger-device-add');
// Create a receiver OOP iframe.
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);
var receiverIframe = document.createElement('iframe');
receiverIframe.setAttribute('remote', 'true');
receiverIframe.setAttribute('mozbrowser', 'true');
receiverIframe.setAttribute('src', receiverUrl);
// This event is triggered when the iframe calls "alert".
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(aEvent) {
receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(aEvent) {
var message = aEvent.detail.message;
if (/^OK /.exec(message)) {
ok(true, "Message from iframe: " + message);
@ -50,19 +54,58 @@ function setup() {
gScript.sendAsyncMessage(command.name, command.data);
} else if (/^DONE$/.exec(message)) {
ok(true, "Messaging from iframe complete.");
iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
receiverIframe.removeEventListener('mozbrowsershowmodalprompt', receiverListener);
teardown();
isReceiverFinished = true;
if (isNonReceiverFinished) {
teardown();
}
}
}, false);
var promise = new Promise(function(aResolve, aReject) {
document.body.appendChild(iframe);
document.body.appendChild(receiverIframe);
aResolve(iframe);
aResolve(receiverIframe);
});
obs.notifyObservers(promise, 'setup-request-promise', null);
// Create a non-receiver OOP iframe.
SpecialPowers.addPermission('presentation', true, { url: nonReceiverUrl,
appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
isInBrowserElement: true });
var nonReceiverIframe = document.createElement('iframe');
nonReceiverIframe.setAttribute('remote', 'true');
nonReceiverIframe.setAttribute('mozbrowser', 'true');
nonReceiverIframe.setAttribute('src', nonReceiverUrl);
// This event is triggered when the iframe calls "alert".
nonReceiverIframe.addEventListener('mozbrowsershowmodalprompt', function nonReceiverListener(aEvent) {
var message = aEvent.detail.message;
if (/^OK /.exec(message)) {
ok(true, "Message from iframe: " + message);
} else if (/^KO /.exec(message)) {
ok(false, "Message from iframe: " + message);
} else if (/^INFO /.exec(message)) {
info("Message from iframe: " + message);
} else if (/^COMMAND /.exec(message)) {
var command = JSON.parse(message.replace(/^COMMAND /, ''));
gScript.sendAsyncMessage(command.name, command.data);
} else if (/^DONE$/.exec(message)) {
ok(true, "Messaging from iframe complete.");
nonReceiverIframe.removeEventListener('mozbrowsershowmodalprompt', nonReceiverListener);
isNonReceiverFinished = true;
if (isReceiverFinished) {
teardown();
}
}
}, false);
document.body.appendChild(nonReceiverIframe);
gScript.addMessageListener('offer-received', function offerReceivedHandler() {
gScript.removeMessageListener('offer-received', offerReceivedHandler);
info("An offer is received.");