Bug 1105827 - Part 9: Add PermissionObserver to watch for perm-changed notifications. r=baku
☠☠ backed out by 7b6f60b570c2 ☠ ☠
authorBirunthan Mohanathas <birunthan@mohanathas.com>
Fri, 31 Jul 2015 15:53:46 -0700
changeset 291564 7ee1772ffc10ba685e52e9c4c4d6a3dc419dec8d
parent 291563 fc34b9b8e87464bb423d0f2d7843448d7e970732
child 291565 b78a97800675cda35b9ac81d4b535b185001c197
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1105827
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1105827 - Part 9: Add PermissionObserver to watch for perm-changed notifications. r=baku
dom/permission/PermissionObserver.cpp
dom/permission/PermissionObserver.h
dom/permission/PermissionStatus.cpp
dom/permission/PermissionStatus.h
dom/permission/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/permission/PermissionObserver.cpp
@@ -0,0 +1,131 @@
+/* -*- 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 "PermissionObserver.h"
+
+#include "mozilla/dom/PermissionStatus.h"
+#include "mozilla/Services.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIObserverService.h"
+#include "nsIPermission.h"
+#include "PermissionUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+PermissionObserver* gInstance = nullptr;
+} // namespace
+
+NS_IMPL_ISUPPORTS(PermissionObserver,
+                  nsIObserver,
+                  nsISupportsWeakReference)
+
+PermissionObserver::PermissionObserver()
+{
+  MOZ_ASSERT(!gInstance);
+}
+
+PermissionObserver::~PermissionObserver()
+{
+  MOZ_ASSERT(mSinks.IsEmpty());
+  MOZ_ASSERT(gInstance == this);
+
+  gInstance = nullptr;
+}
+
+/* static */ already_AddRefed<PermissionObserver>
+PermissionObserver::GetInstance()
+{
+  nsRefPtr<PermissionObserver> instance = gInstance;
+  if (!instance) {
+    instance = new PermissionObserver();
+
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    if (NS_WARN_IF(!obs)) {
+      return nullptr;
+    }
+
+    nsresult rv = obs->AddObserver(instance, "perm-changed", true);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    gInstance = instance;
+  }
+
+  return instance.forget();
+}
+
+void
+PermissionObserver::AddSink(PermissionStatus* aSink)
+{
+  MOZ_ASSERT(aSink);
+  MOZ_ASSERT(!mSinks.Contains(aSink));
+
+  mSinks.AppendElement(aSink);
+}
+
+void
+PermissionObserver::RemoveSink(PermissionStatus* aSink)
+{
+  MOZ_ASSERT(aSink);
+  MOZ_ASSERT(mSinks.Contains(aSink));
+
+  mSinks.RemoveElement(aSink);
+}
+
+void
+PermissionObserver::Notify(PermissionName aName, nsIPrincipal& aPrincipal)
+{
+  for (auto* sink : mSinks) {
+    if (sink->mName != aName) {
+      continue;
+    }
+
+    nsIPrincipal* sinkPrincipal = sink->GetPrincipal();
+    if (NS_WARN_IF(!sinkPrincipal) || !aPrincipal.Equals(sinkPrincipal)) {
+      continue;
+    }
+
+    sink->PermissionChanged();
+  }
+}
+
+NS_IMETHODIMP
+PermissionObserver::Observe(nsISupports* aSubject,
+                            const char* aTopic,
+                            const char16_t* aData)
+{
+  MOZ_ASSERT(!strcmp(aTopic, "perm-changed"));
+
+  if (mSinks.IsEmpty()) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIPermission> perm = do_QueryInterface(aSubject);
+  if (!perm) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal;
+  perm->GetPrincipal(getter_AddRefs(principal));
+  if (!principal) {
+    return NS_OK;
+  }
+
+  nsAutoCString type;
+  perm->GetType(type);
+  Maybe<PermissionName> permission = TypeToPermissionName(type.get());
+  if (permission) {
+    Notify(permission.value(), *principal);
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/permission/PermissionObserver.h
@@ -0,0 +1,49 @@
+/* -*- 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_PermissionObserver_h_
+#define mozilla_dom_PermissionObserver_h_
+
+#include "mozilla/dom/PermissionsBinding.h"
+
+#include "nsIObserver.h"
+#include "nsIPrincipal.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+namespace dom {
+
+class PermissionStatus;
+
+// Singleton that watches for perm-changed notifications in order to notify
+// PermissionStatus objects.
+class PermissionObserver final
+  : public nsIObserver
+  , public nsSupportsWeakReference
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static already_AddRefed<PermissionObserver> GetInstance();
+
+  void AddSink(PermissionStatus* aObs);
+  void RemoveSink(PermissionStatus* aObs);
+
+private:
+  PermissionObserver();
+  virtual ~PermissionObserver();
+
+  void Notify(PermissionName aName, nsIPrincipal& aPrincipal);
+
+  nsTArray<PermissionStatus*> mSinks;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/permission/PermissionStatus.cpp
+++ b/dom/permission/PermissionStatus.cpp
@@ -4,49 +4,71 @@
  * 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/PermissionStatus.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/UniquePtr.h"
 #include "nsIPermissionManager.h"
+#include "PermissionObserver.h"
 #include "PermissionUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ nsresult
 PermissionStatus::Create(nsPIDOMWindow* aWindow,
                          PermissionName aName,
                          PermissionStatus** aStatus)
 {
   MOZ_ASSERT(aStatus);
   *aStatus = nullptr;
 
   UniquePtr<PermissionStatus> status(new PermissionStatus(aWindow, aName));
-  nsresult rv = status->UpdateState();
+  nsresult rv = status->Init();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   *aStatus = status.release();
   return NS_OK;
 }
 
 PermissionStatus::PermissionStatus(nsPIDOMWindow* aWindow,
                                    PermissionName aName)
   : DOMEventTargetHelper(aWindow)
   , mName(aName)
   , mState(PermissionState::Denied)
 {
 }
 
+nsresult
+PermissionStatus::Init()
+{
+  mObserver = PermissionObserver::GetInstance();
+  if (NS_WARN_IF(!mObserver)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mObserver->AddSink(this);
+
+  nsresult rv = UpdateState();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 PermissionStatus::~PermissionStatus()
 {
+  if (mObserver) {
+    mObserver->RemoveSink(this);
+  }
 }
 
 JSObject*
 PermissionStatus::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PermissionStatusBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -70,10 +92,26 @@ PermissionStatus::UpdateState()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mState = ActionToPermissionState(action);
   return NS_OK;
 }
 
+nsIPrincipal*
+PermissionStatus::GetPrincipal() const
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
+  if (NS_WARN_IF(!window)) {
+    return nullptr;
+  }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    return nullptr;
+  }
+
+  return doc->NodePrincipal();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/permission/PermissionStatus.h
+++ b/dom/permission/PermissionStatus.h
@@ -9,19 +9,23 @@
 
 #include "mozilla/dom/PermissionsBinding.h"
 #include "mozilla/dom/PermissionStatusBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
 namespace mozilla {
 namespace dom {
 
+class PermissionObserver;
+
 class PermissionStatus final
   : public DOMEventTargetHelper
 {
+  friend class PermissionObserver;
+
 public:
   ~PermissionStatus();
 
   static nsresult Create(nsPIDOMWindow* aWindow,
                          PermissionName aName,
                          PermissionStatus** aStatus);
 
   JSObject* WrapObject(JSContext* aCx,
@@ -29,18 +33,26 @@ public:
 
   PermissionState State() const { return mState; }
 
   IMPL_EVENT_HANDLER(change)
 
 private:
   PermissionStatus(nsPIDOMWindow* aWindow, PermissionName aName);
 
+  nsresult Init();
+
   nsresult UpdateState();
 
+  nsIPrincipal* GetPrincipal() const;
+
+  void PermissionChanged() {}
+
   PermissionName mName;
   PermissionState mState;
+
+  nsRefPtr<PermissionObserver> mObserver;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_permissionstatus_h_
--- a/dom/permission/moz.build
+++ b/dom/permission/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
     'Permissions.h',
     'PermissionStatus.h',
 ]
 
 UNIFIED_SOURCES += [
+    'PermissionObserver.cpp',
     'Permissions.cpp',
     'PermissionStatus.cpp',
     'PermissionUtils.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'PermissionPromptService.js',
     'PermissionPromptService.manifest',