1 /*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31 #include "modules/webdatabase/DatabaseBackendBase.h"
32
33 #include "core/dom/ExceptionCode.h"
34 #include "platform/Logging.h"
35 #include "modules/webdatabase/DatabaseAuthorizer.h"
36 #include "modules/webdatabase/DatabaseBase.h"
37 #include "modules/webdatabase/DatabaseContext.h"
38 #include "modules/webdatabase/DatabaseManager.h"
39 #include "modules/webdatabase/DatabaseTracker.h"
40 #include "modules/webdatabase/sqlite/SQLiteStatement.h"
41 #include "modules/webdatabase/sqlite/SQLiteTransaction.h"
42 #include "platform/weborigin/DatabaseIdentifier.h"
43 #include "public/platform/Platform.h"
44 #include "public/platform/WebDatabaseObserver.h"
45 #include "wtf/HashMap.h"
46 #include "wtf/HashSet.h"
47 #include "wtf/PassRefPtr.h"
48 #include "wtf/StdLibExtras.h"
49 #include "wtf/text/CString.h"
50 #include "wtf/text/StringHash.h"
51
52 // Registering "opened" databases with the DatabaseTracker
53 // =======================================================
54 // The DatabaseTracker maintains a list of databases that have been
55 // "opened" so that the client can call interrupt or delete on every database
56 // associated with a DatabaseContext.
57 //
58 // We will only call DatabaseTracker::addOpenDatabase() to add the database
59 // to the tracker as opened when we've succeeded in opening the database,
60 // and will set m_opened to true. Similarly, we only call
61 // DatabaseTracker::removeOpenDatabase() to remove the database from the
62 // tracker when we set m_opened to false in closeDatabase(). This sets up
63 // a simple symmetry between open and close operations, and a direct
64 // correlation to adding and removing databases from the tracker's list,
65 // thus ensuring that we have a correct list for the interrupt and
66 // delete operations to work on.
67 //
68 // The only databases instances not tracked by the tracker's open database
69 // list are the ones that have not been added yet, or the ones that we
70 // attempted an open on but failed to. Such instances only exist in the
71 // DatabaseServer's factory methods for creating database backends.
72 //
73 // The factory methods will either call openAndVerifyVersion() or
74 // performOpenAndVerify(). These methods will add the newly instantiated
75 // database backend if they succeed in opening the requested database.
76 // In the case of failure to open the database, the factory methods will
77 // simply discard the newly instantiated database backend when they return.
78 // The ref counting mechanims will automatically destruct the un-added
79 // (and un-returned) databases instances.
80
81 namespace WebCore {
82
83 static const char versionKey[] = "WebKitDatabaseVersionKey";
84 static const char infoTableName[] = "__WebKitDatabaseInfoTable__";
85
formatErrorMessage(const char * message,int sqliteErrorCode,const char * sqliteErrorMessage)86 static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage)
87 {
88 return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage);
89 }
90
retrieveTextResultFromDatabase(SQLiteDatabase & db,const String & query,String & resultString)91 static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
92 {
93 SQLiteStatement statement(db, query);
94 int result = statement.prepare();
95
96 if (result != SQLResultOk) {
97 WTF_LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
98 return false;
99 }
100
101 result = statement.step();
102 if (result == SQLResultRow) {
103 resultString = statement.getColumnText(0);
104 return true;
105 }
106 if (result == SQLResultDone) {
107 resultString = String();
108 return true;
109 }
110
111 WTF_LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
112 return false;
113 }
114
setTextValueInDatabase(SQLiteDatabase & db,const String & query,const String & value)115 static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
116 {
117 SQLiteStatement statement(db, query);
118 int result = statement.prepare();
119
120 if (result != SQLResultOk) {
121 WTF_LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
122 return false;
123 }
124
125 statement.bindText(1, value);
126
127 result = statement.step();
128 if (result != SQLResultDone) {
129 WTF_LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
130 return false;
131 }
132
133 return true;
134 }
135
136 // FIXME: move all guid-related functions to a DatabaseVersionTracker class.
guidMutex()137 static Mutex& guidMutex()
138 {
139 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
140 return mutex;
141 }
142
143 typedef HashMap<DatabaseGuid, String> GuidVersionMap;
guidToVersionMap()144 static GuidVersionMap& guidToVersionMap()
145 {
146 // Ensure the the mutex is locked.
147 ASSERT(!guidMutex().tryLock());
148 DEFINE_STATIC_LOCAL(GuidVersionMap, map, ());
149 return map;
150 }
151
152 // NOTE: Caller must lock guidMutex().
updateGuidVersionMap(DatabaseGuid guid,String newVersion)153 static inline void updateGuidVersionMap(DatabaseGuid guid, String newVersion)
154 {
155 // Ensure the the mutex is locked.
156 ASSERT(!guidMutex().tryLock());
157
158 // Note: It is not safe to put an empty string into the guidToVersionMap() map.
159 // That's because the map is cross-thread, but empty strings are per-thread.
160 // The copy() function makes a version of the string you can use on the current
161 // thread, but we need a string we can keep in a cross-thread data structure.
162 // FIXME: This is a quite-awkward restriction to have to program with.
163
164 // Map null string to empty string (see comment above).
165 guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy());
166 }
167
168 typedef HashMap<DatabaseGuid, HashSet<DatabaseBackendBase*>*> GuidDatabaseMap;
guidToDatabaseMap()169 static GuidDatabaseMap& guidToDatabaseMap()
170 {
171 // Ensure the the mutex is locked.
172 ASSERT(!guidMutex().tryLock());
173 DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ());
174 return map;
175 }
176
guidForOriginAndName(const String & origin,const String & name)177 static DatabaseGuid guidForOriginAndName(const String& origin, const String& name)
178 {
179 // Ensure the the mutex is locked.
180 ASSERT(!guidMutex().tryLock());
181
182 String stringID = origin + "/" + name;
183
184 typedef HashMap<String, int> IDGuidMap;
185 DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ());
186 DatabaseGuid guid = stringIdentifierToGUIDMap.get(stringID);
187 if (!guid) {
188 static int currentNewGUID = 1;
189 guid = currentNewGUID++;
190 stringIdentifierToGUIDMap.set(stringID, guid);
191 }
192
193 return guid;
194 }
195
196 // static
databaseInfoTableName()197 const char* DatabaseBackendBase::databaseInfoTableName()
198 {
199 return infoTableName;
200 }
201
DatabaseBackendBase(PassRefPtr<DatabaseContext> databaseContext,const String & name,const String & expectedVersion,const String & displayName,unsigned long estimatedSize,DatabaseType databaseType)202 DatabaseBackendBase::DatabaseBackendBase(PassRefPtr<DatabaseContext> databaseContext, const String& name,
203 const String& expectedVersion, const String& displayName, unsigned long estimatedSize, DatabaseType databaseType)
204 : m_databaseContext(databaseContext)
205 , m_name(name.isolatedCopy())
206 , m_expectedVersion(expectedVersion.isolatedCopy())
207 , m_displayName(displayName.isolatedCopy())
208 , m_estimatedSize(estimatedSize)
209 , m_guid(0)
210 , m_opened(false)
211 , m_new(false)
212 , m_isSyncDatabase(databaseType == DatabaseType::Sync)
213 {
214 m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy();
215
216 m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName);
217
218 if (m_name.isNull())
219 m_name = "";
220
221 {
222 MutexLocker locker(guidMutex());
223 m_guid = guidForOriginAndName(securityOrigin()->toString(), name);
224 HashSet<DatabaseBackendBase*>* hashSet = guidToDatabaseMap().get(m_guid);
225 if (!hashSet) {
226 hashSet = new HashSet<DatabaseBackendBase*>;
227 guidToDatabaseMap().set(m_guid, hashSet);
228 }
229
230 hashSet->add(this);
231 }
232
233 m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name);
234 }
235
~DatabaseBackendBase()236 DatabaseBackendBase::~DatabaseBackendBase()
237 {
238 // SQLite is "multi-thread safe", but each database handle can only be used
239 // on a single thread at a time.
240 //
241 // For DatabaseBackend, we open the SQLite database on the DatabaseThread,
242 // and hence we should also close it on that same thread. This means that the
243 // SQLite database need to be closed by another mechanism (see
244 // DatabaseContext::stopDatabases()). By the time we get here, the SQLite
245 // database should have already been closed.
246
247 ASSERT(!m_opened);
248 }
249
closeDatabase()250 void DatabaseBackendBase::closeDatabase()
251 {
252 if (!m_opened)
253 return;
254
255 m_sqliteDatabase.close();
256 m_opened = false;
257 // See comment at the top this file regarding calling removeOpenDatabase().
258 DatabaseTracker::tracker().removeOpenDatabase(this);
259 {
260 MutexLocker locker(guidMutex());
261
262 HashSet<DatabaseBackendBase*>* hashSet = guidToDatabaseMap().get(m_guid);
263 ASSERT(hashSet);
264 ASSERT(hashSet->contains(this));
265 hashSet->remove(this);
266 if (hashSet->isEmpty()) {
267 guidToDatabaseMap().remove(m_guid);
268 delete hashSet;
269 guidToVersionMap().remove(m_guid);
270 }
271 }
272 }
273
version() const274 String DatabaseBackendBase::version() const
275 {
276 // Note: In multi-process browsers the cached value may be accurate, but we cannot read the
277 // actual version from the database without potentially inducing a deadlock.
278 // FIXME: Add an async version getter to the DatabaseAPI.
279 return getCachedVersion();
280 }
281
282 class DoneCreatingDatabaseOnExitCaller {
283 public:
DoneCreatingDatabaseOnExitCaller(DatabaseBackendBase * database)284 DoneCreatingDatabaseOnExitCaller(DatabaseBackendBase* database)
285 : m_database(database)
286 , m_openSucceeded(false)
287 {
288 }
~DoneCreatingDatabaseOnExitCaller()289 ~DoneCreatingDatabaseOnExitCaller()
290 {
291 if (!m_openSucceeded)
292 DatabaseTracker::tracker().failedToOpenDatabase(m_database);
293 }
294
setOpenSucceeded()295 void setOpenSucceeded() { m_openSucceeded = true; }
296
297 private:
298 DatabaseBackendBase* m_database;
299 bool m_openSucceeded;
300 };
301
performOpenAndVerify(bool shouldSetVersionInNewDatabase,DatabaseError & error,String & errorMessage)302 bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage)
303 {
304 DoneCreatingDatabaseOnExitCaller onExitCaller(this);
305 ASSERT(errorMessage.isEmpty());
306 ASSERT(error == DatabaseError::None); // Better not have any errors already.
307 error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below.
308
309 const int maxSqliteBusyWaitTime = 30000;
310
311 if (!m_sqliteDatabase.open(m_filename, true)) {
312 reportOpenDatabaseResult(1, InvalidStateError, m_sqliteDatabase.lastError());
313 errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
314 return false;
315 }
316 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum())
317 WTF_LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
318
319 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
320
321 String currentVersion;
322 {
323 MutexLocker locker(guidMutex());
324
325 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid);
326 if (entry != guidToVersionMap().end()) {
327 // Map null string to empty string (see updateGuidVersionMap()).
328 currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy();
329 WTF_LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
330
331 // Note: In multi-process browsers the cached value may be inaccurate, but
332 // we cannot read the actual version from the database without potentially
333 // inducing a form of deadlock, a busytimeout error when trying to
334 // access the database. So we'll use the cached value if we're unable to read
335 // the value from the database file without waiting.
336 // FIXME: Add an async openDatabase method to the DatabaseAPI.
337 const int noSqliteBusyWaitTime = 0;
338 m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime);
339 String versionFromDatabase;
340 if (getVersionFromDatabase(versionFromDatabase, false)) {
341 currentVersion = versionFromDatabase;
342 updateGuidVersionMap(m_guid, currentVersion);
343 }
344 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
345 } else {
346 WTF_LOG(StorageAPI, "No cached version for guid %i", m_guid);
347
348 SQLiteTransaction transaction(m_sqliteDatabase);
349 transaction.begin();
350 if (!transaction.inProgress()) {
351 reportOpenDatabaseResult(2, InvalidStateError, m_sqliteDatabase.lastError());
352 errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
353 m_sqliteDatabase.close();
354 return false;
355 }
356
357 String tableName(infoTableName);
358 if (!m_sqliteDatabase.tableExists(tableName)) {
359 m_new = true;
360
361 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
362 reportOpenDatabaseResult(3, InvalidStateError, m_sqliteDatabase.lastError());
363 errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
364 transaction.rollback();
365 m_sqliteDatabase.close();
366 return false;
367 }
368 } else if (!getVersionFromDatabase(currentVersion, false)) {
369 reportOpenDatabaseResult(4, InvalidStateError, m_sqliteDatabase.lastError());
370 errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
371 transaction.rollback();
372 m_sqliteDatabase.close();
373 return false;
374 }
375
376 if (currentVersion.length()) {
377 WTF_LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
378 } else if (!m_new || shouldSetVersionInNewDatabase) {
379 WTF_LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
380 if (!setVersionInDatabase(m_expectedVersion, false)) {
381 reportOpenDatabaseResult(5, InvalidStateError, m_sqliteDatabase.lastError());
382 errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
383 transaction.rollback();
384 m_sqliteDatabase.close();
385 return false;
386 }
387 currentVersion = m_expectedVersion;
388 }
389 updateGuidVersionMap(m_guid, currentVersion);
390 transaction.commit();
391 }
392 }
393
394 if (currentVersion.isNull()) {
395 WTF_LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
396 currentVersion = "";
397 }
398
399 // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception.
400 // If the expected version is the empty string, then we always return with whatever version of the database we have.
401 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) {
402 reportOpenDatabaseResult(6, InvalidStateError, 0);
403 errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'";
404 m_sqliteDatabase.close();
405 return false;
406 }
407
408 ASSERT(m_databaseAuthorizer);
409 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer);
410
411 // See comment at the top this file regarding calling addOpenDatabase().
412 DatabaseTracker::tracker().addOpenDatabase(this);
413 m_opened = true;
414
415 // Declare success:
416 error = DatabaseError::None; // Clear the presumed error from above.
417 onExitCaller.setOpenSucceeded();
418
419 if (m_new && !shouldSetVersionInNewDatabase)
420 m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version.
421
422 reportOpenDatabaseResult(0, -1, 0); // OK
423 return true;
424 }
425
securityOrigin() const426 SecurityOrigin* DatabaseBackendBase::securityOrigin() const
427 {
428 return m_contextThreadSecurityOrigin.get();
429 }
430
stringIdentifier() const431 String DatabaseBackendBase::stringIdentifier() const
432 {
433 // Return a deep copy for ref counting thread safety
434 return m_name.isolatedCopy();
435 }
436
displayName() const437 String DatabaseBackendBase::displayName() const
438 {
439 // Return a deep copy for ref counting thread safety
440 return m_displayName.isolatedCopy();
441 }
442
estimatedSize() const443 unsigned long DatabaseBackendBase::estimatedSize() const
444 {
445 return m_estimatedSize;
446 }
447
fileName() const448 String DatabaseBackendBase::fileName() const
449 {
450 // Return a deep copy for ref counting thread safety
451 return m_filename.isolatedCopy();
452 }
453
details() const454 DatabaseDetails DatabaseBackendBase::details() const
455 {
456 return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0);
457 }
458
getVersionFromDatabase(String & version,bool shouldCacheVersion)459 bool DatabaseBackendBase::getVersionFromDatabase(String& version, bool shouldCacheVersion)
460 {
461 String query(String("SELECT value FROM ") + infoTableName + " WHERE key = '" + versionKey + "';");
462
463 m_databaseAuthorizer->disable();
464
465 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version);
466 if (result) {
467 if (shouldCacheVersion)
468 setCachedVersion(version);
469 } else
470 WTF_LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
471
472 m_databaseAuthorizer->enable();
473
474 return result;
475 }
476
setVersionInDatabase(const String & version,bool shouldCacheVersion)477 bool DatabaseBackendBase::setVersionInDatabase(const String& version, bool shouldCacheVersion)
478 {
479 // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE
480 // clause in the CREATE statement (see Database::performOpenAndVerify()).
481 String query(String("INSERT INTO ") + infoTableName + " (key, value) VALUES ('" + versionKey + "', ?);");
482
483 m_databaseAuthorizer->disable();
484
485 bool result = setTextValueInDatabase(m_sqliteDatabase, query, version);
486 if (result) {
487 if (shouldCacheVersion)
488 setCachedVersion(version);
489 } else
490 WTF_LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data());
491
492 m_databaseAuthorizer->enable();
493
494 return result;
495 }
496
setExpectedVersion(const String & version)497 void DatabaseBackendBase::setExpectedVersion(const String& version)
498 {
499 m_expectedVersion = version.isolatedCopy();
500 }
501
getCachedVersion() const502 String DatabaseBackendBase::getCachedVersion() const
503 {
504 MutexLocker locker(guidMutex());
505 return guidToVersionMap().get(m_guid).isolatedCopy();
506 }
507
setCachedVersion(const String & actualVersion)508 void DatabaseBackendBase::setCachedVersion(const String& actualVersion)
509 {
510 // Update the in memory database version map.
511 MutexLocker locker(guidMutex());
512 updateGuidVersionMap(m_guid, actualVersion);
513 }
514
getActualVersionForTransaction(String & actualVersion)515 bool DatabaseBackendBase::getActualVersionForTransaction(String &actualVersion)
516 {
517 ASSERT(m_sqliteDatabase.transactionInProgress());
518 // Note: In multi-process browsers the cached value may be inaccurate.
519 // So we retrieve the value from the database and update the cached value here.
520 return getVersionFromDatabase(actualVersion, true);
521 }
522
disableAuthorizer()523 void DatabaseBackendBase::disableAuthorizer()
524 {
525 ASSERT(m_databaseAuthorizer);
526 m_databaseAuthorizer->disable();
527 }
528
enableAuthorizer()529 void DatabaseBackendBase::enableAuthorizer()
530 {
531 ASSERT(m_databaseAuthorizer);
532 m_databaseAuthorizer->enable();
533 }
534
setAuthorizerReadOnly()535 void DatabaseBackendBase::setAuthorizerReadOnly()
536 {
537 ASSERT(m_databaseAuthorizer);
538 m_databaseAuthorizer->setReadOnly();
539 }
540
setAuthorizerPermissions(int permissions)541 void DatabaseBackendBase::setAuthorizerPermissions(int permissions)
542 {
543 ASSERT(m_databaseAuthorizer);
544 m_databaseAuthorizer->setPermissions(permissions);
545 }
546
lastActionChangedDatabase()547 bool DatabaseBackendBase::lastActionChangedDatabase()
548 {
549 ASSERT(m_databaseAuthorizer);
550 return m_databaseAuthorizer->lastActionChangedDatabase();
551 }
552
lastActionWasInsert()553 bool DatabaseBackendBase::lastActionWasInsert()
554 {
555 ASSERT(m_databaseAuthorizer);
556 return m_databaseAuthorizer->lastActionWasInsert();
557 }
558
resetDeletes()559 void DatabaseBackendBase::resetDeletes()
560 {
561 ASSERT(m_databaseAuthorizer);
562 m_databaseAuthorizer->resetDeletes();
563 }
564
hadDeletes()565 bool DatabaseBackendBase::hadDeletes()
566 {
567 ASSERT(m_databaseAuthorizer);
568 return m_databaseAuthorizer->hadDeletes();
569 }
570
resetAuthorizer()571 void DatabaseBackendBase::resetAuthorizer()
572 {
573 if (m_databaseAuthorizer)
574 m_databaseAuthorizer->reset();
575 }
576
maximumSize() const577 unsigned long long DatabaseBackendBase::maximumSize() const
578 {
579 return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
580 }
581
incrementalVacuumIfNeeded()582 void DatabaseBackendBase::incrementalVacuumIfNeeded()
583 {
584 int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize();
585 int64_t totalSize = m_sqliteDatabase.totalSize();
586 if (totalSize <= 10 * freeSpaceSize) {
587 int result = m_sqliteDatabase.runIncrementalVacuumCommand();
588 reportVacuumDatabaseResult(result);
589 if (result != SQLResultOk)
590 m_frontend->logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg()));
591 }
592 }
593
interrupt()594 void DatabaseBackendBase::interrupt()
595 {
596 m_sqliteDatabase.interrupt();
597 }
598
isInterrupted()599 bool DatabaseBackendBase::isInterrupted()
600 {
601 MutexLocker locker(m_sqliteDatabase.databaseMutex());
602 return m_sqliteDatabase.isInterrupted();
603 }
604
605 // These are used to generate histograms of errors seen with websql.
606 // See about:histograms in chromium.
reportOpenDatabaseResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)607 void DatabaseBackendBase::reportOpenDatabaseResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
608 {
609 if (blink::Platform::current()->databaseObserver()) {
610 blink::Platform::current()->databaseObserver()->reportOpenDatabaseResult(
611 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
612 stringIdentifier(), isSyncDatabase(),
613 errorSite, webSqlErrorCode, sqliteErrorCode);
614 }
615 }
616
reportChangeVersionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)617 void DatabaseBackendBase::reportChangeVersionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
618 {
619 if (blink::Platform::current()->databaseObserver()) {
620 blink::Platform::current()->databaseObserver()->reportChangeVersionResult(
621 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
622 stringIdentifier(), isSyncDatabase(),
623 errorSite, webSqlErrorCode, sqliteErrorCode);
624 }
625 }
626
reportStartTransactionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)627 void DatabaseBackendBase::reportStartTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
628 {
629 if (blink::Platform::current()->databaseObserver()) {
630 blink::Platform::current()->databaseObserver()->reportStartTransactionResult(
631 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
632 stringIdentifier(), isSyncDatabase(),
633 errorSite, webSqlErrorCode, sqliteErrorCode);
634 }
635 }
636
reportCommitTransactionResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)637 void DatabaseBackendBase::reportCommitTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
638 {
639 if (blink::Platform::current()->databaseObserver()) {
640 blink::Platform::current()->databaseObserver()->reportCommitTransactionResult(
641 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
642 stringIdentifier(), isSyncDatabase(),
643 errorSite, webSqlErrorCode, sqliteErrorCode);
644 }
645 }
646
reportExecuteStatementResult(int errorSite,int webSqlErrorCode,int sqliteErrorCode)647 void DatabaseBackendBase::reportExecuteStatementResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
648 {
649 if (blink::Platform::current()->databaseObserver()) {
650 blink::Platform::current()->databaseObserver()->reportExecuteStatementResult(
651 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
652 stringIdentifier(), isSyncDatabase(),
653 errorSite, webSqlErrorCode, sqliteErrorCode);
654 }
655 }
656
reportVacuumDatabaseResult(int sqliteErrorCode)657 void DatabaseBackendBase::reportVacuumDatabaseResult(int sqliteErrorCode)
658 {
659 if (blink::Platform::current()->databaseObserver()) {
660 blink::Platform::current()->databaseObserver()->reportVacuumDatabaseResult(
661 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()),
662 stringIdentifier(), isSyncDatabase(), sqliteErrorCode);
663 }
664 }
665
666
667 } // namespace WebCore
668