Coverage Report

Created: 2018-09-25 14:53

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