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 |