[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / first_run_dialog.mm
blobb838ae68bd784dbcb1d410d3f1ab727bb2c10b97
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/first_run_dialog.h"
7 #include "base/bind.h"
8 #include "base/mac/bundle_locations.h"
9 #include "base/mac/mac_util.h"
10 #import "base/mac/scoped_nsobject.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "chrome/browser/first_run/first_run.h"
15 #include "chrome/browser/first_run/first_run_dialog.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/search_engines/template_url_service.h"
18 #include "chrome/browser/search_engines/template_url_service_factory.h"
19 #include "chrome/browser/shell_integration.h"
20 #include "chrome/common/chrome_version_info.h"
21 #include "chrome/common/url_constants.h"
22 #include "grit/locale_settings.h"
23 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
24 #include "ui/base/l10n/l10n_util_mac.h"
25 #include "url/gurl.h"
27 #if defined(GOOGLE_CHROME_BUILD)
28 #include "base/prefs/pref_service.h"
29 #include "chrome/browser/browser_process.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/installer/util/google_update_settings.h"
32 #import "components/breakpad/app/breakpad_mac.h"
33 #endif
35 @interface FirstRunDialogController (PrivateMethods)
36 // Show the dialog.
37 - (void)show;
38 @end
40 namespace {
42 // Compare function for -[NSArray sortedArrayUsingFunction:context:] that
43 // sorts the views in Y order bottom up.
44 NSInteger CompareFrameY(id view1, id view2, void* context) {
45   CGFloat y1 = NSMinY([view1 frame]);
46   CGFloat y2 = NSMinY([view2 frame]);
47   if (y1 < y2)
48     return NSOrderedAscending;
49   else if (y1 > y2)
50     return NSOrderedDescending;
51   else
52     return NSOrderedSame;
55 class FirstRunShowBridge : public base::RefCounted<FirstRunShowBridge> {
56  public:
57   FirstRunShowBridge(FirstRunDialogController* controller);
59   void ShowDialog();
61  private:
62   friend class base::RefCounted<FirstRunShowBridge>;
64   ~FirstRunShowBridge();
66   FirstRunDialogController* controller_;
69 FirstRunShowBridge::FirstRunShowBridge(
70     FirstRunDialogController* controller) : controller_(controller) {
73 void FirstRunShowBridge::ShowDialog() {
74   [controller_ show];
75   base::MessageLoop::current()->QuitNow();
78 FirstRunShowBridge::~FirstRunShowBridge() {}
80 // Show the first run UI.
81 // Returns true if the first run dialog was shown.
82 bool ShowFirstRun(Profile* profile) {
83   bool dialog_shown = false;
84 #if defined(GOOGLE_CHROME_BUILD)
85   // The purpose of the dialog is to ask the user to enable stats and crash
86   // reporting. This setting may be controlled through configuration management
87   // in enterprise scenarios. If that is the case, skip the dialog entirely, as
88   // it's not worth bothering the user for only the default browser question
89   // (which is likely to be forced in enterprise deployments anyway).
90   const PrefService::Preference* metrics_reporting_pref =
91       g_browser_process->local_state()->FindPreference(
92           prefs::kMetricsReportingEnabled);
93   if (!metrics_reporting_pref || !metrics_reporting_pref->IsManaged()) {
94     base::scoped_nsobject<FirstRunDialogController> dialog(
95         [[FirstRunDialogController alloc] init]);
97     [dialog.get() showWindow:nil];
98     dialog_shown = true;
100     // If the dialog asked the user to opt-in for stats and crash reporting,
101     // record the decision and enable the crash reporter if appropriate.
102     bool stats_enabled = [dialog.get() statsEnabled];
103     GoogleUpdateSettings::SetCollectStatsConsent(stats_enabled);
105     // Breakpad is normally enabled very early in the startup process.  However,
106     // on the first run it may not have been enabled due to the missing opt-in
107     // from the user.  If the user agreed now, enable breakpad if necessary.
108     if (!breakpad::IsCrashReporterEnabled() && stats_enabled) {
109       breakpad::InitCrashReporter(std::string());
110       breakpad::InitCrashProcessInfo(std::string());
111     }
113     // If selected set as default browser.
114     BOOL make_default_browser = [dialog.get() makeDefaultBrowser];
115     if (make_default_browser) {
116       bool success = ShellIntegration::SetAsDefaultBrowser();
117       DCHECK(success);
118     }
119   }
120 #else  // GOOGLE_CHROME_BUILD
121   // We don't show the dialog in Chromium.
122 #endif  // GOOGLE_CHROME_BUILD
124   // Set preference to show first run bubble and welcome page.
125   // Only display the bubble if there is a default search provider.
126   TemplateURLService* search_engines_model =
127       TemplateURLServiceFactory::GetForProfile(profile);
128   if (search_engines_model &&
129       search_engines_model->GetDefaultSearchProvider()) {
130     first_run::SetShowFirstRunBubblePref(first_run::FIRST_RUN_BUBBLE_SHOW);
131   }
132   first_run::SetShouldShowWelcomePage();
134   return dialog_shown;
137 // True when the stats checkbox should be checked by default. This is only
138 // the case when the canary is running.
139 bool StatsCheckboxDefault() {
140   return chrome::VersionInfo::GetChannel() ==
141       chrome::VersionInfo::CHANNEL_CANARY;
144 }  // namespace
146 namespace first_run {
148 bool ShowFirstRunDialog(Profile* profile) {
149   return ShowFirstRun(profile);
152 }  // namespace first_run
154 @implementation FirstRunDialogController
156 @synthesize statsEnabled = statsEnabled_;
157 @synthesize makeDefaultBrowser = makeDefaultBrowser_;
159 - (id)init {
160   NSString* nibpath =
161       [base::mac::FrameworkBundle() pathForResource:@"FirstRunDialog"
162                                              ofType:@"nib"];
163   if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
164     // Bound to the dialog checkboxes.
165     makeDefaultBrowser_ = ShellIntegration::CanSetAsDefaultBrowser() !=
166         ShellIntegration::SET_DEFAULT_NOT_ALLOWED;
167     statsEnabled_ = StatsCheckboxDefault();
168   }
169   return self;
172 - (void)dealloc {
173   [super dealloc];
176 - (IBAction)showWindow:(id)sender {
177   // The main MessageLoop has not yet run, but has been spun. If we call
178   // -[NSApplication runModalForWindow:] we will hang <http://crbug.com/54248>.
179   // Therefore the main MessageLoop is run so things work.
181   scoped_refptr<FirstRunShowBridge> bridge(new FirstRunShowBridge(self));
182   base::MessageLoop::current()->PostTask(FROM_HERE,
183       base::Bind(&FirstRunShowBridge::ShowDialog, bridge.get()));
184   base::MessageLoop::current()->Run();
187 - (void)show {
188   NSWindow* win = [self window];
190   if (!ShellIntegration::CanSetAsDefaultBrowser()) {
191     [setAsDefaultCheckbox_ setHidden:YES];
192   }
194   // Only support the sizing the window once.
195   DCHECK(!beenSized_) << "ShowWindow was called twice?";
196   if (!beenSized_) {
197     beenSized_ = YES;
198     DCHECK_GT([objectsToSize_ count], 0U);
200     // Size everything to fit, collecting the widest growth needed (XIB provides
201     // the min size, i.e.-never shrink, just grow).
202     CGFloat largestWidthChange = 0.0;
203     for (NSView* view in objectsToSize_) {
204       DCHECK_NE(statsCheckbox_, view) << "Stats checkbox shouldn't be in list";
205       if (![view isHidden]) {
206         NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:view];
207         DCHECK_EQ(delta.height, 0.0)
208             << "Didn't expect anything to change heights";
209         if (largestWidthChange < delta.width)
210           largestWidthChange = delta.width;
211       }
212     }
214     // Make the window wide enough to fit everything.
215     if (largestWidthChange > 0.0) {
216       NSView* contentView = [win contentView];
217       NSRect windowFrame = [contentView convertRect:[win frame] fromView:nil];
218       windowFrame.size.width += largestWidthChange;
219       windowFrame = [contentView convertRect:windowFrame toView:nil];
220       [win setFrame:windowFrame display:NO];
221     }
223     // The stats checkbox gets some really long text, so it gets word wrapped
224     // and then sized.
225     DCHECK(statsCheckbox_);
226     CGFloat statsCheckboxHeightChange = 0.0;
227     [GTMUILocalizerAndLayoutTweaker wrapButtonTitleForWidth:statsCheckbox_];
228     statsCheckboxHeightChange =
229         [GTMUILocalizerAndLayoutTweaker sizeToFitView:statsCheckbox_].height;
231     // Walk bottom up shuffling for all the hidden views.
232     NSArray* subViews =
233         [[[win contentView] subviews] sortedArrayUsingFunction:CompareFrameY
234                                                        context:NULL];
235     CGFloat moveDown = 0.0;
236     NSUInteger numSubViews = [subViews count];
237     for (NSUInteger idx = 0 ; idx < numSubViews ; ++idx) {
238       NSView* view = [subViews objectAtIndex:idx];
240       // If the view is hidden, collect the amount to move everything above it
241       // down, if it's not hidden, apply any shift down.
242       if ([view isHidden]) {
243         DCHECK_GT((numSubViews - 1), idx)
244             << "Don't support top view being hidden";
245         NSView* nextView = [subViews objectAtIndex:(idx + 1)];
246         CGFloat viewBottom = [view frame].origin.y;
247         CGFloat nextViewBottom = [nextView frame].origin.y;
248         moveDown += nextViewBottom - viewBottom;
249       } else {
250         if (moveDown != 0.0) {
251           NSPoint origin = [view frame].origin;
252           origin.y -= moveDown;
253           [view setFrameOrigin:origin];
254         }
255       }
256       // Special case, if this is the stats checkbox, everything above it needs
257       // to get moved up by the amount it changed height.
258       if (view == statsCheckbox_) {
259         moveDown -= statsCheckboxHeightChange;
260       }
261     }
263     // Resize the window for any height change from hidden views, etc.
264     if (moveDown != 0.0) {
265       NSView* contentView = [win contentView];
266       [contentView setAutoresizesSubviews:NO];
267       NSRect windowFrame = [contentView convertRect:[win frame] fromView:nil];
268       windowFrame.size.height -= moveDown;
269       windowFrame = [contentView convertRect:windowFrame toView:nil];
270       [win setFrame:windowFrame display:NO];
271       [contentView setAutoresizesSubviews:YES];
272     }
274   }
276   // Neat weirdness in the below code - the Application menu stays enabled
277   // while the window is open but selecting items from it (e.g. Quit) has
278   // no effect.  I'm guessing that this is an artifact of us being a
279   // background-only application at this stage and displaying a modal
280   // window.
282   // Display dialog.
283   [win center];
284   [NSApp runModalForWindow:win];
287 - (IBAction)ok:(id)sender {
288   [[self window] close];
289   [NSApp stopModal];
292 - (IBAction)learnMore:(id)sender {
293   NSString* urlStr = base::SysUTF8ToNSString(chrome::kLearnMoreReportingURL);
294   NSURL* learnMoreUrl = [NSURL URLWithString:urlStr];
295   [[NSWorkspace sharedWorkspace] openURL:learnMoreUrl];
298 @end