| // 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 <Cocoa/Cocoa.h> |
| |
| #include "include/base/cef_bind.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 <NSWindowDelegate> { |
| @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<RootWindowMacImpl, DeleteOnMainThread> { |
| 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, |
| const RootWindowConfig& config, |
| const CefBrowserSettings& settings); |
| void InitAsPopup(RootWindow::Delegate* delegate, |
| bool with_controls, |
| bool with_osr, |
| const CefPopupFeatures& popupFeatures, |
| CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient>& 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<CefBrowser> GetBrowser() const; |
| ClientWindowHandle GetWindowHandle() const; |
| bool WithWindowlessRendering() const; |
| bool WithExtension() const; |
| |
| // BrowserWindow::Delegate methods. |
| void OnBrowserCreated(CefRefPtr<CefBrowser> 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<CefDraggableRegion>& 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_; |
| scoped_ptr<BrowserWindow> 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, |
| const RootWindowConfig& 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::Bind(&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<CefClient>& 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<int>(width); |
| content_rect.size.height = |
| static_cast<int>(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<RootWindowDelegate*>([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<CefBrowser> RootWindowMacImpl::GetBrowser() const { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (browser_window_) |
| return browser_window_->GetBrowser(); |
| return NULL; |
| } |
| |
| 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, NULL, |
| 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<CefBrowser> 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<CefDraggableRegion>& 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<CefBrowser> browser = GetBrowser(); |
| if (browser) { |
| scoped_ptr<window_test::WindowTestRunnerMac> 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<int>(new_size.width); |
| content_rect.size.height = |
| static_cast<int>(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, |
| const RootWindowConfig& config, |
| const CefBrowserSettings& settings) { |
| DCHECK(delegate); |
| delegate_ = delegate; |
| impl_->Init(delegate, config, settings); |
| } |
| |
| void RootWindowMac::InitAsPopup(RootWindow::Delegate* delegate, |
| bool with_controls, |
| bool with_osr, |
| const CefPopupFeatures& popupFeatures, |
| CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient>& 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<CefBrowser> 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<CefBrowser> 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<CefDraggableRegion>& regions) { |
| impl_->OnSetDraggableRegions(regions); |
| } |
| |
| void RootWindowMac::OnNativeWindowClosed() { |
| impl_->OnNativeWindowClosed(); |
| } |
| |
| // static |
| scoped_refptr<RootWindow> RootWindow::GetForNSWindow(NSWindow* window) { |
| RootWindowDelegate* delegate = |
| static_cast<RootWindowDelegate*>([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<CefBrowser> browser = root_window_->GetBrowser(); |
| if (browser.get()) |
| browser->GoBack(); |
| } |
| |
| - (IBAction)goForward:(id)sender { |
| CefRefPtr<CefBrowser> browser = root_window_->GetBrowser(); |
| if (browser.get()) |
| browser->GoForward(); |
| } |
| |
| - (IBAction)reload:(id)sender { |
| CefRefPtr<CefBrowser> browser = root_window_->GetBrowser(); |
| if (browser.get()) |
| browser->Reload(); |
| } |
| |
| - (IBAction)stopLoading:(id)sender { |
| CefRefPtr<CefBrowser> browser = root_window_->GetBrowser(); |
| if (browser.get()) |
| browser->StopLoad(); |
| } |
| |
| - (IBAction)takeURLStringValueFrom:(NSTextField*)sender { |
| CefRefPtr<CefBrowser> 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<CefBrowser> 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; |
| } |
| } |
| } |
| |
| // Don't want any more delegate callbacks after we destroy ourselves. |
| window_.delegate = nil; |
| // Delete the window. |
| #if !__has_feature(objc_arc) |
| [window autorelease]; |
| #endif // !__has_feature(objc_arc) |
| window_ = nil; |
| |
| // 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 |