// 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" #import "chrome/browser/cocoa/import_progress_dialog.h" #include "chrome/browser/importer/importer.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/shell_integration.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/google_update_settings.h" //------------------ Start Temporary Code --------------------- // The Mac version used to store first run in the user defaults, this has // now been moved to the profile directory like other platforms. // These functions are here to use for migration, they should be removed // in the near future once most people are upgraded. // This should be removed after 2 dev release cycles following the checkin // of this code, or by 15-Sept-2009. Whichever comes first. namespace old_first_run_mac { const NSString *kOldUsageStatsPrefName = @"usagestats"; // returns true if the first run sentinel is present in the dictionary // false if no sentinel is present. // |usage_stats_enabled| - Where the usage stats previously enabled? bool IsOldChromeFirstRunFromDictionary(NSDictionary *dict, bool *usage_stats_enabled) { *usage_stats_enabled = false; // Use presence of kOldUsageStatsPrefName key as an indicator of whether or // not this is the first run. NSNumber* val = [dict objectForKey:kOldUsageStatsPrefName]; if (val == nil) { return false; } if ([val respondsToSelector:@selector(boolValue)]) { *usage_stats_enabled = [val boolValue] ? true : false; } return true; } bool IsOldChromeFirstRun(bool *usage_stats_enabled) { NSUserDefaults* std_defaults = [NSUserDefaults standardUserDefaults]; NSDictionary* defaults_dict = [std_defaults dictionaryRepresentation]; return IsOldChromeFirstRunFromDictionary(defaults_dict, usage_stats_enabled); } // Remove the old first run key from the defaults dictionary. void RemoveOldFirstRunDefaultsKey() { NSUserDefaults* std_defaults = [NSUserDefaults standardUserDefaults]; [std_defaults removeObjectForKey:kOldUsageStatsPrefName]; [std_defaults synchronize]; } // returns: // true - If old first run sentinel found and migration was performed. // false - no previous first run sentinel found. bool MigrateOldFirstRun() { bool usage_stats_enabled = false; if (!IsOldChromeFirstRun(&usage_stats_enabled)) return false; FirstRun::CreateSentinel(); GoogleUpdateSettings::SetCollectStatsConsent(usage_stats_enabled); // Migrate old first run data. #if defined(GOOGLE_CHROME_BUILD) // 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 (usage_stats_enabled) { InitCrashReporter(); InitCrashProcessInfo(); } #endif // GOOGLE_CHROME_BUILD RemoveOldFirstRunDefaultsKey(); return true; } } // namespace old_first_run_mac //------------------ End Temporary Code --------------------- // Class that handles conducting the first run operation. // FirstRunController deletes itself when the first run operation ends. class FirstRunController : public ImportObserver { public: explicit FirstRunController(); virtual ~FirstRunController() {} // Overridden methods from ImportObserver. virtual void ImportCanceled() { FirstRunDone(); } virtual void ImportComplete() { FirstRunDone(); } // Display first run UI, start the import and return when it's all over. bool DoFirstRun(Profile* profile, ProcessSingleton* process_singleton); private: // This method closes the first run window and quits the message loop so that // the Chrome startup can continue. This should be called when all the // first run tasks are done. void FirstRunDone(); scoped_refptr importer_host_; DISALLOW_COPY_AND_ASSIGN(FirstRunController); }; bool OpenFirstRunDialog(Profile* profile, bool homepage_defined, ProcessSingleton* process_singleton) { FirstRunController* controller = new FirstRunController; return controller->DoFirstRun(profile, process_singleton); } FirstRunController::FirstRunController() : importer_host_(new ImporterHost) { } void FirstRunController::FirstRunDone() { // Set preference to show first run bubble and welcome page. // TODO(jeremy): Implement // FirstRun::SetShowFirstRunBubblePref(); // FirstRun::SetShowWelcomePagePref(); delete this; } bool FirstRunController::DoFirstRun(Profile* profile, ProcessSingleton* process_singleton) { // This object is responsible for deleting itself, make sure that happens. scoped_ptr gc(this); // 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()); //------------------ Start Temporary Code --------------------- // Migrate old first run format. if (old_first_run_mac::MigrateOldFirstRun()) { return true; } //------------------ End Temporary Code --------------------- scoped_nsobject dialog( [[FirstRunDialogController alloc] init]); // Set list of browsers we know how to import. ssize_t profiles_count = importer_host_->GetAvailableProfileCount(); // TODO(jeremy): Test on newly created account. // TODO(jeremy): Correctly handle case where no browsers to import from // are detected. NSMutableArray *browsers = [NSMutableArray arrayWithCapacity:profiles_count]; for (int i = 0; i < profiles_count; ++i) { std::wstring profile = importer_host_->GetSourceProfileNameAt(i); [browsers addObject:base::SysWideToNSString(profile)]; } [dialog.get() setBrowserImportList:browsers]; // FirstRunDialogController will call exit if "Cancel" is clicked. [dialog.get() showWindow:nil]; // If user clicked cancel, bail - browser_main will return if we haven't // turned off the first run flag when this function returns. if ([dialog.get() userDidCancel]) { return false; } // Don't enable stats in Chromium. bool stats_enabled = false; #if defined(GOOGLE_CHROME_BUILD) stats_enabled = [dialog.get() statsEnabled] ? true : false; #endif // GOOGLE_CHROME_BUILD FirstRun::CreateSentinel(); GoogleUpdateSettings::SetCollectStatsConsent(stats_enabled); #if defined(GOOGLE_CHROME_BUILD) // 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(); InitCrashProcessInfo(); } #endif // GOOGLE_CHROME_BUILD // If selected set as default browser. BOOL make_default_browser = [dialog.get() makeDefaultBrowser]; if (make_default_browser) { bool success = ShellIntegration::SetAsDefaultBrowser(); DCHECK(success); } // Import bookmarks. if ([dialog.get() importBookmarks]) { const ProfileInfo& source_profile = importer_host_->GetSourceProfileInfoAt( [dialog.get() browserImportSelectedIndex]); int16 items = source_profile.services_supported; // TODO(port): Do the actual import in a new process like Windows. gc.release(); StartImportingWithUI(nil, items, importer_host_.get(), source_profile, profile, this, true); } return true; }