gecko-dev/dom/power/WakeLock.cpp
Makoto Kato a10b8df4d2 Bug 1492736 - Part 1. Add nsIWakeLock to manage wake lock from devtools. r=baku
DevTools wants to use wake lock to keep screen.  But since MozWakeLock is
removed by bug 1369194, there is no way to unlock wake lock from script.

So this issue adds nsIWakeLock to unlock wake lock.

Differential Revision: https://phabricator.services.mozilla.com/D7158

--HG--
extra : rebase_source : 4d6069fce27a7d85ce484a1418a8a17de83166f1
2018-09-28 14:05:07 +09:00

279 lines
7.1 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WakeLock.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Event.h" // for Event
#include "mozilla/Hal.h"
#include "mozilla/HalWakeLock.h"
#include "nsError.h"
#include "nsIDocument.h"
#include "nsIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsIPropertyBag2.h"
using namespace mozilla::hal;
namespace mozilla {
namespace dom {
NS_INTERFACE_MAP_BEGIN(WakeLock)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIWakeLock)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(WakeLock)
NS_IMPL_RELEASE(WakeLock)
WakeLock::WakeLock()
: mLocked(false)
, mHidden(true)
, mContentParentID(CONTENT_PROCESS_ID_UNKNOWN)
{
}
WakeLock::~WakeLock()
{
DoUnlock();
DetachEventListener();
}
nsresult
WakeLock::Init(const nsAString &aTopic, nsPIDOMWindowInner* aWindow)
{
// Don't Init() a WakeLock twice.
MOZ_ASSERT(mTopic.IsEmpty());
if (aTopic.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
mTopic.Assign(aTopic);
mWindow = do_GetWeakReference(aWindow);
/**
* Null windows are allowed. A wake lock without associated window
* is always considered invisible.
*/
if (aWindow) {
nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
NS_ENSURE_STATE(doc);
mHidden = doc->Hidden();
}
AttachEventListener();
DoLock();
return NS_OK;
}
nsresult
WakeLock::Init(const nsAString& aTopic, ContentParent* aContentParent)
{
// Don't Init() a WakeLock twice.
MOZ_ASSERT(mTopic.IsEmpty());
MOZ_ASSERT(aContentParent);
if (aTopic.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
mTopic.Assign(aTopic);
mContentParentID = aContentParent->ChildID();
mHidden = false;
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
}
DoLock();
return NS_OK;
}
NS_IMETHODIMP
WakeLock::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
{
// If this wake lock was acquired on behalf of another process, unlock it
// when that process dies.
//
// Note that we do /not/ call DoUnlock() here! The wake lock back-end is
// already listening for ipc:content-shutdown messages and will clear out its
// tally for the process when it dies. All we need to do here is ensure that
// unlock() becomes a nop.
MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
if (!props) {
NS_WARNING("ipc:content-shutdown message without property bag as subject");
return NS_OK;
}
uint64_t childID = 0;
nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
&childID);
if (NS_SUCCEEDED(rv)) {
if (childID == mContentParentID) {
mLocked = false;
}
} else {
NS_WARNING("ipc:content-shutdown message without childID property");
}
return NS_OK;
}
void
WakeLock::DoLock()
{
if (!mLocked) {
// Change the flag immediately to prevent recursive reentering
mLocked = true;
hal::ModifyWakeLock(mTopic,
hal::WAKE_LOCK_ADD_ONE,
mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE,
mContentParentID);
}
}
void
WakeLock::DoUnlock()
{
if (mLocked) {
// Change the flag immediately to prevent recursive reentering
mLocked = false;
hal::ModifyWakeLock(mTopic,
hal::WAKE_LOCK_REMOVE_ONE,
mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE,
mContentParentID);
}
}
void
WakeLock::AttachEventListener()
{
if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow)) {
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (doc) {
doc->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this,
/* useCapture = */ true,
/* wantsUntrusted = */ false);
nsCOMPtr<EventTarget> target = do_QueryInterface(window);
target->AddSystemEventListener(NS_LITERAL_STRING("pagehide"),
this,
/* useCapture = */ true,
/* wantsUntrusted = */ false);
target->AddSystemEventListener(NS_LITERAL_STRING("pageshow"),
this,
/* useCapture = */ true,
/* wantsUntrusted = */ false);
}
}
}
void
WakeLock::DetachEventListener()
{
if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow)) {
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (doc) {
doc->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this,
/* useCapture = */ true);
nsCOMPtr<EventTarget> target = do_QueryInterface(window);
target->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"),
this,
/* useCapture = */ true);
target->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"),
this,
/* useCapture = */ true);
}
}
}
void
WakeLock::Unlock(ErrorResult& aRv)
{
/*
* We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
*/
if (!mLocked) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
DoUnlock();
DetachEventListener();
}
void
WakeLock::GetTopic(nsAString &aTopic)
{
aTopic.Assign(mTopic);
}
NS_IMETHODIMP
WakeLock::HandleEvent(Event *aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (type.EqualsLiteral("visibilitychange")) {
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aEvent->GetTarget());
NS_ENSURE_STATE(doc);
bool oldHidden = mHidden;
mHidden = doc->Hidden();
if (mLocked && oldHidden != mHidden) {
hal::ModifyWakeLock(mTopic,
hal::WAKE_LOCK_NO_CHANGE,
mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
mContentParentID);
}
return NS_OK;
}
if (type.EqualsLiteral("pagehide")) {
DoUnlock();
return NS_OK;
}
if (type.EqualsLiteral("pageshow")) {
DoLock();
return NS_OK;
}
return NS_OK;
}
NS_IMETHODIMP
WakeLock::Unlock()
{
ErrorResult error;
Unlock(error);
return error.StealNSResult();
}
nsPIDOMWindowInner*
WakeLock::GetParentObject() const
{
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mWindow);
return window;
}
} // namespace dom
} // namespace mozilla