toolkit/components/places/Helpers.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:c1679723935b
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 "plbase64.h"
9 #include "prio.h"
10 #include "nsString.h"
11 #include "nsNavHistory.h"
12 #include "mozilla/Services.h"
13
14 // The length of guids that are used by history and bookmarks.
15 #define GUID_LENGTH 12
16
17 namespace mozilla {
18 namespace places {
19
20 ////////////////////////////////////////////////////////////////////////////////
21 //// AsyncStatementCallback
22
23 NS_IMPL_ISUPPORTS(
24 AsyncStatementCallback
25 , mozIStorageStatementCallback
26 )
27
28 NS_IMETHODIMP
29 AsyncStatementCallback::HandleResult(mozIStorageResultSet *aResultSet)
30 {
31 NS_ABORT_IF_FALSE(false, "Was not expecting a resultset, but got it.");
32 return NS_OK;
33 }
34
35 NS_IMETHODIMP
36 AsyncStatementCallback::HandleCompletion(uint16_t aReason)
37 {
38 return NS_OK;
39 }
40
41 NS_IMETHODIMP
42 AsyncStatementCallback::HandleError(mozIStorageError *aError)
43 {
44 #ifdef DEBUG
45 int32_t result;
46 nsresult rv = aError->GetResult(&result);
47 NS_ENSURE_SUCCESS(rv, rv);
48 nsAutoCString message;
49 rv = aError->GetMessage(message);
50 NS_ENSURE_SUCCESS(rv, rv);
51
52 nsAutoCString warnMsg;
53 warnMsg.Append("An error occurred while executing an async statement: ");
54 warnMsg.AppendInt(result);
55 warnMsg.Append(" ");
56 warnMsg.Append(message);
57 NS_WARNING(warnMsg.get());
58 #endif
59
60 return NS_OK;
61 }
62
63 #define URI_TO_URLCSTRING(uri, spec) \
64 nsAutoCString spec; \
65 if (NS_FAILED(aURI->GetSpec(spec))) { \
66 return NS_ERROR_UNEXPECTED; \
67 }
68
69 // Bind URI to statement by index.
70 nsresult // static
71 URIBinder::Bind(mozIStorageStatement* aStatement,
72 int32_t aIndex,
73 nsIURI* aURI)
74 {
75 NS_ASSERTION(aStatement, "Must have non-null statement");
76 NS_ASSERTION(aURI, "Must have non-null uri");
77
78 URI_TO_URLCSTRING(aURI, spec);
79 return URIBinder::Bind(aStatement, aIndex, spec);
80 }
81
82 // Statement URLCString to statement by index.
83 nsresult // static
84 URIBinder::Bind(mozIStorageStatement* aStatement,
85 int32_t index,
86 const nsACString& aURLString)
87 {
88 NS_ASSERTION(aStatement, "Must have non-null statement");
89 return aStatement->BindUTF8StringByIndex(
90 index, StringHead(aURLString, URI_LENGTH_MAX)
91 );
92 }
93
94 // Bind URI to statement by name.
95 nsresult // static
96 URIBinder::Bind(mozIStorageStatement* aStatement,
97 const nsACString& aName,
98 nsIURI* aURI)
99 {
100 NS_ASSERTION(aStatement, "Must have non-null statement");
101 NS_ASSERTION(aURI, "Must have non-null uri");
102
103 URI_TO_URLCSTRING(aURI, spec);
104 return URIBinder::Bind(aStatement, aName, spec);
105 }
106
107 // Bind URLCString to statement by name.
108 nsresult // static
109 URIBinder::Bind(mozIStorageStatement* aStatement,
110 const nsACString& aName,
111 const nsACString& aURLString)
112 {
113 NS_ASSERTION(aStatement, "Must have non-null statement");
114 return aStatement->BindUTF8StringByName(
115 aName, StringHead(aURLString, URI_LENGTH_MAX)
116 );
117 }
118
119 // Bind URI to params by index.
120 nsresult // static
121 URIBinder::Bind(mozIStorageBindingParams* aParams,
122 int32_t aIndex,
123 nsIURI* aURI)
124 {
125 NS_ASSERTION(aParams, "Must have non-null statement");
126 NS_ASSERTION(aURI, "Must have non-null uri");
127
128 URI_TO_URLCSTRING(aURI, spec);
129 return URIBinder::Bind(aParams, aIndex, spec);
130 }
131
132 // Bind URLCString to params by index.
133 nsresult // static
134 URIBinder::Bind(mozIStorageBindingParams* aParams,
135 int32_t index,
136 const nsACString& aURLString)
137 {
138 NS_ASSERTION(aParams, "Must have non-null statement");
139 return aParams->BindUTF8StringByIndex(
140 index, StringHead(aURLString, URI_LENGTH_MAX)
141 );
142 }
143
144 // Bind URI to params by name.
145 nsresult // static
146 URIBinder::Bind(mozIStorageBindingParams* aParams,
147 const nsACString& aName,
148 nsIURI* aURI)
149 {
150 NS_ASSERTION(aParams, "Must have non-null params array");
151 NS_ASSERTION(aURI, "Must have non-null uri");
152
153 URI_TO_URLCSTRING(aURI, spec);
154 return URIBinder::Bind(aParams, aName, spec);
155 }
156
157 // Bind URLCString to params by name.
158 nsresult // static
159 URIBinder::Bind(mozIStorageBindingParams* aParams,
160 const nsACString& aName,
161 const nsACString& aURLString)
162 {
163 NS_ASSERTION(aParams, "Must have non-null params array");
164
165 nsresult rv = aParams->BindUTF8StringByName(
166 aName, StringHead(aURLString, URI_LENGTH_MAX)
167 );
168 NS_ENSURE_SUCCESS(rv, rv);
169 return NS_OK;
170 }
171
172 #undef URI_TO_URLCSTRING
173
174 nsresult
175 GetReversedHostname(nsIURI* aURI, nsString& aRevHost)
176 {
177 nsAutoCString forward8;
178 nsresult rv = aURI->GetHost(forward8);
179 // Not all URIs have a host.
180 if (NS_FAILED(rv))
181 return rv;
182
183 // can't do reversing in UTF8, better use 16-bit chars
184 GetReversedHostname(NS_ConvertUTF8toUTF16(forward8), aRevHost);
185 return NS_OK;
186 }
187
188 void
189 GetReversedHostname(const nsString& aForward, nsString& aRevHost)
190 {
191 ReverseString(aForward, aRevHost);
192 aRevHost.Append(char16_t('.'));
193 }
194
195 void
196 ReverseString(const nsString& aInput, nsString& aReversed)
197 {
198 aReversed.Truncate(0);
199 for (int32_t i = aInput.Length() - 1; i >= 0; i--) {
200 aReversed.Append(aInput[i]);
201 }
202 }
203
204 static
205 nsresult
206 Base64urlEncode(const uint8_t* aBytes,
207 uint32_t aNumBytes,
208 nsCString& _result)
209 {
210 // SetLength does not set aside space for null termination. PL_Base64Encode
211 // will not null terminate, however, nsCStrings must be null terminated. As a
212 // result, we set the capacity to be one greater than what we need, and the
213 // length to our desired length.
214 uint32_t length = (aNumBytes + 2) / 3 * 4; // +2 due to integer math.
215 NS_ENSURE_TRUE(_result.SetCapacity(length + 1, fallible_t()),
216 NS_ERROR_OUT_OF_MEMORY);
217 _result.SetLength(length);
218 (void)PL_Base64Encode(reinterpret_cast<const char*>(aBytes), aNumBytes,
219 _result.BeginWriting());
220
221 // base64url encoding is defined in RFC 4648. It replaces the last two
222 // alphabet characters of base64 encoding with '-' and '_' respectively.
223 _result.ReplaceChar('+', '-');
224 _result.ReplaceChar('/', '_');
225 return NS_OK;
226 }
227
228 #ifdef XP_WIN
229 } // namespace places
230 } // namespace mozilla
231
232 // Included here because windows.h conflicts with the use of mozIStorageError
233 // above, but make sure that these are not included inside mozilla::places.
234 #include <windows.h>
235 #include <wincrypt.h>
236
237 namespace mozilla {
238 namespace places {
239 #endif
240
241 static
242 nsresult
243 GenerateRandomBytes(uint32_t aSize,
244 uint8_t* _buffer)
245 {
246 // On Windows, we'll use its built-in cryptographic API.
247 #if defined(XP_WIN)
248 HCRYPTPROV cryptoProvider;
249 BOOL rc = CryptAcquireContext(&cryptoProvider, 0, 0, PROV_RSA_FULL,
250 CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
251 if (rc) {
252 rc = CryptGenRandom(cryptoProvider, aSize, _buffer);
253 (void)CryptReleaseContext(cryptoProvider, 0);
254 }
255 return rc ? NS_OK : NS_ERROR_FAILURE;
256
257 // On Unix, we'll just read in from /dev/urandom.
258 #elif defined(XP_UNIX)
259 NS_ENSURE_ARG_MAX(aSize, INT32_MAX);
260 PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0);
261 nsresult rv = NS_ERROR_FAILURE;
262 if (urandom) {
263 int32_t bytesRead = PR_Read(urandom, _buffer, aSize);
264 if (bytesRead == static_cast<int32_t>(aSize)) {
265 rv = NS_OK;
266 }
267 (void)PR_Close(urandom);
268 }
269 return rv;
270 #endif
271 }
272
273 nsresult
274 GenerateGUID(nsCString& _guid)
275 {
276 _guid.Truncate();
277
278 // Request raw random bytes and base64url encode them. For each set of three
279 // bytes, we get one character.
280 const uint32_t kRequiredBytesLength =
281 static_cast<uint32_t>(GUID_LENGTH / 4 * 3);
282
283 uint8_t buffer[kRequiredBytesLength];
284 nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer);
285 NS_ENSURE_SUCCESS(rv, rv);
286
287 rv = Base64urlEncode(buffer, kRequiredBytesLength, _guid);
288 NS_ENSURE_SUCCESS(rv, rv);
289
290 NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!");
291 return NS_OK;
292 }
293
294 bool
295 IsValidGUID(const nsACString& aGUID)
296 {
297 nsCString::size_type len = aGUID.Length();
298 if (len != GUID_LENGTH) {
299 return false;
300 }
301
302 for (nsCString::size_type i = 0; i < len; i++ ) {
303 char c = aGUID[i];
304 if ((c >= 'a' && c <= 'z') || // a-z
305 (c >= 'A' && c <= 'Z') || // A-Z
306 (c >= '0' && c <= '9') || // 0-9
307 c == '-' || c == '_') { // - or _
308 continue;
309 }
310 return false;
311 }
312 return true;
313 }
314
315 void
316 TruncateTitle(const nsACString& aTitle, nsACString& aTrimmed)
317 {
318 aTrimmed = aTitle;
319 if (aTitle.Length() > TITLE_LENGTH_MAX) {
320 aTrimmed = StringHead(aTitle, TITLE_LENGTH_MAX);
321 }
322 }
323
324 void
325 ForceWALCheckpoint()
326 {
327 nsRefPtr<Database> DB = Database::GetDatabase();
328 if (DB) {
329 nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
330 "pragma wal_checkpoint "
331 );
332 if (stmt) {
333 nsCOMPtr<mozIStoragePendingStatement> handle;
334 (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(handle));
335 }
336 }
337 }
338
339 bool
340 GetHiddenState(bool aIsRedirect,
341 uint32_t aTransitionType)
342 {
343 return aTransitionType == nsINavHistoryService::TRANSITION_FRAMED_LINK ||
344 aTransitionType == nsINavHistoryService::TRANSITION_EMBED ||
345 aIsRedirect;
346 }
347
348 ////////////////////////////////////////////////////////////////////////////////
349 //// PlacesEvent
350
351 PlacesEvent::PlacesEvent(const char* aTopic)
352 : mTopic(aTopic)
353 {
354 }
355
356 NS_IMETHODIMP
357 PlacesEvent::Run()
358 {
359 Notify();
360 return NS_OK;
361 }
362
363 void
364 PlacesEvent::Notify()
365 {
366 NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!");
367 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
368 if (obs) {
369 (void)obs->NotifyObservers(nullptr, mTopic, nullptr);
370 }
371 }
372
373 NS_IMPL_ISUPPORTS(
374 PlacesEvent
375 , nsIRunnable
376 )
377
378 ////////////////////////////////////////////////////////////////////////////////
379 //// AsyncStatementCallbackNotifier
380
381 NS_IMETHODIMP
382 AsyncStatementCallbackNotifier::HandleCompletion(uint16_t aReason)
383 {
384 if (aReason != mozIStorageStatementCallback::REASON_FINISHED)
385 return NS_ERROR_UNEXPECTED;
386
387 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
388 if (obs) {
389 (void)obs->NotifyObservers(nullptr, mTopic, nullptr);
390 }
391
392 return NS_OK;
393 }
394
395 ////////////////////////////////////////////////////////////////////////////////
396 //// AsyncStatementCallbackNotifier
397
398 NS_IMETHODIMP
399 AsyncStatementTelemetryTimer::HandleCompletion(uint16_t aReason)
400 {
401 if (aReason == mozIStorageStatementCallback::REASON_FINISHED) {
402 Telemetry::AccumulateTimeDelta(mHistogramId, mStart);
403 }
404 return NS_OK;
405 }
406
407 } // namespace places
408 } // namespace mozilla

mercurial