| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "chrome/browser/ui/cocoa/first_run_dialog_cocoa.h" |
| |
| #include "base/apple/bundle_locations.h" |
| #include "base/command_line.h" |
| #include "base/compiler_specific.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/run_loop.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/task/current_thread.h" |
| #import "base/task/single_thread_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "chrome/browser/browser_process_impl.h" |
| #include "chrome/browser/first_run/first_run.h" |
| #include "chrome/browser/first_run/first_run_dialog.h" |
| #include "chrome/browser/metrics/metrics_reporting_state.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/shell_integration.h" |
| #include "chrome/browser/ui/cocoa/first_run_dialog_controller.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/public/common/content_switches.h" |
| #include "ui/base/l10n/l10n_util_mac.h" |
| #include "url/gurl.h" |
| |
| @interface FirstRunDialogController (PrivateMethods) |
| // Show the dialog. |
| - (void)show; |
| @end |
| |
| namespace { |
| |
| class FirstRunShowBridge : public base::RefCounted<FirstRunShowBridge> { |
| public: |
| explicit FirstRunShowBridge(FirstRunDialogController* controller); |
| |
| void ShowDialog(base::OnceClosure quit_closure); |
| |
| private: |
| friend class base::RefCounted<FirstRunShowBridge>; |
| |
| ~FirstRunShowBridge(); |
| |
| FirstRunDialogController* controller_; |
| }; |
| |
| FirstRunShowBridge::FirstRunShowBridge(FirstRunDialogController* controller) |
| : controller_(controller) {} |
| |
| void FirstRunShowBridge::ShowDialog(base::OnceClosure quit_closure) { |
| // Proceeding past the modal dialog requires user interaction. Allow nested |
| // tasks to run so that signal handlers operate correctly. |
| base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow; |
| [controller_ show]; |
| std::move(quit_closure).Run(); |
| } |
| |
| FirstRunShowBridge::~FirstRunShowBridge() = default; |
| |
| void ShowFirstRunModal() { |
| FirstRunDialogController* dialog = [[FirstRunDialogController alloc] init]; |
| |
| [dialog showWindow:nil]; |
| |
| // If the dialog asked the user to opt-in for stats and crash reporting, |
| // record the decision and enable the crash reporter if appropriate. |
| bool consent_given = [dialog isStatsReportingEnabled]; |
| ChangeMetricsReportingState( |
| consent_given, ChangeMetricsReportingStateCalledFrom::kUiFirstRun); |
| |
| // If selected, set as default browser. Skip in automated tests so that an OS |
| // dialog confirming the default browser choice isn't left on screen. |
| BOOL make_default_browser = |
| [dialog isMakeDefaultBrowserEnabled] && |
| !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType); |
| if (make_default_browser) { |
| bool success = shell_integration::SetAsDefaultBrowser(); |
| DCHECK(success); |
| } |
| } |
| |
| } // namespace |
| |
| namespace first_run { |
| |
| void ShowFirstRunDialogCocoa() { |
| ShowFirstRunModal(); |
| } |
| |
| } // namespace first_run |
| |
| @implementation FirstRunDialogController { |
| FirstRunDialogViewController* __strong _viewController; |
| } |
| |
| - (instancetype)init { |
| _viewController = [[FirstRunDialogViewController alloc] init]; |
| |
| // Create the content view controller (and the content view) *before* the |
| // window, so that we can find out what the content view's frame is supposed |
| // to be for use here. |
| NSWindow* window = |
| [[NSWindow alloc] initWithContentRect:_viewController.view.frame |
| styleMask:NSWindowStyleMaskTitled |
| backing:NSBackingStoreBuffered |
| defer:YES]; |
| window.contentView = _viewController.view; |
| window.title = [_viewController windowTitle]; |
| |
| self = [super initWithWindow:window]; |
| |
| return self; |
| } |
| |
| - (IBAction)showWindow:(id)sender { |
| // The main MessageLoop has not yet run, but has been spun. If we call |
| // -[NSApplication runModalForWindow:] we will hang <http://crbug.com/54248>. |
| // Therefore the main MessageLoop is run so things work. |
| |
| scoped_refptr<FirstRunShowBridge> bridge(new FirstRunShowBridge(self)); |
| base::RunLoop run_loop; |
| |
| // At this point during startup, ChromeBrowserMain has yet to start the main |
| // message loop. Consequently, this run loop will effectively be the main |
| // message loop for the duration of the dialog's lifetime. Tell the |
| // BrowserProcessImpl how to quit the loop if any of the shutdown signal |
| // handlers is received. (The ShutdownDetector posts a task to the UI thread's |
| // TaskRunner to begin shutdown upon receiving a SIGTERM.) |
| static_cast<BrowserProcessImpl*>(g_browser_process) |
| ->SetQuitClosure(base::BindOnce( |
| [](base::RunLoop* run_loop) { |
| [NSApp abortModal]; |
| run_loop->Quit(); |
| }, |
| &run_loop)); |
| |
| // Barring a shutdown signal, the run loop will quit when the user closes the |
| // first run dialog. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&FirstRunShowBridge::ShowDialog, bridge.get(), |
| run_loop.QuitClosure())); |
| run_loop.Run(); |
| |
| static_cast<BrowserProcessImpl*>(g_browser_process)->ClearQuitClosure(); |
| } |
| |
| - (void)show { |
| NSWindow* win = [self window]; |
| |
| // 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. |
| [win center]; |
| [NSApp runModalForWindow:win]; |
| } |
| |
| - (BOOL)isStatsReportingEnabled { |
| return [_viewController isStatsReportingEnabled]; |
| } |
| |
| - (BOOL)isMakeDefaultBrowserEnabled { |
| return [_viewController isMakeDefaultBrowserEnabled]; |
| } |
| |
| @end |