Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/places/Helpers.cpp
Line
Count
Source (jump to first uncovered line)
1
/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
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 "Helpers.h"
7
#include "mozIStorageError.h"
8
#include "prio.h"
9
#include "nsString.h"
10
#include "nsNavHistory.h"
11
#include "mozilla/Base64.h"
12
#include "mozilla/HashFunctions.h"
13
#include <algorithm>
14
#include "mozilla/Services.h"
15
16
// The length of guids that are used by history and bookmarks.
17
0
#define GUID_LENGTH 12
18
19
// Maximum number of chars to use for calculating hashes. This value has been
20
// picked to ensure low hash collisions on a real world common places.sqlite.
21
// While collisions are not a big deal for functionality, a low ratio allows
22
// for slightly more efficient SELECTs.
23
0
#define MAX_CHARS_TO_HASH 1500U
24
25
namespace mozilla {
26
namespace places {
27
28
////////////////////////////////////////////////////////////////////////////////
29
//// AsyncStatementCallback
30
31
NS_IMPL_ISUPPORTS(
32
  AsyncStatementCallback
33
, mozIStorageStatementCallback
34
)
35
36
NS_IMETHODIMP
37
WeakAsyncStatementCallback::HandleResult(mozIStorageResultSet *aResultSet)
38
0
{
39
0
  MOZ_ASSERT(false, "Was not expecting a resultset, but got it.");
40
0
  return NS_OK;
41
0
}
42
43
NS_IMETHODIMP
44
WeakAsyncStatementCallback::HandleCompletion(uint16_t aReason)
45
0
{
46
0
  return NS_OK;
47
0
}
48
49
NS_IMETHODIMP
50
WeakAsyncStatementCallback::HandleError(mozIStorageError *aError)
51
0
{
52
#ifdef DEBUG
53
  int32_t result;
54
  nsresult rv = aError->GetResult(&result);
55
  NS_ENSURE_SUCCESS(rv, rv);
56
  nsAutoCString message;
57
  rv = aError->GetMessage(message);
58
  NS_ENSURE_SUCCESS(rv, rv);
59
60
  nsAutoCString warnMsg;
61
  warnMsg.AppendLiteral("An error occurred while executing an async statement: ");
62
  warnMsg.AppendInt(result);
63
  warnMsg.Append(' ');
64
  warnMsg.Append(message);
65
  NS_WARNING(warnMsg.get());
66
#endif
67
68
0
  return NS_OK;
69
0
}
70
71
#define URI_TO_URLCSTRING(uri, spec) \
72
0
  nsAutoCString spec; \
73
0
  if (NS_FAILED(aURI->GetSpec(spec))) { \
74
0
    return NS_ERROR_UNEXPECTED; \
75
0
  }
76
77
// Bind URI to statement by index.
78
nsresult // static
79
URIBinder::Bind(mozIStorageStatement* aStatement,
80
                int32_t aIndex,
81
                nsIURI* aURI)
82
0
{
83
0
  NS_ASSERTION(aStatement, "Must have non-null statement");
84
0
  NS_ASSERTION(aURI, "Must have non-null uri");
85
0
86
0
  URI_TO_URLCSTRING(aURI, spec);
87
0
  return URIBinder::Bind(aStatement, aIndex, spec);
88
0
}
89
90
// Statement URLCString to statement by index.
91
nsresult // static
92
URIBinder::Bind(mozIStorageStatement* aStatement,
93
                int32_t index,
94
                const nsACString& aURLString)
95
0
{
96
0
  NS_ASSERTION(aStatement, "Must have non-null statement");
97
0
  return aStatement->BindUTF8StringByIndex(
98
0
    index, StringHead(aURLString, URI_LENGTH_MAX)
99
0
  );
100
0
}
101
102
// Bind URI to statement by name.
103
nsresult // static
104
URIBinder::Bind(mozIStorageStatement* aStatement,
105
                const nsACString& aName,
106
                nsIURI* aURI)
107
0
{
108
0
  NS_ASSERTION(aStatement, "Must have non-null statement");
109
0
  NS_ASSERTION(aURI, "Must have non-null uri");
110
0
111
0
  URI_TO_URLCSTRING(aURI, spec);
112
0
  return URIBinder::Bind(aStatement, aName, spec);
113
0
}
114
115
// Bind URLCString to statement by name.
116
nsresult // static
117
URIBinder::Bind(mozIStorageStatement* aStatement,
118
                const nsACString& aName,
119
                const nsACString& aURLString)
120
0
{
121
0
  NS_ASSERTION(aStatement, "Must have non-null statement");
122
0
  return aStatement->BindUTF8StringByName(
123
0
    aName, StringHead(aURLString, URI_LENGTH_MAX)
124
0
  );
125
0
}
126
127
// Bind URI to params by index.
128
nsresult // static
129
URIBinder::Bind(mozIStorageBindingParams* aParams,
130
                int32_t aIndex,
131
                nsIURI* aURI)
132
0
{
133
0
  NS_ASSERTION(aParams, "Must have non-null statement");
134
0
  NS_ASSERTION(aURI, "Must have non-null uri");
135
0
136
0
  URI_TO_URLCSTRING(aURI, spec);
137
0
  return URIBinder::Bind(aParams, aIndex, spec);
138
0
}
139
140
// Bind URLCString to params by index.
141
nsresult // static
142
URIBinder::Bind(mozIStorageBindingParams* aParams,
143
                int32_t index,
144
                const nsACString& aURLString)
145
0
{
146
0
  NS_ASSERTION(aParams, "Must have non-null statement");
147
0
  return aParams->BindUTF8StringByIndex(
148
0
    index, StringHead(aURLString, URI_LENGTH_MAX)
149
0
  );
150
0
}
151
152
// Bind URI to params by name.
153
nsresult // static
154
URIBinder::Bind(mozIStorageBindingParams* aParams,
155
                const nsACString& aName,
156
                nsIURI* aURI)
157
0
{
158
0
  NS_ASSERTION(aParams, "Must have non-null params array");
159
0
  NS_ASSERTION(aURI, "Must have non-null uri");
160
0
161
0
  URI_TO_URLCSTRING(aURI, spec);
162
0
  return URIBinder::Bind(aParams, aName, spec);
163
0
}
164
165
// Bind URLCString to params by name.
166
nsresult // static
167
URIBinder::Bind(mozIStorageBindingParams* aParams,
168
                const nsACString& aName,
169
                const nsACString& aURLString)
170
0
{
171
0
  NS_ASSERTION(aParams, "Must have non-null params array");
172
0
173
0
  nsresult rv = aParams->BindUTF8StringByName(
174
0
    aName, StringHead(aURLString, URI_LENGTH_MAX)
175
0
  );
176
0
  NS_ENSURE_SUCCESS(rv, rv);
177
0
  return NS_OK;
178
0
}
179
180
#undef URI_TO_URLCSTRING
181
182
nsresult
183
GetReversedHostname(nsIURI* aURI, nsString& aRevHost)
184
0
{
185
0
  nsAutoCString forward8;
186
0
  nsresult rv = aURI->GetHost(forward8);
187
0
  // Not all URIs have a host.
188
0
  if (NS_FAILED(rv))
189
0
    return rv;
190
0
191
0
  // can't do reversing in UTF8, better use 16-bit chars
192
0
  GetReversedHostname(NS_ConvertUTF8toUTF16(forward8), aRevHost);
193
0
  return NS_OK;
194
0
}
195
196
void
197
GetReversedHostname(const nsString& aForward, nsString& aRevHost)
198
0
{
199
0
  ReverseString(aForward, aRevHost);
200
0
  aRevHost.Append(char16_t('.'));
201
0
}
202
203
void
204
ReverseString(const nsString& aInput, nsString& aReversed)
205
0
{
206
0
  aReversed.Truncate(0);
207
0
  for (int32_t i = aInput.Length() - 1; i >= 0; i--) {
208
0
    aReversed.Append(aInput[i]);
209
0
  }
210
0
}
211
212
static
213
nsresult
214
GenerateRandomBytes(uint32_t aSize,
215
                    uint8_t* _buffer)
216
0
{
217
0
  // On Windows, we'll use its built-in cryptographic API.
218
#if defined(XP_WIN)
219
  const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
220
  HCRYPTPROV cryptoProvider;
221
  nsresult rv = history->GetCryptoProvider(cryptoProvider);
222
  NS_ENSURE_SUCCESS(rv, rv);
223
  BOOL rc = CryptGenRandom(cryptoProvider, aSize, _buffer);
224
  return rc ? NS_OK : NS_ERROR_FAILURE;
225
226
  // On Unix, we'll just read in from /dev/urandom.
227
#elif defined(XP_UNIX)
228
0
  NS_ENSURE_ARG_MAX(aSize, INT32_MAX);
229
0
  PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0);
230
0
  nsresult rv = NS_ERROR_FAILURE;
231
0
  if (urandom) {
232
0
    int32_t bytesRead = PR_Read(urandom, _buffer, aSize);
233
0
    if (bytesRead == static_cast<int32_t>(aSize)) {
234
0
      rv = NS_OK;
235
0
    }
236
0
    (void)PR_Close(urandom);
237
0
  }
238
0
  return rv;
239
0
#endif
240
0
}
241
242
nsresult
243
GenerateGUID(nsACString& _guid)
244
0
{
245
0
  _guid.Truncate();
246
0
247
0
  // Request raw random bytes and base64url encode them.  For each set of three
248
0
  // bytes, we get one character.
249
0
  const uint32_t kRequiredBytesLength =
250
0
    static_cast<uint32_t>(GUID_LENGTH / 4 * 3);
251
0
252
0
  uint8_t buffer[kRequiredBytesLength];
253
0
  nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer);
254
0
  NS_ENSURE_SUCCESS(rv, rv);
255
0
256
0
  rv = Base64URLEncode(kRequiredBytesLength, buffer,
257
0
                       Base64URLEncodePaddingPolicy::Omit, _guid);
258
0
  NS_ENSURE_SUCCESS(rv, rv);
259
0
260
0
  NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!");
261
0
  return NS_OK;
262
0
}
263
264
bool
265
IsValidGUID(const nsACString& aGUID)
266
0
{
267
0
  nsCString::size_type len = aGUID.Length();
268
0
  if (len != GUID_LENGTH) {
269
0
    return false;
270
0
  }
271
0
272
0
  for (nsCString::size_type i = 0; i < len; i++ ) {
273
0
    char c = aGUID[i];
274
0
    if ((c >= 'a' && c <= 'z') || // a-z
275
0
        (c >= 'A' && c <= 'Z') || // A-Z
276
0
        (c >= '0' && c <= '9') || // 0-9
277
0
        c == '-' || c == '_') { // - or _
278
0
      continue;
279
0
    }
280
0
    return false;
281
0
  }
282
0
  return true;
283
0
}
284
285
void
286
TruncateTitle(const nsACString& aTitle, nsACString& aTrimmed)
287
0
{
288
0
  if (aTitle.IsVoid()) {
289
0
    return;
290
0
  }
291
0
  aTrimmed = aTitle;
292
0
  if (aTitle.Length() > TITLE_LENGTH_MAX) {
293
0
    aTrimmed = StringHead(aTitle, TITLE_LENGTH_MAX);
294
0
  }
295
0
}
296
297
PRTime
298
0
RoundToMilliseconds(PRTime aTime) {
299
0
  return aTime - (aTime % PR_USEC_PER_MSEC);
300
0
}
301
302
PRTime
303
0
RoundedPRNow() {
304
0
  return RoundToMilliseconds(PR_Now());
305
0
}
306
307
nsresult
308
HashURL(const nsACString& aSpec, const nsACString& aMode, uint64_t *_hash)
309
0
{
310
0
  NS_ENSURE_ARG_POINTER(_hash);
311
0
312
0
  // HashString doesn't stop at the string boundaries if a length is passed to
313
0
  // it, so ensure to pass a proper value.
314
0
  const uint32_t maxLenToHash = std::min(static_cast<uint32_t>(aSpec.Length()),
315
0
                                         MAX_CHARS_TO_HASH);
316
0
317
0
  if (aMode.IsEmpty()) {
318
0
    // URI-like strings (having a prefix before a colon), are handled specially,
319
0
    // as a 48 bit hash, where first 16 bits are the prefix hash, while the
320
0
    // other 32 are the string hash.
321
0
    // The 16 bits have been decided based on the fact hashing all of the IANA
322
0
    // known schemes, plus "places", does not generate collisions.
323
0
    // Since we only care about schemes, we just search in the first 50 chars.
324
0
    // The longest known IANA scheme, at this time, is 30 chars.
325
0
    const nsDependentCSubstring& strHead = StringHead(aSpec, 50);
326
0
    nsACString::const_iterator start, tip, end;
327
0
    strHead.BeginReading(tip);
328
0
    start = tip;
329
0
    strHead.EndReading(end);
330
0
    uint32_t strHash = HashString(aSpec.BeginReading(), maxLenToHash);
331
0
    if (FindCharInReadable(':', tip, end)) {
332
0
      const nsDependentCSubstring& prefix = Substring(start, tip);
333
0
      uint64_t prefixHash = static_cast<uint64_t>(HashString(prefix) & 0x0000FFFF);
334
0
      // The second half of the url is more likely to be unique, so we add it.
335
0
      *_hash = (prefixHash << 32) + strHash;
336
0
    } else {
337
0
      *_hash = strHash;
338
0
    }
339
0
  } else if (aMode.EqualsLiteral("prefix_lo")) {
340
0
    // Keep only 16 bits.
341
0
    *_hash = static_cast<uint64_t>(HashString(aSpec.BeginReading(), maxLenToHash) & 0x0000FFFF) << 32;
342
0
  } else if (aMode.EqualsLiteral("prefix_hi")) {
343
0
    // Keep only 16 bits.
344
0
    *_hash = static_cast<uint64_t>(HashString(aSpec.BeginReading(), maxLenToHash) & 0x0000FFFF) << 32;
345
0
    // Make this a prefix upper bound by filling the lowest 32 bits.
346
0
    *_hash +=  0xFFFFFFFF;
347
0
  } else {
348
0
    return NS_ERROR_FAILURE;
349
0
  }
350
0
351
0
  return NS_OK;
352
0
}
353
354
bool
355
GetHiddenState(bool aIsRedirect,
356
               uint32_t aTransitionType)
357
0
{
358
0
  return aTransitionType == nsINavHistoryService::TRANSITION_FRAMED_LINK ||
359
0
         aTransitionType == nsINavHistoryService::TRANSITION_EMBED ||
360
0
         aIsRedirect;
361
0
}
362
363
nsresult
364
TokenizeQueryString(const nsACString& aQuery,
365
                    nsTArray<QueryKeyValuePair>* aTokens)
366
0
{
367
0
  // Strip off the "place:" prefix
368
0
  const uint32_t prefixlen = 6; // = strlen("place:");
369
0
  nsCString query;
370
0
  if (aQuery.Length() >= prefixlen &&
371
0
      Substring(aQuery, 0, prefixlen).EqualsLiteral("place:"))
372
0
    query = Substring(aQuery, prefixlen);
373
0
  else
374
0
    query = aQuery;
375
0
376
0
  int32_t keyFirstIndex = 0;
377
0
  int32_t equalsIndex = 0;
378
0
  for (uint32_t i = 0; i < query.Length(); i ++) {
379
0
    if (query[i] == '&') {
380
0
      // new clause, save last one
381
0
      if (i - keyFirstIndex > 1) {
382
0
        if (! aTokens->AppendElement(QueryKeyValuePair(query, keyFirstIndex,
383
0
                                                       equalsIndex, i)))
384
0
          return NS_ERROR_OUT_OF_MEMORY;
385
0
      }
386
0
      keyFirstIndex = equalsIndex = i + 1;
387
0
    } else if (query[i] == '=') {
388
0
      equalsIndex = i;
389
0
    }
390
0
  }
391
0
392
0
  // handle last pair, if any
393
0
  if (query.Length() - keyFirstIndex > 1) {
394
0
    if (! aTokens->AppendElement(QueryKeyValuePair(query, keyFirstIndex,
395
0
                                                   equalsIndex, query.Length())))
396
0
      return NS_ERROR_OUT_OF_MEMORY;
397
0
  }
398
0
  return NS_OK;
399
0
}
400
401
void
402
TokensToQueryString(const nsTArray<QueryKeyValuePair> &aTokens,
403
                    nsACString &aQuery)
404
0
{
405
0
  aQuery = NS_LITERAL_CSTRING("place:");
406
0
  for (uint32_t i = 0; i < aTokens.Length(); i++) {
407
0
    if (i > 0) {
408
0
      aQuery.Append("&");
409
0
    }
410
0
    aQuery.Append(aTokens[i].key);
411
0
    aQuery.AppendLiteral("=");
412
0
    aQuery.Append(aTokens[i].value);
413
0
  }
414
0
}
415
416
////////////////////////////////////////////////////////////////////////////////
417
//// AsyncStatementCallbackNotifier
418
419
NS_IMETHODIMP
420
AsyncStatementCallbackNotifier::HandleCompletion(uint16_t aReason)
421
0
{
422
0
  if (aReason != mozIStorageStatementCallback::REASON_FINISHED)
423
0
    return NS_ERROR_UNEXPECTED;
424
0
425
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
426
0
  if (obs) {
427
0
    (void)obs->NotifyObservers(nullptr, mTopic, nullptr);
428
0
  }
429
0
430
0
  return NS_OK;
431
0
}
432
433
////////////////////////////////////////////////////////////////////////////////
434
//// AsyncStatementCallbackNotifier
435
436
NS_IMETHODIMP
437
AsyncStatementTelemetryTimer::HandleCompletion(uint16_t aReason)
438
0
{
439
0
  if (aReason == mozIStorageStatementCallback::REASON_FINISHED) {
440
0
    Telemetry::AccumulateTimeDelta(mHistogramId, mStart);
441
0
  }
442
0
  return NS_OK;
443
0
}
444
445
} // namespace places
446
} // namespace mozilla