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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "SettingsChangeObserver.h"
8 #include "ContentBlockingUserInteraction.h"
9 
10 #include "mozilla/Services.h"
11 #include "mozilla/Preferences.h"
12 #include "nsIObserverService.h"
13 #include "nsIPermission.h"
14 #include "nsTArray.h"
15 
16 using namespace mozilla;
17 
18 namespace {
19 
20 UniquePtr<nsTArray<SettingsChangeObserver::AntiTrackingSettingsChangedCallback>>
21     gSettingsChangedCallbacks;
22 
23 }
24 
NS_IMPL_ISUPPORTS(SettingsChangeObserver,nsIObserver)25 NS_IMPL_ISUPPORTS(SettingsChangeObserver, nsIObserver)
26 
27 NS_IMETHODIMP SettingsChangeObserver::Observe(nsISupports* aSubject,
28                                               const char* aTopic,
29                                               const char16_t* aData) {
30   if (!strcmp(aTopic, "xpcom-shutdown")) {
31     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
32     if (obs) {
33       obs->RemoveObserver(this, "perm-added");
34       obs->RemoveObserver(this, "perm-changed");
35       obs->RemoveObserver(this, "perm-cleared");
36       obs->RemoveObserver(this, "perm-deleted");
37       obs->RemoveObserver(this, "xpcom-shutdown");
38 
39       Preferences::UnregisterPrefixCallback(
40           SettingsChangeObserver::PrivacyPrefChanged,
41           "browser.contentblocking.");
42       Preferences::UnregisterPrefixCallback(
43           SettingsChangeObserver::PrivacyPrefChanged, "network.cookie.");
44       Preferences::UnregisterPrefixCallback(
45           SettingsChangeObserver::PrivacyPrefChanged, "privacy.");
46 
47       gSettingsChangedCallbacks = nullptr;
48     }
49   } else {
50     nsCOMPtr<nsIPermission> perm = do_QueryInterface(aSubject);
51     if (perm) {
52       nsAutoCString type;
53       nsresult rv = perm->GetType(type);
54       if (NS_WARN_IF(NS_FAILED(rv)) || type.Equals(USER_INTERACTION_PERM)) {
55         // Ignore failures or notifications that have been sent because of
56         // user interactions.
57         return NS_OK;
58       }
59     }
60 
61     RunAntiTrackingSettingsChangedCallbacks();
62   }
63 
64   return NS_OK;
65 }
66 
67 // static
PrivacyPrefChanged(const char * aPref,void * aClosure)68 void SettingsChangeObserver::PrivacyPrefChanged(const char* aPref,
69                                                 void* aClosure) {
70   RunAntiTrackingSettingsChangedCallbacks();
71 }
72 
73 // static
RunAntiTrackingSettingsChangedCallbacks()74 void SettingsChangeObserver::RunAntiTrackingSettingsChangedCallbacks() {
75   if (gSettingsChangedCallbacks) {
76     for (auto& callback : *gSettingsChangedCallbacks) {
77       callback();
78     }
79   }
80 }
81 
82 // static
OnAntiTrackingSettingsChanged(const SettingsChangeObserver::AntiTrackingSettingsChangedCallback & aCallback)83 void SettingsChangeObserver::OnAntiTrackingSettingsChanged(
84     const SettingsChangeObserver::AntiTrackingSettingsChangedCallback&
85         aCallback) {
86   static bool initialized = false;
87   if (!initialized) {
88     // It is possible that while we have some data in our cache, something
89     // changes in our environment that causes the anti-tracking checks below to
90     // change their response.  Therefore, we need to clear our cache when we
91     // detect a related change.
92     Preferences::RegisterPrefixCallback(
93         SettingsChangeObserver::PrivacyPrefChanged, "browser.contentblocking.");
94     Preferences::RegisterPrefixCallback(
95         SettingsChangeObserver::PrivacyPrefChanged, "network.cookie.");
96     Preferences::RegisterPrefixCallback(
97         SettingsChangeObserver::PrivacyPrefChanged, "privacy.");
98 
99     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
100     if (obs) {
101       RefPtr<SettingsChangeObserver> observer = new SettingsChangeObserver();
102       obs->AddObserver(observer, "perm-added", false);
103       obs->AddObserver(observer, "perm-changed", false);
104       obs->AddObserver(observer, "perm-cleared", false);
105       obs->AddObserver(observer, "perm-deleted", false);
106       obs->AddObserver(observer, "xpcom-shutdown", false);
107     }
108 
109     gSettingsChangedCallbacks =
110         MakeUnique<nsTArray<AntiTrackingSettingsChangedCallback>>();
111 
112     initialized = true;
113   }
114 
115   gSettingsChangedCallbacks->AppendElement(aCallback);
116 }
117