1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/Helpers.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,408 @@ 1.4 +/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "Helpers.h" 1.10 +#include "mozIStorageError.h" 1.11 +#include "plbase64.h" 1.12 +#include "prio.h" 1.13 +#include "nsString.h" 1.14 +#include "nsNavHistory.h" 1.15 +#include "mozilla/Services.h" 1.16 + 1.17 +// The length of guids that are used by history and bookmarks. 1.18 +#define GUID_LENGTH 12 1.19 + 1.20 +namespace mozilla { 1.21 +namespace places { 1.22 + 1.23 +//////////////////////////////////////////////////////////////////////////////// 1.24 +//// AsyncStatementCallback 1.25 + 1.26 +NS_IMPL_ISUPPORTS( 1.27 + AsyncStatementCallback 1.28 +, mozIStorageStatementCallback 1.29 +) 1.30 + 1.31 +NS_IMETHODIMP 1.32 +AsyncStatementCallback::HandleResult(mozIStorageResultSet *aResultSet) 1.33 +{ 1.34 + NS_ABORT_IF_FALSE(false, "Was not expecting a resultset, but got it."); 1.35 + return NS_OK; 1.36 +} 1.37 + 1.38 +NS_IMETHODIMP 1.39 +AsyncStatementCallback::HandleCompletion(uint16_t aReason) 1.40 +{ 1.41 + return NS_OK; 1.42 +} 1.43 + 1.44 +NS_IMETHODIMP 1.45 +AsyncStatementCallback::HandleError(mozIStorageError *aError) 1.46 +{ 1.47 +#ifdef DEBUG 1.48 + int32_t result; 1.49 + nsresult rv = aError->GetResult(&result); 1.50 + NS_ENSURE_SUCCESS(rv, rv); 1.51 + nsAutoCString message; 1.52 + rv = aError->GetMessage(message); 1.53 + NS_ENSURE_SUCCESS(rv, rv); 1.54 + 1.55 + nsAutoCString warnMsg; 1.56 + warnMsg.Append("An error occurred while executing an async statement: "); 1.57 + warnMsg.AppendInt(result); 1.58 + warnMsg.Append(" "); 1.59 + warnMsg.Append(message); 1.60 + NS_WARNING(warnMsg.get()); 1.61 +#endif 1.62 + 1.63 + return NS_OK; 1.64 +} 1.65 + 1.66 +#define URI_TO_URLCSTRING(uri, spec) \ 1.67 + nsAutoCString spec; \ 1.68 + if (NS_FAILED(aURI->GetSpec(spec))) { \ 1.69 + return NS_ERROR_UNEXPECTED; \ 1.70 + } 1.71 + 1.72 +// Bind URI to statement by index. 1.73 +nsresult // static 1.74 +URIBinder::Bind(mozIStorageStatement* aStatement, 1.75 + int32_t aIndex, 1.76 + nsIURI* aURI) 1.77 +{ 1.78 + NS_ASSERTION(aStatement, "Must have non-null statement"); 1.79 + NS_ASSERTION(aURI, "Must have non-null uri"); 1.80 + 1.81 + URI_TO_URLCSTRING(aURI, spec); 1.82 + return URIBinder::Bind(aStatement, aIndex, spec); 1.83 +} 1.84 + 1.85 +// Statement URLCString to statement by index. 1.86 +nsresult // static 1.87 +URIBinder::Bind(mozIStorageStatement* aStatement, 1.88 + int32_t index, 1.89 + const nsACString& aURLString) 1.90 +{ 1.91 + NS_ASSERTION(aStatement, "Must have non-null statement"); 1.92 + return aStatement->BindUTF8StringByIndex( 1.93 + index, StringHead(aURLString, URI_LENGTH_MAX) 1.94 + ); 1.95 +} 1.96 + 1.97 +// Bind URI to statement by name. 1.98 +nsresult // static 1.99 +URIBinder::Bind(mozIStorageStatement* aStatement, 1.100 + const nsACString& aName, 1.101 + nsIURI* aURI) 1.102 +{ 1.103 + NS_ASSERTION(aStatement, "Must have non-null statement"); 1.104 + NS_ASSERTION(aURI, "Must have non-null uri"); 1.105 + 1.106 + URI_TO_URLCSTRING(aURI, spec); 1.107 + return URIBinder::Bind(aStatement, aName, spec); 1.108 +} 1.109 + 1.110 +// Bind URLCString to statement by name. 1.111 +nsresult // static 1.112 +URIBinder::Bind(mozIStorageStatement* aStatement, 1.113 + const nsACString& aName, 1.114 + const nsACString& aURLString) 1.115 +{ 1.116 + NS_ASSERTION(aStatement, "Must have non-null statement"); 1.117 + return aStatement->BindUTF8StringByName( 1.118 + aName, StringHead(aURLString, URI_LENGTH_MAX) 1.119 + ); 1.120 +} 1.121 + 1.122 +// Bind URI to params by index. 1.123 +nsresult // static 1.124 +URIBinder::Bind(mozIStorageBindingParams* aParams, 1.125 + int32_t aIndex, 1.126 + nsIURI* aURI) 1.127 +{ 1.128 + NS_ASSERTION(aParams, "Must have non-null statement"); 1.129 + NS_ASSERTION(aURI, "Must have non-null uri"); 1.130 + 1.131 + URI_TO_URLCSTRING(aURI, spec); 1.132 + return URIBinder::Bind(aParams, aIndex, spec); 1.133 +} 1.134 + 1.135 +// Bind URLCString to params by index. 1.136 +nsresult // static 1.137 +URIBinder::Bind(mozIStorageBindingParams* aParams, 1.138 + int32_t index, 1.139 + const nsACString& aURLString) 1.140 +{ 1.141 + NS_ASSERTION(aParams, "Must have non-null statement"); 1.142 + return aParams->BindUTF8StringByIndex( 1.143 + index, StringHead(aURLString, URI_LENGTH_MAX) 1.144 + ); 1.145 +} 1.146 + 1.147 +// Bind URI to params by name. 1.148 +nsresult // static 1.149 +URIBinder::Bind(mozIStorageBindingParams* aParams, 1.150 + const nsACString& aName, 1.151 + nsIURI* aURI) 1.152 +{ 1.153 + NS_ASSERTION(aParams, "Must have non-null params array"); 1.154 + NS_ASSERTION(aURI, "Must have non-null uri"); 1.155 + 1.156 + URI_TO_URLCSTRING(aURI, spec); 1.157 + return URIBinder::Bind(aParams, aName, spec); 1.158 +} 1.159 + 1.160 +// Bind URLCString to params by name. 1.161 +nsresult // static 1.162 +URIBinder::Bind(mozIStorageBindingParams* aParams, 1.163 + const nsACString& aName, 1.164 + const nsACString& aURLString) 1.165 +{ 1.166 + NS_ASSERTION(aParams, "Must have non-null params array"); 1.167 + 1.168 + nsresult rv = aParams->BindUTF8StringByName( 1.169 + aName, StringHead(aURLString, URI_LENGTH_MAX) 1.170 + ); 1.171 + NS_ENSURE_SUCCESS(rv, rv); 1.172 + return NS_OK; 1.173 +} 1.174 + 1.175 +#undef URI_TO_URLCSTRING 1.176 + 1.177 +nsresult 1.178 +GetReversedHostname(nsIURI* aURI, nsString& aRevHost) 1.179 +{ 1.180 + nsAutoCString forward8; 1.181 + nsresult rv = aURI->GetHost(forward8); 1.182 + // Not all URIs have a host. 1.183 + if (NS_FAILED(rv)) 1.184 + return rv; 1.185 + 1.186 + // can't do reversing in UTF8, better use 16-bit chars 1.187 + GetReversedHostname(NS_ConvertUTF8toUTF16(forward8), aRevHost); 1.188 + return NS_OK; 1.189 +} 1.190 + 1.191 +void 1.192 +GetReversedHostname(const nsString& aForward, nsString& aRevHost) 1.193 +{ 1.194 + ReverseString(aForward, aRevHost); 1.195 + aRevHost.Append(char16_t('.')); 1.196 +} 1.197 + 1.198 +void 1.199 +ReverseString(const nsString& aInput, nsString& aReversed) 1.200 +{ 1.201 + aReversed.Truncate(0); 1.202 + for (int32_t i = aInput.Length() - 1; i >= 0; i--) { 1.203 + aReversed.Append(aInput[i]); 1.204 + } 1.205 +} 1.206 + 1.207 +static 1.208 +nsresult 1.209 +Base64urlEncode(const uint8_t* aBytes, 1.210 + uint32_t aNumBytes, 1.211 + nsCString& _result) 1.212 +{ 1.213 + // SetLength does not set aside space for null termination. PL_Base64Encode 1.214 + // will not null terminate, however, nsCStrings must be null terminated. As a 1.215 + // result, we set the capacity to be one greater than what we need, and the 1.216 + // length to our desired length. 1.217 + uint32_t length = (aNumBytes + 2) / 3 * 4; // +2 due to integer math. 1.218 + NS_ENSURE_TRUE(_result.SetCapacity(length + 1, fallible_t()), 1.219 + NS_ERROR_OUT_OF_MEMORY); 1.220 + _result.SetLength(length); 1.221 + (void)PL_Base64Encode(reinterpret_cast<const char*>(aBytes), aNumBytes, 1.222 + _result.BeginWriting()); 1.223 + 1.224 + // base64url encoding is defined in RFC 4648. It replaces the last two 1.225 + // alphabet characters of base64 encoding with '-' and '_' respectively. 1.226 + _result.ReplaceChar('+', '-'); 1.227 + _result.ReplaceChar('/', '_'); 1.228 + return NS_OK; 1.229 +} 1.230 + 1.231 +#ifdef XP_WIN 1.232 +} // namespace places 1.233 +} // namespace mozilla 1.234 + 1.235 +// Included here because windows.h conflicts with the use of mozIStorageError 1.236 +// above, but make sure that these are not included inside mozilla::places. 1.237 +#include <windows.h> 1.238 +#include <wincrypt.h> 1.239 + 1.240 +namespace mozilla { 1.241 +namespace places { 1.242 +#endif 1.243 + 1.244 +static 1.245 +nsresult 1.246 +GenerateRandomBytes(uint32_t aSize, 1.247 + uint8_t* _buffer) 1.248 +{ 1.249 + // On Windows, we'll use its built-in cryptographic API. 1.250 +#if defined(XP_WIN) 1.251 + HCRYPTPROV cryptoProvider; 1.252 + BOOL rc = CryptAcquireContext(&cryptoProvider, 0, 0, PROV_RSA_FULL, 1.253 + CRYPT_VERIFYCONTEXT | CRYPT_SILENT); 1.254 + if (rc) { 1.255 + rc = CryptGenRandom(cryptoProvider, aSize, _buffer); 1.256 + (void)CryptReleaseContext(cryptoProvider, 0); 1.257 + } 1.258 + return rc ? NS_OK : NS_ERROR_FAILURE; 1.259 + 1.260 + // On Unix, we'll just read in from /dev/urandom. 1.261 +#elif defined(XP_UNIX) 1.262 + NS_ENSURE_ARG_MAX(aSize, INT32_MAX); 1.263 + PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0); 1.264 + nsresult rv = NS_ERROR_FAILURE; 1.265 + if (urandom) { 1.266 + int32_t bytesRead = PR_Read(urandom, _buffer, aSize); 1.267 + if (bytesRead == static_cast<int32_t>(aSize)) { 1.268 + rv = NS_OK; 1.269 + } 1.270 + (void)PR_Close(urandom); 1.271 + } 1.272 + return rv; 1.273 +#endif 1.274 +} 1.275 + 1.276 +nsresult 1.277 +GenerateGUID(nsCString& _guid) 1.278 +{ 1.279 + _guid.Truncate(); 1.280 + 1.281 + // Request raw random bytes and base64url encode them. For each set of three 1.282 + // bytes, we get one character. 1.283 + const uint32_t kRequiredBytesLength = 1.284 + static_cast<uint32_t>(GUID_LENGTH / 4 * 3); 1.285 + 1.286 + uint8_t buffer[kRequiredBytesLength]; 1.287 + nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer); 1.288 + NS_ENSURE_SUCCESS(rv, rv); 1.289 + 1.290 + rv = Base64urlEncode(buffer, kRequiredBytesLength, _guid); 1.291 + NS_ENSURE_SUCCESS(rv, rv); 1.292 + 1.293 + NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!"); 1.294 + return NS_OK; 1.295 +} 1.296 + 1.297 +bool 1.298 +IsValidGUID(const nsACString& aGUID) 1.299 +{ 1.300 + nsCString::size_type len = aGUID.Length(); 1.301 + if (len != GUID_LENGTH) { 1.302 + return false; 1.303 + } 1.304 + 1.305 + for (nsCString::size_type i = 0; i < len; i++ ) { 1.306 + char c = aGUID[i]; 1.307 + if ((c >= 'a' && c <= 'z') || // a-z 1.308 + (c >= 'A' && c <= 'Z') || // A-Z 1.309 + (c >= '0' && c <= '9') || // 0-9 1.310 + c == '-' || c == '_') { // - or _ 1.311 + continue; 1.312 + } 1.313 + return false; 1.314 + } 1.315 + return true; 1.316 +} 1.317 + 1.318 +void 1.319 +TruncateTitle(const nsACString& aTitle, nsACString& aTrimmed) 1.320 +{ 1.321 + aTrimmed = aTitle; 1.322 + if (aTitle.Length() > TITLE_LENGTH_MAX) { 1.323 + aTrimmed = StringHead(aTitle, TITLE_LENGTH_MAX); 1.324 + } 1.325 +} 1.326 + 1.327 +void 1.328 +ForceWALCheckpoint() 1.329 +{ 1.330 + nsRefPtr<Database> DB = Database::GetDatabase(); 1.331 + if (DB) { 1.332 + nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement( 1.333 + "pragma wal_checkpoint " 1.334 + ); 1.335 + if (stmt) { 1.336 + nsCOMPtr<mozIStoragePendingStatement> handle; 1.337 + (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(handle)); 1.338 + } 1.339 + } 1.340 +} 1.341 + 1.342 +bool 1.343 +GetHiddenState(bool aIsRedirect, 1.344 + uint32_t aTransitionType) 1.345 +{ 1.346 + return aTransitionType == nsINavHistoryService::TRANSITION_FRAMED_LINK || 1.347 + aTransitionType == nsINavHistoryService::TRANSITION_EMBED || 1.348 + aIsRedirect; 1.349 +} 1.350 + 1.351 +//////////////////////////////////////////////////////////////////////////////// 1.352 +//// PlacesEvent 1.353 + 1.354 +PlacesEvent::PlacesEvent(const char* aTopic) 1.355 +: mTopic(aTopic) 1.356 +{ 1.357 +} 1.358 + 1.359 +NS_IMETHODIMP 1.360 +PlacesEvent::Run() 1.361 +{ 1.362 + Notify(); 1.363 + return NS_OK; 1.364 +} 1.365 + 1.366 +void 1.367 +PlacesEvent::Notify() 1.368 +{ 1.369 + NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!"); 1.370 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.371 + if (obs) { 1.372 + (void)obs->NotifyObservers(nullptr, mTopic, nullptr); 1.373 + } 1.374 +} 1.375 + 1.376 +NS_IMPL_ISUPPORTS( 1.377 + PlacesEvent 1.378 +, nsIRunnable 1.379 +) 1.380 + 1.381 +//////////////////////////////////////////////////////////////////////////////// 1.382 +//// AsyncStatementCallbackNotifier 1.383 + 1.384 +NS_IMETHODIMP 1.385 +AsyncStatementCallbackNotifier::HandleCompletion(uint16_t aReason) 1.386 +{ 1.387 + if (aReason != mozIStorageStatementCallback::REASON_FINISHED) 1.388 + return NS_ERROR_UNEXPECTED; 1.389 + 1.390 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.391 + if (obs) { 1.392 + (void)obs->NotifyObservers(nullptr, mTopic, nullptr); 1.393 + } 1.394 + 1.395 + return NS_OK; 1.396 +} 1.397 + 1.398 +//////////////////////////////////////////////////////////////////////////////// 1.399 +//// AsyncStatementCallbackNotifier 1.400 + 1.401 +NS_IMETHODIMP 1.402 +AsyncStatementTelemetryTimer::HandleCompletion(uint16_t aReason) 1.403 +{ 1.404 + if (aReason == mozIStorageStatementCallback::REASON_FINISHED) { 1.405 + Telemetry::AccumulateTimeDelta(mHistogramId, mStart); 1.406 + } 1.407 + return NS_OK; 1.408 +} 1.409 + 1.410 +} // namespace places 1.411 +} // namespace mozilla