1// Copyright (c) 2013 The Chromium Embedded Framework Authors. 2// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5 6#import <Cocoa/Cocoa.h> 7 8#include "include/cef_application_mac.h" 9#include "include/cef_command_line.h" 10#include "include/wrapper/cef_helpers.h" 11#include "include/wrapper/cef_library_loader.h" 12#include "tests/cefsimple/simple_app.h" 13#include "tests/cefsimple/simple_handler.h" 14 15// Receives notifications from the application. 16@interface SimpleAppDelegate : NSObject <NSApplicationDelegate> 17 18- (void)createApplication:(id)object; 19- (void)tryToTerminateApplication:(NSApplication*)app; 20@end 21 22// Provide the CefAppProtocol implementation required by CEF. 23@interface SimpleApplication : NSApplication <CefAppProtocol> { 24 @private 25 BOOL handlingSendEvent_; 26} 27@end 28 29@implementation SimpleApplication 30- (BOOL)isHandlingSendEvent { 31 return handlingSendEvent_; 32} 33 34- (void)setHandlingSendEvent:(BOOL)handlingSendEvent { 35 handlingSendEvent_ = handlingSendEvent; 36} 37 38- (void)sendEvent:(NSEvent*)event { 39 CefScopedSendingEvent sendingEventScoper; 40 [super sendEvent:event]; 41} 42 43// |-terminate:| is the entry point for orderly "quit" operations in Cocoa. This 44// includes the application menu's quit menu item and keyboard equivalent, the 45// application's dock icon menu's quit menu item, "quit" (not "force quit") in 46// the Activity Monitor, and quits triggered by user logout and system restart 47// and shutdown. 48// 49// The default |-terminate:| implementation ends the process by calling exit(), 50// and thus never leaves the main run loop. This is unsuitable for Chromium 51// since Chromium depends on leaving the main run loop to perform an orderly 52// shutdown. We support the normal |-terminate:| interface by overriding the 53// default implementation. Our implementation, which is very specific to the 54// needs of Chromium, works by asking the application delegate to terminate 55// using its |-tryToTerminateApplication:| method. 56// 57// |-tryToTerminateApplication:| differs from the standard 58// |-applicationShouldTerminate:| in that no special event loop is run in the 59// case that immediate termination is not possible (e.g., if dialog boxes 60// allowing the user to cancel have to be shown). Instead, this method tries to 61// close all browsers by calling CloseBrowser(false) via 62// ClientHandler::CloseAllBrowsers. Calling CloseBrowser will result in a call 63// to ClientHandler::DoClose and execution of |-performClose:| on the NSWindow. 64// DoClose sets a flag that is used to differentiate between new close events 65// (e.g., user clicked the window close button) and in-progress close events 66// (e.g., user approved the close window dialog). The NSWindowDelegate 67// |-windowShouldClose:| method checks this flag and either calls 68// CloseBrowser(false) in the case of a new close event or destructs the 69// NSWindow in the case of an in-progress close event. 70// ClientHandler::OnBeforeClose will be called after the CEF NSView hosted in 71// the NSWindow is dealloc'ed. 72// 73// After the final browser window has closed ClientHandler::OnBeforeClose will 74// begin actual tear-down of the application by calling CefQuitMessageLoop. 75// This ends the NSApplication event loop and execution then returns to the 76// main() function for cleanup before application termination. 77// 78// The standard |-applicationShouldTerminate:| is not supported, and code paths 79// leading to it must be redirected. 80- (void)terminate:(id)sender { 81 SimpleAppDelegate* delegate = 82 static_cast<SimpleAppDelegate*>([NSApp delegate]); 83 [delegate tryToTerminateApplication:self]; 84 // Return, don't exit. The application is responsible for exiting on its own. 85} 86@end 87 88@implementation SimpleAppDelegate 89 90// Create the application on the UI thread. 91- (void)createApplication:(id)object { 92 [[NSBundle mainBundle] loadNibNamed:@"MainMenu" 93 owner:NSApp 94 topLevelObjects:nil]; 95 96 // Set the delegate for application events. 97 [[NSApplication sharedApplication] setDelegate:self]; 98} 99 100- (void)tryToTerminateApplication:(NSApplication*)app { 101 SimpleHandler* handler = SimpleHandler::GetInstance(); 102 if (handler && !handler->IsClosing()) 103 handler->CloseAllBrowsers(false); 104} 105 106- (NSApplicationTerminateReply)applicationShouldTerminate: 107 (NSApplication*)sender { 108 return NSTerminateNow; 109} 110@end 111 112// Entry point function for the browser process. 113int main(int argc, char* argv[]) { 114 // Load the CEF framework library at runtime instead of linking directly 115 // as required by the macOS sandbox implementation. 116 CefScopedLibraryLoader library_loader; 117 if (!library_loader.LoadInMain()) 118 return 1; 119 120 // Provide CEF with command-line arguments. 121 CefMainArgs main_args(argc, argv); 122 123 @autoreleasepool { 124 // Initialize the SimpleApplication instance. 125 [SimpleApplication sharedApplication]; 126 127 // If there was an invocation to NSApp prior to this method, then the NSApp 128 // will not be a SimpleApplication, but will instead be an NSApplication. 129 // This is undesirable and we must enforce that this doesn't happen. 130 CHECK([NSApp isKindOfClass:[SimpleApplication class]]); 131 132 // Parse command-line arguments for use in this method. 133 CefRefPtr<CefCommandLine> command_line = 134 CefCommandLine::CreateCommandLine(); 135 command_line->InitFromArgv(argc, argv); 136 137 // Specify CEF global settings here. 138 CefSettings settings; 139 140 const bool with_chrome_runtime = 141 command_line->HasSwitch("enable-chrome-runtime"); 142 143 if (with_chrome_runtime) { 144 // Enable experimental Chrome runtime. See issue #2969 for details. 145 settings.chrome_runtime = true; 146 } 147 148 // When generating projects with CMake the CEF_USE_SANDBOX value will be 149 // defined automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line 150 // to disable use of the sandbox. 151#if !defined(CEF_USE_SANDBOX) 152 settings.no_sandbox = true; 153#endif 154 155 // SimpleApp implements application-level callbacks for the browser process. 156 // It will create the first browser instance in OnContextInitialized() after 157 // CEF has initialized. 158 CefRefPtr<SimpleApp> app(new SimpleApp); 159 160 // Initialize CEF for the browser process. 161 CefInitialize(main_args, settings, app.get(), nullptr); 162 163 // Create the application delegate. 164 NSObject* delegate = [[SimpleAppDelegate alloc] init]; 165 [delegate performSelectorOnMainThread:@selector(createApplication:) 166 withObject:nil 167 waitUntilDone:NO]; 168 169 // Run the CEF message loop. This will block until CefQuitMessageLoop() is 170 // called. 171 CefRunMessageLoop(); 172 173 // Shut down CEF. 174 CefShutdown(); 175 176 // Release the delegate. 177#if !__has_feature(objc_arc) 178 [delegate release]; 179#endif // !__has_feature(objc_arc) 180 delegate = nil; 181 } // @autoreleasepool 182 183 return 0; 184} 185