Source code

Revision control

Other Tools

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 "StorageActivityService.h"
8
9
#include "mozilla/ipc/BackgroundUtils.h"
10
#include "mozilla/StaticPtr.h"
11
#include "nsIMutableArray.h"
12
#include "nsSupportsPrimitives.h"
13
#include "nsXPCOM.h"
14
15
// This const is used to know when origin activities should be purged because
16
// too old. This value should be in sync with what the UI needs.
17
#define TIME_MAX_SECS 86400 /* 24 hours */
18
19
namespace mozilla {
20
namespace dom {
21
22
static StaticRefPtr<StorageActivityService> gStorageActivityService;
23
static bool gStorageActivityShutdown = false;
24
25
/* static */
26
void StorageActivityService::SendActivity(nsIPrincipal* aPrincipal) {
27
MOZ_ASSERT(NS_IsMainThread());
28
29
if (!aPrincipal || BasePrincipal::Cast(aPrincipal)->Kind() !=
30
BasePrincipal::eContentPrincipal) {
31
// Only content principals.
32
return;
33
}
34
35
RefPtr<StorageActivityService> service = GetOrCreate();
36
if (NS_WARN_IF(!service)) {
37
return;
38
}
39
40
service->SendActivityInternal(aPrincipal);
41
}
42
43
/* static */
44
void StorageActivityService::SendActivity(
45
const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
46
if (aPrincipalInfo.type() !=
47
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
48
// only content principal.
49
return;
50
}
51
52
RefPtr<Runnable> r = NS_NewRunnableFunction(
53
"StorageActivityService::SendActivity", [aPrincipalInfo]() {
54
MOZ_ASSERT(NS_IsMainThread());
55
56
nsCOMPtr<nsIPrincipal> principal =
57
mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo);
58
59
StorageActivityService::SendActivity(principal);
60
});
61
62
SystemGroup::Dispatch(TaskCategory::Other, r.forget());
63
}
64
65
/* static */
66
void StorageActivityService::SendActivity(const nsACString& aOrigin) {
67
MOZ_ASSERT(XRE_IsParentProcess());
68
69
nsCString origin;
70
origin.Assign(aOrigin);
71
72
RefPtr<Runnable> r = NS_NewRunnableFunction(
73
"StorageActivityService::SendActivity", [origin]() {
74
MOZ_ASSERT(NS_IsMainThread());
75
76
RefPtr<StorageActivityService> service = GetOrCreate();
77
if (NS_WARN_IF(!service)) {
78
return;
79
}
80
81
service->SendActivityInternal(origin);
82
});
83
84
if (NS_IsMainThread()) {
85
Unused << r->Run();
86
} else {
87
SystemGroup::Dispatch(TaskCategory::Other, r.forget());
88
}
89
}
90
91
/* static */
92
already_AddRefed<StorageActivityService> StorageActivityService::GetOrCreate() {
93
MOZ_ASSERT(NS_IsMainThread());
94
95
if (!gStorageActivityService && !gStorageActivityShutdown) {
96
RefPtr<StorageActivityService> service = new StorageActivityService();
97
98
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
99
if (NS_WARN_IF(!obs)) {
100
return nullptr;
101
}
102
103
nsresult rv =
104
obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
105
if (NS_WARN_IF(NS_FAILED(rv))) {
106
return nullptr;
107
}
108
109
gStorageActivityService = service;
110
}
111
112
RefPtr<StorageActivityService> service = gStorageActivityService;
113
return service.forget();
114
}
115
116
StorageActivityService::StorageActivityService() {
117
MOZ_ASSERT(NS_IsMainThread());
118
}
119
120
StorageActivityService::~StorageActivityService() {
121
MOZ_ASSERT(NS_IsMainThread());
122
MOZ_ASSERT(!mTimer);
123
}
124
125
void StorageActivityService::SendActivityInternal(nsIPrincipal* aPrincipal) {
126
MOZ_ASSERT(NS_IsMainThread());
127
MOZ_ASSERT(aPrincipal);
128
MOZ_ASSERT(BasePrincipal::Cast(aPrincipal)->Kind() ==
129
BasePrincipal::eContentPrincipal);
130
131
if (!XRE_IsParentProcess()) {
132
SendActivityToParent(aPrincipal);
133
return;
134
}
135
136
nsAutoCString origin;
137
nsresult rv = aPrincipal->GetOrigin(origin);
138
if (NS_WARN_IF(NS_FAILED(rv))) {
139
return;
140
}
141
142
SendActivityInternal(origin);
143
}
144
145
void StorageActivityService::SendActivityInternal(const nsACString& aOrigin) {
146
MOZ_ASSERT(XRE_IsParentProcess());
147
148
mActivities.Put(aOrigin, PR_Now());
149
MaybeStartTimer();
150
}
151
152
void StorageActivityService::SendActivityToParent(nsIPrincipal* aPrincipal) {
153
MOZ_ASSERT(NS_IsMainThread());
154
MOZ_ASSERT(!XRE_IsParentProcess());
155
156
PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
157
if (NS_WARN_IF(!actor)) {
158
return;
159
}
160
161
mozilla::ipc::PrincipalInfo principalInfo;
162
nsresult rv =
163
mozilla::ipc::PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
164
if (NS_WARN_IF(NS_FAILED(rv))) {
165
return;
166
}
167
168
actor->SendStorageActivity(principalInfo);
169
}
170
171
NS_IMETHODIMP
172
StorageActivityService::Observe(nsISupports* aSubject, const char* aTopic,
173
const char16_t* aData) {
174
MOZ_ASSERT(NS_IsMainThread());
175
MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
176
177
MaybeStopTimer();
178
179
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
180
if (obs) {
181
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
182
}
183
184
gStorageActivityShutdown = true;
185
gStorageActivityService = nullptr;
186
return NS_OK;
187
}
188
189
void StorageActivityService::MaybeStartTimer() {
190
MOZ_ASSERT(NS_IsMainThread());
191
192
if (!mTimer) {
193
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
194
mTimer->InitWithCallback(this, 1000 * 5 * 60 /* any 5 minutes */,
195
nsITimer::TYPE_REPEATING_SLACK);
196
}
197
}
198
199
void StorageActivityService::MaybeStopTimer() {
200
MOZ_ASSERT(NS_IsMainThread());
201
202
if (mTimer) {
203
mTimer->Cancel();
204
mTimer = nullptr;
205
}
206
}
207
208
NS_IMETHODIMP
209
StorageActivityService::Notify(nsITimer* aTimer) {
210
MOZ_ASSERT(NS_IsMainThread());
211
MOZ_ASSERT(mTimer == aTimer);
212
213
uint64_t now = PR_Now();
214
215
for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
216
if ((now - iter.UserData()) / PR_USEC_PER_SEC > TIME_MAX_SECS) {
217
iter.Remove();
218
}
219
}
220
221
// If no activities, let's stop the timer.
222
if (mActivities.Count() == 0) {
223
MaybeStopTimer();
224
}
225
226
return NS_OK;
227
}
228
229
NS_IMETHODIMP
230
StorageActivityService::GetActiveOrigins(PRTime aFrom, PRTime aTo,
231
nsIArray** aRetval) {
232
uint64_t now = PR_Now();
233
if (((now - aFrom) / PR_USEC_PER_SEC) > TIME_MAX_SECS || aFrom >= aTo) {
234
return NS_ERROR_INVALID_ARG;
235
}
236
237
nsresult rv = NS_OK;
238
nsCOMPtr<nsIMutableArray> devices =
239
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
240
if (NS_WARN_IF(NS_FAILED(rv))) {
241
return rv;
242
}
243
244
for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
245
if (iter.UserData() >= aFrom && iter.UserData() <= aTo) {
246
RefPtr<BasePrincipal> principal =
247
BasePrincipal::CreateContentPrincipal(iter.Key());
248
MOZ_ASSERT(principal);
249
250
rv = devices->AppendElement(principal);
251
if (NS_WARN_IF(NS_FAILED(rv))) {
252
return rv;
253
}
254
}
255
}
256
257
devices.forget(aRetval);
258
return NS_OK;
259
}
260
261
NS_IMETHODIMP
262
StorageActivityService::MoveOriginInTime(nsIPrincipal* aPrincipal,
263
PRTime aWhen) {
264
if (!XRE_IsParentProcess()) {
265
return NS_ERROR_FAILURE;
266
}
267
268
nsAutoCString origin;
269
nsresult rv = aPrincipal->GetOrigin(origin);
270
if (NS_WARN_IF(NS_FAILED(rv))) {
271
return rv;
272
}
273
274
mActivities.Put(origin, aWhen / PR_USEC_PER_SEC);
275
return NS_OK;
276
}
277
278
NS_IMETHODIMP
279
StorageActivityService::TestOnlyReset() {
280
mActivities.Clear();
281
return NS_OK;
282
}
283
284
NS_INTERFACE_MAP_BEGIN(StorageActivityService)
285
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStorageActivityService)
286
NS_INTERFACE_MAP_ENTRY(nsIStorageActivityService)
287
NS_INTERFACE_MAP_ENTRY(nsIObserver)
288
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
289
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
290
NS_INTERFACE_MAP_END
291
292
NS_IMPL_ADDREF(StorageActivityService)
293
NS_IMPL_RELEASE(StorageActivityService)
294
295
} // namespace dom
296
} // namespace mozilla