// Copyright (c) 2015 The Chromium Embedded Framework 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 "tests/cefclient/browser/root_window_mac.h" #include #include "include/base/cef_callback.h" #include "include/cef_app.h" #include "include/cef_application_mac.h" #include "tests/cefclient/browser/browser_window_osr_mac.h" #include "tests/cefclient/browser/browser_window_std_mac.h" #include "tests/cefclient/browser/main_context.h" #include "tests/cefclient/browser/temp_window.h" #include "tests/cefclient/browser/window_test_runner_mac.h" #include "tests/shared/browser/main_message_loop.h" #include "tests/shared/common/client_switches.h" // Receives notifications from controls and the browser window. Will delete // itself when done. @interface RootWindowDelegate : NSObject { @private NSWindow* window_; client::RootWindowMac* root_window_; bool force_close_; } @property(nonatomic, readonly) client::RootWindowMac* root_window; @property(nonatomic, readwrite) bool force_close; - (id)initWithWindow:(NSWindow*)window andRootWindow:(client::RootWindowMac*)root_window; - (IBAction)goBack:(id)sender; - (IBAction)goForward:(id)sender; - (IBAction)reload:(id)sender; - (IBAction)stopLoading:(id)sender; - (IBAction)takeURLStringValueFrom:(NSTextField*)sender; @end // @interface RootWindowDelegate namespace client { namespace { // Sizes for URL bar layout. #define BUTTON_HEIGHT 22 #define BUTTON_WIDTH 72 #define BUTTON_MARGIN 8 #define URLBAR_HEIGHT 32 NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { NSButton* button = [[NSButton alloc] initWithFrame:*rect]; #if !__has_feature(objc_arc) [button autorelease]; #endif // !__has_feature(objc_arc) [button setTitle:title]; [button setBezelStyle:NSSmallSquareBezelStyle]; [button setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)]; [parent addSubview:button]; rect->origin.x += BUTTON_WIDTH; return button; } NSRect GetScreenRectForWindow(NSWindow* window) { NSScreen* screen = [window screen]; if (screen == nil) screen = [NSScreen mainScreen]; return [screen visibleFrame]; } } // namespace class RootWindowMacImpl : public base::RefCountedThreadSafe { public: RootWindowMacImpl(RootWindowMac& root_window); ~RootWindowMacImpl(); // Called by RootWindowDelegate after the associated NSWindow has been // closed. void OnNativeWindowClosed(); void CreateBrowserWindow(const std::string& startup_url); void CreateRootWindow(const CefBrowserSettings& settings, bool initially_hidden); // RootWindow methods. void Init(RootWindow::Delegate* delegate, std::unique_ptr config, const CefBrowserSettings& settings); void InitAsPopup(RootWindow::Delegate* delegate, bool with_controls, bool with_osr, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings); void Show(RootWindow::ShowMode mode); void Hide(); void SetBounds(int x, int y, size_t width, size_t height); void Close(bool force); void SetDeviceScaleFactor(float device_scale_factor); float GetDeviceScaleFactor() const; CefRefPtr GetBrowser() const; ClientWindowHandle GetWindowHandle() const; bool WithWindowlessRendering() const; bool WithExtension() const; // BrowserWindow::Delegate methods. void OnBrowserCreated(CefRefPtr browser); void OnBrowserWindowDestroyed(); void OnSetAddress(const std::string& url); void OnSetTitle(const std::string& title); void OnSetFullscreen(bool fullscreen); void OnAutoResize(const CefSize& new_size); void OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward); void OnSetDraggableRegions(const std::vector& regions); void NotifyDestroyedIfDone(); // After initialization all members are only accessed on the main thread. // Members set during initialization. RootWindowMac& root_window_; bool with_controls_; bool with_osr_; bool with_extension_; bool is_popup_; CefRect start_rect_; std::unique_ptr browser_window_; bool initialized_; // Main window. NSWindow* window_; RootWindowDelegate* window_delegate_; // Buttons. NSButton* back_button_; NSButton* forward_button_; NSButton* reload_button_; NSButton* stop_button_; // URL text field. NSTextField* url_textfield_; bool window_destroyed_; bool browser_destroyed_; }; RootWindowMacImpl::RootWindowMacImpl(RootWindowMac& root_window) : root_window_(root_window), with_controls_(false), with_osr_(false), is_popup_(false), initialized_(false), window_(nil), back_button_(nil), forward_button_(nil), reload_button_(nil), stop_button_(nil), url_textfield_(nil), window_destroyed_(false), browser_destroyed_(false) {} RootWindowMacImpl::~RootWindowMacImpl() { REQUIRE_MAIN_THREAD(); // The window and browser should already have been destroyed. DCHECK(window_destroyed_); DCHECK(browser_destroyed_); } void RootWindowMacImpl::Init(RootWindow::Delegate* delegate, std::unique_ptr config, const CefBrowserSettings& settings) { DCHECK(!initialized_); with_controls_ = config->with_controls; with_osr_ = config->with_osr; with_extension_ = config->with_extension; start_rect_ = config->bounds; CreateBrowserWindow(config->url); initialized_ = true; // Create the native root window on the main thread. if (CURRENTLY_ON_MAIN_THREAD()) { CreateRootWindow(settings, config->initially_hidden); } else { MAIN_POST_CLOSURE(base::BindOnce(&RootWindowMacImpl::CreateRootWindow, this, settings, config->initially_hidden)); } } void RootWindowMacImpl::InitAsPopup(RootWindow::Delegate* delegate, bool with_controls, bool with_osr, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings) { DCHECK(delegate); DCHECK(!initialized_); with_controls_ = with_controls; with_osr_ = with_osr; is_popup_ = true; if (popupFeatures.xSet) start_rect_.x = popupFeatures.x; if (popupFeatures.ySet) start_rect_.y = popupFeatures.y; if (popupFeatures.widthSet) start_rect_.width = popupFeatures.width; if (popupFeatures.heightSet) start_rect_.height = popupFeatures.height; CreateBrowserWindow(std::string()); initialized_ = true; // The new popup is initially parented to a temporary window. The native root // window will be created after the browser is created and the popup window // will be re-parented to it at that time. browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo, client, settings); } void RootWindowMacImpl::Show(RootWindow::ShowMode mode) { REQUIRE_MAIN_THREAD(); if (!window_) return; const bool is_visible = [window_ isVisible]; const bool is_minimized = [window_ isMiniaturized]; const bool is_maximized = [window_ isZoomed]; if ((mode == RootWindow::ShowMinimized && is_minimized) || (mode == RootWindow::ShowMaximized && is_maximized) || (mode == RootWindow::ShowNormal && is_visible)) { // The window is already in the desired state. return; } // Undo the previous state since it's not the desired state. if (is_minimized) [window_ deminiaturize:nil]; else if (is_maximized) [window_ performZoom:nil]; // Window visibility may change after (for example) deminiaturizing the // window. if (![window_ isVisible]) [window_ makeKeyAndOrderFront:nil]; if (mode == RootWindow::ShowMinimized) [window_ performMiniaturize:nil]; else if (mode == RootWindow::ShowMaximized) [window_ performZoom:nil]; } void RootWindowMacImpl::Hide() { REQUIRE_MAIN_THREAD(); if (!window_) return; // Undo miniaturization, if any, so the window will actually be hidden. if ([window_ isMiniaturized]) [window_ deminiaturize:nil]; // Hide the window. [window_ orderOut:nil]; } void RootWindowMacImpl::SetBounds(int x, int y, size_t width, size_t height) { REQUIRE_MAIN_THREAD(); if (!window_) return; NSRect screen_rect = GetScreenRectForWindow(window_); // Desired content rectangle. NSRect content_rect; content_rect.size.width = static_cast(width); content_rect.size.height = static_cast(height) + (with_controls_ ? URLBAR_HEIGHT : 0); // Convert to a frame rectangle. NSRect frame_rect = [window_ frameRectForContentRect:content_rect]; frame_rect.origin.x = x; frame_rect.origin.y = screen_rect.size.height - y; [window_ setFrame:frame_rect display:YES]; } void RootWindowMacImpl::Close(bool force) { REQUIRE_MAIN_THREAD(); if (window_) { static_cast([window_ delegate]).force_close = force; [window_ performClose:nil]; window_destroyed_ = true; } } void RootWindowMacImpl::SetDeviceScaleFactor(float device_scale_factor) { REQUIRE_MAIN_THREAD(); if (browser_window_ && with_osr_) browser_window_->SetDeviceScaleFactor(device_scale_factor); } float RootWindowMacImpl::GetDeviceScaleFactor() const { REQUIRE_MAIN_THREAD(); if (browser_window_ && with_osr_) return browser_window_->GetDeviceScaleFactor(); NOTREACHED(); return 0.0f; } CefRefPtr RootWindowMacImpl::GetBrowser() const { REQUIRE_MAIN_THREAD(); if (browser_window_) return browser_window_->GetBrowser(); return nullptr; } ClientWindowHandle RootWindowMacImpl::GetWindowHandle() const { REQUIRE_MAIN_THREAD(); return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE([window_ contentView]); } bool RootWindowMacImpl::WithWindowlessRendering() const { REQUIRE_MAIN_THREAD(); return with_osr_; } bool RootWindowMacImpl::WithExtension() const { REQUIRE_MAIN_THREAD(); return with_extension_; } void RootWindowMacImpl::OnNativeWindowClosed() { window_ = nil; window_destroyed_ = true; NotifyDestroyedIfDone(); } void RootWindowMacImpl::CreateBrowserWindow(const std::string& startup_url) { if (with_osr_) { OsrRendererSettings settings = {}; MainContext::Get()->PopulateOsrSettings(&settings); browser_window_.reset( new BrowserWindowOsrMac(&root_window_, startup_url, settings)); } else { browser_window_.reset(new BrowserWindowStdMac(&root_window_, startup_url)); } } void RootWindowMacImpl::CreateRootWindow(const CefBrowserSettings& settings, bool initially_hidden) { REQUIRE_MAIN_THREAD(); DCHECK(!window_); // TODO(port): If no x,y position is specified the window will always appear // in the upper-left corner. Maybe there's a better default place to put it? int x = start_rect_.x; int y = start_rect_.y; int width, height; if (start_rect_.IsEmpty()) { // TODO(port): Also, maybe there's a better way to choose the default size. width = 800; height = 600; } else { width = start_rect_.width; height = start_rect_.height; } // Create the main window. NSRect screen_rect = [[NSScreen mainScreen] visibleFrame]; NSRect window_rect = NSMakeRect(x, screen_rect.size.height - y, width, height); // The CEF framework library is loaded at runtime so we need to use this // mechanism for retrieving the class. Class window_class = NSClassFromString(@"UnderlayOpenGLHostingWindow"); CHECK(window_class); window_ = [[window_class alloc] initWithContentRect:window_rect styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask | NSUnifiedTitleAndToolbarWindowMask) backing:NSBackingStoreBuffered defer:NO]; [window_ setTitle:@"cefclient"]; // No dark mode, please window_.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua]; // Create the delegate for control and browser window events. window_delegate_ = [[RootWindowDelegate alloc] initWithWindow:window_ andRootWindow:&root_window_]; // Rely on the window delegate to clean us up rather than immediately // releasing when the window gets closed. We use the delegate to do // everything from the autorelease pool so the window isn't on the stack // during cleanup (ie, a window close from javascript). [window_ setReleasedWhenClosed:NO]; const cef_color_t background_color = MainContext::Get()->GetBackgroundColor(); [window_ setBackgroundColor:[NSColor colorWithCalibratedRed:float(CefColorGetR( background_color)) / 255.0f green:float(CefColorGetG( background_color)) / 255.0f blue:float(CefColorGetB( background_color)) / 255.0f alpha:1.f]]; NSView* contentView = [window_ contentView]; NSRect contentBounds = [contentView bounds]; if (!with_osr_) { // Make the content view for the window have a layer. This will make all // sub-views have layers. This is necessary to ensure correct layer // ordering of all child views and their layers. [contentView setWantsLayer:YES]; } if (with_controls_) { // Create the buttons. NSRect button_rect = contentBounds; button_rect.origin.y = window_rect.size.height - URLBAR_HEIGHT + (URLBAR_HEIGHT - BUTTON_HEIGHT) / 2; button_rect.size.height = BUTTON_HEIGHT; button_rect.origin.x += BUTTON_MARGIN; button_rect.size.width = BUTTON_WIDTH; contentBounds.size.height -= URLBAR_HEIGHT; back_button_ = MakeButton(&button_rect, @"Back", contentView); [back_button_ setTarget:window_delegate_]; [back_button_ setAction:@selector(goBack:)]; [back_button_ setEnabled:NO]; forward_button_ = MakeButton(&button_rect, @"Forward", contentView); [forward_button_ setTarget:window_delegate_]; [forward_button_ setAction:@selector(goForward:)]; [forward_button_ setEnabled:NO]; reload_button_ = MakeButton(&button_rect, @"Reload", contentView); [reload_button_ setTarget:window_delegate_]; [reload_button_ setAction:@selector(reload:)]; [reload_button_ setEnabled:NO]; stop_button_ = MakeButton(&button_rect, @"Stop", contentView); [stop_button_ setTarget:window_delegate_]; [stop_button_ setAction:@selector(stopLoading:)]; [stop_button_ setEnabled:NO]; // Create the URL text field. button_rect.origin.x += BUTTON_MARGIN; button_rect.size.width = [contentView bounds].size.width - button_rect.origin.x - BUTTON_MARGIN; url_textfield_ = [[NSTextField alloc] initWithFrame:button_rect]; [contentView addSubview:url_textfield_]; [url_textfield_ setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; [url_textfield_ setTarget:window_delegate_]; [url_textfield_ setAction:@selector(takeURLStringValueFrom:)]; [url_textfield_ setEnabled:NO]; [[url_textfield_ cell] setWraps:NO]; [[url_textfield_ cell] setScrollable:YES]; } if (!is_popup_) { // Create the browser window. browser_window_->CreateBrowser( CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(contentView), CefRect(0, 0, width, height), settings, nullptr, root_window_.delegate_->GetRequestContext(&root_window_)); } else { // With popups we already have a browser window. Parent the browser window // to the root window and show it in the correct location. browser_window_->ShowPopup(CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(contentView), 0, 0, contentBounds.size.width, contentBounds.size.height); } if (!initially_hidden) { // Show the window. Show(RootWindow::ShowNormal); // Size the window. SetBounds(x, y, width, height); } } void RootWindowMacImpl::OnBrowserCreated(CefRefPtr browser) { REQUIRE_MAIN_THREAD(); // For popup browsers create the root window once the browser has been // created. if (is_popup_) CreateRootWindow(CefBrowserSettings(), false); root_window_.delegate_->OnBrowserCreated(&root_window_, browser); } void RootWindowMacImpl::OnBrowserWindowDestroyed() { REQUIRE_MAIN_THREAD(); browser_window_.reset(); if (!window_destroyed_) { // The browser was destroyed first. This could be due to the use of // off-screen rendering or execution of JavaScript window.close(). // Close the RootWindow. Close(true); } browser_destroyed_ = true; NotifyDestroyedIfDone(); } void RootWindowMacImpl::OnSetAddress(const std::string& url) { REQUIRE_MAIN_THREAD(); if (url_textfield_) { std::string urlStr(url); NSString* str = [NSString stringWithUTF8String:urlStr.c_str()]; [url_textfield_ setStringValue:str]; } } void RootWindowMacImpl::OnSetDraggableRegions( const std::vector& regions) { REQUIRE_MAIN_THREAD(); // TODO(cef): Implement support for draggable regions on this platform. } void RootWindowMacImpl::OnSetTitle(const std::string& title) { REQUIRE_MAIN_THREAD(); if (window_) { std::string titleStr(title); NSString* str = [NSString stringWithUTF8String:titleStr.c_str()]; [window_ setTitle:str]; } } void RootWindowMacImpl::OnSetFullscreen(bool fullscreen) { REQUIRE_MAIN_THREAD(); CefRefPtr browser = GetBrowser(); if (browser) { std::unique_ptr test_runner( new window_test::WindowTestRunnerMac()); if (fullscreen) test_runner->Maximize(browser); else test_runner->Restore(browser); } } void RootWindowMacImpl::OnAutoResize(const CefSize& new_size) { REQUIRE_MAIN_THREAD(); if (!window_) return; // Desired content rectangle. NSRect content_rect; content_rect.size.width = static_cast(new_size.width); content_rect.size.height = static_cast(new_size.height) + (with_controls_ ? URLBAR_HEIGHT : 0); // Convert to a frame rectangle. NSRect frame_rect = [window_ frameRectForContentRect:content_rect]; // Don't change the origin. frame_rect.origin = window_.frame.origin; [window_ setFrame:frame_rect display:YES]; // Make sure the window is visible. Show(RootWindow::ShowNormal); } void RootWindowMacImpl::OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward) { REQUIRE_MAIN_THREAD(); if (with_controls_) { [url_textfield_ setEnabled:YES]; [reload_button_ setEnabled:!isLoading]; [stop_button_ setEnabled:isLoading]; [back_button_ setEnabled:canGoBack]; [forward_button_ setEnabled:canGoForward]; } // After Loading is done, check if voiceover is running and accessibility // should be enabled. if (!isLoading) { Boolean keyExists = false; // On OSX there is no API to query if VoiceOver is active or not. The value // however is stored in preferences that can be queried. if (CFPreferencesGetAppBooleanValue(CFSTR("voiceOverOnOffKey"), CFSTR("com.apple.universalaccess"), &keyExists)) { GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED); } } } void RootWindowMacImpl::NotifyDestroyedIfDone() { // Notify once both the window and the browser have been destroyed. if (window_destroyed_ && browser_destroyed_) root_window_.delegate_->OnRootWindowDestroyed(&root_window_); } RootWindowMac::RootWindowMac() { impl_ = new RootWindowMacImpl(*this); } RootWindowMac::~RootWindowMac() {} BrowserWindow* RootWindowMac::browser_window() const { return impl_->browser_window_.get(); } RootWindow::Delegate* RootWindowMac::delegate() const { return delegate_; } void RootWindowMac::Init(RootWindow::Delegate* delegate, std::unique_ptr config, const CefBrowserSettings& settings) { DCHECK(delegate); delegate_ = delegate; impl_->Init(delegate, std::move(config), settings); } void RootWindowMac::InitAsPopup(RootWindow::Delegate* delegate, bool with_controls, bool with_osr, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings) { DCHECK(delegate); delegate_ = delegate; impl_->InitAsPopup(delegate, with_controls, with_osr, popupFeatures, windowInfo, client, settings); } void RootWindowMac::Show(ShowMode mode) { impl_->Show(mode); } void RootWindowMac::Hide() { impl_->Hide(); } void RootWindowMac::SetBounds(int x, int y, size_t width, size_t height) { impl_->SetBounds(x, y, width, height); } void RootWindowMac::Close(bool force) { impl_->Close(force); } void RootWindowMac::SetDeviceScaleFactor(float device_scale_factor) { impl_->SetDeviceScaleFactor(device_scale_factor); } float RootWindowMac::GetDeviceScaleFactor() const { return impl_->GetDeviceScaleFactor(); } CefRefPtr RootWindowMac::GetBrowser() const { return impl_->GetBrowser(); } ClientWindowHandle RootWindowMac::GetWindowHandle() const { return impl_->GetWindowHandle(); } bool RootWindowMac::WithWindowlessRendering() const { return impl_->WithWindowlessRendering(); } bool RootWindowMac::WithExtension() const { return impl_->WithExtension(); } void RootWindowMac::OnBrowserCreated(CefRefPtr browser) { impl_->OnBrowserCreated(browser); } void RootWindowMac::OnBrowserWindowDestroyed() { impl_->OnBrowserWindowDestroyed(); } void RootWindowMac::OnSetAddress(const std::string& url) { impl_->OnSetAddress(url); } void RootWindowMac::OnSetTitle(const std::string& title) { impl_->OnSetTitle(title); } void RootWindowMac::OnSetFullscreen(bool fullscreen) { impl_->OnSetFullscreen(fullscreen); } void RootWindowMac::OnAutoResize(const CefSize& new_size) { impl_->OnAutoResize(new_size); } void RootWindowMac::OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward) { impl_->OnSetLoadingState(isLoading, canGoBack, canGoForward); } void RootWindowMac::OnSetDraggableRegions( const std::vector& regions) { impl_->OnSetDraggableRegions(regions); } void RootWindowMac::OnNativeWindowClosed() { impl_->OnNativeWindowClosed(); } // static scoped_refptr RootWindow::GetForNSWindow(NSWindow* window) { RootWindowDelegate* delegate = static_cast([window delegate]); return [delegate root_window]; } } // namespace client @implementation RootWindowDelegate @synthesize root_window = root_window_; @synthesize force_close = force_close_; - (id)initWithWindow:(NSWindow*)window andRootWindow:(client::RootWindowMac*)root_window { if (self = [super init]) { window_ = window; [window_ setDelegate:self]; root_window_ = root_window; force_close_ = false; // Register for application hide/unhide notifications. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidHide:) name:NSApplicationDidHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidUnhide:) name:NSApplicationDidUnhideNotification object:nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; #if !__has_feature(objc_arc) [super dealloc]; #endif // !__has_feature(objc_arc) } - (IBAction)goBack:(id)sender { CefRefPtr browser = root_window_->GetBrowser(); if (browser.get()) browser->GoBack(); } - (IBAction)goForward:(id)sender { CefRefPtr browser = root_window_->GetBrowser(); if (browser.get()) browser->GoForward(); } - (IBAction)reload:(id)sender { CefRefPtr browser = root_window_->GetBrowser(); if (browser.get()) browser->Reload(); } - (IBAction)stopLoading:(id)sender { CefRefPtr browser = root_window_->GetBrowser(); if (browser.get()) browser->StopLoad(); } - (IBAction)takeURLStringValueFrom:(NSTextField*)sender { CefRefPtr browser = root_window_->GetBrowser(); if (!browser.get()) return; NSString* url = [sender stringValue]; // if it doesn't already have a prefix, add http. If we can't parse it, // just don't bother rather than making things worse. NSURL* tempUrl = [NSURL URLWithString:url]; if (tempUrl && ![tempUrl scheme]) url = [@"http://" stringByAppendingString:url]; std::string urlStr = [url UTF8String]; browser->GetMainFrame()->LoadURL(urlStr); } // Called when we are activated (when we gain focus). - (void)windowDidBecomeKey:(NSNotification*)notification { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window) browser_window->SetFocus(true); root_window_->delegate()->OnRootWindowActivated(root_window_); } // Called when we are deactivated (when we lose focus). - (void)windowDidResignKey:(NSNotification*)notification { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window) browser_window->SetFocus(false); } // Called when we have been minimized. - (void)windowDidMiniaturize:(NSNotification*)notification { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window) browser_window->Hide(); } // Called when we have been unminimized. - (void)windowDidDeminiaturize:(NSNotification*)notification { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window) browser_window->Show(); } // Called when the application has been hidden. - (void)applicationDidHide:(NSNotification*)notification { // If the window is miniaturized then nothing has really changed. if (![window_ isMiniaturized]) { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window) browser_window->Hide(); } } // Called when the application has been unhidden. - (void)applicationDidUnhide:(NSNotification*)notification { // If the window is miniaturized then nothing has really changed. if (![window_ isMiniaturized]) { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window) browser_window->Show(); } } // Called when the window is about to close. Perform the self-destruction // sequence by getting rid of the window. By returning YES, we allow the window // to be removed from the screen. - (BOOL)windowShouldClose:(NSWindow*)window { if (!force_close_) { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window && !browser_window->IsClosing()) { CefRefPtr browser = browser_window->GetBrowser(); if (browser.get()) { // Notify the browser window that we would like to close it. This // will result in a call to ClientHandler::DoClose() if the // JavaScript 'onbeforeunload' event handler allows it. browser->GetHost()->CloseBrowser(false); // Cancel the close. return NO; } } } // Clean ourselves up after clearing the stack of anything that might have the // window on it. [self cleanup]; // Allow the close. return YES; } // Deletes itself. - (void)cleanup { // Don't want any more delegate callbacks after we destroy ourselves. window_.delegate = nil; window_.contentView = [[NSView alloc] initWithFrame:NSZeroRect]; // Delete the window. #if !__has_feature(objc_arc) [window_ autorelease]; #endif // !__has_feature(objc_arc) window_ = nil; root_window_->OnNativeWindowClosed(); } @end // @implementation RootWindowDelegate