summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa
diff options
context:
space:
mode:
authorpinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-06 19:41:37 +0000
committerpinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-06 19:41:37 +0000
commitcd63ef62b897937521c6943b554608b3f9349d27 (patch)
treec79b4ff66eb7d1553095a6389c41f85bca9f2051 /chrome/browser/cocoa
parent8e80744e557dd436dd174b7f7203b4409b90c6e4 (diff)
downloadchromium_src-cd63ef62b897937521c6943b554608b3f9349d27.zip
chromium_src-cd63ef62b897937521c6943b554608b3f9349d27.tar.gz
chromium_src-cd63ef62b897937521c6943b554608b3f9349d27.tar.bz2
Implement most of the "basics" pref panel on Mac, including code to set the default browser. Fix up some related comments around the code.
Review URL: http://codereview.chromium.org/113032 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15445 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r--chrome/browser/cocoa/preferences_window_controller.h47
-rw-r--r--chrome/browser/cocoa/preferences_window_controller.mm372
-rw-r--r--chrome/browser/cocoa/preferences_window_controller_unittest.mm10
3 files changed, 414 insertions, 15 deletions
diff --git a/chrome/browser/cocoa/preferences_window_controller.h b/chrome/browser/cocoa/preferences_window_controller.h
index 1b1fd28..1f24b75 100644
--- a/chrome/browser/cocoa/preferences_window_controller.h
+++ b/chrome/browser/cocoa/preferences_window_controller.h
@@ -4,19 +4,56 @@
#import <Cocoa/Cocoa.h>
+#include "base/scoped_ptr.h"
+#include "base/scoped_nsobject.h"
+#include "chrome/common/pref_member.h"
+
+class PrefObserverBridge;
class PrefService;
+class Profile;
+@class StartupURLDataSource;
-// A window controller that handles the preferences window.
+// A window controller that handles the preferences window. The bulk of the
+// work is handled via Cocoa Bindings and getter/setter methods that wrap
+// cross-platform PrefMember objects. When prefs change in the back-end
+// (that is, outside of this UI), our observer recieves a notification and can
+// tickle the KVO to update the UI so we are always in sync. The bindings are
+// specified in the nib file. Preferences are persisted into the back-end
+// as they are changed in the UI, and are thus immediately available even while
+// the window is still open. When the window closes, a notification is sent
+// via the system NotificationCenter. This can be used as a signal to
+// release this controller, as it's likely the client wants to enforce there
+// only being one (we don't do that internally as it makes it very difficult
+// to unit test).
@interface PreferencesWindowController : NSWindowController {
@private
- PrefService* prefs_; // weak ref
+ Profile* profile_; // weak ref
+ PrefService* prefs_; // weak ref - Obtained from profile_ for convenience.
+ scoped_ptr<PrefObserverBridge> observer_; // Watches for pref changes.
+
+ // Basics panel
+ IntegerPrefMember restoreOnStartup_;
+ scoped_nsobject<StartupURLDataSource> customPagesSource_;
+ BooleanPrefMember newTabPageIsHomePage_;
+ StringPrefMember homepage_;
+ BooleanPrefMember showHomeButton_;
+ BooleanPrefMember showPageOptionButtons_;
+
+ // Minor Tweaks panel
+
+ // Under the hood panel
}
-// Designated initializer. |prefs| should not be NULL.
-- (id)initWithPrefs:(PrefService*)prefs;
+// Designated initializer. |profile| should not be NULL.
+- (id)initWithProfile:(Profile*)profile;
// Show the preferences window.
-- (IBAction)showPreferences:(id)sender;
+- (void)showPreferences:(id)sender;
+
+// IBAction methods for responding to user actions.
+
+// Basics panel
+- (IBAction)makeDefaultBrowser:(id)sender;
@end
diff --git a/chrome/browser/cocoa/preferences_window_controller.mm b/chrome/browser/cocoa/preferences_window_controller.mm
index 632e9cc..8f079e6 100644
--- a/chrome/browser/cocoa/preferences_window_controller.mm
+++ b/chrome/browser/cocoa/preferences_window_controller.mm
@@ -5,38 +5,396 @@
#import "chrome/browser/cocoa/preferences_window_controller.h"
#include "base/mac_util.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/net/url_fixer_upper.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/session_startup_pref.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
+#include "chrome/common/url_constants.h"
NSString* const kUserDoneEditingPrefsNotification =
@"kUserDoneEditingPrefsNotification";
+namespace {
+std::wstring GetNewTabUIURLString() {
+ return UTF8ToWide(chrome::kChromeUINewTabURL);
+}
+} // namespace
+
+// A data source object for the "startup urls" table.
+// TODO(pinkerton): hook this up to bindings.
+@interface StartupURLDataSource : NSObject {
+ @private
+ Profile* profile_; // weak, used to load icons
+}
+- (id)initWithProfile:(Profile*)profile;
+@end
+
+@implementation StartupURLDataSource
+- (id)initWithProfile:(Profile*)profile {
+ if ((self = [super init])) {
+ profile_ = profile;
+ }
+ return self;
+}
+@end
+
+//-------------------------------------------------------------------------
+
+@interface PreferencesWindowController(Private)
+// Callback when preferences are changed. |prefName| is the name of the
+// pref that has changed, or |NULL| if all prefs should be updated.
+- (void)prefChanged:(std::wstring*)prefName;
+// Record the user performed a certain action and save the preferences.
+- (void)recordUserAction:(const wchar_t*)action;
+- (void)registerPrefObservers;
+- (void)unregisterPrefObservers;
+
+// KVC setter methods.
+- (void)setNewTabPageIsHomePage:(NSInteger)val;
+- (void)setHomepageURL:(NSString*)urlString;
+- (void)setRestoreOnStartupIndex:(NSInteger)type;
+- (void)setShowHomeButton:(BOOL)value;
+- (void)setShowPageOptionsButtons:(BOOL)value;
+- (void)setDefaultBrowser:(BOOL)value;
+@end
+
+// A C++ class registered for changes in preferences. Bridges the
+// notification back to the PWC.
+class PrefObserverBridge : public NotificationObserver {
+ public:
+ PrefObserverBridge(PreferencesWindowController* controller)
+ : controller_(controller) { }
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::PREF_CHANGED)
+ [controller_ prefChanged:Details<std::wstring>(details).ptr()];
+ }
+ private:
+ PreferencesWindowController* controller_; // weak, owns us
+};
+
@implementation PreferencesWindowController
-- (id)initWithPrefs:(PrefService*)prefs {
- DCHECK(prefs);
+- (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:@"Preferences"
ofType:@"nib"];
if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
- prefs_ = prefs;
+ profile_ = profile;
+ prefs_ = profile->GetPrefs();
+ DCHECK(prefs_);
+ observer_.reset(new PrefObserverBridge(self));
+ customPagesSource_.reset([[StartupURLDataSource alloc]
+ initWithProfile:profile_]);
+ // This needs to be done before awakeFromNib: because the bindings set up
+ // in the nib rely on it.
+ [self registerPrefObservers];
}
return self;
}
- (void)awakeFromNib {
+ // TODO(pinkerton): save/restore size based on prefs.
+ [[self window] center];
+
+ // TODO(pinkerton): Ensure the "basics" tab is selected.
+}
+
+- (void)dealloc {
+ [self unregisterPrefObservers];
+ [super dealloc];
+}
+
+// Register our interest in the preferences we're displaying so if anything
+// else in the UI changes them we will be updated.
+- (void)registerPrefObservers {
+ if (!prefs_) return;
+
+ // Basics panel
+ prefs_->AddPrefObserver(prefs::kURLsToRestoreOnStartup, observer_.get());
+ restoreOnStartup_.Init(prefs::kRestoreOnStartup, prefs_, observer_.get());
+ newTabPageIsHomePage_.Init(prefs::kHomePageIsNewTabPage,
+ prefs_, observer_.get());
+ homepage_.Init(prefs::kHomePage, prefs_, observer_.get());
+ showHomeButton_.Init(prefs::kShowHomeButton, prefs_, observer_.get());
+ showPageOptionButtons_.Init(prefs::kShowPageOptionsButtons, prefs_,
+ observer_.get());
+ // TODO(pinkerton): Register Default search.
+
+ // TODO(pinkerton): do other panels...
+}
+
+// Clean up what was registered in -registerPrefObservers. We only have to
+// clean up the non-PrefMember registrations.
+- (void)unregisterPrefObservers {
+ if (!prefs_) return;
+
+ // Basics
+ prefs_->RemovePrefObserver(prefs::kURLsToRestoreOnStartup, observer_.get());
+
+ // TODO(pinkerton): do other panels...
+}
+
+// Record the user performed a certain action and save the preferences.
+- (void)recordUserAction:(const wchar_t*)action {
+ UserMetrics::RecordComputedAction(action, profile_);
+ if (prefs_)
+ prefs_->ScheduleSavePersistentPrefs();
+}
+
+// Returns the set of keys that |key| depends on for its value so it can be
+// re-computed when any of those change as well.
++ (NSSet*)keyPathsForValuesAffectingValueForKey:(NSString*)key {
+ NSSet* paths = [super keyPathsForValuesAffectingValueForKey:key];
+ if ([key isEqualToString:@"isHomepageURLEnabled"]) {
+ paths = [paths setByAddingObject:@"newTabPageIsHomePageIndex"];
+ } else if ([key isEqualToString:@"enableRestoreButtons"]) {
+ paths = [paths setByAddingObject:@"restoreOnStartupIndex"];
+ } else if ([key isEqualToString:@"isDefaultBrowser"]) {
+ paths = [paths setByAddingObject:@"isDefaultBrowser"];
+ }
+ return paths;
+}
+
+// Called when the user clicks the button to make Chromium the default
+// browser. Registers http and https.
+- (IBAction)makeDefaultBrowser:(id)sender {
+ ShellIntegration::SetAsDefaultBrowser();
+
+ // Tickle KVO so that the UI updates.
+ [self setDefaultBrowser:YES];
+}
+
+// A stub setter so that we can trick KVO into thinking the UI needs
+// to be updated.
+- (void)setDefaultBrowser:(BOOL)ignore {
+ // Do nothing.
+}
+
+// Returns if Chromium is the default browser.
+- (BOOL)isDefaultBrowser {
+ return ShellIntegration::IsDefaultBrowser() ? YES : NO;
+}
+
+//-------------------------------------------------------------------------
+// Basics panel
+
+// Sets the home page preferences for kNewTabPageIsHomePage and kHomePage. If a
+// blank string is passed in we revert to using NewTab page as the Home page.
+// When setting the Home Page to NewTab page, we preserve the old value of
+// kHomePage (we don't overwrite it). Note: using SetValue() causes the
+// observers not to fire, which is actually a good thing as we could end up in a
+// state where setting the homepage to an empty url would automatically reset
+// the prefs back to using the NTP, so we'd be never be able to change it.
+- (void)setHomepage:(const std::wstring&)homepage {
+ if (homepage.empty() || homepage == GetNewTabUIURLString()) {
+ newTabPageIsHomePage_.SetValue(true);
+ } else {
+ newTabPageIsHomePage_.SetValue(false);
+ homepage_.SetValue(homepage);
+ }
+}
+
+// Callback when preferences are changed by someone modifying the prefs backend
+// externally. |prefName| is the name of the pref that has changed. Unlike on
+// Windows, we don't need to use this method for initializing, that's handled by
+// Cocoa Bindings.
+// Handles prefs for the "Basics" panel.
+- (void)basicsPrefChanged:(std::wstring*)prefName {
+ if (*prefName == prefs::kRestoreOnStartup) {
+ const SessionStartupPref startupPref =
+ SessionStartupPref::GetStartupPref(prefs_);
+ [self setRestoreOnStartupIndex:startupPref.type];
+ }
+
+ // TODO(beng): Note that the kURLsToRestoreOnStartup pref is a mutable list,
+ // and changes to mutable lists aren't broadcast through the
+ // observer system, so the second half of this condition will
+ // never match. Once support for broadcasting such updates is
+ // added, this will automagically start to work, and this comment
+ // can be removed.
+ if (*prefName == prefs::kURLsToRestoreOnStartup) {
+ const SessionStartupPref startupPref =
+ SessionStartupPref::GetStartupPref(prefs_);
+ // Set table model.
+ NOTIMPLEMENTED();
+ }
+
+ if (*prefName == prefs::kHomePageIsNewTabPage) {
+ NSInteger useNewTabPage = newTabPageIsHomePage_.GetValue() ? 0 : 1;
+ [self setNewTabPageIsHomePage:useNewTabPage];
+ }
+ if (*prefName == prefs::kHomePage) {
+ NSString* value = base::SysWideToNSString(homepage_.GetValue());
+ [self setHomepageURL:value];
+ }
+
+ if (*prefName == prefs::kShowHomeButton) {
+ [self setShowHomeButton:showHomeButton_.GetValue() ? YES : NO];
+ }
+ if (*prefName == prefs::kShowPageOptionsButtons) {
+ [self setShowPageOptionsButtons:showPageOptionButtons_.GetValue() ?
+ YES : NO];
+ }
+}
+
+// Returns the index of the selected cell in the "on startup" matrix based
+// on the "restore on startup" pref. The ordering of the cells is in the
+// same order as the pref.
+- (NSInteger)restoreOnStartupIndex {
+ const SessionStartupPref startupPref =
+ SessionStartupPref::GetStartupPref(prefs_);
+ return startupPref.type;
+}
+
+// Sets the pref based on the index of the selected cell in the matrix and
+// marks the appropriate user metric.
+- (void)setRestoreOnStartupIndex:(NSInteger)type {
+ SessionStartupPref pref;
+ pref.type = static_cast<SessionStartupPref::Type>(type);
+ // TODO(pinkerton): list of pages in |pref.urls|
+ switch (pref.type) {
+ case SessionStartupPref::DEFAULT:
+ [self recordUserAction:L"Options_Startup_Homepage"];
+ break;
+ case SessionStartupPref::LAST:
+ [self recordUserAction:L"Options_Startup_LastSession"];
+ break;
+ case SessionStartupPref::URLS:
+ [self recordUserAction:L"Options_Startup_Custom"];
+ break;
+ default:
+ NOTREACHED();
+ }
+ SessionStartupPref::SetStartupPref(prefs_, pref);
+}
+
+// Returns whether or not the +/-/Current buttons should be enabled, based on
+// the current pref value for the startup urls.
+- (BOOL)enableRestoreButtons {
+ return [self restoreOnStartupIndex] == SessionStartupPref::URLS;
+}
+
+enum { kHomepageNewTabPage, kHomepageURL };
+
+// Returns the index of the selected cell in the "home page" marix based on
+// the "new tab is home page" pref. Sadly, the ordering is reversed from the
+// pref value.
+- (NSInteger)newTabPageIsHomePageIndex {
+ return newTabPageIsHomePage_.GetValue() ?
+ kHomepageNewTabPage : kHomepageURL;
+}
+// Sets the pref based on the given index into the matrix and marks the
+// appropriate user metric.
+- (void)setNewTabPageIsHomePageIndex:(NSInteger)index {
+ bool useNewTabPage = index == kHomepageNewTabPage ? true : false;
+ if (useNewTabPage)
+ [self recordUserAction:L"Options_Homepage_UseNewTab"];
+ else
+ [self recordUserAction:L"Options_Homepage_UseURL"];
+ newTabPageIsHomePage_.SetValue(useNewTabPage);
}
-// Synchronizes the window's UI elements with the values in |prefs_|.
-- (void)syncWithPrefs {
- // TODO(pinkerton): do it...
+// Returns whether or not the homepage URL text field should be enabled
+// based on if the new tab page is the home page.
+- (BOOL)isHomepageURLEnabled {
+ return newTabPageIsHomePage_.GetValue() ? NO : YES;
+}
+
+// Returns the homepage URL.
+- (NSString*)homepageURL {
+ NSString* value = base::SysWideToNSString(homepage_.GetValue());
+ return value;
+}
+
+// Sets the homepage URL to |urlString| with some fixing up.
+- (void)setHomepageURL:(NSString*)urlString {
+ // If the text field contains a valid URL, sync it to prefs. We run it
+ // through the fixer upper to allow input like "google.com" to be converted
+ // to something valid ("http://google.com").
+ std::wstring temp = base::SysNSStringToWide(urlString);
+ std::wstring fixedString = URLFixerUpper::FixupURL(temp, std::wstring());
+ if (GURL(WideToUTF8(fixedString)).is_valid())
+ [self setHomepage:fixedString];
+}
+
+// Returns whether the home button should be checked based on the preference.
+- (BOOL)showHomeButton {
+ return showHomeButton_.GetValue() ? YES : NO;
+}
+
+// Sets the backend pref for whether or not the home button should be displayed
+// based on |value|.
+- (void)setShowHomeButton:(BOOL)value {
+ if (value)
+ [self recordUserAction:L"Options_Homepage_ShowHomeButton"];
+ else
+ [self recordUserAction:L"Options_Homepage_HideHomeButton"];
+ showHomeButton_.SetValue(value ? true : false);
+}
+
+// Returns whether the page and options button should be checked based on the
+// preference.
+- (BOOL)showPageOptionsButtons {
+ return showPageOptionButtons_.GetValue() ? YES : NO;
+}
+
+// Sets the backend pref for whether or not the page and options buttons should
+// be displayed based on |value|.
+- (void)setShowPageOptionsButtons:(BOOL)value {
+ if (value)
+ [self recordUserAction:L"Options_Homepage_ShowPageOptionsButtons"];
+ else
+ [self recordUserAction:L"Options_Homepage_HidePageOptionsButtons"];
+ showPageOptionButtons_.SetValue(value ? true : false);
+}
+
+//-------------------------------------------------------------------------
+// Minor Tweaks panel
+
+// Callback when preferences are changed. |prefName| is the name of the
+// pref that has changed, or |NULL| if all prefs should be updated.
+// Handles prefs for the "Minor Tweaks" panel.
+- (void)minorTweaksPrefChanged:(std::wstring*)prefName {
+}
+
+//-------------------------------------------------------------------------
+// Under the hood panel
+
+// Callback when preferences are changed. |prefName| is the name of the
+// pref that has changed, or |NULL| if all prefs should be updated.
+// Handles prefs for the "Under the hood" panel.
+- (void)underHoodPrefChanged:(std::wstring*)prefName {
+}
+
+//-------------------------------------------------------------------------
+
+// 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;
+ [self basicsPrefChanged:prefName];
+ [self minorTweaksPrefChanged:prefName];
+ [self underHoodPrefChanged:prefName];
}
// Show the preferences window.
- (IBAction)showPreferences:(id)sender {
- [self syncWithPrefs];
[self showWindow:sender];
}
diff --git a/chrome/browser/cocoa/preferences_window_controller_unittest.mm b/chrome/browser/cocoa/preferences_window_controller_unittest.mm
index 33bc12b..e276bf2 100644
--- a/chrome/browser/cocoa/preferences_window_controller_unittest.mm
+++ b/chrome/browser/cocoa/preferences_window_controller_unittest.mm
@@ -8,6 +8,7 @@
#import "chrome/browser/cocoa/preferences_window_controller.h"
#include "chrome/browser/cocoa/browser_test_helper.h"
#include "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "chrome/common/pref_names.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
@@ -31,9 +32,13 @@ namespace {
class PrefsControllerTest : public PlatformTest {
public:
PrefsControllerTest() {
+ // Since this is a platform-specific preference, it's not registered by
+ // any of the shared code. We have to register it ourselves.
PrefService* prefs = browser_helper_.profile()->GetPrefs();
+ prefs->RegisterBooleanPref(prefs::kShowPageOptionsButtons, false);
+
pref_controller_.reset([[PreferencesWindowController alloc]
- initWithPrefs:prefs]);
+ initWithProfile:browser_helper_.profile()]);
EXPECT_TRUE(pref_controller_.get());
}
@@ -46,8 +51,7 @@ class PrefsControllerTest : public PlatformTest {
// making sure we get the notification when it's closed.
TEST_F(PrefsControllerTest, ShowAndClose) {
#if 0
-// TODO(pinkerton): this works locally, but fails on the buildbot. Need to
-// investigate.
+// TODO(pinkerton): this crashes deep w/in performClose:. Need to investigate.
[pref_controller_ showPreferences:nil];
EXPECT_TRUE([[pref_controller_ window] isVisible]);