These were originally exposed directly as static methods on nsGlobalWindow, but as they are clearly associated with either the inner or outer window, it makes more sense for them to be called as such. MozReview-Commit-ID: LFq8EfnhDlo
541 lines
17 KiB
C++
541 lines
17 KiB
C++
/* -*- 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/. */
|
|
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/PermissionMessageUtils.h"
|
|
#include "mozilla/dom/PPresentation.h"
|
|
#include "mozilla/dom/TabParent.h"
|
|
#include "mozilla/ipc/InputStreamUtils.h"
|
|
#include "mozilla/ipc/URIUtils.h"
|
|
#include "nsGlobalWindow.h"
|
|
#include "nsIPresentationListener.h"
|
|
#include "PresentationCallbacks.h"
|
|
#include "PresentationChild.h"
|
|
#include "PresentationContentSessionInfo.h"
|
|
#include "PresentationIPCService.h"
|
|
#include "PresentationLog.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::ipc;
|
|
|
|
namespace {
|
|
|
|
PresentationChild* sPresentationChild;
|
|
|
|
} // anonymous
|
|
|
|
NS_IMPL_ISUPPORTS(PresentationIPCService,
|
|
nsIPresentationService,
|
|
nsIPresentationAvailabilityListener)
|
|
|
|
PresentationIPCService::PresentationIPCService()
|
|
{
|
|
ContentChild* contentChild = ContentChild::GetSingleton();
|
|
if (NS_WARN_IF(!contentChild || contentChild->IsShuttingDown())) {
|
|
return;
|
|
}
|
|
sPresentationChild = new PresentationChild(this);
|
|
Unused <<
|
|
NS_WARN_IF(!contentChild->SendPPresentationConstructor(sPresentationChild));
|
|
}
|
|
|
|
/* virtual */
|
|
PresentationIPCService::~PresentationIPCService()
|
|
{
|
|
Shutdown();
|
|
|
|
mSessionListeners.Clear();
|
|
mSessionInfoAtController.Clear();
|
|
mSessionInfoAtReceiver.Clear();
|
|
sPresentationChild = nullptr;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::StartSession(
|
|
const nsTArray<nsString>& aUrls,
|
|
const nsAString& aSessionId,
|
|
const nsAString& aOrigin,
|
|
const nsAString& aDeviceId,
|
|
uint64_t aWindowId,
|
|
nsIDOMEventTarget* aEventTarget,
|
|
nsIPrincipal* aPrincipal,
|
|
nsIPresentationServiceCallback* aCallback,
|
|
nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
|
|
{
|
|
if (aWindowId != 0) {
|
|
AddRespondingSessionId(aWindowId,
|
|
aSessionId,
|
|
nsIPresentationService::ROLE_CONTROLLER);
|
|
}
|
|
|
|
nsPIDOMWindowInner* window =
|
|
nsGlobalWindowInner::GetInnerWindowWithId(aWindowId)->AsInner();
|
|
TabId tabId = TabParent::GetTabIdFrom(window->GetDocShell());
|
|
|
|
return SendRequest(aCallback, StartSessionRequest(aUrls,
|
|
nsString(aSessionId),
|
|
nsString(aOrigin),
|
|
nsString(aDeviceId),
|
|
aWindowId,
|
|
tabId,
|
|
IPC::Principal(aPrincipal)));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::SendSessionMessage(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
const nsAString& aData)
|
|
{
|
|
MOZ_ASSERT(!aSessionId.IsEmpty());
|
|
MOZ_ASSERT(!aData.IsEmpty());
|
|
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
GetSessionInfo(aSessionId, aRole);
|
|
// data channel session transport is maintained by content process
|
|
if (info) {
|
|
return info->Send(aData);
|
|
}
|
|
|
|
return SendRequest(nullptr, SendSessionMessageRequest(nsString(aSessionId),
|
|
aRole,
|
|
nsString(aData)));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::SendSessionBinaryMsg(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
const nsACString &aData)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!aData.IsEmpty());
|
|
MOZ_ASSERT(!aSessionId.IsEmpty());
|
|
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
|
|
aRole == nsIPresentationService::ROLE_RECEIVER);
|
|
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
GetSessionInfo(aSessionId, aRole);
|
|
// data channel session transport is maintained by content process
|
|
if (info) {
|
|
return info->SendBinaryMsg(aData);
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::SendSessionBlob(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
nsIDOMBlob* aBlob)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!aSessionId.IsEmpty());
|
|
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
|
|
aRole == nsIPresentationService::ROLE_RECEIVER);
|
|
MOZ_ASSERT(aBlob);
|
|
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
GetSessionInfo(aSessionId, aRole);
|
|
// data channel session transport is maintained by content process
|
|
if (info) {
|
|
return info->SendBlob(aBlob);
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::CloseSession(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
uint8_t aClosedReason)
|
|
{
|
|
MOZ_ASSERT(!aSessionId.IsEmpty());
|
|
|
|
nsresult rv = SendRequest(nullptr, CloseSessionRequest(nsString(aSessionId),
|
|
aRole,
|
|
aClosedReason));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
GetSessionInfo(aSessionId, aRole);
|
|
if (info) {
|
|
return info->Close(NS_OK);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::TerminateSession(const nsAString& aSessionId,
|
|
uint8_t aRole)
|
|
{
|
|
MOZ_ASSERT(!aSessionId.IsEmpty());
|
|
|
|
nsresult rv = SendRequest(nullptr, TerminateSessionRequest(nsString(aSessionId), aRole));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
GetSessionInfo(aSessionId, aRole);
|
|
if (info) {
|
|
return info->Close(NS_OK);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::ReconnectSession(const nsTArray<nsString>& aUrls,
|
|
const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
nsIPresentationServiceCallback* aCallback)
|
|
{
|
|
MOZ_ASSERT(!aSessionId.IsEmpty());
|
|
|
|
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
|
MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
return SendRequest(aCallback, ReconnectSessionRequest(aUrls,
|
|
nsString(aSessionId),
|
|
aRole));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::BuildTransport(const nsAString& aSessionId,
|
|
uint8_t aRole)
|
|
{
|
|
MOZ_ASSERT(!aSessionId.IsEmpty());
|
|
|
|
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
|
MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
return SendRequest(nullptr, BuildTransportRequest(nsString(aSessionId),
|
|
aRole));
|
|
}
|
|
|
|
nsresult
|
|
PresentationIPCService::SendRequest(nsIPresentationServiceCallback* aCallback,
|
|
const PresentationIPCRequest& aRequest)
|
|
{
|
|
if (sPresentationChild) {
|
|
PresentationRequestChild* actor = new PresentationRequestChild(aCallback);
|
|
Unused << NS_WARN_IF(!sPresentationChild->SendPPresentationRequestConstructor(actor, aRequest));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::RegisterAvailabilityListener(
|
|
const nsTArray<nsString>& aAvailabilityUrls,
|
|
nsIPresentationAvailabilityListener* aListener)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!aAvailabilityUrls.IsEmpty());
|
|
MOZ_ASSERT(aListener);
|
|
|
|
nsTArray<nsString> addedUrls;
|
|
mAvailabilityManager.AddAvailabilityListener(aAvailabilityUrls,
|
|
aListener,
|
|
addedUrls);
|
|
|
|
if (sPresentationChild && !addedUrls.IsEmpty()) {
|
|
Unused <<
|
|
NS_WARN_IF(
|
|
!sPresentationChild->SendRegisterAvailabilityHandler(addedUrls));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::UnregisterAvailabilityListener(
|
|
const nsTArray<nsString>& aAvailabilityUrls,
|
|
nsIPresentationAvailabilityListener* aListener)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsTArray<nsString> removedUrls;
|
|
mAvailabilityManager.RemoveAvailabilityListener(aAvailabilityUrls,
|
|
aListener,
|
|
removedUrls);
|
|
|
|
if (sPresentationChild && !removedUrls.IsEmpty()) {
|
|
Unused <<
|
|
NS_WARN_IF(
|
|
!sPresentationChild->SendUnregisterAvailabilityHandler(removedUrls));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::RegisterSessionListener(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
nsIPresentationSessionListener* aListener)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aListener);
|
|
|
|
nsCOMPtr<nsIPresentationSessionListener> listener;
|
|
if (mSessionListeners.Get(aSessionId, getter_AddRefs(listener))) {
|
|
mSessionListeners.Put(aSessionId, aListener);
|
|
return NS_OK;
|
|
}
|
|
|
|
mSessionListeners.Put(aSessionId, aListener);
|
|
if (sPresentationChild) {
|
|
Unused <<
|
|
NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler(
|
|
nsString(aSessionId), aRole));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId,
|
|
uint8_t aRole)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
UntrackSessionInfo(aSessionId, aRole);
|
|
|
|
mSessionListeners.Remove(aSessionId);
|
|
if (sPresentationChild) {
|
|
Unused <<
|
|
NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler(
|
|
nsString(aSessionId), aRole));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::RegisterRespondingListener(uint64_t aWindowId,
|
|
nsIPresentationRespondingListener* aListener)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mRespondingListeners.Put(aWindowId, aListener);
|
|
if (sPresentationChild) {
|
|
Unused <<
|
|
NS_WARN_IF(!sPresentationChild->SendRegisterRespondingHandler(aWindowId));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::UnregisterRespondingListener(uint64_t aWindowId)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mRespondingListeners.Remove(aWindowId);
|
|
if (sPresentationChild) {
|
|
Unused <<
|
|
NS_WARN_IF(!sPresentationChild->SendUnregisterRespondingHandler(
|
|
aWindowId));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PresentationIPCService::NotifySessionTransport(const nsString& aSessionId,
|
|
const uint8_t& aRole,
|
|
nsIPresentationSessionTransport* aTransport)
|
|
{
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
new PresentationContentSessionInfo(aSessionId, aRole, aTransport);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(info->Init()))) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
|
|
mSessionInfoAtController.Put(aSessionId, info);
|
|
} else {
|
|
mSessionInfoAtReceiver.Put(aSessionId, info);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::GetWindowIdBySessionId(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
uint64_t* aWindowId)
|
|
{
|
|
return GetWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::UpdateWindowIdBySessionId(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
const uint64_t aWindowId)
|
|
{
|
|
return UpdateWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId);
|
|
}
|
|
|
|
nsresult
|
|
PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId,
|
|
uint16_t aState,
|
|
nsresult aReason)
|
|
{
|
|
nsCOMPtr<nsIPresentationSessionListener> listener;
|
|
if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
return listener->NotifyStateChange(aSessionId, aState, aReason);
|
|
}
|
|
|
|
// Only used for OOP RTCDataChannel session transport case.
|
|
nsresult
|
|
PresentationIPCService::NotifyMessage(const nsAString& aSessionId,
|
|
const nsACString& aData,
|
|
const bool& aIsBinary)
|
|
{
|
|
nsCOMPtr<nsIPresentationSessionListener> listener;
|
|
if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
return listener->NotifyMessage(aSessionId, aData, aIsBinary);
|
|
}
|
|
|
|
// Only used for OOP RTCDataChannel session transport case.
|
|
nsresult
|
|
PresentationIPCService::NotifyTransportClosed(const nsAString& aSessionId,
|
|
uint8_t aRole,
|
|
nsresult aReason)
|
|
{
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
GetSessionInfo(aSessionId, aRole);
|
|
if (NS_WARN_IF(!info)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
Unused << NS_WARN_IF(!sPresentationChild->SendNotifyTransportClosed(nsString(aSessionId), aRole, aReason));
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PresentationIPCService::NotifySessionConnect(uint64_t aWindowId,
|
|
const nsAString& aSessionId)
|
|
{
|
|
nsCOMPtr<nsIPresentationRespondingListener> listener;
|
|
if (NS_WARN_IF(!mRespondingListeners.Get(aWindowId, getter_AddRefs(listener)))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
return listener->NotifySessionConnect(aWindowId, aSessionId);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::NotifyAvailableChange(
|
|
const nsTArray<nsString>& aAvailabilityUrls,
|
|
bool aAvailable)
|
|
{
|
|
return mAvailabilityManager.DoNotifyAvailableChange(aAvailabilityUrls,
|
|
aAvailable);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::NotifyReceiverReady(
|
|
const nsAString& aSessionId,
|
|
uint64_t aWindowId,
|
|
bool aIsLoading,
|
|
nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
|
|
{
|
|
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.
|
|
AddRespondingSessionId(aWindowId,
|
|
aSessionId,
|
|
nsIPresentationService::ROLE_RECEIVER);
|
|
|
|
Unused << NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsString(aSessionId),
|
|
aWindowId,
|
|
aIsLoading));
|
|
|
|
// Release mCallback after using aSessionId
|
|
// because aSessionId is held by mCallback.
|
|
mCallback = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId,
|
|
uint8_t aRole)
|
|
{
|
|
PRES_DEBUG("content %s:id[%s], role[%d]\n", __func__,
|
|
NS_ConvertUTF16toUTF8(aSessionId).get(), aRole);
|
|
|
|
if (nsIPresentationService::ROLE_RECEIVER == aRole) {
|
|
// Terminate receiver page.
|
|
uint64_t windowId;
|
|
if (NS_SUCCEEDED(GetWindowIdBySessionIdInternal(aSessionId,
|
|
aRole,
|
|
&windowId))) {
|
|
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
|
"dom::PresentationIPCService::UntrackSessionInfo",
|
|
[windowId]() -> void {
|
|
PRES_DEBUG("Attempt to close window[%" PRIu64 "]\n", windowId);
|
|
|
|
if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(windowId)) {
|
|
window->Close();
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
// Remove the OOP responding info (if it has never been used).
|
|
RemoveRespondingSessionId(aSessionId, aRole);
|
|
|
|
if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
|
|
mSessionInfoAtController.Remove(aSessionId);
|
|
} else {
|
|
mSessionInfoAtReceiver.Remove(aSessionId);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
PresentationIPCService::NotifyPresentationChildDestroyed()
|
|
{
|
|
sPresentationChild = nullptr;
|
|
}
|
|
|
|
nsresult
|
|
PresentationIPCService::MonitorResponderLoading(const nsAString& aSessionId,
|
|
nsIDocShell* aDocShell)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mCallback = new PresentationResponderLoadingCallback(aSessionId);
|
|
return mCallback->Init(aDocShell);
|
|
}
|
|
|
|
nsresult
|
|
PresentationIPCService::CloseContentSessionTransport(const nsString& aSessionId,
|
|
uint8_t aRole,
|
|
nsresult aReason)
|
|
{
|
|
RefPtr<PresentationContentSessionInfo> info =
|
|
GetSessionInfo(aSessionId, aRole);
|
|
if (NS_WARN_IF(!info)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
return info->Close(aReason);
|
|
}
|