mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-01-28 01:59:25 +01:00
019611c764
Under ARC (Automatic Reference Counting), assigning to an Objective-C pointer has different semantics than assigning to a void* pointer. This makes it dangerous to treat the same memory address as an Objective-C pointer in some cases and as a "regular C pointer" in other cases. This change removes the conditional type defines and instead uses void* everywhere. Explicit type casting in combination with ARC annotations makes it safe to get typed Objective-C pointers from the void* pointers. This change enables ARC by default in the CEF binary distribution CMake configuration for the cefclient and cefsimple sample applications. It can be disabled by adding `-DOPTION_USE_ARC=Off` to the CMake command line. ARC is not supported when building Chromium due to the substantial number of changes that would be required in the Chromium code base.
930 lines
29 KiB
Plaintext
930 lines
29 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_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,
|
|
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
|