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
3 // found in the LICENSE file.
5 #import "ios/web/public/crw_browsing_data_store.h"
7 #import <Foundation/Foundation.h>
9 #import "base/ios/weak_nsobject.h"
10 #include "base/logging.h"
11 #import "base/mac/scoped_nsobject.h"
12 #import "ios/web/browsing_data_managers/crw_cookie_browsing_data_manager.h"
13 #include "ios/web/public/active_state_manager.h"
14 #include "ios/web/public/browser_state.h"
17 NSString* const kBrowsingDataTypeCookies = @"kBrowsingDataTypeCookies";
21 // Represents the type of operations that a CRWBrowsingDataStore can perform.
29 @interface CRWBrowsingDataStore ()
30 // Returns a serial queue on which stash and restore operations can be scheduled
31 // to be run. All stash/restore operations need to be run on the same queue
32 // hence it is shared with all CRWBrowsingDataStores.
33 + (NSOperationQueue*)operationQueueForStashAndRestoreOperations;
34 // Returns a concurrent queue on which remove operations can be scheduled to be
35 // run. All remove operations need to be run on the same queue hence it is
36 // shared with all CRWBrowsingDataStores.
37 + (NSOperationQueue*)operationQueueForRemoveOperations;
39 // The array of all browsing data managers. Must be accessed from the main
41 @property(nonatomic, readonly) NSArray* allBrowsingDataManagers;
42 // Returns the |CRWBrowsingDataManager| that manages the browsing data for
43 // |browsingDataType|.
44 - (id<CRWBrowsingDataManager>)browsingDataManagerForBrowsingDataType:
45 (NSString*)browsingDataType;
46 // Returns an array of browsing data managers for all the |browsingDataType|.
47 - (NSArray*)browsingDataManagersForBrowsingDataTypes:(NSSet*)browsingDataTypes;
49 // Redefined to be read-write. Must be called from the main thread.
50 @property(nonatomic, assign) CRWBrowsingDataStoreMode mode;
51 // Sets the mode iff there are no more stash or restore operations that are
52 // still pending. |mode| can only be either |ACTIVE| or |INACTIVE|.
53 - (void)setModeIfNotStashingOrRestoring:(CRWBrowsingDataStoreMode)mode;
55 // Performs operations of type |operationType| on each of the
56 // |browsingDataManagers|.
57 // The kind of operations are:
58 // 1) STASH: Stash operation involves stashing browsing data that web views
59 // (UIWebViews and WKWebViews) create to the associated BrowserState's state
61 // 2) RESTORE: Restore operation involves restoring browsing data from the
62 // associated BrowserState's state path so that web views (UIWebViews and
63 // WKWebViews) can read from them.
64 // 3) REMOVE: Remove operation involves removing the data that web views
65 // (UIWebViews and WKWebViews) create.
66 // Precondition: There must be no web views associated with the BrowserState.
67 // |completionHandler| is called on the main thread and cannot be nil.
68 - (void)performOperationWithType:(OperationType)operationType
69 browsingDataManagers:(NSArray*)browsingDataManagers
70 completionHandler:(ProceduralBlock)completionHandler;
72 // Creates an NSOperation that calls |selector| on all the
73 // |browsingDataManagers|. |selector| needs to be one of the methods in
74 // CRWBrowsingDataManager. The created NSOperation will have a completionBlock
75 // of |completionHandler|.
77 newOperationWithBrowsingDataManagers:(NSArray*)browsingDataManagers
78 selector:(SEL)selector
79 completionHandler:(ProceduralBlock)completionHandler;
80 // Enqueues |operation| to be run on |queue|. All operations are serialized to
81 // be run one after another.
82 - (void)addOperation:(NSOperation*)operation toQueue:(NSOperationQueue*)queue;
85 @implementation CRWBrowsingDataStore {
86 web::BrowserState* _browserState; // Weak, owns this object.
87 // The mode of the CRWBrowsingDataStore.
88 CRWBrowsingDataStoreMode _mode;
89 // The dictionary that maps a browsing data type to its
90 // CRWBrowsingDataManager.
91 base::scoped_nsobject<NSDictionary> _browsingDataTypeMap;
92 // The last operation that was enqueued to be run. Can be stash, restore or a
94 base::scoped_nsobject<NSOperation> _lastDispatchedOperation;
95 // The last stash or restore operations that was dispatched to be run.
96 base::scoped_nsobject<NSOperation> _lastDispatchedStashOrRestoreOperation;
99 + (NSOperationQueue*)operationQueueForStashAndRestoreOperations {
100 static dispatch_once_t onceToken = 0;
101 static NSOperationQueue* operationQueueForStashAndRestoreOperations = nil;
102 dispatch_once(&onceToken, ^{
103 operationQueueForStashAndRestoreOperations =
104 [[NSOperationQueue alloc] init];
105 [operationQueueForStashAndRestoreOperations
106 setMaxConcurrentOperationCount:1U];
107 [operationQueueForStashAndRestoreOperations
108 setQualityOfService:NSQualityOfServiceUserInteractive];
110 return operationQueueForStashAndRestoreOperations;
113 + (NSOperationQueue*)operationQueueForRemoveOperations {
114 static dispatch_once_t onceToken = 0;
115 static NSOperationQueue* operationQueueForRemoveOperations = nil;
116 dispatch_once(&onceToken, ^{
117 operationQueueForRemoveOperations = [[NSOperationQueue alloc] init];
118 [operationQueueForRemoveOperations
119 setMaxConcurrentOperationCount:NSUIntegerMax];
120 [operationQueueForRemoveOperations
121 setQualityOfService:NSQualityOfServiceUserInitiated];
123 return operationQueueForRemoveOperations;
126 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState {
129 DCHECK(web::BrowserState::GetActiveStateManager(browserState)->IsActive());
130 DCHECK([NSThread isMainThread]);
131 // TODO(shreyasv): Instantiate the necessary CRWBrowsingDataManagers that
132 // are encapsulated within this class. crbug.com/480654.
133 _browserState = browserState;
135 // TODO(shreyasv): If the creation of CRWBrowsingDataManagers turns out to
136 // be an expensive operations re-visit this with a lazy-evaluation approach.
137 base::scoped_nsobject<CRWCookieBrowsingDataManager>
138 cookieBrowsingDataManager([[CRWCookieBrowsingDataManager alloc]
139 initWithBrowserState:browserState]);
140 _browsingDataTypeMap.reset([@{
141 web::kBrowsingDataTypeCookies : cookieBrowsingDataManager,
147 - (NSString*)description {
148 NSString* format = @"<%@: %p; hasPendingOperations = { %@ }>";
149 NSString* hasPendingOperationsString =
150 [self hasPendingOperations] ? @"YES" : @"NO";
152 [NSString stringWithFormat:format, NSStringFromClass(self.class), self,
153 hasPendingOperationsString];
157 + (NSSet*)allBrowsingDataTypes {
158 return [[[NSSet alloc]
159 initWithObjects:web::kBrowsingDataTypeCookies, nil] autorelease];
162 - (NSArray*)allBrowsingDataManagers {
163 DCHECK([NSThread isMainThread]);
164 return [_browsingDataTypeMap allValues];
167 - (id<CRWBrowsingDataManager>)browsingDataManagerForBrowsingDataType:
168 (NSString*)browsingDataType {
169 return [_browsingDataTypeMap objectForKey:browsingDataType];
172 - (NSArray*)browsingDataManagersForBrowsingDataTypes:(NSSet*)browsingDataTypes {
173 return [_browsingDataTypeMap objectsForKeys:[browsingDataTypes allObjects]
174 notFoundMarker:[NSNull null]];
177 - (CRWBrowsingDataStoreMode)mode {
178 DCHECK([NSThread isMainThread]);
182 - (void)setMode:(CRWBrowsingDataStoreMode)mode {
183 DCHECK([NSThread isMainThread]);
187 - (void)setModeIfNotStashingOrRestoring:(CRWBrowsingDataStoreMode)mode {
188 DCHECK([NSThread isMainThread]);
189 DCHECK_NE(SYNCHRONIZING, mode);
190 if ([_lastDispatchedStashOrRestoreOperation isFinished]) {
195 - (void)makeActiveWithCompletionHandler:(ProceduralBlock)completionHandler {
196 DCHECK([NSThread isMainThread]);
198 base::WeakNSObject<CRWBrowsingDataStore> weakSelf(self);
199 [self performOperationWithType:RESTORE
200 browsingDataManagers:[self allBrowsingDataManagers]
202 [weakSelf setModeIfNotStashingOrRestoring:ACTIVE];
203 if (completionHandler) {
209 - (void)makeInactiveWithCompletionHandler:(ProceduralBlock)completionHandler {
210 DCHECK([NSThread isMainThread]);
212 base::WeakNSObject<CRWBrowsingDataStore> weakSelf(self);
213 [self performOperationWithType:STASH
214 browsingDataManagers:[self allBrowsingDataManagers]
216 [weakSelf setModeIfNotStashingOrRestoring:INACTIVE];
217 if (completionHandler) {
223 - (void)removeDataOfTypes:(NSSet*)browsingDataTypes
224 completionHandler:(ProceduralBlock)completionHandler {
225 DCHECK([NSThread isMainThread]);
227 NSArray* browsingDataManagers =
228 [self browsingDataManagersForBrowsingDataTypes:browsingDataTypes];
229 [self performOperationWithType:REMOVE
230 browsingDataManagers:browsingDataManagers
232 // Since this may be called on a background thread, bounce to
234 dispatch_async(dispatch_get_main_queue(), completionHandler);
238 - (BOOL)hasPendingOperations {
239 if (!_lastDispatchedOperation) {
242 return ![_lastDispatchedOperation isFinished];
245 - (void)performOperationWithType:(OperationType)operationType
246 browsingDataManagers:(NSArray*)browsingDataManagers
247 completionHandler:(ProceduralBlock)completionHandler {
248 DCHECK([NSThread isMainThread]);
249 DCHECK(completionHandler);
251 SEL selector = nullptr;
252 switch (operationType) {
254 selector = @selector(stashData);
257 selector = @selector(restoreData);
260 selector = @selector(removeData);
267 id callCompletionHandlerOnMainThread = ^{
268 // This can be called on a background thread, hence the need to bounce to
270 dispatch_async(dispatch_get_main_queue(), ^{
274 base::scoped_nsobject<NSOperation> operation([self
275 newOperationWithBrowsingDataManagers:browsingDataManagers
277 completionHandler:callCompletionHandlerOnMainThread]);
279 if (operationType == RESTORE || operationType == STASH) {
280 _mode = SYNCHRONIZING;
281 _lastDispatchedStashOrRestoreOperation.reset([operation retain]);
284 NSOperationQueue* queue = nil;
285 switch (operationType) {
288 queue = [CRWBrowsingDataStore operationQueueForStashAndRestoreOperations];
291 queue = [CRWBrowsingDataStore operationQueueForRemoveOperations];
298 [self addOperation:operation toQueue:queue];
302 newOperationWithBrowsingDataManagers:(NSArray*)browsingDataManagers
303 selector:(SEL)selector
304 completionHandler:(ProceduralBlock)completionHandler {
305 NSBlockOperation* operation = [[NSBlockOperation alloc] init];
306 for (id<CRWBrowsingDataManager> manager : browsingDataManagers) {
307 // |addExecutionBlock| farms out the different blocks added to it. hence the
308 // operations are implicitly parallelized.
309 [operation addExecutionBlock:^{
310 [manager performSelector:selector];
313 [operation setCompletionBlock:completionHandler];
317 - (void)addOperation:(NSOperation*)operation toQueue:(NSOperationQueue*)queue {
318 DCHECK([NSThread isMainThread]);
322 if (_lastDispatchedOperation) {
323 [operation addDependency:_lastDispatchedOperation];
325 _lastDispatchedOperation.reset([operation retain]);
326 [queue addOperation:operation];