Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/web/public/crw_browsing_data_store.h" | 5 #import "ios/web/public/crw_browsing_data_store.h" |
| 6 | 6 |
| 7 #import <Foundation/Foundation.h> | 7 #import <Foundation/Foundation.h> |
| 8 | 8 |
| 9 #include "base/ios/ios_util.h" | 9 #include "base/ios/ios_util.h" |
| 10 #import "base/ios/weak_nsobject.h" | 10 #import "base/ios/weak_nsobject.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #import "base/mac/scoped_nsobject.h" | 12 #import "base/mac/scoped_nsobject.h" |
| 13 #import "ios/web/browsing_data_managers/crw_cookie_browsing_data_manager.h" | 13 #import "ios/web/browsing_data_managers/crw_cookie_browsing_data_manager.h" |
| 14 #include "ios/web/public/active_state_manager.h" | 14 #include "ios/web/public/active_state_manager.h" |
| 15 #include "ios/web/public/browser_state.h" | 15 #include "ios/web/public/browser_state.h" |
| 16 | 16 |
| 17 namespace { | 17 namespace { |
| 18 // Represents the type of operations that a CRWBrowsingDataStore can perform. | 18 // Represents the type of operations that a CRWBrowsingDataStore can perform. |
| 19 enum OperationType { | 19 enum OperationType { |
| 20 // Represents NOP. | 20 // Represents NOP. |
| 21 NONE = 1, | 21 NONE = 1, |
| 22 // Stash operation involves stashing browsing data that web views create to | 22 // Stash operation involves stashing browsing data that web views create to |
| 23 // the associated BrowserState's state path. | 23 // the associated BrowserState's state path. |
| 24 STASH, | 24 STASH, |
| 25 // Restore operation involves restoring browsing data from the | 25 // Restore operation involves restoring browsing data from the |
| 26 // associated BrowserState's state path so that web views (UIWebViews and | 26 // associated BrowserState's state path so that web views can read from them. |
| 27 // WKWebViews) can read from them. | |
| 28 RESTORE, | 27 RESTORE, |
| 29 // Remove operation involves removing the data that web views create. | 28 // Remove operation involves removing the data that web views create. |
| 30 REMOVE, | 29 REMOVE, |
| 31 }; | 30 }; |
| 32 | 31 |
| 33 // The name of the NSOperation that performs a restore operation. | 32 // The name of the NSOperation that performs a |RESTORE| operation. |
|
stuartmorgan
2015/07/09 16:33:50
FWIW, while the | are okay here, they are only req
shreyasv1
2015/07/09 17:18:01
My concern is with consistency. I noticed that som
| |
| 34 NSString* const kRestoreOperationName = @"CRWBrowsingDataStore.RESTORE"; | 33 NSString* const kRestoreOperationName = @"CRWBrowsingDataStore.RESTORE"; |
| 35 // The name of the NSOperation that performs a stash operation. | 34 // The name of the NSOperation that performs a |STASH| operation. |
| 36 NSString* const kStashOperationName = @"CRWBrowsingDataStore.STASH"; | 35 NSString* const kStashOperationName = @"CRWBrowsingDataStore.STASH"; |
| 37 } // namespace | 36 } // namespace |
| 38 | 37 |
| 38 // CRWBrowsingDataStore is implemented using 2 queues. | |
| 39 // 1) operationQueueForStashAndRestoreOperations: | |
| 40 // - This queue is used to perform |STASH| and |RESTORE| operations. | |
| 41 // - |STASH|, |RESTORE| operations are the core bits that need to be performed | |
| 42 // in order to change the mode of a CRWBrowsingDataStore and hence they are | |
| 43 // prioritized with a QoS of NSQualityOfServiceUserInteractive. | |
| 44 // - |STASH|, |RESTORE| operations from 2 different CRWBrowsingDataStores are | |
| 45 // not re-entrant. Hence this is a serial queue. | |
| 46 // 2) operationQueueForRemoveOperations: | |
| 47 // - This queue is used to perform |REMOVE| operations. | |
| 48 // - |REMOVE| operations are not as critical as |STASH|, |RESTORE|. Hence they | |
| 49 // the queue has a QoS of NSQualityOfServiceUserInitiated -- in order to not | |
| 50 // overwhelm the system. | |
| 51 // - |REMOVE| operations from 2 different CRWBrowingDataStores can be run in | |
| 52 // parallel. Hence this is a concurrent queue. | |
| 53 // | |
| 54 // |STASH|, |RESTORE|, |REMOVE| operations of a particular CRWBrowsingDataStore | |
| 55 // are not re-entrant. Hence these operations are serialized. | |
| 56 | |
| 39 @interface CRWBrowsingDataStore () | 57 @interface CRWBrowsingDataStore () |
| 58 // Redefined to be read-write. Must be called from the main thread. | |
| 59 @property(nonatomic, assign) web::BrowsingDataStoreMode mode; | |
| 60 // The array of all browsing data managers. Must be called from the main | |
| 61 // thread. | |
| 62 @property(nonatomic, readonly) NSArray* allBrowsingDataManagers; | |
| 63 // The number of stash or restore operations that are still pending. Must be | |
| 64 // called from the main thread. | |
| 65 @property(nonatomic, assign) NSUInteger numberOfPendingStashOrRestoreOperations; | |
| 66 | |
| 40 // Returns a serial queue on which stash and restore operations can be scheduled | 67 // Returns a serial queue on which stash and restore operations can be scheduled |
| 41 // to be run. All stash/restore operations need to be run on the same queue | 68 // to be run. All |STASH|/|RESTORE| operations need to be run on the same queue |
| 42 // hence it is shared with all CRWBrowsingDataStores. | 69 // hence it is shared with all CRWBrowsingDataStores. |
| 43 + (NSOperationQueue*)operationQueueForStashAndRestoreOperations; | 70 + (NSOperationQueue*)operationQueueForStashAndRestoreOperations; |
| 44 // Returns a concurrent queue on which remove operations can be scheduled to be | 71 // Returns a concurrent queue on which remove operations can be scheduled to be |
| 45 // run. All remove operations need to be run on the same queue hence it is | 72 // run. All |REMOVE| operations need to be run on the same queue hence it is |
| 46 // shared with all CRWBrowsingDataStores. | 73 // shared with all CRWBrowsingDataStores. |
| 47 + (NSOperationQueue*)operationQueueForRemoveOperations; | 74 + (NSOperationQueue*)operationQueueForRemoveOperations; |
| 48 - (instancetype)init NS_UNAVAILABLE; | |
| 49 | 75 |
| 50 // The array of all browsing data managers. Must be accessed from the main | 76 // Returns an array of CRWBrowsingDataManagers for the given |
| 51 // thread. | 77 // |browsingDataTypes|. |
| 52 @property(nonatomic, readonly) NSArray* allBrowsingDataManagers; | |
| 53 // Returns an array of browsing data managers for the given |browsingDataTypes|. | |
| 54 - (NSArray*)browsingDataManagersForBrowsingDataTypes: | 78 - (NSArray*)browsingDataManagersForBrowsingDataTypes: |
| 55 (web::BrowsingDataTypes)browsingDataTypes; | 79 (web::BrowsingDataTypes)browsingDataTypes; |
| 56 // Returns the selector that needs to be performed on the | 80 // Returns the selector that needs to be performed on the |
| 57 // CRWBrowsingDataManagers for the |operationType|. |operationType| cannot be | 81 // CRWBrowsingDataManagers for the |operationType|. |operationType| cannot be |
| 58 // |NONE|. | 82 // |NONE|. |
| 59 - (SEL)browsingDataManagerSelectorForOperationType:(OperationType)operationType; | 83 - (SEL)browsingDataManagerSelectorForOperationType:(OperationType)operationType; |
| 60 // Returns the selector that needs to be performed on the | 84 // Returns the selector that needs to be performed on the |
| 61 // CRWBrowsingDataManagers for a REMOVE operation. | 85 // CRWBrowsingDataManagers for a |REMOVE| operation. |
| 62 - (SEL)browsingDataManagerSelectorForRemoveOperationType; | 86 - (SEL)browsingDataManagerSelectorForRemoveOperationType; |
| 63 | 87 |
| 64 // Redefined to be read-write. Must be called from the main thread. | 88 // Sets the mode iff there are no more |STASH| or |RESTORE| operations that are |
| 65 @property(nonatomic, assign) web::BrowsingDataStoreMode mode; | |
| 66 // Sets the mode iff there are no more stash or restore operations that are | |
| 67 // still pending. |mode| can only be either |ACTIVE| or |INACTIVE|. | 89 // still pending. |mode| can only be either |ACTIVE| or |INACTIVE|. |
| 68 // |handler| is called immediately (in the same runloop) with a BOOL indicating | 90 // |handler| is called immediately (in the same runloop) with a BOOL indicating |
| 69 // whether the mode change was successful or not. |handler| can be nil. | 91 // whether the mode change was successful or not. |handler| can be nil. |
| 70 - (void)finalizeChangeToMode:(web::BrowsingDataStoreMode)mode | 92 - (void)finalizeChangeToMode:(web::BrowsingDataStoreMode)mode |
| 71 andCallCompletionHandler:(void (^)(BOOL modeChangeWasSuccessful))handler; | 93 andCallCompletionHandler:(void (^)(BOOL modeChangeWasSuccessful))handler; |
| 72 | |
| 73 // Changes the mode of the CRWBrowsingDataStore to |mode|. This is an | 94 // Changes the mode of the CRWBrowsingDataStore to |mode|. This is an |
| 74 // asynchronous operation and the mode is not changed immediately. | 95 // asynchronous operation and the mode is not changed immediately. |
| 75 // |completionHandler| can be nil. | 96 // |completionHandler| can be nil. |
| 76 // |completionHandler| is called on the main thread. This block has no return | 97 // |completionHandler| is called on the main thread. This block has no return |
| 77 // value and takes a single BOOL argument that indicates whether or not the | 98 // value and takes a single BOOL argument that indicates whether or not the |
| 78 // mode change was successfully changed to |mode|. | 99 // mode change was successfully changed to |mode|. |
| 79 - (void)changeMode:(web::BrowsingDataStoreMode)mode | 100 - (void)changeMode:(web::BrowsingDataStoreMode)mode |
| 80 completionHandler:(void (^)(BOOL modeChangeWasSuccessful))completionHandler; | 101 completionHandler:(void (^)(BOOL modeChangeWasSuccessful))completionHandler; |
| 81 | 102 |
| 82 // The number of stash or restore operations that are still pending. | 103 // Returns the OperationType (that needs to be performed) in order to change the |
| 83 @property(nonatomic, assign) NSUInteger numberOfPendingStashOrRestoreOperations; | 104 // mode to |mode|. Consults the delegate if one is present. |mode| cannot be |
| 84 | 105 // |CHANGING|. |
| 85 // Performs operations of type |operationType| on each of the | 106 - (OperationType)operationTypeToChangeMode:(web::BrowsingDataStoreMode)mode; |
| 107 // Performs an operation of type |operationType| on each of the | |
| 86 // |browsingDataManagers|. |operationType| cannot be |NONE|. | 108 // |browsingDataManagers|. |operationType| cannot be |NONE|. |
| 87 // Precondition: There must be no web views associated with the BrowserState. | 109 // Precondition: There must be no web views associated with the BrowserState. |
| 88 // |completionHandler| is called on the main thread and cannot be nil. | 110 // |completionHandler| is called on the main thread and cannot be nil. |
| 89 - (void)performOperationWithType:(OperationType)operationType | 111 - (void)performOperationWithType:(OperationType)operationType |
| 90 browsingDataManagers:(NSArray*)browsingDataManagers | 112 browsingDataManagers:(NSArray*)browsingDataManagers |
| 91 completionHandler:(ProceduralBlock)completionHandler; | 113 completionHandler:(ProceduralBlock)completionHandler; |
| 92 | 114 // Returns an NSOperation that calls |selector| on all the |
| 93 // Creates an NSOperation that calls |selector| on all the | |
| 94 // |browsingDataManagers|. |selector| needs to be one of the methods in | 115 // |browsingDataManagers|. |selector| needs to be one of the methods in |
| 95 // CRWBrowsingDataManager. | 116 // CRWBrowsingDataManager. |
| 96 - (NSOperation*) | 117 - (NSOperation*)operationWithBrowsingDataManagers:(NSArray*)browsingDataManagers |
| 97 newOperationWithBrowsingDataManagers:(NSArray*)browsingDataManagers | 118 selector:(SEL)selector; |
| 98 selector:(SEL)selector; | |
| 99 // Enqueues |operation| to be run on |queue|. All operations are serialized to | 119 // Enqueues |operation| to be run on |queue|. All operations are serialized to |
| 100 // be run one after another. | 120 // be run one after another. |
| 101 - (void)addOperation:(NSOperation*)operation toQueue:(NSOperationQueue*)queue; | 121 - (void)addOperation:(NSOperation*)operation toQueue:(NSOperationQueue*)queue; |
| 102 @end | 122 @end |
| 103 | 123 |
| 104 @implementation CRWBrowsingDataStore { | 124 @implementation CRWBrowsingDataStore { |
| 105 web::BrowserState* _browserState; // Weak, owns this object. | 125 web::BrowserState* _browserState; // Weak, owns this object. |
| 106 // The delegate. | 126 // The delegate. |
| 107 base::WeakNSProtocol<id<CRWBrowsingDataStoreDelegate>> _delegate; | 127 base::WeakNSProtocol<id<CRWBrowsingDataStoreDelegate>> _delegate; |
| 108 // The mode of the CRWBrowsingDataStore. | 128 // The mode of the CRWBrowsingDataStore. |
| 109 web::BrowsingDataStoreMode _mode; | 129 web::BrowsingDataStoreMode _mode; |
| 110 // The dictionary that maps a browsing data type to its | 130 // The dictionary that maps a browsing data type to its |
| 111 // CRWBrowsingDataManager. | 131 // CRWBrowsingDataManager. |
| 112 base::scoped_nsobject<NSDictionary> _browsingDataTypeMap; | 132 base::scoped_nsobject<NSDictionary> _browsingDataTypeMap; |
| 113 // The last operation that was enqueued to be run. Can be stash, restore or a | 133 // The last operation that was enqueued to be run. Can be a |STASH|, |RESTORE| |
| 114 // delete operation. | 134 // or a |REMOVE| operation. |
| 115 base::scoped_nsobject<NSOperation> _lastDispatchedOperation; | 135 base::scoped_nsobject<NSOperation> _lastDispatchedOperation; |
| 116 // The last dispatched stash or restore operation that was enqueued to be run. | 136 // The last dispatched |STASH| or |RESTORE| operation that was enqueued to be |
| 137 // run. | |
| 117 base::scoped_nsobject<NSOperation> _lastDispatchedStashOrRestoreOperation; | 138 base::scoped_nsobject<NSOperation> _lastDispatchedStashOrRestoreOperation; |
| 118 // The number of stash or restore operations that are still pending. If this | 139 // The number of |STASH| or |RESTORE| operations that are still pending. If |
| 119 // value > 0 the mode of the CRWBrowsingDataStore is |CHANGING|. The mode | 140 // the number of stash or restore operations > 0U, the mode of the |
| 120 // can be made ACTIVE or INACTIVE only be set when this value is 0. | 141 // CRWBrowsingDataStore is |CHANGING|. The mode can be made ACTIVE or INACTIVE |
| 142 // only be set when this value is 0U. | |
| 121 NSUInteger _numberOfPendingStashOrRestoreOperations; | 143 NSUInteger _numberOfPendingStashOrRestoreOperations; |
| 122 } | 144 } |
| 123 | 145 |
| 124 + (NSOperationQueue*)operationQueueForStashAndRestoreOperations { | 146 #pragma mark - |
| 125 static dispatch_once_t onceToken = 0; | 147 #pragma mark NSObject Methods |
| 126 static NSOperationQueue* operationQueueForStashAndRestoreOperations = nil; | |
| 127 dispatch_once(&onceToken, ^{ | |
| 128 operationQueueForStashAndRestoreOperations = | |
| 129 [[NSOperationQueue alloc] init]; | |
| 130 [operationQueueForStashAndRestoreOperations | |
| 131 setMaxConcurrentOperationCount:1U]; | |
| 132 if (base::ios::IsRunningOnIOS8OrLater()) { | |
| 133 [operationQueueForStashAndRestoreOperations | |
| 134 setQualityOfService:NSQualityOfServiceUserInteractive]; | |
| 135 } | |
| 136 }); | |
| 137 return operationQueueForStashAndRestoreOperations; | |
| 138 } | |
| 139 | |
| 140 + (NSOperationQueue*)operationQueueForRemoveOperations { | |
| 141 static dispatch_once_t onceToken = 0; | |
| 142 static NSOperationQueue* operationQueueForRemoveOperations = nil; | |
| 143 dispatch_once(&onceToken, ^{ | |
| 144 operationQueueForRemoveOperations = [[NSOperationQueue alloc] init]; | |
| 145 [operationQueueForRemoveOperations | |
| 146 setMaxConcurrentOperationCount:NSUIntegerMax]; | |
| 147 if (base::ios::IsRunningOnIOS8OrLater()) { | |
| 148 [operationQueueForRemoveOperations | |
| 149 setQualityOfService:NSQualityOfServiceUserInitiated]; | |
| 150 } | |
| 151 }); | |
| 152 return operationQueueForRemoveOperations; | |
| 153 } | |
| 154 | 148 |
| 155 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState { | 149 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState { |
| 156 self = [super init]; | 150 self = [super init]; |
| 157 if (self) { | 151 if (self) { |
| 158 DCHECK([NSThread isMainThread]); | 152 DCHECK([NSThread isMainThread]); |
| 153 DCHECK(browserState); | |
| 159 // TODO(shreyasv): Instantiate the necessary CRWBrowsingDataManagers that | 154 // TODO(shreyasv): Instantiate the necessary CRWBrowsingDataManagers that |
| 160 // are encapsulated within this class. crbug.com/480654. | 155 // are encapsulated within this class. crbug.com/480654. |
| 161 _browserState = browserState; | 156 _browserState = browserState; |
| 162 web::ActiveStateManager* activeStateManager = | 157 web::ActiveStateManager* activeStateManager = |
| 163 web::BrowserState::GetActiveStateManager(browserState); | 158 web::BrowserState::GetActiveStateManager(browserState); |
| 164 DCHECK(activeStateManager); | 159 DCHECK(activeStateManager); |
| 165 _mode = activeStateManager->IsActive() ? web::ACTIVE : web::INACTIVE; | 160 _mode = activeStateManager->IsActive() ? web::ACTIVE : web::INACTIVE; |
| 166 // TODO(shreyasv): If the creation of CRWBrowsingDataManagers turns out to | 161 // TODO(shreyasv): If the creation of CRWBrowsingDataManagers turns out to |
| 167 // be an expensive operations re-visit this with a lazy-evaluation approach. | 162 // be an expensive operations re-visit this with a lazy-evaluation approach. |
| 168 base::scoped_nsobject<CRWCookieBrowsingDataManager> | 163 base::scoped_nsobject<CRWCookieBrowsingDataManager> |
| 169 cookieBrowsingDataManager([[CRWCookieBrowsingDataManager alloc] | 164 cookieBrowsingDataManager([[CRWCookieBrowsingDataManager alloc] |
| 170 initWithBrowserState:browserState]); | 165 initWithBrowserState:browserState]); |
| 171 _browsingDataTypeMap.reset([@{ | 166 _browsingDataTypeMap.reset([@{ |
| 172 @(web::BROWSING_DATA_TYPE_COOKIES) : cookieBrowsingDataManager, | 167 @(web::BROWSING_DATA_TYPE_COOKIES) : cookieBrowsingDataManager, |
| 173 } retain]); | 168 } retain]); |
| 174 } | 169 } |
| 175 return self; | 170 return self; |
| 176 } | 171 } |
| 177 | 172 |
| 178 - (instancetype)init { | 173 - (instancetype)init { |
| 179 NOTREACHED(); | 174 NOTREACHED(); |
| 180 return nil; | 175 return nil; |
| 181 } | 176 } |
| 182 | 177 |
| 183 - (NSString*)description { | 178 - (NSString*)description { |
| 184 NSString* format = @"<%@: %p; hasPendingOperations = { %@ }>"; | 179 NSString* format = @"<%@: %p; hasPendingOperations = { %@ }; mode = { %@ }>"; |
| 185 NSString* hasPendingOperationsString = | 180 NSString* hasPendingOperationsString = |
| 186 [self hasPendingOperations] ? @"YES" : @"NO"; | 181 [self hasPendingOperations] ? @"YES" : @"NO"; |
| 187 NSString* result = | 182 NSString* modeString = nil; |
| 188 [NSString stringWithFormat:format, NSStringFromClass(self.class), self, | 183 switch (self.mode) { |
| 189 hasPendingOperationsString]; | 184 case web::ACTIVE: |
| 185 modeString = @"ACTIVE"; | |
| 186 break; | |
| 187 case web::CHANGING: | |
| 188 modeString = @"CHANGING"; | |
| 189 break; | |
| 190 case web::INACTIVE: | |
| 191 modeString = @"INACTIVE"; | |
| 192 break; | |
| 193 } | |
| 194 NSString* result = [NSString stringWithFormat:format, | |
| 195 NSStringFromClass(self.class), self, hasPendingOperationsString, | |
| 196 modeString]; | |
|
stuartmorgan
2015/07/09 16:33:50
This shouldn't be indented any more than the line
| |
| 190 return result; | 197 return result; |
| 191 } | 198 } |
| 192 | 199 |
| 200 + (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key { | |
| 201 // It is necessary to override this for |mode| because the default KVO | |
| 202 // behavior in NSObject is to fire a notification irrespective of if an actual | |
| 203 // change was made to the ivar or not. The |mode| property needs fine grained | |
| 204 // control over the actual notifications being fired since observers need to | |
| 205 // be notified iff the |mode| actually changed. | |
| 206 if ([key isEqual:@"mode"]) { | |
| 207 return NO; | |
| 208 } | |
| 209 return [super automaticallyNotifiesObserversForKey:(NSString*)key]; | |
|
stuartmorgan
2015/07/09 16:33:50
Remove (NSString*)
shreyasv1
2015/07/09 17:18:01
Done.
| |
| 210 } | |
| 211 | |
| 212 #pragma mark - | |
| 213 #pragma mark Public Properties | |
| 214 | |
| 215 - (id<CRWBrowsingDataStoreDelegate>)delegate { | |
| 216 return _delegate; | |
| 217 } | |
| 218 | |
| 219 - (void)setDelegate:(id<CRWBrowsingDataStoreDelegate>)delegate { | |
| 220 _delegate.reset(delegate); | |
| 221 } | |
| 222 | |
| 223 - (web::BrowsingDataStoreMode)mode { | |
| 224 DCHECK([NSThread isMainThread]); | |
| 225 return _mode; | |
| 226 } | |
| 227 | |
| 228 - (BOOL)hasPendingOperations { | |
| 229 if (!_lastDispatchedOperation) { | |
| 230 return NO; | |
| 231 } | |
| 232 return ![_lastDispatchedOperation isFinished]; | |
| 233 } | |
| 234 | |
| 235 #pragma mark - | |
| 236 #pragma mark Public Methods | |
| 237 | |
| 238 - (void)makeActiveWithCompletionHandler: | |
| 239 (void (^)(BOOL success))completionHandler { | |
| 240 DCHECK([NSThread isMainThread]); | |
| 241 // TODO(shreyasv): Verify the preconditions for this method when | |
| 242 // web::WebViewCounter class is implemented. crbug.com/480507. | |
| 243 | |
| 244 [self changeMode:web::ACTIVE completionHandler:completionHandler]; | |
| 245 } | |
| 246 | |
| 247 - (void)makeInactiveWithCompletionHandler: | |
| 248 (void (^)(BOOL success))completionHandler { | |
| 249 DCHECK([NSThread isMainThread]); | |
| 250 // TODO(shreyasv): Verify the preconditions for this method when | |
| 251 // web::WebViewCounter class is implemented. crbug.com/480507. | |
| 252 | |
| 253 [self changeMode:web::INACTIVE completionHandler:completionHandler]; | |
| 254 } | |
| 255 | |
| 256 - (void)removeDataOfTypes:(web::BrowsingDataTypes)browsingDataTypes | |
| 257 completionHandler:(ProceduralBlock)completionHandler { | |
| 258 DCHECK([NSThread isMainThread]); | |
| 259 // TODO(shreyasv): Verify the preconditions for this method when | |
| 260 // web::WebViewCounter class is implemented. crbug.com/480507. | |
| 261 | |
| 262 NSArray* browsingDataManagers = | |
| 263 [self browsingDataManagersForBrowsingDataTypes:browsingDataTypes]; | |
| 264 [self performOperationWithType:REMOVE | |
| 265 browsingDataManagers:browsingDataManagers | |
| 266 completionHandler:completionHandler]; | |
| 267 } | |
| 268 | |
| 269 #pragma mark - | |
| 270 #pragma mark Private Properties | |
| 271 | |
| 272 - (void)setMode:(web::BrowsingDataStoreMode)mode { | |
| 273 DCHECK([NSThread isMainThread]); | |
| 274 | |
| 275 if (_mode == mode) { | |
| 276 return; | |
| 277 } | |
| 278 if (mode == web::ACTIVE || mode == web::INACTIVE) { | |
| 279 DCHECK(!self.numberOfPendingStashOrRestoreOperations); | |
| 280 } | |
| 281 [self willChangeValueForKey:@"mode"]; | |
| 282 _mode = mode; | |
| 283 [self didChangeValueForKey:@"mode"]; | |
| 284 } | |
| 193 | 285 |
| 194 - (NSArray*)allBrowsingDataManagers { | 286 - (NSArray*)allBrowsingDataManagers { |
| 195 DCHECK([NSThread isMainThread]); | 287 DCHECK([NSThread isMainThread]); |
| 196 return [_browsingDataTypeMap allValues]; | 288 return [_browsingDataTypeMap allValues]; |
| 197 } | 289 } |
| 198 | 290 |
| 291 - (NSUInteger)numberOfPendingStashOrRestoreOperations { | |
| 292 DCHECK([NSThread isMainThread]); | |
| 293 return _numberOfPendingStashOrRestoreOperations; | |
| 294 } | |
| 295 | |
| 296 - (void)setNumberOfPendingStashOrRestoreOperations: | |
| 297 (NSUInteger)numberOfPendingStashOrRestoreOperations { | |
| 298 DCHECK([NSThread isMainThread]); | |
| 299 _numberOfPendingStashOrRestoreOperations = | |
| 300 numberOfPendingStashOrRestoreOperations; | |
| 301 } | |
| 302 | |
| 303 #pragma mark - | |
| 304 #pragma mark Private Class Methods | |
| 305 | |
| 306 + (NSOperationQueue*)operationQueueForStashAndRestoreOperations { | |
| 307 static dispatch_once_t onceToken = 0; | |
| 308 static NSOperationQueue* operationQueueForStashAndRestoreOperations = nil; | |
| 309 dispatch_once(&onceToken, ^{ | |
| 310 operationQueueForStashAndRestoreOperations = | |
| 311 [[NSOperationQueue alloc] init]; | |
| 312 [operationQueueForStashAndRestoreOperations | |
| 313 setMaxConcurrentOperationCount:1U]; | |
| 314 if (base::ios::IsRunningOnIOS8OrLater()) { | |
| 315 [operationQueueForStashAndRestoreOperations | |
| 316 setQualityOfService:NSQualityOfServiceUserInteractive]; | |
| 317 } | |
| 318 }); | |
| 319 return operationQueueForStashAndRestoreOperations; | |
| 320 } | |
| 321 | |
| 322 + (NSOperationQueue*)operationQueueForRemoveOperations { | |
| 323 static dispatch_once_t onceToken = 0; | |
| 324 static NSOperationQueue* operationQueueForRemoveOperations = nil; | |
| 325 dispatch_once(&onceToken, ^{ | |
| 326 operationQueueForRemoveOperations = [[NSOperationQueue alloc] init]; | |
| 327 [operationQueueForRemoveOperations | |
| 328 setMaxConcurrentOperationCount:NSUIntegerMax]; | |
| 329 if (base::ios::IsRunningOnIOS8OrLater()) { | |
| 330 [operationQueueForRemoveOperations | |
| 331 setQualityOfService:NSQualityOfServiceUserInitiated]; | |
| 332 } | |
| 333 }); | |
| 334 return operationQueueForRemoveOperations; | |
| 335 } | |
| 336 | |
| 337 #pragma mark - | |
| 338 #pragma mark Private Methods | |
| 339 | |
| 199 - (NSArray*)browsingDataManagersForBrowsingDataTypes: | 340 - (NSArray*)browsingDataManagersForBrowsingDataTypes: |
| 200 (web::BrowsingDataTypes)browsingDataTypes { | 341 (web::BrowsingDataTypes)browsingDataTypes { |
| 201 __block NSMutableArray* result = [NSMutableArray array]; | 342 __block NSMutableArray* result = [NSMutableArray array]; |
| 202 [_browsingDataTypeMap | 343 [_browsingDataTypeMap |
| 203 enumerateKeysAndObjectsUsingBlock:^(NSNumber* dataType, | 344 enumerateKeysAndObjectsUsingBlock:^(NSNumber* dataType, |
| 204 id<CRWBrowsingDataManager> manager, | 345 id<CRWBrowsingDataManager> manager, |
| 205 BOOL*) { | 346 BOOL*) { |
| 206 if ([dataType unsignedIntegerValue] & browsingDataTypes) { | 347 if ([dataType unsignedIntegerValue] & browsingDataTypes) { |
| 207 [result addObject:manager]; | 348 [result addObject:manager]; |
| 208 } | 349 } |
| 209 }]; | 350 }]; |
| 210 return result; | 351 return result; |
| 211 } | 352 } |
| 212 | 353 |
| 213 - (id<CRWBrowsingDataStoreDelegate>)delegate { | |
| 214 return _delegate; | |
| 215 } | |
| 216 | |
| 217 - (void)setDelegate:(id<CRWBrowsingDataStoreDelegate>)delegate { | |
| 218 _delegate.reset(delegate); | |
| 219 } | |
| 220 | |
| 221 - (SEL)browsingDataManagerSelectorForOperationType: | 354 - (SEL)browsingDataManagerSelectorForOperationType: |
| 222 (OperationType)operationType { | 355 (OperationType)operationType { |
| 223 switch (operationType) { | 356 switch (operationType) { |
| 224 case NONE: | 357 case NONE: |
| 225 NOTREACHED(); | 358 NOTREACHED(); |
| 226 return nullptr; | 359 return nullptr; |
| 227 case STASH: | 360 case STASH: |
| 228 return @selector(stashData); | 361 return @selector(stashData); |
| 229 case RESTORE: | 362 case RESTORE: |
| 230 return @selector(restoreData); | 363 return @selector(restoreData); |
| 231 case REMOVE: | 364 case REMOVE: |
| 232 return [self browsingDataManagerSelectorForRemoveOperationType]; | 365 return [self browsingDataManagerSelectorForRemoveOperationType]; |
| 233 }; | 366 }; |
| 234 NOTREACHED(); | |
| 235 return nullptr; | |
| 236 } | 367 } |
| 237 | 368 |
| 238 - (SEL)browsingDataManagerSelectorForRemoveOperationType { | 369 - (SEL)browsingDataManagerSelectorForRemoveOperationType { |
| 239 if (self.mode == web::ACTIVE) { | 370 if (self.mode == web::ACTIVE) { |
| 240 return @selector(removeDataAtCanonicalPath); | 371 return @selector(removeDataAtCanonicalPath); |
| 241 } | 372 } |
| 242 if (self.mode == web::INACTIVE) { | 373 if (self.mode == web::INACTIVE) { |
| 243 return @selector(removeDataAtStashPath); | 374 return @selector(removeDataAtStashPath); |
| 244 } | 375 } |
| 376 // Since the mode is |CHANGING|, find the last |STASH| or |RESTORE| operation | |
| 377 // that was enqueued in order to find out the eventual mode that the | |
| 378 // CRWBrowsingDataStore will be in when this |REMOVE| operation is run. | |
| 245 DCHECK(_lastDispatchedStashOrRestoreOperation); | 379 DCHECK(_lastDispatchedStashOrRestoreOperation); |
| 246 NSString* lastDispatchedStashOrRestoreOperationName = | 380 NSString* lastDispatchedStashOrRestoreOperationName = |
| 247 [_lastDispatchedStashOrRestoreOperation name]; | 381 [_lastDispatchedStashOrRestoreOperation name]; |
| 248 if ([lastDispatchedStashOrRestoreOperationName | 382 if ([lastDispatchedStashOrRestoreOperationName |
| 249 isEqual:kRestoreOperationName]) { | 383 isEqual:kRestoreOperationName]) { |
| 250 return @selector(removeDataAtCanonicalPath); | 384 return @selector(removeDataAtCanonicalPath); |
| 251 } | 385 } |
| 252 if ([lastDispatchedStashOrRestoreOperationName isEqual:kStashOperationName]) { | 386 if ([lastDispatchedStashOrRestoreOperationName isEqual:kStashOperationName]) { |
| 253 return @selector(removeDataAtStashPath); | 387 return @selector(removeDataAtStashPath); |
| 254 } | 388 } |
| 255 NOTREACHED(); | 389 NOTREACHED(); |
| 256 return nullptr; | 390 return nullptr; |
| 257 } | 391 } |
| 258 | 392 |
| 259 + (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key { | |
| 260 // It is necessary to override this for |mode| because the default KVO | |
| 261 // behavior in NSObject is to fire a notification irrespective of if an actual | |
| 262 // change was made to the ivar or not. The |mode| property needs fine grained | |
| 263 // control over the actual notifications being fired since observers need to | |
| 264 // be notified iff the |mode| actually changed. | |
| 265 if ([key isEqual:@"mode"]) { | |
| 266 return NO; | |
| 267 } | |
| 268 return [super automaticallyNotifiesObserversForKey:(NSString*)key]; | |
| 269 } | |
| 270 | |
| 271 - (web::BrowsingDataStoreMode)mode { | |
| 272 DCHECK([NSThread isMainThread]); | |
| 273 return _mode; | |
| 274 } | |
| 275 | |
| 276 - (void)setMode:(web::BrowsingDataStoreMode)mode { | |
| 277 DCHECK([NSThread isMainThread]); | |
| 278 if (_mode == mode) { | |
| 279 return; | |
| 280 } | |
| 281 if (mode == web::ACTIVE || mode == web::INACTIVE) { | |
| 282 DCHECK(!self.numberOfPendingStashOrRestoreOperations); | |
| 283 } | |
| 284 [self willChangeValueForKey:@"mode"]; | |
| 285 _mode = mode; | |
| 286 [self didChangeValueForKey:@"mode"]; | |
| 287 } | |
| 288 | |
| 289 - (void)finalizeChangeToMode:(web::BrowsingDataStoreMode)mode | 393 - (void)finalizeChangeToMode:(web::BrowsingDataStoreMode)mode |
| 290 andCallCompletionHandler:(void (^)(BOOL modeChangeWasSuccessful))handler { | 394 andCallCompletionHandler:(void (^)(BOOL modeChangeWasSuccessful))handler { |
| 291 DCHECK([NSThread isMainThread]); | 395 DCHECK([NSThread isMainThread]); |
| 292 DCHECK_NE(web::CHANGING, mode); | 396 DCHECK_NE(web::CHANGING, mode); |
| 293 | 397 |
| 294 BOOL modeChangeWasSuccessful = NO; | 398 BOOL modeChangeWasSuccessful = NO; |
| 295 if (!self.numberOfPendingStashOrRestoreOperations) { | 399 if (!self.numberOfPendingStashOrRestoreOperations) { |
| 296 [self setMode:mode]; | 400 [self setMode:mode]; |
| 297 modeChangeWasSuccessful = YES; | 401 modeChangeWasSuccessful = YES; |
| 298 } | 402 } |
| 299 if (handler) { | 403 if (handler) { |
| 300 handler(modeChangeWasSuccessful); | 404 handler(modeChangeWasSuccessful); |
| 301 } | 405 } |
| 302 } | 406 } |
| 303 | 407 |
| 304 - (NSUInteger)numberOfPendingStashOrRestoreOperations { | |
| 305 DCHECK([NSThread isMainThread]); | |
| 306 return _numberOfPendingStashOrRestoreOperations; | |
| 307 } | |
| 308 | |
| 309 - (void)setNumberOfPendingStashOrRestoreOperations: | |
| 310 (NSUInteger)numberOfPendingStashOrRestoreOperations { | |
| 311 DCHECK([NSThread isMainThread]); | |
| 312 _numberOfPendingStashOrRestoreOperations = | |
| 313 numberOfPendingStashOrRestoreOperations; | |
| 314 } | |
| 315 | |
| 316 - (void)makeActiveWithCompletionHandler: | |
| 317 (void (^)(BOOL success))completionHandler { | |
| 318 DCHECK([NSThread isMainThread]); | |
| 319 | |
| 320 [self changeMode:web::ACTIVE completionHandler:completionHandler]; | |
| 321 } | |
| 322 | |
| 323 - (void)makeInactiveWithCompletionHandler: | |
| 324 (void (^)(BOOL success))completionHandler { | |
| 325 DCHECK([NSThread isMainThread]); | |
| 326 | |
| 327 [self changeMode:web::INACTIVE completionHandler:completionHandler]; | |
| 328 } | |
| 329 | |
| 330 - (void)changeMode:(web::BrowsingDataStoreMode)mode | 408 - (void)changeMode:(web::BrowsingDataStoreMode)mode |
| 331 completionHandler: | 409 completionHandler: |
| 332 (void (^)(BOOL modeChangeWasSuccessful))completionHandler { | 410 (void (^)(BOOL modeChangeWasSuccessful))completionHandler { |
| 333 DCHECK([NSThread isMainThread]); | 411 DCHECK([NSThread isMainThread]); |
| 334 | 412 |
| 335 ProceduralBlock completionHandlerAfterPerformingOperation = ^{ | 413 ProceduralBlock completionHandlerAfterPerformingOperation = ^{ |
| 336 [self finalizeChangeToMode:mode andCallCompletionHandler:completionHandler]; | 414 [self finalizeChangeToMode:mode andCallCompletionHandler:completionHandler]; |
| 337 }; | 415 }; |
| 338 | 416 |
| 339 // Already in the desired mode. | 417 OperationType operationType = [self operationTypeToChangeMode:mode]; |
| 340 if (self.mode == mode) { | 418 if (operationType == NONE) { |
| 341 // As a caller of this API, it is awkward to get the callback before the | 419 // As a caller of this API, it is awkward to get the callback before the |
| 342 // method call has completed, hence defer it. | 420 // method call has completed, hence defer it. |
| 343 dispatch_async(dispatch_get_main_queue(), ^{ | 421 dispatch_async(dispatch_get_main_queue(), |
| 344 completionHandlerAfterPerformingOperation(); | 422 completionHandlerAfterPerformingOperation); |
| 345 }); | 423 } else { |
| 346 return; | 424 [self performOperationWithType:operationType |
| 425 browsingDataManagers:[self allBrowsingDataManagers] | |
| 426 completionHandler:completionHandlerAfterPerformingOperation]; | |
| 347 } | 427 } |
| 428 } | |
| 429 | |
| 430 - (OperationType)operationTypeToChangeMode:(web::BrowsingDataStoreMode)mode { | |
| 431 DCHECK_NE(web::CHANGING, mode); | |
| 348 | 432 |
| 349 OperationType operationType = NONE; | 433 OperationType operationType = NONE; |
| 350 if (mode == web::ACTIVE) { | 434 if (mode == self.mode) { |
| 435 // Already in the desired mode. | |
| 436 operationType = NONE; | |
| 437 } else if (mode == web::ACTIVE) { | |
| 351 // By default a |RESTORE| operation is performed when the mode is changed | 438 // By default a |RESTORE| operation is performed when the mode is changed |
| 352 // to |ACTIVE|. | 439 // to |ACTIVE|. |
| 353 operationType = RESTORE; | 440 operationType = RESTORE; |
| 354 web::BrowsingDataStoreMakeActivePolicy makeActivePolicy = | 441 web::BrowsingDataStoreMakeActivePolicy makeActivePolicy = |
| 355 [_delegate decideMakeActiveOperationPolicyForBrowsingDataStore:self]; | 442 [_delegate decideMakeActiveOperationPolicyForBrowsingDataStore:self]; |
| 356 operationType = (makeActivePolicy == web::ADOPT) ? REMOVE : RESTORE; | 443 operationType = (makeActivePolicy == web::ADOPT) ? REMOVE : RESTORE; |
| 357 } else { | 444 } else { |
| 358 // By default a |STASH| operation is performed when the mode is changed to | 445 // By default a |STASH| operation is performed when the mode is changed to |
| 359 // |INACTIVE|. | 446 // |INACTIVE|. |
| 360 operationType = STASH; | 447 operationType = STASH; |
| 361 web::BrowsingDataStoreMakeInactivePolicy makeInactivePolicy = | 448 web::BrowsingDataStoreMakeInactivePolicy makeInactivePolicy = |
| 362 [_delegate decideMakeInactiveOperationPolicyForBrowsingDataStore:self]; | 449 [_delegate decideMakeInactiveOperationPolicyForBrowsingDataStore:self]; |
| 363 operationType = (makeInactivePolicy == web::DELETE) ? REMOVE : STASH; | 450 operationType = (makeInactivePolicy == web::DELETE) ? REMOVE : STASH; |
| 364 } | 451 } |
| 365 DCHECK_NE(NONE, operationType); | 452 return operationType; |
| 366 [self performOperationWithType:operationType | |
| 367 browsingDataManagers:[self allBrowsingDataManagers] | |
| 368 completionHandler:completionHandlerAfterPerformingOperation]; | |
| 369 } | |
| 370 | |
| 371 - (void)removeDataOfTypes:(web::BrowsingDataTypes)browsingDataTypes | |
| 372 completionHandler:(ProceduralBlock)completionHandler { | |
| 373 DCHECK([NSThread isMainThread]); | |
| 374 | |
| 375 NSArray* browsingDataManagers = | |
| 376 [self browsingDataManagersForBrowsingDataTypes:browsingDataTypes]; | |
| 377 [self performOperationWithType:REMOVE | |
| 378 browsingDataManagers:browsingDataManagers | |
| 379 completionHandler:^{ | |
| 380 // Since this may be called on a background thread, bounce to | |
| 381 // the main thread. | |
| 382 dispatch_async(dispatch_get_main_queue(), completionHandler); | |
| 383 }]; | |
| 384 } | |
| 385 | |
| 386 - (BOOL)hasPendingOperations { | |
| 387 if (!_lastDispatchedOperation) { | |
| 388 return NO; | |
| 389 } | |
| 390 return ![_lastDispatchedOperation isFinished]; | |
| 391 } | 453 } |
| 392 | 454 |
| 393 - (void)performOperationWithType:(OperationType)operationType | 455 - (void)performOperationWithType:(OperationType)operationType |
| 394 browsingDataManagers:(NSArray*)browsingDataManagers | 456 browsingDataManagers:(NSArray*)browsingDataManagers |
| 395 completionHandler:(ProceduralBlock)completionHandler { | 457 completionHandler:(ProceduralBlock)completionHandler { |
| 396 DCHECK([NSThread isMainThread]); | 458 DCHECK([NSThread isMainThread]); |
| 397 DCHECK(completionHandler); | 459 DCHECK(completionHandler); |
| 398 DCHECK_NE(NONE, operationType); | 460 DCHECK_NE(NONE, operationType); |
| 399 | 461 |
| 400 SEL selector = | 462 SEL selector = |
| 401 [self browsingDataManagerSelectorForOperationType:operationType]; | 463 [self browsingDataManagerSelectorForOperationType:operationType]; |
| 402 DCHECK(selector); | 464 DCHECK(selector); |
| 403 | 465 |
| 404 if (operationType == RESTORE || operationType == STASH) { | 466 if (operationType == RESTORE || operationType == STASH) { |
| 405 [self setMode:web::CHANGING]; | 467 [self setMode:web::CHANGING]; |
| 406 ++self.numberOfPendingStashOrRestoreOperations; | 468 ++self.numberOfPendingStashOrRestoreOperations; |
| 407 completionHandler = ^{ | 469 completionHandler = ^{ |
| 408 --self.numberOfPendingStashOrRestoreOperations; | 470 --self.numberOfPendingStashOrRestoreOperations; |
| 409 // It is safe to this and does not lead to the block (|completionHandler|) | 471 // It is safe to this and does not lead to the block (|completionHandler|) |
| 410 // retaining itself. | 472 // retaining itself. |
| 411 completionHandler(); | 473 completionHandler(); |
| 412 }; | 474 }; |
| 413 } | 475 } |
| 414 | 476 |
| 415 ProceduralBlock callCompletionHandlerOnMainThread = ^{ | 477 ProceduralBlock callCompletionHandlerOnMainThread = ^{ |
| 416 // This is called on a background thread, hence the need to bounce to the | 478 // This is called on a background thread, hence the need to bounce to the |
| 417 // main thread. | 479 // main thread. |
| 418 dispatch_async(dispatch_get_main_queue(), ^{ | 480 dispatch_async(dispatch_get_main_queue(), completionHandler); |
| 419 completionHandler(); | |
| 420 }); | |
| 421 }; | 481 }; |
| 422 base::scoped_nsobject<NSOperation> operation( | 482 NSOperation* operation = |
| 423 [self newOperationWithBrowsingDataManagers:browsingDataManagers | 483 [self operationWithBrowsingDataManagers:browsingDataManagers |
| 424 selector:selector]); | 484 selector:selector]; |
| 425 | 485 |
| 426 if (operationType == RESTORE || operationType == STASH) { | 486 if (operationType == RESTORE || operationType == STASH) { |
| 427 [operation setName:(RESTORE ? kRestoreOperationName : kStashOperationName)]; | 487 [operation setName:(RESTORE ? kRestoreOperationName : kStashOperationName)]; |
| 428 _lastDispatchedStashOrRestoreOperation.reset([operation retain]); | 488 _lastDispatchedStashOrRestoreOperation.reset([operation retain]); |
| 429 } | 489 } |
| 430 | 490 |
| 431 NSOperationQueue* queue = nil; | 491 NSOperationQueue* queue = nil; |
| 432 switch (operationType) { | 492 switch (operationType) { |
| 493 case NONE: | |
| 494 NOTREACHED(); | |
| 495 break; | |
| 433 case STASH: | 496 case STASH: |
| 434 case RESTORE: | 497 case RESTORE: |
| 435 queue = [CRWBrowsingDataStore operationQueueForStashAndRestoreOperations]; | 498 queue = [CRWBrowsingDataStore operationQueueForStashAndRestoreOperations]; |
| 436 break; | 499 break; |
| 437 case REMOVE: | 500 case REMOVE: |
| 438 queue = [CRWBrowsingDataStore operationQueueForRemoveOperations]; | 501 queue = [CRWBrowsingDataStore operationQueueForRemoveOperations]; |
| 439 break; | 502 break; |
| 440 default: | |
| 441 NOTREACHED(); | |
| 442 break; | |
| 443 } | 503 } |
| 444 | 504 |
| 445 NSOperation* completionHandlerOperation = [NSBlockOperation | 505 NSOperation* completionHandlerOperation = [NSBlockOperation |
| 446 blockOperationWithBlock:callCompletionHandlerOnMainThread]; | 506 blockOperationWithBlock:callCompletionHandlerOnMainThread]; |
| 447 | 507 |
| 448 [self addOperation:operation toQueue:queue]; | 508 [self addOperation:operation toQueue:queue]; |
| 449 [self addOperation:completionHandlerOperation toQueue:queue]; | 509 [self addOperation:completionHandlerOperation toQueue:queue]; |
| 450 } | 510 } |
| 451 | 511 |
| 452 - (NSOperation*) | 512 - (NSOperation*)operationWithBrowsingDataManagers:(NSArray*)browsingDataManagers |
| 453 newOperationWithBrowsingDataManagers:(NSArray*)browsingDataManagers | 513 selector:(SEL)selector { |
| 454 selector:(SEL)selector { | 514 NSBlockOperation* operation = [[[NSBlockOperation alloc] init] autorelease]; |
| 455 NSBlockOperation* operation = [[NSBlockOperation alloc] init]; | |
| 456 for (id<CRWBrowsingDataManager> manager : browsingDataManagers) { | 515 for (id<CRWBrowsingDataManager> manager : browsingDataManagers) { |
| 457 // |addExecutionBlock| farms out the different blocks added to it. hence the | 516 // |addExecutionBlock| farms out the different blocks added to it. hence the |
| 458 // operations are implicitly parallelized. | 517 // operations are implicitly parallelized. |
| 459 [operation addExecutionBlock:^{ | 518 [operation addExecutionBlock:^{ |
| 460 [manager performSelector:selector]; | 519 [manager performSelector:selector]; |
| 461 }]; | 520 }]; |
| 462 } | 521 } |
| 463 return operation; | 522 return operation; |
| 464 } | 523 } |
| 465 | 524 |
| 466 - (void)addOperation:(NSOperation*)operation toQueue:(NSOperationQueue*)queue { | 525 - (void)addOperation:(NSOperation*)operation toQueue:(NSOperationQueue*)queue { |
| 467 DCHECK([NSThread isMainThread]); | 526 DCHECK([NSThread isMainThread]); |
| 468 DCHECK(operation); | 527 DCHECK(operation); |
| 469 DCHECK(queue); | 528 DCHECK(queue); |
| 470 | 529 |
| 471 if (_lastDispatchedOperation) { | 530 if (_lastDispatchedOperation) { |
| 472 [operation addDependency:_lastDispatchedOperation]; | 531 [operation addDependency:_lastDispatchedOperation]; |
| 473 } | 532 } |
| 474 _lastDispatchedOperation.reset([operation retain]); | 533 _lastDispatchedOperation.reset([operation retain]); |
| 475 [queue addOperation:operation]; | 534 [queue addOperation:operation]; |
| 476 } | 535 } |
| 477 | 536 |
| 478 @end | 537 @end |
| OLD | NEW |