dom/power/WakeLock.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "WakeLock.h"
     7 #include "mozilla/dom/ContentParent.h"
     8 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
     9 #include "mozilla/dom/MozWakeLockBinding.h"
    10 #include "mozilla/Hal.h"
    11 #include "mozilla/HalWakeLock.h"
    12 #include "nsError.h"
    13 #include "nsIDocument.h"
    14 #include "nsIDOMWindow.h"
    15 #include "nsIDOMEvent.h"
    16 #include "nsPIDOMWindow.h"
    17 #include "nsIPropertyBag2.h"
    19 using namespace mozilla::hal;
    21 namespace mozilla {
    22 namespace dom {
    24 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WakeLock)
    26 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WakeLock)
    27   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    28   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
    29   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
    30   NS_INTERFACE_MAP_ENTRY(nsIObserver)
    31   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    32 NS_INTERFACE_MAP_END
    34 NS_IMPL_CYCLE_COLLECTING_ADDREF(WakeLock)
    35 NS_IMPL_CYCLE_COLLECTING_RELEASE(WakeLock)
    37 WakeLock::WakeLock()
    38   : mLocked(false)
    39   , mHidden(true)
    40   , mContentParentID(CONTENT_PROCESS_ID_UNKNOWN)
    41 {
    42   SetIsDOMBinding();
    43 }
    45 WakeLock::~WakeLock()
    46 {
    47   DoUnlock();
    48   DetachEventListener();
    49 }
    51 JSObject*
    52 WakeLock::WrapObject(JSContext* aCx)
    53 {
    54   return MozWakeLockBinding::Wrap(aCx, this);
    55 }
    57 nsresult
    58 WakeLock::Init(const nsAString &aTopic, nsIDOMWindow *aWindow)
    59 {
    60   // Don't Init() a WakeLock twice.
    61   MOZ_ASSERT(mTopic.IsEmpty());
    63   if (aTopic.IsEmpty()) {
    64     return NS_ERROR_INVALID_ARG;
    65   }
    67   mTopic.Assign(aTopic);
    69   mWindow = do_GetWeakReference(aWindow);
    70   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
    72   /**
    73    * Null windows are allowed. A wake lock without associated window
    74    * is always considered invisible.
    75    */
    76   if (window) {
    77     nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
    78     NS_ENSURE_STATE(doc);
    79     mHidden = doc->Hidden();
    80   }
    82   AttachEventListener();
    83   DoLock();
    85   return NS_OK;
    86 }
    88 nsresult
    89 WakeLock::Init(const nsAString& aTopic, ContentParent* aContentParent)
    90 {
    91   // Don't Init() a WakeLock twice.
    92   MOZ_ASSERT(mTopic.IsEmpty());
    93   MOZ_ASSERT(aContentParent);
    95   if (aTopic.IsEmpty()) {
    96     return NS_ERROR_INVALID_ARG;
    97   }
    99   mTopic.Assign(aTopic);
   100   mContentParentID = aContentParent->ChildID();
   101   mHidden = false;
   103   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   104   if (obs) {
   105     obs->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
   106   }
   108   DoLock();
   109   return NS_OK;
   110 }
   112 NS_IMETHODIMP
   113 WakeLock::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
   114 {
   115   // If this wake lock was acquired on behalf of another process, unlock it
   116   // when that process dies.
   117   //
   118   // Note that we do /not/ call DoUnlock() here!  The wake lock back-end is
   119   // already listening for ipc:content-shutdown messages and will clear out its
   120   // tally for the process when it dies.  All we need to do here is ensure that
   121   // unlock() becomes a nop.
   123   MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
   125   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
   126   if (!props) {
   127     NS_WARNING("ipc:content-shutdown message without property bag as subject");
   128     return NS_OK;
   129   }
   131   uint64_t childID = 0;
   132   nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
   133                                            &childID);
   134   if (NS_SUCCEEDED(rv)) {
   135     if (childID == mContentParentID) {
   136       mLocked = false;
   137     }
   138   } else {
   139     NS_WARNING("ipc:content-shutdown message without childID property");
   140   }
   141   return NS_OK;
   142 }
   144 void
   145 WakeLock::DoLock()
   146 {
   147   if (!mLocked) {
   148     // Change the flag immediately to prevent recursive reentering
   149     mLocked = true;
   151     hal::ModifyWakeLock(mTopic,
   152                         hal::WAKE_LOCK_ADD_ONE,
   153                         mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE,
   154                         mContentParentID);
   155   }
   156 }
   158 void
   159 WakeLock::DoUnlock()
   160 {
   161   if (mLocked) {
   162     // Change the flag immediately to prevent recursive reentering
   163     mLocked = false;
   165     hal::ModifyWakeLock(mTopic,
   166                         hal::WAKE_LOCK_REMOVE_ONE,
   167                         mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE,
   168                         mContentParentID);
   169   }
   170 }
   172 void
   173 WakeLock::AttachEventListener()
   174 {
   175   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   177   if (window) {
   178     nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   179     if (doc) {
   180       doc->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
   181                                   this,
   182                                   /* useCapture = */ true,
   183                                   /* wantsUntrusted = */ false);
   185       nsCOMPtr<EventTarget> target = do_QueryInterface(window);
   186       target->AddSystemEventListener(NS_LITERAL_STRING("pagehide"),
   187                                      this,
   188                                      /* useCapture = */ true,
   189                                      /* wantsUntrusted = */ false);
   190       target->AddSystemEventListener(NS_LITERAL_STRING("pageshow"),
   191                                      this,
   192                                      /* useCapture = */ true,
   193                                      /* wantsUntrusted = */ false);
   194     }
   195   }
   196 }
   198 void
   199 WakeLock::DetachEventListener()
   200 {
   201   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   203   if (window) {
   204     nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   205     if (doc) {
   206       doc->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
   207                                      this,
   208                                      /* useCapture = */ true);
   209       nsCOMPtr<EventTarget> target = do_QueryInterface(window);
   210       target->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"),
   211                                         this,
   212                                         /* useCapture = */ true);
   213       target->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"),
   214                                         this,
   215                                         /* useCapture = */ true);
   216     }
   217   }
   218 }
   220 void
   221 WakeLock::Unlock(ErrorResult& aRv)
   222 {
   223   /*
   224    * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
   225    */
   226   if (!mLocked) {
   227     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   228     return;
   229   }
   231   DoUnlock();
   232   DetachEventListener();
   233 }
   235 void
   236 WakeLock::GetTopic(nsAString &aTopic)
   237 {
   238   aTopic.Assign(mTopic);
   239 }
   241 NS_IMETHODIMP
   242 WakeLock::HandleEvent(nsIDOMEvent *aEvent)
   243 {
   244   nsAutoString type;
   245   aEvent->GetType(type);
   247   if (type.EqualsLiteral("visibilitychange")) {
   248     nsCOMPtr<nsIDocument> doc =
   249       do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
   250     NS_ENSURE_STATE(doc);
   252     bool oldHidden = mHidden;
   253     mHidden = doc->Hidden();
   255     if (mLocked && oldHidden != mHidden) {
   256       hal::ModifyWakeLock(mTopic,
   257                           hal::WAKE_LOCK_NO_CHANGE,
   258                           mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
   259                           mContentParentID);
   260     }
   262     return NS_OK;
   263   }
   265   if (type.EqualsLiteral("pagehide")) {
   266     DoUnlock();
   267     return NS_OK;
   268   }
   270   if (type.EqualsLiteral("pageshow")) {
   271     DoLock();
   272     return NS_OK;
   273   }
   275   return NS_OK;
   276 }
   278 nsISupports*
   279 WakeLock::GetParentObject() const
   280 {
   281   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mWindow);
   282   return window;
   283 }
   285 } // dom
   286 } // mozilla

mercurial