Return-Path: X-Original-To: apmail-incubator-callback-commits-archive@minotaur.apache.org Delivered-To: apmail-incubator-callback-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 3B3089D4D for ; Tue, 20 Mar 2012 22:53:45 +0000 (UTC) Received: (qmail 44501 invoked by uid 500); 20 Mar 2012 22:53:45 -0000 Delivered-To: apmail-incubator-callback-commits-archive@incubator.apache.org Received: (qmail 44454 invoked by uid 500); 20 Mar 2012 22:53:45 -0000 Mailing-List: contact callback-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: callback-dev@incubator.apache.org Delivered-To: mailing list callback-commits@incubator.apache.org Received: (qmail 44447 invoked by uid 99); 20 Mar 2012 22:53:45 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 20 Mar 2012 22:53:45 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id AE4808301; Tue, 20 Mar 2012 22:53:44 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: shazron@apache.org To: callback-commits@incubator.apache.org X-Mailer: ASF-Git Admin Mailer Subject: [2/2] ios commit: Fixed CB-330 - localStorage / SQLDatabase no longer persistent after iOS 5.01 update Fixed CB-347 - iOS 5.1 localStorage / SQLDatabase error after upgrading an app Message-Id: <20120320225344.AE4808301@tyr.zones.apache.org> Date: Tue, 20 Mar 2012 22:53:44 +0000 (UTC) Fixed CB-330 - localStorage / SQLDatabase no longer persistent after iOS 5.01 update Fixed CB-347 - iOS 5.1 localStorage / SQLDatabase error after upgrading an app Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/commit/20527867 Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/20527867 Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/20527867 Branch: refs/heads/master Commit: 20527867f4958c331e34aeb4dd83695756e13585 Parents: 1a9027e Author: Shazron Abdullah Authored: Tue Mar 20 15:20:01 2012 -0700 Committer: Shazron Abdullah Committed: Tue Mar 20 15:20:01 2012 -0700 ---------------------------------------------------------------------- CordovaLib/Classes/CDV.h | 1 + CordovaLib/Classes/CDVAvailability.h | 10 + CordovaLib/Classes/CDVCommandDelegate.h | 3 + CordovaLib/Classes/CDVLocalStorage.h | 41 +++ CordovaLib/Classes/CDVLocalStorage.m | 299 ++++++++++++++++++ CordovaLib/Classes/CDVViewController.m | 43 ++-- CordovaLib/CordovaLib.xcodeproj/project.pbxproj | 12 + 7 files changed, 389 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDV.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDV.h b/CordovaLib/Classes/CDV.h index 414ac59..b4e4ecd 100644 --- a/CordovaLib/Classes/CDV.h +++ b/CordovaLib/Classes/CDV.h @@ -45,6 +45,7 @@ #import "CDVSound.h" #import "CDVSplashScreen.h" #import "CDVWhitelist.h" +#import "CDVLocalStorage.h" #import "NSData+Base64.h" #import "NSDictionary+Extensions.h" http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVAvailability.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVAvailability.h b/CordovaLib/Classes/CDVAvailability.h index 363d175..ea643ff 100644 --- a/CordovaLib/Classes/CDVAvailability.h +++ b/CordovaLib/Classes/CDVAvailability.h @@ -38,3 +38,13 @@ #ifndef CORDOVA_VERSION_MIN_REQUIRED #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_1_5_0 #endif + +/* + Returns YES if it is at least version specified as NSString(X) + Usage: + if (IsiOSVersion(@"5.1")) { + // do something for iOS 5.1 or greater + } + */ +#define IsiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending) + http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVCommandDelegate.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVCommandDelegate.h b/CordovaLib/Classes/CDVCommandDelegate.h index 7b379b5..74aef38 100644 --- a/CordovaLib/Classes/CDVCommandDelegate.h +++ b/CordovaLib/Classes/CDVCommandDelegate.h @@ -19,10 +19,13 @@ #import "CDVInvokedUrlCommand.h" +@class CDVPlugin; + @protocol CDVCommandDelegate - (NSString*) pathForResource:(NSString*)resourcepath; - (id) getCommandInstance:(NSString*)pluginName; +- (void) registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className; - (BOOL) execute:(CDVInvokedUrlCommand*)command; @end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVLocalStorage.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVLocalStorage.h b/CordovaLib/Classes/CDVLocalStorage.h new file mode 100644 index 0000000..0abd43d --- /dev/null +++ b/CordovaLib/Classes/CDVLocalStorage.h @@ -0,0 +1,41 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVPlugin.h" + +#define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain" +#define kCDVLocalStorageFileOperationError 1 + +@interface CDVLocalStorage : CDVPlugin < UIWebViewDelegate > + +@property (nonatomic, readonly, retain) NSMutableArray* backupInfo; + +- (void) backup:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) restore:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) verifyAndFixDatabaseLocations:(NSArray*)arguments withDict:(NSMutableDictionary*)options; + +@end + +@interface CDVBackupInfo : NSObject + +@property (nonatomic, copy) NSString* original; +@property (nonatomic, copy) NSString* backup; +@property (nonatomic, copy) NSString* label; + +@end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVLocalStorage.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVLocalStorage.m b/CordovaLib/Classes/CDVLocalStorage.m new file mode 100644 index 0000000..fbd5d1c --- /dev/null +++ b/CordovaLib/Classes/CDVLocalStorage.m @@ -0,0 +1,299 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVLocalStorage.h" +#import "CDV.h" + +@interface CDVLocalStorage () + +@property (nonatomic, readwrite, retain) NSMutableArray* backupInfo; // array of CDVBackupInfo objects +@property (nonatomic, readwrite, assign) id webviewDelegate; + +@end + + +@implementation CDVLocalStorage + +@synthesize backupInfo, webviewDelegate; + + +- (CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (CDVLocalStorage*)[super initWithWebView:theWebView]; + if (self) + { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive) + name:UIApplicationWillResignActiveNotification object:nil]; + + NSString *original, *backup; + self.backupInfo = [NSMutableArray arrayWithCapacity:3]; + + // set up common folders + NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)objectAtIndex:0]; + NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + NSString* backupsFolder = [appDocumentsFolder stringByAppendingPathComponent:@"Backups"]; + + // create the backups folder + [[NSFileManager defaultManager] createDirectoryAtPath:backupsFolder withIntermediateDirectories:YES attributes:nil error:nil]; + + //////////// LOCALSTORAGE + + original = [[appLibraryFolder stringByAppendingPathComponent: + (IsiOSVersion(@"5.1")) ? @"Caches" : @"WebKit/LocalStorage"] + stringByAppendingPathComponent:@"file__0.localstorage"]; + + backup = [backupsFolder stringByAppendingPathComponent:@"localstorage.appdata.db"]; + + CDVBackupInfo* backupItem = [[[CDVBackupInfo alloc] init] autorelease]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"localStorage database"; + + [self.backupInfo addObject:backupItem]; + + //////////// WEBSQL MAIN DB + + original = [[appLibraryFolder stringByAppendingPathComponent: + (IsiOSVersion(@"5.1")) ? @"Caches" : @"WebKit/Databases"] + stringByAppendingPathComponent:@"Databases.db"]; + + backup = [backupsFolder stringByAppendingPathComponent:@"websqlmain.appdata.db"]; + + backupItem = [[[CDVBackupInfo alloc] init] autorelease]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"websql main database"; + + [self.backupInfo addObject:backupItem]; + + //////////// WEBSQL DATABASES + + original = [[appLibraryFolder stringByAppendingPathComponent: + (IsiOSVersion(@"5.1")) ? @"Caches" : @"WebKit/Databases"] + stringByAppendingPathComponent:@"file__0"]; + + backup = [backupsFolder stringByAppendingPathComponent:@"websqldbs.appdata.db"]; + + backupItem = [[[CDVBackupInfo alloc] init] autorelease]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"websql databases"; + + [self.backupInfo addObject:backupItem]; + + //////////// + + // over-ride current webview delegate (for restore reasons) + self.webviewDelegate = theWebView.delegate; + theWebView.delegate = self; + + // verify the and fix the iOS 5.1 database locations once + [self verifyAndFixDatabaseLocations:nil withDict:nil]; + } + + return self; +} + +#pragma mark - +#pragma mark Plugin interface methods + +- (BOOL) copyFrom:(NSString*)src to:(NSString*)dest error:(NSError**)error +{ + NSFileManager* fileManager = [NSFileManager defaultManager]; + + if (![fileManager fileExistsAtPath:src]) { + + NSString* errorString = [NSString stringWithFormat:@"%@ file does not exist.", src]; + if (error != NULL) { + (*error) = [NSError errorWithDomain:kCDVLocalStorageErrorDomain + code:kCDVLocalStorageFileOperationError + userInfo:[NSDictionary dictionaryWithObject:errorString + forKey:NSLocalizedDescriptionKey]]; + } + return NO; + } + + // generate unique filepath in temp directory + CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef); + NSString* tempBackup = [[NSTemporaryDirectory() stringByAppendingPathComponent:(NSString*)uuidString] stringByAppendingPathExtension:@"bak"]; + CFRelease(uuidString); + CFRelease(uuidRef); + + BOOL destExists = [fileManager fileExistsAtPath:dest]; + + // backup the dest + if (destExists && ![fileManager copyItemAtPath:dest toPath:tempBackup error:error]) { + return NO; + } + + // remove the dest + if (destExists && ![fileManager removeItemAtPath:dest error:error]) { + return NO; + } + + // copy src to dest + if ([fileManager copyItemAtPath:src toPath:dest error:error]) { + // success - cleanup - delete the backup to the dest + if ([fileManager fileExistsAtPath:tempBackup]) { + [fileManager removeItemAtPath:tempBackup error:error]; + } + return YES; + + } else { + // failure - we restore the temp backup file to dest + [fileManager copyItemAtPath:tempBackup toPath:dest error:error]; + // cleanup - delete the backup to the dest + if ([fileManager fileExistsAtPath:tempBackup]) { + [fileManager removeItemAtPath:tempBackup error:error]; + } + return NO; + } +} + +/* copy from webkitDbLocation to persistentDbLocation */ +- (void) backup:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +{ + NSError* error = nil; + + for (CDVBackupInfo* info in self.backupInfo) + { + [self copyFrom:info.original to:info.backup error:&error]; + + if (error == nil) { + NSLog(@"Backed up: %@", info.label); + } else { + NSLog(@"Error in CDVLocalStorage (%@) backup: %@", info.label, [error localizedDescription]); + } + } +} + +/* copy from persistentDbLocation to webkitDbLocation */ +- (void) restore:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +{ + NSError* error = nil; + + for (CDVBackupInfo* info in self.backupInfo) + { + [self copyFrom:info.backup to:info.original error:&error]; + + if (error == nil) { + NSLog(@"CDVLocalStorage restored: %@", info.label); + } else { + NSLog(@"Error in CDVLocalStorage (%@) restore: %@", info.label, [error localizedDescription]); + } + } +} + +- (void) verifyAndFixDatabaseLocations:(NSArray*)arguments withDict:(NSMutableDictionary*)options +{ + NSUserDefaults* appPreferences = [NSUserDefaults standardUserDefaults]; + NSBundle* mainBundle = [NSBundle mainBundle]; + + NSString* bundlePath = [[mainBundle bundlePath] stringByDeletingLastPathComponent]; + NSString* bundleIdentifier = [[mainBundle infoDictionary] objectForKey:@"CFBundleIdentifier"]; + + NSString* appPlistPath = [[bundlePath stringByAppendingPathComponent:@"Library/Preferences"] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", bundleIdentifier]]; + NSMutableDictionary* appPlistDict = [NSMutableDictionary dictionaryWithContentsOfFile:appPlistPath]; + + NSArray* keysToCheck = [NSArray arrayWithObjects: + @"WebKitLocalStorageDatabasePathPreferenceKey", @"WebDatabaseDirectory", nil]; + + + BOOL dirty = NO; + NSString* pathSuffix = (IsiOSVersion(@"5.1")) ? @"Library/Caches" : @"Library/WebKit"; + + for (NSString* key in keysToCheck) + { + NSString* value = [appPlistDict objectForKey:key]; + // verify path is in app bundle, if not - fix + if (![value hasPrefix:bundlePath]) { + [appPlistDict setValue:[bundlePath stringByAppendingPathComponent:pathSuffix] forKey:key]; + dirty = YES; + } + } + + if (dirty) + { + BOOL ok = [appPlistDict writeToFile:appPlistPath atomically:YES]; + NSLog(@"Fix applied for database locations?: %@", ok? @"YES":@"NO"); + + [appPreferences synchronize]; + } +} + +#pragma mark - +#pragma mark Notification handlers + +- (void) onResignActive +{ + [self backup:nil withDict:nil]; +} + +- (void) onAppTerminate +{ + [self backup:nil withDict:nil]; +} + +#pragma mark - +#pragma mark UIWebviewDelegate implementation and forwarding + +- (void) webViewDidStartLoad:(UIWebView*)theWebView +{ + [self restore:nil withDict:nil]; + + return [self.webviewDelegate webViewDidStartLoad:theWebView]; +} + +- (void) webViewDidFinishLoad:(UIWebView*)theWebView +{ + return [self.webviewDelegate webViewDidFinishLoad:theWebView]; +} + +- (void) webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +{ + return [self.webviewDelegate webView:theWebView didFailLoadWithError:error]; +} + +- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + return [self.webviewDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; +} + +#pragma mark - +#pragma mark Over-rides + +- (void) dealloc +{ + self.backupInfo = nil; + + [super dealloc]; +} + +@end + + +#pragma mark - +#pragma mark CDVBackupInfo implementation + +@implementation CDVBackupInfo + +@synthesize original, backup, label; + +@end \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVViewController.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVViewController.m b/CordovaLib/Classes/CDVViewController.m index 6058e5b..f94814b 100644 --- a/CordovaLib/Classes/CDVViewController.m +++ b/CordovaLib/Classes/CDVViewController.m @@ -17,11 +17,7 @@ under the License. */ -#import "CDVViewController.h" -#import "CDVPlugin.h" -#import "CDVLocation.h" -#import "CDVConnection.h" -#import "NSDictionary+Extensions.h" +#import "CDV.h" #define SYMBOL_TO_NSSTRING_HELPER(x) @#x #define SYMBOL_TO_NSSTRING(x) SYMBOL_TO_NSSTRING_HELPER(x) @@ -70,6 +66,7 @@ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; + self.commandDelegate = self; self.wwwFolderName = @"www"; self.startPage = @"index.html"; [self setWantsFullScreenLayout:YES]; @@ -170,6 +167,11 @@ } /* + * Fire up CDVLocalStorage to work-around iOS 5.1 WebKit storage bug limitations + */ + [self.commandDelegate registerPlugin:[[[CDVLocalStorage alloc] initWithWebView:self.webView] autorelease] withClassName:NSStringFromClass([CDVLocalStorage class])]; + + /* * This is for iOS 4.x, where you can allow inline