// 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/content_settings_dialog_controller.h" #import #include "app/l10n_util.h" #include "base/mac_util.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" #import "chrome/browser/cocoa/content_exceptions_window_controller.h" #import "chrome/browser/cocoa/cookies_window_controller.h" #import "chrome/browser/cocoa/geolocation_exceptions_window_controller.h" #import "chrome/browser/cocoa/l10n_util.h" #import "chrome/browser/geolocation/geolocation_content_settings_map.h" #import "chrome/browser/host_content_settings_map.h" #include "chrome/browser/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "grit/locale_settings.h" namespace { // Stores the currently visible content settings dialog, if any. ContentSettingsDialogController* g_instance = nil; // Walks views in top-down order, wraps each to their current width, and moves // the latter ones down to prevent overlaps. CGFloat VerticallyReflowGroup(NSArray* views) { views = [views sortedArrayUsingFunction:cocoa_l10n_util::CompareFrameY context:NULL]; CGFloat localVerticalShift = 0; for (NSInteger index = [views count] - 1; index >= 0; --index) { NSView* view = [views objectAtIndex:index]; // Since the tab pane is in a horizontal resizer in IB, it's convenient // to give all the subviews flexible width so that their sizes are // autoupdated in IB. However, in chrome, the subviews shouldn't have // flexible widths as this looks weird. [view setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin]; NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view); localVerticalShift += delta.height; if (localVerticalShift) { NSPoint origin = [view frame].origin; origin.y -= localVerticalShift; [view setFrameOrigin:origin]; } } return localVerticalShift; } } // namespace @interface ContentSettingsDialogController(Private) - (id)initWithProfile:(Profile*)profile; - (void)selectTab:(ContentSettingsType)settingsType; - (void)showExceptionsForType:(ContentSettingsType)settingsType; // Callback when preferences are changed. |prefName| is the name of the // pref that has changed. - (void)prefChanged:(std::wstring*)prefName; @end namespace ContentSettingsDialogControllerInternal { // A C++ class registered for changes in preferences. class PrefObserverBridge : public NotificationObserver { public: PrefObserverBridge(ContentSettingsDialogController* controller) : controller_(controller), disabled_(false) {} virtual ~PrefObserverBridge() {} virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (!disabled_ && type == NotificationType::PREF_CHANGED) { [controller_ prefChanged:Details(details).ptr()]; } } void SetDisabled(bool disabled) { disabled_ = disabled; } private: ContentSettingsDialogController* controller_; // weak, owns us bool disabled_; // true if notifications should be ignored. }; // A C++ utility class to disable notifications for PrefsObserverBridge. // The intended usage is to create on the stack. class PrefObserverDisabler { public: PrefObserverDisabler(PrefObserverBridge *bridge) : bridge_(bridge) { bridge_->SetDisabled(true); } ~PrefObserverDisabler() { bridge_->SetDisabled(false); } private: PrefObserverBridge *bridge_; }; } // ContentSettingsDialogControllerInternal @implementation ContentSettingsDialogController + (id)showContentSettingsForType:(ContentSettingsType)settingsType profile:(Profile*)profile { profile = profile->GetOriginalProfile(); if (!g_instance) g_instance = [[self alloc] initWithProfile:profile]; // The code doesn't expect multiple profiles. Check that support for that // hasn't been added. DCHECK(g_instance->profile_ == profile); // Select desired tab. if (settingsType == CONTENT_SETTINGS_TYPE_DEFAULT) { // Remember the last visited page from local state. int value = g_instance->lastSelectedTab_.GetValue(); if (value >= 0 && value < CONTENT_SETTINGS_NUM_TYPES) settingsType = static_cast(value); if (settingsType == CONTENT_SETTINGS_TYPE_DEFAULT) settingsType = CONTENT_SETTINGS_TYPE_COOKIES; } // TODO(thakis): Autosave window pos. [g_instance selectTab:settingsType]; [g_instance showWindow:nil]; [g_instance closeExceptionsSheet]; return g_instance; } - (id)initWithProfile:(Profile*)profile { DCHECK(profile); NSString* nibpath = [mac_util::MainAppBundle() pathForResource:@"ContentSettings" ofType:@"nib"]; if ((self = [super initWithWindowNibPath:nibpath owner:self])) { profile_ = profile; observer_.reset( new ContentSettingsDialogControllerInternal::PrefObserverBridge(self)); clearSiteDataOnExit_.Init(prefs::kClearSiteDataOnExit, profile_->GetPrefs(), observer_.get()); // Manually observe notifications for preferences that are grouped in // the HostContentSettingsMap or GeolocationContentSettingsMap. PrefService* prefs = profile_->GetPrefs(); prefs->AddPrefObserver(prefs::kBlockThirdPartyCookies, observer_.get()); prefs->AddPrefObserver(prefs::kDefaultContentSettings, observer_.get()); prefs->AddPrefObserver(prefs::kGeolocationDefaultContentSetting, observer_.get()); // We don't need to observe changes in this value. lastSelectedTab_.Init(prefs::kContentSettingsWindowLastTabIndex, profile_->GetPrefs(), NULL); } return self; } - (void)dealloc { if (profile_) { PrefService* prefs = profile_->GetPrefs(); prefs->RemovePrefObserver(prefs::kBlockThirdPartyCookies, observer_.get()); prefs->RemovePrefObserver(prefs::kDefaultContentSettings, observer_.get()); prefs->RemovePrefObserver(prefs::kGeolocationDefaultContentSetting, observer_.get()); } [super dealloc]; } - (void)closeExceptionsSheet { NSWindow* attachedSheet = [[self window] attachedSheet]; if (attachedSheet) { [NSApp endSheet:attachedSheet]; } } - (void)awakeFromNib { DCHECK([self window]); DCHECK_EQ(self, [[self window] delegate]); // Adapt views to potentially long localized strings. CGFloat windowDelta = 0; for (NSTabViewItem* tab in [tabView_ tabViewItems]) { windowDelta = MAX(windowDelta, VerticallyReflowGroup([[tab view] subviews])); } NSRect frame = [[self window] frame]; frame.origin.y -= windowDelta; frame.size.height += windowDelta; [[self window] setFrame:frame display:NO]; } // NSWindowDelegate method. - (void)windowWillClose:(NSNotification*)notification { [self autorelease]; g_instance = nil; } - (void)selectTab:(ContentSettingsType)settingsType { [self window]; // Make sure the nib file is loaded. DCHECK(tabView_); [tabView_ selectTabViewItemAtIndex:settingsType]; } // NSTabViewDelegate method. - (void) tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem { DCHECK_EQ(tabView_, tabView); NSInteger index = [tabView indexOfTabViewItem:tabViewItem]; DCHECK_GT(index, CONTENT_SETTINGS_TYPE_DEFAULT); DCHECK_LT(index, CONTENT_SETTINGS_NUM_TYPES); if (index > CONTENT_SETTINGS_TYPE_DEFAULT && index < CONTENT_SETTINGS_NUM_TYPES) lastSelectedTab_.SetValue(index); } // Let esc close the window. - (void)cancel:(id)sender { [self close]; } - (void)setCookieSettingIndex:(NSInteger)value { ContentSetting setting = CONTENT_SETTING_DEFAULT; switch (value) { case kCookieEnabledIndex: setting = CONTENT_SETTING_ALLOW; break; case kCookieAskIndex: setting = CONTENT_SETTING_ASK; break; case kCookieDisabledIndex: setting = CONTENT_SETTING_BLOCK; break; default: NOTREACHED(); } ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); profile_->GetHostContentSettingsMap()->SetDefaultContentSetting( CONTENT_SETTINGS_TYPE_COOKIES, setting); } - (NSInteger)cookieSettingIndex { switch (profile_->GetHostContentSettingsMap()->GetDefaultContentSetting( CONTENT_SETTINGS_TYPE_COOKIES)) { case CONTENT_SETTING_ALLOW: return kCookieEnabledIndex; case CONTENT_SETTING_ASK: return kCookieAskIndex; case CONTENT_SETTING_BLOCK: return kCookieDisabledIndex; default: NOTREACHED(); return kCookieEnabledIndex; } } - (BOOL)blockThirdPartyCookies { HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap(); return settingsMap->BlockThirdPartyCookies(); } - (void)setBlockThirdPartyCookies:(BOOL)value { HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap(); ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); settingsMap->SetBlockThirdPartyCookies(value); } - (BOOL)clearSiteDataOnExit { return clearSiteDataOnExit_.GetValue(); } - (void)setClearSiteDataOnExit:(BOOL)value { ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); clearSiteDataOnExit_.SetValue(value); } // Shows the cookies controller. - (IBAction)showCookies:(id)sender { // The cookie controller will autorelease itself when it's closed. BrowsingDataDatabaseHelper* databaseHelper = new BrowsingDataDatabaseHelper(profile_); BrowsingDataLocalStorageHelper* storageHelper = new BrowsingDataLocalStorageHelper(profile_); BrowsingDataAppCacheHelper* appcacheHelper = new BrowsingDataAppCacheHelper(profile_); CookiesWindowController* controller = [[CookiesWindowController alloc] initWithProfile:profile_ databaseHelper:databaseHelper storageHelper:storageHelper appcacheHelper:appcacheHelper]; [controller attachSheetTo:[self window]]; } // Called when the user clicks the "Flash Player storage settings" button. - (IBAction)openFlashPlayerSettings:(id)sender { Browser* browser = Browser::Create(profile_); browser->OpenURL(GURL(l10n_util::GetStringUTF8(IDS_FLASH_STORAGE_URL)), GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); browser->window()->Show(); } // Called when the user clicks the "Disable individual plug-ins..." button. - (IBAction)openPluginsPage:(id)sender { Browser* browser = Browser::Create(profile_); browser->OpenURL(GURL(chrome::kChromeUIPluginsURL), GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); browser->window()->Show(); } - (IBAction)showCookieExceptions:(id)sender { [self showExceptionsForType:CONTENT_SETTINGS_TYPE_COOKIES]; } - (IBAction)showImagesExceptions:(id)sender { [self showExceptionsForType:CONTENT_SETTINGS_TYPE_IMAGES]; } - (IBAction)showJavaScriptExceptions:(id)sender { [self showExceptionsForType:CONTENT_SETTINGS_TYPE_JAVASCRIPT]; } - (IBAction)showPluginsExceptions:(id)sender { [self showExceptionsForType:CONTENT_SETTINGS_TYPE_PLUGINS]; } - (IBAction)showPopupsExceptions:(id)sender { [self showExceptionsForType:CONTENT_SETTINGS_TYPE_POPUPS]; } - (IBAction)showGeolocationExceptions:(id)sender { GeolocationContentSettingsMap* settingsMap = profile_->GetGeolocationContentSettingsMap(); [[GeolocationExceptionsWindowController controllerWithSettingsMap:settingsMap] attachSheetTo:[self window]]; } - (void)showExceptionsForType:(ContentSettingsType)settingsType { HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap(); [[ContentExceptionsWindowController controllerForType:settingsType settingsMap:settingsMap] attachSheetTo:[self window]]; } - (void)setImagesEnabledIndex:(NSInteger)value { ContentSetting setting = value == kContentSettingsEnabledIndex ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); profile_->GetHostContentSettingsMap()->SetDefaultContentSetting( CONTENT_SETTINGS_TYPE_IMAGES, setting); } - (NSInteger)imagesEnabledIndex { HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap(); bool enabled = settingsMap->GetDefaultContentSetting(CONTENT_SETTINGS_TYPE_IMAGES) == CONTENT_SETTING_ALLOW; return enabled ? kContentSettingsEnabledIndex : kContentSettingsDisabledIndex; } - (void)setJavaScriptEnabledIndex:(NSInteger)value { ContentSetting setting = value == kContentSettingsEnabledIndex ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); profile_->GetHostContentSettingsMap()->SetDefaultContentSetting( CONTENT_SETTINGS_TYPE_JAVASCRIPT, setting); } - (NSInteger)javaScriptEnabledIndex { HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap(); bool enabled = settingsMap->GetDefaultContentSetting(CONTENT_SETTINGS_TYPE_JAVASCRIPT) == CONTENT_SETTING_ALLOW; return enabled ? kContentSettingsEnabledIndex : kContentSettingsDisabledIndex; } - (void)setPluginsEnabledIndex:(NSInteger)value { ContentSetting setting = value == kContentSettingsEnabledIndex ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); profile_->GetHostContentSettingsMap()->SetDefaultContentSetting( CONTENT_SETTINGS_TYPE_PLUGINS, setting); } - (NSInteger)pluginsEnabledIndex { HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap(); bool enabled = settingsMap->GetDefaultContentSetting(CONTENT_SETTINGS_TYPE_PLUGINS) == CONTENT_SETTING_ALLOW; return enabled ? kContentSettingsEnabledIndex : kContentSettingsDisabledIndex; } - (void)setPopupsEnabledIndex:(NSInteger)value { ContentSetting setting = value == kContentSettingsEnabledIndex ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); profile_->GetHostContentSettingsMap()->SetDefaultContentSetting( CONTENT_SETTINGS_TYPE_POPUPS, setting); } - (NSInteger)popupsEnabledIndex { HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap(); bool enabled = settingsMap->GetDefaultContentSetting(CONTENT_SETTINGS_TYPE_POPUPS) == CONTENT_SETTING_ALLOW; return enabled ? kContentSettingsEnabledIndex : kContentSettingsDisabledIndex; } - (void)setGeolocationSettingIndex:(NSInteger)value { ContentSetting setting = CONTENT_SETTING_DEFAULT; switch (value) { case kGeolocationEnabledIndex: setting = CONTENT_SETTING_ALLOW; break; case kGeolocationAskIndex: setting = CONTENT_SETTING_ASK; break; case kGeolocationDisabledIndex: setting = CONTENT_SETTING_BLOCK; break; default: NOTREACHED(); } ContentSettingsDialogControllerInternal::PrefObserverDisabler disabler(observer_.get()); profile_->GetGeolocationContentSettingsMap()->SetDefaultContentSetting( setting); } - (NSInteger)geolocationSettingIndex { ContentSetting setting = profile_->GetGeolocationContentSettingsMap()->GetDefaultContentSetting(); switch (setting) { case CONTENT_SETTING_ALLOW: return kGeolocationEnabledIndex; case CONTENT_SETTING_ASK: return kGeolocationAskIndex; case CONTENT_SETTING_BLOCK: return kGeolocationDisabledIndex; default: NOTREACHED(); return kGeolocationAskIndex; } } // Callback when preferences are changed. |prefName| is the name of the // pref that has changed and should not be NULL. - (void)prefChanged:(std::wstring*)prefName { DCHECK(prefName); if (!prefName) return; if (*prefName == prefs::kClearSiteDataOnExit) { [self willChangeValueForKey:@"clearSiteDataOnExit"]; [self didChangeValueForKey:@"clearSiteDataOnExit"]; } if (*prefName == prefs::kBlockThirdPartyCookies) { [self willChangeValueForKey:@"blockThirdPartyCookies"]; [self didChangeValueForKey:@"blockThirdPartyCookies"]; } if (*prefName == prefs::kDefaultContentSettings) { // We don't know exactly which setting has changed, so we'll tickle all // of the properties that apply to kDefaultContentSettings. This will // keep the UI up-to-date. [self willChangeValueForKey:@"cookieSettingIndex"]; [self didChangeValueForKey:@"cookieSettingIndex"]; [self willChangeValueForKey:@"imagesEnabledIndex"]; [self didChangeValueForKey:@"imagesEnabledIndex"]; [self willChangeValueForKey:@"javaScriptEnabledIndex"]; [self didChangeValueForKey:@"javaScriptEnabledIndex"]; [self willChangeValueForKey:@"pluginsEnabledIndex"]; [self didChangeValueForKey:@"pluginsEnabledIndex"]; [self willChangeValueForKey:@"popupsEnabledIndex"]; [self didChangeValueForKey:@"popupsEnabledIndex"]; } if (*prefName == prefs::kGeolocationDefaultContentSetting) { [self willChangeValueForKey:@"geolocationSettingIndex"]; [self didChangeValueForKey:@"geolocationSettingIndex"]; } } @end