922 lines
28 KiB
Plaintext
922 lines
28 KiB
Plaintext
// 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_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 <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,
|
|
std::unique_ptr<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_;
|
|
std::unique_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,
|
|
std::unique_ptr<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::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<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 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<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) {
|
|
std::unique_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,
|
|
std::unique_ptr<RootWindowConfig> 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<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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|