chromium/chrome/browser/app_controller_mac.h

// 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.

#ifndef CHROME_BROWSER_APP_CONTROLLER_MAC_H_
#define CHROME_BROWSER_APP_CONTROLLER_MAC_H_

#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/profiles/profile.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/tab_restore_service.h"
#include "components/sessions/core/tab_restore_service_observer.h"

#if defined(__OBJC__)

#import <AuthenticationServices/AuthenticationServices.h>
#import <Cocoa/Cocoa.h>

#include <memory>
#include <vector>

#include "base/files/file_path.h"
#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
#include "components/prefs/pref_change_registrar.h"

class BookmarkMenuBridge;
class GURL;
class HistoryMenuBridge;
class Profile;
class TabMenuBridge;

namespace ui {
class ColorProvider;
}  // namespace ui

// The application controller object, created by loading the MainMenu nib.
// This handles things like responding to menus when there are no windows
// open, etc and acts as the NSApplication delegate.
@interface AppController
    : NSObject <NSUserInterfaceValidations,
                NSMenuDelegate,
                NSApplicationDelegate,
                ASWebAuthenticationSessionWebBrowserSessionHandling>

// The app-wide singleton AppController. Guaranteed to be the delegate of NSApp
// inside of Chromium (not inside of app shims; see AppShimDelegate). Guaranteed
// to not be nil.
@property(readonly, nonatomic, class) AppController* sharedController;

@property(readonly, nonatomic) BOOL startupComplete;
@property(readonly, nonatomic) Profile* lastProfileIfLoaded;

// DEPRECATED: use lastProfileIfLoaded instead.
// TODO(crbug.com/40054768): May be blocking, migrate all callers to
// |-lastProfileIfLoaded|.
@property(readonly, nonatomic) Profile* lastProfile;

// Do not create new instances of AppController; use the `sharedController`
// property so that the invariants of there always being exactly one
// AppController and that that instance is the NSApp delegate always hold true.
- (instancetype)init NS_UNAVAILABLE;

// This method is called very early in application startup after the main menu
// has been created.
- (void)mainMenuCreated;

- (void)didEndMainMessageLoop;

// Try to close all browser windows, and if that succeeds then quit.
- (BOOL)tryToTerminateApplication:(NSApplication*)app;

// Stop trying to terminate the application. That is, prevent the final browser
// window closure from causing the application to quit.
- (void)stopTryingToTerminateApplication:(NSApplication*)app;

// Run the quit confirmation panel and return whether or not to continue
// quitting.
- (BOOL)runConfirmQuitPanel;

// Indicate that the system is powering off or logging out.
- (void)willPowerOff:(NSNotification*)inNotification;

// Returns true if there is a modal window (either window- or application-
// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
// sheets) will not count as blocking the browser. But things like open/save
// dialogs that are window modal will block the browser.
- (BOOL)keyWindowIsModal;

// Called when the user picks a menu item when there are no key windows, or when
// there is no foreground browser window. Calls through to the browser object to
// execute the command. This assumes that the command is supported and doesn't
// check, otherwise it should have been disabled in the UI in
// |-validateUserInterfaceItem:|.
- (void)commandDispatch:(id)sender;

// Helper function called by -commandDispatch:, to actually execute the command.
// This runs after -commandDispatch: has obtained a pointer to the last Profile
// (which possibly requires an async Profile load).
- (void)executeCommand:(id)sender withProfile:(Profile*)profile;

// Show the preferences window, or bring it to the front if it's already
// visible.
- (IBAction)showPreferences:(id)sender;
- (IBAction)showPreferencesForProfile:(Profile*)profile;

// Redirect in the menu item from the expected target of "File's
// Owner" (NSApplication) for a Branded About Box
- (IBAction)orderFrontStandardAboutPanel:(id)sender;
- (IBAction)orderFrontStandardAboutPanelForProfile:(Profile*)profile;

// Toggles the "Confirm to Quit" preference.
- (IBAction)toggleConfirmToQuit:(id)sender;

// Delegate method to return the dock menu.
- (NSMenu*)applicationDockMenu:(NSApplication*)sender;

// Get the URLs that Launch Services expects the browser to open at startup.
- (const std::vector<GURL>&)startupUrls;

- (BookmarkMenuBridge*)bookmarkMenuBridge;
- (HistoryMenuBridge*)historyMenuBridge;
- (TabMenuBridge*)tabMenuBridge;

// Called when the user has changed browser windows, meaning the backing profile
// may have changed. This can cause a rebuild of the user-data menus. This is a
// no-op if the new profile is the same as the current one. This can be either
// the original or the incognito profile.
- (void)setLastProfile:(Profile*)profile;

// Returns the last active ColorProvider.
- (const ui::ColorProvider&)lastActiveColorProvider;

// This is called when the system wide light or dark mode changes.
- (void)nativeThemeDidChange;

// Certain NSMenuItems [Close Tab and Close Window] have different
// keyEquivalents depending on context. This must be invoked in two locations:
//   * In menuNeedsUpdate:, which is called prior to showing the NSMenu.
//   * In CommandDispatcher, which independently searches for a matching
//     keyEquivalent.
- (void)updateMenuItemKeyEquivalents;

// Returns YES if `window` is a normal, tabbed, non-app browser window.
// Serves as a swizzle point for unit tests to avoid creating Browser
// instances.
- (BOOL)windowHasBrowserTabs:(NSWindow*)window;

// Testing API.
- (void)setCmdWMenuItemForTesting:(NSMenuItem*)menuItem;
- (void)setShiftCmdWMenuItemForTesting:(NSMenuItem*)menuItem;

// As of macOS Ventura, the browser test harness can no longer make Chrome the
// active app. This can cause mainWindow and related to return nil. For cases
// where having the correct mainWindow is important, set it here.
- (void)setMainWindowForTesting:(NSWindow*)window;
- (void)setLastProfileForTesting:(Profile*)profile;

@end

#endif  // __OBJC__

// Functions that may be accessed from non-Objective-C C/C++ code.

namespace app_controller_mac {

// True if we are currently handling an IDC_NEW_{TAB,WINDOW} command. Used in
// SessionService::Observe() to get around windows/linux and mac having
// different models of application lifetime.
bool IsOpeningNewWindow();

// Create a guest profile if one is needed. Afterwards, even if the profile
// already existed, notify the AppController of the profile in use.
void CreateGuestProfileIfNeeded();

// Called when Enterprise startup dialog is close and repost
// applicationDidFinished notification.
void EnterpriseStartupDialogClosed();

// Tells RunInSafeProfile() or RunInSpecificSafeProfile() what to do if the
// profile cannot be loaded from disk.
enum ProfileLoadFailureBehavior {
  // Silently fail, and run |callback| with nullptr.
  kIgnoreOnFailure,
  // Show the profile picker, and run |callback| with nullptr.
  kShowProfilePickerOnFailure,
};

// Tries to load the profile returned by |-safeProfileForNewWindows:|. If it
// succeeds, calls |callback| with it.
//
// |callback| must be valid.
void RunInLastProfileSafely(base::OnceCallback<void(Profile*)> callback,
                            ProfileLoadFailureBehavior on_failure);

// Tries to load the profile in |profile_dir|. If it succeeds, calls
// |callback| with it. If the profile was already loaded, |callback| runs
// immediately.
//
// |callback| must be valid.
void RunInProfileSafely(const base::FilePath& profile_dir,
                        base::OnceCallback<void(Profile*)> callback,
                        ProfileLoadFailureBehavior on_failure);

// Allows application to terminate when the last Browser is closed by releasing
// the keep alive object held by the |AppController|. Note that all commands
// received after this call will be ignored, which is OK since the application
// is being terminated anyway.
void AllowApplicationToTerminate();

// Waits for the TabRestoreService to have loaded its entries, then calls
// OpenWindowWithRestoredTabs().
//
// Owned by itself.
class TabRestorer : public sessions::TabRestoreServiceObserver {
 public:
  // Restore the most recent tab in |profile|, e.g. for Cmd+Shift+T.
  static void RestoreMostRecent(Profile* profile);

  // Restore a specific tab in |profile|, e.g. for a History menu item.
  // |session_id| can be a |tab_restore::Entry::id|, or a
  // |TabRestoreEntryService::Entry::original_id|.
  static void RestoreByID(Profile* profile, SessionID session_id);

  ~TabRestorer() override;

  // sessions::TabRestoreServiceObserver:
  void TabRestoreServiceDestroyed(
      sessions::TabRestoreService* service) override;
  void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override;

 private:
  TabRestorer(Profile* profile, SessionID session_id);

  // Performs the tab restore. Called either in TabRestoreServiceLoaded(), or
  // directly from RestoreMostRecent()/RestoreByID() if the service was already
  // loaded.
  static void DoRestoreTab(Profile* profile, SessionID session_id);

  base::ScopedObservation<sessions::TabRestoreService,
                          sessions::TabRestoreServiceObserver>
      observation_{this};
  raw_ptr<Profile> profile_;
  SessionID session_id_;
};

// If the current chrome instance is running as a hidden application (with
// activation policy set to NSApplicationActivationPolicyProhibited), after this
// method is called the browser process will no longer keep itself alive as long
// as that is the case.
// This method should be called after chrome is launched as hidden application
// as soon as any other keep-alives have been created to keep the browser
// process alive.
void ResetKeepAliveWhileHidden();

}  // namespace app_controller_mac

#endif  // CHROME_BROWSER_APP_CONTROLLER_MAC_H_