| Index: chrome/browser/cocoa/clear_browsing_data_controller.mm
|
| ===================================================================
|
| --- chrome/browser/cocoa/clear_browsing_data_controller.mm (revision 61509)
|
| +++ chrome/browser/cocoa/clear_browsing_data_controller.mm (working copy)
|
| @@ -5,15 +5,20 @@
|
| #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"
|
|
|
| @@ -22,27 +27,43 @@
|
| 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 {
|
| +class ClearBrowsingObserver : public BrowsingDataRemover::Observer,
|
| + public ProfileSyncServiceObserver {
|
| public:
|
| ClearBrowsingObserver(ClearBrowsingDataController* controller)
|
| : controller_(controller) { }
|
| void OnBrowsingDataRemoverDone() { [controller_ dataRemoverDidFinish]; }
|
| + void OnStateChanged() { [controller_ syncStateChanged]; }
|
| private:
|
| ClearBrowsingDataController* controller_;
|
| };
|
|
|
| -namespace {
|
| -
|
| -typedef std::map<Profile*, ClearBrowsingDataController*> ProfileControllerMap;
|
| -
|
| -} // namespace
|
| -
|
| @implementation ClearBrowsingDataController
|
|
|
| @synthesize clearBrowsingHistory = clearBrowsingHistory_;
|
| @@ -53,14 +74,15 @@
|
| @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
|
| - // api call. It caused problems when launching the dialog from the
|
| - // DomUI history page. See bug and code review for more details.
|
| + // 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
|
| @@ -101,6 +123,8 @@
|
| if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
|
| profile_ = profile;
|
| observer_.reset(new ClearBrowsingObserver(self));
|
| + profile_->GetProfileSyncService()->ResetClearServerDataState();
|
| + profile_->GetProfileSyncService()->AddObserver(observer_.get());
|
| [self initFromPrefs];
|
| }
|
| return self;
|
| @@ -113,6 +137,8 @@
|
| // while clearing is in progress as the dialog is modal and not closeable).
|
| remover_->RemoveObserver(observer_.get());
|
| }
|
| + profile_->GetProfileSyncService()->RemoveObserver(observer_.get());
|
| + [self setClearingStatus:nil];
|
|
|
| [super dealloc];
|
| }
|
| @@ -124,28 +150,65 @@
|
| // 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.
|
| + // 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;
|
| - 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);
|
| + 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];
|
| }
|
| - 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];
|
| +
|
| + // 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];
|
| }
|
| @@ -160,9 +223,9 @@
|
| if (emptyCache_)
|
| removeMask |= BrowsingDataRemover::REMOVE_CACHE;
|
| if (deleteCookies_)
|
| - removeMask |= BrowsingDataRemover::REMOVE_COOKIES;
|
| + removeMask |= BrowsingDataRemover::REMOVE_COOKIES;
|
| if (clearSavedPasswords_)
|
| - removeMask |= BrowsingDataRemover::REMOVE_PASSWORDS;
|
| + removeMask |= BrowsingDataRemover::REMOVE_PASSWORDS;
|
| if (clearFormData_)
|
| removeMask |= BrowsingDataRemover::REMOVE_FORM_DATA;
|
| return removeMask;
|
| @@ -174,6 +237,7 @@
|
| // 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];
|
| @@ -204,6 +268,17 @@
|
| 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_);
|
| @@ -256,9 +331,73 @@
|
| userInfo:userInfo];
|
|
|
| [self closeDialog];
|
| - [[self window] orderOut:self];
|
| + [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 (profile_->GetProfileSyncService()->HasSyncSetupCompleted()) {
|
| + bool clear = platform_util::SimpleYesNoBox(
|
| + nil,
|
| + l10n_util::GetStringUTF16(IDS_CONFIRM_CLEAR_TITLE),
|
| + l10n_util::GetStringUTF16(IDS_CONFIRM_CLEAR_DESCRIPTION));
|
| + if (clear) {
|
| + profile_->GetProfileSyncService()->ClearServerData();
|
| + [self syncStateChanged];
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (void)syncStateChanged {
|
| + bool deleteInProgress = false;
|
| +
|
| + ProfileSyncService::ClearServerDataState clearState =
|
| + profile_->GetProfileSyncService()->GetClearServerDataState();
|
| + profile_->GetProfileSyncService()->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)isSyncEnabled {
|
| + BOOL allowClearServerDataUI =
|
| + CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kEnableClearServerData);
|
| +
|
| + return allowClearServerDataUI &&
|
| + profile_->GetProfileSyncService()->HasSyncSetupCompleted();
|
| +}
|
| +
|
| +- (NSFont*)labelFont {
|
| + return [NSFont boldSystemFontOfSize:13];
|
| +}
|
| +
|
| @end
|
|
|