summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorjeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-21 21:49:05 +0000
committerjeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-21 21:49:05 +0000
commit1d8e4ce0743eed37a143d99e568c28abbe3202f1 (patch)
treece8c850b2fa6d8a87f419255c368817c02f23a4f /chrome/browser
parentf59a9c06d5cc9b7e0faace2b76582d3a12c3ec6c (diff)
downloadchromium_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.cc19
-rw-r--r--chrome/browser/cocoa/first_run_dialog.h34
-rw-r--r--chrome/browser/cocoa/first_run_dialog.mm52
-rw-r--r--chrome/browser/cocoa/shell_dialogs_mac.mm2
-rw-r--r--chrome/browser/first_run_mac.mm54
-rw-r--r--chrome/browser/google_update_settings_mac.mm12
-rw-r--r--chrome/browser/google_update_settings_mac_unittest.mm6
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));
}