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/ExecutionContext.h"
35 #include "core/dom/ExecutionContextTask.h"
36 #include "modules/webdatabase/Database.h"
37 #include "modules/webdatabase/DatabaseClient.h"
38 #include "modules/webdatabase/DatabaseContext.h"
39 #include "modules/webdatabase/QuotaTracker.h"
40 #include "modules/webdatabase/sqlite/SQLiteFileSystem.h"
41 #include "platform/weborigin/DatabaseIdentifier.h"
42 #include "platform/weborigin/SecurityOrigin.h"
43 #include "platform/weborigin/SecurityOriginHash.h"
44 #include "public/platform/Platform.h"
45 #include "public/platform/WebDatabaseObserver.h"
46 #include "wtf/Assertions.h"
47 #include "wtf/StdLibExtras.h"
48
49 namespace blink {
50
databaseClosed(Database * database)51 static void databaseClosed(Database* database)
52 {
53 if (Platform::current()->databaseObserver()) {
54 Platform::current()->databaseObserver()->databaseClosed(
55 createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()),
56 database->stringIdentifier());
57 }
58 }
59
tracker()60 DatabaseTracker& DatabaseTracker::tracker()
61 {
62 AtomicallyInitializedStatic(DatabaseTracker&, tracker = *new DatabaseTracker());
63 return tracker;
64 }
65
DatabaseTracker()66 DatabaseTracker::DatabaseTracker()
67 {
68 SQLiteFileSystem::registerSQLiteVFS();
69 }
70
canEstablishDatabase(DatabaseContext * databaseContext,const String & name,const String & displayName,unsigned long estimatedSize,DatabaseError & error)71 bool DatabaseTracker::canEstablishDatabase(DatabaseContext* databaseContext, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError& error)
72 {
73 ExecutionContext* executionContext = databaseContext->executionContext();
74 bool success = DatabaseClient::from(executionContext)->allowDatabase(executionContext, name, displayName, estimatedSize);
75 if (!success)
76 error = DatabaseError::GenericSecurityError;
77 return success;
78 }
79
fullPathForDatabase(SecurityOrigin * origin,const String & name,bool)80 String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool)
81 {
82 return createDatabaseIdentifierFromSecurityOrigin(origin) + "/" + name + "#";
83 }
84
addOpenDatabase(Database * database)85 void DatabaseTracker::addOpenDatabase(Database* database)
86 {
87 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
88 if (!m_openDatabaseMap)
89 m_openDatabaseMap = adoptPtr(new DatabaseOriginMap);
90
91 String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin());
92 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
93 if (!nameMap) {
94 nameMap = new DatabaseNameMap();
95 m_openDatabaseMap->set(originIdentifier, nameMap);
96 }
97
98 String name(database->stringIdentifier());
99 DatabaseSet* databaseSet = nameMap->get(name);
100 if (!databaseSet) {
101 databaseSet = new DatabaseSet();
102 nameMap->set(name, databaseSet);
103 }
104
105 databaseSet->add(database);
106 }
107
108 class NotifyDatabaseObserverOnCloseTask FINAL : public ExecutionContextTask {
109 public:
create(PassRefPtrWillBeRawPtr<Database> database)110 static PassOwnPtr<NotifyDatabaseObserverOnCloseTask> create(PassRefPtrWillBeRawPtr<Database> database)
111 {
112 return adoptPtr(new NotifyDatabaseObserverOnCloseTask(database));
113 }
114
performTask(ExecutionContext *)115 virtual void performTask(ExecutionContext*) OVERRIDE
116 {
117 databaseClosed(m_database.get());
118 }
119
isCleanupTask() const120 virtual bool isCleanupTask() const OVERRIDE
121 {
122 return true;
123 }
124
125 private:
NotifyDatabaseObserverOnCloseTask(PassRefPtrWillBeRawPtr<Database> database)126 explicit NotifyDatabaseObserverOnCloseTask(PassRefPtrWillBeRawPtr<Database> database)
127 : m_database(database)
128 {
129 }
130
131 RefPtrWillBeCrossThreadPersistent<Database> m_database;
132 };
133
removeOpenDatabase(Database * database)134 void DatabaseTracker::removeOpenDatabase(Database* database)
135 {
136 String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin());
137 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
138 ASSERT(m_openDatabaseMap);
139 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
140 if (!nameMap)
141 return;
142
143 String name(database->stringIdentifier());
144 DatabaseSet* databaseSet = nameMap->get(name);
145 if (!databaseSet)
146 return;
147
148 DatabaseSet::iterator found = databaseSet->find(database);
149 if (found == databaseSet->end())
150 return;
151
152 databaseSet->remove(found);
153 if (databaseSet->isEmpty()) {
154 nameMap->remove(name);
155 delete databaseSet;
156 if (nameMap->isEmpty()) {
157 m_openDatabaseMap->remove(originIdentifier);
158 delete nameMap;
159 }
160 }
161
162 ExecutionContext* executionContext = database->databaseContext()->executionContext();
163 if (!executionContext->isContextThread())
164 executionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database));
165 else
166 databaseClosed(database);
167 }
168
prepareToOpenDatabase(Database * database)169 void DatabaseTracker::prepareToOpenDatabase(Database* database)
170 {
171 ASSERT(database->databaseContext()->executionContext()->isContextThread());
172 if (Platform::current()->databaseObserver()) {
173 Platform::current()->databaseObserver()->databaseOpened(
174 createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()),
175 database->stringIdentifier(),
176 database->displayName(),
177 database->estimatedSize());
178 }
179 }
180
failedToOpenDatabase(Database * database)181 void DatabaseTracker::failedToOpenDatabase(Database* database)
182 {
183 ExecutionContext* executionContext = database->databaseContext()->executionContext();
184 if (!executionContext->isContextThread())
185 executionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database));
186 else
187 databaseClosed(database);
188 }
189
getMaxSizeForDatabase(const Database * database)190 unsigned long long DatabaseTracker::getMaxSizeForDatabase(const Database* database)
191 {
192 unsigned long long spaceAvailable = 0;
193 unsigned long long databaseSize = 0;
194 QuotaTracker::instance().getDatabaseSizeAndSpaceAvailableToOrigin(
195 createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()),
196 database->stringIdentifier(), &databaseSize, &spaceAvailable);
197 return databaseSize + spaceAvailable;
198 }
199
200 class DatabaseTracker::CloseOneDatabaseImmediatelyTask FINAL : public ExecutionContextTask {
201 public:
create(const String & originIdentifier,const String & name,Database * database)202 static PassOwnPtr<CloseOneDatabaseImmediatelyTask> create(const String& originIdentifier, const String& name, Database* database)
203 {
204 return adoptPtr(new CloseOneDatabaseImmediatelyTask(originIdentifier, name, database));
205 }
206
performTask(ExecutionContext *)207 virtual void performTask(ExecutionContext*) OVERRIDE
208 {
209 DatabaseTracker::tracker().closeOneDatabaseImmediately(m_originIdentifier, m_name, m_database);
210 }
211
212 private:
CloseOneDatabaseImmediatelyTask(const String & originIdentifier,const String & name,Database * database)213 CloseOneDatabaseImmediatelyTask(const String& originIdentifier, const String& name, Database* database)
214 : m_originIdentifier(originIdentifier.isolatedCopy())
215 , m_name(name.isolatedCopy())
216 , m_database(database)
217 {
218 }
219
220 String m_originIdentifier;
221 String m_name;
222 Database* m_database; // Intentionally a raw pointer.
223 };
224
closeDatabasesImmediately(const String & originIdentifier,const String & name)225 void DatabaseTracker::closeDatabasesImmediately(const String& originIdentifier, const String& name)
226 {
227 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
228 if (!m_openDatabaseMap)
229 return;
230
231 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
232 if (!nameMap)
233 return;
234
235 DatabaseSet* databaseSet = nameMap->get(name);
236 if (!databaseSet)
237 return;
238
239 // We have to call closeImmediately() on the context thread and we cannot safely add a reference to
240 // the database in our collection when not on the context thread (which is always the case given
241 // current usage).
242 for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it)
243 (*it)->databaseContext()->executionContext()->postTask(CloseOneDatabaseImmediatelyTask::create(originIdentifier, name, *it));
244 }
245
closeOneDatabaseImmediately(const String & originIdentifier,const String & name,Database * database)246 void DatabaseTracker::closeOneDatabaseImmediately(const String& originIdentifier, const String& name, Database* database)
247 {
248 // First we have to confirm the 'database' is still in our collection.
249 {
250 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
251 if (!m_openDatabaseMap)
252 return;
253
254 DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
255 if (!nameMap)
256 return;
257
258 DatabaseSet* databaseSet = nameMap->get(name);
259 if (!databaseSet)
260 return;
261
262 DatabaseSet::iterator found = databaseSet->find(database);
263 if (found == databaseSet->end())
264 return;
265 }
266
267 // And we have to call closeImmediately() without our collection lock being held.
268 database->closeImmediately();
269 }
270
271 } // namespace blink
272