diff options
author | jeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-21 21:49:05 +0000 |
---|---|---|
committer | jeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-21 21:49:05 +0000 |
commit | 1d8e4ce0743eed37a143d99e568c28abbe3202f1 (patch) | |
tree | ce8c850b2fa6d8a87f419255c368817c02f23a4f /chrome/browser | |
parent | f59a9c06d5cc9b7e0faace2b76582d3a12c3ec6c (diff) | |
download | chromium_src-1d8e4ce0743eed37a143d99e568c28abbe3202f1.zip chromium_src-1d8e4ce0743eed37a143d99e568c28abbe3202f1.tar.gz chromium_src-1d8e4ce0743eed37a143d99e568c28abbe3202f1.tar.bz2 |
Implement temporary First Run Dialog on OS X
We use a modal dialog with a single checkbox on OS X.
We use the OSX defaults system since we want something quick and reliable. The
dialog is displayed at a very early stage in Chrome startup (Before any
subsystems start relying on the stats variable). This means there are a few
quirks in displaying the UI.
A change was also needed to our event handling code since when the dialog is
shown we spin an event loop at a very early stage in the process lifetime.
Changed default value for stats to false and updated unit tests to reflect that.
Also some misc. minor cleanup.
BUG=11971,12046
Review URL: http://codereview.chromium.org/115608
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16669 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/browser_main.cc | 19 | ||||
-rw-r--r-- | chrome/browser/cocoa/first_run_dialog.h | 34 | ||||
-rw-r--r-- | chrome/browser/cocoa/first_run_dialog.mm | 52 | ||||
-rw-r--r-- | chrome/browser/cocoa/shell_dialogs_mac.mm | 2 | ||||
-rw-r--r-- | chrome/browser/first_run_mac.mm | 54 | ||||
-rw-r--r-- | chrome/browser/google_update_settings_mac.mm | 12 | ||||
-rw-r--r-- | chrome/browser/google_update_settings_mac_unittest.mm | 6 |
7 files changed, 171 insertions, 8 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 0f023c3..78ae72d 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -327,6 +327,18 @@ int BrowserMain(const MainFunctionParams& parameters) { local_state->RegisterStringPref(prefs::kApplicationLocale, L""); local_state->RegisterBooleanPref(prefs::kMetricsReportingEnabled, false); +#if defined(OS_POSIX) + // On POSIX we display the first run dialog as early as possible, so we can + // get the stats enabled. + if (is_first_run && !first_run_ui_bypass) { + // Dummy value, we don't need the profile for the OS X version of this + // method at present. + Profile* profile = NULL; + OpenFirstRunDialog(profile, &process_singleton); + } +#endif // OS_POSIX + + // During first run we read the google_update registry key to find what // language the user selected when downloading the installer. This // becomes our default language in the prefs. @@ -511,6 +523,12 @@ int BrowserMain(const MainFunctionParams& parameters) { process_singleton.Create(); + // TODO: This block of code should probably be used on all platforms! + // On POSIX we need to display this dialog before setting the value of + // kMetricsReportingEnabled, so we display this dialog much earlier. + // On Windows a download is tagged with stats enabled/disabled so the UI + // can be displayed later in the startup process. +#if defined(OS_WIN) // Show the First Run UI if this is the first time Chrome has been run on // this computer, or we're being compelled to do so by a command line flag. // Note that this be done _after_ the PrefService is initialized and all @@ -519,6 +537,7 @@ int BrowserMain(const MainFunctionParams& parameters) { if (is_first_run && !first_run_ui_bypass) { OpenFirstRunDialog(profile, &process_singleton); } +#endif // OS_WIN // Sets things up so that if we crash from this point on, a dialog will // popup asking the user to restart chrome. It is done this late to avoid diff --git a/chrome/browser/cocoa/first_run_dialog.h b/chrome/browser/cocoa/first_run_dialog.h new file mode 100644 index 0000000..6d0f53a --- /dev/null +++ b/chrome/browser/cocoa/first_run_dialog.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_FIRST_RUN_DIALOG_H_ +#define CHROME_BROWSER_FIRST_RUN_DIALOG_H_ + +#import <Cocoa/Cocoa.h> + +// Class that acts as a controller for the temporary Modal first run dialog. +// The dialog asks the user's explicit permission for reporting stats to help +// us improve Chromium. +// This code is temporary and while we'd like to avoid modal dialogs at all +// costs, it's important to us, even at this early stage in development to +// to not send any stats back unless the user has explicitly consented. +// TODO: In the final version of this code, if we keep this class around, we +// should probably subclass from NSWindowController. +@interface FirstRunDialogController : NSObject { + IBOutlet NSWindow* first_run_dialog_; + bool stats_enabled_; +} + +// One shot method to show the dialog and return the value of the stats +// enabled/disabled checkbox. +// returns: +// true - stats enabled. +// flase - stats disabled. +- (bool)Show; + +// Called when the OK button is pressed. +- (IBAction)CloseDialog:(id)sender; +@end + +#endif // CHROME_BROWSER_FIRST_RUN_DIALOG_H_ diff --git a/chrome/browser/cocoa/first_run_dialog.mm b/chrome/browser/cocoa/first_run_dialog.mm new file mode 100644 index 0000000..cb4e0c9 --- /dev/null +++ b/chrome/browser/cocoa/first_run_dialog.mm @@ -0,0 +1,52 @@ +// Copyright (c) 2009 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/first_run_dialog.h" + +#include "base/logging.h" +#include "base/mac_util.h" +#import "base/scoped_nsobject.h" + +@implementation FirstRunDialogController + +-(id)init { + self = [super init]; + if (self != nil) { + // Bound to the dialog checkbox, default to true. + stats_enabled_ = true; + } + return self; +} + +- (bool)Show { + // Load and instantiate our NIB + scoped_nsobject<NSNib> nib([[NSNib alloc] + initWithNibNamed:@"FirstRunDialog" + bundle:mac_util::MainAppBundle()]); + CHECK(nib); + [nib.get() instantiateNibWithOwner:self topLevelObjects:nil]; + CHECK(first_run_dialog_); // Should be set by above call. + + // Neat weirdness in the below code - the Application menu stays enabled + // while the window is open but selecting items from it (e.g. Quit) has + // no effect. I'm guessing that this is an artifact of us being a + // background-only application at this stage and displaying a modal + // window. + + // Display dialog. + [NSApp runModalForWindow:first_run_dialog_]; + // First run dialog has "release on close" disabled, otherwise the + // runModalForWindow call above crashes. + [first_run_dialog_ release]; + first_run_dialog_ = nil; + + return stats_enabled_; +} + +- (IBAction)CloseDialog:(id)sender { + [first_run_dialog_ close]; + [NSApp stopModal]; +} + +@end diff --git a/chrome/browser/cocoa/shell_dialogs_mac.mm b/chrome/browser/cocoa/shell_dialogs_mac.mm index 2c5a528..d439a5a 100644 --- a/chrome/browser/cocoa/shell_dialogs_mac.mm +++ b/chrome/browser/cocoa/shell_dialogs_mac.mm @@ -11,7 +11,7 @@ #include "base/logging.h" #include "base/scoped_cftyperef.h" -#include "base/scoped_nsobject.h" +#import "base/scoped_nsobject.h" #include "base/sys_string_conversions.h" static const int kFileTypePopupTag = 1234; diff --git a/chrome/browser/first_run_mac.mm b/chrome/browser/first_run_mac.mm new file mode 100644 index 0000000..41bc9a5 --- /dev/null +++ b/chrome/browser/first_run_mac.mm @@ -0,0 +1,54 @@ +// Copyright (c) 2009 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. + +#include "chrome/browser/first_run.h" + +#import "base/scoped_nsobject.h" +#include "base/sys_string_conversions.h" +#import "chrome/app/breakpad_mac.h" +#import "chrome/browser/cocoa/first_run_dialog.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/google_update_settings.h" + +// static +bool FirstRun::IsChromeFirstRun() { + // Use presence of kRegUsageStatsField key as an indicator of whether or not + // this is the first run. + // See chrome/browser/google_update_settings_mac.mm for details on why we use + // the defualts dictionary here. + NSUserDefaults* std_defaults = [NSUserDefaults standardUserDefaults]; + NSDictionary* defaults_dict = [std_defaults dictionaryRepresentation]; + NSString* collect_stats_key = base::SysWideToNSString( + google_update::kRegUsageStatsField); + + bool not_in_dict = [defaults_dict objectForKey:collect_stats_key] == nil; + return not_in_dict; +} + +void OpenFirstRunDialog(Profile* profile, ProcessSingleton* process_singleton) { +// OpenFirstRunDialog is a no-op on non-branded builds. +#if defined(GOOGLE_CHROME_BUILD) + // Breakpad should not be enabled on first run until the user has explicitly + // opted-into stats. + // TODO: The behavior we probably want here is to enable Breakpad on first run + // but display a confirmation dialog before sending a crash report so we + // respect a user's privacy while still getting any crashes that might happen + // before this point. Then remove the need for that dialog here. + DCHECK(!IsCrashReporterEnabled()); + + scoped_nsobject<FirstRunDialogController> dialog( + [[FirstRunDialogController alloc] init]); + + bool stats_enabled = [dialog.get() Show]; + + GoogleUpdateSettings::SetCollectStatsConsent(stats_enabled); + + // Breakpad is normally enabled very early in the startup process, + // however, on the first run it's off by default. If the user opts-in to + // stats, enable breakpad. + if (stats_enabled) { + InitCrashReporter(); + } +#endif // defined(GOOGLE_CHROME_BUILD) +} diff --git a/chrome/browser/google_update_settings_mac.mm b/chrome/browser/google_update_settings_mac.mm index a246803..95ca90e 100644 --- a/chrome/browser/google_update_settings_mac.mm +++ b/chrome/browser/google_update_settings_mac.mm @@ -23,14 +23,14 @@ namespace google_update { const wchar_t kRegUsageStatsField[] = L"usagestats"; // Declared in a public namespace for testing purposes. -// If pref not set, assume true. +// If pref not set, assume false. bool GetCollectStatsConsentFromDictionary(NSDictionary* dict) { NSString* collect_stats_key = base::SysWideToNSString( google_update::kRegUsageStatsField); NSNumber* val = [dict objectForKey:collect_stats_key]; if (![val respondsToSelector:@selector(boolValue)]) { - return true; + return false; } return ([val boolValue] == YES); @@ -52,8 +52,12 @@ bool GoogleUpdateSettings::GetCollectStatsConsent() { // static bool GoogleUpdateSettings::SetCollectStatsConsent(bool consented) { - NOTIMPLEMENTED(); - return false; + NSString* collect_stats_key = base::SysWideToNSString( + google_update::kRegUsageStatsField); + NSUserDefaults* std_defaults = [NSUserDefaults standardUserDefaults]; + BOOL val_to_store = consented ? YES : NO; + [std_defaults setBool:val_to_store forKey:collect_stats_key]; + return [std_defaults synchronize]; } // static diff --git a/chrome/browser/google_update_settings_mac_unittest.mm b/chrome/browser/google_update_settings_mac_unittest.mm index 89bd8f2..d24c843 100644 --- a/chrome/browser/google_update_settings_mac_unittest.mm +++ b/chrome/browser/google_update_settings_mac_unittest.mm @@ -24,9 +24,9 @@ class GoogleUpdateTest : public PlatformTest { TEST_F(GoogleUpdateTest, StatsConstent) { using google_update::GetCollectStatsConsentFromDictionary; - // Stats are on by default. + // Stats are off by default. NSDictionary* empty_dict = [NSDictionary dictionary]; - ASSERT_TRUE(GetCollectStatsConsentFromDictionary(empty_dict)); + ASSERT_FALSE(GetCollectStatsConsentFromDictionary(empty_dict)); NSString* collect_stats_key = base::SysWideToNSString( google_update::kRegUsageStatsField); @@ -49,5 +49,5 @@ TEST_F(GoogleUpdateTest, StatsConstent) { NSDictionary* wrong_type_dict = [NSDictionary dictionaryWithObject:empty_dict forKey:collect_stats_key]; - ASSERT_TRUE(GetCollectStatsConsentFromDictionary(wrong_type_dict)); + ASSERT_FALSE(GetCollectStatsConsentFromDictionary(wrong_type_dict)); } |