2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #import "WebDatabaseManagerPrivate.h"
31 #import "WebDatabaseManagerClient.h"
32 #import "WebPlatformStrategies.h"
33 #import "WebSecurityOriginInternal.h"
35 #import <WebCore/DatabaseManager.h>
36 #import <WebCore/SecurityOrigin.h>
39 #import "WebDatabaseManagerInternal.h"
40 #import <WebCore/DatabaseTracker.h>
41 #import <WebCore/WebCoreThread.h>
44 using namespace WebCore;
46 NSString *WebDatabaseDirectoryDefaultsKey = @"WebDatabaseDirectory";
48 NSString *WebDatabaseDisplayNameKey = @"WebDatabaseDisplayNameKey";
49 NSString *WebDatabaseExpectedSizeKey = @"WebDatabaseExpectedSizeKey";
50 NSString *WebDatabaseUsageKey = @"WebDatabaseUsageKey";
52 NSString *WebDatabaseDidModifyOriginNotification = @"WebDatabaseDidModifyOriginNotification";
53 NSString *WebDatabaseDidModifyDatabaseNotification = @"WebDatabaseDidModifyDatabaseNotification";
54 NSString *WebDatabaseIdentifierKey = @"WebDatabaseIdentifierKey";
57 CFStringRef WebDatabaseOriginsDidChangeNotification = CFSTR("WebDatabaseOriginsDidChangeNotification");
60 static NSString *databasesDirectoryPath();
62 @implementation WebDatabaseManager
64 + (WebDatabaseManager *) sharedWebDatabaseManager
66 static WebDatabaseManager *sharedManager = [[WebDatabaseManager alloc] init];
72 if (!(self = [super init]))
75 WebPlatformStrategies::initializeIfNecessary();
77 DatabaseManager& dbManager = DatabaseManager::singleton();
79 // Set the database root path in WebCore
80 dbManager.initialize(databasesDirectoryPath());
82 // Set the DatabaseManagerClient
83 dbManager.setClient(WebDatabaseManagerClient::sharedWebDatabaseManagerClient());
90 Vector<RefPtr<SecurityOrigin>> coreOrigins;
91 DatabaseManager::singleton().origins(coreOrigins);
92 NSMutableArray *webOrigins = [[NSMutableArray alloc] initWithCapacity:coreOrigins.size()];
94 for (unsigned i = 0; i < coreOrigins.size(); ++i) {
95 WebSecurityOrigin *webOrigin = [[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:coreOrigins[i].get()];
96 [webOrigins addObject:webOrigin];
100 return [webOrigins autorelease];
103 - (NSArray *)databasesWithOrigin:(WebSecurityOrigin *)origin
105 Vector<String> nameVector;
106 if (!DatabaseManager::singleton().databaseNamesForOrigin([origin _core], nameVector))
109 NSMutableArray *names = [[NSMutableArray alloc] initWithCapacity:nameVector.size()];
111 for (unsigned i = 0; i < nameVector.size(); ++i)
112 [names addObject:(NSString *)nameVector[i]];
114 return [names autorelease];
117 - (NSDictionary *)detailsForDatabase:(NSString *)databaseIdentifier withOrigin:(WebSecurityOrigin *)origin
119 static id keys[3] = {WebDatabaseDisplayNameKey, WebDatabaseExpectedSizeKey, WebDatabaseUsageKey};
121 DatabaseDetails details = DatabaseManager::singleton().detailsForNameAndOrigin(databaseIdentifier, [origin _core]);
122 if (details.name().isNull())
126 objects[0] = details.displayName().isEmpty() ? databaseIdentifier : (NSString *)details.displayName();
127 objects[1] = [NSNumber numberWithUnsignedLongLong:details.expectedUsage()];
128 objects[2] = [NSNumber numberWithUnsignedLongLong:details.currentUsage()];
130 return [[[NSDictionary alloc] initWithObjects:objects forKeys:keys count:3] autorelease];
133 - (void)deleteAllDatabases
135 DatabaseManager::singleton().deleteAllDatabases();
137 // FIXME: This needs to be removed once DatabaseTrackers in multiple processes
138 // are in sync: <rdar://problem/9567500> Remove Website Data pane is not kept in sync with Safari
139 [[NSFileManager defaultManager] removeItemAtPath:databasesDirectoryPath() error:NULL];
143 - (BOOL)deleteOrigin:(WebSecurityOrigin *)origin
145 return DatabaseManager::singleton().deleteOrigin([origin _core]);
148 - (BOOL)deleteDatabase:(NSString *)databaseIdentifier withOrigin:(WebSecurityOrigin *)origin
150 return DatabaseManager::singleton().deleteDatabase([origin _core], databaseIdentifier);
154 static bool isFileHidden(NSString *file)
156 ASSERT([file length]);
157 return [file characterAtIndex:0] == '.';
160 + (void)removeEmptyDatabaseFiles
162 NSString *databasesDirectory = databasesDirectoryPath();
163 NSFileManager *fileManager = [NSFileManager defaultManager];
164 NSArray *array = [fileManager contentsOfDirectoryAtPath:databasesDirectory error:0];
168 NSUInteger count = [array count];
169 for (NSUInteger i = 0; i < count; ++i) {
170 NSString *fileName = [array objectAtIndex:i];
171 // Skip hidden files.
172 if (![fileName length] || isFileHidden(fileName))
175 NSString *path = [databasesDirectory stringByAppendingPathComponent:fileName];
176 // Look for directories that contain database files belonging to the same origins.
178 if (![fileManager fileExistsAtPath:path isDirectory:&isDirectory] || !isDirectory)
181 // Make sure the directory is not a symbolic link that points to something else.
182 NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:0];
183 if ([attributes fileType] == NSFileTypeSymbolicLink)
186 NSArray *databaseFilesInOrigin = [fileManager contentsOfDirectoryAtPath:path error:0];
187 NSUInteger databaseFileCount = [databaseFilesInOrigin count];
188 NSUInteger deletedDatabaseFileCount = 0;
189 for (NSUInteger j = 0; j < databaseFileCount; ++j) {
190 NSString *dbFileName = [databaseFilesInOrigin objectAtIndex:j];
191 // Skip hidden files.
192 if (![dbFileName length] || isFileHidden(dbFileName))
195 NSString *dbFilePath = [path stringByAppendingPathComponent:dbFileName];
197 // There shouldn't be any directories in this folder - but check for it anyway.
198 if (![fileManager fileExistsAtPath:dbFilePath isDirectory:&isDirectory] || isDirectory)
201 if (DatabaseTracker::deleteDatabaseFileIfEmpty(dbFilePath))
202 ++deletedDatabaseFileCount;
205 // If we have removed every database file for this origin, delete the folder for this origin.
206 if (databaseFileCount == deletedDatabaseFileCount) {
207 // Use rmdir - we don't want the deletion to happen if the folder is not empty.
208 rmdir([path fileSystemRepresentation]);
213 + (void)scheduleEmptyDatabaseRemoval
215 DatabaseTracker::emptyDatabaseFilesRemovalTaskWillBeScheduled();
217 dispatch_async(dispatch_get_global_queue(0, 0), ^{
218 [WebDatabaseManager removeEmptyDatabaseFiles];
219 DatabaseTracker::emptyDatabaseFilesRemovalTaskDidFinish();
222 #endif // PLATFORM(IOS)
227 @implementation WebDatabaseManager (WebDatabaseManagerInternal)
229 static DeprecatedMutex& transactionBackgroundTaskIdentifierLock()
231 DEPRECATED_DEFINE_STATIC_LOCAL(DeprecatedMutex, mutex, ());
235 static WebBackgroundTaskIdentifier transactionBackgroundTaskIdentifier;
237 static void setTransactionBackgroundTaskIdentifier(WebBackgroundTaskIdentifier identifier)
239 transactionBackgroundTaskIdentifier = identifier;
242 static WebBackgroundTaskIdentifier getTransactionBackgroundTaskIdentifier()
244 static dispatch_once_t pred;
245 dispatch_once(&pred, ^{
246 setTransactionBackgroundTaskIdentifier(invalidWebBackgroundTaskIdentifier());
249 return transactionBackgroundTaskIdentifier;
252 + (void)willBeginFirstTransaction
254 [self startBackgroundTask];
257 + (void)didFinishLastTransaction
259 [self endBackgroundTask];
262 + (void)startBackgroundTask
264 DeprecatedMutexLocker lock(transactionBackgroundTaskIdentifierLock());
266 // If there's already an existing background task going on, there's no need to start a new one.
267 if (getTransactionBackgroundTaskIdentifier() != invalidWebBackgroundTaskIdentifier())
270 setTransactionBackgroundTaskIdentifier(startBackgroundTask(^ {
271 DatabaseTracker::tracker().closeAllDatabases();
272 [WebDatabaseManager endBackgroundTask];
276 + (void)endBackgroundTask
278 DeprecatedMutexLocker lock(transactionBackgroundTaskIdentifierLock());
280 // It is possible that we were unable to start the background task when the first transaction began.
281 // Don't try to end the task in that case.
282 // It is also possible we finally finish the last transaction right when the background task expires
283 // and this will end up being called twice for the same background task. transactionBackgroundTaskIdentifier
284 // will be invalid for the second caller.
285 if (getTransactionBackgroundTaskIdentifier() == invalidWebBackgroundTaskIdentifier())
288 endBackgroundTask(getTransactionBackgroundTaskIdentifier());
289 setTransactionBackgroundTaskIdentifier(invalidWebBackgroundTaskIdentifier());
294 #endif // PLATFORM(IOS)
296 static NSString *databasesDirectoryPath()
298 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
299 NSString *databasesDirectory = [defaults objectForKey:WebDatabaseDirectoryDefaultsKey];
300 if (!databasesDirectory || ![databasesDirectory isKindOfClass:[NSString class]])
301 databasesDirectory = @"~/Library/WebKit/Databases";
303 return [databasesDirectory stringByStandardizingPath];