mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 18:55:30 +00:00
a2a2c152ab
MozReview-Commit-ID: JrqeavLGub1 --HG-- extra : rebase_source : fc2eca80d59dc36e97a7af8ed3de6597faf38703
581 lines
14 KiB
C++
581 lines
14 KiB
C++
/* -*- 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
|