Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; 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
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsRFPService.h"
7 :
8 : #include <time.h>
9 :
10 : #include "mozilla/ClearOnShutdown.h"
11 : #include "mozilla/Preferences.h"
12 : #include "mozilla/Services.h"
13 : #include "mozilla/StaticPtr.h"
14 :
15 : #include "nsCOMPtr.h"
16 : #include "nsServiceManagerUtils.h"
17 : #include "nsString.h"
18 : #include "nsXULAppAPI.h"
19 :
20 : #include "nsIObserverService.h"
21 : #include "nsIPrefBranch.h"
22 : #include "nsIPrefService.h"
23 : #include "nsJSUtils.h"
24 :
25 : #include "prenv.h"
26 :
27 : #include "js/Date.h"
28 :
29 : using namespace mozilla;
30 :
31 : #define RESIST_FINGERPRINTING_PREF "privacy.resistFingerprinting"
32 : #define PROFILE_INITIALIZED_TOPIC "profile-initial-state"
33 :
34 30 : NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
35 :
36 3 : static StaticRefPtr<nsRFPService> sRFPService;
37 : static bool sInitialized = false;
38 : Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyResistFingerprinting;
39 : static uint32_t kResolutionUSec = 100000;
40 :
41 : /* static */
42 : nsRFPService*
43 3 : nsRFPService::GetOrCreate()
44 : {
45 3 : if (!sInitialized) {
46 3 : sRFPService = new nsRFPService();
47 3 : nsresult rv = sRFPService->Init();
48 :
49 3 : if (NS_FAILED(rv)) {
50 0 : sRFPService = nullptr;
51 0 : return nullptr;
52 : }
53 :
54 3 : ClearOnShutdown(&sRFPService);
55 3 : sInitialized = true;
56 : }
57 :
58 3 : return sRFPService;
59 : }
60 :
61 : /* static */
62 : double
63 58 : nsRFPService::ReduceTimePrecisionAsMSecs(double aTime)
64 : {
65 58 : if (!IsResistFingerprintingEnabled()) {
66 58 : return aTime;
67 : }
68 0 : const double resolutionMSec = kResolutionUSec / 1000.0;
69 0 : return floor(aTime / resolutionMSec) * resolutionMSec;
70 : }
71 :
72 : /* static */
73 : double
74 0 : nsRFPService::ReduceTimePrecisionAsUSecs(double aTime)
75 : {
76 0 : if (!IsResistFingerprintingEnabled()) {
77 0 : return aTime;
78 : }
79 0 : return floor(aTime / kResolutionUSec) * kResolutionUSec;
80 : }
81 :
82 : /* static */
83 : double
84 0 : nsRFPService::ReduceTimePrecisionAsSecs(double aTime)
85 : {
86 0 : if (!IsResistFingerprintingEnabled()) {
87 0 : return aTime;
88 : }
89 0 : if (kResolutionUSec < 1000000) {
90 : // The resolution is smaller than one sec. Use the reciprocal to avoid
91 : // floating point error.
92 0 : const double resolutionSecReciprocal = 1000000.0 / kResolutionUSec;
93 0 : return floor(aTime * resolutionSecReciprocal) / resolutionSecReciprocal;
94 : }
95 0 : const double resolutionSec = kResolutionUSec / 1000000.0;
96 0 : return floor(aTime / resolutionSec) * resolutionSec;
97 : }
98 :
99 : nsresult
100 3 : nsRFPService::Init()
101 : {
102 3 : MOZ_ASSERT(NS_IsMainThread());
103 :
104 : nsresult rv;
105 :
106 6 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
107 3 : NS_ENSURE_TRUE(obs, NS_ERROR_NOT_AVAILABLE);
108 :
109 3 : rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
110 3 : NS_ENSURE_SUCCESS(rv, rv);
111 :
112 : #if defined(XP_WIN)
113 : rv = obs->AddObserver(this, PROFILE_INITIALIZED_TOPIC, false);
114 : NS_ENSURE_SUCCESS(rv, rv);
115 : #endif
116 :
117 6 : nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
118 3 : NS_ENSURE_TRUE(prefs, NS_ERROR_NOT_AVAILABLE);
119 :
120 3 : rv = prefs->AddObserver(RESIST_FINGERPRINTING_PREF, this, false);
121 3 : NS_ENSURE_SUCCESS(rv, rv);
122 :
123 : // We backup the original TZ value here.
124 3 : const char* tzValue = PR_GetEnv("TZ");
125 3 : if (tzValue) {
126 0 : mInitialTZValue = nsCString(tzValue);
127 : }
128 :
129 : // Call UpdatePref() here to cache the value of 'privacy.resistFingerprinting'
130 : // and set the timezone.
131 3 : UpdatePref();
132 :
133 3 : return rv;
134 : }
135 :
136 : void
137 3 : nsRFPService::UpdatePref()
138 : {
139 3 : MOZ_ASSERT(NS_IsMainThread());
140 3 : sPrivacyResistFingerprinting = Preferences::GetBool(RESIST_FINGERPRINTING_PREF);
141 :
142 3 : if (sPrivacyResistFingerprinting) {
143 0 : PR_SetEnv("TZ=UTC");
144 0 : JS::SetTimeResolutionUsec(kResolutionUSec);
145 3 : } else if (sInitialized) {
146 0 : JS::SetTimeResolutionUsec(0);
147 : // We will not touch the TZ value if 'privacy.resistFingerprinting' is false during
148 : // the time of initialization.
149 0 : if (!mInitialTZValue.IsEmpty()) {
150 0 : nsAutoCString tzValue = NS_LITERAL_CSTRING("TZ=") + mInitialTZValue;
151 0 : PR_SetEnv(tzValue.get());
152 : } else {
153 : #if defined(XP_LINUX) || defined (XP_MACOSX)
154 : // For POSIX like system, we reset the TZ to the /etc/localtime, which is the
155 : // system timezone.
156 0 : PR_SetEnv("TZ=:/etc/localtime");
157 : #else
158 : // For Windows, we reset the TZ to an empty string. This will make Windows to use
159 : // its system timezone.
160 : PR_SetEnv("TZ=");
161 : #endif
162 : }
163 : }
164 :
165 3 : nsJSUtils::ResetTimeZone();
166 3 : }
167 :
168 : void
169 0 : nsRFPService::StartShutdown()
170 : {
171 0 : MOZ_ASSERT(NS_IsMainThread());
172 :
173 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
174 :
175 0 : if (obs) {
176 0 : obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
177 :
178 0 : nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
179 :
180 0 : if (prefs) {
181 0 : prefs->RemoveObserver(RESIST_FINGERPRINTING_PREF, this);
182 : }
183 : }
184 0 : }
185 :
186 : NS_IMETHODIMP
187 0 : nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
188 : const char16_t* aMessage)
189 : {
190 0 : if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
191 0 : NS_ConvertUTF16toUTF8 pref(aMessage);
192 :
193 0 : if (pref.EqualsLiteral(RESIST_FINGERPRINTING_PREF)) {
194 0 : UpdatePref();
195 :
196 : #if defined(XP_WIN)
197 : if (!XRE_IsE10sParentProcess()) {
198 : // Windows does not follow POSIX. Updates to the TZ environment variable
199 : // are not reflected immediately on that platform as they are on UNIX
200 : // systems without this call.
201 : _tzset();
202 : }
203 : #endif
204 : }
205 : }
206 :
207 0 : if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
208 0 : StartShutdown();
209 : }
210 : #if defined(XP_WIN)
211 : else if (!strcmp(PROFILE_INITIALIZED_TOPIC, aTopic)) {
212 : // If we're e10s, then we don't need to run this, since the child process will
213 : // simply inherit the environment variable from the parent process, in which
214 : // case it's unnecessary to call _tzset().
215 : if (XRE_IsParentProcess() && !XRE_IsE10sParentProcess()) {
216 : // Windows does not follow POSIX. Updates to the TZ environment variable
217 : // are not reflected immediately on that platform as they are on UNIX
218 : // systems without this call.
219 : _tzset();
220 : }
221 :
222 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
223 : NS_ENSURE_TRUE(obs, NS_ERROR_NOT_AVAILABLE);
224 :
225 : nsresult rv = obs->RemoveObserver(this, PROFILE_INITIALIZED_TOPIC);
226 : NS_ENSURE_SUCCESS(rv, rv);
227 : }
228 : #endif
229 :
230 0 : return NS_OK;
231 9 : }
|