1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "modules/webdatabase/DatabaseTracker.h" 33 34 #include "core/dom/ScriptExecutionContext.h" 35 #include "core/platform/sql/SQLiteFileSystem.h" 36 #include "modules/webdatabase/DatabaseBackendBase.h" 37 #include "modules/webdatabase/DatabaseBackendContext.h" 38 #include "modules/webdatabase/DatabaseObserver.h" 39 #include "modules/webdatabase/QuotaTracker.h" 40 #include "weborigin/DatabaseIdentifier.h" 41 #include "weborigin/SecurityOrigin.h" 42 #include "weborigin/SecurityOriginHash.h" 43 #include "wtf/Assertions.h" 44 #include "wtf/StdLibExtras.h" 45 #include "wtf/text/WTFString.h" 46 47 namespace WebCore { 48 49 DatabaseTracker& DatabaseTracker::tracker() 50 { 51 AtomicallyInitializedStatic(DatabaseTracker&, tracker = *new DatabaseTracker()); 52 return tracker; 53 } 54 55 DatabaseTracker::DatabaseTracker() 56 { 57 SQLiteFileSystem::registerSQLiteVFS(); 58 } 59 60 bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* databaseContext, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError& error) 61 { 62 ScriptExecutionContext* scriptExecutionContext = databaseContext->scriptExecutionContext(); 63 bool success = DatabaseObserver::canEstablishDatabase(scriptExecutionContext, name, displayName, estimatedSize); 64 if (!success) 65 error = DatabaseError::GenericSecurityError; 66 return success; 67 } 68 69 String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool) 70 { 71 return createDatabaseIdentifierFromSecurityOrigin(origin) + "/" + name + "#"; 72 } 73 74 void DatabaseTracker::addOpenDatabase(DatabaseBackendBase* database) 75 { 76 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 77 if (!m_openDatabaseMap) 78 m_openDatabaseMap = adoptPtr(new DatabaseOriginMap); 79 80 String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()); 81 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); 82 if (!nameMap) { 83 nameMap = new DatabaseNameMap(); 84 m_openDatabaseMap->set(originIdentifier, nameMap); 85 } 86 87 String name(database->stringIdentifier()); 88 DatabaseSet* databaseSet = nameMap->get(name); 89 if (!databaseSet) { 90 databaseSet = new DatabaseSet(); 91 nameMap->set(name, databaseSet); 92 } 93 94 databaseSet->add(database); 95 } 96 97 class NotifyDatabaseObserverOnCloseTask : public ScriptExecutionContext::Task { 98 public: 99 static PassOwnPtr<NotifyDatabaseObserverOnCloseTask> create(PassRefPtr<DatabaseBackendBase> database) 100 { 101 return adoptPtr(new NotifyDatabaseObserverOnCloseTask(database)); 102 } 103 104 virtual void performTask(ScriptExecutionContext* context) 105 { 106 DatabaseObserver::databaseClosed(m_database.get()); 107 } 108 109 virtual bool isCleanupTask() const 110 { 111 return true; 112 } 113 114 private: 115 NotifyDatabaseObserverOnCloseTask(PassRefPtr<DatabaseBackendBase> database) 116 : m_database(database) 117 { 118 } 119 120 RefPtr<DatabaseBackendBase> m_database; 121 }; 122 123 void DatabaseTracker::removeOpenDatabase(DatabaseBackendBase* database) 124 { 125 String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()); 126 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 127 ASSERT(m_openDatabaseMap); 128 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); 129 if (!nameMap) 130 return; 131 132 String name(database->stringIdentifier()); 133 DatabaseSet* databaseSet = nameMap->get(name); 134 if (!databaseSet) 135 return; 136 137 DatabaseSet::iterator found = databaseSet->find(database); 138 if (found == databaseSet->end()) 139 return; 140 141 databaseSet->remove(found); 142 if (databaseSet->isEmpty()) { 143 nameMap->remove(name); 144 delete databaseSet; 145 if (nameMap->isEmpty()) { 146 m_openDatabaseMap->remove(originIdentifier); 147 delete nameMap; 148 } 149 } 150 151 ScriptExecutionContext* scriptExecutionContext = database->databaseContext()->scriptExecutionContext(); 152 if (!scriptExecutionContext->isContextThread()) 153 scriptExecutionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database)); 154 else 155 DatabaseObserver::databaseClosed(database); 156 } 157 158 void DatabaseTracker::prepareToOpenDatabase(DatabaseBackendBase* database) 159 { 160 ASSERT(database->databaseContext()->scriptExecutionContext()->isContextThread()); 161 DatabaseObserver::databaseOpened(database); 162 } 163 164 void DatabaseTracker::failedToOpenDatabase(DatabaseBackendBase* database) 165 { 166 ScriptExecutionContext* scriptExecutionContext = database->databaseContext()->scriptExecutionContext(); 167 if (!scriptExecutionContext->isContextThread()) 168 scriptExecutionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database)); 169 else 170 DatabaseObserver::databaseClosed(database); 171 } 172 173 unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendBase* database) 174 { 175 unsigned long long spaceAvailable = 0; 176 unsigned long long databaseSize = 0; 177 QuotaTracker::instance().getDatabaseSizeAndSpaceAvailableToOrigin( 178 createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()), 179 database->stringIdentifier(), &databaseSize, &spaceAvailable); 180 return databaseSize + spaceAvailable; 181 } 182 183 void DatabaseTracker::interruptAllDatabasesForContext(const DatabaseBackendContext* context) 184 { 185 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 186 187 if (!m_openDatabaseMap) 188 return; 189 190 DatabaseNameMap* nameMap = m_openDatabaseMap->get(createDatabaseIdentifierFromSecurityOrigin(context->securityOrigin())); 191 if (!nameMap) 192 return; 193 194 DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end(); 195 for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) { 196 DatabaseSet* databaseSet = dbNameMapIt->value; 197 DatabaseSet::const_iterator end = databaseSet->end(); 198 for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) { 199 if ((*it)->databaseContext() == context) 200 (*it)->interrupt(); 201 } 202 } 203 } 204 205 class DatabaseTracker::CloseOneDatabaseImmediatelyTask : public ScriptExecutionContext::Task { 206 public: 207 static PassOwnPtr<CloseOneDatabaseImmediatelyTask> create(const String& originIdentifier, const String& name, DatabaseBackendBase* database) 208 { 209 return adoptPtr(new CloseOneDatabaseImmediatelyTask(originIdentifier, name, database)); 210 } 211 212 virtual void performTask(ScriptExecutionContext* context) 213 { 214 DatabaseTracker::tracker().closeOneDatabaseImmediately(m_originIdentifier, m_name, m_database); 215 } 216 217 private: 218 CloseOneDatabaseImmediatelyTask(const String& originIdentifier, const String& name, DatabaseBackendBase* database) 219 : m_originIdentifier(originIdentifier.isolatedCopy()) 220 , m_name(name.isolatedCopy()) 221 , m_database(database) 222 { 223 } 224 225 String m_originIdentifier; 226 String m_name; 227 DatabaseBackendBase* m_database; // Intentionally a raw pointer. 228 }; 229 230 void DatabaseTracker::closeDatabasesImmediately(const String& originIdentifier, const String& name) 231 { 232 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 233 if (!m_openDatabaseMap) 234 return; 235 236 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); 237 if (!nameMap) 238 return; 239 240 DatabaseSet* databaseSet = nameMap->get(name); 241 if (!databaseSet) 242 return; 243 244 // We have to call closeImmediately() on the context thread and we cannot safely add a reference to 245 // the database in our collection when not on the context thread (which is always the case given 246 // current usage). 247 for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) 248 (*it)->databaseContext()->scriptExecutionContext()->postTask(CloseOneDatabaseImmediatelyTask::create(originIdentifier, name, *it)); 249 } 250 251 void DatabaseTracker::closeOneDatabaseImmediately(const String& originIdentifier, const String& name, DatabaseBackendBase* database) 252 { 253 // First we have to confirm the 'database' is still in our collection. 254 { 255 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 256 if (!m_openDatabaseMap) 257 return; 258 259 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); 260 if (!nameMap) 261 return; 262 263 DatabaseSet* databaseSet = nameMap->get(name); 264 if (!databaseSet) 265 return; 266 267 DatabaseSet::iterator found = databaseSet->find(database); 268 if (found == databaseSet->end()) 269 return; 270 } 271 272 // And we have to call closeImmediately() without our collection lock being held. 273 database->closeImmediately(); 274 } 275 276 } 277