Bug 1208417 - Part 2 - Add DisplayDeviceProvider to Presentation API, r=schien, r=smaug

This commit is contained in:
KuoE0 2016-04-28 15:05:25 +08:00 коммит произвёл Liang-Heng Chen
Родитель c96544e832
Коммит f6da5068fb
9 изменённых файлов: 452 добавлений и 94 удалений

Просмотреть файл

@ -21,7 +21,6 @@ Cu.import('resource://gre/modules/RequestSyncService.jsm');
Cu.import('resource://gre/modules/SystemUpdateService.jsm');
if (isGonk) {
Cu.import('resource://gre/modules/MultiscreenHandler.jsm');
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
Cu.import('resource://gre/modules/ResourceStatsService.jsm');
}

Просмотреть файл

@ -1,92 +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";
this.EXPORTED_SYMBOLS = ["MultiscreenHandler"];
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
function debug(aStr) {
// dump("MultiscreenHandler: " + aStr + "\n");
}
var window = Services.wm.getMostRecentWindow("navigator:browser");
// Multi-screen support on b2g. The following implementation will open a new
// top-level window once we receive a display connected event.
var MultiscreenHandler = {
topLevelWindows: new Map(),
init: function init() {
Services.obs.addObserver(this, "display-changed", false);
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
uninit: function uninit() {
Services.obs.removeObserver(this, "display-changed");
Services.obs.removeObserver(this, "xpcom-shutdown");
},
observe: function observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "display-changed":
this.handleDisplayChangeEvent(aSubject);
break
case "xpcom-shutdown":
this.uninit();
break
}
},
openTopLevelWindow: function openTopLevelWindow(aDisplay) {
if (this.topLevelWindows.get(aDisplay.id)) {
debug("Top level window for display id: " + aDisplay.id + " has been opened.");
return;
}
let flags = Services.prefs.getCharPref("toolkit.defaultChromeFeatures") +
",mozDisplayId=" + aDisplay.id;
let remoteShellURL = Services.prefs.getCharPref("b2g.multiscreen.chrome_remote_url") +
"#" + aDisplay.id;
let win = Services.ww.openWindow(null, remoteShellURL, "myTopWindow" + aDisplay.id, flags, null);
this.topLevelWindows.set(aDisplay.id, win);
},
closeTopLevelWindow: function closeTopLevelWindow(aDisplay) {
let win = this.topLevelWindows.get(aDisplay.id);
if (win) {
win.close();
this.topLevelWindows.delete(aDisplay.id);
}
},
handleDisplayChangeEvent: function handleDisplayChangeEvent(aSubject) {
let display = aSubject.QueryInterface(Ci.nsIDisplayInfo);
let name = "multiscreen.enabled";
let req = window.navigator.mozSettings.createLock().get(name);
req.addEventListener("success", () => {
let isMultiscreenEnabled = req.result[name];
if (display.connected) {
if (isMultiscreenEnabled) {
this.openTopLevelWindow(display);
}
} else {
this.closeTopLevelWindow(display);
}
});
},
};
MultiscreenHandler.init();
this.MultiscreenHandler = MultiscreenHandler;

Просмотреть файл

@ -68,7 +68,6 @@ EXTRA_JS_MODULES += [
'LogCapture.jsm',
'LogParser.jsm',
'LogShake.jsm',
'MultiscreenHandler.jsm',
'OrientationChangeHandler.jsm',
'PersistentDataBlock.jsm',
'SafeMode.jsm',

Просмотреть файл

@ -35,3 +35,5 @@ interface nsIPresentationDevice : nsISupports
// Do something when presentation session is disconnected.
void disconnect();
};

Просмотреть файл

@ -0,0 +1,318 @@
/* -*- 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 "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 "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"
namespace mozilla {
namespace dom {
namespace presentation {
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(const nsAString& aUrl,
const nsAString& aPresentationId,
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->RequestSession(this, aUrl, aPresentationId, aControlChannel);
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::Disconnect()
{
nsresult rv = CloseTopLevelWindow();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
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)
DisplayDeviceProvider::~DisplayDeviceProvider()
{
Uninit();
}
nsresult
DisplayDeviceProvider::Init()
{
// Provider must be initialized only once.
if (mInitialized) {
return NS_OK;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
MOZ_ASSERT(obs);
obs->AddObserver(this, DISPLAY_CHANGED_NOTIFICATION, false);
mDevice = new HDMIDisplayDevice(this);
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();
mInitialized = false;
return NS_OK;
}
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;
}
// 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;
}
}
}
return NS_OK;
}
nsresult
DisplayDeviceProvider::RequestSession(HDMIDisplayDevice* aDevice,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel** aControlChannel)
{
// Implement in part 3
return NS_OK;
}
} // namespace presentation
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,115 @@
/* -*- 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 "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 DisplayDeviceProvider final : public nsIObserver
, public nsIPresentationDeviceProvider
, 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")
, mProvider(aProvider)
{}
nsresult OpenTopLevelWindow();
nsresult CloseTopLevelWindow();
const nsCString& Id() const { return mWindowId; }
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;
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
// For using WeakPtr when MOZ_REFCOUNTED_LEAK_CHECKING defined
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(DisplayDeviceProvider)
nsresult RequestSession(HDMIDisplayDevice* aDevice,
const nsAString& aUrl,
const nsAString& aPresentationId,
nsIPresentationControlChannel** aControlChannel);
private:
virtual ~DisplayDeviceProvider();
nsresult Init();
nsresult Uninit();
nsresult AddExternalScreen();
nsresult RemoveExternalScreen();
// 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;
bool mInitialized = false;
};
} // mozilla
} // dom
} // presentation
#endif // mozilla_dom_presentation_provider_DisplayDeviceProvider_h

Просмотреть файл

@ -1055,6 +1055,7 @@ MulticastDNSDeviceProvider::Device::EstablishControlChannel(const nsAString& aUr
NS_IMETHODIMP
MulticastDNSDeviceProvider::Device::Disconnect()
{
// No need to do anything when disconnect.
return NS_OK;
}

Просмотреть файл

@ -3,33 +3,48 @@
* 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"
#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 } }
#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"
using mozilla::dom::presentation::MulticastDNSDeviceProvider;
using mozilla::dom::presentation::DisplayDeviceProvider;
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);
static const mozilla::Module::CIDEntry kPresentationDeviceProviderCIDs[] = {
{ &kMULTICAST_DNS_PROVIDER_CID, false, nullptr, MulticastDNSDeviceProviderConstructor },
{ &kDISPLAY_DEVICE_PROVIDER_CID, false, nullptr, DisplayDeviceProviderConstructor },
{ 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 },
{ nullptr }
};
static const mozilla::Module::CategoryEntry kPresentationDeviceProviderCategories[] = {
#if defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_ANDROID) || (defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 16)
{ PRESENTATION_DEVICE_PROVIDER_CATEGORY, "MulticastDNSDeviceProvider", MULTICAST_DNS_PROVIDER_CONTRACT_ID },
#endif
#if defined(MOZ_WIDGET_GONK)
{ PRESENTATION_DEVICE_PROVIDER_CATEGORY, "DisplayDeviceProvider", DISPLAY_DEVICE_PROVIDER_CONTRACT_ID },
#endif
{ nullptr }
};

Просмотреть файл

@ -10,6 +10,7 @@ EXTRA_COMPONENTS += [
]
UNIFIED_SOURCES += [
'DisplayDeviceProvider.cpp',
'MulticastDNSDeviceProvider.cpp',
'PresentationDeviceProviderModule.cpp',
]