Issue #1390 - Get rid of the Presentation API

This commit is contained in:
Matt A. Tobin 2020-02-06 16:03:39 -05:00 committed by Roy Tam
commit eda1d47bc7
180 changed files with 0 additions and 29305 deletions

View file

@ -40,7 +40,6 @@
#include "mozilla/dom/FlyWebPublishedServer.h"
#include "mozilla/dom/FlyWebService.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/Presentation.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/TCPSocket.h"
@ -67,8 +66,6 @@
#include "nsIAppsService.h"
#include "mozIApplication.h"
#include "WidgetUtils.h"
#include "nsIPresentationService.h"
#include "mozilla/dom/MediaDevices.h"
#include "MediaManager.h"
@ -218,7 +215,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
#ifdef MOZ_EME
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
#ifdef MOZ_GAMEPAD
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
#endif
@ -284,10 +280,6 @@ Navigator::Invalidate()
mTimeManager = nullptr;
}
if (mPresentation) {
mPresentation = nullptr;
}
mServiceWorkerContainer = nullptr;
#ifdef MOZ_EME
@ -1931,19 +1923,5 @@ Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
}
#endif
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

View file

@ -74,7 +74,6 @@ class Connection;
} // namespace network
class PowerManager;
class Presentation;
class LegacyMozTCPSocket;
class StorageManager;
@ -210,8 +209,6 @@ 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);
@ -288,7 +285,6 @@ private:
RefPtr<time::TimeManager> mTimeManager;
RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<Presentation> mPresentation;
#ifdef MOZ_GAMEPAD
RefPtr<GamepadServiceTest> mGamepadServiceTest;
#endif

View file

@ -150,8 +150,6 @@
#endif
#include "mozilla/dom/File.h"
#include "mozilla/dom/PPresentationChild.h"
#include "mozilla/dom/PresentationIPCService.h"
#include "mozilla/ipc/InputStreamUtils.h"
#ifdef MOZ_WEBSPEECH
@ -1417,20 +1415,6 @@ ContentChild::SendPBlobConstructor(PBlobChild* aActor,
return PContentChild::SendPBlobConstructor(aActor, aParams);
}
PPresentationChild*
ContentChild::AllocPPresentationChild()
{
MOZ_CRASH("We should never be manually allocating PPresentationChild actors");
return nullptr;
}
bool
ContentChild::DeallocPPresentationChild(PPresentationChild* aActor)
{
delete aActor;
return true;
}
PFlyWebPublishedServerChild*
ContentChild::AllocPFlyWebPublishedServerChild(const nsString& name,
const FlyWebPublishOptions& params)
@ -1447,35 +1431,6 @@ ContentChild::DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aA
return true;
}
bool
ContentChild::RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
const nsString& aSessionId)
{
nsCOMPtr<nsIDocShell> docShell =
do_GetInterface(static_cast<TabChild*>(aIframe)->WebNavigation());
NS_WARNING_ASSERTION(docShell, "WebNavigation failed");
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
NS_WARNING_ASSERTION(service, "presentation service is missing");
Unused << NS_WARN_IF(NS_FAILED(static_cast<PresentationIPCService*>(service.get())->MonitorResponderLoading(aSessionId, docShell)));
return true;
}
bool
ContentChild::RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId)
{
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
NS_WARNING_ASSERTION(service, "presentation service is missing");
Unused << NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER)));
return true;
}
bool
ContentChild::RecvNotifyEmptyHTTPCache()
{

View file

@ -297,23 +297,12 @@ public:
virtual bool DeallocPStorageChild(PStorageChild* aActor) override;
virtual PPresentationChild* AllocPPresentationChild() override;
virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override;
virtual PFlyWebPublishedServerChild*
AllocPFlyWebPublishedServerChild(const nsString& name,
const FlyWebPublishOptions& params) override;
virtual bool DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor) override;
virtual bool
RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
const nsString& aSessionId) override;
virtual bool
RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override;
virtual bool RecvNotifyEmptyHTTPCache() override;
virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override;

View file

@ -51,8 +51,6 @@
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/PresentationParent.h"
#include "mozilla/dom/PPresentationParent.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/FlyWebPublishedServerIPC.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
@ -3123,27 +3121,6 @@ ContentParent::DeallocPStorageParent(PStorageParent* aActor)
return true;
}
PPresentationParent*
ContentParent::AllocPPresentationParent()
{
RefPtr<PresentationParent> actor = new PresentationParent();
return actor.forget().take();
}
bool
ContentParent::DeallocPPresentationParent(PPresentationParent* aActor)
{
RefPtr<PresentationParent> actor =
dont_AddRef(static_cast<PresentationParent*>(aActor));
return true;
}
bool
ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor)
{
return static_cast<PresentationParent*>(aActor)->Init(mChildID);
}
PFlyWebPublishedServerParent*
ContentParent::AllocPFlyWebPublishedServerParent(const nsString& name,
const FlyWebPublishOptions& params)

View file

@ -800,12 +800,6 @@ private:
virtual bool DeallocPStorageParent(PStorageParent* aActor) override;
virtual PPresentationParent* AllocPPresentationParent() override;
virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override;
virtual bool RecvPPresentationConstructor(PPresentationParent* aActor) override;
virtual PFlyWebPublishedServerParent*
AllocPFlyWebPublishedServerParent(const nsString& name,
const FlyWebPublishOptions& params) override;

View file

@ -43,7 +43,6 @@ include protocol PJavaScript;
include protocol PRemoteSpellcheckEngine;
include protocol PWebBrowserPersistDocument;
include protocol PWebrtcGlobal;
include protocol PPresentation;
include protocol PVideoDecoderManager;
include protocol PFlyWebPublishedServer;
include DOMTypes;
@ -260,7 +259,6 @@ nested(upto inside_cpow) sync protocol PContent
manages PRemoteSpellcheckEngine;
manages PWebBrowserPersistDocument;
manages PWebrtcGlobal;
manages PPresentation;
manages PFlyWebPublishedServer;
both:
@ -480,18 +478,6 @@ child:
*/
async UpdateWindow(uintptr_t aChildId);
/**
* Notify the child that presentation receiver has been launched with the
* correspondent iframe.
*/
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);
/**
* Notify the child that cache is emptied.
*/
@ -682,8 +668,6 @@ parent:
async PWebrtcGlobal();
async PPresentation();
async PFlyWebPublishedServer(nsString name, FlyWebPublishOptions params);
// Services remoting

View file

@ -107,8 +107,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
if CONFIG['MOZ_SECUREELEMENT']:
DIRS += ['secureelement']
DIRS += ['presentation']
TEST_DIRS += [
'tests',
'imptests',

View file

@ -1,99 +0,0 @@
/* -*- 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 "AvailabilityCollection.h"
#include "mozilla/ClearOnShutdown.h"
#include "PresentationAvailability.h"
namespace mozilla {
namespace dom {
/* static */
StaticAutoPtr<AvailabilityCollection>
AvailabilityCollection::sSingleton;
static bool gOnceAliveNowDead = false;
/* static */ AvailabilityCollection*
AvailabilityCollection::GetSingleton()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton && !gOnceAliveNowDead) {
sSingleton = new AvailabilityCollection();
ClearOnShutdown(&sSingleton);
}
return sSingleton;
}
AvailabilityCollection::AvailabilityCollection()
{
MOZ_COUNT_CTOR(AvailabilityCollection);
}
AvailabilityCollection::~AvailabilityCollection()
{
MOZ_COUNT_DTOR(AvailabilityCollection);
gOnceAliveNowDead = true;
}
void
AvailabilityCollection::Add(PresentationAvailability* aAvailability)
{
MOZ_ASSERT(NS_IsMainThread());
if (!aAvailability) {
return;
}
WeakPtr<PresentationAvailability> availability = aAvailability;
if (mAvailabilities.Contains(aAvailability)) {
return;
}
mAvailabilities.AppendElement(aAvailability);
}
void
AvailabilityCollection::Remove(PresentationAvailability* aAvailability)
{
MOZ_ASSERT(NS_IsMainThread());
if (!aAvailability) {
return;
}
WeakPtr<PresentationAvailability> availability = aAvailability;
mAvailabilities.RemoveElement(availability);
}
already_AddRefed<PresentationAvailability>
AvailabilityCollection::Find(const uint64_t aWindowId, const nsTArray<nsString>& aUrls)
{
MOZ_ASSERT(NS_IsMainThread());
// Loop backwards to allow removing elements in the loop.
for (int i = mAvailabilities.Length() - 1; i >= 0; --i) {
WeakPtr<PresentationAvailability> availability = mAvailabilities[i];
if (!availability) {
// The availability object was destroyed. Remove it from the list.
mAvailabilities.RemoveElementAt(i);
continue;
}
if (availability->Equals(aWindowId, aUrls)) {
RefPtr<PresentationAvailability> matchedAvailability = availability.get();
return matchedAvailability.forget();
}
}
return nullptr;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,45 +0,0 @@
/* -*- 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_AvailabilityCollection_h
#define mozilla_dom_AvailabilityCollection_h
#include "mozilla/StaticPtr.h"
#include "mozilla/WeakPtr.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
class PresentationAvailability;
class AvailabilityCollection final
{
public:
static AvailabilityCollection* GetSingleton();
void Add(PresentationAvailability* aAvailability);
void Remove(PresentationAvailability* aAvailability);
already_AddRefed<PresentationAvailability>
Find(const uint64_t aWindowId, const nsTArray<nsString>& aUrls);
private:
friend class StaticAutoPtr<AvailabilityCollection>;
AvailabilityCollection();
virtual ~AvailabilityCollection();
static StaticAutoPtr<AvailabilityCollection> sSingleton;
nsTArray<WeakPtr<PresentationAvailability>> mAvailabilities;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_AvailabilityCollection_h

View file

@ -1,116 +0,0 @@
/* -*- 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 "ControllerConnectionCollection.h"
#include "mozilla/ClearOnShutdown.h"
#include "nsIPresentationService.h"
#include "PresentationConnection.h"
namespace mozilla {
namespace dom {
/* static */
StaticAutoPtr<ControllerConnectionCollection>
ControllerConnectionCollection::sSingleton;
/* static */ ControllerConnectionCollection*
ControllerConnectionCollection::GetSingleton()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton) {
sSingleton = new ControllerConnectionCollection();
ClearOnShutdown(&sSingleton);
}
return sSingleton;
}
ControllerConnectionCollection::ControllerConnectionCollection()
{
MOZ_COUNT_CTOR(ControllerConnectionCollection);
}
ControllerConnectionCollection::~ControllerConnectionCollection()
{
MOZ_COUNT_DTOR(ControllerConnectionCollection);
}
void
ControllerConnectionCollection::AddConnection(
PresentationConnection* aConnection,
const uint8_t aRole)
{
MOZ_ASSERT(NS_IsMainThread());
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
MOZ_ASSERT(false, "This is allowed only to be called at controller side.");
return;
}
if (!aConnection) {
return;
}
WeakPtr<PresentationConnection> connection = aConnection;
if (mConnections.Contains(connection)) {
return;
}
mConnections.AppendElement(connection);
}
void
ControllerConnectionCollection::RemoveConnection(
PresentationConnection* aConnection,
const uint8_t aRole)
{
MOZ_ASSERT(NS_IsMainThread());
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
MOZ_ASSERT(false, "This is allowed only to be called at controller side.");
return;
}
if (!aConnection) {
return;
}
WeakPtr<PresentationConnection> connection = aConnection;
mConnections.RemoveElement(connection);
}
already_AddRefed<PresentationConnection>
ControllerConnectionCollection::FindConnection(
uint64_t aWindowId,
const nsAString& aId,
const uint8_t aRole)
{
MOZ_ASSERT(NS_IsMainThread());
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
MOZ_ASSERT(false, "This is allowed only to be called at controller side.");
return nullptr;
}
// Loop backwards to allow removing elements in the loop.
for (int i = mConnections.Length() - 1; i >= 0; --i) {
WeakPtr<PresentationConnection> connection = mConnections[i];
if (!connection) {
// The connection was destroyed. Remove it from the list.
mConnections.RemoveElementAt(i);
continue;
}
if (connection->Equals(aWindowId, aId)) {
RefPtr<PresentationConnection> matchedConnection = connection.get();
return matchedConnection.forget();
}
}
return nullptr;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,49 +0,0 @@
/* -*- 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_ControllerConnectionCollection_h
#define mozilla_dom_ControllerConnectionCollection_h
#include "mozilla/StaticPtr.h"
#include "mozilla/WeakPtr.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
class PresentationConnection;
class ControllerConnectionCollection final
{
public:
static ControllerConnectionCollection* GetSingleton();
void AddConnection(PresentationConnection* aConnection,
const uint8_t aRole);
void RemoveConnection(PresentationConnection* aConnection,
const uint8_t aRole);
already_AddRefed<PresentationConnection>
FindConnection(uint64_t aWindowId,
const nsAString& aId,
const uint8_t aRole);
private:
friend class StaticAutoPtr<ControllerConnectionCollection>;
ControllerConnectionCollection();
virtual ~ControllerConnectionCollection();
static StaticAutoPtr<ControllerConnectionCollection> sSingleton;
nsTArray<WeakPtr<PresentationConnection>> mConnections;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ControllerConnectionCollection_h

View file

@ -1,46 +0,0 @@
/* -*- 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 "DCPresentationChannelDescription.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(DCPresentationChannelDescription,
nsIPresentationChannelDescription)
NS_IMETHODIMP
DCPresentationChannelDescription::GetType(uint8_t* aRetVal)
{
if (NS_WARN_IF(!aRetVal)) {
return NS_ERROR_INVALID_POINTER;
}
*aRetVal = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
return NS_OK;
}
NS_IMETHODIMP
DCPresentationChannelDescription::GetTcpAddress(nsIArray** aRetVal)
{
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
DCPresentationChannelDescription::GetTcpPort(uint16_t* aRetVal)
{
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
DCPresentationChannelDescription::GetDataChannelSDP(nsAString& aDataChannelSDP)
{
aDataChannelSDP = mSDP;
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,37 +0,0 @@
/* -*- 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_DCPresentationChannelDescription_h
#define mozilla_dom_DCPresentationChannelDescription_h
#include "nsIPresentationControlChannel.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
// PresentationChannelDescription for Data Channel
class DCPresentationChannelDescription final : public nsIPresentationChannelDescription
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONCHANNELDESCRIPTION
explicit DCPresentationChannelDescription(const nsAString& aSDP)
: mSDP(aSDP)
{
}
private:
virtual ~DCPresentationChannelDescription() = default;
nsString mSDP;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_DCPresentationChannelDescription_h

View file

@ -1,182 +0,0 @@
/* -*- 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 "Presentation.h"
#include <ctype.h>
#include "mozilla/dom/PresentationBinding.h"
#include "mozilla/dom/Promise.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDocShell.h"
#include "nsIPresentationService.h"
#include "nsIScriptSecurityManager.h"
#include "nsJSUtils.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
#include "nsSandboxFlags.h"
#include "nsServiceManagerUtils.h"
#include "PresentationReceiver.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Presentation,
mWindow,
mDefaultRequest, mReceiver)
NS_IMPL_CYCLE_COLLECTING_ADDREF(Presentation)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Presentation)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Presentation)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/* static */ already_AddRefed<Presentation>
Presentation::Create(nsPIDOMWindowInner* aWindow)
{
RefPtr<Presentation> presentation = new Presentation(aWindow);
return presentation.forget();
}
Presentation::Presentation(nsPIDOMWindowInner* aWindow)
: mWindow(aWindow)
{
}
Presentation::~Presentation()
{
}
/* virtual */ JSObject*
Presentation::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return PresentationBinding::Wrap(aCx, this, aGivenProto);
}
void
Presentation::SetDefaultRequest(PresentationRequest* aRequest)
{
nsCOMPtr<nsIDocument> doc = mWindow ? mWindow->GetExtantDoc() : nullptr;
if (NS_WARN_IF(!doc)) {
return;
}
if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
return;
}
mDefaultRequest = aRequest;
}
already_AddRefed<PresentationRequest>
Presentation::GetDefaultRequest() const
{
RefPtr<PresentationRequest> request = mDefaultRequest;
return request.forget();
}
already_AddRefed<PresentationReceiver>
Presentation::GetReceiver()
{
// return the same receiver if already created
if (mReceiver) {
RefPtr<PresentationReceiver> receiver = mReceiver;
return receiver.forget();
}
if (!HasReceiverSupport() || !IsInPresentedContent()) {
return nullptr;
}
mReceiver = PresentationReceiver::Create(mWindow);
if (NS_WARN_IF(!mReceiver)) {
MOZ_ASSERT(mReceiver);
return nullptr;
}
RefPtr<PresentationReceiver> receiver = mReceiver;
return receiver.forget();
}
void
Presentation::SetStartSessionUnsettled(bool aIsUnsettled)
{
mStartSessionUnsettled = aIsUnsettled;
}
bool
Presentation::IsStartSessionUnsettled() const
{
return mStartSessionUnsettled;
}
bool
Presentation::HasReceiverSupport() const
{
if (!mWindow) {
return false;
}
// Grant access to browser receiving pages and their same-origin iframes. (App
// pages should be controlled by "presentation" permission in app manifests.)
nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell();
if (!docShell) {
return false;
}
if (!Preferences::GetBool("dom.presentation.testing.simulate-receiver") &&
!docShell->GetIsInMozBrowserOrApp() &&
!docShell->GetIsTopLevelContentDocShell()) {
return false;
}
nsAutoString presentationURL;
nsContentUtils::GetPresentationURL(docShell, presentationURL);
if (presentationURL.IsEmpty()) {
return false;
}
nsCOMPtr<nsIScriptSecurityManager> securityManager =
nsContentUtils::GetSecurityManager();
if (!securityManager) {
return false;
}
nsCOMPtr<nsIURI> presentationURI;
nsresult rv = NS_NewURI(getter_AddRefs(presentationURI), presentationURL);
if (NS_FAILED(rv)) {
return false;
}
nsCOMPtr<nsIURI> docURI = mWindow->GetDocumentURI();
return NS_SUCCEEDED(securityManager->CheckSameOriginURI(presentationURI,
docURI,
false));
}
bool
Presentation::IsInPresentedContent() const
{
if (!mWindow) {
return false;
}
nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell();
MOZ_ASSERT(docShell);
nsAutoString presentationURL;
nsContentUtils::GetPresentationURL(docShell, presentationURL);
return !presentationURL.IsEmpty();
}
} // namespace dom
} // namespace mozilla

View file

@ -1,70 +0,0 @@
/* -*- 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_Presentation_h
#define mozilla_dom_Presentation_h
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
#include "nsWrapperCache.h"
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
class Promise;
class PresentationReceiver;
class PresentationRequest;
class Presentation final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Presentation)
static already_AddRefed<Presentation> Create(nsPIDOMWindowInner* aWindow);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
nsPIDOMWindowInner* GetParentObject() const
{
return mWindow;
}
// WebIDL (public APIs)
void SetDefaultRequest(PresentationRequest* aRequest);
already_AddRefed<PresentationRequest> GetDefaultRequest() const;
already_AddRefed<PresentationReceiver> GetReceiver();
// For bookkeeping unsettled start session request
void SetStartSessionUnsettled(bool aIsUnsettled);
bool IsStartSessionUnsettled() const;
private:
explicit Presentation(nsPIDOMWindowInner* aWindow);
virtual ~Presentation();
bool HasReceiverSupport() const;
bool IsInPresentedContent() const;
RefPtr<PresentationRequest> mDefaultRequest;
RefPtr<PresentationReceiver> mReceiver;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
bool mStartSessionUnsettled = false;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Presentation_h

View file

@ -1,206 +0,0 @@
/* -*- 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 "PresentationAvailability.h"
#include "mozilla/dom/PresentationAvailabilityBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/Unused.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIPresentationDeviceManager.h"
#include "nsIPresentationService.h"
#include "nsServiceManagerUtils.h"
#include "PresentationLog.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationAvailability)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromises);
tmp->Shutdown();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_ADDREF_INHERITED(PresentationAvailability, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(PresentationAvailability, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationAvailability)
NS_INTERFACE_MAP_ENTRY(nsIPresentationAvailabilityListener)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
/* static */ already_AddRefed<PresentationAvailability>
PresentationAvailability::Create(nsPIDOMWindowInner* aWindow,
const nsTArray<nsString>& aUrls,
RefPtr<Promise>& aPromise)
{
RefPtr<PresentationAvailability> availability =
new PresentationAvailability(aWindow, aUrls);
return NS_WARN_IF(!availability->Init(aPromise)) ? nullptr
: availability.forget();
}
PresentationAvailability::PresentationAvailability(nsPIDOMWindowInner* aWindow,
const nsTArray<nsString>& aUrls)
: DOMEventTargetHelper(aWindow)
, mIsAvailable(false)
, mUrls(aUrls)
{
for (uint32_t i = 0; i < mUrls.Length(); ++i) {
mAvailabilityOfUrl.AppendElement(false);
}
}
PresentationAvailability::~PresentationAvailability()
{
Shutdown();
}
bool
PresentationAvailability::Init(RefPtr<Promise>& aPromise)
{
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return false;
}
nsresult rv = service->RegisterAvailabilityListener(mUrls, this);
if (NS_WARN_IF(NS_FAILED(rv))) {
// If the user agent is unable to monitor available device,
// Resolve promise with |value| set to false.
mIsAvailable = false;
aPromise->MaybeResolve(this);
return true;
}
EnqueuePromise(aPromise);
AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
if (collection) {
collection->Add(this);
}
return true;
}
void PresentationAvailability::Shutdown()
{
AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
if (collection ) {
collection->Remove(this);
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return;
}
Unused <<
NS_WARN_IF(NS_FAILED(service->UnregisterAvailabilityListener(mUrls,
this)));
}
/* virtual */ void
PresentationAvailability::DisconnectFromOwner()
{
Shutdown();
DOMEventTargetHelper::DisconnectFromOwner();
}
/* virtual */ JSObject*
PresentationAvailability::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return PresentationAvailabilityBinding::Wrap(aCx, this, aGivenProto);
}
bool
PresentationAvailability::Equals(const uint64_t aWindowID,
const nsTArray<nsString>& aUrls) const
{
if (GetOwner() && GetOwner()->WindowID() == aWindowID &&
mUrls.Length() == aUrls.Length()) {
for (const auto& url : aUrls) {
if (!mUrls.Contains(url)) {
return false;
}
}
return true;
}
return false;
}
bool
PresentationAvailability::IsCachedValueReady()
{
// All pending promises will be solved when cached value is ready and
// no promise should be enqueued afterward.
return mPromises.IsEmpty();
}
void
PresentationAvailability::EnqueuePromise(RefPtr<Promise>& aPromise)
{
mPromises.AppendElement(aPromise);
}
bool
PresentationAvailability::Value() const
{
return mIsAvailable;
}
NS_IMETHODIMP
PresentationAvailability::NotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls,
bool aIsAvailable)
{
bool available = false;
for (uint32_t i = 0; i < mUrls.Length(); ++i) {
if (aAvailabilityUrls.Contains(mUrls[i])) {
mAvailabilityOfUrl[i] = aIsAvailable;
}
available |= mAvailabilityOfUrl[i];
}
return NS_DispatchToCurrentThread(NewRunnableMethod
<bool>(this,
&PresentationAvailability::UpdateAvailabilityAndDispatchEvent,
available));
}
void
PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable)
{
PRES_DEBUG("%s\n", __func__);
bool isChanged = (aIsAvailable != mIsAvailable);
mIsAvailable = aIsAvailable;
if (!mPromises.IsEmpty()) {
// Use the first availability change notification to resolve promise.
do {
nsTArray<RefPtr<Promise>> promises = Move(mPromises);
for (auto& promise : promises) {
promise->MaybeResolve(this);
}
// more promises may have been added to mPromises, at least in theory
} while (!mPromises.IsEmpty());
return;
}
if (isChanged) {
Unused <<
NS_WARN_IF(NS_FAILED(DispatchTrustedEvent(NS_LITERAL_STRING("change"))));
}
}

View file

@ -1,74 +0,0 @@
/* -*- 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_PresentationAvailability_h
#define mozilla_dom_PresentationAvailability_h
#include "mozilla/DOMEventTargetHelper.h"
#include "nsIPresentationListener.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
class Promise;
class PresentationAvailability final : public DOMEventTargetHelper
, public nsIPresentationAvailabilityListener
, public SupportsWeakPtr<PresentationAvailability>
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationAvailability,
DOMEventTargetHelper)
NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PresentationAvailability)
static already_AddRefed<PresentationAvailability>
Create(nsPIDOMWindowInner* aWindow,
const nsTArray<nsString>& aUrls,
RefPtr<Promise>& aPromise);
virtual void DisconnectFromOwner() override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
bool Equals(const uint64_t aWindowID, const nsTArray<nsString>& aUrls) const;
bool IsCachedValueReady();
void EnqueuePromise(RefPtr<Promise>& aPromise);
// WebIDL (public APIs)
bool Value() const;
IMPL_EVENT_HANDLER(change);
private:
explicit PresentationAvailability(nsPIDOMWindowInner* aWindow,
const nsTArray<nsString>& aUrls);
virtual ~PresentationAvailability();
bool Init(RefPtr<Promise>& aPromise);
void Shutdown();
void UpdateAvailabilityAndDispatchEvent(bool aIsAvailable);
bool mIsAvailable;
nsTArray<RefPtr<Promise>> mPromises;
nsTArray<nsString> mUrls;
nsTArray<bool> mAvailabilityOfUrl;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationAvailability_h

View file

@ -1,282 +0,0 @@
/* -*- 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 "nsThreadUtils.h"
#include "PresentationCallbacks.h"
#include "PresentationRequest.h"
#include "PresentationConnection.h"
#include "PresentationTransportBuilderConstructor.h"
using namespace mozilla;
using namespace mozilla::dom;
/*
* Implementation of PresentationRequesterCallback
*/
NS_IMPL_ISUPPORTS(PresentationRequesterCallback, nsIPresentationServiceCallback)
PresentationRequesterCallback::PresentationRequesterCallback(PresentationRequest* aRequest,
const nsAString& aSessionId,
Promise* aPromise)
: mRequest(aRequest)
, mSessionId(aSessionId)
, mPromise(aPromise)
{
MOZ_ASSERT(mRequest);
MOZ_ASSERT(mPromise);
MOZ_ASSERT(!mSessionId.IsEmpty());
}
PresentationRequesterCallback::~PresentationRequesterCallback()
{
}
// nsIPresentationServiceCallback
NS_IMETHODIMP
PresentationRequesterCallback::NotifySuccess(const nsAString& aUrl)
{
MOZ_ASSERT(NS_IsMainThread());
if (aUrl.IsEmpty()) {
return NotifyError(NS_ERROR_DOM_OPERATION_ERR);
}
RefPtr<PresentationConnection> connection =
PresentationConnection::Create(mRequest->GetOwner(), mSessionId, aUrl,
nsIPresentationService::ROLE_CONTROLLER);
if (NS_WARN_IF(!connection)) {
return NotifyError(NS_ERROR_DOM_OPERATION_ERR);
}
mRequest->NotifyPromiseSettled();
mPromise->MaybeResolve(connection);
return mRequest->DispatchConnectionAvailableEvent(connection);
}
NS_IMETHODIMP
PresentationRequesterCallback::NotifyError(nsresult aError)
{
MOZ_ASSERT(NS_IsMainThread());
mRequest->NotifyPromiseSettled();
mPromise->MaybeReject(aError);
return NS_OK;
}
/*
* Implementation of PresentationRequesterCallback
*/
NS_IMPL_ISUPPORTS_INHERITED0(PresentationReconnectCallback,
PresentationRequesterCallback)
PresentationReconnectCallback::PresentationReconnectCallback(
PresentationRequest* aRequest,
const nsAString& aSessionId,
Promise* aPromise,
PresentationConnection* aConnection)
: PresentationRequesterCallback(aRequest, aSessionId, aPromise)
, mConnection(aConnection)
{
}
PresentationReconnectCallback::~PresentationReconnectCallback()
{
}
NS_IMETHODIMP
PresentationReconnectCallback::NotifySuccess(const nsAString& aUrl)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = NS_OK;
// We found a matched connection with the same window ID, URL, and
// the session ID. Resolve the promise with this connection and dispatch
// the event.
if (mConnection) {
mConnection->NotifyStateChange(
mSessionId,
nsIPresentationSessionListener::STATE_CONNECTING,
NS_OK);
mPromise->MaybeResolve(mConnection);
rv = mRequest->DispatchConnectionAvailableEvent(mConnection);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
// Use |PresentationRequesterCallback::NotifySuccess| to create a new
// connection since we don't find one that can be reused.
rv = PresentationRequesterCallback::NotifySuccess(aUrl);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = service->UpdateWindowIdBySessionId(mSessionId,
nsIPresentationService::ROLE_CONTROLLER,
mRequest->GetOwner()->WindowID());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsString sessionId = nsString(mSessionId);
return NS_DispatchToMainThread(
NS_NewRunnableFunction([sessionId, service]() -> void {
service->BuildTransport(sessionId,
nsIPresentationService::ROLE_CONTROLLER);
}));
}
NS_IMETHODIMP
PresentationReconnectCallback::NotifyError(nsresult aError)
{
if (mConnection) {
mConnection->NotifyStateChange(
mSessionId,
nsIPresentationSessionListener::STATE_CLOSED,
aError);
}
return PresentationRequesterCallback::NotifyError(aError);
}
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(/* isLoading = */ true);
}
// Start to listen to document state change event |STATE_TRANSFERRING|.
return mProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
}
nsresult
PresentationResponderLoadingCallback::NotifyReceiverReady(bool aIsLoading)
{
nsCOMPtr<nsPIDOMWindowOuter> 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;
}
nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
PresentationTransportBuilderConstructor::Create();
return service->NotifyReceiverReady(mSessionId,
windowId,aIsLoading,
constructor);
}
// nsIWebProgressListener
NS_IMETHODIMP
PresentationResponderLoadingCallback::OnStateChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aStateFlags,
nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
if (aStateFlags & (nsIWebProgressListener::STATE_TRANSFERRING |
nsIWebProgressListener::STATE_STOP)) {
mProgress->RemoveProgressListener(this);
bool isLoading = aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING;
return NotifyReceiverReady(isLoading);
}
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;
}

View file

@ -1,85 +0,0 @@
/* -*- 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/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsIPresentationService.h"
#include "nsIWebProgressListener.h"
#include "nsString.h"
#include "nsWeakReference.h"
class nsIDocShell;
class nsIWebProgress;
namespace mozilla {
namespace dom {
class PresentationConnection;
class PresentationRequest;
class Promise;
class PresentationRequesterCallback : public nsIPresentationServiceCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSERVICECALLBACK
PresentationRequesterCallback(PresentationRequest* aRequest,
const nsAString& aSessionId,
Promise* aPromise);
protected:
virtual ~PresentationRequesterCallback();
RefPtr<PresentationRequest> mRequest;
nsString mSessionId;
RefPtr<Promise> mPromise;
};
class PresentationReconnectCallback final : public PresentationRequesterCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPRESENTATIONSERVICECALLBACK
PresentationReconnectCallback(PresentationRequest* aRequest,
const nsAString& aSessionId,
Promise* aPromise,
PresentationConnection* aConnection);
private:
virtual ~PresentationReconnectCallback();
RefPtr<PresentationConnection> mConnection;
};
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(bool aIsLoading);
nsString mSessionId;
nsCOMPtr<nsIWebProgress> mProgress;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationCallbacks_h

View file

@ -1,763 +0,0 @@
/* -*- 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 "PresentationConnection.h"
#include "ControllerConnectionCollection.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/PresentationConnectionCloseEvent.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/DebugOnly.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIPresentationService.h"
#include "nsServiceManagerUtils.h"
#include "nsStringStream.h"
#include "PresentationConnectionList.h"
#include "PresentationLog.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationConnection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningConnectionList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
tmp->Shutdown();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningConnectionList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_ADDREF_INHERITED(PresentationConnection, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(PresentationConnection, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnection)
NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
const nsAString& aId,
const nsAString& aUrl,
const uint8_t aRole,
PresentationConnectionList* aList)
: DOMEventTargetHelper(aWindow)
, mId(aId)
, mUrl(aUrl)
, mState(PresentationConnectionState::Connecting)
, mOwningConnectionList(aList)
, mBinaryType(PresentationConnectionBinaryType::Arraybuffer)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
mRole = aRole;
}
/* virtual */ PresentationConnection::~PresentationConnection()
{
}
/* static */ already_AddRefed<PresentationConnection>
PresentationConnection::Create(nsPIDOMWindowInner* aWindow,
const nsAString& aId,
const nsAString& aUrl,
const uint8_t aRole,
PresentationConnectionList* aList)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
RefPtr<PresentationConnection> connection =
new PresentationConnection(aWindow, aId, aUrl, aRole, aList);
if (NS_WARN_IF(!connection->Init())) {
return nullptr;
}
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
ControllerConnectionCollection::GetSingleton()->AddConnection(connection,
aRole);
}
return connection.forget();
}
bool
PresentationConnection::Init()
{
if (NS_WARN_IF(mId.IsEmpty())) {
return false;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
return false;
}
nsresult rv = service->RegisterSessionListener(mId, mRole, this);
if(NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
rv = AddIntoLoadGroup();
if(NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return true;
}
void
PresentationConnection::Shutdown()
{
PRES_DEBUG("connection shutdown:id[%s], role[%d]\n",
NS_ConvertUTF16toUTF8(mId).get(), mRole);
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return;
}
DebugOnly<nsresult> rv = service->UnregisterSessionListener(mId, mRole);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "UnregisterSessionListener failed");
DebugOnly<nsresult> rv2 = RemoveFromLoadGroup();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv2), "RemoveFromLoadGroup failed");
if (mRole == nsIPresentationService::ROLE_CONTROLLER) {
ControllerConnectionCollection::GetSingleton()->RemoveConnection(this,
mRole);
}
}
/* virtual */ void
PresentationConnection::DisconnectFromOwner()
{
Unused << NS_WARN_IF(NS_FAILED(ProcessConnectionWentAway()));
DOMEventTargetHelper::DisconnectFromOwner();
}
/* virtual */ JSObject*
PresentationConnection::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return PresentationConnectionBinding::Wrap(aCx, this, aGivenProto);
}
void
PresentationConnection::GetId(nsAString& aId) const
{
aId = mId;
}
void
PresentationConnection::GetUrl(nsAString& aUrl) const
{
aUrl = mUrl;
}
PresentationConnectionState
PresentationConnection::State() const
{
return mState;
}
PresentationConnectionBinaryType
PresentationConnection::BinaryType() const
{
return mBinaryType;
}
void
PresentationConnection::SetBinaryType(PresentationConnectionBinaryType aType)
{
mBinaryType = aType;
}
void
PresentationConnection::Send(const nsAString& aData,
ErrorResult& aRv)
{
// Sending is not allowed if the session is not connected.
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
nsresult rv = service->SendSessionMessage(mId, mRole, aData);
if(NS_WARN_IF(NS_FAILED(rv))) {
const uint32_t kMaxMessageLength = 256;
nsAutoString data(Substring(aData, 0, kMaxMessageLength));
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message: \"") + data +
NS_LITERAL_STRING("\""));
}
}
void
PresentationConnection::Send(Blob& aData,
ErrorResult& aRv)
{
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
nsresult rv = service->SendSessionBlob(mId, mRole, &aData);
if(NS_WARN_IF(NS_FAILED(rv))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send binary message for Blob message."));
}
}
void
PresentationConnection::Send(const ArrayBuffer& aData,
ErrorResult& aRv)
{
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t length = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());
nsDependentCSubstring msgString(data, length);
nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString);
if(NS_WARN_IF(NS_FAILED(rv))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send binary message for ArrayBuffer message."));
}
}
void
PresentationConnection::Send(const ArrayBufferView& aData,
ErrorResult& aRv)
{
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send message due to an internal error."));
return;
}
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t length = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());
nsDependentCSubstring msgString(data, length);
nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString);
if(NS_WARN_IF(NS_FAILED(rv))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to send binary message for ArrayBufferView message."));
}
}
void
PresentationConnection::Close(ErrorResult& aRv)
{
// It only works when the state is CONNECTED or CONNECTING.
if (NS_WARN_IF(mState != PresentationConnectionState::Connected &&
mState != PresentationConnectionState::Connecting)) {
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
return;
}
Unused << NS_WARN_IF(NS_FAILED(
service->CloseSession(mId,
mRole,
nsIPresentationService::CLOSED_REASON_CLOSED)));
}
void
PresentationConnection::Terminate(ErrorResult& aRv)
{
// It only works when the state is CONNECTED.
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
return;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
return;
}
Unused << NS_WARN_IF(NS_FAILED(service->TerminateSession(mId, mRole)));
}
bool
PresentationConnection::Equals(uint64_t aWindowId,
const nsAString& aId)
{
return GetOwner() &&
aWindowId == GetOwner()->WindowID() &&
mId.Equals(aId);
}
NS_IMETHODIMP
PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
uint16_t aState,
nsresult aReason)
{
PRES_DEBUG("connection state change:id[%s], state[%x], reason[%x], role[%d]\n",
NS_ConvertUTF16toUTF8(aSessionId).get(), aState,
aReason, mRole);
if (!aSessionId.Equals(mId)) {
return NS_ERROR_INVALID_ARG;
}
// A terminated connection should always remain in terminated.
if (mState == PresentationConnectionState::Terminated) {
return NS_OK;
}
PresentationConnectionState state;
switch (aState) {
case nsIPresentationSessionListener::STATE_CONNECTING:
state = PresentationConnectionState::Connecting;
break;
case nsIPresentationSessionListener::STATE_CONNECTED:
state = PresentationConnectionState::Connected;
break;
case nsIPresentationSessionListener::STATE_CLOSED:
state = PresentationConnectionState::Closed;
break;
case nsIPresentationSessionListener::STATE_TERMINATED:
state = PresentationConnectionState::Terminated;
break;
default:
NS_WARNING("Unknown presentation session state.");
return NS_ERROR_INVALID_ARG;
}
if (mState == state) {
return NS_OK;
}
mState = state;
nsresult rv = ProcessStateChanged(aReason);
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mOwningConnectionList) {
mOwningConnectionList->NotifyStateChange(aSessionId, this);
}
return NS_OK;
}
nsresult
PresentationConnection::ProcessStateChanged(nsresult aReason)
{
switch (mState) {
case PresentationConnectionState::Connecting:
return NS_OK;
case PresentationConnectionState::Connected: {
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, NS_LITERAL_STRING("connect"), false);
return asyncDispatcher->PostDOMEvent();
}
case PresentationConnectionState::Closed: {
PresentationConnectionClosedReason reason =
PresentationConnectionClosedReason::Closed;
nsString errorMsg;
if (NS_FAILED(aReason)) {
reason = PresentationConnectionClosedReason::Error;
nsCString name, message;
// If aReason is not a DOM error, use error name as message.
if (NS_FAILED(NS_GetNameAndMessageForDOMNSResult(aReason,
name,
message))) {
mozilla::GetErrorName(aReason, message);
message.InsertLiteral("Internal error: ", 0);
}
CopyUTF8toUTF16(message, errorMsg);
}
Unused <<
NS_WARN_IF(NS_FAILED(DispatchConnectionCloseEvent(reason, errorMsg)));
return RemoveFromLoadGroup();
}
case PresentationConnectionState::Terminated: {
// Ensure onterminate event is fired.
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, NS_LITERAL_STRING("terminate"), false);
Unused << NS_WARN_IF(NS_FAILED(asyncDispatcher->PostDOMEvent()));
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = service->UnregisterSessionListener(mId, mRole);
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return RemoveFromLoadGroup();
}
default:
MOZ_CRASH("Unknown presentation session state.");
return NS_ERROR_INVALID_ARG;
}
}
NS_IMETHODIMP
PresentationConnection::NotifyMessage(const nsAString& aSessionId,
const nsACString& aData,
bool aIsBinary)
{
PRES_DEBUG("connection %s:id[%s], data[%s], role[%d]\n", __func__,
NS_ConvertUTF16toUTF8(aSessionId).get(),
nsPromiseFlatCString(aData).get(), mRole);
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 != PresentationConnectionState::Connected)) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (NS_WARN_IF(NS_FAILED(DoReceiveMessage(aData, aIsBinary)))) {
AsyncCloseConnectionWithErrorMsg(
NS_LITERAL_STRING("Unable to receive a message."));
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
PresentationConnection::DoReceiveMessage(const nsACString& aData, bool aIsBinary)
{
// Transform the data.
AutoJSAPI jsapi;
if (!jsapi.Init(GetOwner())) {
return NS_ERROR_FAILURE;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> jsData(cx);
nsresult rv;
if (aIsBinary) {
if (mBinaryType == PresentationConnectionBinaryType::Blob) {
RefPtr<Blob> blob =
Blob::CreateStringBlob(GetOwner(), aData, EmptyString());
MOZ_ASSERT(blob);
if (!ToJSValue(cx, blob, &jsData)) {
return NS_ERROR_FAILURE;
}
} else if (mBinaryType == PresentationConnectionBinaryType::Arraybuffer) {
JS::Rooted<JSObject*> arrayBuf(cx);
rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
jsData.setObject(*arrayBuf);
} else {
NS_RUNTIMEABORT("Unknown binary type!");
return NS_ERROR_UNEXPECTED;
}
} else {
NS_ConvertUTF8toUTF16 utf16Data(aData);
if(NS_WARN_IF(!ToJSValue(cx, utf16Data, &jsData))) {
return NS_ERROR_FAILURE;
}
}
return DispatchMessageEvent(jsData);
}
nsresult
PresentationConnection::DispatchConnectionCloseEvent(
PresentationConnectionClosedReason aReason,
const nsAString& aMessage,
bool aDispatchNow)
{
if (mState != PresentationConnectionState::Closed) {
MOZ_ASSERT(false, "The connection state should be closed.");
return NS_ERROR_FAILURE;
}
PresentationConnectionCloseEventInit init;
init.mReason = aReason;
init.mMessage = aMessage;
RefPtr<PresentationConnectionCloseEvent> closedEvent =
PresentationConnectionCloseEvent::Constructor(this,
NS_LITERAL_STRING("close"),
init);
closedEvent->SetTrusted(true);
if (aDispatchNow) {
bool ignore;
return DOMEventTargetHelper::DispatchEvent(closedEvent, &ignore);
}
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, static_cast<Event*>(closedEvent));
return asyncDispatcher->PostDOMEvent();
}
nsresult
PresentationConnection::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;
}
RefPtr<MessageEvent> messageEvent = new MessageEvent(this, nullptr, nullptr);
messageEvent->InitMessageEvent(nullptr,
NS_LITERAL_STRING("message"),
false, false, aData, origin,
EmptyString(), nullptr,
Sequence<OwningNonNull<MessagePort>>());
messageEvent->SetTrusted(true);
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, static_cast<Event*>(messageEvent));
return asyncDispatcher->PostDOMEvent();
}
nsresult
PresentationConnection::ProcessConnectionWentAway()
{
if (mState != PresentationConnectionState::Connected &&
mState != PresentationConnectionState::Connecting) {
// If the state is not connected or connecting, do not need to
// close the session.
return NS_OK;
}
mState = PresentationConnectionState::Terminated;
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
return service->CloseSession(
mId, mRole, nsIPresentationService::CLOSED_REASON_WENTAWAY);
}
NS_IMETHODIMP
PresentationConnection::GetName(nsACString &aResult)
{
aResult.AssignLiteral("about:presentation-connection");
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::IsPending(bool* aRetval)
{
*aRetval = true;
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::GetStatus(nsresult* aStatus)
{
*aStatus = NS_OK;
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::Cancel(nsresult aStatus)
{
nsCOMPtr<nsIRunnable> event =
NewRunnableMethod(this, &PresentationConnection::ProcessConnectionWentAway);
return NS_DispatchToCurrentThread(event);
}
NS_IMETHODIMP
PresentationConnection::Suspend(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
PresentationConnection::Resume(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
PresentationConnection::GetLoadGroup(nsILoadGroup** aLoadGroup)
{
*aLoadGroup = nullptr;
nsCOMPtr<nsIDocument> doc = GetOwner() ? GetOwner()->GetExtantDoc() : nullptr;
if (!doc) {
return NS_ERROR_FAILURE;
}
*aLoadGroup = doc->GetDocumentLoadGroup().take();
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::SetLoadGroup(nsILoadGroup * aLoadGroup)
{
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
PresentationConnection::GetLoadFlags(nsLoadFlags* aLoadFlags)
{
*aLoadFlags = nsIRequest::LOAD_BACKGROUND;
return NS_OK;
}
NS_IMETHODIMP
PresentationConnection::SetLoadFlags(nsLoadFlags aLoadFlags)
{
return NS_OK;
}
nsresult
PresentationConnection::AddIntoLoadGroup()
{
// Avoid adding to loadgroup multiple times
if (mWeakLoadGroup) {
return NS_OK;
}
nsCOMPtr<nsILoadGroup> loadGroup;
nsresult rv = GetLoadGroup(getter_AddRefs(loadGroup));
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = loadGroup->AddRequest(this, nullptr);
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mWeakLoadGroup = do_GetWeakReference(loadGroup);
return NS_OK;
}
nsresult
PresentationConnection::RemoveFromLoadGroup()
{
if (!mWeakLoadGroup) {
return NS_OK;
}
nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakLoadGroup);
if (loadGroup) {
mWeakLoadGroup = nullptr;
return loadGroup->RemoveRequest(this, nullptr, NS_OK);
}
return NS_OK;
}
void
PresentationConnection::AsyncCloseConnectionWithErrorMsg(const nsAString& aMessage)
{
if (mState == PresentationConnectionState::Terminated) {
return;
}
nsString message = nsString(aMessage);
RefPtr<PresentationConnection> self = this;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([self, message]() -> void {
// Set |mState| to |PresentationConnectionState::Closed| here to avoid
// calling |ProcessStateChanged|.
self->mState = PresentationConnectionState::Closed;
// Make sure dispatching the event and closing the connection are invoked
// at the same time by setting |aDispatchNow| to true.
Unused << NS_WARN_IF(NS_FAILED(
self->DispatchConnectionCloseEvent(PresentationConnectionClosedReason::Error,
message,
true)));
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
return;
}
Unused << NS_WARN_IF(NS_FAILED(
service->CloseSession(self->mId,
self->mRole,
nsIPresentationService::CLOSED_REASON_ERROR)));
});
Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)));
}

View file

@ -1,128 +0,0 @@
/* -*- 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_PresentationConnection_h
#define mozilla_dom_PresentationConnection_h
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/PresentationConnectionBinding.h"
#include "mozilla/dom/PresentationConnectionCloseEventBinding.h"
#include "nsIPresentationListener.h"
#include "nsIRequest.h"
#include "nsWeakReference.h"
namespace mozilla {
namespace dom {
class Blob;
class PresentationConnectionList;
class PresentationConnection final : public DOMEventTargetHelper
, public nsIPresentationSessionListener
, public nsIRequest
, public SupportsWeakPtr<PresentationConnection>
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnection,
DOMEventTargetHelper)
NS_DECL_NSIPRESENTATIONSESSIONLISTENER
NS_DECL_NSIREQUEST
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PresentationConnection)
static already_AddRefed<PresentationConnection>
Create(nsPIDOMWindowInner* aWindow,
const nsAString& aId,
const nsAString& aUrl,
const uint8_t aRole,
PresentationConnectionList* aList = nullptr);
virtual void DisconnectFromOwner() override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
// WebIDL (public APIs)
void GetId(nsAString& aId) const;
void GetUrl(nsAString& aUrl) const;
PresentationConnectionState State() const;
PresentationConnectionBinaryType BinaryType() const;
void SetBinaryType(PresentationConnectionBinaryType aType);
void Send(const nsAString& aData,
ErrorResult& aRv);
void Send(Blob& aData,
ErrorResult& aRv);
void Send(const ArrayBuffer& aData,
ErrorResult& aRv);
void Send(const ArrayBufferView& aData,
ErrorResult& aRv);
void Close(ErrorResult& aRv);
void Terminate(ErrorResult& aRv);
bool
Equals(uint64_t aWindowId, const nsAString& aId);
IMPL_EVENT_HANDLER(connect);
IMPL_EVENT_HANDLER(close);
IMPL_EVENT_HANDLER(terminate);
IMPL_EVENT_HANDLER(message);
private:
PresentationConnection(nsPIDOMWindowInner* aWindow,
const nsAString& aId,
const nsAString& aUrl,
const uint8_t aRole,
PresentationConnectionList* aList);
~PresentationConnection();
bool Init();
void Shutdown();
nsresult ProcessStateChanged(nsresult aReason);
nsresult DispatchConnectionCloseEvent(PresentationConnectionClosedReason aReason,
const nsAString& aMessage,
bool aDispatchNow = false);
nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
nsresult ProcessConnectionWentAway();
nsresult AddIntoLoadGroup();
nsresult RemoveFromLoadGroup();
void AsyncCloseConnectionWithErrorMsg(const nsAString& aMessage);
nsresult DoReceiveMessage(const nsACString& aData, bool aIsBinary);
nsString mId;
nsString mUrl;
uint8_t mRole;
PresentationConnectionState mState;
RefPtr<PresentationConnectionList> mOwningConnectionList;
nsWeakPtr mWeakLoadGroup;
PresentationConnectionBinaryType mBinaryType;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationConnection_h

View file

@ -1,125 +0,0 @@
/* -*- 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 "PresentationConnectionList.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/PresentationConnectionAvailableEvent.h"
#include "mozilla/dom/PresentationConnectionListBinding.h"
#include "mozilla/dom/Promise.h"
#include "PresentationConnection.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(PresentationConnectionList, DOMEventTargetHelper,
mGetConnectionListPromise,
mConnections)
NS_IMPL_ADDREF_INHERITED(PresentationConnectionList, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(PresentationConnectionList, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnectionList)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
PresentationConnectionList::PresentationConnectionList(nsPIDOMWindowInner* aWindow,
Promise* aPromise)
: DOMEventTargetHelper(aWindow)
, mGetConnectionListPromise(aPromise)
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aPromise);
}
/* virtual */ JSObject*
PresentationConnectionList::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return PresentationConnectionListBinding::Wrap(aCx, this, aGivenProto);
}
void
PresentationConnectionList::GetConnections(
nsTArray<RefPtr<PresentationConnection>>& aConnections) const
{
aConnections = mConnections;
}
nsresult
PresentationConnectionList::DispatchConnectionAvailableEvent(
PresentationConnection* aConnection)
{
PresentationConnectionAvailableEventInit init;
init.mConnection = aConnection;
RefPtr<PresentationConnectionAvailableEvent> event =
PresentationConnectionAvailableEvent::Constructor(
this,
NS_LITERAL_STRING("connectionavailable"),
init);
if (NS_WARN_IF(!event)) {
return NS_ERROR_FAILURE;
}
event->SetTrusted(true);
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, event);
return asyncDispatcher->PostDOMEvent();
}
PresentationConnectionList::ConnectionArrayIndex
PresentationConnectionList::FindConnectionById(
const nsAString& aId)
{
for (ConnectionArrayIndex i = 0; i < mConnections.Length(); i++) {
nsAutoString id;
mConnections[i]->GetId(id);
if (id == nsAutoString(aId)) {
return i;
}
}
return mConnections.NoIndex;
}
void
PresentationConnectionList::NotifyStateChange(const nsAString& aSessionId,
PresentationConnection* aConnection)
{
if (!aConnection) {
MOZ_ASSERT(false, "PresentationConnection can not be null.");
return;
}
bool connectionFound =
FindConnectionById(aSessionId) != mConnections.NoIndex ? true : false;
PresentationConnectionListBinding::ClearCachedConnectionsValue(this);
switch (aConnection->State()) {
case PresentationConnectionState::Connected:
if (!connectionFound) {
mConnections.AppendElement(aConnection);
if (mGetConnectionListPromise) {
mGetConnectionListPromise->MaybeResolve(this);
mGetConnectionListPromise = nullptr;
return;
}
}
DispatchConnectionAvailableEvent(aConnection);
break;
case PresentationConnectionState::Terminated:
if (connectionFound) {
mConnections.RemoveElement(aConnection);
}
break;
default:
break;
}
}
} // namespace dom
} // namespace mozilla

View file

@ -1,57 +0,0 @@
/* -*- 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_PresentationConnectionList_h
#define mozilla_dom_PresentationConnectionList_h
#include "mozilla/DOMEventTargetHelper.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
class PresentationConnection;
class Promise;
class PresentationConnectionList final : public DOMEventTargetHelper
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnectionList,
DOMEventTargetHelper)
PresentationConnectionList(nsPIDOMWindowInner* aWindow,
Promise* aPromise);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void GetConnections(nsTArray<RefPtr<PresentationConnection>>& aConnections) const;
void NotifyStateChange(const nsAString& aSessionId, PresentationConnection* aConnection);
IMPL_EVENT_HANDLER(connectionavailable);
private:
virtual ~PresentationConnectionList() = default;
nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection);
typedef nsTArray<RefPtr<PresentationConnection>> ConnectionArray;
typedef ConnectionArray::index_type ConnectionArrayIndex;
ConnectionArrayIndex FindConnectionById(const nsAString& aId);
RefPtr<Promise> mGetConnectionListPromise;
// This array stores only non-terminsted connections.
ConnectionArray mConnections;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationConnectionList_h

View file

@ -1,378 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// Bug 1228209 - plan to remove this eventually
function log(aMsg) {
//dump("-*- PresentationDataChannelSessionTransport.js : " + aMsg + "\n");
}
const PRESENTATIONTRANSPORT_CID = Components.ID("{dd2bbf2f-3399-4389-8f5f-d382afb8b2d6}");
const PRESENTATIONTRANSPORT_CONTRACTID = "mozilla.org/presentation/datachanneltransport;1";
const PRESENTATIONTRANSPORTBUILDER_CID = Components.ID("{215b2f62-46e2-4004-a3d1-6858e56c20f3}");
const PRESENTATIONTRANSPORTBUILDER_CONTRACTID = "mozilla.org/presentation/datachanneltransportbuilder;1";
function PresentationDataChannelDescription(aDataChannelSDP) {
this._dataChannelSDP = JSON.stringify(aDataChannelSDP);
}
PresentationDataChannelDescription.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
get type() {
return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL;
},
get tcpAddress() {
return null;
},
get tcpPort() {
return null;
},
get dataChannelSDP() {
return this._dataChannelSDP;
}
};
function PresentationTransportBuilder() {
log("PresentationTransportBuilder construct");
this._isControlChannelNeeded = true;
}
PresentationTransportBuilder.prototype = {
classID: PRESENTATIONTRANSPORTBUILDER_CID,
contractID: PRESENTATIONTRANSPORTBUILDER_CONTRACTID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilder,
Ci.nsIPresentationDataChannelSessionTransportBuilder,
Ci.nsITimerCallback]),
buildDataChannelTransport: function(aRole, aWindow, aListener) {
if (!aRole || !aWindow || !aListener) {
log("buildDataChannelTransport with illegal parameters");
throw Cr.NS_ERROR_ILLEGAL_VALUE;
}
if (this._window) {
log("buildDataChannelTransport has started.");
throw Cr.NS_ERROR_UNEXPECTED;
}
log("buildDataChannelTransport with role " + aRole);
this._role = aRole;
this._window = aWindow;
this._listener = aListener.QueryInterface(Ci.nsIPresentationSessionTransportBuilderListener);
// TODO bug 1227053 set iceServers from |nsIPresentationDevice|
this._peerConnection = new this._window.RTCPeerConnection();
// |this._listener == null| will throw since the control channel is
// abnormally closed.
this._peerConnection.onicecandidate = aEvent => aEvent.candidate &&
this._listener.sendIceCandidate(JSON.stringify(aEvent.candidate));
this._peerConnection.onnegotiationneeded = () => {
log("onnegotiationneeded with role " + this._role);
if (!this._peerConnection) {
log("ignoring negotiationneeded without PeerConnection");
return;
}
this._peerConnection.createOffer()
.then(aOffer => this._peerConnection.setLocalDescription(aOffer))
.then(() => this._listener
.sendOffer(new PresentationDataChannelDescription(this._peerConnection.localDescription)))
.catch(e => this._reportError(e));
}
switch (this._role) {
case Ci.nsIPresentationService.ROLE_CONTROLLER:
this._dataChannel = this._peerConnection.createDataChannel("presentationAPI");
this._setDataChannel();
break;
case Ci.nsIPresentationService.ROLE_RECEIVER:
this._peerConnection.ondatachannel = aEvent => {
this._dataChannel = aEvent.channel;
// Ensure the binaryType of dataChannel is blob.
this._dataChannel.binaryType = "blob";
this._setDataChannel();
}
break;
default:
throw Cr.NS_ERROR_ILLEGAL_VALUE;
}
// TODO bug 1228235 we should have a way to let device providers customize
// the time-out duration.
let timeout = Services.prefs.getIntPref("presentation.receiver.loading.timeout", 10000);
// The timer is to check if the negotiation finishes on time.
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._timer.initWithCallback(this, timeout, this._timer.TYPE_ONE_SHOT);
},
notify: function() {
if (!this._sessionTransport) {
this._cleanup(Cr.NS_ERROR_NET_TIMEOUT);
}
},
_reportError: function(aError) {
log("report Error " + aError.name + ":" + aError.message);
this._cleanup(Cr.NS_ERROR_FAILURE);
},
_setDataChannel: function() {
this._dataChannel.onopen = () => {
log("data channel is open, notify the listener, role " + this._role);
// Handoff the ownership of _peerConnection and _dataChannel to
// _sessionTransport
this._sessionTransport = new PresentationTransport();
this._sessionTransport.init(this._peerConnection, this._dataChannel, this._window);
this._peerConnection.onicecandidate = null;
this._peerConnection.onnegotiationneeded = null;
this._peerConnection = this._dataChannel = null;
this._listener.onSessionTransport(this._sessionTransport);
this._sessionTransport.callback.notifyTransportReady();
this._cleanup(Cr.NS_OK);
};
this._dataChannel.onerror = aError => {
log("data channel onerror " + aError.name + ":" + aError.message);
this._cleanup(Cr.NS_ERROR_FAILURE);
}
},
_cleanup: function(aReason) {
if (aReason != Cr.NS_OK) {
this._listener.onError(aReason);
}
if (this._dataChannel) {
this._dataChannel.close();
this._dataChannel = null;
}
if (this._peerConnection) {
this._peerConnection.close();
this._peerConnection = null;
}
this._role = null;
this._window = null;
this._listener = null;
this._sessionTransport = null;
if (this._timer) {
this._timer.cancel();
this._timer = null;
}
},
// nsIPresentationControlChannelListener
onOffer: function(aOffer) {
if (this._role !== Ci.nsIPresentationService.ROLE_RECEIVER ||
this._sessionTransport) {
log("onOffer status error");
this._cleanup(Cr.NS_ERROR_FAILURE);
}
log("onOffer: " + aOffer.dataChannelSDP + " with role " + this._role);
let offer = new this._window
.RTCSessionDescription(JSON.parse(aOffer.dataChannelSDP));
this._peerConnection.setRemoteDescription(offer)
.then(() => this._peerConnection.signalingState == "stable" ||
this._peerConnection.createAnswer())
.then(aAnswer => this._peerConnection.setLocalDescription(aAnswer))
.then(() => {
this._isControlChannelNeeded = false;
this._listener
.sendAnswer(new PresentationDataChannelDescription(this._peerConnection.localDescription))
}).catch(e => this._reportError(e));
},
onAnswer: function(aAnswer) {
if (this._role !== Ci.nsIPresentationService.ROLE_CONTROLLER ||
this._sessionTransport) {
log("onAnswer status error");
this._cleanup(Cr.NS_ERROR_FAILURE);
}
log("onAnswer: " + aAnswer.dataChannelSDP + " with role " + this._role);
let answer = new this._window
.RTCSessionDescription(JSON.parse(aAnswer.dataChannelSDP));
this._peerConnection.setRemoteDescription(answer).catch(e => this._reportError(e));
this._isControlChannelNeeded = false;
},
onIceCandidate: function(aCandidate) {
log("onIceCandidate: " + aCandidate + " with role " + this._role);
if (!this._window || !this._peerConnection) {
log("ignoring ICE candidate after connection");
return;
}
let candidate = new this._window.RTCIceCandidate(JSON.parse(aCandidate));
this._peerConnection.addIceCandidate(candidate).catch(e => this._reportError(e));
},
notifyDisconnected: function(aReason) {
log("notifyDisconnected reason: " + aReason);
if (aReason != Cr.NS_OK) {
this._cleanup(aReason);
} else if (this._isControlChannelNeeded) {
this._cleanup(Cr.NS_ERROR_FAILURE);
}
},
};
function PresentationTransport() {
this._messageQueue = [];
this._closeReason = Cr.NS_OK;
}
PresentationTransport.prototype = {
classID: PRESENTATIONTRANSPORT_CID,
contractID: PRESENTATIONTRANSPORT_CONTRACTID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport]),
init: function(aPeerConnection, aDataChannel, aWindow) {
log("initWithDataChannel");
this._enableDataNotification = false;
this._dataChannel = aDataChannel;
this._peerConnection = aPeerConnection;
this._window = aWindow;
this._dataChannel.onopen = () => {
log("data channel reopen. Should never touch here");
};
this._dataChannel.onclose = () => {
log("data channel onclose");
if (this._callback) {
this._callback.notifyTransportClosed(this._closeReason);
}
this._cleanup();
}
this._dataChannel.onmessage = aEvent => {
log("data channel onmessage " + aEvent.data);
if (!this._enableDataNotification || !this._callback) {
log("queue message");
this._messageQueue.push(aEvent.data);
return;
}
this._doNotifyData(aEvent.data);
};
this._dataChannel.onerror = aError => {
log("data channel onerror " + aError.name + ":" + aError.message);
if (this._callback) {
this._callback.notifyTransportClosed(Cr.NS_ERROR_FAILURE);
}
this._cleanup();
}
},
// nsIPresentationTransport
get selfAddress() {
throw NS_ERROR_NOT_AVAILABLE;
},
get callback() {
return this._callback;
},
set callback(aCallback) {
this._callback = aCallback;
},
send: function(aData) {
log("send " + aData);
this._dataChannel.send(aData);
},
sendBinaryMsg: function(aData) {
log("sendBinaryMsg");
let array = new Uint8Array(aData.length);
for (let i = 0; i < aData.length; i++) {
array[i] = aData.charCodeAt(i);
}
this._dataChannel.send(array);
},
sendBlob: function(aBlob) {
log("sendBlob");
this._dataChannel.send(aBlob);
},
enableDataNotification: function() {
log("enableDataNotification");
if (this._enableDataNotification) {
return;
}
if (!this._callback) {
throw NS_ERROR_NOT_AVAILABLE;
}
this._enableDataNotification = true;
this._messageQueue.forEach(aData => this._doNotifyData(aData));
this._messageQueue = [];
},
close: function(aReason) {
this._closeReason = aReason;
this._dataChannel.close();
},
_cleanup: function() {
this._dataChannel = null;
if (this._peerConnection) {
this._peerConnection.close();
this._peerConnection = null;
}
this._callback = null;
this._messageQueue = [];
this._window = null;
},
_doNotifyData: function(aData) {
if (!this._callback) {
throw NS_ERROR_NOT_AVAILABLE;
}
if (aData instanceof this._window.Blob) {
let reader = new this._window.FileReader();
reader.addEventListener("load", (aEvent) => {
this._callback.notifyData(aEvent.target.result, true);
});
reader.readAsBinaryString(aData);
} else {
this._callback.notifyData(aData, false);
}
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationTransportBuilder,
PresentationTransport]);

View file

@ -1,6 +0,0 @@
# PresentationDataChannelSessionTransport.js
component {dd2bbf2f-3399-4389-8f5f-d382afb8b2d6} PresentationDataChannelSessionTransport.js
contract @mozilla.org/presentation/datachanneltransport;1 {dd2bbf2f-3399-4389-8f5f-d382afb8b2d6}
component {215b2f62-46e2-4004-a3d1-6858e56c20f3} PresentationDataChannelSessionTransport.js
contract @mozilla.org/presentation/datachanneltransportbuilder;1 {215b2f62-46e2-4004-a3d1-6858e56c20f3}

View file

@ -1,119 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
function log(aMsg) {
//dump("-*- PresentationDeviceInfoManager.js : " + aMsg + "\n");
}
const PRESENTATIONDEVICEINFOMANAGER_CID = Components.ID("{1bd66bef-f643-4be3-b690-0c656353eafd}");
const PRESENTATIONDEVICEINFOMANAGER_CONTRACTID = "@mozilla.org/presentation-device/deviceInfo;1";
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
function PresentationDeviceInfoManager() {}
PresentationDeviceInfoManager.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
classID: PRESENTATIONDEVICEINFOMANAGER_CID,
contractID: PRESENTATIONDEVICEINFOMANAGER_CONTRACTID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIObserver,
Ci.nsIDOMGlobalPropertyInitializer]),
receiveMessage: function(aMsg) {
if (!aMsg || !aMsg.data) {
return;
}
let data = aMsg.data;
log("receive aMsg: " + aMsg.name);
switch (aMsg.name) {
case "PresentationDeviceInfoManager:OnDeviceChange": {
let detail = {
detail: {
type: data.type,
deviceInfo: data.deviceInfo,
}
};
let event = new this._window.CustomEvent("devicechange", Cu.cloneInto(detail, this._window));
this.__DOM_IMPL__.dispatchEvent(event);
break;
}
case "PresentationDeviceInfoManager:GetAll:Result:Ok": {
let resolver = this.takePromiseResolver(data.requestId);
if (!resolver) {
return;
}
resolver.resolve(Cu.cloneInto(data.devices, this._window));
break;
}
case "PresentationDeviceInfoManager:GetAll:Result:Error": {
let resolver = this.takePromiseResolver(data.requestId);
if (!resolver) {
return;
}
resolver.reject(data.error);
break;
}
}
},
init: function(aWin) {
log("init");
this.initDOMRequestHelper(aWin, [
{name: "PresentationDeviceInfoManager:OnDeviceChange", weakRef: true},
{name: "PresentationDeviceInfoManager:GetAll:Result:Ok", weakRef: true},
{name: "PresentationDeviceInfoManager:GetAll:Result:Error", weakRef: true},
]);
},
uninit: function() {
log("uninit");
let self = this;
this.forEachPromiseResolver(function(aKey) {
self.takePromiseResolver(aKey).reject("PresentationDeviceInfoManager got destroyed");
});
},
get ondevicechange() {
return this.__DOM_IMPL__.getEventHandler("ondevicechange");
},
set ondevicechange(aHandler) {
this.__DOM_IMPL__.setEventHandler("ondevicechange", aHandler);
},
getAll: function() {
log("getAll");
let self = this;
return this.createPromiseWithId(function(aResolverId) {
cpmm.sendAsyncMessage("PresentationDeviceInfoManager:GetAll", {
requestId: aResolverId,
});
});
},
forceDiscovery: function() {
cpmm.sendAsyncMessage("PresentationDeviceInfoManager:ForceDiscovery");
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationDeviceInfoManager]);

View file

@ -1,104 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
this.EXPORTED_SYMBOLS = ["PresentationDeviceInfoService"];
function log(aMsg) {
//dump("PresentationDeviceInfoManager.jsm: " + aMsg + "\n");
}
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "presentationDeviceManager",
"@mozilla.org/presentation-device/manager;1",
"nsIPresentationDeviceManager");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster");
this.PresentationDeviceInfoService = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
Ci.nsIObserver]),
init: function() {
log("init");
ppmm.addMessageListener("PresentationDeviceInfoManager:GetAll", this);
ppmm.addMessageListener("PresentationDeviceInfoManager:ForceDiscovery", this);
Services.obs.addObserver(this, "presentation-device-change", false);
},
getAll: function(aData, aMm) {
log("getAll");
let deviceArray = presentationDeviceManager.getAvailableDevices().QueryInterface(Ci.nsIArray);
if (!deviceArray) {
aData.error = "DataError";
aMm.sendAsyncMessage("PresentationDeviceInfoManager:GetAll:Result:Error", aData);
return;
}
aData.devices = [];
for (let i = 0; i < deviceArray.length; i++) {
let device = deviceArray.queryElementAt(i, Ci.nsIPresentationDevice);
aData.devices.push({
id: device.id,
name: device.name,
type: device.type,
});
}
aMm.sendAsyncMessage("PresentationDeviceInfoManager:GetAll:Result:Ok", aData);
},
forceDiscovery: function() {
log("forceDiscovery");
presentationDeviceManager.forceDiscovery();
},
observe: function(aSubject, aTopic, aData) {
log("observe: " + aTopic);
let device = aSubject.QueryInterface(Ci.nsIPresentationDevice);
let data = {
type: aData,
deviceInfo: {
id: device.id,
name: device.name,
type: device.type,
},
};
ppmm.broadcastAsyncMessage("PresentationDeviceInfoManager:OnDeviceChange", data);
},
receiveMessage: function(aMessage) {
if (!aMessage.target.assertPermission("presentation-device-manage")) {
debug("receive message " + aMessage.name +
" from a content process with no 'presentation-device-manage' privileges.");
return null;
}
let msg = aMessage.data || {};
let mm = aMessage.target;
log("receiveMessage: " + aMessage.name);
switch (aMessage.name) {
case "PresentationDeviceInfoManager:GetAll": {
this.getAll(msg, mm);
break;
}
case "PresentationDeviceInfoManager:ForceDiscovery": {
this.forceDiscovery();
break;
}
}
},
};
this.PresentationDeviceInfoService.init();

View file

@ -1,3 +0,0 @@
# PresentationDeviceInfoManager.js
component {1bd66bef-f643-4be3-b690-0c656353eafd} PresentationDeviceInfoManager.js
contract @mozilla.org/presentation-device/deviceInfo;1 {1bd66bef-f643-4be3-b690-0c656353eafd}

View file

@ -1,336 +0,0 @@
/* -*- 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 "PresentationDeviceManager.h"
#include "mozilla/Services.h"
#include "MainThreadUtils.h"
#include "nsArrayUtils.h"
#include "nsCategoryCache.h"
#include "nsCOMPtr.h"
#include "nsIMutableArray.h"
#include "nsIObserverService.h"
#include "nsXULAppAPI.h"
#include "PresentationSessionRequest.h"
#include "PresentationTerminateRequest.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(PresentationDeviceManager,
nsIPresentationDeviceManager,
nsIPresentationDeviceListener,
nsIObserver,
nsISupportsWeakReference)
PresentationDeviceManager::PresentationDeviceManager()
{
}
PresentationDeviceManager::~PresentationDeviceManager()
{
UnloadDeviceProviders();
mDevices.Clear();
}
void
PresentationDeviceManager::Init()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
}
LoadDeviceProviders();
}
void
PresentationDeviceManager::Shutdown()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
}
UnloadDeviceProviders();
}
void
PresentationDeviceManager::LoadDeviceProviders()
{
MOZ_ASSERT(mProviders.IsEmpty());
nsCategoryCache<nsIPresentationDeviceProvider> providerCache(PRESENTATION_DEVICE_PROVIDER_CATEGORY);
providerCache.GetEntries(mProviders);
for (uint32_t i = 0; i < mProviders.Length(); ++i) {
mProviders[i]->SetListener(this);
}
}
void
PresentationDeviceManager::UnloadDeviceProviders()
{
for (uint32_t i = 0; i < mProviders.Length(); ++i) {
mProviders[i]->SetListener(nullptr);
}
mProviders.Clear();
}
void
PresentationDeviceManager::NotifyDeviceChange(nsIPresentationDevice* aDevice,
const char16_t* aType)
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(aDevice,
PRESENTATION_DEVICE_CHANGE_TOPIC,
aType);
}
}
// nsIPresentationDeviceManager
NS_IMETHODIMP
PresentationDeviceManager::ForceDiscovery()
{
MOZ_ASSERT(NS_IsMainThread());
for (uint32_t i = 0; i < mProviders.Length(); ++i) {
mProviders[i]->ForceDiscovery();
}
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::AddDeviceProvider(nsIPresentationDeviceProvider* aProvider)
{
NS_ENSURE_ARG(aProvider);
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(mProviders.Contains(aProvider))) {
return NS_OK;
}
mProviders.AppendElement(aProvider);
aProvider->SetListener(this);
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::RemoveDeviceProvider(nsIPresentationDeviceProvider* aProvider)
{
NS_ENSURE_ARG(aProvider);
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!mProviders.RemoveElement(aProvider))) {
return NS_ERROR_FAILURE;
}
aProvider->SetListener(nullptr);
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::GetDeviceAvailable(bool* aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
MOZ_ASSERT(NS_IsMainThread());
*aRetVal = !mDevices.IsEmpty();
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::GetAvailableDevices(nsIArray* aPresentationUrls, nsIArray** aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
MOZ_ASSERT(NS_IsMainThread());
// Bug 1194049: some providers may discontinue discovery after timeout.
// Call |ForceDiscovery()| here to make sure device lists are updated.
NS_DispatchToMainThread(
NewRunnableMethod(this, &PresentationDeviceManager::ForceDiscovery));
nsTArray<nsString> presentationUrls;
if (aPresentationUrls) {
uint32_t length;
nsresult rv = aPresentationUrls->GetLength(&length);
if (NS_SUCCEEDED(rv)) {
for (uint32_t i = 0; i < length; ++i) {
nsCOMPtr<nsISupportsString> isupportStr =
do_QueryElementAt(aPresentationUrls, i);
nsAutoString presentationUrl;
rv = isupportStr->GetData(presentationUrl);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
presentationUrls.AppendElement(presentationUrl);
}
}
}
nsCOMPtr<nsIMutableArray> devices = do_CreateInstance(NS_ARRAY_CONTRACTID);
for (uint32_t i = 0; i < mDevices.Length(); ++i) {
if (presentationUrls.IsEmpty()) {
devices->AppendElement(mDevices[i], false);
continue;
}
for (uint32_t j = 0; j < presentationUrls.Length(); ++j) {
bool isSupported;
if (NS_SUCCEEDED(mDevices[i]->IsRequestedUrlSupported(presentationUrls[j],
&isSupported)) &&
isSupported) {
devices->AppendElement(mDevices[i], false);
break;
}
}
}
devices.forget(aRetVal);
return NS_OK;
}
// nsIPresentationDeviceListener
NS_IMETHODIMP
PresentationDeviceManager::AddDevice(nsIPresentationDevice* aDevice)
{
NS_ENSURE_ARG(aDevice);
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(mDevices.Contains(aDevice))) {
return NS_ERROR_FAILURE;
}
mDevices.AppendElement(aDevice);
NotifyDeviceChange(aDevice, u"add");
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::RemoveDevice(nsIPresentationDevice* aDevice)
{
NS_ENSURE_ARG(aDevice);
MOZ_ASSERT(NS_IsMainThread());
int32_t index = mDevices.IndexOf(aDevice);
if (NS_WARN_IF(index < 0)) {
return NS_ERROR_FAILURE;
}
mDevices.RemoveElementAt(index);
NotifyDeviceChange(aDevice, u"remove");
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::UpdateDevice(nsIPresentationDevice* aDevice)
{
NS_ENSURE_ARG(aDevice);
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!mDevices.Contains(aDevice))) {
return NS_ERROR_FAILURE;
}
NotifyDeviceChange(aDevice, u"update");
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::OnSessionRequest(nsIPresentationDevice* aDevice,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel)
{
NS_ENSURE_ARG(aDevice);
NS_ENSURE_ARG(aControlChannel);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
RefPtr<PresentationSessionRequest> request =
new PresentationSessionRequest(aDevice, aUrl, aPresentationId, aControlChannel);
obs->NotifyObservers(request,
PRESENTATION_SESSION_REQUEST_TOPIC,
nullptr);
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::OnTerminateRequest(nsIPresentationDevice* aDevice,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver)
{
NS_ENSURE_ARG(aDevice);
NS_ENSURE_ARG(aControlChannel);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
RefPtr<PresentationTerminateRequest> request =
new PresentationTerminateRequest(aDevice, aPresentationId,
aControlChannel, aIsFromReceiver);
obs->NotifyObservers(request,
PRESENTATION_TERMINATE_REQUEST_TOPIC,
nullptr);
return NS_OK;
}
NS_IMETHODIMP
PresentationDeviceManager::OnReconnectRequest(nsIPresentationDevice* aDevice,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel)
{
NS_ENSURE_ARG(aDevice);
NS_ENSURE_ARG(aControlChannel);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
RefPtr<PresentationSessionRequest> request =
new PresentationSessionRequest(aDevice, aUrl, aPresentationId, aControlChannel);
obs->NotifyObservers(request,
PRESENTATION_RECONNECT_REQUEST_TOPIC,
nullptr);
return NS_OK;
}
// nsIObserver
NS_IMETHODIMP
PresentationDeviceManager::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
if (!strcmp(aTopic, "profile-after-change")) {
Init();
} else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
Shutdown();
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,54 +0,0 @@
/* -*- 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_PresentationDeviceManager_h__
#define mozilla_dom_PresentationDeviceManager_h__
#include "nsIObserver.h"
#include "nsIPresentationDevice.h"
#include "nsIPresentationDeviceManager.h"
#include "nsIPresentationDeviceProvider.h"
#include "nsCOMArray.h"
#include "nsWeakReference.h"
namespace mozilla {
namespace dom {
class PresentationDeviceManager final : public nsIPresentationDeviceManager
, public nsIPresentationDeviceListener
, public nsIObserver
, public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONDEVICEMANAGER
NS_DECL_NSIPRESENTATIONDEVICELISTENER
NS_DECL_NSIOBSERVER
PresentationDeviceManager();
private:
virtual ~PresentationDeviceManager();
void Init();
void Shutdown();
void LoadDeviceProviders();
void UnloadDeviceProviders();
void NotifyDeviceChange(nsIPresentationDevice* aDevice,
const char16_t* aType);
nsCOMArray<nsIPresentationDeviceProvider> mProviders;
nsCOMArray<nsIPresentationDevice> mDevices;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PresentationDeviceManager_h__ */

View file

@ -1,26 +0,0 @@
/* -*- 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_PresentationLog_h
#define mozilla_dom_PresentationLog_h
/*
* MOZ_LOG=Presentation:5
* For detail, see PresentationService.cpp
*/
namespace mozilla {
namespace dom {
extern mozilla::LazyLogModule gPresentationLog;
}
}
#undef PRES_ERROR
#define PRES_ERROR(...) MOZ_LOG(mozilla::dom::gPresentationLog, mozilla::LogLevel::Error, (__VA_ARGS__))
#undef PRES_DEBUG
#define PRES_DEBUG(...) MOZ_LOG(mozilla::dom::gPresentationLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#endif // mozilla_dom_PresentationLog_h

View file

@ -1,28 +0,0 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const NETWORKHELPER_CID = Components.ID("{5fb96caa-6d49-4f6b-9a4b-65dd0d51f92d}");
function PresentationNetworkHelper() {}
PresentationNetworkHelper.prototype = {
classID: NETWORKHELPER_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationNetworkHelper]),
getWifiIPAddress: function(aListener) {
Messaging.sendRequestForResult({type: "Wifi:GetIPAddress"})
.then(result => aListener.onGetWifiIPAddress(result),
err => aListener.onError(err));
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationNetworkHelper]);

View file

@ -1,3 +0,0 @@
# PresentationNetworkHelper.js
component {5fb96caa-6d49-4f6b-9a4b-65dd0d51f92d} PresentationNetworkHelper.js
contract @mozilla.org/presentation-device/networkHelper;1 {5fb96caa-6d49-4f6b-9a4b-65dd0d51f92d}

View file

@ -1,179 +0,0 @@
/* -*- 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 "PresentationReceiver.h"
#include "mozilla/dom/PresentationReceiverBinding.h"
#include "mozilla/dom/Promise.h"
#include "nsContentUtils.h"
#include "nsIPresentationService.h"
#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "PresentationConnection.h"
#include "PresentationConnectionList.h"
#include "PresentationLog.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PresentationReceiver,
mOwner,
mGetConnectionListPromise,
mConnectionList)
NS_IMPL_CYCLE_COLLECTING_ADDREF(PresentationReceiver)
NS_IMPL_CYCLE_COLLECTING_RELEASE(PresentationReceiver)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PresentationReceiver)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsIPresentationRespondingListener)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/* static */ already_AddRefed<PresentationReceiver>
PresentationReceiver::Create(nsPIDOMWindowInner* aWindow)
{
RefPtr<PresentationReceiver> receiver = new PresentationReceiver(aWindow);
return NS_WARN_IF(!receiver->Init()) ? nullptr : receiver.forget();
}
PresentationReceiver::PresentationReceiver(nsPIDOMWindowInner* aWindow)
: mOwner(aWindow)
{
MOZ_ASSERT(aWindow);
}
PresentationReceiver::~PresentationReceiver()
{
Shutdown();
}
bool
PresentationReceiver::Init()
{
if (NS_WARN_IF(!mOwner)) {
return false;
}
mWindowId = mOwner->WindowID();
nsCOMPtr<nsIDocShell> docShell = mOwner->GetDocShell();
MOZ_ASSERT(docShell);
nsContentUtils::GetPresentationURL(docShell, mUrl);
return !mUrl.IsEmpty();
}
void PresentationReceiver::Shutdown()
{
PRES_DEBUG("receiver shutdown:windowId[%d]\n", mWindowId);
// Unregister listener for incoming sessions.
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return;
}
Unused <<
NS_WARN_IF(NS_FAILED(service->UnregisterRespondingListener(mWindowId)));
}
/* virtual */ JSObject*
PresentationReceiver::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return PresentationReceiverBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMETHODIMP
PresentationReceiver::NotifySessionConnect(uint64_t aWindowId,
const nsAString& aSessionId)
{
PRES_DEBUG("receiver session connect:id[%s], windowId[%x]\n",
NS_ConvertUTF16toUTF8(aSessionId).get(), aWindowId);
if (NS_WARN_IF(!mOwner)) {
return NS_ERROR_FAILURE;
}
if (NS_WARN_IF(aWindowId != mWindowId)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!mConnectionList)) {
return NS_ERROR_FAILURE;
}
RefPtr<PresentationConnection> connection =
PresentationConnection::Create(mOwner, aSessionId, mUrl,
nsIPresentationService::ROLE_RECEIVER,
mConnectionList);
if (NS_WARN_IF(!connection)) {
return NS_ERROR_NOT_AVAILABLE;
}
return NS_OK;
}
already_AddRefed<Promise>
PresentationReceiver::GetConnectionList(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mOwner);
if (NS_WARN_IF(!global)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!mGetConnectionListPromise) {
mGetConnectionListPromise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
RefPtr<PresentationReceiver> self = this;
nsresult rv =
NS_DispatchToMainThread(NS_NewRunnableFunction([self] () -> void {
self->CreateConnectionList();
}));
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
}
RefPtr<Promise> promise = mGetConnectionListPromise;
return promise.forget();
}
void
PresentationReceiver::CreateConnectionList()
{
MOZ_ASSERT(mGetConnectionListPromise);
if (mConnectionList) {
return;
}
mConnectionList = new PresentationConnectionList(mOwner,
mGetConnectionListPromise);
// Register listener for incoming sessions.
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
mGetConnectionListPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return;
}
nsresult rv = service->RegisterRespondingListener(mWindowId, this);
if (NS_WARN_IF(NS_FAILED(rv))) {
mGetConnectionListPromise->MaybeReject(rv);
}
}
} // namespace dom
} // namespace mozilla

View file

@ -1,71 +0,0 @@
/* -*- 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_PresentationReceiver_h
#define mozilla_dom_PresentationReceiver_h
#include "mozilla/ErrorResult.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIPresentationListener.h"
#include "nsWrapperCache.h"
#include "nsString.h"
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
class PresentationConnection;
class PresentationConnectionList;
class Promise;
class PresentationReceiver final : public nsIPresentationRespondingListener
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PresentationReceiver)
NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER
static already_AddRefed<PresentationReceiver> Create(nsPIDOMWindowInner* aWindow);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
nsPIDOMWindowInner* GetParentObject() const
{
return mOwner;
}
// WebIDL (public APIs)
already_AddRefed<Promise> GetConnectionList(ErrorResult& aRv);
private:
explicit PresentationReceiver(nsPIDOMWindowInner* aWindow);
virtual ~PresentationReceiver();
MOZ_IS_CLASS_INIT bool Init();
void Shutdown();
void CreateConnectionList();
// Store the inner window ID for |UnregisterRespondingListener| call in
// |Shutdown| since the inner window may not exist at that moment.
uint64_t mWindowId;
nsCOMPtr<nsPIDOMWindowInner> mOwner;
nsString mUrl;
RefPtr<Promise> mGetConnectionListPromise;
RefPtr<PresentationConnectionList> mConnectionList;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationReceiver_h

View file

@ -1,563 +0,0 @@
/* -*- 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 "PresentationRequest.h"
#include "AvailabilityCollection.h"
#include "ControllerConnectionCollection.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/PresentationRequestBinding.h"
#include "mozilla/dom/PresentationConnectionAvailableEvent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/Move.h"
#include "mozIThirdPartyUtil.h"
#include "nsContentSecurityManager.h"
#include "nsCycleCollectionParticipant.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
#include "nsIPresentationService.h"
#include "nsIURI.h"
#include "nsIUUIDGenerator.h"
#include "nsNetUtil.h"
#include "nsSandboxFlags.h"
#include "nsServiceManagerUtils.h"
#include "Presentation.h"
#include "PresentationAvailability.h"
#include "PresentationCallbacks.h"
#include "PresentationLog.h"
#include "PresentationTransportBuilderConstructor.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_ADDREF_INHERITED(PresentationRequest, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(PresentationRequest, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
static nsresult
GetAbsoluteURL(const nsAString& aUrl,
nsIURI* aBaseUri,
nsIDocument* aDocument,
nsAString& aAbsoluteUrl)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri),
aUrl,
aDocument ? aDocument->GetDocumentCharacterSet().get()
: nullptr,
aBaseUri);
if (NS_FAILED(rv)) {
return rv;
}
nsAutoCString spec;
uri->GetSpec(spec);
aAbsoluteUrl = NS_ConvertUTF8toUTF16(spec);
return NS_OK;
}
/* static */ already_AddRefed<PresentationRequest>
PresentationRequest::Constructor(const GlobalObject& aGlobal,
const nsAString& aUrl,
ErrorResult& aRv)
{
Sequence<nsString> urls;
urls.AppendElement(aUrl, fallible);
return Constructor(aGlobal, urls, aRv);
}
/* static */ already_AddRefed<PresentationRequest>
PresentationRequest::Constructor(const GlobalObject& aGlobal,
const Sequence<nsString>& aUrls,
ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (aUrls.IsEmpty()) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
// Resolve relative URL to absolute URL
nsCOMPtr<nsIURI> baseUri = window->GetDocBaseURI();
nsTArray<nsString> urls;
for (const auto& url : aUrls) {
nsAutoString absoluteUrl;
nsresult rv =
GetAbsoluteURL(url, baseUri, window->GetExtantDoc(), absoluteUrl);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return nullptr;
}
urls.AppendElement(absoluteUrl);
}
RefPtr<PresentationRequest> request =
new PresentationRequest(window, Move(urls));
return NS_WARN_IF(!request->Init()) ? nullptr : request.forget();
}
PresentationRequest::PresentationRequest(nsPIDOMWindowInner* aWindow,
nsTArray<nsString>&& aUrls)
: DOMEventTargetHelper(aWindow)
, mUrls(Move(aUrls))
{
}
PresentationRequest::~PresentationRequest()
{
}
bool
PresentationRequest::Init()
{
return true;
}
/* virtual */ JSObject*
PresentationRequest::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return PresentationRequestBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<Promise>
PresentationRequest::Start(ErrorResult& aRv)
{
return StartWithDevice(NullString(), aRv);
}
already_AddRefed<Promise>
PresentationRequest::StartWithDevice(const nsAString& aDeviceId,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
if (NS_WARN_IF(!global)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
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;
}
nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (IsProhibitMixedSecurityContexts(doc) &&
!IsAllURLAuthenticated()) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
RefPtr<Navigator> navigator =
nsGlobalWindow::Cast(GetOwner())->GetNavigator(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
RefPtr<Presentation> presentation = navigator->GetPresentation(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (presentation->IsStartSessionUnsettled()) {
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return promise.forget();
}
// Generate a session ID.
nsCOMPtr<nsIUUIDGenerator> uuidgen =
do_GetService("@mozilla.org/uuid-generator;1");
if(NS_WARN_IF(!uuidgen)) {
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return promise.forget();
}
nsID uuid;
uuidgen->GenerateUUIDInPlace(&uuid);
char buffer[NSID_LENGTH];
uuid.ToProvidedString(buffer);
nsAutoString id;
CopyASCIItoUTF16(buffer, id);
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return promise.forget();
}
presentation->SetStartSessionUnsettled(true);
// Get xul:browser element in parent process or nsWindowRoot object in child
// process. If it's in child process, the corresponding xul:browser element
// will be obtained at PresentationRequestParent::DoRequest in its parent
// process.
nsCOMPtr<nsIDOMEventTarget> handler =
do_QueryInterface(GetOwner()->GetChromeEventHandler());
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
nsCOMPtr<nsIPresentationServiceCallback> callback =
new PresentationRequesterCallback(this, id, promise);
nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
PresentationTransportBuilderConstructor::Create();
rv = service->StartSession(mUrls,
id,
origin,
aDeviceId,
GetOwner()->WindowID(),
handler,
principal,
callback,
constructor);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
NotifyPromiseSettled();
}
return promise.forget();
}
already_AddRefed<Promise>
PresentationRequest::Reconnect(const nsAString& aPresentationId,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
if (NS_WARN_IF(!global)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (IsProhibitMixedSecurityContexts(doc) &&
!IsAllURLAuthenticated()) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
nsString presentationId = nsString(aPresentationId);
nsCOMPtr<nsIRunnable> r =
NewRunnableMethod<nsString, RefPtr<Promise>>(
this,
&PresentationRequest::FindOrCreatePresentationConnection,
presentationId,
promise);
if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
}
return promise.forget();
}
void
PresentationRequest::FindOrCreatePresentationConnection(
const nsAString& aPresentationId,
Promise* aPromise)
{
MOZ_ASSERT(aPromise);
if (NS_WARN_IF(!GetOwner())) {
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return;
}
RefPtr<PresentationConnection> connection =
ControllerConnectionCollection::GetSingleton()->FindConnection(
GetOwner()->WindowID(),
aPresentationId,
nsIPresentationService::ROLE_CONTROLLER);
if (connection) {
nsAutoString url;
connection->GetUrl(url);
if (mUrls.Contains(url)) {
switch (connection->State()) {
case PresentationConnectionState::Closed:
// We found the matched connection.
break;
case PresentationConnectionState::Connecting:
case PresentationConnectionState::Connected:
aPromise->MaybeResolve(connection);
return;
case PresentationConnectionState::Terminated:
// A terminated connection cannot be reused.
connection = nullptr;
break;
default:
MOZ_CRASH("Unknown presentation session state.");
return;
}
} else {
connection = nullptr;
}
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if(NS_WARN_IF(!service)) {
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return;
}
nsCOMPtr<nsIPresentationServiceCallback> callback =
new PresentationReconnectCallback(this,
aPresentationId,
aPromise,
connection);
nsresult rv =
service->ReconnectSession(mUrls,
aPresentationId,
nsIPresentationService::ROLE_CONTROLLER,
callback);
if (NS_WARN_IF(NS_FAILED(rv))) {
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
}
}
already_AddRefed<Promise>
PresentationRequest::GetAvailability(ErrorResult& aRv)
{
PRES_DEBUG("%s\n", __func__);
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
if (NS_WARN_IF(!global)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (IsProhibitMixedSecurityContexts(doc) &&
!IsAllURLAuthenticated()) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
return promise.forget();
}
FindOrCreatePresentationAvailability(promise);
return promise.forget();
}
void
PresentationRequest::FindOrCreatePresentationAvailability(RefPtr<Promise>& aPromise)
{
MOZ_ASSERT(aPromise);
if (NS_WARN_IF(!GetOwner())) {
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return;
}
AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
if (NS_WARN_IF(!collection)) {
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return;
}
RefPtr<PresentationAvailability> availability =
collection->Find(GetOwner()->WindowID(), mUrls);
if (!availability) {
availability = PresentationAvailability::Create(GetOwner(), mUrls, aPromise);
} else {
PRES_DEBUG(">resolve with same object\n");
// Fetching cached available devices is asynchronous in our implementation,
// we need to ensure the promise is resolved in order.
if (availability->IsCachedValueReady()) {
aPromise->MaybeResolve(availability);
return;
}
availability->EnqueuePromise(aPromise);
}
if (!availability) {
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
}
nsresult
PresentationRequest::DispatchConnectionAvailableEvent(PresentationConnection* aConnection)
{
PresentationConnectionAvailableEventInit init;
init.mConnection = aConnection;
RefPtr<PresentationConnectionAvailableEvent> event =
PresentationConnectionAvailableEvent::Constructor(this,
NS_LITERAL_STRING("connectionavailable"),
init);
if (NS_WARN_IF(!event)) {
return NS_ERROR_FAILURE;
}
event->SetTrusted(true);
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, event);
return asyncDispatcher->PostDOMEvent();
}
void
PresentationRequest::NotifyPromiseSettled()
{
PRES_DEBUG("%s\n", __func__);
if (!GetOwner()) {
return;
}
ErrorResult rv;
RefPtr<Navigator> navigator =
nsGlobalWindow::Cast(GetOwner())->GetNavigator(rv);
if (!navigator) {
return;
}
RefPtr<Presentation> presentation = navigator->GetPresentation(rv);
if (presentation) {
presentation->SetStartSessionUnsettled(false);
}
}
bool
PresentationRequest::IsProhibitMixedSecurityContexts(nsIDocument* aDocument)
{
MOZ_ASSERT(aDocument);
if (nsContentUtils::IsChromeDoc(aDocument)) {
return true;
}
nsCOMPtr<nsIDocument> doc = aDocument;
while (doc && !nsContentUtils::IsChromeDoc(doc)) {
if (nsContentUtils::HttpsStateIsModern(doc)) {
return true;
}
doc = doc->GetParentDocument();
}
return false;
}
bool
PresentationRequest::IsPrioriAuthenticatedURL(const nsAString& aUrl)
{
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), aUrl))) {
return false;
}
nsAutoCString scheme;
nsresult rv = uri->GetScheme(scheme);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
if (scheme.EqualsLiteral("data")) {
return true;
}
nsAutoCString uriSpec;
rv = uri->GetSpec(uriSpec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
if (uriSpec.EqualsLiteral("about:blank") ||
uriSpec.EqualsLiteral("about:srcdoc")) {
return true;
}
PrincipalOriginAttributes attrs;
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateCodebasePrincipal(uri, attrs);
if (NS_WARN_IF(!principal)) {
return false;
}
nsCOMPtr<nsIContentSecurityManager> csm =
do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
if (NS_WARN_IF(!csm)) {
return false;
}
bool isTrustworthyOrigin = false;
csm->IsOriginPotentiallyTrustworthy(principal, &isTrustworthyOrigin);
return isTrustworthyOrigin;
}
bool
PresentationRequest::IsAllURLAuthenticated()
{
for (const auto& url : mUrls) {
if (!IsPrioriAuthenticatedURL(url)) {
return false;
}
}
return true;
}

View file

@ -1,84 +0,0 @@
/* -*- 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_PresentationRequest_h
#define mozilla_dom_PresentationRequest_h
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/DOMEventTargetHelper.h"
class nsIDocument;
namespace mozilla {
namespace dom {
class Promise;
class PresentationAvailability;
class PresentationConnection;
class PresentationRequest final : public DOMEventTargetHelper
{
public:
NS_DECL_ISUPPORTS_INHERITED
static already_AddRefed<PresentationRequest> Constructor(
const GlobalObject& aGlobal,
const nsAString& aUrl,
ErrorResult& aRv);
static already_AddRefed<PresentationRequest> Constructor(
const GlobalObject& aGlobal,
const Sequence<nsString>& aUrls,
ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
// WebIDL (public APIs)
already_AddRefed<Promise> Start(ErrorResult& aRv);
already_AddRefed<Promise> StartWithDevice(const nsAString& aDeviceId,
ErrorResult& aRv);
already_AddRefed<Promise> Reconnect(const nsAString& aPresentationId,
ErrorResult& aRv);
already_AddRefed<Promise> GetAvailability(ErrorResult& aRv);
IMPL_EVENT_HANDLER(connectionavailable);
nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection);
void NotifyPromiseSettled();
private:
PresentationRequest(nsPIDOMWindowInner* aWindow,
nsTArray<nsString>&& aUrls);
~PresentationRequest();
bool Init();
void FindOrCreatePresentationConnection(const nsAString& aPresentationId,
Promise* aPromise);
void FindOrCreatePresentationAvailability(RefPtr<Promise>& aPromise);
// Implement https://w3c.github.io/webappsec-mixed-content/#categorize-settings-object
bool IsProhibitMixedSecurityContexts(nsIDocument* aDocument);
// Implement https://w3c.github.io/webappsec-mixed-content/#a-priori-authenticated-url
bool IsPrioriAuthenticatedURL(const nsAString& aUrl);
bool IsAllURLAuthenticated();
nsTArray<nsString> mUrls;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationRequest_h

File diff suppressed because it is too large Load diff

View file

@ -1,68 +0,0 @@
/* -*- 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 "PresentationServiceBase.h"
#include "PresentationSessionInfo.h"
class nsIPresentationSessionRequest;
class nsIPresentationTerminateRequest;
class nsIURI;
class nsIPresentationSessionTransportBuilder;
namespace mozilla {
namespace dom {
class PresentationDeviceRequest;
class PresentationRespondingInfo;
class PresentationService final
: public nsIPresentationService
, public nsIObserver
, public PresentationServiceBase<PresentationSessionInfo>
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIPRESENTATIONSERVICE
PresentationService();
bool Init();
bool IsSessionAccessible(const nsAString& aSessionId,
const uint8_t aRole,
base::ProcessId aProcessId);
private:
friend class PresentationDeviceRequest;
virtual ~PresentationService();
void HandleShutdown();
nsresult HandleDeviceAdded(nsIPresentationDevice* aDevice);
nsresult HandleDeviceRemoved();
nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
nsresult HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest);
nsresult HandleReconnectRequest(nsIPresentationSessionRequest* aRequest);
// This is meant to be called by PresentationDeviceRequest.
already_AddRefed<PresentationSessionInfo>
CreateControllingSessionInfo(const nsAString& aUrl,
const nsAString& aSessionId,
uint64_t aWindowId);
// Emumerate all devices to get the availability of each input Urls.
nsresult UpdateAvailabilityUrlChange(
const nsTArray<nsString>& aAvailabilityUrls);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationService_h

View file

@ -1,401 +0,0 @@
/* -*- 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_PresentationServiceBase_h
#define mozilla_dom_PresentationServiceBase_h
#include "mozilla/Unused.h"
#include "nsClassHashtable.h"
#include "nsCOMArray.h"
#include "nsIPresentationListener.h"
#include "nsIPresentationService.h"
#include "nsRefPtrHashtable.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
template<class T>
class PresentationServiceBase
{
public:
PresentationServiceBase() = default;
already_AddRefed<T>
GetSessionInfo(const nsAString& aSessionId, const uint8_t aRole)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
RefPtr<T> info;
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
return mSessionInfoAtController.Get(aSessionId, getter_AddRefs(info)) ?
info.forget() : nullptr;
} else {
return mSessionInfoAtReceiver.Get(aSessionId, getter_AddRefs(info)) ?
info.forget() : nullptr;
}
}
protected:
class SessionIdManager final
{
public:
explicit SessionIdManager()
{
MOZ_COUNT_CTOR(SessionIdManager);
}
~SessionIdManager()
{
MOZ_COUNT_DTOR(SessionIdManager);
}
nsresult GetWindowId(const nsAString& aSessionId, uint64_t* aWindowId)
{
MOZ_ASSERT(NS_IsMainThread());
if (mRespondingWindowIds.Get(aSessionId, aWindowId)) {
return NS_OK;
}
return NS_ERROR_NOT_AVAILABLE;
}
nsresult GetSessionIds(uint64_t aWindowId, nsTArray<nsString>& aSessionIds)
{
MOZ_ASSERT(NS_IsMainThread());
nsTArray<nsString>* sessionIdArray;
if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
return NS_ERROR_INVALID_ARG;
}
aSessionIds.Assign(*sessionIdArray);
return NS_OK;
}
void AddSessionId(uint64_t aWindowId, const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(aWindowId == 0)) {
return;
}
nsTArray<nsString>* sessionIdArray;
if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) {
sessionIdArray = new nsTArray<nsString>();
mRespondingSessionIds.Put(aWindowId, sessionIdArray);
}
sessionIdArray->AppendElement(nsString(aSessionId));
mRespondingWindowIds.Put(aSessionId, aWindowId);
}
void RemoveSessionId(const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
uint64_t windowId = 0;
if (mRespondingWindowIds.Get(aSessionId, &windowId)) {
mRespondingWindowIds.Remove(aSessionId);
nsTArray<nsString>* sessionIdArray;
if (mRespondingSessionIds.Get(windowId, &sessionIdArray)) {
sessionIdArray->RemoveElement(nsString(aSessionId));
if (sessionIdArray->IsEmpty()) {
mRespondingSessionIds.Remove(windowId);
}
}
}
}
nsresult UpdateWindowId(const nsAString& aSessionId, const uint64_t aWindowId)
{
MOZ_ASSERT(NS_IsMainThread());
RemoveSessionId(aSessionId);
AddSessionId(aWindowId, aSessionId);
return NS_OK;
}
void Clear()
{
mRespondingSessionIds.Clear();
mRespondingWindowIds.Clear();
}
private:
nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mRespondingSessionIds;
nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds;
};
class AvailabilityManager final
{
public:
explicit AvailabilityManager()
{
MOZ_COUNT_CTOR(AvailabilityManager);
}
~AvailabilityManager()
{
MOZ_COUNT_DTOR(AvailabilityManager);
}
void AddAvailabilityListener(
const nsTArray<nsString>& aAvailabilityUrls,
nsIPresentationAvailabilityListener* aListener)
{
nsTArray<nsString> dummy;
AddAvailabilityListener(aAvailabilityUrls, aListener, dummy);
}
void AddAvailabilityListener(
const nsTArray<nsString>& aAvailabilityUrls,
nsIPresentationAvailabilityListener* aListener,
nsTArray<nsString>& aAddedUrls)
{
if (!aListener) {
MOZ_ASSERT(false, "aListener should not be null.");
return;
}
if (aAvailabilityUrls.IsEmpty()) {
MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
return;
}
aAddedUrls.Clear();
nsTArray<nsString> knownAvailableUrls;
for (const auto& url : aAvailabilityUrls) {
AvailabilityEntry* entry;
if (!mAvailabilityUrlTable.Get(url, &entry)) {
entry = new AvailabilityEntry();
mAvailabilityUrlTable.Put(url, entry);
aAddedUrls.AppendElement(url);
}
if (!entry->mListeners.Contains(aListener)) {
entry->mListeners.AppendElement(aListener);
}
if (entry->mAvailable) {
knownAvailableUrls.AppendElement(url);
}
}
if (!knownAvailableUrls.IsEmpty()) {
Unused <<
NS_WARN_IF(
NS_FAILED(aListener->NotifyAvailableChange(knownAvailableUrls,
true)));
} else {
// If we can't find any known available url and there is no newly
// added url, we still need to notify the listener of the result.
// So, the promise returned by |getAvailability| can be resolved.
if (aAddedUrls.IsEmpty()) {
Unused <<
NS_WARN_IF(
NS_FAILED(aListener->NotifyAvailableChange(aAvailabilityUrls,
false)));
}
}
}
void RemoveAvailabilityListener(
const nsTArray<nsString>& aAvailabilityUrls,
nsIPresentationAvailabilityListener* aListener)
{
nsTArray<nsString> dummy;
RemoveAvailabilityListener(aAvailabilityUrls, aListener, dummy);
}
void RemoveAvailabilityListener(
const nsTArray<nsString>& aAvailabilityUrls,
nsIPresentationAvailabilityListener* aListener,
nsTArray<nsString>& aRemovedUrls)
{
if (!aListener) {
MOZ_ASSERT(false, "aListener should not be null.");
return;
}
if (aAvailabilityUrls.IsEmpty()) {
MOZ_ASSERT(false, "aAvailabilityUrls should not be empty.");
return;
}
aRemovedUrls.Clear();
for (const auto& url : aAvailabilityUrls) {
AvailabilityEntry* entry;
if (mAvailabilityUrlTable.Get(url, &entry)) {
entry->mListeners.RemoveElement(aListener);
if (entry->mListeners.IsEmpty()) {
mAvailabilityUrlTable.Remove(url);
aRemovedUrls.AppendElement(url);
}
}
}
}
nsresult DoNotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls,
bool aAvailable)
{
typedef nsClassHashtable<nsISupportsHashKey,
nsTArray<nsString>> ListenerToUrlsMap;
ListenerToUrlsMap availabilityListenerTable;
// Create a mapping from nsIPresentationAvailabilityListener to
// availabilityUrls.
for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) {
if (aAvailabilityUrls.Contains(it.Key())) {
AvailabilityEntry* entry = it.UserData();
entry->mAvailable = aAvailable;
for (uint32_t i = 0; i < entry->mListeners.Length(); ++i) {
nsIPresentationAvailabilityListener* listener =
entry->mListeners.ObjectAt(i);
nsTArray<nsString>* urlArray;
if (!availabilityListenerTable.Get(listener, &urlArray)) {
urlArray = new nsTArray<nsString>();
availabilityListenerTable.Put(listener, urlArray);
}
urlArray->AppendElement(it.Key());
}
}
}
for (auto it = availabilityListenerTable.Iter(); !it.Done(); it.Next()) {
auto listener =
static_cast<nsIPresentationAvailabilityListener*>(it.Key());
Unused <<
NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(*it.UserData(),
aAvailable)));
}
return NS_OK;
}
void GetAvailbilityUrlByAvailability(nsTArray<nsString>& aOutArray,
bool aAvailable)
{
aOutArray.Clear();
for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) {
if (it.UserData()->mAvailable == aAvailable) {
aOutArray.AppendElement(it.Key());
}
}
}
void Clear()
{
mAvailabilityUrlTable.Clear();
}
private:
struct AvailabilityEntry
{
explicit AvailabilityEntry()
: mAvailable(false)
{}
bool mAvailable;
nsCOMArray<nsIPresentationAvailabilityListener> mListeners;
};
nsClassHashtable<nsStringHashKey, AvailabilityEntry> mAvailabilityUrlTable;
};
virtual ~PresentationServiceBase() = default;
void Shutdown()
{
mRespondingListeners.Clear();
mControllerSessionIdManager.Clear();
mReceiverSessionIdManager.Clear();
}
nsresult GetWindowIdBySessionIdInternal(const nsAString& aSessionId,
uint8_t aRole,
uint64_t* aWindowId)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
if (NS_WARN_IF(!aWindowId)) {
return NS_ERROR_INVALID_POINTER;
}
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
return mControllerSessionIdManager.GetWindowId(aSessionId, aWindowId);
}
return mReceiverSessionIdManager.GetWindowId(aSessionId, aWindowId);
}
void AddRespondingSessionId(uint64_t aWindowId,
const nsAString& aSessionId,
uint8_t aRole)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
mControllerSessionIdManager.AddSessionId(aWindowId, aSessionId);
} else {
mReceiverSessionIdManager.AddSessionId(aWindowId, aSessionId);
}
}
void RemoveRespondingSessionId(const nsAString& aSessionId,
uint8_t aRole)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
mControllerSessionIdManager.RemoveSessionId(aSessionId);
} else {
mReceiverSessionIdManager.RemoveSessionId(aSessionId);
}
}
nsresult UpdateWindowIdBySessionIdInternal(const nsAString& aSessionId,
uint8_t aRole,
const uint64_t aWindowId)
{
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
return mControllerSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
}
return mReceiverSessionIdManager.UpdateWindowId(aSessionId, aWindowId);
}
// Store the responding listener based on the window ID of the (in-process or
// OOP) receiver page.
nsRefPtrHashtable<nsUint64HashKey, nsIPresentationRespondingListener>
mRespondingListeners;
// Store the mapping between the window ID of the in-process and OOP page and the ID
// of the responding session. It's used for both controller and 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.
SessionIdManager mControllerSessionIdManager;
SessionIdManager mReceiverSessionIdManager;
nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtController;
nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtReceiver;
AvailabilityManager mAvailabilityManager;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationServiceBase_h

File diff suppressed because it is too large Load diff

View file

@ -1,304 +0,0 @@
/* -*- 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 "base/process.h"
#include "mozilla/dom/nsIContentParent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsINetworkInfoService.h"
#include "nsIPresentationControlChannel.h"
#include "nsIPresentationDevice.h"
#include "nsIPresentationListener.h"
#include "nsIPresentationService.h"
#include "nsIPresentationSessionTransport.h"
#include "nsIPresentationSessionTransportBuilder.h"
#include "nsIServerSocket.h"
#include "nsITimer.h"
#include "nsString.h"
#include "PresentationCallbacks.h"
namespace mozilla {
namespace dom {
class PresentationSessionInfo : public nsIPresentationSessionTransportCallback
, public nsIPresentationControlChannelListener
, public nsIPresentationSessionTransportBuilderListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER
PresentationSessionInfo(const nsAString& aUrl,
const nsAString& aSessionId,
const uint8_t aRole)
: mUrl(aUrl)
, mSessionId(aSessionId)
, mIsResponderReady(false)
, mIsTransportReady(false)
, mState(nsIPresentationSessionListener::STATE_CONNECTING)
, mReason(NS_OK)
{
MOZ_ASSERT(!mUrl.IsEmpty());
MOZ_ASSERT(!mSessionId.IsEmpty());
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
mRole = aRole;
}
virtual nsresult Init(nsIPresentationControlChannel* aControlChannel);
const nsAString& GetUrl() const
{
return mUrl;
}
const nsAString& GetSessionId() const
{
return mSessionId;
}
uint8_t GetRole() const
{
return mRole;
}
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(const nsAString& aData);
nsresult SendBinaryMsg(const nsACString& aData);
nsresult SendBlob(nsIDOMBlob* aBlob);
nsresult Close(nsresult aReason,
uint32_t aState);
nsresult OnTerminate(nsIPresentationControlChannel* aControlChannel);
nsresult ReplyError(nsresult aReason);
virtual bool IsAccessible(base::ProcessId aProcessId);
void SetTransportBuilderConstructor(
nsIPresentationTransportBuilderConstructor* aBuilderConstructor)
{
mBuilderConstructor = aBuilderConstructor;
}
protected:
virtual ~PresentationSessionInfo()
{
Shutdown(NS_OK);
}
virtual void Shutdown(nsresult aReason);
nsresult ReplySuccess();
bool IsSessionReady()
{
return mIsResponderReady && mIsTransportReady;
}
virtual nsresult UntrackFromService();
void SetStateWithReason(uint32_t aState, nsresult aReason)
{
if (mState == aState) {
return;
}
mState = aState;
mReason = aReason;
// Notify session state change.
if (mListener) {
DebugOnly<nsresult> rv =
mListener->NotifyStateChange(mSessionId, mState, aReason);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyStateChanged");
}
}
void ContinueTermination();
void ResetBuilder()
{
mBuilder = nullptr;
}
// Should be nsIPresentationChannelDescription::TYPE_TCP/TYPE_DATACHANNEL
uint8_t mTransportType = 0;
nsPIDOMWindowInner* GetWindow();
nsString mUrl;
nsString mSessionId;
// mRole should be nsIPresentationService::ROLE_CONTROLLER
// or nsIPresentationService::ROLE_RECEIVER.
uint8_t mRole;
bool mIsResponderReady;
bool mIsTransportReady;
bool mIsOnTerminating = false;
uint32_t mState; // CONNECTED, CLOSED, TERMINATED
nsresult mReason;
nsCOMPtr<nsIPresentationSessionListener> mListener;
nsCOMPtr<nsIPresentationDevice> mDevice;
nsCOMPtr<nsIPresentationSessionTransport> mTransport;
nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
nsCOMPtr<nsIPresentationSessionTransportBuilder> mBuilder;
nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor;
};
// Session info with controlling browsing context (sender side) behaviors.
class PresentationControllingInfo final : public PresentationSessionInfo
, public nsIServerSocketListener
, public nsIListNetworkAddressesListener
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
NS_DECL_NSISERVERSOCKETLISTENER
NS_DECL_NSILISTNETWORKADDRESSESLISTENER
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK
PresentationControllingInfo(const nsAString& aUrl,
const nsAString& aSessionId)
: PresentationSessionInfo(aUrl,
aSessionId,
nsIPresentationService::ROLE_CONTROLLER)
{}
nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
nsresult Reconnect(nsIPresentationServiceCallback* aCallback);
nsresult BuildTransport();
private:
~PresentationControllingInfo()
{
Shutdown(NS_OK);
}
void Shutdown(nsresult aReason) override;
nsresult GetAddress();
nsresult OnGetAddress(const nsACString& aAddress);
nsresult ContinueReconnect();
nsresult NotifyReconnectResult(nsresult aStatus);
nsCOMPtr<nsIServerSocket> mServerSocket;
nsCOMPtr<nsIPresentationServiceCallback> mReconnectCallback;
bool mIsReconnecting = false;
bool mDoReconnectAfterClose = false;
};
// Session info with presenting browsing context (receiver side) behaviors.
class PresentationPresentingInfo final : public PresentationSessionInfo
, public PromiseNativeHandler
, public nsITimerCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
NS_DECL_NSITIMERCALLBACK
PresentationPresentingInfo(const nsAString& aUrl,
const nsAString& aSessionId,
nsIPresentationDevice* aDevice)
: PresentationSessionInfo(aUrl,
aSessionId,
nsIPresentationService::ROLE_RECEIVER)
{
MOZ_ASSERT(aDevice);
SetDevice(aDevice);
}
nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
nsresult NotifyResponderReady();
nsresult NotifyResponderFailure();
NS_IMETHODIMP OnSessionTransport(nsIPresentationSessionTransport* transport) override;
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);
}
bool IsAccessible(base::ProcessId aProcessId) override;
nsresult DoReconnect();
private:
~PresentationPresentingInfo()
{
Shutdown(NS_OK);
}
void Shutdown(nsresult aReason) override;
nsresult InitTransportAndSendAnswer();
nsresult UntrackFromService() override;
NS_IMETHODIMP
FlushPendingEvents(nsIPresentationDataChannelSessionTransportBuilder* builder);
bool mHasFlushPendingEvents = false;
RefPtr<PresentationResponderLoadingCallback> mLoadingCallback;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIPresentationChannelDescription> mRequesterDescription;
nsTArray<nsString> mPendingCandidates;
RefPtr<Promise> mPromise;
// The content parent communicating with the content process which the OOP
// receiver page belongs to.
nsCOMPtr<nsIContentParent> mContentParent;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationSessionInfo_h

View file

@ -1,72 +0,0 @@
/* -*- 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 "PresentationSessionRequest.h"
#include "nsIPresentationControlChannel.h"
#include "nsIPresentationDevice.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(PresentationSessionRequest, nsIPresentationSessionRequest)
PresentationSessionRequest::PresentationSessionRequest(nsIPresentationDevice* aDevice,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel)
: mUrl(aUrl)
, mPresentationId(aPresentationId)
, mDevice(aDevice)
, mControlChannel(aControlChannel)
{
}
PresentationSessionRequest::~PresentationSessionRequest()
{
}
// nsIPresentationSessionRequest
NS_IMETHODIMP
PresentationSessionRequest::GetDevice(nsIPresentationDevice** aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
nsCOMPtr<nsIPresentationDevice> device = mDevice;
device.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionRequest::GetUrl(nsAString& aRetVal)
{
aRetVal = mUrl;
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionRequest::GetPresentationId(nsAString& aRetVal)
{
aRetVal = mPresentationId;
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionRequest::GetControlChannel(nsIPresentationControlChannel** aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
nsCOMPtr<nsIPresentationControlChannel> controlChannel = mControlChannel;
controlChannel.forget(aRetVal);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,41 +0,0 @@
/* -*- 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_PresentationSessionRequest_h__
#define mozilla_dom_PresentationSessionRequest_h__
#include "nsIPresentationSessionRequest.h"
#include "nsCOMPtr.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
class PresentationSessionRequest final : public nsIPresentationSessionRequest
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSESSIONREQUEST
PresentationSessionRequest(nsIPresentationDevice* aDevice,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel);
private:
virtual ~PresentationSessionRequest();
nsString mUrl;
nsString mPresentationId;
nsCOMPtr<nsIPresentationDevice> mDevice;
nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PresentationSessionRequest_h__ */

View file

@ -1,589 +0,0 @@
/* -*- 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 "nsQueryObject.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "PresentationLog.h"
#include "PresentationTCPSessionTransport.h"
#define BUFFER_SIZE 65536
using namespace mozilla;
using namespace mozilla::dom;
class CopierCallbacks final : public nsIRequestObserver
{
public:
explicit CopierCallbacks(PresentationTCPSessionTransport* aTransport)
: mOwner(aTransport)
{}
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
private:
~CopierCallbacks() {}
RefPtr<PresentationTCPSessionTransport> 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_CYCLE_COLLECTION(PresentationTCPSessionTransport, mTransport,
mSocketInputStream, mSocketOutputStream,
mInputStreamPump, mInputStreamScriptable,
mMultiplexStream, mMultiplexStreamCopier, mCallback)
NS_IMPL_CYCLE_COLLECTING_ADDREF(PresentationTCPSessionTransport)
NS_IMPL_CYCLE_COLLECTING_RELEASE(PresentationTCPSessionTransport)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PresentationTCPSessionTransport)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPresentationSessionTransport)
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionTransport)
NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionTransportBuilder)
NS_INTERFACE_MAP_ENTRY(nsIPresentationTCPSessionTransportBuilder)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
NS_INTERFACE_MAP_END
PresentationTCPSessionTransport::PresentationTCPSessionTransport()
: mReadyState(ReadyState::CLOSED)
, mAsyncCopierActive(false)
, mCloseStatus(NS_OK)
, mDataNotificationEnabled(false)
{
}
PresentationTCPSessionTransport::~PresentationTCPSessionTransport()
{
}
NS_IMETHODIMP
PresentationTCPSessionTransport::BuildTCPSenderTransport(nsISocketTransport* aTransport,
nsIPresentationSessionTransportBuilderListener* aListener)
{
if (NS_WARN_IF(!aTransport)) {
return NS_ERROR_INVALID_ARG;
}
mTransport = aTransport;
if (NS_WARN_IF(!aListener)) {
return NS_ERROR_INVALID_ARG;
}
mListener = aListener;
nsresult rv = CreateStream();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mRole = nsIPresentationService::ROLE_CONTROLLER;
nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
nsCOMPtr<nsIRunnable> onSessionTransportRunnable =
NewRunnableMethod
<nsIPresentationSessionTransport*>(mListener,
&nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
sessionTransport);
NS_DispatchToCurrentThread(onSessionTransportRunnable.forget());
nsCOMPtr<nsIRunnable> setReadyStateRunnable =
NewRunnableMethod<ReadyState>(this,
&PresentationTCPSessionTransport::SetReadyState,
ReadyState::OPEN);
return NS_DispatchToCurrentThread(setReadyStateRunnable.forget());
}
NS_IMETHODIMP
PresentationTCPSessionTransport::BuildTCPReceiverTransport(nsIPresentationChannelDescription* aDescription,
nsIPresentationSessionTransportBuilderListener* aListener)
{
if (NS_WARN_IF(!aDescription)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!aListener)) {
return NS_ERROR_INVALID_ARG;
}
mListener = aListener;
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 1228504 Take all IP addresses in PresentationChannelDescription
// into account. 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;
}
PRES_DEBUG("%s:ServerHost[%s],ServerPort[%d]\n", __func__, serverHost.get(), serverPort);
SetReadyState(ReadyState::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;
}
mRole = nsIPresentationService::ROLE_RECEIVER;
nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod
<nsIPresentationSessionTransport*>(mListener,
&nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
sessionTransport);
return NS_DispatchToCurrentThread(runnable.forget());
}
nsresult
PresentationTCPSessionTransport::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
PresentationTCPSessionTransport::CreateInputStreamPump()
{
if (NS_WARN_IF(mInputStreamPump)) {
return NS_OK;
}
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
PresentationTCPSessionTransport::EnableDataNotification()
{
if (NS_WARN_IF(!mCallback)) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (mDataNotificationEnabled) {
return NS_OK;
}
mDataNotificationEnabled = true;
if (IsReadyToNotifyData()) {
return CreateInputStreamPump();
}
return NS_OK;
}
// nsIPresentationSessionTransportBuilderListener
NS_IMETHODIMP
PresentationTCPSessionTransport::GetCallback(nsIPresentationSessionTransportCallback** aCallback)
{
nsCOMPtr<nsIPresentationSessionTransportCallback> callback = mCallback;
callback.forget(aCallback);
return NS_OK;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::SetCallback(nsIPresentationSessionTransportCallback* aCallback)
{
mCallback = aCallback;
if (!!mCallback && ReadyState::OPEN == mReadyState) {
// Notify the transport channel is ready.
Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady()));
}
return NS_OK;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::GetSelfAddress(nsINetAddr** aSelfAddress)
{
if (NS_WARN_IF(!mTransport)) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
return mTransport->GetScriptableSelfAddr(aSelfAddress);
}
void
PresentationTCPSessionTransport::EnsureCopying()
{
if (mAsyncCopierActive) {
return;
}
mAsyncCopierActive = true;
RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
Unused << NS_WARN_IF(NS_FAILED(mMultiplexStreamCopier->AsyncCopy(callbacks, nullptr)));
}
void
PresentationTCPSessionTransport::NotifyCopyComplete(nsresult aStatus)
{
mAsyncCopierActive = false;
mMultiplexStream->RemoveStream(0);
if (NS_WARN_IF(NS_FAILED(aStatus))) {
if (mReadyState != ReadyState::CLOSED) {
mCloseStatus = aStatus;
SetReadyState(ReadyState::CLOSED);
}
return;
}
uint32_t count;
nsresult rv = mMultiplexStream->GetCount(&count);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (count) {
EnsureCopying();
return;
}
if (mReadyState == ReadyState::CLOSING) {
mSocketOutputStream->Close();
mCloseStatus = NS_OK;
SetReadyState(ReadyState::CLOSED);
}
}
NS_IMETHODIMP
PresentationTCPSessionTransport::Send(const nsAString& aData)
{
if (NS_WARN_IF(mReadyState != ReadyState::OPEN)) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsresult rv;
nsCOMPtr<nsIStringInputStream> stream =
do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
if(NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
NS_ConvertUTF16toUTF8 msgString(aData);
rv = stream->SetData(msgString.BeginReading(), msgString.Length());
if(NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
mMultiplexStream->AppendStream(stream);
EnsureCopying();
return NS_OK;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::SendBinaryMsg(const nsACString& aData)
{
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::SendBlob(nsIDOMBlob* aBlob)
{
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::Close(nsresult aReason)
{
PRES_DEBUG("%s:reason[%x]\n", __func__, aReason);
if (mReadyState == ReadyState::CLOSED || mReadyState == ReadyState::CLOSING) {
return NS_OK;
}
mCloseStatus = aReason;
SetReadyState(ReadyState::CLOSING);
uint32_t count = 0;
mMultiplexStream->GetCount(&count);
if (!count) {
mSocketOutputStream->Close();
}
mSocketInputStream->Close();
mDataNotificationEnabled = false;
mListener = nullptr;
return NS_OK;
}
void
PresentationTCPSessionTransport::SetReadyState(ReadyState aReadyState)
{
mReadyState = aReadyState;
if (mReadyState == ReadyState::OPEN) {
if (IsReadyToNotifyData()) {
CreateInputStreamPump();
}
if (NS_WARN_IF(!mCallback)) {
return;
}
// Notify the transport channel is ready.
Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady()));
} else if (mReadyState == ReadyState::CLOSED && mCallback) {
if (NS_WARN_IF(!mCallback)) {
return;
}
// Notify the transport channel has been shut down.
Unused <<
NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportClosed(mCloseStatus)));
mCallback = nullptr;
}
}
// nsITransportEventSink
NS_IMETHODIMP
PresentationTCPSessionTransport::OnTransportStatus(nsITransport* aTransport,
nsresult aStatus,
int64_t aProgress,
int64_t aProgressMax)
{
PRES_DEBUG("%s:aStatus[%x]\n", __func__, aStatus);
MOZ_ASSERT(NS_IsMainThread());
if (aStatus != NS_NET_STATUS_CONNECTED_TO) {
return NS_OK;
}
SetReadyState(ReadyState::OPEN);
return NS_OK;
}
// nsIInputStreamCallback
NS_IMETHODIMP
PresentationTCPSessionTransport::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 != ReadyState::CLOSED) {
mCloseStatus = NS_ERROR_CONNECTION_REFUSED;
SetReadyState(ReadyState::CLOSED);
}
}
return NS_OK;
}
// nsIRequestObserver
NS_IMETHODIMP
PresentationTCPSessionTransport::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
// Do nothing.
return NS_OK;
}
NS_IMETHODIMP
PresentationTCPSessionTransport::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatusCode)
{
PRES_DEBUG("%s:aStatusCode[%x]\n", __func__, 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 != ReadyState::CLOSED) {
mCloseStatus = aStatusCode;
SetReadyState(ReadyState::CLOSED);
}
return NS_OK;
}
// nsIStreamListener
NS_IMETHODIMP
PresentationTCPSessionTransport::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, false);
}

View file

@ -1,110 +0,0 @@
/* -*- 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/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsIAsyncInputStream.h"
#include "nsIPresentationSessionTransport.h"
#include "nsIPresentationSessionTransportBuilder.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
* |PresentationControllingInfo| (sender side) or |PresentationPresentingInfo|
* (receiver side) in PresentationSessionInfo.cpp.
*/
class PresentationTCPSessionTransport final : public nsIPresentationSessionTransport
, public nsIPresentationTCPSessionTransportBuilder
, public nsITransportEventSink
, public nsIInputStreamCallback
, public nsIStreamListener
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PresentationTCPSessionTransport,
nsIPresentationSessionTransport)
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDER
NS_DECL_NSIPRESENTATIONTCPSESSIONTRANSPORTBUILDER
NS_DECL_NSITRANSPORTEVENTSINK
NS_DECL_NSIINPUTSTREAMCALLBACK
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
PresentationTCPSessionTransport();
void NotifyCopyComplete(nsresult aStatus);
private:
~PresentationTCPSessionTransport();
nsresult CreateStream();
nsresult CreateInputStreamPump();
void EnsureCopying();
enum class ReadyState {
CONNECTING,
OPEN,
CLOSING,
CLOSED
};
void SetReadyState(ReadyState aReadyState);
bool IsReadyToNotifyData()
{
return mDataNotificationEnabled && mReadyState == ReadyState::OPEN;
}
ReadyState mReadyState;
bool mAsyncCopierActive;
nsresult mCloseStatus;
bool mDataNotificationEnabled;
uint8_t mRole = 0;
// 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;
nsCOMPtr<nsIPresentationSessionTransportBuilderListener> mListener;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationSessionTransport_h

View file

@ -1,73 +0,0 @@
/* -*- 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 "PresentationTerminateRequest.h"
#include "nsIPresentationControlChannel.h"
#include "nsIPresentationDevice.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(PresentationTerminateRequest, nsIPresentationTerminateRequest)
PresentationTerminateRequest::PresentationTerminateRequest(
nsIPresentationDevice* aDevice,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver)
: mPresentationId(aPresentationId)
, mDevice(aDevice)
, mControlChannel(aControlChannel)
, mIsFromReceiver(aIsFromReceiver)
{
}
PresentationTerminateRequest::~PresentationTerminateRequest()
{
}
// nsIPresentationTerminateRequest
NS_IMETHODIMP
PresentationTerminateRequest::GetDevice(nsIPresentationDevice** aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
nsCOMPtr<nsIPresentationDevice> device = mDevice;
device.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
PresentationTerminateRequest::GetPresentationId(nsAString& aRetVal)
{
aRetVal = mPresentationId;
return NS_OK;
}
NS_IMETHODIMP
PresentationTerminateRequest::GetControlChannel(
nsIPresentationControlChannel** aRetVal)
{
NS_ENSURE_ARG_POINTER(aRetVal);
nsCOMPtr<nsIPresentationControlChannel> controlChannel = mControlChannel;
controlChannel.forget(aRetVal);
return NS_OK;
}
NS_IMETHODIMP
PresentationTerminateRequest::GetIsFromReceiver(bool* aRetVal)
{
*aRetVal = mIsFromReceiver;
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,41 +0,0 @@
/* -*- 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_PresentationTerminateRequest_h__
#define mozilla_dom_PresentationTerminateRequest_h__
#include "nsIPresentationTerminateRequest.h"
#include "nsCOMPtr.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
class PresentationTerminateRequest final : public nsIPresentationTerminateRequest
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONTERMINATEREQUEST
PresentationTerminateRequest(nsIPresentationDevice* aDevice,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver);
private:
virtual ~PresentationTerminateRequest();
nsString mPresentationId;
nsCOMPtr<nsIPresentationDevice> mDevice;
nsCOMPtr<nsIPresentationControlChannel> mControlChannel;
bool mIsFromReceiver;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_PresentationTerminateRequest_h__ */

View file

@ -1,85 +0,0 @@
/* -*- 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 "PresentationTransportBuilderConstructor.h"
#include "nsComponentManagerUtils.h"
#include "nsIPresentationControlChannel.h"
#include "nsIPresentationSessionTransport.h"
#include "nsXULAppAPI.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(DummyPresentationTransportBuilderConstructor,
nsIPresentationTransportBuilderConstructor)
NS_IMETHODIMP
DummyPresentationTransportBuilderConstructor::CreateTransportBuilder(
uint8_t aType,
nsIPresentationSessionTransportBuilder** aRetval)
{
MOZ_ASSERT(false, "Unexpected to be invoked.");
return NS_OK;
}
NS_IMPL_ISUPPORTS_INHERITED0(PresentationTransportBuilderConstructor,
DummyPresentationTransportBuilderConstructor)
/* static */ already_AddRefed<nsIPresentationTransportBuilderConstructor>
PresentationTransportBuilderConstructor::Create()
{
nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor;
if (XRE_IsContentProcess()) {
constructor = new DummyPresentationTransportBuilderConstructor();
} else {
constructor = new PresentationTransportBuilderConstructor();
}
return constructor.forget();
}
NS_IMETHODIMP
PresentationTransportBuilderConstructor::CreateTransportBuilder(
uint8_t aType,
nsIPresentationSessionTransportBuilder** aRetval)
{
if (NS_WARN_IF(!aRetval)) {
return NS_ERROR_INVALID_ARG;
}
*aRetval = nullptr;
if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
return NS_ERROR_INVALID_ARG;
}
if (XRE_IsContentProcess()) {
MOZ_ASSERT(false,
"CreateTransportBuilder can only be invoked in parent process.");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
builder =
do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
} else {
builder =
do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
}
if (NS_WARN_IF(!builder)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
builder.forget(aRetval);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,48 +0,0 @@
/* -*- 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_PresentationTransportBuilderConstructor_h
#define mozilla_dom_PresentationTransportBuilderConstructor_h
#include "nsCOMPtr.h"
#include "nsIPresentationSessionTransportBuilder.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace dom {
class DummyPresentationTransportBuilderConstructor :
public nsIPresentationTransportBuilderConstructor
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
DummyPresentationTransportBuilderConstructor() = default;
protected:
virtual ~DummyPresentationTransportBuilderConstructor() = default;
};
class PresentationTransportBuilderConstructor final :
public DummyPresentationTransportBuilderConstructor
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
static already_AddRefed<nsIPresentationTransportBuilderConstructor>
Create();
private:
PresentationTransportBuilderConstructor() = default;
virtual ~PresentationTransportBuilderConstructor() = default;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationTransportBuilderConstructor_h

View file

@ -1,30 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
XPIDL_SOURCES += [
'nsIPresentationControlChannel.idl',
'nsIPresentationControlService.idl',
'nsIPresentationDevice.idl',
'nsIPresentationDeviceManager.idl',
'nsIPresentationDevicePrompt.idl',
'nsIPresentationDeviceProvider.idl',
'nsIPresentationListener.idl',
'nsIPresentationLocalDevice.idl',
'nsIPresentationRequestUIGlue.idl',
'nsIPresentationService.idl',
'nsIPresentationSessionRequest.idl',
'nsIPresentationSessionTransport.idl',
'nsIPresentationSessionTransportBuilder.idl',
'nsIPresentationTerminateRequest.idl',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
XPIDL_SOURCES += [
'nsIPresentationNetworkHelper.idl',
]
XPIDL_MODULE = 'dom_presentation'

View file

@ -1,139 +0,0 @@
/* 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 nsIArray;
interface nsIInputStream;
[scriptable, uuid(ae318e05-2a4e-4f85-95c0-e8b191ad812c)]
interface nsIPresentationChannelDescription: nsISupports
{
const unsigned short TYPE_TCP = 1;
const unsigned short TYPE_DATACHANNEL = 2;
// Type of transport channel.
readonly attribute uint8_t type;
// Addresses for TCP channel (as a list of nsISupportsCString).
// Should only be used while type == TYPE_TCP.
readonly attribute nsIArray tcpAddress;
// Port number for TCP channel.
// Should only be used while type == TYPE_TCP.
readonly attribute uint16_t tcpPort;
// SDP for Data Channel.
// Should only be used while type == TYPE_DATACHANNEL.
readonly attribute DOMString dataChannelSDP;
};
/*
* The callbacks for events on control channel.
*/
[scriptable, uuid(96dd548f-7d0f-43c1-b1ad-28e666cf1e82)]
interface nsIPresentationControlChannelListener: nsISupports
{
/*
* Callback for receiving offer from remote endpoint.
* @param offer The received offer.
*/
void onOffer(in nsIPresentationChannelDescription offer);
/*
* Callback for receiving answer from remote endpoint.
* @param answer The received answer.
*/
void onAnswer(in nsIPresentationChannelDescription answer);
/*
* Callback for receiving ICE candidate from remote endpoint.
* @param answer The received answer.
*/
void onIceCandidate(in DOMString candidate);
/*
* The callback for notifying channel connected. This should be async called
* after nsIPresentationDevice::establishControlChannel.
*/
void notifyConnected();
/*
* The callback for notifying channel disconnected.
* @param reason The reason of channel close, NS_OK represents normal close.
*/
void notifyDisconnected(in nsresult reason);
/*
* The callback for notifying the reconnect command is acknowledged.
*/
void notifyReconnected();
};
/*
* The control channel for establishing RTCPeerConnection for a presentation
* session. SDP Offer/Answer will be exchanged through this interface. The
* control channel should be in-order.
*/
[scriptable, uuid(e60e208c-a9f5-4bc6-9a3e-47f3e4ae9c57)]
interface nsIPresentationControlChannel: nsISupports
{
// The listener for handling events of this control channel.
// All the events should be pending until listener is assigned.
attribute nsIPresentationControlChannelListener listener;
/*
* Send offer to remote endpoint. |onOffer| should be invoked on remote
* endpoint.
* @param offer The offer to send.
* @throws NS_ERROR_FAILURE on failure
*/
void sendOffer(in nsIPresentationChannelDescription offer);
/*
* Send answer to remote endpoint. |onAnswer| should be invoked on remote
* endpoint.
* @param answer The answer to send.
* @throws NS_ERROR_FAILURE on failure
*/
void sendAnswer(in nsIPresentationChannelDescription answer);
/*
* Send ICE candidate to remote endpoint. |onIceCandidate| should be invoked
* on remote endpoint.
* @param candidate The candidate to send
* @throws NS_ERROR_FAILURE on failure
*/
void sendIceCandidate(in DOMString candidate);
/*
* Launch a presentation on remote endpoint.
* @param presentationId The Id for representing this session.
* @param url The URL requested to open by remote device.
* @throws NS_ERROR_FAILURE on failure
*/
void launch(in DOMString presentationId, in DOMString url);
/*
* Terminate a presentation on remote endpoint.
* @param presentationId The Id for representing this session.
* @throws NS_ERROR_FAILURE on failure
*/
void terminate(in DOMString presentationId);
/*
* Disconnect the control channel.
* @param reason The reason of disconnecting channel; NS_OK represents normal.
*/
void disconnect(in nsresult reason);
/*
* Reconnect a presentation on remote endpoint.
* Note that only controller is allowed to reconnect a session.
* @param presentationId The Id for representing this session.
* @param url The URL requested to open by remote device.
* @throws NS_ERROR_FAILURE on failure
*/
void reconnect(in DOMString presentationId, in DOMString url);
};

View file

@ -1,156 +0,0 @@
/* 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 nsIPresentationControlChannel;
%{C++
#define PRESENTATION_CONTROL_SERVICE_CONTACT_ID \
"@mozilla.org/presentation/control-service;1"
%}
/*
* The device information required for establishing control channel.
*/
[scriptable, uuid(296fd171-e4d0-4de0-99ff-ad8ed52ddef3)]
interface nsITCPDeviceInfo: nsISupports
{
readonly attribute AUTF8String id;
readonly attribute AUTF8String address;
readonly attribute uint16_t port;
// SHA-256 fingerprint of server certificate. Empty string represents
// server doesn't support TLS or not available.
readonly attribute AUTF8String certFingerprint;
};
[scriptable, uuid(09bddfaf-fcc2-4dc9-b33e-a509a1c2fb6d)]
interface nsIPresentationControlServerListener: nsISupports
{
/**
* Callback while the server is ready or restarted.
* @param aPort
* The port of the server socket.
* @param aCertFingerprint
* The SHA-256 fingerprint of TLS server certificate.
* Empty string represents server started without encryption.
*/
void onServerReady(in uint16_t aPort, in AUTF8String aCertFingerprint);
/**
* Callback while the server is stopped or fails to start.
* @param aResult
* The error cause of server stopped or the reason of
* start failure.
* NS_OK means the server is stopped by close.
*/
void onServerStopped(in nsresult aResult);
/**
* Callback while the remote host is requesting to start a presentation session.
* @param aDeviceInfo The device information related to the remote host.
* @param aUrl The URL requested to open by remote device.
* @param aPresentationId The Id for representing this session.
* @param aControlChannel The control channel for this session.
*/
void onSessionRequest(in nsITCPDeviceInfo aDeviceInfo,
in DOMString aUrl,
in DOMString aPresentationId,
in nsIPresentationControlChannel aControlChannel);
/**
* Callback while the remote host is requesting to terminate a presentation session.
* @param aDeviceInfo The device information related to the remote host.
* @param aPresentationId The Id for representing this session.
* @param aControlChannel The control channel for this session.
* @param aIsFromReceiver true if termination is initiated by receiver.
*/
void onTerminateRequest(in nsITCPDeviceInfo aDeviceInfo,
in DOMString aPresentationId,
in nsIPresentationControlChannel aControlChannel,
in boolean aIsFromReceiver);
/**
* Callback while the remote host is requesting to reconnect a presentation session.
* @param aDeviceInfo The device information related to the remote host.
* @param aUrl The URL requested to open by remote device.
* @param aPresentationId The Id for representing this session.
* @param aControlChannel The control channel for this session.
*/
void onReconnectRequest(in nsITCPDeviceInfo aDeviceInfo,
in DOMString url,
in DOMString aPresentationId,
in nsIPresentationControlChannel aControlChannel);
};
/**
* Presentation control service which can be used for both presentation
* control client and server.
*/
[scriptable, uuid(55d6b605-2389-4aae-a8fe-60d4440540ea)]
interface nsIPresentationControlService: nsISupports
{
/**
* This method initializes server socket. Caller should set listener and
* monitor onServerReady event to get the correct server info.
* @param aEncrypted
* True for using TLS control channel.
* @param aPort
* The port of the server socket. Pass 0 or opt-out to indicate no
* preference, and a port will be selected automatically.
* @throws NS_ERROR_FAILURE if the server socket has been inited or the
* server socket can not be inited.
*/
void startServer(in boolean aEncrypted, [optional] in uint16_t aPort);
/**
* Request connection to designated remote presentation control receiver.
* @param aDeviceInfo
* The remtoe device info for establish connection.
* @returns The control channel for this session.
* @throws NS_ERROR_FAILURE if the Id hasn't been inited.
*/
nsIPresentationControlChannel connect(in nsITCPDeviceInfo aDeviceInfo);
/**
* Check the compatibility to remote presentation control server.
* @param aVersion
* The version of remote server.
*/
boolean isCompatibleServer(in uint32_t aVersion);
/**
* Close server socket and call |listener.onClose(NS_OK)|
*/
void close();
/**
* Get the listen port of the TCP socket, valid after the server is ready.
* 0 indicates the server socket is not ready or is closed.
*/
readonly attribute uint16_t port;
/**
* The protocol version implemented by this server.
*/
readonly attribute uint32_t version;
/**
* The id of the TCP presentation server. |requestSession| won't
* work until the |id| is set.
*/
attribute AUTF8String id;
/**
* The fingerprint of the TLS server certificate.
* Empty string indicates the server is not ready or not encrypted.
*/
attribute AUTF8String certFingerprint;
/**
* The listener for handling events of this presentation control server.
* Listener must be provided before invoke |startServer| and |close|.
*/
attribute nsIPresentationControlServerListener listener;
};

View file

@ -1,43 +0,0 @@
/* 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 nsIPresentationControlChannel;
/*
* Remote device.
*/
[scriptable, uuid(b1e0a7af-5936-4066-8f2e-f789fb9a7e8f)]
interface nsIPresentationDevice : nsISupports
{
// The unique Id for the device. UUID is recommanded.
readonly attribute AUTF8String id;
// The human-readable name of this device.
readonly attribute AUTF8String name;
// TODO expose more info in order to fulfill UX spec
// The category of this device, could be "wifi", "bluetooth", "hdmi", etc.
readonly attribute AUTF8String type;
/*
* Establish a control channel to this device.
* @returns The control channel for this session.
* @throws NS_ERROR_FAILURE if the establishment fails
*/
nsIPresentationControlChannel establishControlChannel();
// Do something when presentation session is disconnected.
void disconnect();
/*
* Query if requested presentation URL is supported.
* @params requestedUrl the designated URL for a presentation request.
* @returns true if designated URL is supported.
*/
boolean isRequestedUrlSupported(in DOMString requestedUrl);
};

View file

@ -1,51 +0,0 @@
/* 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 nsIArray;
interface nsIPresentationDeviceProvider;
%{C++
#define PRESENTATION_DEVICE_MANAGER_CONTRACTID "@mozilla.org/presentation-device/manager;1"
#define PRESENTATION_DEVICE_CHANGE_TOPIC "presentation-device-change"
%}
/*
* Manager for the device availability. User can observe "presentation-device-change"
* for any update of the available devices.
*/
[scriptable, uuid(beb61db5-3d5f-454f-a15a-dbfa0337c569)]
interface nsIPresentationDeviceManager : nsISupports
{
// true if there is any device available.
readonly attribute boolean deviceAvailable;
/*
* Register a device provider manually.
* @param provider The device provider to add.
*/
void addDeviceProvider(in nsIPresentationDeviceProvider provider);
/*
* Unregister a device provider manually.
* @param provider The device provider to remove.
*/
void removeDeviceProvider(in nsIPresentationDeviceProvider provider);
/*
* Force all registered device providers to update device information.
*/
void forceDiscovery();
/*
* Retrieve all available devices or all available devices that supports
* designated presentation URLs, return a list of nsIPresentationDevice.
* The returned list is a cached device list and could be out-of-date.
* Observe device change events to get following updates.
* @param presentationUrls the target presentation URLs for device filtering
*/
nsIArray getAvailableDevices([optional] in nsIArray presentationUrls);
};

View file

@ -1,58 +0,0 @@
/* 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 nsIArray;
interface nsIDOMEventTarget;
interface nsIPresentationDevice;
interface nsIPrincipal;
%{C++
#define PRESENTATION_DEVICE_PROMPT_CONTRACTID "@mozilla.org/presentation-device/prompt;1"
%}
/*
* The information and callbacks for device selection
*/
[scriptable, uuid(b2aa7f6a-9448-446a-bba4-9c29638b0ed4)]
interface nsIPresentationDeviceRequest : nsISupports
{
// The origin which initiate the request.
readonly attribute DOMString origin;
// The array of candidate URLs.
readonly attribute nsIArray requestURLs;
// The XUL browser element that the request was originated in.
readonly attribute nsIDOMEventTarget chromeEventHandler;
// The principal of the request.
readonly attribute nsIPrincipal principal;
/*
* Callback after selecting a device
* @param device The selected device.
*/
void select(in nsIPresentationDevice device);
/*
* Callback after selection failed or canceled by user.
* @param reason The error cause for canceling this request.
*/
void cancel(in nsresult reason);
};
/*
* UI prompt for device selection.
*/
[scriptable, uuid(ac1a7e44-de86-454f-a9f1-276de2539831)]
interface nsIPresentationDevicePrompt : nsISupports
{
/*
* Request a device selection.
* @param request The information and callbacks of this selection request.
*/
void promptDeviceSelection(in nsIPresentationDeviceRequest request);
};

View file

@ -1,75 +0,0 @@
/* 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 nsIPresentationDevice;
interface nsIPresentationControlChannel;
%{C++
#define PRESENTATION_DEVICE_PROVIDER_CATEGORY "presentation-device-provider"
%}
/*
* The callbacks for any device updates and session request.
*/
[scriptable, uuid(46fd372b-2e40-4179-9b36-0478d141e440)]
interface nsIPresentationDeviceListener: nsISupports
{
void addDevice(in nsIPresentationDevice device);
void removeDevice(in nsIPresentationDevice device);
void updateDevice(in nsIPresentationDevice device);
/*
* Callback while the remote device is requesting to start a presentation session.
* @param device The remote device that sent session request.
* @param url The URL requested to open by remote device.
* @param presentationId The Id for representing this session.
* @param controlChannel The control channel for this session.
*/
void onSessionRequest(in nsIPresentationDevice device,
in DOMString url,
in DOMString presentationId,
in nsIPresentationControlChannel controlChannel);
/*
* Callback while the remote device is requesting to terminate a presentation session.
* @param device The remote device that sent session request.
* @param presentationId The Id for representing this session.
* @param controlChannel The control channel for this session.
* @param aIsFromReceiver true if termination is initiated by receiver.
*/
void onTerminateRequest(in nsIPresentationDevice device,
in DOMString presentationId,
in nsIPresentationControlChannel controlChannel,
in boolean aIsFromReceiver);
/*
* Callback while the remote device is requesting to reconnect a presentation session.
* @param device The remote device that sent session request.
* @param aUrl The URL requested to open by remote device.
* @param presentationId The Id for representing this session.
* @param controlChannel The control channel for this session.
*/
void onReconnectRequest(in nsIPresentationDevice device,
in DOMString url,
in DOMString presentationId,
in nsIPresentationControlChannel controlChannel);
};
/*
* Device provider for any device protocol, can be registered as default
* providers by adding its contractID to category "presentation-device-provider".
*/
[scriptable, uuid(3db2578a-0f50-44ad-b01b-28427b71b7bf)]
interface nsIPresentationDeviceProvider: nsISupports
{
// The listener for handling any device update.
attribute nsIPresentationDeviceListener listener;
/*
* Force to update device information.
*/
void forceDiscovery();
};

View file

@ -1,50 +0,0 @@
/* 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"
[ref] native URLArrayRef(const nsTArray<nsString>);
[uuid(0105f837-4279-4715-9d5b-2dc3f8b65353)]
interface nsIPresentationAvailabilityListener : nsISupports
{
/*
* Called when device availability changes.
*/
[noscript] void notifyAvailableChange(in URLArrayRef urls,
in bool available);
};
[scriptable, uuid(7dd48df8-8f8c-48c7-ac37-7b9fd1acf2f8)]
interface nsIPresentationSessionListener : nsISupports
{
const unsigned short STATE_CONNECTING = 0;
const unsigned short STATE_CONNECTED = 1;
const unsigned short STATE_CLOSED = 2;
const unsigned short STATE_TERMINATED = 3;
/*
* Called when session state changes.
*/
void notifyStateChange(in DOMString sessionId,
in unsigned short state,
in nsresult reason);
/*
* Called when receive messages.
*/
void notifyMessage(in DOMString sessionId,
in ACString data,
in boolean isBinary);
};
[scriptable, uuid(27f101d7-9ed1-429e-b4f8-43b00e8e111c)]
interface nsIPresentationRespondingListener : nsISupports
{
/*
* Called when an incoming session connects.
*/
void notifySessionConnect(in unsigned long long windowId,
in DOMString sessionId);
};

View file

@ -1,17 +0,0 @@
/* 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 "nsIPresentationDevice.idl"
/*
* Local device.
* This device is used for 1-UA use case. The result for display is rendered by
* this host device.
*/
[scriptable, uuid(dd239720-cab6-4fb5-9025-cba23f1bbc2d)]
interface nsIPresentationLocalDevice : nsIPresentationDevice
{
// (1-UA only) The property is used to get the window ID of 1-UA device.
readonly attribute AUTF8String windowId;
};

View file

@ -1,36 +0,0 @@
/* 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_NETWORK_HELPER_CONTRACTID \
"@mozilla.org/presentation-device/networkHelper;1"
%}
[scriptable, uuid(0a7e134f-ff80-4e73-91e6-12b3134fe568)]
interface nsIPresentationNetworkHelperListener : nsISupports
{
/**
* Called when error occurs.
* @param aReason error message.
*/
void onError(in AUTF8String aReason);
/**
* Called when get Wi-Fi IP address.
* @param aIPAddress the IP address of Wi-Fi interface.
*/
void onGetWifiIPAddress(in AUTF8String aIPAddress);
};
[scriptable, uuid(650dc16b-3d9c-49a6-9037-1d6f2d18c90c)]
interface nsIPresentationNetworkHelper : nsISupports
{
/**
* Get IP address of Wi-Fi interface.
* @param aListener the callback interface.
*/
void getWifiIPAddress(in nsIPresentationNetworkHelperListener aListener);
};

View file

@ -1,29 +0,0 @@
/* 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 nsIPresentationDevice;
%{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,
in nsIPresentationDevice device);
};

View file

@ -1,275 +0,0 @@
/* 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 nsIDOMBlob;
interface nsIDOMEventTarget;
interface nsIInputStream;
interface nsIPresentationAvailabilityListener;
interface nsIPresentationRespondingListener;
interface nsIPresentationSessionListener;
interface nsIPresentationTransportBuilderConstructor;
interface nsIPrincipal;
%{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"
#include "nsTArray.h"
class nsString;
%}
[ref] native URLArrayRef(const nsTArray<nsString>);
[scriptable, uuid(12073206-0065-4b10-9488-a6eb9b23e65b)]
interface nsIPresentationServiceCallback : nsISupports
{
/*
* Called when the operation succeeds.
*
* @param url: the selected request url used to start or reconnect a session.
*/
void notifySuccess(in DOMString url);
/*
* Called when the operation fails.
*
* @param error: error message.
*/
void notifyError(in nsresult error);
};
[scriptable, uuid(de42b741-5619-4650-b961-c2cebb572c95)]
interface nsIPresentationService : nsISupports
{
const unsigned short ROLE_CONTROLLER = 0x1;
const unsigned short ROLE_RECEIVER = 0x2;
const unsigned short CLOSED_REASON_ERROR = 0x1;
const unsigned short CLOSED_REASON_CLOSED = 0x2;
const unsigned short CLOSED_REASON_WENTAWAY = 0x3;
/*
* Start a new presentation session and display a prompt box which asks users
* to select a device.
*
* @param urls: The candidate Urls of presenting page. Only one url would be used.
* @param sessionId: An ID to identify presentation session.
* @param origin: The url of requesting page.
* @param deviceId: The specified device of handling this request, null string
* for prompt device selection dialog.
* @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. Generally it's the case the window is
* located in different process from this service)
* @param eventTarget: The chrome event handler, in particular XUL browser
* element in parent process, that the request was
* originated in.
* @param principal: The principal that initiated the session.
* @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.
* @param constructor: The constructor for creating a transport builder.
*/
[noscript] void startSession(in URLArrayRef urls,
in DOMString sessionId,
in DOMString origin,
in DOMString deviceId,
in unsigned long long windowId,
in nsIDOMEventTarget eventTarget,
in nsIPrincipal principal,
in nsIPresentationServiceCallback callback,
in nsIPresentationTransportBuilderConstructor constructor);
/*
* Send the message to the session.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param data: the message being sent out.
*/
void sendSessionMessage(in DOMString sessionId,
in uint8_t role,
in DOMString data);
/*
* Send the binary message to the session.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param data: the message being sent out.
*/
void sendSessionBinaryMsg(in DOMString sessionId,
in uint8_t role,
in ACString data);
/*
* Send the blob to the session.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param blob: The input blob to be sent.
*/
void sendSessionBlob(in DOMString sessionId,
in uint8_t role,
in nsIDOMBlob blob);
/*
* Close the session.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
*/
void closeSession(in DOMString sessionId,
in uint8_t role,
in uint8_t closedReason);
/*
* Terminate the session.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
*/
void terminateSession(in DOMString sessionId,
in uint8_t role);
/*
* Reconnect the session.
*
* @param url: The request Urls.
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param callback: NotifySuccess() is called when a control channel
* is opened successfully.
* Otherwise, NotifyError() is called with a error message.
*/
[noscript] void reconnectSession(in URLArrayRef urls,
in DOMString sessionId,
in uint8_t role,
in nsIPresentationServiceCallback callback);
/*
* Register an availability listener. Must be called from the main thread.
*
* @param availabilityUrls: The Urls that this listener is interested in.
* @param listener: The listener to register.
*/
[noscript] void registerAvailabilityListener(
in URLArrayRef availabilityUrls,
in nsIPresentationAvailabilityListener listener);
/*
* Unregister an availability listener. Must be called from the main thread.
*
* @param availabilityUrls: The Urls that are registered before.
* @param listener: The listener to unregister.
*/
[noscript] void unregisterAvailabilityListener(
in URLArrayRef availabilityUrls,
in nsIPresentationAvailabilityListener listener);
/*
* Register a session listener. Must be called from the main thread.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param listener: The listener to register.
*/
void registerSessionListener(in DOMString sessionId,
in uint8_t role,
in nsIPresentationSessionListener listener);
/*
* Unregister a session listener. Must be called from the main thread.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
*/
void unregisterSessionListener(in DOMString sessionId,
in uint8_t role);
/*
* Register a responding listener. Must be called from the main thread.
*
* @param windowId: The window ID associated with the listener.
* @param listener: The listener to register.
*/
void registerRespondingListener(in unsigned long long windowId,
in nsIPresentationRespondingListener listener);
/*
* Unregister a responding listener. Must be called from the main thread.
* @param windowId: The window ID associated with the listener.
*/
void unregisterRespondingListener(in unsigned long long 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.
* @param isLoading true if receiver page is loading successfully.
* @param constructor: The constructor for creating a transport builder.
*/
void notifyReceiverReady(in DOMString sessionId,
in unsigned long long windowId,
in boolean isLoading,
in nsIPresentationTransportBuilderConstructor constructor);
/*
* Notify the transport is closed
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param reason: the error message. NS_OK indicates it is closed normally.
*/
void NotifyTransportClosed(in DOMString sessionId,
in uint8_t role,
in nsresult reason);
/*
* Untrack the relevant info about the presentation session if there's any.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
*/
void untrackSessionInfo(in DOMString sessionId, in uint8_t role);
/*
* The windowId for building RTCDataChannel session transport
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
*/
unsigned long long getWindowIdBySessionId(in DOMString sessionId,
in uint8_t role);
/*
* Update the mapping of the session ID and window ID.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
* @param windowId: The inner window ID associated with the presentation
* session.
*/
void updateWindowIdBySessionId(in DOMString sessionId,
in uint8_t role,
in unsigned long long windowId);
/*
* To build the session transport.
* NOTE: This function should be only called at controller side.
*
* @param sessionId: An ID to identify presentation session.
* @param role: Identify the function called by controller or receiver.
*/
void buildTransport(in DOMString sessionId, in uint8_t role);
};

View file

@ -1,35 +0,0 @@
/* 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 nsIPresentationDevice;
interface nsIPresentationControlChannel;
%{C++
#define PRESENTATION_SESSION_REQUEST_TOPIC "presentation-session-request"
#define PRESENTATION_RECONNECT_REQUEST_TOPIC "presentation-reconnect-request"
%}
/*
* The event of a device requesting for starting or reconnecting
* a presentation session. User can monitor the session request
* on every device by observing "presentation-sesion-request" for a
* new session and "presentation-reconnect-request" for reconnecting.
*/
[scriptable, uuid(d808a084-d0f8-455a-a8df-5879e05a755b)]
interface nsIPresentationSessionRequest: nsISupports
{
// The device which requesting the presentation session.
readonly attribute nsIPresentationDevice device;
// The URL requested to open by remote device.
readonly attribute DOMString url;
// The Id for representing this session.
readonly attribute DOMString presentationId;
// The control channel for this session.
readonly attribute nsIPresentationControlChannel controlChannel;
};

View file

@ -1,69 +0,0 @@
/* 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 nsIDOMBlob;
interface nsIInputStream;
interface nsINetAddr;
%{C++
#define PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID \
"@mozilla.org/presentation/presentationtcpsessiontransport;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, in boolean isBinary);
};
/*
* App-to-App transport channel for the presentation session.
*/
[scriptable, uuid(670b7e1b-65be-42b6-a596-be571907fa18)]
interface nsIPresentationSessionTransport : nsISupports
{
// Should be set once the underlying session transport is built
attribute nsIPresentationSessionTransportCallback callback;
// valid for TCP session transport
readonly attribute nsINetAddr selfAddress;
/*
* Enable the notification for incoming data. |notifyData| of
* |nsIPresentationSessionTransportCallback| can start getting invoked.
* Should set callback before |enableDataNotification| is called.
*/
void enableDataNotification();
/*
* Send message to the remote endpoint.
* @param data The message to send.
*/
void send(in DOMString data);
/*
* Send the binary message to the remote endpoint.
* @param data: the message being sent out.
*/
void sendBinaryMsg(in ACString data);
/*
* Send the blob to the remote endpoint.
* @param blob: The input blob to be sent.
*/
void sendBlob(in nsIDOMBlob blob);
/*
* Close this session transport.
* @param reason The reason for closing this session transport.
*/
void close(in nsresult reason);
};

View file

@ -1,80 +0,0 @@
/* 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 nsIPresentationChannelDescription;
interface nsISocketTransport;
interface mozIDOMWindow;
interface nsIPresentationControlChannel;
interface nsIPresentationSessionTransport;
[scriptable, uuid(673f6de1-e253-41b8-9be8-b7ff161fa8dc)]
interface nsIPresentationSessionTransportBuilderListener : nsISupports
{
// Should set |transport.callback| in |onSessionTransport|.
void onSessionTransport(in nsIPresentationSessionTransport transport);
void onError(in nsresult reason);
void sendOffer(in nsIPresentationChannelDescription offer);
void sendAnswer(in nsIPresentationChannelDescription answer);
void sendIceCandidate(in DOMString candidate);
void close(in nsresult reason);
};
[scriptable, uuid(2fdbe67d-80f9-48dc-8237-5bef8fa19801)]
interface nsIPresentationSessionTransportBuilder : nsISupports
{
};
/**
* The constructor for creating a transport builder.
*/
[scriptable, uuid(706482b2-1b51-4bed-a21d-785a9cfcfac7)]
interface nsIPresentationTransportBuilderConstructor : nsISupports
{
nsIPresentationSessionTransportBuilder createTransportBuilder(in uint8_t type);
};
/**
* Builder for TCP session transport
*/
[scriptable, uuid(cde36d6e-f471-4262-a70d-f932a26b21d9)]
interface nsIPresentationTCPSessionTransportBuilder : nsIPresentationSessionTransportBuilder
{
/**
* The following creation functions will trigger |listener.onSessionTransport|
* if the session transport is successfully built, |listener.onError| if some
* error occurs during building session transport.
*/
void buildTCPSenderTransport(in nsISocketTransport aTransport,
in nsIPresentationSessionTransportBuilderListener aListener);
void buildTCPReceiverTransport(in nsIPresentationChannelDescription aDescription,
in nsIPresentationSessionTransportBuilderListener aListener);
};
/**
* Builder for WebRTC data channel session transport
*/
[scriptable, uuid(8131c4e0-3a8c-4bc1-a92a-8431473d2fe8)]
interface nsIPresentationDataChannelSessionTransportBuilder : nsIPresentationSessionTransportBuilder
{
/**
* The following creation function will trigger |listener.onSessionTransport|
* if the session transport is successfully built, |listener.onError| if some
* error occurs during creating session transport. The |notifyConnected| of
* |aControlChannel| should be called before calling
* |buildDataChannelTransport|.
*/
void buildDataChannelTransport(in uint8_t aRole,
in mozIDOMWindow aWindow,
in nsIPresentationSessionTransportBuilderListener aListener);
// Bug 1275150 - Merge TCP builder with the following APIs
void onOffer(in nsIPresentationChannelDescription offer);
void onAnswer(in nsIPresentationChannelDescription answer);
void onIceCandidate(in DOMString candidate);
void notifyDisconnected(in nsresult reason);
};

View file

@ -1,33 +0,0 @@
/* 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 nsIPresentationDevice;
interface nsIPresentationControlChannel;
%{C++
#define PRESENTATION_TERMINATE_REQUEST_TOPIC "presentation-terminate-request"
%}
/*
* The event of a device requesting for terminating a presentation session. User can
* monitor the terminate request on every device by observing "presentation-terminate-request".
*/
[scriptable, uuid(3ddbf3a4-53ee-4b70-9bbc-58ac90dce6b5)]
interface nsIPresentationTerminateRequest: nsISupports
{
// The device which requesting to terminate presentation session.
readonly attribute nsIPresentationDevice device;
// The Id for representing this session.
readonly attribute DOMString presentationId;
// The control channel for this session.
// Should only use this channel to complete session termination.
readonly attribute nsIPresentationControlChannel controlChannel;
// True if termination is initiated by receiver.
readonly attribute boolean isFromReceiver;
};

View file

@ -1,112 +0,0 @@
/* -*- 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 protocol PPresentationBuilder;
include InputStreamParams;
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
namespace mozilla {
namespace dom {
struct StartSessionRequest
{
nsString[] urls;
nsString sessionId;
nsString origin;
nsString deviceId;
uint64_t windowId;
TabId tabId;
Principal principal;
};
struct SendSessionMessageRequest
{
nsString sessionId;
uint8_t role;
nsString data;
};
struct CloseSessionRequest
{
nsString sessionId;
uint8_t role;
uint8_t closedReason;
};
struct TerminateSessionRequest
{
nsString sessionId;
uint8_t role;
};
struct ReconnectSessionRequest
{
nsString[] urls;
nsString sessionId;
uint8_t role;
};
struct BuildTransportRequest
{
nsString sessionId;
uint8_t role;
};
union PresentationIPCRequest
{
StartSessionRequest;
SendSessionMessageRequest;
CloseSessionRequest;
TerminateSessionRequest;
ReconnectSessionRequest;
BuildTransportRequest;
};
sync protocol PPresentation
{
manager PContent;
manages PPresentationBuilder;
manages PPresentationRequest;
child:
async NotifyAvailableChange(nsString[] aAvailabilityUrls,
bool aAvailable);
async NotifySessionStateChange(nsString aSessionId,
uint16_t aState,
nsresult aReason);
async NotifyMessage(nsString aSessionId, nsCString aData, bool aIsBinary);
async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId);
async NotifyCloseSessionTransport(nsString aSessionId,
uint8_t aRole,
nsresult aReason);
async PPresentationBuilder(nsString aSessionId, uint8_t aRole);
parent:
async __delete__();
async RegisterAvailabilityHandler(nsString[] aAvailabilityUrls);
async UnregisterAvailabilityHandler(nsString[] aAvailabilityUrls);
async RegisterSessionHandler(nsString aSessionId, uint8_t aRole);
async UnregisterSessionHandler(nsString aSessionId, uint8_t aRole);
async RegisterRespondingHandler(uint64_t aWindowId);
async UnregisterRespondingHandler(uint64_t aWindowId);
async PPresentationRequest(PresentationIPCRequest aRequest);
async NotifyReceiverReady(nsString aSessionId, uint64_t aWindowId, bool aIsLoading);
async NotifyTransportClosed(nsString aSessionId, uint8_t aRole, nsresult aReason);
};
} // namespace dom
} // namespace mozilla

View file

@ -1,34 +0,0 @@
/* -*- 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 {
async protocol PPresentationBuilder
{
manager PPresentation;
parent:
async SendOffer(nsString aSDP);
async SendAnswer(nsString aSDP);
async SendIceCandidate(nsString aCandidate);
async Close(nsresult aReason);
async OnSessionTransport();
async OnSessionTransportError(nsresult aReason);
child:
async OnOffer(nsString aSDP);
async OnAnswer(nsString aSDP);
async OnIceCandidate(nsString aCandidate);
async __delete__();
};
} // namespace dom
} // namespace mozilla

View file

@ -1,22 +0,0 @@
/* -*- 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:
async __delete__(nsresult result);
async NotifyRequestUrlSelected(nsString aUrl);
};
} // namespace dom
} // namespace mozilla

View file

@ -1,184 +0,0 @@
/* -*- 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 "DCPresentationChannelDescription.h"
#include "nsComponentManagerUtils.h"
#include "nsGlobalWindow.h"
#include "PresentationBuilderChild.h"
#include "PresentationIPCService.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Unused.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(PresentationBuilderChild,
nsIPresentationSessionTransportBuilderListener)
PresentationBuilderChild::PresentationBuilderChild(const nsString& aSessionId,
uint8_t aRole)
: mSessionId(aSessionId)
, mRole(aRole)
{
}
nsresult PresentationBuilderChild::Init()
{
mBuilder = do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1");
if (NS_WARN_IF(!mBuilder)) {
return NS_ERROR_NOT_AVAILABLE;
}
uint64_t windowId = 0;
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_WARN_IF(NS_FAILED(service->GetWindowIdBySessionId(
mSessionId,
mRole,
&windowId)))) {
return NS_ERROR_NOT_AVAILABLE;
}
nsPIDOMWindowInner* window = nsGlobalWindow::GetInnerWindowWithId(windowId)->AsInner();
if (NS_WARN_IF(!window)) {
return NS_ERROR_NOT_AVAILABLE;
}
return mBuilder->BuildDataChannelTransport(mRole, window, this);
}
void
PresentationBuilderChild::ActorDestroy(ActorDestroyReason aWhy)
{
mBuilder = nullptr;
mActorDestroyed = true;
}
bool
PresentationBuilderChild::RecvOnOffer(const nsString& aSDP)
{
if (NS_WARN_IF(!mBuilder)) {
return false;
}
RefPtr<DCPresentationChannelDescription> description =
new DCPresentationChannelDescription(aSDP);
if (NS_WARN_IF(NS_FAILED(mBuilder->OnOffer(description)))) {
return false;
}
return true;
}
bool
PresentationBuilderChild::RecvOnAnswer(const nsString& aSDP)
{
if (NS_WARN_IF(!mBuilder)) {
return false;
}
RefPtr<DCPresentationChannelDescription> description =
new DCPresentationChannelDescription(aSDP);
if (NS_WARN_IF(NS_FAILED(mBuilder->OnAnswer(description)))) {
return false;
}
return true;
}
bool
PresentationBuilderChild::RecvOnIceCandidate(const nsString& aCandidate)
{
if (NS_WARN_IF(mBuilder && NS_FAILED(mBuilder->OnIceCandidate(aCandidate)))) {
return false;
}
return true;
}
// nsPresentationSessionTransportBuilderListener
NS_IMETHODIMP
PresentationBuilderChild::OnSessionTransport(nsIPresentationSessionTransport* aTransport)
{
if (NS_WARN_IF(mActorDestroyed || !SendOnSessionTransport())){
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
NS_WARNING_ASSERTION(service, "no presentation service");
if (service) {
Unused << NS_WARN_IF(NS_FAILED(static_cast<PresentationIPCService*>(service.get())->
NotifySessionTransport(mSessionId, mRole, aTransport)));
}
mBuilder = nullptr;
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderChild::OnError(nsresult reason)
{
mBuilder = nullptr;
if (NS_WARN_IF(mActorDestroyed || !SendOnSessionTransportError(reason))){
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderChild::SendOffer(nsIPresentationChannelDescription* aOffer)
{
nsAutoString SDP;
nsresult rv = aOffer->GetDataChannelSDP(SDP);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(mActorDestroyed || !SendSendOffer(SDP))){
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderChild::SendAnswer(nsIPresentationChannelDescription* aAnswer)
{
nsAutoString SDP;
nsresult rv = aAnswer->GetDataChannelSDP(SDP);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(mActorDestroyed || !SendSendAnswer(SDP))){
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderChild::SendIceCandidate(const nsAString& candidate)
{
if (NS_WARN_IF(mActorDestroyed || !SendSendIceCandidate(nsString(candidate)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderChild::Close(nsresult reason)
{
if (NS_WARN_IF(mActorDestroyed || !SendClose(reason))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,48 +0,0 @@
/* -*- 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_PresentationBuilderChild_h
#define mozilla_dom_PresentationBuilderChild_h
#include "mozilla/dom/PPresentationBuilderChild.h"
#include "nsIPresentationSessionTransportBuilder.h"
namespace mozilla {
namespace dom {
class PresentationBuilderChild final: public PPresentationBuilderChild
, public nsIPresentationSessionTransportBuilderListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER
explicit PresentationBuilderChild(const nsString& aSessionId,
uint8_t aRole);
nsresult Init();
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual bool RecvOnOffer(const nsString& aSDP) override;
virtual bool RecvOnAnswer(const nsString& aSDP) override;
virtual bool RecvOnIceCandidate(const nsString& aCandidate) override;
private:
virtual ~PresentationBuilderChild() = default;
nsString mSessionId;
uint8_t mRole;
bool mActorDestroyed = false;
nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> mBuilder;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationBuilderChild_h

View file

@ -1,267 +0,0 @@
/* -*- 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 "DCPresentationChannelDescription.h"
#include "PresentationBuilderParent.h"
#include "PresentationSessionInfo.h"
namespace mozilla {
namespace dom {
namespace {
class PresentationSessionTransportIPC final :
public nsIPresentationSessionTransport
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT
PresentationSessionTransportIPC(PresentationParent* aParent,
const nsAString& aSessionId,
uint8_t aRole)
: mParent(aParent)
, mSessionId(aSessionId)
, mRole(aRole)
{
MOZ_ASSERT(mParent);
}
private:
virtual ~PresentationSessionTransportIPC() = default;
RefPtr<PresentationParent> mParent;
nsString mSessionId;
uint8_t mRole;
};
NS_IMPL_ISUPPORTS(PresentationSessionTransportIPC,
nsIPresentationSessionTransport)
NS_IMETHODIMP
PresentationSessionTransportIPC::GetCallback(
nsIPresentationSessionTransportCallback** aCallback)
{
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransportIPC::SetCallback(
nsIPresentationSessionTransportCallback* aCallback)
{
if (aCallback) {
aCallback->NotifyTransportReady();
}
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransportIPC::GetSelfAddress(nsINetAddr** aSelfAddress)
{
MOZ_ASSERT(false, "Not expected.");
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
PresentationSessionTransportIPC::EnableDataNotification()
{
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransportIPC::Send(const nsAString& aData)
{
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransportIPC::SendBinaryMsg(const nsACString& aData)
{
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransportIPC::SendBlob(nsIDOMBlob* aBlob)
{
return NS_OK;
}
NS_IMETHODIMP
PresentationSessionTransportIPC::Close(nsresult aReason)
{
if (NS_WARN_IF(!mParent->SendNotifyCloseSessionTransport(mSessionId,
mRole,
aReason))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
} // anonymous namespace
NS_IMPL_ISUPPORTS(PresentationBuilderParent,
nsIPresentationSessionTransportBuilder,
nsIPresentationDataChannelSessionTransportBuilder)
PresentationBuilderParent::PresentationBuilderParent(PresentationParent* aParent)
: mParent(aParent)
{
MOZ_COUNT_CTOR(PresentationBuilderParent);
}
PresentationBuilderParent::~PresentationBuilderParent()
{
MOZ_COUNT_DTOR(PresentationBuilderParent);
if (mNeedDestroyActor) {
Unused << NS_WARN_IF(!Send__delete__(this));
}
}
NS_IMETHODIMP
PresentationBuilderParent::BuildDataChannelTransport(
uint8_t aRole,
mozIDOMWindow* aWindow, /* unused */
nsIPresentationSessionTransportBuilderListener* aListener)
{
mBuilderListener = aListener;
RefPtr<PresentationSessionInfo> info = static_cast<PresentationSessionInfo*>(aListener);
nsAutoString sessionId(info->GetSessionId());
if (NS_WARN_IF(!mParent->SendPPresentationBuilderConstructor(this,
sessionId,
aRole))) {
return NS_ERROR_FAILURE;
}
mIPCSessionTransport = new PresentationSessionTransportIPC(mParent,
sessionId,
aRole);
mNeedDestroyActor = true;
mParent = nullptr;
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderParent::OnIceCandidate(const nsAString& aCandidate)
{
if (NS_WARN_IF(!SendOnIceCandidate(nsString(aCandidate)))){
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderParent::OnOffer(nsIPresentationChannelDescription* aDescription)
{
nsAutoString SDP;
nsresult rv = aDescription->GetDataChannelSDP(SDP);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!SendOnOffer(SDP))){
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderParent::OnAnswer(nsIPresentationChannelDescription* aDescription)
{
nsAutoString SDP;
nsresult rv = aDescription->GetDataChannelSDP(SDP);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!SendOnAnswer(SDP))){
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationBuilderParent::NotifyDisconnected(nsresult aReason)
{
return NS_OK;
}
void
PresentationBuilderParent::ActorDestroy(ActorDestroyReason aWhy)
{
mNeedDestroyActor = false;
mParent = nullptr;
mBuilderListener = nullptr;
}
bool
PresentationBuilderParent::RecvSendOffer(const nsString& aSDP)
{
RefPtr<DCPresentationChannelDescription> description =
new DCPresentationChannelDescription(aSDP);
if (NS_WARN_IF(!mBuilderListener ||
NS_FAILED(mBuilderListener->SendOffer(description)))) {
return false;
}
return true;
}
bool
PresentationBuilderParent::RecvSendAnswer(const nsString& aSDP)
{
RefPtr<DCPresentationChannelDescription> description =
new DCPresentationChannelDescription(aSDP);
if (NS_WARN_IF(!mBuilderListener ||
NS_FAILED(mBuilderListener->SendAnswer(description)))) {
return false;
}
return true;
}
bool
PresentationBuilderParent::RecvSendIceCandidate(const nsString& aCandidate)
{
if (NS_WARN_IF(!mBuilderListener ||
NS_FAILED(mBuilderListener->SendIceCandidate(aCandidate)))) {
return false;
}
return true;
}
bool
PresentationBuilderParent::RecvClose(const nsresult& aReason)
{
if (NS_WARN_IF(!mBuilderListener ||
NS_FAILED(mBuilderListener->Close(aReason)))) {
return false;
}
return true;
}
// Delegate to nsIPresentationSessionTransportBuilderListener
bool
PresentationBuilderParent::RecvOnSessionTransport()
{
RefPtr<PresentationBuilderParent> kungFuDeathGrip = this;
Unused <<
NS_WARN_IF(!mBuilderListener ||
NS_FAILED(mBuilderListener->OnSessionTransport(mIPCSessionTransport)));
return true;
}
bool
PresentationBuilderParent::RecvOnSessionTransportError(const nsresult& aReason)
{
if (NS_WARN_IF(!mBuilderListener ||
NS_FAILED(mBuilderListener->OnError(aReason)))) {
return false;
}
return true;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,52 +0,0 @@
/* -*- 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_PresentationBuilderParent_h__
#define mozilla_dom_PresentationBuilderParent_h__
#include "mozilla/dom/PPresentationBuilderParent.h"
#include "PresentationParent.h"
#include "nsIPresentationSessionTransportBuilder.h"
namespace mozilla {
namespace dom {
class PresentationBuilderParent final: public PPresentationBuilderParent
, public nsIPresentationDataChannelSessionTransportBuilder
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDER
NS_DECL_NSIPRESENTATIONDATACHANNELSESSIONTRANSPORTBUILDER
explicit PresentationBuilderParent(PresentationParent* aParent);
virtual bool RecvSendOffer(const nsString& aSDP) override;
virtual bool RecvSendAnswer(const nsString& aSDP) override;
virtual bool RecvSendIceCandidate(const nsString& aCandidate) override;
virtual bool RecvClose(const nsresult& aReason) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual bool RecvOnSessionTransport() override;
virtual bool RecvOnSessionTransportError(const nsresult& aReason) override;
private:
virtual ~PresentationBuilderParent();
bool mNeedDestroyActor = false;
RefPtr<PresentationParent> mParent;
nsCOMPtr<nsIPresentationSessionTransportBuilderListener> mBuilderListener;
nsCOMPtr<nsIPresentationSessionTransport> mIPCSessionTransport;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationBuilderParent_h__

View file

@ -1,198 +0,0 @@
/* -*- 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 "DCPresentationChannelDescription.h"
#include "mozilla/StaticPtr.h"
#include "PresentationBuilderChild.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 PresentationIPCRequest& aRequest)
{
NS_NOTREACHED("We should never be manually allocating PPresentationRequestChild actors");
return nullptr;
}
bool
PresentationChild::DeallocPPresentationRequestChild(PPresentationRequestChild* aActor)
{
delete aActor;
return true;
}
bool PresentationChild::RecvPPresentationBuilderConstructor(
PPresentationBuilderChild* aActor,
const nsString& aSessionId,
const uint8_t& aRole)
{
// Child will build the session transport
PresentationBuilderChild* actor = static_cast<PresentationBuilderChild*>(aActor);
return NS_WARN_IF(NS_FAILED(actor->Init())) ? false : true;
}
PPresentationBuilderChild*
PresentationChild::AllocPPresentationBuilderChild(const nsString& aSessionId,
const uint8_t& aRole)
{
RefPtr<PresentationBuilderChild> actor
= new PresentationBuilderChild(aSessionId, aRole);
return actor.forget().take();
}
bool
PresentationChild::DeallocPPresentationBuilderChild(PPresentationBuilderChild* aActor)
{
RefPtr<PresentationBuilderChild> actor =
dont_AddRef(static_cast<PresentationBuilderChild*>(aActor));
return true;
}
bool
PresentationChild::RecvNotifyAvailableChange(
nsTArray<nsString>&& aAvailabilityUrls,
const bool& aAvailable)
{
if (mService) {
Unused <<
NS_WARN_IF(NS_FAILED(mService->NotifyAvailableChange(aAvailabilityUrls,
aAvailable)));
}
return true;
}
bool
PresentationChild::RecvNotifySessionStateChange(const nsString& aSessionId,
const uint16_t& aState,
const nsresult& aReason)
{
if (mService) {
Unused << NS_WARN_IF(NS_FAILED(mService->NotifySessionStateChange(aSessionId,
aState,
aReason)));
}
return true;
}
bool
PresentationChild::RecvNotifyMessage(const nsString& aSessionId,
const nsCString& aData,
const bool& aIsBinary)
{
if (mService) {
Unused << NS_WARN_IF(NS_FAILED(mService->NotifyMessage(aSessionId,
aData,
aIsBinary)));
}
return true;
}
bool
PresentationChild::RecvNotifySessionConnect(const uint64_t& aWindowId,
const nsString& aSessionId)
{
if (mService) {
Unused << NS_WARN_IF(NS_FAILED(mService->NotifySessionConnect(aWindowId, aSessionId)));
}
return true;
}
bool
PresentationChild::RecvNotifyCloseSessionTransport(const nsString& aSessionId,
const uint8_t& aRole,
const nsresult& aReason)
{
if (mService) {
Unused << NS_WARN_IF(NS_FAILED(
mService->CloseContentSessionTransport(aSessionId, aRole, aReason)));
}
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_FAILED(aResult)) {
Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyError(aResult)));
}
}
return true;
}
bool
PresentationRequestChild::RecvNotifyRequestUrlSelected(const nsString& aUrl)
{
Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess(aUrl)));
return true;
}

View file

@ -1,101 +0,0 @@
/* -*- 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/PPresentationBuilderChild.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 PresentationIPCRequest& aRequest) override;
virtual bool
DeallocPPresentationRequestChild(PPresentationRequestChild* aActor) override;
bool RecvPPresentationBuilderConstructor(PPresentationBuilderChild* aActor,
const nsString& aSessionId,
const uint8_t& aRole) override;
virtual PPresentationBuilderChild*
AllocPPresentationBuilderChild(const nsString& aSessionId, const uint8_t& aRole) override;
virtual bool
DeallocPPresentationBuilderChild(PPresentationBuilderChild* aActor) override;
virtual bool
RecvNotifyAvailableChange(nsTArray<nsString>&& aAvailabilityUrls,
const bool& aAvailable) override;
virtual bool
RecvNotifySessionStateChange(const nsString& aSessionId,
const uint16_t& aState,
const nsresult& aReason) override;
virtual bool
RecvNotifyMessage(const nsString& aSessionId,
const nsCString& aData,
const bool& aIsBinary) override;
virtual bool
RecvNotifySessionConnect(const uint64_t& aWindowId,
const nsString& aSessionId) override;
virtual bool
RecvNotifyCloseSessionTransport(const nsString& aSessionId,
const uint8_t& aRole,
const nsresult& aReason) override;
private:
virtual ~PresentationChild();
bool mActorDestroyed = false;
RefPtr<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;
virtual bool
RecvNotifyRequestUrlSelected(const nsString& aUrl) override;
private:
virtual ~PresentationRequestChild();
bool mActorDestroyed = false;
nsCOMPtr<nsIPresentationServiceCallback> mCallback;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationChild_h

View file

@ -1,109 +0,0 @@
/* -*- 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 "nsServiceManagerUtils.h"
#include "PresentationContentSessionInfo.h"
#include "PresentationIPCService.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(PresentationContentSessionInfo,
nsIPresentationSessionTransportCallback);
nsresult
PresentationContentSessionInfo::Init() {
if (NS_WARN_IF(NS_FAILED(mTransport->SetCallback(this)))) {
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_WARN_IF(NS_FAILED(mTransport->EnableDataNotification()))) {
return NS_ERROR_NOT_AVAILABLE;
}
return NS_OK;
}
nsresult
PresentationContentSessionInfo::Send(const nsAString& aData)
{
if (!mTransport) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->Send(aData);
}
nsresult
PresentationContentSessionInfo::SendBinaryMsg(const nsACString& aData)
{
if (NS_WARN_IF(!mTransport)) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->SendBinaryMsg(aData);
}
nsresult
PresentationContentSessionInfo::SendBlob(nsIDOMBlob* aBlob)
{
if (NS_WARN_IF(!mTransport)) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->SendBlob(aBlob);
}
nsresult
PresentationContentSessionInfo::Close(nsresult aReason)
{
if (!mTransport) {
return NS_ERROR_NOT_AVAILABLE;
}
return mTransport->Close(aReason);
}
// nsIPresentationSessionTransportCallback
NS_IMETHODIMP
PresentationContentSessionInfo::NotifyTransportReady()
{
// do nothing since |onSessionTransport| implies this
return NS_OK;
}
NS_IMETHODIMP
PresentationContentSessionInfo::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 = nullptr;
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
return static_cast<PresentationIPCService*>(service.get())->
NotifyTransportClosed(mSessionId, mRole, aReason);
}
NS_IMETHODIMP
PresentationContentSessionInfo::NotifyData(const nsACString& aData,
bool aIsBinary)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPresentationService> service =
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
if (NS_WARN_IF(!service)) {
return NS_ERROR_NOT_AVAILABLE;
}
return static_cast<PresentationIPCService*>(service.get())->
NotifyMessage(mSessionId, aData, aIsBinary);
}
} // namespace dom
} // namespace mozilla

View file

@ -1,62 +0,0 @@
/* -*- 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_PresentationContentSessionInfo_h
#define mozilla_dom_PresentationContentSessionInfo_h
#include "nsCOMPtr.h"
#include "nsIPresentationSessionTransport.h"
namespace mozilla {
namespace dom {
/**
* PresentationContentSessionInfo manages nsIPresentationSessionTransport and
* delegates the callbacks to PresentationIPCService. Only lives in content
* process.
*/
class PresentationContentSessionInfo final : public nsIPresentationSessionTransportCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK
PresentationContentSessionInfo(const nsAString& aSessionId,
uint8_t aRole,
nsIPresentationSessionTransport* aTransport)
: mSessionId(aSessionId)
, mRole(aRole)
, mTransport(aTransport)
{
MOZ_ASSERT(XRE_IsContentProcess());
MOZ_ASSERT(!aSessionId.IsEmpty());
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
aRole == nsIPresentationService::ROLE_RECEIVER);
MOZ_ASSERT(aTransport);
}
nsresult Init();
nsresult Send(const nsAString& aData);
nsresult SendBinaryMsg(const nsACString& aData);
nsresult SendBlob(nsIDOMBlob* aBlob);
nsresult Close(nsresult aReason);
private:
virtual ~PresentationContentSessionInfo() {}
nsString mSessionId;
uint8_t mRole;
nsCOMPtr<nsIPresentationSessionTransport> mTransport;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationContentSessionInfo_h

View file

@ -1,538 +0,0 @@
/* -*- 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/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)) {
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 =
nsGlobalWindow::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([windowId]() -> void {
PRES_DEBUG("Attempt to close window[%d]\n", windowId);
if (auto* window = nsGlobalWindow::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);
}

View file

@ -1,75 +0,0 @@
/* -*- 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 "mozilla/dom/PresentationServiceBase.h"
#include "nsIPresentationListener.h"
#include "nsIPresentationSessionTransport.h"
#include "nsIPresentationService.h"
class nsIDocShell;
namespace mozilla {
namespace dom {
class PresentationIPCRequest;
class PresentationContentSessionInfo;
class PresentationResponderLoadingCallback;
class PresentationIPCService final
: public nsIPresentationAvailabilityListener
, public nsIPresentationService
, public PresentationServiceBase<PresentationContentSessionInfo>
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER
NS_DECL_NSIPRESENTATIONSERVICE
PresentationIPCService();
nsresult NotifySessionStateChange(const nsAString& aSessionId,
uint16_t aState,
nsresult aReason);
nsresult NotifyMessage(const nsAString& aSessionId,
const nsACString& aData,
const bool& aIsBinary);
nsresult NotifySessionConnect(uint64_t aWindowId,
const nsAString& aSessionId);
void NotifyPresentationChildDestroyed();
nsresult MonitorResponderLoading(const nsAString& aSessionId,
nsIDocShell* aDocShell);
nsresult NotifySessionTransport(const nsString& aSessionId,
const uint8_t& aRole,
nsIPresentationSessionTransport* transport);
nsresult CloseContentSessionTransport(const nsString& aSessionId,
uint8_t aRole,
nsresult aReason);
private:
virtual ~PresentationIPCService();
nsresult SendRequest(nsIPresentationServiceCallback* aCallback,
const PresentationIPCRequest& aRequest);
nsRefPtrHashtable<nsStringHashKey,
nsIPresentationSessionListener> mSessionListeners;
nsRefPtrHashtable<nsUint64HashKey,
nsIPresentationRespondingListener> mRespondingListeners;
RefPtr<PresentationResponderLoadingCallback> mCallback;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationIPCService_h

View file

@ -1,553 +0,0 @@
/* -*- 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 "DCPresentationChannelDescription.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/Unused.h"
#include "nsIPresentationDeviceManager.h"
#include "nsIPresentationSessionTransport.h"
#include "nsIPresentationSessionTransportBuilder.h"
#include "nsServiceManagerUtils.h"
#include "PresentationBuilderParent.h"
#include "PresentationParent.h"
#include "PresentationService.h"
#include "PresentationSessionInfo.h"
namespace mozilla {
namespace dom {
namespace {
class PresentationTransportBuilderConstructorIPC final :
public nsIPresentationTransportBuilderConstructor
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
explicit PresentationTransportBuilderConstructorIPC(PresentationParent* aParent)
: mParent(aParent)
{
}
private:
virtual ~PresentationTransportBuilderConstructorIPC() = default;
RefPtr<PresentationParent> mParent;
};
NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC,
nsIPresentationTransportBuilderConstructor)
NS_IMETHODIMP
PresentationTransportBuilderConstructorIPC::CreateTransportBuilder(
uint8_t aType,
nsIPresentationSessionTransportBuilder** aRetval)
{
if (NS_WARN_IF(!aRetval)) {
return NS_ERROR_INVALID_ARG;
}
*aRetval = nullptr;
if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
return NS_ERROR_INVALID_ARG;
}
if (XRE_IsContentProcess()) {
MOZ_ASSERT(false,
"CreateTransportBuilder can only be invoked in parent process.");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
builder = do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
} else {
builder = new PresentationBuilderParent(mParent);
}
if (NS_WARN_IF(!builder)) {
return NS_ERROR_DOM_OPERATION_ERR;
}
builder.forget(aRetval);
return NS_OK;
}
} // anonymous namespace
/*
* Implementation of PresentationParent
*/
NS_IMPL_ISUPPORTS(PresentationParent,
nsIPresentationAvailabilityListener,
nsIPresentationSessionListener,
nsIPresentationRespondingListener)
PresentationParent::PresentationParent()
{
MOZ_COUNT_CTOR(PresentationParent);
}
/* virtual */ PresentationParent::~PresentationParent()
{
MOZ_COUNT_DTOR(PresentationParent);
}
bool
PresentationParent::Init(ContentParentId aContentParentId)
{
MOZ_ASSERT(!mService);
mService = do_GetService(PRESENTATION_SERVICE_CONTRACTID);
mChildId = aContentParentId;
return NS_WARN_IF(!mService) ? false : true;
}
void
PresentationParent::ActorDestroy(ActorDestroyReason aWhy)
{
mActorDestroyed = true;
for (uint32_t i = 0; i < mSessionIdsAtController.Length(); i++) {
Unused << NS_WARN_IF(NS_FAILED(mService->
UnregisterSessionListener(mSessionIdsAtController[i],
nsIPresentationService::ROLE_CONTROLLER)));
}
mSessionIdsAtController.Clear();
for (uint32_t i = 0; i < mSessionIdsAtReceiver.Length(); i++) {
Unused << NS_WARN_IF(NS_FAILED(mService->
UnregisterSessionListener(mSessionIdsAtReceiver[i], nsIPresentationService::ROLE_RECEIVER)));
}
mSessionIdsAtReceiver.Clear();
for (uint32_t i = 0; i < mWindowIds.Length(); i++) {
Unused << NS_WARN_IF(NS_FAILED(mService->
UnregisterRespondingListener(mWindowIds[i])));
}
mWindowIds.Clear();
if (!mContentAvailabilityUrls.IsEmpty()) {
mService->UnregisterAvailabilityListener(mContentAvailabilityUrls, this);
}
mService = nullptr;
}
bool
PresentationParent::RecvPPresentationRequestConstructor(
PPresentationRequestParent* aActor,
const PresentationIPCRequest& aRequest)
{
PresentationRequestParent* actor = static_cast<PresentationRequestParent*>(aActor);
nsresult rv = NS_ERROR_FAILURE;
switch (aRequest.type()) {
case PresentationIPCRequest::TStartSessionRequest:
rv = actor->DoRequest(aRequest.get_StartSessionRequest());
break;
case PresentationIPCRequest::TSendSessionMessageRequest:
rv = actor->DoRequest(aRequest.get_SendSessionMessageRequest());
break;
case PresentationIPCRequest::TCloseSessionRequest:
rv = actor->DoRequest(aRequest.get_CloseSessionRequest());
break;
case PresentationIPCRequest::TTerminateSessionRequest:
rv = actor->DoRequest(aRequest.get_TerminateSessionRequest());
break;
case PresentationIPCRequest::TReconnectSessionRequest:
rv = actor->DoRequest(aRequest.get_ReconnectSessionRequest());
break;
case PresentationIPCRequest::TBuildTransportRequest:
rv = actor->DoRequest(aRequest.get_BuildTransportRequest());
break;
default:
MOZ_CRASH("Unknown PresentationIPCRequest type");
}
return NS_WARN_IF(NS_FAILED(rv)) ? false : true;
}
PPresentationRequestParent*
PresentationParent::AllocPPresentationRequestParent(
const PresentationIPCRequest& aRequest)
{
MOZ_ASSERT(mService);
RefPtr<PresentationRequestParent> actor = new PresentationRequestParent(mService, mChildId);
return actor.forget().take();
}
bool
PresentationParent::DeallocPPresentationRequestParent(
PPresentationRequestParent* aActor)
{
RefPtr<PresentationRequestParent> actor =
dont_AddRef(static_cast<PresentationRequestParent*>(aActor));
return true;
}
PPresentationBuilderParent*
PresentationParent::AllocPPresentationBuilderParent(const nsString& aSessionId,
const uint8_t& aRole)
{
NS_NOTREACHED("We should never be manually allocating AllocPPresentationBuilderParent actors");
return nullptr;
}
bool
PresentationParent::DeallocPPresentationBuilderParent(
PPresentationBuilderParent* aActor)
{
return true;
}
bool
PresentationParent::Recv__delete__()
{
return true;
}
bool
PresentationParent::RecvRegisterAvailabilityHandler(
nsTArray<nsString>&& aAvailabilityUrls)
{
MOZ_ASSERT(mService);
Unused << NS_WARN_IF(NS_FAILED(mService->RegisterAvailabilityListener(
aAvailabilityUrls,
this)));
mContentAvailabilityUrls.AppendElements(aAvailabilityUrls);
return true;
}
bool
PresentationParent::RecvUnregisterAvailabilityHandler(
nsTArray<nsString>&& aAvailabilityUrls)
{
MOZ_ASSERT(mService);
Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterAvailabilityListener(
aAvailabilityUrls,
this)));
for (const auto& url : aAvailabilityUrls) {
mContentAvailabilityUrls.RemoveElement(url);
}
return true;
}
/* virtual */ bool
PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId,
const uint8_t& aRole)
{
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, aRole, OtherPid()))) {
return true;
}
if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
mSessionIdsAtController.AppendElement(aSessionId);
} else {
mSessionIdsAtReceiver.AppendElement(aSessionId);
}
Unused << NS_WARN_IF(NS_FAILED(mService->RegisterSessionListener(aSessionId, aRole, this)));
return true;
}
/* virtual */ bool
PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId,
const uint8_t& aRole)
{
MOZ_ASSERT(mService);
if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
mSessionIdsAtController.RemoveElement(aSessionId);
} else {
mSessionIdsAtReceiver.RemoveElement(aSessionId);
}
Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(aSessionId, aRole)));
return true;
}
/* virtual */ bool
PresentationParent::RecvRegisterRespondingHandler(const uint64_t& aWindowId)
{
MOZ_ASSERT(mService);
mWindowIds.AppendElement(aWindowId);
Unused << NS_WARN_IF(NS_FAILED(mService->RegisterRespondingListener(aWindowId, this)));
return true;
}
/* virtual */ bool
PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId)
{
MOZ_ASSERT(mService);
mWindowIds.RemoveElement(aWindowId);
Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterRespondingListener(aWindowId)));
return true;
}
NS_IMETHODIMP
PresentationParent::NotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls,
bool aAvailable)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifyAvailableChange(aAvailabilityUrls,
aAvailable))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationParent::NotifyStateChange(const nsAString& aSessionId,
uint16_t aState,
nsresult aReason)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifySessionStateChange(nsString(aSessionId),
aState,
aReason))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationParent::NotifyMessage(const nsAString& aSessionId,
const nsACString& aData,
bool aIsBinary)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifyMessage(nsString(aSessionId),
nsCString(aData),
aIsBinary))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
PresentationParent::NotifySessionConnect(uint64_t aWindowId,
const nsAString& aSessionId)
{
if (NS_WARN_IF(mActorDestroyed ||
!SendNotifySessionConnect(aWindowId, nsString(aSessionId)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
PresentationParent::RecvNotifyReceiverReady(const nsString& aSessionId,
const uint64_t& aWindowId,
const bool& aIsLoading)
{
MOZ_ASSERT(mService);
nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
new PresentationTransportBuilderConstructorIPC(this);
Unused << NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId,
aWindowId,
aIsLoading,
constructor)));
return true;
}
bool
PresentationParent::RecvNotifyTransportClosed(const nsString& aSessionId,
const uint8_t& aRole,
const nsresult& aReason)
{
MOZ_ASSERT(mService);
Unused << NS_WARN_IF(NS_FAILED(mService->NotifyTransportClosed(aSessionId, aRole, aReason)));
return true;
}
/*
* Implementation of PresentationRequestParent
*/
NS_IMPL_ISUPPORTS(PresentationRequestParent, nsIPresentationServiceCallback)
PresentationRequestParent::PresentationRequestParent(nsIPresentationService* aService,
ContentParentId aContentParentId)
: mService(aService)
, mChildId(aContentParentId)
{
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);
mSessionId = aRequest.sessionId();
nsCOMPtr<nsIDOMEventTarget> eventTarget;
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
RefPtr<TabParent> tp =
cpm->GetTopLevelTabParentByProcessAndTabId(mChildId, aRequest.tabId());
if (tp) {
eventTarget = do_QueryInterface(tp->GetOwnerElement());
}
RefPtr<PresentationParent> parent = static_cast<PresentationParent*>(Manager());
nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
new PresentationTransportBuilderConstructorIPC(parent);
return mService->StartSession(aRequest.urls(), aRequest.sessionId(),
aRequest.origin(), aRequest.deviceId(),
aRequest.windowId(), eventTarget,
aRequest.principal(), this, constructor);
}
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(), aRequest.role(), OtherPid()))) {
return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
}
nsresult rv = mService->SendSessionMessage(aRequest.sessionId(),
aRequest.role(),
aRequest.data());
if (NS_WARN_IF(NS_FAILED(rv))) {
return SendResponse(rv);
}
return SendResponse(NS_OK);
}
nsresult
PresentationRequestParent::DoRequest(const CloseSessionRequest& 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(), aRequest.role(), OtherPid()))) {
return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
}
nsresult rv = mService->CloseSession(aRequest.sessionId(),
aRequest.role(),
aRequest.closedReason());
if (NS_WARN_IF(NS_FAILED(rv))) {
return SendResponse(rv);
}
return SendResponse(NS_OK);
}
nsresult
PresentationRequestParent::DoRequest(const TerminateSessionRequest& 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(), aRequest.role(), OtherPid()))) {
return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
}
nsresult rv = mService->TerminateSession(aRequest.sessionId(), aRequest.role());
if (NS_WARN_IF(NS_FAILED(rv))) {
return SendResponse(rv);
}
return SendResponse(NS_OK);
}
nsresult
PresentationRequestParent::DoRequest(const ReconnectSessionRequest& 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(), aRequest.role(), OtherPid()))) {
// NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec.
// https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation
return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR);
}
mSessionId = aRequest.sessionId();
return mService->ReconnectSession(aRequest.urls(),
aRequest.sessionId(),
aRequest.role(),
this);
}
nsresult
PresentationRequestParent::DoRequest(const BuildTransportRequest& 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(), aRequest.role(), OtherPid()))) {
return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
}
nsresult rv = mService->BuildTransport(aRequest.sessionId(), aRequest.role());
if (NS_WARN_IF(NS_FAILED(rv))) {
return SendResponse(rv);
}
return SendResponse(NS_OK);
}
NS_IMETHODIMP
PresentationRequestParent::NotifySuccess(const nsAString& aUrl)
{
Unused << SendNotifyRequestUrlSelected(nsString(aUrl));
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;
}
} // namespace dom
} // namespace mozilla

View file

@ -1,137 +0,0 @@
/* -*- 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/ipc/IdType.h"
#include "mozilla/dom/PPresentationBuilderParent.h"
#include "mozilla/dom/PPresentationParent.h"
#include "mozilla/dom/PPresentationRequestParent.h"
#include "nsIPresentationListener.h"
#include "nsIPresentationService.h"
#include "nsIPresentationSessionTransportBuilder.h"
namespace mozilla {
namespace dom {
class PresentationParent final : public PPresentationParent
, public nsIPresentationAvailabilityListener
, public nsIPresentationSessionListener
, public nsIPresentationRespondingListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER
NS_DECL_NSIPRESENTATIONSESSIONLISTENER
NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER
PresentationParent();
bool Init(ContentParentId aContentParentId);
bool RegisterTransportBuilder(const nsString& aSessionId, const uint8_t& aRole);
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual bool
RecvPPresentationRequestConstructor(PPresentationRequestParent* aActor,
const PresentationIPCRequest& aRequest) override;
virtual PPresentationRequestParent*
AllocPPresentationRequestParent(const PresentationIPCRequest& aRequest) override;
virtual bool
DeallocPPresentationRequestParent(PPresentationRequestParent* aActor) override;
virtual PPresentationBuilderParent*
AllocPPresentationBuilderParent(const nsString& aSessionId,
const uint8_t& aRole) override;
virtual bool
DeallocPPresentationBuilderParent(
PPresentationBuilderParent* aActor) override;
virtual bool Recv__delete__() override;
virtual bool RecvRegisterAvailabilityHandler(
nsTArray<nsString>&& aAvailabilityUrls) override;
virtual bool RecvUnregisterAvailabilityHandler(
nsTArray<nsString>&& aAvailabilityUrls) override;
virtual bool RecvRegisterSessionHandler(const nsString& aSessionId,
const uint8_t& aRole) override;
virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId,
const uint8_t& aRole) override;
virtual bool RecvRegisterRespondingHandler(const uint64_t& aWindowId) override;
virtual bool RecvUnregisterRespondingHandler(const uint64_t& aWindowId) override;
virtual bool RecvNotifyReceiverReady(const nsString& aSessionId,
const uint64_t& aWindowId,
const bool& aIsLoading) override;
virtual bool RecvNotifyTransportClosed(const nsString& aSessionId,
const uint8_t& aRole,
const nsresult& aReason) override;
private:
virtual ~PresentationParent();
bool mActorDestroyed = false;
nsCOMPtr<nsIPresentationService> mService;
nsTArray<nsString> mSessionIdsAtController;
nsTArray<nsString> mSessionIdsAtReceiver;
nsTArray<uint64_t> mWindowIds;
ContentParentId mChildId;
nsTArray<nsString> mContentAvailabilityUrls;
};
class PresentationRequestParent final : public PPresentationRequestParent
, public nsIPresentationServiceCallback
{
friend class PresentationParent;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONSERVICECALLBACK
explicit PresentationRequestParent(nsIPresentationService* aService,
ContentParentId aContentParentId);
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 CloseSessionRequest& aRequest);
nsresult DoRequest(const TerminateSessionRequest& aRequest);
nsresult DoRequest(const ReconnectSessionRequest& aRequest);
nsresult DoRequest(const BuildTransportRequest& aRequest);
bool mActorDestroyed = false;
bool mNeedRegisterBuilder = false;
nsString mSessionId;
nsCOMPtr<nsIPresentationService> mService;
ContentParentId mChildId;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PresentationParent_h__

View file

@ -1,89 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DIRS += ['interfaces', 'provider']
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
EXPORTS.mozilla.dom += [
'DCPresentationChannelDescription.h',
'ipc/PresentationBuilderChild.h',
'ipc/PresentationBuilderParent.h',
'ipc/PresentationChild.h',
'ipc/PresentationIPCService.h',
'ipc/PresentationParent.h',
'Presentation.h',
'PresentationAvailability.h',
'PresentationCallbacks.h',
'PresentationConnection.h',
'PresentationConnectionList.h',
'PresentationDeviceManager.h',
'PresentationReceiver.h',
'PresentationRequest.h',
'PresentationService.h',
'PresentationServiceBase.h',
'PresentationSessionInfo.h',
'PresentationTCPSessionTransport.h',
]
UNIFIED_SOURCES += [
'AvailabilityCollection.cpp',
'ControllerConnectionCollection.cpp',
'DCPresentationChannelDescription.cpp',
'ipc/PresentationBuilderChild.cpp',
'ipc/PresentationBuilderParent.cpp',
'ipc/PresentationChild.cpp',
'ipc/PresentationContentSessionInfo.cpp',
'ipc/PresentationIPCService.cpp',
'ipc/PresentationParent.cpp',
'Presentation.cpp',
'PresentationAvailability.cpp',
'PresentationCallbacks.cpp',
'PresentationConnection.cpp',
'PresentationConnectionList.cpp',
'PresentationDeviceManager.cpp',
'PresentationReceiver.cpp',
'PresentationRequest.cpp',
'PresentationService.cpp',
'PresentationSessionInfo.cpp',
'PresentationSessionRequest.cpp',
'PresentationTCPSessionTransport.cpp',
'PresentationTerminateRequest.cpp',
'PresentationTransportBuilderConstructor.cpp'
]
EXTRA_COMPONENTS += [
'PresentationDataChannelSessionTransport.js',
'PresentationDataChannelSessionTransport.manifest',
'PresentationDeviceInfoManager.js',
'PresentationDeviceInfoManager.manifest',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
EXTRA_COMPONENTS += [
'PresentationNetworkHelper.js',
'PresentationNetworkHelper.manifest',
]
EXTRA_JS_MODULES += [
'PresentationDeviceInfoManager.jsm',
]
IPDL_SOURCES += [
'ipc/PPresentation.ipdl',
'ipc/PPresentationBuilder.ipdl',
'ipc/PPresentationRequest.ipdl'
]
LOCAL_INCLUDES += [
'../base'
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View file

@ -1,461 +0,0 @@
/* -*- 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/. */
/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
/* globals Components, dump */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
// globals XPCOMUtils
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// globals Services
Cu.import("resource://gre/modules/Services.jsm");
// globals Messaging
Cu.import("resource://gre/modules/Messaging.jsm");
function log(str) {
// dump("-*- AndroidCastDeviceProvider -*-: " + str + "\n");
}
// Helper function: transfer nsIPresentationChannelDescription to json
function descriptionToString(aDescription) {
let json = {};
json.type = aDescription.type;
switch(aDescription.type) {
case Ci.nsIPresentationChannelDescription.TYPE_TCP:
let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray);
json.tcpAddress = [];
for (let idx = 0; idx < addresses.length; idx++) {
let address = addresses.queryElementAt(idx, Ci.nsISupportsCString);
json.tcpAddress.push(address.data);
}
json.tcpPort = aDescription.tcpPort;
break;
case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
json.dataChannelSDP = aDescription.dataChannelSDP;
break;
}
return JSON.stringify(json);
}
const TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE = "AndroidCastDevice:SyncDevice";
const TOPIC_ANDROID_CAST_DEVICE_ADDED = "AndroidCastDevice:Added";
const TOPIC_ANDROID_CAST_DEVICE_REMOVED = "AndroidCastDevice:Removed";
const TOPIC_ANDROID_CAST_DEVICE_START = "AndroidCastDevice:Start";
const TOPIC_ANDROID_CAST_DEVICE_STOP = "AndroidCastDevice:Stop";
const TOPIC_PRESENTATION_VIEW_READY = "presentation-view-ready";
function LocalControlChannel(aProvider, aDeviceId, aRole) {
log("LocalControlChannel - create new LocalControlChannel for : "
+ aRole);
this._provider = aProvider;
this._deviceId = aDeviceId;
this._role = aRole;
}
LocalControlChannel.prototype = {
_listener: null,
_provider: null,
_deviceId: null,
_role: null,
_isOnTerminating: false,
_isOnDisconnecting: false,
_pendingConnected: false,
_pendingDisconnect: null,
_pendingOffer: null,
_pendingCandidate: null,
/* For the controller, it would be the control channel of the receiver.
* For the receiver, it would be the control channel of the controller. */
_correspondingControlChannel: null,
set correspondingControlChannel(aCorrespondingControlChannel) {
this._correspondingControlChannel = aCorrespondingControlChannel;
},
get correspondingControlChannel() {
return this._correspondingControlChannel;
},
notifyConnected: function LCC_notifyConnected() {
this._pendingDisconnect = null;
if (!this._listener) {
this._pendingConnected = true;
} else {
this._listener.notifyConnected();
}
},
onOffer: function LCC_onOffer(aOffer) {
if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) {
log("LocalControlChannel - onOffer of controller should not be called.");
return;
}
if (!this._listener) {
this._pendingOffer = aOffer;
} else {
this._listener.onOffer(aOffer);
}
},
onAnswer: function LCC_onAnswer(aAnswer) {
if (this._role == Ci.nsIPresentationService.ROLE_RECEIVER) {
log("LocalControlChannel - onAnswer of receiver should not be called.");
return;
}
this._listener.onAnswer(aAnswer);
},
notifyIceCandidate: function LCC_notifyIceCandidate(aCandidate) {
if (!this._listener) {
this._pendingCandidate = aCandidate;
} else {
this._listener.onIceCandidate(aCandidate);
}
},
// nsIPresentationControlChannel
get listener() {
return this._listener;
},
set listener(aListener) {
this._listener = aListener;
if (!this._listener) {
return;
}
if (this._pendingConnected) {
this.notifyConnected();
this._pendingConnected = false;
}
if (this._pendingOffer) {
this.onOffer(this._pendingOffer);
this._pendingOffer = null;
}
if (this._pendingCandidate) {
this.notifyIceCandidate(this._pendingCandidate);
this._pendingCandidate = null;
}
if (this._pendingDisconnect != null) {
this.disconnect(this._pendingDisconnect);
this._pendingDisconnect = null;
}
},
sendOffer: function LCC_sendOffer(aOffer) {
if (this._role == Ci.nsIPresentationService.ROLE_RECEIVER) {
log("LocalControlChannel - sendOffer of receiver should not be called.");
return;
}
log("LocalControlChannel - sendOffer aOffer=" + descriptionToString(aOffer));
this._correspondingControlChannel.onOffer(aOffer);
},
sendAnswer: function LCC_sendAnswer(aAnswer) {
if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) {
log("LocalControlChannel - sendAnswer of controller should not be called.");
return;
}
log("LocalControlChannel - sendAnswer aAnswer=" + descriptionToString(aAnswer));
this._correspondingControlChannel.onAnswer(aAnswer);
},
sendIceCandidate: function LCC_sendIceCandidate(aCandidate) {
log("LocalControlChannel - sendAnswer aCandidate=" + aCandidate);
this._correspondingControlChannel.notifyIceCandidate(aCandidate);
},
launch: function LCC_launch(aPresentationId, aUrl) {
log("LocalControlChannel - launch aPresentationId="
+ aPresentationId + " aUrl=" + aUrl);
// Create control channel for receiver directly.
let controlChannel = new LocalControlChannel(this._provider,
this._deviceId,
Ci.nsIPresentationService.ROLE_RECEIVER);
// Set up the corresponding control channels for both controller and receiver.
this._correspondingControlChannel = controlChannel;
controlChannel._correspondingControlChannel = this;
this._provider.onSessionRequest(this._deviceId,
aUrl,
aPresentationId,
controlChannel);
controlChannel.notifyConnected();
},
terminate: function LCC_terminate(aPresentationId) {
log("LocalControlChannel - terminate aPresentationId="
+ aPresentationId);
if (this._isOnTerminating) {
return;
}
// Create control channel for corresponding role directly.
let correspondingRole = this._role == Ci.nsIPresentationService.ROLE_CONTROLLER
? Ci.nsIPresentationService.ROLE_RECEIVER
: Ci.nsIPresentationService.ROLE_CONTROLLER;
let controlChannel = new LocalControlChannel(this._provider,
this._deviceId,
correspondingRole);
// Prevent the termination recursion.
controlChannel._isOnTerminating = true;
// Set up the corresponding control channels for both controller and receiver.
this._correspondingControlChannel = controlChannel;
controlChannel._correspondingControlChannel = this;
this._provider.onTerminateRequest(this._deviceId,
aPresentationId,
controlChannel,
this._role == Ci.nsIPresentationService.ROLE_RECEIVER);
controlChannel.notifyConnected();
},
disconnect: function LCC_disconnect(aReason) {
log("LocalControlChannel - disconnect aReason=" + aReason);
if (this._isOnDisconnecting) {
return;
}
this._pendingOffer = null;
this._pendingCandidate = null;
this._pendingConnected = false;
// this._pendingDisconnect is a nsresult.
// If it is null, it means no pending disconnect.
// If it is NS_OK, it means this control channel is disconnected normally.
// If it is other nsresult value, it means this control channel is
// disconnected abnormally.
// Remote endpoint closes the control channel with abnormal reason.
if (aReason == Cr.NS_OK &&
this._pendingDisconnect != null &&
this._pendingDisconnect != Cr.NS_OK) {
aReason = this._pendingDisconnect;
}
if (!this._listener) {
this._pendingDisconnect = aReason;
return;
}
this._isOnDisconnecting = true;
this._correspondingControlChannel.disconnect(aReason);
this._listener.notifyDisconnected(aReason);
},
reconnect: function LCC_reconnect(aPresentationId, aUrl) {
log("1-UA on Android doesn't support reconnect.");
throw Cr.NS_ERROR_FAILURE;
},
classID: Components.ID("{c9be9450-e5c7-4294-a287-376971b017fd}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
};
function ChromecastRemoteDisplayDevice(aProvider, aId, aName, aRole) {
this._provider = aProvider;
this._id = aId;
this._name = aName;
this._role = aRole;
}
ChromecastRemoteDisplayDevice.prototype = {
_id: null,
_name: null,
_role: null,
_provider: null,
_ctrlChannel: null,
update: function CRDD_update(aName) {
this._name = aName || this._name;
},
// nsIPresentationDevice
get id() { return this._id; },
get name() { return this._name; },
get type() { return "chromecast"; },
establishControlChannel: function CRDD_establishControlChannel() {
this._ctrlChannel = new LocalControlChannel(this._provider,
this._id,
this._role);
if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) {
// Only connect to Chromecast for controller.
// Monitor the receiver being ready.
Services.obs.addObserver(this, TOPIC_PRESENTATION_VIEW_READY, true);
// Launch Chromecast service in Android.
Messaging.sendRequestForResult({
type: TOPIC_ANDROID_CAST_DEVICE_START,
id: this.id
}).then(result => {
log("Chromecast is connected.");
}).catch(error => {
log("Can not connect to Chromecast.");
// If Chromecast can not be launched, remove the observer.
Services.obs.removeObserver(this, TOPIC_PRESENTATION_VIEW_READY);
this._ctrlChannel.disconnect(Cr.NS_ERROR_FAILURE);
});
} else {
// If establishControlChannel called from the receiver, we don't need to
// wait the 'presentation-view-ready' event.
this._ctrlChannel.notifyConnected();
}
return this._ctrlChannel;
},
disconnect: function CRDD_disconnect() {
// Disconnect from Chromecast.
Messaging.sendRequestForResult({
type: TOPIC_ANDROID_CAST_DEVICE_STOP,
id: this.id
});
},
isRequestedUrlSupported: function CRDD_isRequestedUrlSupported(aUrl) {
let url = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService)
.newURI(aUrl, null, null);
return url.scheme == "http" || url.scheme == "https";
},
// nsIPresentationLocalDevice
get windowId() { return this._id; },
// nsIObserver
observe: function CRDD_observe(aSubject, aTopic, aData) {
if (aTopic == TOPIC_PRESENTATION_VIEW_READY) {
log("ChromecastRemoteDisplayDevice - observe: aTopic="
+ aTopic + " data=" + aData);
if (this.windowId === aData) {
Services.obs.removeObserver(this, TOPIC_PRESENTATION_VIEW_READY);
this._ctrlChannel.notifyConnected();
}
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice,
Ci.nsIPresentationLocalDevice,
Ci.nsISupportsWeakReference,
Ci.nsIObserver]),
};
function AndroidCastDeviceProvider() {
}
AndroidCastDeviceProvider.prototype = {
_listener: null,
_deviceList: new Map(),
onSessionRequest: function APDP_onSessionRequest(aDeviceId,
aUrl,
aPresentationId,
aControlChannel) {
log("AndroidCastDeviceProvider - onSessionRequest"
+ " aDeviceId=" + aDeviceId);
let device = this._deviceList.get(aDeviceId);
let receiverDevice = new ChromecastRemoteDisplayDevice(this,
device.id,
device.name,
Ci.nsIPresentationService.ROLE_RECEIVER);
this._listener.onSessionRequest(receiverDevice,
aUrl,
aPresentationId,
aControlChannel);
},
onTerminateRequest: function APDP_onTerminateRequest(aDeviceId,
aPresentationId,
aControlChannel,
aIsFromReceiver) {
log("AndroidCastDeviceProvider - onTerminateRequest"
+ " aDeviceId=" + aDeviceId
+ " aPresentationId=" + aPresentationId
+ " aIsFromReceiver=" + aIsFromReceiver);
let device = this._deviceList.get(aDeviceId);
this._listener.onTerminateRequest(device,
aPresentationId,
aControlChannel,
aIsFromReceiver);
},
// nsIPresentationDeviceProvider
set listener(aListener) {
this._listener = aListener;
// When unload this provider.
if (!this._listener) {
// remove observer
Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED);
Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED);
return;
}
// Sync all device already found by Android.
Services.obs.notifyObservers(null, TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE, "");
// Observer registration
Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED, false);
Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED, false);
},
get listener() {
return this._listener;
},
forceDiscovery: function APDP_forceDiscovery() {
// There is no API to do force discovery in Android SDK.
},
// nsIObserver
observe: function APDP_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case TOPIC_ANDROID_CAST_DEVICE_ADDED: {
let deviceInfo = JSON.parse(aData);
let deviceId = deviceInfo.uuid;
if (!this._deviceList.has(deviceId)) {
let device = new ChromecastRemoteDisplayDevice(this,
deviceInfo.uuid,
deviceInfo.friendlyName,
Ci.nsIPresentationService.ROLE_CONTROLLER);
this._deviceList.set(device.id, device);
this._listener.addDevice(device);
} else {
let device = this._deviceList.get(deviceId);
device.update(deviceInfo.friendlyName);
this._listener.updateDevice(device);
}
break;
}
case TOPIC_ANDROID_CAST_DEVICE_REMOVED: {
let deviceId = aData;
let device = this._deviceList.get(deviceId);
this._listener.removeDevice(device);
this._deviceList.delete(deviceId);
break;
}
}
},
classID: Components.ID("{7394f24c-dbc3-48c8-8a47-cd10169b7c6b}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsIPresentationDeviceProvider]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AndroidCastDeviceProvider]);

View file

@ -1,4 +0,0 @@
# AndroidCastDeviceProvider.js
component {7394f24c-dbc3-48c8-8a47-cd10169b7c6b} AndroidCastDeviceProvider.js
contract @mozilla.org/presentation-device/android-cast-device-provider;1 {7394f24c-dbc3-48c8-8a47-cd10169b7c6b}
category presentation-device-provider AndroidCastDeviceProvider @mozilla.org/presentation-device/android-cast-device-provider;1

View file

@ -1,2 +0,0 @@
component {f4079b8b-ede5-4b90-a112-5b415a931deb} PresentationControlService.js
contract @mozilla.org/presentation/control-service;1 {f4079b8b-ede5-4b90-a112-5b415a931deb}

View file

@ -1,240 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
/* globals Components, dump */
"use strict";
this.EXPORTED_SYMBOLS = ["ControllerStateMachine"]; // jshint ignore:line
const { utils: Cu } = Components;
/* globals State, CommandType */
Cu.import("resource://gre/modules/presentation/StateMachineHelper.jsm");
const DEBUG = false;
function debug(str) {
dump("-*- ControllerStateMachine: " + str + "\n");
}
var handlers = [
function _initHandler(stateMachine, command) {
// shouldn't receive any command at init state.
DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
},
function _connectingHandler(stateMachine, command) {
switch (command.type) {
case CommandType.CONNECT_ACK:
stateMachine.state = State.CONNECTED;
stateMachine._notifyDeviceConnected();
break;
case CommandType.DISCONNECT:
stateMachine.state = State.CLOSED;
stateMachine._notifyDisconnected(command.reason);
break;
default:
debug("unexpected command: " + JSON.stringify(command));
// ignore unexpected command.
break;
}
},
function _connectedHandler(stateMachine, command) {
switch (command.type) {
case CommandType.DISCONNECT:
stateMachine.state = State.CLOSED;
stateMachine._notifyDisconnected(command.reason);
break;
case CommandType.LAUNCH_ACK:
stateMachine._notifyLaunch(command.presentationId);
break;
case CommandType.TERMINATE:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.TERMINATE_ACK:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.ANSWER:
case CommandType.ICE_CANDIDATE:
stateMachine._notifyChannelDescriptor(command);
break;
case CommandType.RECONNECT_ACK:
stateMachine._notifyReconnect(command.presentationId);
break;
default:
debug("unexpected command: " + JSON.stringify(command));
// ignore unexpected command.
break;
}
},
function _closingHandler(stateMachine, command) {
switch (command.type) {
case CommandType.DISCONNECT:
stateMachine.state = State.CLOSED;
stateMachine._notifyDisconnected(command.reason);
break;
default:
debug("unexpected command: " + JSON.stringify(command));
// ignore unexpected command.
break;
}
},
function _closedHandler(stateMachine, command) {
// ignore every command in closed state.
DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
},
];
function ControllerStateMachine(channel, deviceId) {
this.state = State.INIT;
this._channel = channel;
this._deviceId = deviceId;
}
ControllerStateMachine.prototype = {
launch: function _launch(presentationId, url) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.LAUNCH,
presentationId: presentationId,
url: url,
});
}
},
terminate: function _terminate(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE,
presentationId: presentationId,
});
}
},
terminateAck: function _terminateAck(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE_ACK,
presentationId: presentationId,
});
}
},
reconnect: function _reconnect(presentationId, url) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.RECONNECT,
presentationId: presentationId,
url: url,
});
}
},
sendOffer: function _sendOffer(offer) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.OFFER,
offer: offer,
});
}
},
sendAnswer: function _sendAnswer() {
// answer can only be sent by presenting UA.
debug("controller shouldn't generate answer");
},
updateIceCandidate: function _updateIceCandidate(candidate) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.ICE_CANDIDATE,
candidate: candidate,
});
}
},
onCommand: function _onCommand(command) {
handlers[this.state](this, command);
},
onChannelReady: function _onChannelReady() {
if (this.state === State.INIT) {
this._sendCommand({
type: CommandType.CONNECT,
deviceId: this._deviceId
});
this.state = State.CONNECTING;
}
},
onChannelClosed: function _onChannelClose(reason, isByRemote) {
switch (this.state) {
case State.CONNECTED:
if (isByRemote) {
this.state = State.CLOSED;
this._notifyDisconnected(reason);
} else {
this._sendCommand({
type: CommandType.DISCONNECT,
reason: reason
});
this.state = State.CLOSING;
this._closeReason = reason;
}
break;
case State.CLOSING:
if (isByRemote) {
this.state = State.CLOSED;
if (this._closeReason) {
reason = this._closeReason;
delete this._closeReason;
}
this._notifyDisconnected(reason);
}
break;
default:
DEBUG && debug("unexpected channel close: " + reason + ", " + isByRemote); // jshint ignore:line
break;
}
},
_sendCommand: function _sendCommand(command) {
this._channel.sendCommand(command);
},
_notifyDeviceConnected: function _notifyDeviceConnected() {
//XXX trigger following command
this._channel.notifyDeviceConnected();
},
_notifyDisconnected: function _notifyDisconnected(reason) {
this._channel.notifyDisconnected(reason);
},
_notifyLaunch: function _notifyLaunch(presentationId) {
this._channel.notifyLaunch(presentationId);
},
_notifyTerminate: function _notifyTerminate(presentationId) {
this._channel.notifyTerminate(presentationId);
},
_notifyReconnect: function _notifyReconnect(presentationId) {
this._channel.notifyReconnect(presentationId);
},
_notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
switch (command.type) {
case CommandType.ANSWER:
this._channel.notifyAnswer(command.answer);
break;
case CommandType.ICE_CANDIDATE:
this._channel.notifyIceCandidate(command.candidate);
break;
}
},
};
this.ControllerStateMachine = ControllerStateMachine; // jshint ignore:line

View file

@ -1,57 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "DeviceProviderHelpers.h"
#include "nsCOMPtr.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace dom {
namespace presentation {
static const char* const kFxTVPresentationAppUrls[] = {
"app://fling-player.gaiamobile.org/index.html",
"app://notification-receiver.gaiamobile.org/index.html",
nullptr
};
/* static */ bool
DeviceProviderHelpers::IsCommonlySupportedScheme(const nsAString& aUrl)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl);
if (NS_FAILED(rv) || !uri) {
return false;
}
nsAutoCString scheme;
uri->GetScheme(scheme);
if (scheme.LowerCaseEqualsLiteral("http") ||
scheme.LowerCaseEqualsLiteral("https")) {
return true;
}
return false;
}
/* static */ bool
DeviceProviderHelpers::IsFxTVSupportedAppUrl(const nsAString& aUrl)
{
// Check if matched with any presentation Apps on TV.
for (uint32_t i = 0; kFxTVPresentationAppUrls[i]; i++) {
if (aUrl.EqualsASCII(kFxTVPresentationAppUrls[i])) {
return true;
}
}
return false;
}
} // namespace presentation
} // namespace dom
} // namespace mozilla

View file

@ -1,30 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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_presentation_DeviceProviderHelpers_h
#define mozilla_dom_presentation_DeviceProviderHelpers_h
#include "nsString.h"
namespace mozilla {
namespace dom {
namespace presentation {
class DeviceProviderHelpers final
{
public:
static bool IsCommonlySupportedScheme(const nsAString& aUrl);
static bool IsFxTVSupportedAppUrl(const nsAString& aUrl);
private:
DeviceProviderHelpers() = delete;
};
} // namespace presentation
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_presentation_DeviceProviderHelpers_h

View file

@ -1,580 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "DisplayDeviceProvider.h"
#include "DeviceProviderHelpers.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "nsIObserverService.h"
#include "nsIServiceManager.h"
#include "nsIWindowWatcher.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
#include "nsSimpleURI.h"
#include "nsTCPDeviceInfo.h"
#include "nsThreadUtils.h"
static mozilla::LazyLogModule gDisplayDeviceProviderLog("DisplayDeviceProvider");
#define LOG(format) MOZ_LOG(gDisplayDeviceProviderLog, mozilla::LogLevel::Debug, format)
#define DISPLAY_CHANGED_NOTIFICATION "display-changed"
#define DEFAULT_CHROME_FEATURES_PREF "toolkit.defaultChromeFeatures"
#define CHROME_REMOTE_URL_PREF "b2g.multiscreen.chrome_remote_url"
#define PREF_PRESENTATION_DISCOVERABLE_RETRY_MS "dom.presentation.discoverable.retry_ms"
namespace mozilla {
namespace dom {
namespace presentation {
/**
* This wrapper is used to break circular-reference problem.
*/
class DisplayDeviceProviderWrappedListener final
: public nsIPresentationControlServerListener
{
public:
NS_DECL_ISUPPORTS
NS_FORWARD_SAFE_NSIPRESENTATIONCONTROLSERVERLISTENER(mListener)
explicit DisplayDeviceProviderWrappedListener() = default;
nsresult SetListener(DisplayDeviceProvider* aListener)
{
mListener = aListener;
return NS_OK;
}
private:
virtual ~DisplayDeviceProviderWrappedListener() = default;
DisplayDeviceProvider* mListener = nullptr;
};
NS_IMPL_ISUPPORTS(DisplayDeviceProviderWrappedListener,
nsIPresentationControlServerListener)
NS_IMPL_ISUPPORTS(DisplayDeviceProvider::HDMIDisplayDevice,
nsIPresentationDevice,
nsIPresentationLocalDevice)
// nsIPresentationDevice
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::GetId(nsACString& aId)
{
aId = mWindowId;
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::GetName(nsACString& aName)
{
aName = mName;
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::GetType(nsACString& aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::GetWindowId(nsACString& aWindowId)
{
aWindowId = mWindowId;
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice
::EstablishControlChannel(nsIPresentationControlChannel** aControlChannel)
{
nsresult rv = OpenTopLevelWindow();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<DisplayDeviceProvider> provider = mProvider.get();
if (NS_WARN_IF(!provider)) {
return NS_ERROR_FAILURE;
}
return provider->Connect(this, aControlChannel);
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::Disconnect()
{
nsresult rv = CloseTopLevelWindow();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;;
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::IsRequestedUrlSupported(
const nsAString& aRequestedUrl,
bool* aRetVal)
{
MOZ_ASSERT(NS_IsMainThread());
if (!aRetVal) {
return NS_ERROR_INVALID_POINTER;
}
// 1-UA device only supports HTTP/HTTPS hosted receiver page.
*aRetVal = DeviceProviderHelpers::IsCommonlySupportedScheme(aRequestedUrl);
return NS_OK;
}
nsresult
DisplayDeviceProvider::HDMIDisplayDevice::OpenTopLevelWindow()
{
MOZ_ASSERT(!mWindow);
nsresult rv;
nsAutoCString flags(Preferences::GetCString(DEFAULT_CHROME_FEATURES_PREF));
if (flags.IsEmpty()) {
return NS_ERROR_NOT_AVAILABLE;
}
flags.AppendLiteral(",mozDisplayId=");
flags.AppendInt(mScreenId);
nsAutoCString remoteShellURLString(Preferences::GetCString(CHROME_REMOTE_URL_PREF));
remoteShellURLString.AppendLiteral("#");
remoteShellURLString.Append(mWindowId);
// URI validation
nsCOMPtr<nsIURI> remoteShellURL;
rv = NS_NewURI(getter_AddRefs(remoteShellURL), remoteShellURLString);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = remoteShellURL->GetSpec(remoteShellURLString);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
MOZ_ASSERT(ww);
rv = ww->OpenWindow(nullptr,
remoteShellURLString.get(),
"_blank",
flags.get(),
nullptr,
getter_AddRefs(mWindow));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
DisplayDeviceProvider::HDMIDisplayDevice::CloseTopLevelWindow()
{
MOZ_ASSERT(mWindow);
nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(mWindow);
nsresult rv = piWindow->Close();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMPL_ISUPPORTS(DisplayDeviceProvider,
nsIObserver,
nsIPresentationDeviceProvider,
nsIPresentationControlServerListener)
DisplayDeviceProvider::~DisplayDeviceProvider()
{
Uninit();
}
nsresult
DisplayDeviceProvider::Init()
{
// Provider must be initialized only once.
if (mInitialized) {
return NS_OK;
}
nsresult rv;
mServerRetryMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERABLE_RETRY_MS);
mServerRetryTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
MOZ_ASSERT(obs);
obs->AddObserver(this, DISPLAY_CHANGED_NOTIFICATION, false);
mDevice = new HDMIDisplayDevice(this);
mWrappedListener = new DisplayDeviceProviderWrappedListener();
rv = mWrappedListener->SetListener(this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mPresentationService = do_CreateInstance(PRESENTATION_CONTROL_SERVICE_CONTACT_ID,
&rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = StartTCPService();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mInitialized = true;
return NS_OK;
}
nsresult
DisplayDeviceProvider::Uninit()
{
// Provider must be deleted only once.
if (!mInitialized) {
return NS_OK;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, DISPLAY_CHANGED_NOTIFICATION);
}
// Remove device from device manager when the provider is uninit
RemoveExternalScreen();
AbortServerRetry();
mInitialized = false;
mWrappedListener->SetListener(nullptr);
return NS_OK;
}
nsresult
DisplayDeviceProvider::StartTCPService()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
rv = mPresentationService->SetId(NS_LITERAL_CSTRING("DisplayDeviceProvider"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
uint16_t servicePort;
rv = mPresentationService->GetPort(&servicePort);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
/*
* If |servicePort| is non-zero, it means PresentationServer is running.
* Otherwise, we should make it start serving.
*/
if (servicePort) {
mPort = servicePort;
return NS_OK;
}
rv = mPresentationService->SetListener(mWrappedListener);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
AbortServerRetry();
// 1-UA doesn't need encryption.
rv = mPresentationService->StartServer(/* aEncrypted = */ false,
/* aPort = */ 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
void
DisplayDeviceProvider::AbortServerRetry()
{
if (mIsServerRetrying) {
mIsServerRetrying = false;
mServerRetryTimer->Cancel();
}
}
nsresult
DisplayDeviceProvider::AddExternalScreen()
{
MOZ_ASSERT(mDeviceListener);
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener;
rv = GetListener(getter_AddRefs(listener));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = listener->AddDevice(mDevice);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
DisplayDeviceProvider::RemoveExternalScreen()
{
MOZ_ASSERT(mDeviceListener);
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener;
rv = GetListener(getter_AddRefs(listener));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = listener->RemoveDevice(mDevice);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mDevice->Disconnect();
return NS_OK;
}
// nsIPresentationDeviceProvider
NS_IMETHODIMP
DisplayDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener)
{
if (NS_WARN_IF(!aListener)) {
return NS_ERROR_INVALID_POINTER;
}
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener =
do_QueryReferent(mDeviceListener, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
listener.forget(aListener);
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener)
{
mDeviceListener = do_GetWeakReference(aListener);
nsresult rv = mDeviceListener ? Init() : Uninit();
if(NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::ForceDiscovery()
{
return NS_OK;
}
// nsIPresentationControlServerListener
NS_IMETHODIMP
DisplayDeviceProvider::OnServerReady(uint16_t aPort,
const nsACString& aCertFingerprint)
{
MOZ_ASSERT(NS_IsMainThread());
mPort = aPort;
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::OnServerStopped(nsresult aResult)
{
MOZ_ASSERT(NS_IsMainThread());
// Try restart server if it is stopped abnormally.
if (NS_FAILED(aResult)) {
mIsServerRetrying = true;
mServerRetryTimer->Init(this, mServerRetryMs, nsITimer::TYPE_ONE_SHOT);
}
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aDeviceInfo);
MOZ_ASSERT(aControlChannel);
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener;
rv = GetListener(getter_AddRefs(listener));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(!listener);
rv = listener->OnSessionRequest(mDevice,
aUrl,
aPresentationId,
aControlChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel,
bool aIsFromReceiver)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aDeviceInfo);
MOZ_ASSERT(aControlChannel);
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener;
rv = GetListener(getter_AddRefs(listener));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(!listener);
rv = listener->OnTerminateRequest(mDevice,
aPresentationId,
aControlChannel,
aIsFromReceiver);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::OnReconnectRequest(nsITCPDeviceInfo* aDeviceInfo,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel* aControlChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aDeviceInfo);
MOZ_ASSERT(aControlChannel);
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener;
rv = GetListener(getter_AddRefs(listener));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(!listener);
rv = listener->OnReconnectRequest(mDevice,
aUrl,
aPresentationId,
aControlChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// nsIObserver
NS_IMETHODIMP
DisplayDeviceProvider::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, DISPLAY_CHANGED_NOTIFICATION)) {
nsCOMPtr<nsIDisplayInfo> displayInfo = do_QueryInterface(aSubject);
MOZ_ASSERT(displayInfo);
int32_t type;
bool isConnected;
displayInfo->GetConnected(&isConnected);
// XXX The ID is as same as the type of display.
// See Bug 1138287 and nsScreenManagerGonk::AddScreen() for more detail.
displayInfo->GetId(&type);
if (type == DisplayType::DISPLAY_EXTERNAL) {
nsresult rv = isConnected ? AddExternalScreen() : RemoveExternalScreen();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
} else if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
if (!timer) {
return NS_ERROR_UNEXPECTED;
}
if (timer == mServerRetryTimer) {
mIsServerRetrying = false;
StartTCPService();
}
}
return NS_OK;
}
nsresult
DisplayDeviceProvider::Connect(HDMIDisplayDevice* aDevice,
nsIPresentationControlChannel** aControlChannel)
{
MOZ_ASSERT(aDevice);
MOZ_ASSERT(mPresentationService);
NS_ENSURE_ARG_POINTER(aControlChannel);
*aControlChannel = nullptr;
nsCOMPtr<nsITCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
aDevice->Address(),
mPort,
EmptyCString());
return mPresentationService->Connect(deviceInfo, aControlChannel);
}
} // namespace presentation
} // namespace dom
} // namespace mozilla

View file

@ -1,136 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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_presentation_provider_DisplayDeviceProvider_h
#define mozilla_dom_presentation_provider_DisplayDeviceProvider_h
#include "mozilla/RefPtr.h"
#include "mozilla/WeakPtr.h"
#include "nsCOMPtr.h"
#include "nsIDOMWindow.h"
#include "nsIDisplayInfo.h"
#include "nsIObserver.h"
#include "nsIPresentationDeviceProvider.h"
#include "nsIPresentationLocalDevice.h"
#include "nsIPresentationControlService.h"
#include "nsITimer.h"
#include "nsIWindowWatcher.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakReference.h"
namespace mozilla {
namespace dom {
namespace presentation {
// Consistent definition with the definition in
// widget/gonk/libdisplay/GonkDisplay.h.
enum DisplayType {
DISPLAY_PRIMARY,
DISPLAY_EXTERNAL,
DISPLAY_VIRTUAL,
NUM_DISPLAY_TYPES
};
class DisplayDeviceProviderWrappedListener;
class DisplayDeviceProvider final : public nsIObserver
, public nsIPresentationDeviceProvider
, public nsIPresentationControlServerListener
, public SupportsWeakPtr<DisplayDeviceProvider>
{
private:
class HDMIDisplayDevice final : public nsIPresentationLocalDevice
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONDEVICE
NS_DECL_NSIPRESENTATIONLOCALDEVICE
// mScreenId is as same as the definition of display type.
explicit HDMIDisplayDevice(DisplayDeviceProvider* aProvider)
: mScreenId(DisplayType::DISPLAY_EXTERNAL)
, mName("HDMI")
, mType("external")
, mWindowId("hdmi")
, mAddress("127.0.0.1")
, mProvider(aProvider)
{}
nsresult OpenTopLevelWindow();
nsresult CloseTopLevelWindow();
const nsCString& Id() const { return mWindowId; }
const nsCString& Address() const { return mAddress; }
private:
virtual ~HDMIDisplayDevice() = default;
// Due to the limitation of nsWinodw, mScreenId must be an integer.
// And mScreenId is also align to the display type defined in
// widget/gonk/libdisplay/GonkDisplay.h.
// HDMI display is DisplayType::DISPLAY_EXTERNAL.
uint32_t mScreenId;
nsCString mName;
nsCString mType;
nsCString mWindowId;
nsCString mAddress;
nsCOMPtr<mozIDOMWindowProxy> mWindow;
// weak pointer
// Provider hold a strong pointer to the device. Use weak pointer to prevent
// the reference cycle.
WeakPtr<DisplayDeviceProvider> mProvider;
};
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIPRESENTATIONDEVICEPROVIDER
NS_DECL_NSIPRESENTATIONCONTROLSERVERLISTENER
// For using WeakPtr when MOZ_REFCOUNTED_LEAK_CHECKING defined
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(DisplayDeviceProvider)
nsresult Connect(HDMIDisplayDevice* aDevice,
nsIPresentationControlChannel** aControlChannel);
private:
virtual ~DisplayDeviceProvider();
nsresult Init();
nsresult Uninit();
nsresult AddExternalScreen();
nsresult RemoveExternalScreen();
nsresult StartTCPService();
void AbortServerRetry();
// Now support HDMI display only and there should be only one HDMI display.
nsCOMPtr<nsIPresentationLocalDevice> mDevice = nullptr;
// weak pointer
// PresentationDeviceManager (mDeviceListener) hold strong pointer to
// DisplayDeviceProvider. Use nsWeakPtr to avoid reference cycle.
nsWeakPtr mDeviceListener = nullptr;
nsCOMPtr<nsIPresentationControlService> mPresentationService;
// Used to prevent reference cycle between DisplayDeviceProvider and
// TCPPresentationServer.
RefPtr<DisplayDeviceProviderWrappedListener> mWrappedListener;
bool mInitialized = false;
uint16_t mPort;
bool mIsServerRetrying = false;
uint32_t mServerRetryMs;
nsCOMPtr<nsITimer> mServerRetryTimer;
};
} // mozilla
} // dom
} // presentation
#endif // mozilla_dom_presentation_provider_DisplayDeviceProvider_h

View file

@ -1,774 +0,0 @@
/* -*- 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 "LegacyMDNSDeviceProvider.h"
#include "DeviceProviderHelpers.h"
#include "MainThreadUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "nsComponentManagerUtils.h"
#include "nsIObserverService.h"
#include "nsIWritablePropertyBag2.h"
#include "nsServiceManagerUtils.h"
#include "nsTCPDeviceInfo.h"
#include "nsThreadUtils.h"
#include "nsIPropertyBag2.h"
#define PREF_PRESENTATION_DISCOVERY_LEGACY "dom.presentation.discovery.legacy.enabled"
#define PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS "dom.presentation.discovery.timeout_ms"
#define PREF_PRESENTATION_DEVICE_NAME "dom.presentation.device.name"
#define LEGACY_SERVICE_TYPE "_mozilla_papi._tcp"
#define LEGACY_PRESENTATION_CONTROL_SERVICE_CONTACT_ID "@mozilla.org/presentation/legacy-control-service;1"
static mozilla::LazyLogModule sLegacyMDNSProviderLogModule("LegacyMDNSDeviceProvider");
#undef LOG_I
#define LOG_I(...) MOZ_LOG(sLegacyMDNSProviderLogModule, mozilla::LogLevel::Debug, (__VA_ARGS__))
#undef LOG_E
#define LOG_E(...) MOZ_LOG(sLegacyMDNSProviderLogModule, mozilla::LogLevel::Error, (__VA_ARGS__))
namespace mozilla {
namespace dom {
namespace presentation {
namespace legacy {
static const char* kObservedPrefs[] = {
PREF_PRESENTATION_DISCOVERY_LEGACY,
PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS,
PREF_PRESENTATION_DEVICE_NAME,
nullptr
};
namespace {
static void
GetAndroidDeviceName(nsACString& aRetVal)
{
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
MOZ_ASSERT(infoService, "Could not find a system info service");
Unused << NS_WARN_IF(NS_FAILED(infoService->GetPropertyAsACString(
NS_LITERAL_STRING("device"), aRetVal)));
}
} //anonymous namespace
/**
* This wrapper is used to break circular-reference problem.
*/
class DNSServiceWrappedListener final
: public nsIDNSServiceDiscoveryListener
, public nsIDNSServiceResolveListener
{
public:
NS_DECL_ISUPPORTS
NS_FORWARD_SAFE_NSIDNSSERVICEDISCOVERYLISTENER(mListener)
NS_FORWARD_SAFE_NSIDNSSERVICERESOLVELISTENER(mListener)
explicit DNSServiceWrappedListener() = default;
nsresult SetListener(LegacyMDNSDeviceProvider* aListener)
{
mListener = aListener;
return NS_OK;
}
private:
virtual ~DNSServiceWrappedListener() = default;
LegacyMDNSDeviceProvider* mListener = nullptr;
};
NS_IMPL_ISUPPORTS(DNSServiceWrappedListener,
nsIDNSServiceDiscoveryListener,
nsIDNSServiceResolveListener)
NS_IMPL_ISUPPORTS(LegacyMDNSDeviceProvider,
nsIPresentationDeviceProvider,
nsIDNSServiceDiscoveryListener,
nsIDNSServiceResolveListener,
nsIObserver)
LegacyMDNSDeviceProvider::~LegacyMDNSDeviceProvider()
{
Uninit();
}
nsresult
LegacyMDNSDeviceProvider::Init()
{
MOZ_ASSERT(NS_IsMainThread());
if (mInitialized) {
return NS_OK;
}
nsresult rv;
mMulticastDNS = do_GetService(DNSSERVICEDISCOVERY_CONTRACT_ID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mWrappedListener = new DNSServiceWrappedListener();
if (NS_WARN_IF(NS_FAILED(rv = mWrappedListener->SetListener(this)))) {
return rv;
}
mPresentationService = do_CreateInstance(LEGACY_PRESENTATION_CONTROL_SERVICE_CONTACT_ID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mDiscoveryTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Preferences::AddStrongObservers(this, kObservedPrefs);
mDiscoveryEnabled = Preferences::GetBool(PREF_PRESENTATION_DISCOVERY_LEGACY);
mDiscoveryTimeoutMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS);
mServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME);
// FIXME: Bug 1185806 - Provide a common device name setting.
if (mServiceName.IsEmpty()) {
GetAndroidDeviceName(mServiceName);
Unused << Preferences::SetCString(PREF_PRESENTATION_DEVICE_NAME, mServiceName);
}
Unused << mPresentationService->SetId(mServiceName);
if (mDiscoveryEnabled && NS_WARN_IF(NS_FAILED(rv = ForceDiscovery()))) {
return rv;
}
mInitialized = true;
return NS_OK;
}
nsresult
LegacyMDNSDeviceProvider::Uninit()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mInitialized) {
return NS_OK;
}
ClearDevices();
Preferences::RemoveObservers(this, kObservedPrefs);
StopDiscovery(NS_OK);
mMulticastDNS = nullptr;
if (mWrappedListener) {
mWrappedListener->SetListener(nullptr);
mWrappedListener = nullptr;
}
mInitialized = false;
return NS_OK;
}
nsresult
LegacyMDNSDeviceProvider::StopDiscovery(nsresult aReason)
{
LOG_I("StopDiscovery (0x%08x)", aReason);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDiscoveryTimer);
Unused << mDiscoveryTimer->Cancel();
if (mDiscoveryRequest) {
mDiscoveryRequest->Cancel(aReason);
mDiscoveryRequest = nullptr;
}
return NS_OK;
}
nsresult
LegacyMDNSDeviceProvider::Connect(Device* aDevice,
nsIPresentationControlChannel** aRetVal)
{
MOZ_ASSERT(aDevice);
MOZ_ASSERT(mPresentationService);
RefPtr<TCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
aDevice->Address(),
aDevice->Port(),
EmptyCString());
return mPresentationService->Connect(deviceInfo, aRetVal);
}
nsresult
LegacyMDNSDeviceProvider::AddDevice(const nsACString& aId,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPresentationService);
RefPtr<Device> device = new Device(aId, /* ID */
aServiceName,
aServiceType,
aAddress,
aPort,
DeviceState::eActive,
this);
nsCOMPtr<nsIPresentationDeviceListener> listener;
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
Unused << listener->AddDevice(device);
}
mDevices.AppendElement(device);
return NS_OK;
}
nsresult
LegacyMDNSDeviceProvider::UpdateDevice(const uint32_t aIndex,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPresentationService);
if (NS_WARN_IF(aIndex >= mDevices.Length())) {
return NS_ERROR_INVALID_ARG;
}
RefPtr<Device> device = mDevices[aIndex];
device->Update(aServiceName, aServiceType, aAddress, aPort);
device->ChangeState(DeviceState::eActive);
nsCOMPtr<nsIPresentationDeviceListener> listener;
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
Unused << listener->UpdateDevice(device);
}
return NS_OK;
}
nsresult
LegacyMDNSDeviceProvider::RemoveDevice(const uint32_t aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPresentationService);
if (NS_WARN_IF(aIndex >= mDevices.Length())) {
return NS_ERROR_INVALID_ARG;
}
RefPtr<Device> device = mDevices[aIndex];
LOG_I("RemoveDevice: %s", device->Id().get());
mDevices.RemoveElementAt(aIndex);
nsCOMPtr<nsIPresentationDeviceListener> listener;
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
Unused << listener->RemoveDevice(device);
}
return NS_OK;
}
bool
LegacyMDNSDeviceProvider::FindDeviceById(const nsACString& aId,
uint32_t& aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Device> device = new Device(aId,
/* aName = */ EmptyCString(),
/* aType = */ EmptyCString(),
/* aHost = */ EmptyCString(),
/* aPort = */ 0,
/* aState = */ DeviceState::eUnknown,
/* aProvider = */ nullptr);
size_t index = mDevices.IndexOf(device, 0, DeviceIdComparator());
if (index == mDevices.NoIndex) {
return false;
}
aIndex = index;
return true;
}
bool
LegacyMDNSDeviceProvider::FindDeviceByAddress(const nsACString& aAddress,
uint32_t& aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Device> device = new Device(/* aId = */ EmptyCString(),
/* aName = */ EmptyCString(),
/* aType = */ EmptyCString(),
aAddress,
/* aPort = */ 0,
/* aState = */ DeviceState::eUnknown,
/* aProvider = */ nullptr);
size_t index = mDevices.IndexOf(device, 0, DeviceAddressComparator());
if (index == mDevices.NoIndex) {
return false;
}
aIndex = index;
return true;
}
void
LegacyMDNSDeviceProvider::MarkAllDevicesUnknown()
{
MOZ_ASSERT(NS_IsMainThread());
for (auto& device : mDevices) {
device->ChangeState(DeviceState::eUnknown);
}
}
void
LegacyMDNSDeviceProvider::ClearUnknownDevices()
{
MOZ_ASSERT(NS_IsMainThread());
size_t i = mDevices.Length();
while (i > 0) {
--i;
if (mDevices[i]->State() == DeviceState::eUnknown) {
Unused << NS_WARN_IF(NS_FAILED(RemoveDevice(i)));
}
}
}
void
LegacyMDNSDeviceProvider::ClearDevices()
{
MOZ_ASSERT(NS_IsMainThread());
size_t i = mDevices.Length();
while (i > 0) {
--i;
Unused << NS_WARN_IF(NS_FAILED(RemoveDevice(i)));
}
}
// nsIPresentationDeviceProvider
NS_IMETHODIMP
LegacyMDNSDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aListener)) {
return NS_ERROR_INVALID_POINTER;
}
nsresult rv;
nsCOMPtr<nsIPresentationDeviceListener> listener =
do_QueryReferent(mDeviceListener, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
listener.forget(aListener);
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener)
{
MOZ_ASSERT(NS_IsMainThread());
mDeviceListener = do_GetWeakReference(aListener);
nsresult rv;
if (mDeviceListener) {
if (NS_WARN_IF(NS_FAILED(rv = Init()))) {
return rv;
}
} else {
if (NS_WARN_IF(NS_FAILED(rv = Uninit()))) {
return rv;
}
}
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::ForceDiscovery()
{
LOG_I("ForceDiscovery (%d)", mDiscoveryEnabled);
MOZ_ASSERT(NS_IsMainThread());
if (!mDiscoveryEnabled) {
return NS_OK;
}
MOZ_ASSERT(mDiscoveryTimer);
MOZ_ASSERT(mMulticastDNS);
// if it's already discovering, extend existing discovery timeout.
nsresult rv;
if (mIsDiscovering) {
Unused << mDiscoveryTimer->Cancel();
if (NS_WARN_IF(NS_FAILED( rv = mDiscoveryTimer->Init(this,
mDiscoveryTimeoutMs,
nsITimer::TYPE_ONE_SHOT)))) {
return rv;
}
return NS_OK;
}
StopDiscovery(NS_OK);
if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->StartDiscovery(
NS_LITERAL_CSTRING(LEGACY_SERVICE_TYPE),
mWrappedListener,
getter_AddRefs(mDiscoveryRequest))))) {
return rv;
}
return NS_OK;
}
// nsIDNSServiceDiscoveryListener
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnDiscoveryStarted(const nsACString& aServiceType)
{
LOG_I("OnDiscoveryStarted");
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDiscoveryTimer);
MarkAllDevicesUnknown();
nsresult rv;
if (NS_WARN_IF(NS_FAILED(rv = mDiscoveryTimer->Init(this,
mDiscoveryTimeoutMs,
nsITimer::TYPE_ONE_SHOT)))) {
return rv;
}
mIsDiscovering = true;
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnDiscoveryStopped(const nsACString& aServiceType)
{
LOG_I("OnDiscoveryStopped");
MOZ_ASSERT(NS_IsMainThread());
ClearUnknownDevices();
mIsDiscovering = false;
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnServiceFound(nsIDNSServiceInfo* aServiceInfo)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aServiceInfo)) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv ;
nsAutoCString serviceName;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) {
return rv;
}
LOG_I("OnServiceFound: %s", serviceName.get());
if (mMulticastDNS) {
if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService(
aServiceInfo, mWrappedListener)))) {
return rv;
}
}
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnServiceLost(nsIDNSServiceInfo* aServiceInfo)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aServiceInfo)) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
nsAutoCString serviceName;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) {
return rv;
}
LOG_I("OnServiceLost: %s", serviceName.get());
nsAutoCString host;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) {
return rv;
}
uint32_t index;
if (!FindDeviceById(host, index)) {
// given device was not found
return NS_OK;
}
if (NS_WARN_IF(NS_FAILED(rv = RemoveDevice(index)))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnStartDiscoveryFailed(const nsACString& aServiceType,
int32_t aErrorCode)
{
LOG_E("OnStartDiscoveryFailed: %d", aErrorCode);
MOZ_ASSERT(NS_IsMainThread());
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnStopDiscoveryFailed(const nsACString& aServiceType,
int32_t aErrorCode)
{
LOG_E("OnStopDiscoveryFailed: %d", aErrorCode);
MOZ_ASSERT(NS_IsMainThread());
return NS_OK;
}
// nsIDNSServiceResolveListener
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aServiceInfo)) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
nsAutoCString serviceName;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) {
return rv;
}
LOG_I("OnServiceResolved: %s", serviceName.get());
nsAutoCString host;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) {
return rv;
}
nsAutoCString address;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetAddress(address)))) {
return rv;
}
uint16_t port;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetPort(&port)))) {
return rv;
}
nsAutoCString serviceType;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceType(serviceType)))) {
return rv;
}
uint32_t index;
if (FindDeviceById(host, index)) {
return UpdateDevice(index,
serviceName,
serviceType,
address,
port);
} else {
return AddDevice(host,
serviceName,
serviceType,
address,
port);
}
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo,
int32_t aErrorCode)
{
LOG_E("OnResolveFailed: %d", aErrorCode);
MOZ_ASSERT(NS_IsMainThread());
return NS_OK;
}
// nsIObserver
NS_IMETHODIMP
LegacyMDNSDeviceProvider::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ConvertUTF16toUTF8 data(aData);
LOG_I("Observe: topic = %s, data = %s", aTopic, data.get());
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_LEGACY)) {
OnDiscoveryChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERY_LEGACY));
} else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS)) {
OnDiscoveryTimeoutChanged(Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS));
} else if (data.EqualsLiteral(PREF_PRESENTATION_DEVICE_NAME)) {
nsAdoptingCString newServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME);
if (!mServiceName.Equals(newServiceName)) {
OnServiceNameChanged(newServiceName);
}
}
} else if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
StopDiscovery(NS_OK);
}
return NS_OK;
}
nsresult
LegacyMDNSDeviceProvider::OnDiscoveryChanged(bool aEnabled)
{
LOG_I("DiscoveryEnabled = %d\n", aEnabled);
MOZ_ASSERT(NS_IsMainThread());
mDiscoveryEnabled = aEnabled;
if (mDiscoveryEnabled) {
return ForceDiscovery();
}
return StopDiscovery(NS_OK);
}
nsresult
LegacyMDNSDeviceProvider::OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs)
{
LOG_I("OnDiscoveryTimeoutChanged = %d\n", aTimeoutMs);
MOZ_ASSERT(NS_IsMainThread());
mDiscoveryTimeoutMs = aTimeoutMs;
return NS_OK;
}
nsresult
LegacyMDNSDeviceProvider::OnServiceNameChanged(const nsACString& aServiceName)
{
LOG_I("serviceName = %s\n", PromiseFlatCString(aServiceName).get());
MOZ_ASSERT(NS_IsMainThread());
mServiceName = aServiceName;
mPresentationService->SetId(mServiceName);
return NS_OK;
}
// LegacyMDNSDeviceProvider::Device
NS_IMPL_ISUPPORTS(LegacyMDNSDeviceProvider::Device,
nsIPresentationDevice)
// nsIPresentationDevice
NS_IMETHODIMP
LegacyMDNSDeviceProvider::Device::GetId(nsACString& aId)
{
aId = mId;
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::Device::GetName(nsACString& aName)
{
aName = mName;
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::Device::GetType(nsACString& aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::Device::EstablishControlChannel(
nsIPresentationControlChannel** aRetVal)
{
if (!mProvider) {
return NS_ERROR_FAILURE;
}
return mProvider->Connect(this, aRetVal);
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::Device::Disconnect()
{
// No need to do anything when disconnect.
return NS_OK;
}
NS_IMETHODIMP
LegacyMDNSDeviceProvider::Device::IsRequestedUrlSupported(
const nsAString& aRequestedUrl,
bool* aRetVal)
{
if (!aRetVal) {
return NS_ERROR_INVALID_POINTER;
}
// Legacy TV 2.5 device only support a fixed set of presentation Apps.
*aRetVal = DeviceProviderHelpers::IsFxTVSupportedAppUrl(aRequestedUrl);
return NS_OK;
}
} // namespace legacy
} // namespace presentation
} // namespace dom
} // namespace mozilla

View file

@ -1,191 +0,0 @@
/* -*- 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/. */
#ifndef mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h
#define mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsICancelable.h"
#include "nsIDNSServiceDiscovery.h"
#include "nsIObserver.h"
#include "nsIPresentationDevice.h"
#include "nsIPresentationDeviceProvider.h"
#include "nsIPresentationControlService.h"
#include "nsITimer.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakPtr.h"
namespace mozilla {
namespace dom {
namespace presentation {
namespace legacy {
class DNSServiceWrappedListener;
class MulticastDNSService;
class LegacyMDNSDeviceProvider final
: public nsIPresentationDeviceProvider
, public nsIDNSServiceDiscoveryListener
, public nsIDNSServiceResolveListener
, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONDEVICEPROVIDER
NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER
NS_DECL_NSIDNSSERVICERESOLVELISTENER
NS_DECL_NSIOBSERVER
explicit LegacyMDNSDeviceProvider() = default;
nsresult Init();
nsresult Uninit();
private:
enum class DeviceState : uint32_t {
eUnknown,
eActive
};
class Device final : public nsIPresentationDevice
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONDEVICE
explicit Device(const nsACString& aId,
const nsACString& aName,
const nsACString& aType,
const nsACString& aAddress,
const uint16_t aPort,
DeviceState aState,
LegacyMDNSDeviceProvider* aProvider)
: mId(aId)
, mName(aName)
, mType(aType)
, mAddress(aAddress)
, mPort(aPort)
, mState(aState)
, mProvider(aProvider)
{
}
const nsCString& Id() const
{
return mId;
}
const nsCString& Address() const
{
return mAddress;
}
uint16_t Port() const
{
return mPort;
}
DeviceState State() const
{
return mState;
}
void ChangeState(DeviceState aState)
{
mState = aState;
}
void Update(const nsACString& aName,
const nsACString& aType,
const nsACString& aAddress,
const uint16_t aPort)
{
mName = aName;
mType = aType;
mAddress = aAddress;
mPort = aPort;
}
private:
virtual ~Device() = default;
nsCString mId;
nsCString mName;
nsCString mType;
nsCString mAddress;
uint16_t mPort;
DeviceState mState;
LegacyMDNSDeviceProvider* mProvider;
};
struct DeviceIdComparator {
bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const {
return aA->Id() == aB->Id();
}
};
struct DeviceAddressComparator {
bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const {
return aA->Address() == aB->Address();
}
};
virtual ~LegacyMDNSDeviceProvider();
nsresult StopDiscovery(nsresult aReason);
nsresult Connect(Device* aDevice,
nsIPresentationControlChannel** aRetVal);
// device manipulation
nsresult AddDevice(const nsACString& aId,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort);
nsresult UpdateDevice(const uint32_t aIndex,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort);
nsresult RemoveDevice(const uint32_t aIndex);
bool FindDeviceById(const nsACString& aId,
uint32_t& aIndex);
bool FindDeviceByAddress(const nsACString& aAddress,
uint32_t& aIndex);
void MarkAllDevicesUnknown();
void ClearUnknownDevices();
void ClearDevices();
// preferences
nsresult OnDiscoveryChanged(bool aEnabled);
nsresult OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs);
nsresult OnServiceNameChanged(const nsACString& aServiceName);
bool mInitialized = false;
nsWeakPtr mDeviceListener;
nsCOMPtr<nsIPresentationControlService> mPresentationService;
nsCOMPtr<nsIDNSServiceDiscovery> mMulticastDNS;
RefPtr<DNSServiceWrappedListener> mWrappedListener;
nsCOMPtr<nsICancelable> mDiscoveryRequest;
nsTArray<RefPtr<Device>> mDevices;
bool mDiscoveryEnabled = false;
bool mIsDiscovering = false;
uint32_t mDiscoveryTimeoutMs;
nsCOMPtr<nsITimer> mDiscoveryTimer;
nsCString mServiceName;
};
} // namespace legacy
} // namespace presentation
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h

View file

@ -1,488 +0,0 @@
/* 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/. */
/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
/* globals Components, dump */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/* globals XPCOMUtils */
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/* globals Services */
Cu.import("resource://gre/modules/Services.jsm");
/* globals NetUtil */
Cu.import("resource://gre/modules/NetUtil.jsm");
const DEBUG = Services.prefs.getBoolPref("dom.presentation.tcp_server.debug");
function log(aMsg) {
dump("-*- LegacyPresentationControlService.js: " + aMsg + "\n");
}
function LegacyPresentationControlService() {
DEBUG && log("LegacyPresentationControlService - ctor"); //jshint ignore:line
this._id = null;
}
LegacyPresentationControlService.prototype = {
startServer: function() {
DEBUG && log("LegacyPresentationControlService - doesn't support receiver mode"); //jshint ignore:line
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
get id() {
return this._id;
},
set id(aId) {
this._id = aId;
},
get port() {
return 0;
},
get version() {
return 0;
},
set listener(aListener) { //jshint ignore:line
DEBUG && log("LegacyPresentationControlService - doesn't support receiver mode"); //jshint ignore:line
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
get listener() {
return null;
},
connect: function(aDeviceInfo) {
if (!this.id) {
DEBUG && log("LegacyPresentationControlService - Id has not initialized; requestSession fails"); //jshint ignore:line
return null;
}
DEBUG && log("LegacyPresentationControlService - requestSession to " + aDeviceInfo.id); //jshint ignore:line
let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
.getService(Ci.nsISocketTransportService);
let socketTransport;
try {
socketTransport = sts.createTransport(null,
0,
aDeviceInfo.address,
aDeviceInfo.port,
null);
} catch (e) {
DEBUG && log("LegacyPresentationControlService - createTransport throws: " + e); //jshint ignore:line
// Pop the exception to |TCPDevice.establishControlChannel|
throw Cr.NS_ERROR_FAILURE;
}
return new LegacyTCPControlChannel(this.id,
socketTransport,
aDeviceInfo);
},
close: function() {
DEBUG && log("LegacyPresentationControlService - close"); //jshint ignore:line
},
classID: Components.ID("{b21816fe-8aff-4811-86d2-85a7444c557e}"),
QueryInterface : XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
};
function ChannelDescription(aInit) {
this._type = aInit.type;
switch (this._type) {
case Ci.nsIPresentationChannelDescription.TYPE_TCP:
this._tcpAddresses = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
for (let address of aInit.tcpAddress) {
let wrapper = Cc["@mozilla.org/supports-cstring;1"]
.createInstance(Ci.nsISupportsCString);
wrapper.data = address;
this._tcpAddresses.appendElement(wrapper, false);
}
this._tcpPort = aInit.tcpPort;
break;
case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
this._dataChannelSDP = aInit.dataChannelSDP;
break;
}
}
ChannelDescription.prototype = {
_type: 0,
_tcpAddresses: null,
_tcpPort: 0,
_dataChannelSDP: "",
get type() {
return this._type;
},
get tcpAddress() {
return this._tcpAddresses;
},
get tcpPort() {
return this._tcpPort;
},
get dataChannelSDP() {
return this._dataChannelSDP;
},
classID: Components.ID("{d69fc81c-4f40-47a3-97e6-b4cf5db2294e}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
};
// Helper function: transfer nsIPresentationChannelDescription to json
function discriptionAsJson(aDescription) {
let json = {};
json.type = aDescription.type;
switch(aDescription.type) {
case Ci.nsIPresentationChannelDescription.TYPE_TCP:
let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray);
json.tcpAddress = [];
for (let idx = 0; idx < addresses.length; idx++) {
let address = addresses.queryElementAt(idx, Ci.nsISupportsCString);
json.tcpAddress.push(address.data);
}
json.tcpPort = aDescription.tcpPort;
break;
case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
json.dataChannelSDP = aDescription.dataChannelSDP;
break;
}
return json;
}
function LegacyTCPControlChannel(id,
transport,
deviceInfo) {
DEBUG && log("create LegacyTCPControlChannel"); //jshint ignore:line
this._deviceInfo = deviceInfo;
this._transport = transport;
this._id = id;
let currentThread = Services.tm.currentThread;
transport.setEventSink(this, currentThread);
this._input = this._transport.openInputStream(0, 0, 0)
.QueryInterface(Ci.nsIAsyncInputStream);
this._input.asyncWait(this.QueryInterface(Ci.nsIStreamListener),
Ci.nsIAsyncInputStream.WAIT_CLOSURE_ONLY,
0,
currentThread);
this._output = this._transport
.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
}
LegacyTCPControlChannel.prototype = {
_connected: false,
_pendingOpen: false,
_pendingAnswer: null,
_pendingClose: null,
_pendingCloseReason: null,
_sendMessage: function(aJSONData, aOnThrow) {
if (!aOnThrow) {
aOnThrow = function(e) {throw e.result;};
}
if (!aJSONData) {
aOnThrow();
return;
}
if (!this._connected) {
DEBUG && log("LegacyTCPControlChannel - send" + aJSONData.type + " fails"); //jshint ignore:line
throw Cr.NS_ERROR_FAILURE;
}
try {
this._send(aJSONData);
} catch (e) {
aOnThrow(e);
}
},
_sendInit: function() {
let msg = {
type: "requestSession:Init",
presentationId: this._presentationId,
url: this._url,
id: this._id,
};
this._sendMessage(msg, function(e) {
this.disconnect();
this._notifyDisconnected(e.result);
});
},
launch: function(aPresentationId, aUrl) {
this._presentationId = aPresentationId;
this._url = aUrl;
this._sendInit();
},
terminate: function() {
// Legacy protocol doesn't support extra terminate protocol.
// Trigger error handling for browser to shutdown all the resource locally.
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
sendOffer: function(aOffer) {
let msg = {
type: "requestSession:Offer",
presentationId: this._presentationId,
offer: discriptionAsJson(aOffer),
};
this._sendMessage(msg);
},
sendAnswer: function(aAnswer) { //jshint ignore:line
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
sendIceCandidate: function(aCandidate) {
let msg = {
type: "requestSession:IceCandidate",
presentationId: this._presentationId,
iceCandidate: aCandidate,
};
this._sendMessage(msg);
},
// may throw an exception
_send: function(aMsg) {
DEBUG && log("LegacyTCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2)); //jshint ignore:line
/**
* XXX In TCP streaming, it is possible that more than one message in one
* TCP packet. We use line delimited JSON to identify where one JSON encoded
* object ends and the next begins. Therefore, we do not allow newline
* characters whithin the whole message, and add a newline at the end.
* Please see the parser code in |onDataAvailable|.
*/
let message = JSON.stringify(aMsg).replace(["\n"], "") + "\n";
try {
this._output.write(message, message.length);
} catch(e) {
DEBUG && log("LegacyTCPControlChannel - Failed to send message: " + e.name); //jshint ignore:line
throw e;
}
},
// nsIAsyncInputStream (Triggered by nsIInputStream.asyncWait)
// Only used for detecting connection refused
onInputStreamReady: function(aStream) {
try {
aStream.available();
} catch (e) {
DEBUG && log("LegacyTCPControlChannel - onInputStreamReady error: " + e.name); //jshint ignore:line
// NS_ERROR_CONNECTION_REFUSED
this._listener.notifyDisconnected(e.result);
}
},
// nsITransportEventSink (Triggered by nsISocketTransport.setEventSink)
onTransportStatus: function(aTransport, aStatus, aProg, aProgMax) { //jshint ignore:line
DEBUG && log("LegacyTCPControlChannel - onTransportStatus: "
+ aStatus.toString(16)); //jshint ignore:line
if (aStatus === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
this._connected = true;
if (!this._pump) {
this._createInputStreamPump();
}
this._notifyConnected();
}
},
// nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
onStartRequest: function() {
DEBUG && log("LegacyTCPControlChannel - onStartRequest"); //jshint ignore:line
},
// nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
onStopRequest: function(aRequest, aContext, aStatus) {
DEBUG && log("LegacyTCPControlChannel - onStopRequest: " + aStatus); //jshint ignore:line
this.disconnect(aStatus);
this._notifyDisconnected(aStatus);
},
// nsIStreamListener (Triggered by nsIInputStreamPump.asyncRead)
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) { //jshint ignore:line
let data = NetUtil.readInputStreamToString(aInputStream,
aInputStream.available());
DEBUG && log("LegacyTCPControlChannel - onDataAvailable: " + data); //jshint ignore:line
// Parser of line delimited JSON. Please see |_send| for more informaiton.
let jsonArray = data.split("\n");
jsonArray.pop();
for (let json of jsonArray) {
let msg;
try {
msg = JSON.parse(json);
} catch (e) {
DEBUG && log("LegacyTCPSignalingChannel - error in parsing json: " + e); //jshint ignore:line
}
this._handleMessage(msg);
}
},
_createInputStreamPump: function() {
DEBUG && log("LegacyTCPControlChannel - create pump"); //jshint ignore:line
this._pump = Cc["@mozilla.org/network/input-stream-pump;1"].
createInstance(Ci.nsIInputStreamPump);
this._pump.init(this._input, -1, -1, 0, 0, false);
this._pump.asyncRead(this, null);
},
// Handle command from remote side
_handleMessage: function(aMsg) {
DEBUG && log("LegacyTCPControlChannel - handleMessage from "
+ JSON.stringify(this._deviceInfo) + ": " + JSON.stringify(aMsg)); //jshint ignore:line
switch (aMsg.type) {
case "requestSession:Answer": {
this._onAnswer(aMsg.answer);
break;
}
case "requestSession:IceCandidate": {
this._listener.onIceCandidate(aMsg.iceCandidate);
break;
}
case "requestSession:CloseReason": {
this._pendingCloseReason = aMsg.reason;
break;
}
}
},
get listener() {
return this._listener;
},
set listener(aListener) {
DEBUG && log("LegacyTCPControlChannel - set listener: " + aListener); //jshint ignore:line
if (!aListener) {
this._listener = null;
return;
}
this._listener = aListener;
if (this._pendingOpen) {
this._pendingOpen = false;
DEBUG && log("LegacyTCPControlChannel - notify pending opened"); //jshint ignore:line
this._listener.notifyConnected();
}
if (this._pendingAnswer) {
let answer = this._pendingAnswer;
DEBUG && log("LegacyTCPControlChannel - notify pending answer: " +
JSON.stringify(answer)); // jshint ignore:line
this._listener.onAnswer(new ChannelDescription(answer));
this._pendingAnswer = null;
}
if (this._pendingClose) {
DEBUG && log("LegacyTCPControlChannel - notify pending closed"); //jshint ignore:line
this._notifyDisconnected(this._pendingCloseReason);
this._pendingClose = null;
}
},
/**
* These functions are designed to handle the interaction with listener
* appropriately. |_FUNC| is to handle |this._listener.FUNC|.
*/
_onAnswer: function(aAnswer) {
if (!this._connected) {
return;
}
if (!this._listener) {
this._pendingAnswer = aAnswer;
return;
}
DEBUG && log("LegacyTCPControlChannel - notify answer: " + JSON.stringify(aAnswer)); //jshint ignore:line
this._listener.onAnswer(new ChannelDescription(aAnswer));
},
_notifyConnected: function() {
this._connected = true;
this._pendingClose = false;
this._pendingCloseReason = Cr.NS_OK;
if (!this._listener) {
this._pendingOpen = true;
return;
}
DEBUG && log("LegacyTCPControlChannel - notify opened"); //jshint ignore:line
this._listener.notifyConnected();
},
_notifyDisconnected: function(aReason) {
this._connected = false;
this._pendingOpen = false;
this._pendingAnswer = null;
// Remote endpoint closes the control channel with abnormal reason.
if (aReason == Cr.NS_OK && this._pendingCloseReason != Cr.NS_OK) {
aReason = this._pendingCloseReason;
}
if (!this._listener) {
this._pendingClose = true;
this._pendingCloseReason = aReason;
return;
}
DEBUG && log("LegacyTCPControlChannel - notify closed"); //jshint ignore:line
this._listener.notifyDisconnected(aReason);
},
disconnect: function(aReason) {
DEBUG && log("LegacyTCPControlChannel - close with reason: " + aReason); //jshint ignore:line
if (this._connected) {
// default reason is NS_OK
if (typeof aReason !== "undefined" && aReason !== Cr.NS_OK) {
let msg = {
type: "requestSession:CloseReason",
presentationId: this._presentationId,
reason: aReason,
};
this._sendMessage(msg);
this._pendingCloseReason = aReason;
}
this._transport.setEventSink(null, null);
this._pump = null;
this._input.close();
this._output.close();
this._connected = false;
}
},
reconnect: function() {
// Legacy protocol doesn't support extra reconnect protocol.
// Trigger error handling for browser to shutdown all the resource locally.
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
classID: Components.ID("{4027ce3d-06e3-4d06-a235-df329cb0d411}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel,
Ci.nsIStreamListener]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([LegacyPresentationControlService]); //jshint ignore:line

View file

@ -1,2 +0,0 @@
component {b21816fe-8aff-4811-86d2-85a7444c557e} LegacyPresentationControlService.js
contract @mozilla.org/presentation/legacy-control-service;1 {b21816fe-8aff-4811-86d2-85a7444c557e}

File diff suppressed because it is too large Load diff

View file

@ -1,225 +0,0 @@
/* -*- 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/. */
#ifndef mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h
#define mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsICancelable.h"
#include "nsIDNSServiceDiscovery.h"
#include "nsIObserver.h"
#include "nsIPresentationDevice.h"
#include "nsIPresentationDeviceProvider.h"
#include "nsIPresentationControlService.h"
#include "nsITimer.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakPtr.h"
class nsITCPDeviceInfo;
namespace mozilla {
namespace dom {
namespace presentation {
class DNSServiceWrappedListener;
class MulticastDNSService;
class MulticastDNSDeviceProvider final
: public nsIPresentationDeviceProvider
, public nsIDNSServiceDiscoveryListener
, public nsIDNSRegistrationListener
, public nsIDNSServiceResolveListener
, public nsIPresentationControlServerListener
, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONDEVICEPROVIDER
NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER
NS_DECL_NSIDNSREGISTRATIONLISTENER
NS_DECL_NSIDNSSERVICERESOLVELISTENER
NS_DECL_NSIPRESENTATIONCONTROLSERVERLISTENER
NS_DECL_NSIOBSERVER
explicit MulticastDNSDeviceProvider() = default;
nsresult Init();
nsresult Uninit();
private:
enum class DeviceState : uint32_t {
eUnknown,
eActive
};
class Device final : public nsIPresentationDevice
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONDEVICE
explicit Device(const nsACString& aId,
const nsACString& aName,
const nsACString& aType,
const nsACString& aAddress,
const uint16_t aPort,
const nsACString& aCertFingerprint,
DeviceState aState,
MulticastDNSDeviceProvider* aProvider)
: mId(aId)
, mName(aName)
, mType(aType)
, mAddress(aAddress)
, mPort(aPort)
, mCertFingerprint(aCertFingerprint)
, mState(aState)
, mProvider(aProvider)
{
}
const nsCString& Id() const
{
return mId;
}
const nsCString& Address() const
{
return mAddress;
}
uint16_t Port() const
{
return mPort;
}
const nsCString& CertFingerprint() const
{
return mCertFingerprint;
}
DeviceState State() const
{
return mState;
}
void ChangeState(DeviceState aState)
{
mState = aState;
}
void Update(const nsACString& aName,
const nsACString& aType,
const nsACString& aAddress,
const uint16_t aPort,
const nsACString& aCertFingerprint)
{
mName = aName;
mType = aType;
mAddress = aAddress;
mPort = aPort;
mCertFingerprint = aCertFingerprint;
}
private:
virtual ~Device() = default;
nsCString mId;
nsCString mName;
nsCString mType;
nsCString mAddress;
uint16_t mPort;
nsCString mCertFingerprint;
DeviceState mState;
MulticastDNSDeviceProvider* mProvider;
};
struct DeviceIdComparator {
bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const {
return aA->Id() == aB->Id();
}
};
struct DeviceAddressComparator {
bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const {
return aA->Address() == aB->Address();
}
};
virtual ~MulticastDNSDeviceProvider();
nsresult StartServer();
nsresult StopServer();
void AbortServerRetry();
nsresult RegisterMDNSService();
nsresult UnregisterMDNSService(nsresult aReason);
nsresult StopDiscovery(nsresult aReason);
nsresult Connect(Device* aDevice,
nsIPresentationControlChannel** aRetVal);
bool IsCompatibleServer(nsIDNSServiceInfo* aServiceInfo);
// device manipulation
nsresult AddDevice(const nsACString& aId,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort,
const nsACString& aCertFingerprint);
nsresult UpdateDevice(const uint32_t aIndex,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort,
const nsACString& aCertFingerprint);
nsresult RemoveDevice(const uint32_t aIndex);
bool FindDeviceById(const nsACString& aId,
uint32_t& aIndex);
bool FindDeviceByAddress(const nsACString& aAddress,
uint32_t& aIndex);
already_AddRefed<Device>
GetOrCreateDevice(nsITCPDeviceInfo* aDeviceInfo);
void MarkAllDevicesUnknown();
void ClearUnknownDevices();
void ClearDevices();
// preferences
nsresult OnDiscoveryChanged(bool aEnabled);
nsresult OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs);
nsresult OnDiscoverableChanged(bool aEnabled);
nsresult OnServiceNameChanged(const nsACString& aServiceName);
bool mInitialized = false;
nsWeakPtr mDeviceListener;
nsCOMPtr<nsIPresentationControlService> mPresentationService;
nsCOMPtr<nsIDNSServiceDiscovery> mMulticastDNS;
RefPtr<DNSServiceWrappedListener> mWrappedListener;
nsCOMPtr<nsICancelable> mDiscoveryRequest;
nsCOMPtr<nsICancelable> mRegisterRequest;
nsTArray<RefPtr<Device>> mDevices;
bool mDiscoveryEnabled = false;
bool mIsDiscovering = false;
uint32_t mDiscoveryTimeoutMs;
nsCOMPtr<nsITimer> mDiscoveryTimer;
bool mDiscoverable = false;
bool mDiscoverableEncrypted = false;
bool mIsServerRetrying = false;
uint32_t mServerRetryMs;
nsCOMPtr<nsITimer> mServerRetryTimer;
nsCString mServiceName;
nsCString mRegisteredName;
};
} // namespace presentation
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h

View file

@ -1,960 +0,0 @@
/* 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/. */
/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
/* globals Components, dump */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/* globals XPCOMUtils */
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/* globals Services */
Cu.import("resource://gre/modules/Services.jsm");
/* globals NetUtil */
Cu.import("resource://gre/modules/NetUtil.jsm");
/* globals setTimeout, clearTimeout */
Cu.import("resource://gre/modules/Timer.jsm");
/* globals ControllerStateMachine */
XPCOMUtils.defineLazyModuleGetter(this, "ControllerStateMachine", // jshint ignore:line
"resource://gre/modules/presentation/ControllerStateMachine.jsm");
/* global ReceiverStateMachine */
XPCOMUtils.defineLazyModuleGetter(this, "ReceiverStateMachine", // jshint ignore:line
"resource://gre/modules/presentation/ReceiverStateMachine.jsm");
const kProtocolVersion = 1; // need to review isCompatibleServer while fiddling the version number.
const kLocalCertName = "presentation";
const DEBUG = Services.prefs.getBoolPref("dom.presentation.tcp_server.debug");
function log(aMsg) {
dump("-*- PresentationControlService.js: " + aMsg + "\n");
}
function TCPDeviceInfo(aAddress, aPort, aId, aCertFingerprint) {
this.address = aAddress;
this.port = aPort;
this.id = aId;
this.certFingerprint = aCertFingerprint || "";
}
function PresentationControlService() {
this._id = null;
this._port = 0;
this._serverSocket = null;
}
PresentationControlService.prototype = {
/**
* If a user agent connects to this server, we create a control channel but
* hand it to |TCPDevice.listener| when the initial information exchange
* finishes. Therefore, we hold the control channels in this period.
*/
_controlChannels: [],
startServer: function(aEncrypted, aPort) {
if (this._isServiceInit()) {
DEBUG && log("PresentationControlService - server socket has been initialized"); // jshint ignore:line
throw Cr.NS_ERROR_FAILURE;
}
/**
* 0 or undefined indicates opt-out parameter, and a port will be selected
* automatically.
*/
let serverSocketPort = (typeof aPort !== "undefined" && aPort !== 0) ? aPort : -1;
if (aEncrypted) {
let self = this;
let localCertService = Cc["@mozilla.org/security/local-cert-service;1"]
.getService(Ci.nsILocalCertService);
localCertService.getOrCreateCert(kLocalCertName, {
handleCert: function(aCert, aRv) {
DEBUG && log("PresentationControlService - handleCert"); // jshint ignore:line
if (aRv) {
self._notifyServerStopped(aRv);
} else {
self._serverSocket = Cc["@mozilla.org/network/tls-server-socket;1"]
.createInstance(Ci.nsITLSServerSocket);
self._serverSocketInit(serverSocketPort, aCert);
}
}
});
} else {
this._serverSocket = Cc["@mozilla.org/network/server-socket;1"]
.createInstance(Ci.nsIServerSocket);
this._serverSocketInit(serverSocketPort, null);
}
},
_serverSocketInit: function(aPort, aCert) {
if (!this._serverSocket) {
DEBUG && log("PresentationControlService - create server socket fail."); // jshint ignore:line
throw Cr.NS_ERROR_FAILURE;
}
try {
this._serverSocket.init(aPort, false, -1);
if (aCert) {
this._serverSocket.serverCert = aCert;
this._serverSocket.setSessionTickets(false);
let requestCert = Ci.nsITLSServerSocket.REQUEST_NEVER;
this._serverSocket.setRequestClientCertificate(requestCert);
}
this._serverSocket.asyncListen(this);
} catch (e) {
// NS_ERROR_SOCKET_ADDRESS_IN_USE
DEBUG && log("PresentationControlService - init server socket fail: " + e); // jshint ignore:line
throw Cr.NS_ERROR_FAILURE;
}
this._port = this._serverSocket.port;
DEBUG && log("PresentationControlService - service start on port: " + this._port); // jshint ignore:line
// Monitor network interface change to restart server socket.
Services.obs.addObserver(this, "network:offline-status-changed", false);
this._notifyServerReady();
},
_notifyServerReady: function() {
Services.tm.mainThread.dispatch(() => {
if (this._listener) {
this._listener.onServerReady(this._port, this.certFingerprint);
}
}, Ci.nsIThread.DISPATCH_NORMAL);
},
_notifyServerStopped: function(aRv) {
Services.tm.mainThread.dispatch(() => {
if (this._listener) {
this._listener.onServerStopped(aRv);
}
}, Ci.nsIThread.DISPATCH_NORMAL);
},
isCompatibleServer: function(aVersion) {
// No compatibility issue for the first version of control protocol
return this.version === aVersion;
},
get id() {
return this._id;
},
set id(aId) {
this._id = aId;
},
get port() {
return this._port;
},
get version() {
return kProtocolVersion;
},
get certFingerprint() {
if (!this._serverSocket.serverCert) {
return null;
}
return this._serverSocket.serverCert.sha256Fingerprint;
},
set listener(aListener) {
this._listener = aListener;
},
get listener() {
return this._listener;
},
_isServiceInit: function() {
return this._serverSocket !== null;
},
connect: function(aDeviceInfo) {
if (!this.id) {
DEBUG && log("PresentationControlService - Id has not initialized; connect fails"); // jshint ignore:line
return null;
}
DEBUG && log("PresentationControlService - connect to " + aDeviceInfo.id); // jshint ignore:line
let socketTransport = this._attemptConnect(aDeviceInfo);
return new TCPControlChannel(this,
socketTransport,
aDeviceInfo,
"sender");
},
_attemptConnect: function(aDeviceInfo) {
let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
.getService(Ci.nsISocketTransportService);
let socketTransport;
try {
if (aDeviceInfo.certFingerprint) {
let overrideService = Cc["@mozilla.org/security/certoverride;1"]
.getService(Ci.nsICertOverrideService);
overrideService.rememberTemporaryValidityOverrideUsingFingerprint(
aDeviceInfo.address,
aDeviceInfo.port,
aDeviceInfo.certFingerprint,
Ci.nsICertOverrideService.ERROR_UNTRUSTED | Ci.nsICertOverrideService.ERROR_MISMATCH);
socketTransport = sts.createTransport(["ssl"],
1,
aDeviceInfo.address,
aDeviceInfo.port,
null);
} else {
socketTransport = sts.createTransport(null,
0,
aDeviceInfo.address,
aDeviceInfo.port,
null);
}
// Shorten the connection failure procedure.
socketTransport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 2);
} catch (e) {
DEBUG && log("PresentationControlService - createTransport throws: " + e); // jshint ignore:line
// Pop the exception to |TCPDevice.establishControlChannel|
throw Cr.NS_ERROR_FAILURE;
}
return socketTransport;
},
responseSession: function(aDeviceInfo, aSocketTransport) {
if (!this._isServiceInit()) {
DEBUG && log("PresentationControlService - should never receive remote " +
"session request before server socket initialization"); // jshint ignore:line
return null;
}
DEBUG && log("PresentationControlService - responseSession to " +
JSON.stringify(aDeviceInfo)); // jshint ignore:line
return new TCPControlChannel(this,
aSocketTransport,
aDeviceInfo,
"receiver");
},
// Triggered by TCPControlChannel
onSessionRequest: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) {
DEBUG && log("PresentationControlService - onSessionRequest: " +
aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line
if (!this.listener) {
this.releaseControlChannel(aControlChannel);
return;
}
this.listener.onSessionRequest(aDeviceInfo,
aUrl,
aPresentationId,
aControlChannel);
this.releaseControlChannel(aControlChannel);
},
onSessionTerminate: function(aDeviceInfo, aPresentationId, aControlChannel, aIsFromReceiver) {
DEBUG && log("TCPPresentationServer - onSessionTerminate: " +
aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line
if (!this.listener) {
this.releaseControlChannel(aControlChannel);
return;
}
this.listener.onTerminateRequest(aDeviceInfo,
aPresentationId,
aControlChannel,
aIsFromReceiver);
this.releaseControlChannel(aControlChannel);
},
onSessionReconnect: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) {
DEBUG && log("TCPPresentationServer - onSessionReconnect: " +
aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line
if (!this.listener) {
this.releaseControlChannel(aControlChannel);
return;
}
this.listener.onReconnectRequest(aDeviceInfo,
aUrl,
aPresentationId,
aControlChannel);
this.releaseControlChannel(aControlChannel);
},
// nsIServerSocketListener (Triggered by nsIServerSocket.init)
onSocketAccepted: function(aServerSocket, aClientSocket) {
DEBUG && log("PresentationControlService - onSocketAccepted: " +
aClientSocket.host + ":" + aClientSocket.port); // jshint ignore:line
let deviceInfo = new TCPDeviceInfo(aClientSocket.host, aClientSocket.port);
this.holdControlChannel(this.responseSession(deviceInfo, aClientSocket));
},
holdControlChannel: function(aControlChannel) {
this._controlChannels.push(aControlChannel);
},
releaseControlChannel: function(aControlChannel) {
let index = this._controlChannels.indexOf(aControlChannel);
if (index !== -1) {
delete this._controlChannels[index];
}
},
// nsIServerSocketListener (Triggered by nsIServerSocket.init)
onStopListening: function(aServerSocket, aStatus) {
DEBUG && log("PresentationControlService - onStopListening: " + aStatus); // jshint ignore:line
},
close: function() {
DEBUG && log("PresentationControlService - close"); // jshint ignore:line
if (this._isServiceInit()) {
DEBUG && log("PresentationControlService - close server socket"); // jshint ignore:line
this._serverSocket.close();
this._serverSocket = null;
Services.obs.removeObserver(this, "network:offline-status-changed");
this._notifyServerStopped(Cr.NS_OK);
}
this._port = 0;
},
// nsIObserver
observe: function(aSubject, aTopic, aData) {
DEBUG && log("PresentationControlService - observe: " + aTopic); // jshint ignore:line
switch (aTopic) {
case "network:offline-status-changed": {
if (aData == "offline") {
DEBUG && log("network offline"); // jshint ignore:line
return;
}
this._restartServer();
break;
}
}
},
_restartServer: function() {
DEBUG && log("PresentationControlService - restart service"); // jshint ignore:line
// restart server socket
if (this._isServiceInit()) {
this.close();
try {
this.startServer();
} catch (e) {
DEBUG && log("PresentationControlService - restart service fail: " + e); // jshint ignore:line
}
}
},
classID: Components.ID("{f4079b8b-ede5-4b90-a112-5b415a931deb}"),
QueryInterface : XPCOMUtils.generateQI([Ci.nsIServerSocketListener,
Ci.nsIPresentationControlService,
Ci.nsIObserver]),
};
function ChannelDescription(aInit) {
this._type = aInit.type;
switch (this._type) {
case Ci.nsIPresentationChannelDescription.TYPE_TCP:
this._tcpAddresses = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
for (let address of aInit.tcpAddress) {
let wrapper = Cc["@mozilla.org/supports-cstring;1"]
.createInstance(Ci.nsISupportsCString);
wrapper.data = address;
this._tcpAddresses.appendElement(wrapper, false);
}
this._tcpPort = aInit.tcpPort;
break;
case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
this._dataChannelSDP = aInit.dataChannelSDP;
break;
}
}
ChannelDescription.prototype = {
_type: 0,
_tcpAddresses: null,
_tcpPort: 0,
_dataChannelSDP: "",
get type() {
return this._type;
},
get tcpAddress() {
return this._tcpAddresses;
},
get tcpPort() {
return this._tcpPort;
},
get dataChannelSDP() {
return this._dataChannelSDP;
},
classID: Components.ID("{82507aea-78a2-487e-904a-858a6c5bf4e1}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
};
// Helper function: transfer nsIPresentationChannelDescription to json
function discriptionAsJson(aDescription) {
let json = {};
json.type = aDescription.type;
switch(aDescription.type) {
case Ci.nsIPresentationChannelDescription.TYPE_TCP:
let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray);
json.tcpAddress = [];
for (let idx = 0; idx < addresses.length; idx++) {
let address = addresses.queryElementAt(idx, Ci.nsISupportsCString);
json.tcpAddress.push(address.data);
}
json.tcpPort = aDescription.tcpPort;
break;
case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
json.dataChannelSDP = aDescription.dataChannelSDP;
break;
}
return json;
}
const kDisconnectTimeout = 5000;
const kTerminateTimeout = 5000;
function TCPControlChannel(presentationService,
transport,
deviceInfo,
direction) {
DEBUG && log("create TCPControlChannel for : " + direction); // jshint ignore:line
this._deviceInfo = deviceInfo;
this._direction = direction;
this._transport = transport;
this._presentationService = presentationService;
if (direction === "receiver") {
// Need to set security observer before I/O stream operation.
this._setSecurityObserver(this);
}
let currentThread = Services.tm.currentThread;
transport.setEventSink(this, currentThread);
this._input = this._transport.openInputStream(0, 0, 0)
.QueryInterface(Ci.nsIAsyncInputStream);
this._input.asyncWait(this.QueryInterface(Ci.nsIStreamListener),
Ci.nsIAsyncInputStream.WAIT_CLOSURE_ONLY,
0,
currentThread);
this._output = this._transport
.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0)
.QueryInterface(Ci.nsIAsyncOutputStream);
this._outgoingMsgs = [];
this._stateMachine =
(direction === "sender") ? new ControllerStateMachine(this, presentationService.id)
: new ReceiverStateMachine(this);
if (direction === "receiver" && !transport.securityInfo) {
// Since the transport created by server socket is already CONNECTED_TO.
this._outgoingEnabled = true;
this._createInputStreamPump();
}
}
TCPControlChannel.prototype = {
_outgoingEnabled: false,
_incomingEnabled: false,
_pendingOpen: false,
_pendingOffer: null,
_pendingAnswer: null,
_pendingClose: null,
_pendingCloseReason: null,
_pendingReconnect: false,
sendOffer: function(aOffer) {
this._stateMachine.sendOffer(discriptionAsJson(aOffer));
},
sendAnswer: function(aAnswer) {
this._stateMachine.sendAnswer(discriptionAsJson(aAnswer));
},
sendIceCandidate: function(aCandidate) {
this._stateMachine.updateIceCandidate(aCandidate);
},
launch: function(aPresentationId, aUrl) {
this._stateMachine.launch(aPresentationId, aUrl);
},
terminate: function(aPresentationId) {
if (!this._terminatingId) {
this._terminatingId = aPresentationId;
this._stateMachine.terminate(aPresentationId);
// Start a guard timer to ensure terminateAck is processed.
this._terminateTimer = setTimeout(() => {
DEBUG && log("TCPControlChannel - terminate timeout: " + aPresentationId); // jshint ignore:line
delete this._terminateTimer;
if (this._pendingDisconnect) {
this._pendingDisconnect();
} else {
this.disconnect(Cr.NS_OK);
}
}, kTerminateTimeout);
} else {
this._stateMachine.terminateAck(aPresentationId);
delete this._terminatingId;
}
},
_flushOutgoing: function() {
if (!this._outgoingEnabled || this._outgoingMsgs.length === 0) {
return;
}
this._output.asyncWait(this, 0, 0, Services.tm.currentThread);
},
// may throw an exception
_send: function(aMsg) {
DEBUG && log("TCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2)); // jshint ignore:line
/**
* XXX In TCP streaming, it is possible that more than one message in one
* TCP packet. We use line delimited JSON to identify where one JSON encoded
* object ends and the next begins. Therefore, we do not allow newline
* characters whithin the whole message, and add a newline at the end.
* Please see the parser code in |onDataAvailable|.
*/
let message = JSON.stringify(aMsg).replace(["\n"], "") + "\n";
try {
this._output.write(message, message.length);
} catch(e) {
DEBUG && log("TCPControlChannel - Failed to send message: " + e.name); // jshint ignore:line
throw e;
}
},
_setSecurityObserver: function(observer) {
if (this._transport && this._transport.securityInfo) {
DEBUG && log("TCPControlChannel - setSecurityObserver: " + observer); // jshint ignore:line
let connectionInfo = this._transport.securityInfo
.QueryInterface(Ci.nsITLSServerConnectionInfo);
connectionInfo.setSecurityObserver(observer);
}
},
// nsITLSServerSecurityObserver
onHandshakeDone: function(socket, clientStatus) {
log("TCPControlChannel - onHandshakeDone: TLS version: " + clientStatus.tlsVersionUsed.toString(16));
this._setSecurityObserver(null);
// Process input/output after TLS handshake is complete.
this._outgoingEnabled = true;
this._createInputStreamPump();
},
// nsIAsyncOutputStream
onOutputStreamReady: function() {
DEBUG && log("TCPControlChannel - onOutputStreamReady"); // jshint ignore:line
if (this._outgoingMsgs.length === 0) {
return;
}
try {
this._send(this._outgoingMsgs[0]);
} catch (e) {
if (e.result === Cr.NS_BASE_STREAM_WOULD_BLOCK) {
this._output.asyncWait(this, 0, 0, Services.tm.currentThread);
return;
}
this._closeTransport();
return;
}
this._outgoingMsgs.shift();
this._flushOutgoing();
},
// nsIAsyncInputStream (Triggered by nsIInputStream.asyncWait)
// Only used for detecting connection refused
onInputStreamReady: function(aStream) {
DEBUG && log("TCPControlChannel - onInputStreamReady"); // jshint ignore:line
try {
aStream.available();
} catch (e) {
DEBUG && log("TCPControlChannel - onInputStreamReady error: " + e.name); // jshint ignore:line
// NS_ERROR_CONNECTION_REFUSED
this._notifyDisconnected(e.result);
}
},
// nsITransportEventSink (Triggered by nsISocketTransport.setEventSink)
onTransportStatus: function(aTransport, aStatus) {
DEBUG && log("TCPControlChannel - onTransportStatus: " + aStatus.toString(16) +
" with role: " + this._direction); // jshint ignore:line
if (aStatus === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
this._outgoingEnabled = true;
this._createInputStreamPump();
}
},
// nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
onStartRequest: function() {
DEBUG && log("TCPControlChannel - onStartRequest with role: " +
this._direction); // jshint ignore:line
this._incomingEnabled = true;
},
// nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
onStopRequest: function(aRequest, aContext, aStatus) {
DEBUG && log("TCPControlChannel - onStopRequest: " + aStatus +
" with role: " + this._direction); // jshint ignore:line
this._stateMachine.onChannelClosed(aStatus, true);
},
// nsIStreamListener (Triggered by nsIInputStreamPump.asyncRead)
onDataAvailable: function(aRequest, aContext, aInputStream) {
let data = NetUtil.readInputStreamToString(aInputStream,
aInputStream.available());
DEBUG && log("TCPControlChannel - onDataAvailable: " + data); // jshint ignore:line
// Parser of line delimited JSON. Please see |_send| for more informaiton.
let jsonArray = data.split("\n");
jsonArray.pop();
for (let json of jsonArray) {
let msg;
try {
msg = JSON.parse(json);
} catch (e) {
DEBUG && log("TCPSignalingChannel - error in parsing json: " + e); // jshint ignore:line
}
this._handleMessage(msg);
}
},
_createInputStreamPump: function() {
if (this._pump) {
return;
}
DEBUG && log("TCPControlChannel - create pump with role: " +
this._direction); // jshint ignore:line
this._pump = Cc["@mozilla.org/network/input-stream-pump;1"].
createInstance(Ci.nsIInputStreamPump);
this._pump.init(this._input, -1, -1, 0, 0, false);
this._pump.asyncRead(this, null);
this._stateMachine.onChannelReady();
},
// Handle command from remote side
_handleMessage: function(aMsg) {
DEBUG && log("TCPControlChannel - handleMessage from " +
JSON.stringify(this._deviceInfo) + ": " + JSON.stringify(aMsg)); // jshint ignore:line
this._stateMachine.onCommand(aMsg);
},
get listener() {
return this._listener;
},
set listener(aListener) {
DEBUG && log("TCPControlChannel - set listener: " + aListener); // jshint ignore:line
if (!aListener) {
this._listener = null;
return;
}
this._listener = aListener;
if (this._pendingOpen) {
this._pendingOpen = false;
DEBUG && log("TCPControlChannel - notify pending opened"); // jshint ignore:line
this._listener.notifyConnected();
}
if (this._pendingOffer) {
let offer = this._pendingOffer;
DEBUG && log("TCPControlChannel - notify pending offer: " +
JSON.stringify(offer)); // jshint ignore:line
this._listener.onOffer(new ChannelDescription(offer));
this._pendingOffer = null;
}
if (this._pendingAnswer) {
let answer = this._pendingAnswer;
DEBUG && log("TCPControlChannel - notify pending answer: " +
JSON.stringify(answer)); // jshint ignore:line
this._listener.onAnswer(new ChannelDescription(answer));
this._pendingAnswer = null;
}
if (this._pendingClose) {
DEBUG && log("TCPControlChannel - notify pending closed"); // jshint ignore:line
this._notifyDisconnected(this._pendingCloseReason);
this._pendingClose = null;
}
if (this._pendingReconnect) {
DEBUG && log("TCPControlChannel - notify pending reconnected"); // jshint ignore:line
this._notifyReconnected();
this._pendingReconnect = false;
}
},
/**
* These functions are designed to handle the interaction with listener
* appropriately. |_FUNC| is to handle |this._listener.FUNC|.
*/
_onOffer: function(aOffer) {
if (!this._incomingEnabled) {
return;
}
if (!this._listener) {
this._pendingOffer = aOffer;
return;
}
DEBUG && log("TCPControlChannel - notify offer: " +
JSON.stringify(aOffer)); // jshint ignore:line
this._listener.onOffer(new ChannelDescription(aOffer));
},
_onAnswer: function(aAnswer) {
if (!this._incomingEnabled) {
return;
}
if (!this._listener) {
this._pendingAnswer = aAnswer;
return;
}
DEBUG && log("TCPControlChannel - notify answer: " +
JSON.stringify(aAnswer)); // jshint ignore:line
this._listener.onAnswer(new ChannelDescription(aAnswer));
},
_notifyConnected: function() {
this._pendingClose = false;
this._pendingCloseReason = Cr.NS_OK;
if (!this._listener) {
this._pendingOpen = true;
return;
}
DEBUG && log("TCPControlChannel - notify opened with role: " +
this._direction); // jshint ignore:line
this._listener.notifyConnected();
},
_notifyDisconnected: function(aReason) {
this._pendingOpen = false;
this._pendingOffer = null;
this._pendingAnswer = null;
// Remote endpoint closes the control channel with abnormal reason.
if (aReason == Cr.NS_OK && this._pendingCloseReason != Cr.NS_OK) {
aReason = this._pendingCloseReason;
}
if (!this._listener) {
this._pendingClose = true;
this._pendingCloseReason = aReason;
return;
}
DEBUG && log("TCPControlChannel - notify closed with role: " +
this._direction); // jshint ignore:line
this._listener.notifyDisconnected(aReason);
},
_notifyReconnected: function() {
if (!this._listener) {
this._pendingReconnect = true;
return;
}
DEBUG && log("TCPControlChannel - notify reconnected with role: " +
this._direction); // jshint ignore:line
this._listener.notifyReconnected();
},
_closeOutgoing: function() {
if (this._outgoingEnabled) {
this._output.close();
this._outgoingEnabled = false;
}
},
_closeIncoming: function() {
if (this._incomingEnabled) {
this._pump = null;
this._input.close();
this._incomingEnabled = false;
}
},
_closeTransport: function() {
if (this._disconnectTimer) {
clearTimeout(this._disconnectTimer);
delete this._disconnectTimer;
}
if (this._terminateTimer) {
clearTimeout(this._terminateTimer);
delete this._terminateTimer;
}
delete this._pendingDisconnect;
this._transport.setEventSink(null, null);
this._closeIncoming();
this._closeOutgoing();
this._presentationService.releaseControlChannel(this);
},
disconnect: function(aReason) {
DEBUG && log("TCPControlChannel - disconnect with reason: " + aReason); // jshint ignore:line
// Pending disconnect during termination procedure.
if (this._terminateTimer) {
// Store only the first disconnect action.
if (!this._pendingDisconnect) {
this._pendingDisconnect = this.disconnect.bind(this, aReason);
}
return;
}
if (this._outgoingEnabled && !this._disconnectTimer) {
// default reason is NS_OK
aReason = !aReason ? Cr.NS_OK : aReason;
this._stateMachine.onChannelClosed(aReason, false);
// Start a guard timer to ensure the transport will be closed.
this._disconnectTimer = setTimeout(() => {
DEBUG && log("TCPControlChannel - disconnect timeout"); // jshint ignore:line
this._closeTransport();
}, kDisconnectTimeout);
}
},
reconnect: function(aPresentationId, aUrl) {
DEBUG && log("TCPControlChannel - reconnect with role: " +
this._direction); // jshint ignore:line
if (this._direction != "sender") {
return Cr.NS_ERROR_FAILURE;
}
this._stateMachine.reconnect(aPresentationId, aUrl);
},
// callback from state machine
sendCommand: function(command) {
this._outgoingMsgs.push(command);
this._flushOutgoing();
},
notifyDeviceConnected: function(deviceId) {
switch (this._direction) {
case "receiver":
this._deviceInfo.id = deviceId;
break;
}
this._notifyConnected();
},
notifyDisconnected: function(reason) {
this._closeTransport();
this._notifyDisconnected(reason);
},
notifyLaunch: function(presentationId, url) {
switch (this._direction) {
case "receiver":
this._presentationService.onSessionRequest(this._deviceInfo,
url,
presentationId,
this);
break;
}
},
notifyTerminate: function(presentationId) {
if (!this._terminatingId) {
this._terminatingId = presentationId;
this._presentationService.onSessionTerminate(this._deviceInfo,
presentationId,
this,
this._direction === "sender");
return;
}
// Cancel terminate guard timer after receiving terminate-ack.
if (this._terminateTimer) {
clearTimeout(this._terminateTimer);
delete this._terminateTimer;
}
if (this._terminatingId !== presentationId) {
// Requested presentation Id doesn't matched with the one in ACK.
// Disconnect the control channel with error.
DEBUG && log("TCPControlChannel - unmatched terminatingId: " + presentationId); // jshint ignore:line
this.disconnect(Cr.NS_ERROR_FAILURE);
}
delete this._terminatingId;
if (this._pendingDisconnect) {
this._pendingDisconnect();
}
},
notifyReconnect: function(presentationId, url) {
switch (this._direction) {
case "receiver":
this._presentationService.onSessionReconnect(this._deviceInfo,
url,
presentationId,
this);
break;
case "sender":
this._notifyReconnected();
break;
}
},
notifyOffer: function(offer) {
this._onOffer(offer);
},
notifyAnswer: function(answer) {
this._onAnswer(answer);
},
notifyIceCandidate: function(candidate) {
this._listener.onIceCandidate(candidate);
},
classID: Components.ID("{fefb8286-0bdc-488b-98bf-0c11b485c955}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel,
Ci.nsIStreamListener]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationControlService]); // jshint ignore:line

View file

@ -1,87 +0,0 @@
/* -*- 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 "DisplayDeviceProvider.h"
#include "MulticastDNSDeviceProvider.h"
#include "mozilla/ModuleUtils.h"
#ifdef MOZ_WIDGET_ANDROID
#include "LegacyMDNSDeviceProvider.h"
#endif //MOZ_WIDGET_ANDROID
#define MULTICAST_DNS_PROVIDER_CID \
{0x814f947a, 0x52f7, 0x41c9, \
{ 0x94, 0xa1, 0x36, 0x84, 0x79, 0x72, 0x84, 0xac }}
#define DISPLAY_DEVICE_PROVIDER_CID \
{ 0x515d9879, 0xfe0b, 0x4d9f, \
{ 0x89, 0x49, 0x7f, 0xa7, 0x65, 0x6c, 0x01, 0x0e } }
#ifdef MOZ_WIDGET_ANDROID
#define LEGACY_MDNS_PROVIDER_CID \
{ 0x6885ff39, 0xd98c, 0x4356, \
{ 0x9e, 0xb3, 0x56, 0x56, 0x31, 0x63, 0x0a, 0xf6 } }
#endif //MOZ_WIDGET_ANDROID
#define DISPLAY_DEVICE_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/displaydevice-provider;1"
#define MULTICAST_DNS_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/multicastdns-provider;1"
#ifdef MOZ_WIDGET_ANDROID
#define LEGACY_MDNS_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/legacy-mdns-provider;1"
#endif //MOZ_WIDGET_ANDROID
using mozilla::dom::presentation::MulticastDNSDeviceProvider;
using mozilla::dom::presentation::DisplayDeviceProvider;
#ifdef MOZ_WIDGET_ANDROID
using mozilla::dom::presentation::legacy::LegacyMDNSDeviceProvider;
#endif //MOZ_WIDGET_ANDROID
NS_GENERIC_FACTORY_CONSTRUCTOR(MulticastDNSDeviceProvider)
NS_DEFINE_NAMED_CID(MULTICAST_DNS_PROVIDER_CID);
NS_GENERIC_FACTORY_CONSTRUCTOR(DisplayDeviceProvider)
NS_DEFINE_NAMED_CID(DISPLAY_DEVICE_PROVIDER_CID);
#ifdef MOZ_WIDGET_ANDROID
NS_GENERIC_FACTORY_CONSTRUCTOR(LegacyMDNSDeviceProvider)
NS_DEFINE_NAMED_CID(LEGACY_MDNS_PROVIDER_CID);
#endif //MOZ_WIDGET_ANDROID
static const mozilla::Module::CIDEntry kPresentationDeviceProviderCIDs[] = {
{ &kMULTICAST_DNS_PROVIDER_CID, false, nullptr, MulticastDNSDeviceProviderConstructor },
{ &kDISPLAY_DEVICE_PROVIDER_CID, false, nullptr, DisplayDeviceProviderConstructor },
#ifdef MOZ_WIDGET_ANDROID
{ &kLEGACY_MDNS_PROVIDER_CID, false, nullptr, LegacyMDNSDeviceProviderConstructor },
#endif //MOZ_WIDGET_ANDROID
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kPresentationDeviceProviderContracts[] = {
{ MULTICAST_DNS_PROVIDER_CONTRACT_ID, &kMULTICAST_DNS_PROVIDER_CID },
{ DISPLAY_DEVICE_PROVIDER_CONTRACT_ID, &kDISPLAY_DEVICE_PROVIDER_CID },
#ifdef MOZ_WIDGET_ANDROID
{ LEGACY_MDNS_PROVIDER_CONTRACT_ID, &kLEGACY_MDNS_PROVIDER_CID },
#endif //MOZ_WIDGET_ANDROID
{ nullptr }
};
static const mozilla::Module::CategoryEntry kPresentationDeviceProviderCategories[] = {
#if defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_ANDROID)
{ PRESENTATION_DEVICE_PROVIDER_CATEGORY, "MulticastDNSDeviceProvider", MULTICAST_DNS_PROVIDER_CONTRACT_ID },
#endif
#ifdef MOZ_WIDGET_ANDROID
{ PRESENTATION_DEVICE_PROVIDER_CATEGORY, "LegacyMDNSDeviceProvider", LEGACY_MDNS_PROVIDER_CONTRACT_ID },
#endif //MOZ_WIDGET_ANDROID
{ nullptr }
};
static const mozilla::Module kPresentationDeviceProviderModule = {
mozilla::Module::kVersion,
kPresentationDeviceProviderCIDs,
kPresentationDeviceProviderContracts,
kPresentationDeviceProviderCategories
};
NSMODULE_DEFN(PresentationDeviceProviderModule) = &kPresentationDeviceProviderModule;

View file

@ -1,238 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
/* globals Components, dump */
"use strict";
this.EXPORTED_SYMBOLS = ["ReceiverStateMachine"]; // jshint ignore:line
const { utils: Cu } = Components;
/* globals State, CommandType */
Cu.import("resource://gre/modules/presentation/StateMachineHelper.jsm");
const DEBUG = false;
function debug(str) {
dump("-*- ReceiverStateMachine: " + str + "\n");
}
var handlers = [
function _initHandler(stateMachine, command) {
// shouldn't receive any command at init state
DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
},
function _connectingHandler(stateMachine, command) {
switch (command.type) {
case CommandType.CONNECT:
stateMachine._sendCommand({
type: CommandType.CONNECT_ACK
});
stateMachine.state = State.CONNECTED;
stateMachine._notifyDeviceConnected(command.deviceId);
break;
case CommandType.DISCONNECT:
stateMachine.state = State.CLOSED;
stateMachine._notifyDisconnected(command.reason);
break;
default:
debug("unexpected command: " + JSON.stringify(command));
// ignore unexpected command
break;
}
},
function _connectedHandler(stateMachine, command) {
switch (command.type) {
case CommandType.DISCONNECT:
stateMachine.state = State.CLOSED;
stateMachine._notifyDisconnected(command.reason);
break;
case CommandType.LAUNCH:
stateMachine._notifyLaunch(command.presentationId,
command.url);
stateMachine._sendCommand({
type: CommandType.LAUNCH_ACK,
presentationId: command.presentationId
});
break;
case CommandType.TERMINATE:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.TERMINATE_ACK:
stateMachine._notifyTerminate(command.presentationId);
break;
case CommandType.OFFER:
case CommandType.ICE_CANDIDATE:
stateMachine._notifyChannelDescriptor(command);
break;
case CommandType.RECONNECT:
stateMachine._notifyReconnect(command.presentationId,
command.url);
stateMachine._sendCommand({
type: CommandType.RECONNECT_ACK,
presentationId: command.presentationId
});
break;
default:
debug("unexpected command: " + JSON.stringify(command));
// ignore unexpected command
break;
}
},
function _closingHandler(stateMachine, command) {
switch (command.type) {
case CommandType.DISCONNECT:
stateMachine.state = State.CLOSED;
stateMachine._notifyDisconnected(command.reason);
break;
default:
debug("unexpected command: " + JSON.stringify(command));
// ignore unexpected command
break;
}
},
function _closedHandler(stateMachine, command) {
// ignore every command in closed state.
DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
},
];
function ReceiverStateMachine(channel) {
this.state = State.INIT;
this._channel = channel;
}
ReceiverStateMachine.prototype = {
launch: function _launch() {
// presentation session can only be launched by controlling UA.
debug("receiver shouldn't trigger launch");
},
terminate: function _terminate(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE,
presentationId: presentationId,
});
}
},
terminateAck: function _terminateAck(presentationId) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.TERMINATE_ACK,
presentationId: presentationId,
});
}
},
reconnect: function _reconnect() {
debug("receiver shouldn't trigger reconnect");
},
sendOffer: function _sendOffer() {
// offer can only be sent by controlling UA.
debug("receiver shouldn't generate offer");
},
sendAnswer: function _sendAnswer(answer) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.ANSWER,
answer: answer,
});
}
},
updateIceCandidate: function _updateIceCandidate(candidate) {
if (this.state === State.CONNECTED) {
this._sendCommand({
type: CommandType.ICE_CANDIDATE,
candidate: candidate,
});
}
},
onCommand: function _onCommand(command) {
handlers[this.state](this, command);
},
onChannelReady: function _onChannelReady() {
if (this.state === State.INIT) {
this.state = State.CONNECTING;
}
},
onChannelClosed: function _onChannelClose(reason, isByRemote) {
switch (this.state) {
case State.CONNECTED:
if (isByRemote) {
this.state = State.CLOSED;
this._notifyDisconnected(reason);
} else {
this._sendCommand({
type: CommandType.DISCONNECT,
reason: reason
});
this.state = State.CLOSING;
this._closeReason = reason;
}
break;
case State.CLOSING:
if (isByRemote) {
this.state = State.CLOSED;
if (this._closeReason) {
reason = this._closeReason;
delete this._closeReason;
}
this._notifyDisconnected(reason);
} else {
// do nothing and wait for remote channel closed.
}
break;
default:
DEBUG && debug("unexpected channel close: " + reason + ", " + isByRemote); // jshint ignore:line
break;
}
},
_sendCommand: function _sendCommand(command) {
this._channel.sendCommand(command);
},
_notifyDeviceConnected: function _notifyDeviceConnected(deviceName) {
this._channel.notifyDeviceConnected(deviceName);
},
_notifyDisconnected: function _notifyDisconnected(reason) {
this._channel.notifyDisconnected(reason);
},
_notifyLaunch: function _notifyLaunch(presentationId, url) {
this._channel.notifyLaunch(presentationId, url);
},
_notifyTerminate: function _notifyTerminate(presentationId) {
this._channel.notifyTerminate(presentationId);
},
_notifyReconnect: function _notifyReconnect(presentationId, url) {
this._channel.notifyReconnect(presentationId, url);
},
_notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
switch (command.type) {
case CommandType.OFFER:
this._channel.notifyOffer(command.offer);
break;
case CommandType.ICE_CANDIDATE:
this._channel.notifyIceCandidate(command.candidate);
break;
}
},
};
this.ReceiverStateMachine = ReceiverStateMachine; // jshint ignore:line

Some files were not shown because too many files have changed in this diff Show more