PermissionObserver.cpp (4233B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "PermissionObserver.h" 8 9 #include "PermissionStatusSink.h" 10 #include "PermissionUtils.h" 11 #include "mozilla/Services.h" 12 #include "mozilla/dom/WindowGlobalChild.h" 13 #include "nsIObserverService.h" 14 #include "nsIPermission.h" 15 16 namespace mozilla::dom { 17 18 namespace { 19 PermissionObserver* gInstance = nullptr; 20 } // namespace 21 22 NS_IMPL_ISUPPORTS(PermissionObserver, nsIObserver, nsISupportsWeakReference) 23 24 PermissionObserver::PermissionObserver() { 25 MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread()); 26 MOZ_ASSERT(!gInstance); 27 } 28 29 PermissionObserver::~PermissionObserver() { 30 MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread()); 31 MOZ_ASSERT(mSinks.IsEmpty()); 32 MOZ_ASSERT(gInstance == this); 33 34 gInstance = nullptr; 35 } 36 37 /* static */ 38 already_AddRefed<PermissionObserver> PermissionObserver::GetInstance() { 39 MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread()); 40 41 RefPtr<PermissionObserver> instance = gInstance; 42 if (!instance) { 43 instance = new PermissionObserver(); 44 45 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 46 if (NS_WARN_IF(!obs)) { 47 return nullptr; 48 } 49 50 nsresult rv = obs->AddObserver(instance, "perm-changed", true); 51 if (NS_WARN_IF(NS_FAILED(rv))) { 52 return nullptr; 53 } 54 55 rv = obs->AddObserver(instance, "perm-changed-notify-only", true); 56 if (NS_WARN_IF(NS_FAILED(rv))) { 57 return nullptr; 58 } 59 60 gInstance = instance; 61 } 62 63 return instance.forget(); 64 } 65 66 void PermissionObserver::AddSink(PermissionStatusSink* aSink) { 67 MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread()); 68 MOZ_ASSERT(aSink); 69 MOZ_ASSERT(!mSinks.Contains(aSink)); 70 71 mSinks.AppendElement(aSink); 72 } 73 74 void PermissionObserver::RemoveSink(PermissionStatusSink* aSink) { 75 MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread()); 76 MOZ_ASSERT(aSink); 77 MOZ_ASSERT(mSinks.Contains(aSink)); 78 79 mSinks.RemoveElement(aSink); 80 } 81 82 NS_IMETHODIMP 83 PermissionObserver::Observe(nsISupports* aSubject, const char* aTopic, 84 const char16_t* aData) { 85 MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread()); 86 MOZ_ASSERT(!strcmp(aTopic, "perm-changed") || 87 !strcmp(aTopic, "perm-changed-notify-only")); 88 89 if (mSinks.IsEmpty()) { 90 return NS_OK; 91 } 92 93 nsCOMPtr<nsIPermission> perm = nullptr; 94 nsCOMPtr<nsPIDOMWindowInner> innerWindow = nullptr; 95 nsAutoCString type; 96 97 if (!strcmp(aTopic, "perm-changed")) { 98 perm = do_QueryInterface(aSubject); 99 if (!perm) { 100 return NS_OK; 101 } 102 perm->GetType(type); 103 } else if (!strcmp(aTopic, "perm-changed-notify-only")) { 104 innerWindow = do_QueryInterface(aSubject); 105 if (!innerWindow) { 106 return NS_OK; 107 } 108 type = NS_ConvertUTF16toUTF8(aData); 109 } 110 111 Maybe<PermissionName> permission = TypeToPermissionName(type); 112 if (permission) { 113 for (PermissionStatusSink* sink : mSinks) { 114 if (sink->Name() != permission.value()) { 115 continue; 116 } 117 // Check for permissions that are changed for this sink's principal 118 // via the "perm-changed" notification. These permissions affect 119 // the window the sink (PermissionStatus) is held in directly. 120 if (perm && sink->MaybeUpdatedByOnMainThread(perm)) { 121 sink->PermissionChangedOnMainThread(); 122 } 123 // Check for permissions that are changed for this sink's principal 124 // via the "perm-changed-notify-only" notification. These permissions 125 // affect the window the sink (PermissionStatus) is held in indirectly- if 126 // the window is same-party with the secondary key of a permission. For 127 // example, a "3rdPartyFrameStorage^https://example.com" permission would 128 // return true on these checks where sink is in a window that is same-site 129 // with https://example.com. 130 if (innerWindow && 131 sink->MaybeUpdatedByNotifyOnlyOnMainThread(innerWindow)) { 132 sink->PermissionChangedOnMainThread(); 133 } 134 } 135 } 136 137 return NS_OK; 138 } 139 140 } // namespace mozilla::dom