1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
|
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/cocoa/clear_browsing_data_controller.h"
#include "app/l10n_util.h"
#include "base/command_line.h"
#include "base/mac_util.h"
#include "base/scoped_nsobject.h"
#include "base/singleton.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/browsing_data_remover.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
NSString* const kClearBrowsingDataControllerDidDelete =
@"kClearBrowsingDataControllerDidDelete";
NSString* const kClearBrowsingDataControllerRemoveMask =
@"kClearBrowsingDataControllerRemoveMask";
namespace {
// Compare function for -[NSArray sortedArrayUsingFunction:context:] that
// sorts the views in Y order top down.
NSInteger CompareFrameY(id view1, id view2, void* context) {
CGFloat y1 = NSMinY([view1 frame]);
CGFloat y2 = NSMinY([view2 frame]);
if (y1 < y2)
return NSOrderedDescending;
else if (y1 > y2)
return NSOrderedAscending;
else
return NSOrderedSame;
}
typedef std::map<Profile*, ClearBrowsingDataController*> ProfileControllerMap;
} // namespace
@interface ClearBrowsingDataController(Private)
- (void)initFromPrefs;
- (void)persistToPrefs;
- (void)dataRemoverDidFinish;
- (void)syncStateChanged;
@end
class ClearBrowsingObserver : public BrowsingDataRemover::Observer,
public ProfileSyncServiceObserver {
public:
ClearBrowsingObserver(ClearBrowsingDataController* controller)
: controller_(controller) { }
void OnBrowsingDataRemoverDone() { [controller_ dataRemoverDidFinish]; }
void OnStateChanged() { [controller_ syncStateChanged]; }
private:
ClearBrowsingDataController* controller_;
};
@implementation ClearBrowsingDataController
@synthesize clearBrowsingHistory = clearBrowsingHistory_;
@synthesize clearDownloadHistory = clearDownloadHistory_;
@synthesize emptyCache = emptyCache_;
@synthesize deleteCookies = deleteCookies_;
@synthesize clearSavedPasswords = clearSavedPasswords_;
@synthesize clearFormData = clearFormData_;
@synthesize timePeriod = timePeriod_;
@synthesize isClearing = isClearing_;
@synthesize clearingStatus = clearingStatus_;
+ (void)showClearBrowsingDialogForProfile:(Profile*)profile {
ClearBrowsingDataController* controller =
[ClearBrowsingDataController controllerForProfile:profile];
if (![controller isWindowLoaded]) {
// This function needs to return instead of blocking, to match the Windows
// version. It caused problems when launching the dialog from the
// DomUI history page. See bug and code review for more details.
// http://crbug.com/37976
[controller performSelector:@selector(runModalDialog)
withObject:nil
afterDelay:0];
}
}
+ (ClearBrowsingDataController *)controllerForProfile:(Profile*)profile {
// Get the original profile in case we get here from an incognito window
// |GetOriginalProfile()| will return the same profile if it is the original
// profile.
profile = profile->GetOriginalProfile();
ProfileControllerMap* map = Singleton<ProfileControllerMap>::get();
DCHECK(map != NULL);
ProfileControllerMap::iterator it = map->find(profile);
if (it == map->end()) {
// Since we don't currently support multiple profiles, this class
// has not been tested against this case.
if (map->size() != 0) {
return nil;
}
ClearBrowsingDataController* controller =
[[self alloc] initWithProfile:profile];
it = map->insert(std::make_pair(profile, controller)).first;
}
return it->second;
}
- (id)initWithProfile:(Profile*)profile {
DCHECK(profile);
// Use initWithWindowNibPath:: instead of initWithWindowNibName: so we
// can override it in a unit test.
NSString *nibpath = [mac_util::MainAppBundle()
pathForResource:@"ClearBrowsingData"
ofType:@"nib"];
if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
observer_.reset(new ClearBrowsingObserver(self));
// Always show preferences for the original profile. Most state when off
// the record comes from the original profile, but we explicitly use
// the original profile to avoid potential problems.
profile_ = profile->GetOriginalProfile();
sync_service_ = profile_->GetProfileSyncService();
if (sync_service_) {
sync_service_->ResetClearServerDataState();
sync_service_->AddObserver(observer_.get());
}
[self initFromPrefs];
}
return self;
}
- (void)dealloc {
if (remover_) {
// We were destroyed while clearing history was in progress. This can only
// occur during automated tests (normally the user can't close the dialog
// while clearing is in progress as the dialog is modal and not closeable).
remover_->RemoveObserver(observer_.get());
}
if (sync_service_)
sync_service_->RemoveObserver(observer_.get());
[self setClearingStatus:nil];
[super dealloc];
}
// Run application modal.
- (void)runModalDialog {
// Check again to make sure there is only one window. Since we use
// |performSelector:afterDelay:| it is possible for this to somehow be
// triggered twice.
DCHECK([NSThread isMainThread]);
if (![self isWindowLoaded]) {
// It takes two passes to adjust the window size. The first pass is to
// determine the width of all the non-wrappable items. The window is then
// resized to that width. Once the width is fixed, the heights of the
// variable-height items can then be calculated, the items can be spaced,
// and the window resized (again) if necessary.
NSWindow* window = [self window];
// Adjust the widths of non-wrappable items.
CGFloat maxWidthGrowth = 0.0;
Class widthBasedTweakerClass = [GTMWidthBasedTweaker class];
for (NSTabViewItem* tabViewItem in [tabView_ tabViewItems])
for (NSView* subView in [[tabViewItem view] subviews])
if ([subView isKindOfClass:widthBasedTweakerClass]) {
GTMWidthBasedTweaker* tweaker = (GTMWidthBasedTweaker*)subView;
CGFloat delta = [tweaker changedWidth];
maxWidthGrowth = std::max(maxWidthGrowth, delta);
}
// Adjust the width of the window.
if (maxWidthGrowth > 0.0) {
NSSize adjustSize = NSMakeSize(maxWidthGrowth, 0);
adjustSize = [[window contentView] convertSize:adjustSize toView:nil];
NSRect windowFrame = [window frame];
windowFrame.size.width += adjustSize.width;
[window setFrame:windowFrame display:NO];
}
// Adjust the heights and locations of the items on the "Other data" tab.
CGFloat cumulativeHeightGrowth = 0.0;
NSArray* subViews =
[[otherDataTab_ subviews] sortedArrayUsingFunction:CompareFrameY
context:NULL];
for (NSView* view in subViews) {
if ([view isHidden])
continue;
if ([objectsToVerticallySize_ containsObject:view]) {
DCHECK([view isKindOfClass:[NSTextField class]]);
CGFloat viewHeightGrowth = [GTMUILocalizerAndLayoutTweaker
sizeToFitFixedWidthTextField:(NSTextField*)view];
if (viewHeightGrowth > 0.0)
cumulativeHeightGrowth += viewHeightGrowth;
}
NSRect viewFrame = [view frame];
viewFrame.origin.y -= cumulativeHeightGrowth;
[view setFrame:viewFrame];
}
// Adjust the height of the window.
if (cumulativeHeightGrowth > 0.0) {
NSSize adjustSize = NSMakeSize(0, cumulativeHeightGrowth);
adjustSize = [[window contentView] convertSize:adjustSize toView:nil];
NSRect windowFrame = [window frame];
windowFrame.size.height += adjustSize.height;
[window setFrame:windowFrame display:NO];
}
// Now start the modal loop.
[NSApp runModalForWindow:window];
}
}
- (int)removeMask {
int removeMask = 0L;
if (clearBrowsingHistory_)
removeMask |= BrowsingDataRemover::REMOVE_HISTORY;
if (clearDownloadHistory_)
removeMask |= BrowsingDataRemover::REMOVE_DOWNLOADS;
if (emptyCache_)
removeMask |= BrowsingDataRemover::REMOVE_CACHE;
if (deleteCookies_)
removeMask |= BrowsingDataRemover::REMOVE_COOKIES;
if (clearSavedPasswords_)
removeMask |= BrowsingDataRemover::REMOVE_PASSWORDS;
if (clearFormData_)
removeMask |= BrowsingDataRemover::REMOVE_FORM_DATA;
return removeMask;
}
// Called when the user clicks the "clear" button. Do the work and persist
// the prefs for next time. We don't stop the modal session until we get
// the callback from the BrowsingDataRemover so the window stays on the screen.
// While we're working, dim the buttons so the user can't click them.
- (IBAction)clearData:(id)sender {
// Set that we're working so that the buttons disable.
[self setClearingStatus:l10n_util::GetNSString(IDS_CLEAR_DATA_DELETING)];
[self setIsClearing:YES];
[self persistToPrefs];
// BrowsingDataRemover deletes itself when done.
remover_ = new BrowsingDataRemover(profile_,
static_cast<BrowsingDataRemover::TimePeriod>(timePeriod_),
base::Time());
remover_->AddObserver(observer_.get());
remover_->Remove([self removeMask]);
}
// Called when the user clicks the cancel button. All we need to do is stop
// the modal session.
- (IBAction)cancel:(id)sender {
[self closeDialog];
}
// Called when the user clicks the "Flash Player storage settings" button.
- (IBAction)openFlashPlayerSettings:(id)sender {
// The "Clear Data" dialog is app-modal on OS X. Hence, close it before
// opening a tab with flash settings.
[self closeDialog];
Browser* browser = Browser::Create(profile_);
browser->OpenURL(GURL(l10n_util::GetStringUTF8(IDS_FLASH_STORAGE_URL)),
GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
browser->window()->Show();
}
- (IBAction)openGoogleDashboard:(id)sender {
// The "Clear Data" dialog is app-modal on OS X. Hence, close it before
// opening a tab with the dashboard.
[self closeDialog];
Browser* browser = Browser::Create(profile_);
browser->OpenURL(GURL(l10n_util::GetStringUTF8(IDS_PRIVACY_DASHBOARD_URL)),
GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
browser->window()->Show();
}
- (void)closeDialog {
ProfileControllerMap* map = Singleton<ProfileControllerMap>::get();
ProfileControllerMap::iterator it = map->find(profile_);
if (it != map->end()) {
map->erase(it);
}
[self autorelease];
[[self window] orderOut:self];
[NSApp stopModal];
}
// Initialize the bools from prefs using the setters to be KVO-compliant.
- (void)initFromPrefs {
PrefService* prefs = profile_->GetPrefs();
[self setClearBrowsingHistory:
prefs->GetBoolean(prefs::kDeleteBrowsingHistory)];
[self setClearDownloadHistory:
prefs->GetBoolean(prefs::kDeleteDownloadHistory)];
[self setEmptyCache:prefs->GetBoolean(prefs::kDeleteCache)];
[self setDeleteCookies:prefs->GetBoolean(prefs::kDeleteCookies)];
[self setClearSavedPasswords:prefs->GetBoolean(prefs::kDeletePasswords)];
[self setClearFormData:prefs->GetBoolean(prefs::kDeleteFormData)];
[self setTimePeriod:prefs->GetInteger(prefs::kDeleteTimePeriod)];
}
// Save the checkbox values to the preferences.
- (void)persistToPrefs {
PrefService* prefs = profile_->GetPrefs();
prefs->SetBoolean(prefs::kDeleteBrowsingHistory,
[self clearBrowsingHistory]);
prefs->SetBoolean(prefs::kDeleteDownloadHistory,
[self clearDownloadHistory]);
prefs->SetBoolean(prefs::kDeleteCache, [self emptyCache]);
prefs->SetBoolean(prefs::kDeleteCookies, [self deleteCookies]);
prefs->SetBoolean(prefs::kDeletePasswords, [self clearSavedPasswords]);
prefs->SetBoolean(prefs::kDeleteFormData, [self clearFormData]);
prefs->SetInteger(prefs::kDeleteTimePeriod, [self timePeriod]);
}
// Called when the data remover object is done with its work. Close the window.
// The remover will delete itself. End the modal session at this point.
- (void)dataRemoverDidFinish {
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
int removeMask = [self removeMask];
NSDictionary* userInfo =
[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:removeMask]
forKey:kClearBrowsingDataControllerRemoveMask];
[center postNotificationName:kClearBrowsingDataControllerDidDelete
object:self
userInfo:userInfo];
[self closeDialog];
[self setClearingStatus:nil];
[self setIsClearing:NO];
remover_ = NULL;
}
- (IBAction)stopSyncAndDeleteData:(id)sender {
// Protect against the unlikely case where the server received a message, and
// the syncer syncs and resets itself before the user tries pressing the Clear
// button in this dialog again. TODO(raz) Confirm whether we have an issue
// here
if (sync_service_->HasSyncSetupCompleted()) {
bool clear = platform_util::SimpleYesNoBox(
nil,
l10n_util::GetStringUTF16(IDS_CONFIRM_CLEAR_TITLE),
l10n_util::GetStringUTF16(IDS_CONFIRM_CLEAR_DESCRIPTION));
if (clear) {
sync_service_->ClearServerData();
[self syncStateChanged];
}
}
}
- (void)syncStateChanged {
bool deleteInProgress = false;
ProfileSyncService::ClearServerDataState clearState =
sync_service_->GetClearServerDataState();
sync_service_->ResetClearServerDataState();
switch (clearState) {
case ProfileSyncService::CLEAR_NOT_STARTED:
// This can occur on a first start and after a failed clear (which does
// not close the tab). Do nothing.
break;
case ProfileSyncService::CLEAR_CLEARING:
// Clearing buttons on all tabs are disabled at this point, throbber is
// going.
[self setClearingStatus:l10n_util::GetNSString(IDS_CLEAR_DATA_SENDING)];
deleteInProgress = true;
break;
case ProfileSyncService::CLEAR_FAILED:
// Show an error and reallow clearing.
[self setClearingStatus:l10n_util::GetNSString(IDS_CLEAR_DATA_ERROR)];
deleteInProgress = false;
break;
case ProfileSyncService::CLEAR_SUCCEEDED:
// Close the dialog box, success!
[self setClearingStatus:nil];
deleteInProgress = false;
[self closeDialog];
break;
}
[self setIsClearing:deleteInProgress];
}
- (BOOL)isSyncVisible {
return CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableClearServerData);
}
- (BOOL)isSyncEnabled {
return sync_service_ && sync_service_->HasSyncSetupCompleted();
}
- (NSFont*)labelFont {
return [NSFont boldSystemFontOfSize:13];
}
@end
|