summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/clear_browsing_data_controller.mm
blob: c79525f703b0ac4044096b540dc10c76320e1974 (plain)
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
// 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/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/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"
#include "grit/locale_settings.h"
#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"

NSString* const kClearBrowsingDataControllerDidDelete =
    @"kClearBrowsingDataControllerDidDelete";
NSString* const kClearBrowsingDataControllerRemoveMask =
    @"kClearBrowsingDataControllerRemoveMask";

@interface ClearBrowsingDataController(Private)
- (void)initFromPrefs;
- (void)persistToPrefs;
- (void)dataRemoverDidFinish;
@end

class ClearBrowsingObserver : public BrowsingDataRemover::Observer {
 public:
  ClearBrowsingObserver(ClearBrowsingDataController* controller)
      : controller_(controller) { }
  void OnBrowsingDataRemoverDone() { [controller_ dataRemoverDidFinish]; }
 private:
  ClearBrowsingDataController* controller_;
};

namespace {

typedef std::map<Profile*, ClearBrowsingDataController*> ProfileControllerMap;

} // namespace

@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_;

+ (void)showClearBrowsingDialogForProfile:(Profile*)profile {
  ClearBrowsingDataController* controller =
      [ClearBrowsingDataController controllerForProfile:profile];
  if (![controller isWindowLoaded]) {
    // This function needs to return instead of blocking, to match the windows
    // api call.  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])) {
    profile_ = profile;
    observer_.reset(new ClearBrowsingObserver(self));
    [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());
  }

  [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]) {
    // The Window size in the nib is a min size, loop over the views collecting
    // the max they grew by, that is how much the window needs to be widened by.
    CGFloat maxWidthGrowth = 0.0;
    NSWindow* window = [self window];
    NSView* contentView = [window contentView];
    Class widthBasedTweakerClass = [GTMWidthBasedTweaker class];
    for (id subView in [contentView subviews]) {
      if ([subView isKindOfClass:widthBasedTweakerClass]) {
        GTMWidthBasedTweaker* tweaker = subView;
        CGFloat delta = [tweaker changedWidth];
        maxWidthGrowth = std::max(maxWidthGrowth, delta);
      }
    }
    if (maxWidthGrowth > 0.0) {
      NSRect rect = [contentView convertRect:[window frame] fromView:nil];
      rect.size.width += maxWidthGrowth;
      rect = [contentView convertRect:rect toView:nil];
      [window setFrame:rect display:NO];
      // For some reason the content view is resizing, but some times not
      // adjusting its origin, so correct it manually.
      [contentView setFrameOrigin:NSZeroPoint];
    }
    // 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 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();
}

- (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 window] orderOut:self];
  [self setIsClearing:NO];
  remover_ = NULL;
}

@end