mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 10:27:03 +00:00
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:
parent
e921df5cdc
commit
16cb2d0274
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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))) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -58,9 +58,6 @@ parent:
|
||||
|
||||
PPresentationRequest(PresentationRequest aRequest);
|
||||
|
||||
sync GetExistentSessionIdAtLaunch()
|
||||
returns (nsString aSessionId);
|
||||
|
||||
NotifyReceiverReady(nsString aSessionId);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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>
|
@ -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]
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -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.");
|
||||
|
Loading…
x
Reference in New Issue
Block a user