From 019611c7649ec982b86a0d8c6969e6dfbd37f2e9 Mon Sep 17 00:00:00 2001 From: Jesper Papmehl-Dufay Date: Tue, 23 Apr 2019 17:17:56 +0000 Subject: [PATCH] macOS: Add support for building clients with ARC enabled (fixes issue #2623). 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. --- include/internal/cef_types_mac.h | 38 +- .../browser_platform_delegate_native_mac.mm | 21 +- .../browser/native/file_dialog_runner_mac.mm | 2 +- tests/cefclient/CMakeLists.txt.in | 10 + .../browser/browser_window_osr_mac.h | 14 +- .../browser/browser_window_osr_mac.mm | 662 ++++++--- .../browser/browser_window_std_mac.mm | 4 +- tests/cefclient/browser/client_types.h | 13 +- .../browser/osr_accessibility_node.h | 19 +- .../browser/osr_accessibility_node_mac.mm | 31 +- tests/cefclient/browser/root_window.h | 6 +- tests/cefclient/browser/root_window_mac.h | 57 +- tests/cefclient/browser/root_window_mac.mm | 1266 ++++++++++------- tests/cefclient/browser/temp_window_mac.h | 4 +- tests/cefclient/browser/temp_window_mac.mm | 40 +- .../browser/window_test_runner_mac.mm | 3 +- tests/cefclient/cefclient_mac.mm | 96 +- tests/cefsimple/CMakeLists.txt.in | 10 + tests/cefsimple/cefsimple_mac.mm | 67 +- tests/cefsimple/simple_handler_mac.mm | 3 +- tests/ceftests/os_rendering_unittest_mac.mm | 2 +- .../main_message_loop_external_pump_mac.mm | 26 +- 22 files changed, 1414 insertions(+), 980 deletions(-) diff --git a/include/internal/cef_types_mac.h b/include/internal/cef_types_mac.h index 6cf350c48..14e61da44 100644 --- a/include/internal/cef_types_mac.h +++ b/include/internal/cef_types_mac.h @@ -37,29 +37,37 @@ #include "include/internal/cef_string.h" // Handle types. -#ifdef __cplusplus -#ifdef __OBJC__ -@class NSCursor; -@class NSEvent; -@class NSView; -#else -class NSCursor; -class NSEvent; -struct NSView; -#endif -#define cef_cursor_handle_t NSCursor* -#define cef_event_handle_t NSEvent* -#define cef_window_handle_t NSView* -#else +// Actually NSCursor* #define cef_cursor_handle_t void* +// Acutally NSEvent* #define cef_event_handle_t void* +// Actually NSView* #define cef_window_handle_t void* -#endif #define kNullCursorHandle NULL #define kNullEventHandle NULL #define kNullWindowHandle NULL +#ifdef __OBJC__ +#if __has_feature(objc_arc) +#define CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(handle) ((__bridge NSCursor*)handle) +#define CAST_CEF_EVENT_HANDLE_TO_NSEVENT(handle) ((__bridge NSEvent*)handle) +#define CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(handle) ((__bridge NSView*)handle) + +#define CAST_NSCURSOR_TO_CEF_CURSOR_HANDLE(cursor) ((__bridge void*)cursor) +#define CAST_NSEVENT_TO_CEF_EVENT_HANDLE(event) ((__bridge void*)event) +#define CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(view) ((__bridge void*)view) +#else // __has_feature(objc_arc) +#define CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(handle) ((NSCursor*)handle) +#define CAST_CEF_EVENT_HANDLE_TO_NSEVENT(handle) ((NSEvent*)handle) +#define CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(handle) ((NSView*)handle) + +#define CAST_NSCURSOR_TO_CEF_CURSOR_HANDLE(cursor) ((void*)cursor) +#define CAST_NSEVENT_TO_CEF_EVENT_HANDLE(event) ((void*)event) +#define CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(view) ((void*)view) +#endif // __has_feature(objc_arc) +#endif // __OBJC__ + #ifdef __cplusplus extern "C" { #endif diff --git a/libcef/browser/native/browser_platform_delegate_native_mac.mm b/libcef/browser/native/browser_platform_delegate_native_mac.mm index 548e1c1c0..8ae6ae2a0 100644 --- a/libcef/browser/native/browser_platform_delegate_native_mac.mm +++ b/libcef/browser/native/browser_platform_delegate_native_mac.mm @@ -56,7 +56,7 @@ @end // Receives notifications from the browser window. Will delete itself when done. -@interface CefWindowDelegate : NSObject { +@interface CefWindowDelegate : NSObject { @private CefBrowserHostImpl* browser_; // weak NSWindow* window_; @@ -165,7 +165,8 @@ bool CefBrowserPlatformDelegateNativeMac::CreateHostWindow() { NSWindow* newWnd = nil; - NSView* parentView = window_info_.parent_view; + NSView* parentView = + CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.parent_view); NSRect contentRect = {{window_info_.x, window_info_.y}, {window_info_.width, window_info_.height}}; if (parentView == nil) { @@ -239,11 +240,11 @@ bool CefBrowserPlatformDelegateNativeMac::CreateHostWindow() { } void CefBrowserPlatformDelegateNativeMac::CloseHostWindow() { - if (window_info_.view != nil) { - [[window_info_.view window] - performSelectorOnMainThread:@selector(performClose:) - withObject:nil - waitUntilDone:NO]; + NSView* nsview = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.view); + if (nsview != nil) { + [[nsview window] performSelectorOnMainThread:@selector(performClose:) + withObject:nil + waitUntilDone:NO]; } } @@ -275,7 +276,7 @@ gfx::Point CefBrowserPlatformDelegateNativeMac::GetScreenPoint( if (windowless_handler_) return windowless_handler_->GetParentScreenPoint(view); - NSView* nsview = window_info_.parent_view; + NSView* nsview = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_info_.parent_view); if (nsview) { NSRect bounds = [nsview bounds]; NSPoint view_pt = {view.x(), bounds.size.height - view.y()}; @@ -336,8 +337,8 @@ void CefBrowserPlatformDelegateNativeMac::TranslateKeyEvent( [[[NSString alloc] initWithCharacters:&key_event.unmodified_character length:1] autorelease]; NSString* characters = - [[[NSString alloc] initWithCharacters:&key_event.character length:1] - autorelease]; + [[[NSString alloc] initWithCharacters:&key_event.character + length:1] autorelease]; NSEvent* synthetic_event = [NSEvent keyEventWithType:event_type diff --git a/libcef/browser/native/file_dialog_runner_mac.mm b/libcef/browser/native/file_dialog_runner_mac.mm index 0a21fa8e9..fe88d9a41 100644 --- a/libcef/browser/native/file_dialog_runner_mac.mm +++ b/libcef/browser/native/file_dialog_runner_mac.mm @@ -387,7 +387,7 @@ void CefFileDialogRunnerMac::Run(CefBrowserHostImpl* browser, const FileChooserParams& params, RunFileChooserCallback callback) { int filter_index = params.selected_accept_filter; - NSView* owner = browser->GetWindowHandle(); + NSView* owner = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetWindowHandle()); if (params.mode == blink::mojom::FileChooserParams::Mode::kOpen || params.mode == blink::mojom::FileChooserParams::Mode::kOpenMultiple || diff --git a/tests/cefclient/CMakeLists.txt.in b/tests/cefclient/CMakeLists.txt.in index 2f12b0d37..c7ae75a7a 100644 --- a/tests/cefclient/CMakeLists.txt.in +++ b/tests/cefclient/CMakeLists.txt.in @@ -152,6 +152,16 @@ endif() # if(OS_MACOSX) + option(OPTION_USE_ARC "Build with ARC (automatic Reference Counting) on macOS." ON) + if(OPTION_USE_ARC) + list(APPEND CEF_COMPILER_FLAGS + -fobjc-arc + ) + set_target_properties(${target} PROPERTIES + CLANG_ENABLE_OBJC_ARC "YES" + ) + endif() + # All sources required by the "cefclient" target. Generates an app bundle that # is used only for the browser process. set(CEFCLIENT_SRCS diff --git a/tests/cefclient/browser/browser_window_osr_mac.h b/tests/cefclient/browser/browser_window_osr_mac.h index b718a203f..2562f77e6 100644 --- a/tests/cefclient/browser/browser_window_osr_mac.h +++ b/tests/cefclient/browser/browser_window_osr_mac.h @@ -13,6 +13,8 @@ namespace client { +class BrowserWindowOsrMacImpl; + // Represents a native child window hosting a single off-screen browser // instance. The methods of this class must be called on the main thread unless // otherwise indicated. @@ -88,17 +90,11 @@ class BrowserWindowOsrMac : public BrowserWindow, void UpdateAccessibilityLocation(CefRefPtr value) OVERRIDE; private: - // Create the NSView. - void Create(ClientWindowHandle parent_handle, const CefRect& rect); - - // The below members will only be accessed on the main thread which should be - // the same as the CEF UI thread. - OsrRenderer renderer_; - ClientWindowHandle nsview_; - bool hidden_; - bool painting_popup_; + scoped_ptr impl_; DISALLOW_COPY_AND_ASSIGN(BrowserWindowOsrMac); + + friend class BrowserWindowOsrMacImpl; }; } // namespace client diff --git a/tests/cefclient/browser/browser_window_osr_mac.mm b/tests/cefclient/browser/browser_window_osr_mac.mm index 9aecaed9c..c830e233c 100644 --- a/tests/cefclient/browser/browser_window_osr_mac.mm +++ b/tests/cefclient/browser/browser_window_osr_mac.mm @@ -21,17 +21,6 @@ #import -namespace { - -CefTextInputClientOSRMac* GetInputClientFromContext( - const NSTextInputContext* context) { - if (!context) - return NULL; - return reinterpret_cast([context client]); -} - -} // namespace - @interface BrowserOpenGLView : NSOpenGLView { @private @@ -51,10 +40,11 @@ CefTextInputClientOSRMac* GetInputClientFromContext( NSDragOperation current_drag_op_; NSDragOperation current_allowed_ops_; NSPasteboard* pasteboard_; - CFStringRef fileUTI_; + NSString* fileUTI_; // For intreacting with IME. NSTextInputContext* text_input_context_osr_mac_; + CefTextInputClientOSRMac* text_input_client_; // Manages Accessibility Tree client::OsrAccessibilityHelper* accessibility_helper_; @@ -63,54 +53,7 @@ CefTextInputClientOSRMac* GetInputClientFromContext( id endWheelMonitor_; } -- (id)initWithFrame:(NSRect)frame - andBrowserWindow:(client::BrowserWindowOsrMac*)browser_window - andRenderer:(client::OsrRenderer*)renderer; -- (void)detach; - -- (CefRefPtr)getBrowser; -- (NSPoint)getClickPointForEvent:(NSEvent*)event; -- (void)getKeyEvent:(CefKeyEvent&)keyEvent forEvent:(NSEvent*)event; -- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event; -- (void)getMouseEvent:(CefMouseEvent&)mouseEvent - forDragInfo:(id)info; -- (int)getModifiersForEvent:(NSEvent*)event; -- (BOOL)isKeyUpEvent:(NSEvent*)event; -- (BOOL)isKeyPadEvent:(NSEvent*)event; -- (BOOL)startDragging:(CefRefPtr)drag_data - allowedOps:(NSDragOperation)ops - point:(NSPoint)p; -- (void)setCurrentDragOp:(NSDragOperation)op; - -- (void)resetDragDrop; -- (void)fillPasteboard; -- (void)populateDropData:(CefRefPtr)data - fromPasteboard:(NSPasteboard*)pboard; -- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint; -- (void)resetDeviceScaleFactor; -- (void)setDeviceScaleFactor:(float)device_scale_factor; -- (float)getDeviceScaleFactor; -- (void)windowDidChangeBackingProperties:(NSNotification*)notification; - -- (bool)isOverPopupWidgetX:(int)x andY:(int)y; -- (void)applyPopupOffsetToX:(int&)x andY:(int&)y; -- (int)getPopupXOffset; -- (int)getPopupYOffset; - -- (void)sendMouseClick:(NSEvent*)event - button:(CefBrowserHost::MouseButtonType)type - isUp:(bool)isUp; - -- (NSPoint)convertPointFromBackingInternal:(NSPoint)aPoint; -- (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint; -- (NSRect)convertRectFromBackingInternal:(NSRect)aRect; -- (NSRect)convertRectToBackingInternal:(NSRect)aRect; -- (void)ChangeCompositionRange:(CefRange)range - character_bounds: - (const CefRenderHandler::RectList&)character_bounds; -- (void)UpdateAccessibilityTree:(CefRefPtr)value; -- (void)UpdateAccessibilityLocation:(CefRefPtr)value; -@end +@end // @interface BrowserOpenGLView namespace { @@ -135,10 +78,6 @@ class ScopedGLContext { const bool swap_buffers_; }; -BrowserOpenGLView* GLView(NSView* view) { - return static_cast(view); -} - NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { NSRect point_rect = NSMakeRect(point.x, point.y, 0, 0); return [window convertRectToScreen:point_rect].origin; @@ -155,7 +94,9 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { initWithAttributes:(NSOpenGLPixelFormatAttribute[]){ NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 32, 0}]; +#if !__has_feature(objc_arc) [pixelFormat autorelease]; +#endif // !__has_feature(objc_arc) if (self = [super initWithFrame:frame pixelFormat:pixelFormat]) { browser_window_ = browser_window; @@ -191,20 +132,20 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:nil]; - +#if !__has_feature(objc_arc) if (text_input_context_osr_mac_) { - [GetInputClientFromContext(text_input_context_osr_mac_) release]; + [text_input_client_ release]; [text_input_context_osr_mac_ release]; } - [super dealloc]; +#endif // !__has_feature(objc_arc) } - (void)detach { renderer_ = NULL; browser_window_ = NULL; - if (text_input_context_osr_mac_) - [GetInputClientFromContext(text_input_context_osr_mac_) detach]; + if (text_input_client_) + [text_input_client_ detach]; } - (CefRefPtr)getBrowser { @@ -344,11 +285,8 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { return; if ([event type] != NSFlagsChanged) { - CefTextInputClientOSRMac* client = - GetInputClientFromContext(text_input_context_osr_mac_); - - if (client) { - [client HandleKeyEventBeforeTextInputClient:event]; + if (text_input_client_) { + [text_input_client_ HandleKeyEventBeforeTextInputClient:event]; // The return value of this method seems to always be set to YES, thus we // ignore it and ask the host view whether IME is active or not. @@ -357,7 +295,7 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { CefKeyEvent keyEvent; [self getKeyEvent:keyEvent forEvent:event]; - [client HandleKeyEventAfterTextInputClient:keyEvent]; + [text_input_client_ HandleKeyEventAfterTextInputClient:keyEvent]; } } @@ -640,12 +578,14 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { - (NSTextInputContext*)inputContext { if (!text_input_context_osr_mac_) { - CefTextInputClientOSRMac* text_input_client = - [[[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]] - retain]; - + text_input_client_ = + [[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]]; text_input_context_osr_mac_ = - [[[NSTextInputContext alloc] initWithClient:text_input_client] retain]; + [[NSTextInputContext alloc] initWithClient:text_input_client_]; +#if !__has_feature(objc_arc) + [text_input_client_ retain]; + [text_input_context_osr_mac_ retain]; +#endif // !__has_feature(objc_arc) } return text_input_context_osr_mac_; @@ -1028,7 +968,7 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { [pboard setString:strTitle forType:kNSURLTitlePboardType]; // File contents. - } else if ([type isEqualToString:(NSString*)fileUTI_]) { + } else if ([type isEqualToString:fileUTI_]) { size_t size = current_drag_data_->GetFileContents(NULL); DCHECK_GT(size, 0U); CefRefPtr handler = @@ -1040,7 +980,7 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { [pboard setData:[NSData dataWithBytes:handler->GetData() length:handler->GetDataSize()] - forType:(NSString*)fileUTI_]; + forType:fileUTI_]; // Plain text. } else if ([type isEqualToString:NSStringPboardType]) { @@ -1084,7 +1024,8 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { client::OsrAXNode* node = accessibility_helper_->GetRootNode(); // Add Root as first Kid NSMutableArray* kids = [NSMutableArray arrayWithCapacity:1]; - NSObject* child = node->GetNativeAccessibleObject(NULL); + NSObject* child = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT( + node->GetNativeAccessibleObject(NULL)); [kids addObject:child]; return NSAccessibilityUnignoredChildren(kids); } else { @@ -1095,7 +1036,9 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { - (id)accessibilityFocusedUIElement { if (accessibility_helper_) { client::OsrAXNode* node = accessibility_helper_->GetFocusedNode(); - return node ? node->GetNativeAccessibleObject(NULL) : nil; + return node ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT( + node->GetNativeAccessibleObject(NULL)) + : nil; } return nil; } @@ -1106,18 +1049,25 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { current_allowed_ops_ = NSDragOperationNone; current_drag_data_ = NULL; if (fileUTI_) { - CFRelease(fileUTI_); - fileUTI_ = NULL; +#if !__has_feature(objc_arc) + [fileUTI_ release]; +#endif // !__has_feature(objc_arc) + fileUTI_ = nil; } if (pasteboard_) { +#if !__has_feature(objc_arc) [pasteboard_ release]; +#endif // !__has_feature(objc_arc) pasteboard_ = nil; } } - (void)fillPasteboard { DCHECK(!pasteboard_); - pasteboard_ = [[NSPasteboard pasteboardWithName:NSDragPboard] retain]; + pasteboard_ = [NSPasteboard pasteboardWithName:NSDragPboard]; +#if !__has_feature(objc_arc) + [pasteboard_ retain]; +#endif // !__has_feature(objc_arc) [pasteboard_ declareTypes:@[ kCEFDragDummyPboardType ] owner:self]; @@ -1146,11 +1096,11 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { mimeTypeCF = CFStringCreateWithCString(kCFAllocatorDefault, mimeType.ToString().c_str(), kCFStringEncodingUTF8); - fileUTI_ = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, - mimeTypeCF, NULL); + fileUTI_ = (__bridge NSString*)UTTypeCreatePreferredIdentifierForTag( + kUTTagClassMIMEType, mimeTypeCF, NULL); CFRelease(mimeTypeCF); // File (HFS) promise. - NSArray* fileUTIList = @[ (NSString*)fileUTI_ ]; + NSArray* fileUTIList = @[ fileUTI_ ]; [pasteboard_ addTypes:@[ NSFilesPromisePboardType ] owner:self]; [pasteboard_ setPropertyList:fileUTIList forType:NSFilesPromisePboardType]; @@ -1287,10 +1237,8 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { - (void)ChangeCompositionRange:(CefRange)range character_bounds:(const CefRenderHandler::RectList&)bounds { - CefTextInputClientOSRMac* client = - GetInputClientFromContext(text_input_context_osr_mac_); - if (client) - [client ChangeCompositionRange:range character_bounds:bounds]; + if (text_input_client_) + [text_input_client_ ChangeCompositionRange:range character_bounds:bounds]; } - (void)UpdateAccessibilityTree:(CefRefPtr)value { @@ -1323,25 +1271,106 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) { namespace client { -BrowserWindowOsrMac::BrowserWindowOsrMac(BrowserWindow::Delegate* delegate, - const std::string& startup_url, - const OsrRendererSettings& settings) - : BrowserWindow(delegate), - renderer_(settings), - nsview_(NULL), - hidden_(false), - painting_popup_(false) { - client_handler_ = new ClientHandlerOsr(this, this, startup_url); -} +class BrowserWindowOsrMacImpl { + public: + BrowserWindowOsrMacImpl(BrowserWindow::Delegate* delegate, + const std::string& startup_url, + const OsrRendererSettings& settings, + BrowserWindowOsrMac& browser_window); + ~BrowserWindowOsrMacImpl(); -BrowserWindowOsrMac::~BrowserWindowOsrMac() { - if (nsview_) { + // BrowserWindow methods. + void CreateBrowser(ClientWindowHandle parent_handle, + const CefRect& rect, + const CefBrowserSettings& settings, + CefRefPtr request_context); + void GetPopupConfig(CefWindowHandle temp_handle, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings); + void ShowPopup(ClientWindowHandle parent_handle, + int x, + int y, + size_t width, + size_t height); + void Show(); + void Hide(); + void SetBounds(int x, int y, size_t width, size_t height); + void SetFocus(bool focus); + void SetDeviceScaleFactor(float device_scale_factor); + float GetDeviceScaleFactor() const; + ClientWindowHandle GetWindowHandle() const; + + // ClientHandlerOsr::OsrDelegate methods. + void OnAfterCreated(CefRefPtr browser); + void OnBeforeClose(CefRefPtr browser); + bool GetRootScreenRect(CefRefPtr browser, CefRect& rect); + void GetViewRect(CefRefPtr browser, CefRect& rect); + bool GetScreenPoint(CefRefPtr browser, + int viewX, + int viewY, + int& screenX, + int& screenY); + bool GetScreenInfo(CefRefPtr browser, CefScreenInfo& screen_info); + void OnPopupShow(CefRefPtr browser, bool show); + void OnPopupSize(CefRefPtr browser, const CefRect& rect); + void OnPaint(CefRefPtr browser, + CefRenderHandler::PaintElementType type, + const CefRenderHandler::RectList& dirtyRects, + const void* buffer, + int width, + int height); + void OnCursorChange(CefRefPtr browser, + CefCursorHandle cursor, + CefRenderHandler::CursorType type, + const CefCursorInfo& custom_cursor_info); + bool StartDragging(CefRefPtr browser, + CefRefPtr drag_data, + CefRenderHandler::DragOperationsMask allowed_ops, + int x, + int y); + void UpdateDragCursor(CefRefPtr browser, + CefRenderHandler::DragOperation operation); + void OnImeCompositionRangeChanged( + CefRefPtr browser, + const CefRange& selection_range, + const CefRenderHandler::RectList& character_bounds); + + void UpdateAccessibilityTree(CefRefPtr value); + void UpdateAccessibilityLocation(CefRefPtr value); + + private: + // Create the NSView. + void Create(ClientWindowHandle parent_handle, const CefRect& rect); + + BrowserWindowOsrMac& browser_window_; + // The below members will only be accessed on the main thread which should be + // the same as the CEF UI thread. + OsrRenderer renderer_; + BrowserOpenGLView* native_browser_view_; + bool hidden_; + bool painting_popup_; +}; + +BrowserWindowOsrMacImpl::BrowserWindowOsrMacImpl( + BrowserWindow::Delegate* delegate, + const std::string& startup_url, + const OsrRendererSettings& settings, + BrowserWindowOsrMac& browser_window) + : browser_window_(browser_window), + renderer_(settings), + native_browser_view_(nil), + hidden_(false), + painting_popup_(false) {} + +BrowserWindowOsrMacImpl::~BrowserWindowOsrMacImpl() { + if (native_browser_view_) { // Disassociate the view with |this|. - [GLView(nsview_) detach]; + [native_browser_view_ detach]; } } -void BrowserWindowOsrMac::CreateBrowser( +void BrowserWindowOsrMacImpl::CreateBrowser( ClientWindowHandle parent_handle, const CefRect& rect, const CefBrowserSettings& settings, @@ -1352,31 +1381,32 @@ void BrowserWindowOsrMac::CreateBrowser( Create(parent_handle, rect); CefWindowInfo window_info; - window_info.SetAsWindowless(nsview_); + window_info.SetAsWindowless( + CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_)); // Create the browser asynchronously. - CefBrowserHost::CreateBrowser(window_info, client_handler_, - client_handler_->startup_url(), settings, - request_context); + CefBrowserHost::CreateBrowser(window_info, browser_window_.client_handler_, + browser_window_.client_handler_->startup_url(), + settings, request_context); } -void BrowserWindowOsrMac::GetPopupConfig(CefWindowHandle temp_handle, - CefWindowInfo& windowInfo, - CefRefPtr& client, - CefBrowserSettings& settings) { +void BrowserWindowOsrMacImpl::GetPopupConfig(CefWindowHandle temp_handle, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings) { CEF_REQUIRE_UI_THREAD(); windowInfo.SetAsWindowless(temp_handle); - client = client_handler_; + client = browser_window_.client_handler_; } -void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle, - int x, - int y, - size_t width, - size_t height) { +void BrowserWindowOsrMacImpl::ShowPopup(ClientWindowHandle parent_handle, + int x, + int y, + size_t width, + size_t height) { REQUIRE_MAIN_THREAD(); - DCHECK(browser_.get()); + DCHECK(browser_window_.browser_.get()); // Create the native NSView. Create(parent_handle, @@ -1384,107 +1414,111 @@ void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle, // Send resize notification so the compositor is assigned the correct // viewport size and begins rendering. - browser_->GetHost()->WasResized(); + browser_window_.browser_->GetHost()->WasResized(); Show(); } -void BrowserWindowOsrMac::Show() { +void BrowserWindowOsrMacImpl::Show() { REQUIRE_MAIN_THREAD(); if (hidden_) { // Set the browser as visible. - browser_->GetHost()->WasHidden(false); + browser_window_.browser_->GetHost()->WasHidden(false); hidden_ = false; } // Give focus to the browser. - browser_->GetHost()->SendFocusEvent(true); + browser_window_.browser_->GetHost()->SendFocusEvent(true); } -void BrowserWindowOsrMac::Hide() { +void BrowserWindowOsrMacImpl::Hide() { REQUIRE_MAIN_THREAD(); - if (!browser_.get()) + if (!browser_window_.browser_.get()) return; // Remove focus from the browser. - browser_->GetHost()->SendFocusEvent(false); + browser_window_.browser_->GetHost()->SendFocusEvent(false); if (!hidden_) { // Set the browser as hidden. - browser_->GetHost()->WasHidden(true); + browser_window_.browser_->GetHost()->WasHidden(true); hidden_ = true; } } -void BrowserWindowOsrMac::SetBounds(int x, int y, size_t width, size_t height) { +void BrowserWindowOsrMacImpl::SetBounds(int x, + int y, + size_t width, + size_t height) { REQUIRE_MAIN_THREAD(); // Nothing to do here. GTK will take care of positioning in the container. } -void BrowserWindowOsrMac::SetFocus(bool focus) { +void BrowserWindowOsrMacImpl::SetFocus(bool focus) { REQUIRE_MAIN_THREAD(); - if (nsview_) - [[nsview_ window] makeFirstResponder:nsview_]; + if (native_browser_view_) + [native_browser_view_.window makeFirstResponder:native_browser_view_]; } -void BrowserWindowOsrMac::SetDeviceScaleFactor(float device_scale_factor) { +void BrowserWindowOsrMacImpl::SetDeviceScaleFactor(float device_scale_factor) { REQUIRE_MAIN_THREAD(); - if (nsview_) - [GLView(nsview_) setDeviceScaleFactor:device_scale_factor]; + if (native_browser_view_) + [native_browser_view_ setDeviceScaleFactor:device_scale_factor]; } -float BrowserWindowOsrMac::GetDeviceScaleFactor() const { +float BrowserWindowOsrMacImpl::GetDeviceScaleFactor() const { REQUIRE_MAIN_THREAD(); - if (nsview_) - return [GLView(nsview_) getDeviceScaleFactor]; + if (native_browser_view_) + return [native_browser_view_ getDeviceScaleFactor]; return 1.0f; } -ClientWindowHandle BrowserWindowOsrMac::GetWindowHandle() const { +ClientWindowHandle BrowserWindowOsrMacImpl::GetWindowHandle() const { REQUIRE_MAIN_THREAD(); - return nsview_; + return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_); } -void BrowserWindowOsrMac::OnAfterCreated(CefRefPtr browser) { +void BrowserWindowOsrMacImpl::OnAfterCreated(CefRefPtr browser) { CEF_REQUIRE_UI_THREAD(); } -void BrowserWindowOsrMac::OnBeforeClose(CefRefPtr browser) { +void BrowserWindowOsrMacImpl::OnBeforeClose(CefRefPtr browser) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); // Detach |this| from the ClientHandlerOsr. - static_cast(client_handler_.get())->DetachOsrDelegate(); + static_cast(browser_window_.client_handler_.get()) + ->DetachOsrDelegate(); } -bool BrowserWindowOsrMac::GetRootScreenRect(CefRefPtr browser, - CefRect& rect) { +bool BrowserWindowOsrMacImpl::GetRootScreenRect(CefRefPtr browser, + CefRect& rect) { CEF_REQUIRE_UI_THREAD(); return false; } -void BrowserWindowOsrMac::GetViewRect(CefRefPtr browser, - CefRect& rect) { +void BrowserWindowOsrMacImpl::GetViewRect(CefRefPtr browser, + CefRect& rect) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); rect.x = rect.y = 0; - if (!nsview_) { + if (!native_browser_view_) { // Never return an empty rectangle. rect.width = rect.height = 1; return; } - const float device_scale_factor = [GLView(nsview_) getDeviceScaleFactor]; + const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; // |bounds| is in OS X view coordinates. - NSRect bounds = [nsview_ bounds]; + NSRect bounds = native_browser_view_.bounds; // Convert to device coordinates. - bounds = [GLView(nsview_) convertRectToBackingInternal:bounds]; + bounds = [native_browser_view_ convertRectToBackingInternal:bounds]; // Convert to browser view coordinates. rect.width = DeviceToLogical(bounds.size.width, device_scale_factor); @@ -1495,18 +1529,18 @@ void BrowserWindowOsrMac::GetViewRect(CefRefPtr browser, rect.height = 1; } -bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr browser, - int viewX, - int viewY, - int& screenX, - int& screenY) { +bool BrowserWindowOsrMacImpl::GetScreenPoint(CefRefPtr browser, + int viewX, + int viewY, + int& screenX, + int& screenY) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - if (!nsview_) + if (!native_browser_view_) return false; - const float device_scale_factor = [GLView(nsview_) getDeviceScaleFactor]; + const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; // (viewX, viewX) is in browser view coordinates. // Convert to device coordinates. @@ -1514,34 +1548,34 @@ bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr browser, LogicalToDevice(viewY, device_scale_factor)); // Convert to OS X view coordinates. - view_pt = [GLView(nsview_) convertPointFromBackingInternal:view_pt]; + view_pt = [native_browser_view_ convertPointFromBackingInternal:view_pt]; // Reverse the Y component. - const NSRect bounds = [nsview_ bounds]; + const NSRect bounds = native_browser_view_.bounds; view_pt.y = bounds.size.height - view_pt.y; // Convert to screen coordinates. - NSPoint window_pt = [nsview_ convertPoint:view_pt toView:nil]; + NSPoint window_pt = [native_browser_view_ convertPoint:view_pt toView:nil]; NSPoint screen_pt = - ConvertPointFromWindowToScreen([nsview_ window], window_pt); + ConvertPointFromWindowToScreen(native_browser_view_.window, window_pt); screenX = screen_pt.x; screenY = screen_pt.y; return true; } -bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr browser, - CefScreenInfo& screen_info) { +bool BrowserWindowOsrMacImpl::GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - if (!nsview_) + if (!native_browser_view_) return false; CefRect view_rect; GetViewRect(browser, view_rect); - screen_info.device_scale_factor = [GLView(nsview_) getDeviceScaleFactor]; + screen_info.device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; // The screen info rectangles are used by the renderer to create and position // popups. Keep popups inside the view rectangle. @@ -1551,12 +1585,12 @@ bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr browser, return true; } -void BrowserWindowOsrMac::OnPopupShow(CefRefPtr browser, - bool show) { +void BrowserWindowOsrMacImpl::OnPopupShow(CefRefPtr browser, + bool show) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - if (!nsview_) + if (!native_browser_view_) return; if (!show) { @@ -1566,15 +1600,15 @@ void BrowserWindowOsrMac::OnPopupShow(CefRefPtr browser, renderer_.OnPopupShow(browser, show); } -void BrowserWindowOsrMac::OnPopupSize(CefRefPtr browser, - const CefRect& rect) { +void BrowserWindowOsrMacImpl::OnPopupSize(CefRefPtr browser, + const CefRect& rect) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - if (!nsview_) + if (!native_browser_view_) return; - const float device_scale_factor = [GLView(nsview_) getDeviceScaleFactor]; + const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor]; // |rect| is in browser view coordinates. Convert to device coordinates. CefRect device_rect = LogicalToDevice(rect, device_scale_factor); @@ -1582,16 +1616,17 @@ void BrowserWindowOsrMac::OnPopupSize(CefRefPtr browser, renderer_.OnPopupSize(browser, device_rect); } -void BrowserWindowOsrMac::OnPaint(CefRefPtr browser, - CefRenderHandler::PaintElementType type, - const CefRenderHandler::RectList& dirtyRects, - const void* buffer, - int width, - int height) { +void BrowserWindowOsrMacImpl::OnPaint( + CefRefPtr browser, + CefRenderHandler::PaintElementType type, + const CefRenderHandler::RectList& dirtyRects, + const void* buffer, + int width, + int height) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - if (!nsview_) + if (!native_browser_view_) return; if (width <= 2 && height <= 2) { @@ -1604,7 +1639,7 @@ void BrowserWindowOsrMac::OnPaint(CefRefPtr browser, return; } - ScopedGLContext scoped_gl_context(GLView(nsview_), true); + ScopedGLContext scoped_gl_context(native_browser_view_, true); renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height); if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) { @@ -1615,7 +1650,7 @@ void BrowserWindowOsrMac::OnPaint(CefRefPtr browser, renderer_.Render(); } -void BrowserWindowOsrMac::OnCursorChange( +void BrowserWindowOsrMacImpl::OnCursorChange( CefRefPtr browser, CefCursorHandle cursor, CefRenderHandler::CursorType type, @@ -1623,10 +1658,10 @@ void BrowserWindowOsrMac::OnCursorChange( CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - [cursor set]; + [CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(cursor) set]; } -bool BrowserWindowOsrMac::StartDragging( +bool BrowserWindowOsrMacImpl::StartDragging( CefRefPtr browser, CefRefPtr drag_data, CefRenderHandler::DragOperationsMask allowed_ops, @@ -1635,10 +1670,11 @@ bool BrowserWindowOsrMac::StartDragging( CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - if (!nsview_) + if (!native_browser_view_) return false; - static float device_scale_factor = [GLView(nsview_) getDeviceScaleFactor]; + static float device_scale_factor = + [native_browser_view_ getDeviceScaleFactor]; // |point| is in browser view coordinates. NSPoint point = NSMakePoint(x, y); @@ -1648,74 +1684,230 @@ bool BrowserWindowOsrMac::StartDragging( point.y = LogicalToDevice(point.y, device_scale_factor); // Convert to OS X view coordinates. - point = [GLView(nsview_) convertPointFromBackingInternal:point]; + point = [native_browser_view_ convertPointFromBackingInternal:point]; - return - [GLView(nsview_) startDragging:drag_data - allowedOps:static_cast(allowed_ops) - point:point]; + return [native_browser_view_ + startDragging:drag_data + allowedOps:static_cast(allowed_ops) + point:point]; } -void BrowserWindowOsrMac::UpdateDragCursor( +void BrowserWindowOsrMacImpl::UpdateDragCursor( CefRefPtr browser, CefRenderHandler::DragOperation operation) { CEF_REQUIRE_UI_THREAD(); REQUIRE_MAIN_THREAD(); - if (nsview_) - [GLView(nsview_) setCurrentDragOp:operation]; + if (native_browser_view_) + [native_browser_view_ setCurrentDragOp:operation]; } -void BrowserWindowOsrMac::OnImeCompositionRangeChanged( +void BrowserWindowOsrMacImpl::OnImeCompositionRangeChanged( CefRefPtr browser, const CefRange& selection_range, const CefRenderHandler::RectList& bounds) { CEF_REQUIRE_UI_THREAD(); - if (nsview_) { - [GLView(nsview_) ChangeCompositionRange:selection_range - character_bounds:bounds]; + if (native_browser_view_) { + [native_browser_view_ ChangeCompositionRange:selection_range + character_bounds:bounds]; } } +void BrowserWindowOsrMacImpl::UpdateAccessibilityTree( + CefRefPtr value) { + CEF_REQUIRE_UI_THREAD(); + + if (native_browser_view_) { + [native_browser_view_ UpdateAccessibilityTree:value]; + } +} + +void BrowserWindowOsrMacImpl::UpdateAccessibilityLocation( + CefRefPtr value) { + CEF_REQUIRE_UI_THREAD(); + + if (native_browser_view_) { + [native_browser_view_ UpdateAccessibilityLocation:value]; + } +} + +void BrowserWindowOsrMacImpl::Create(ClientWindowHandle parent_handle, + const CefRect& rect) { + REQUIRE_MAIN_THREAD(); + DCHECK(!native_browser_view_); + + NSRect window_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); + native_browser_view_ = + [[BrowserOpenGLView alloc] initWithFrame:window_rect + andBrowserWindow:&browser_window_ + andRenderer:&renderer_]; + native_browser_view_.autoresizingMask = + (NSViewWidthSizable | NSViewHeightSizable); + native_browser_view_.autoresizesSubviews = YES; + [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle) + addSubview:native_browser_view_]; + + // Determine the default scale factor. + [native_browser_view_ resetDeviceScaleFactor]; + + [[NSNotificationCenter defaultCenter] + addObserver:native_browser_view_ + selector:@selector(windowDidChangeBackingProperties:) + name:NSWindowDidChangeBackingPropertiesNotification + object:native_browser_view_.window]; +} + +BrowserWindowOsrMac::BrowserWindowOsrMac(BrowserWindow::Delegate* delegate, + const std::string& startup_url, + const OsrRendererSettings& settings) + : BrowserWindow(delegate) { + client_handler_ = new ClientHandlerOsr(this, this, startup_url); + impl_.reset( + new BrowserWindowOsrMacImpl(delegate, startup_url, settings, *this)); +} + +BrowserWindowOsrMac::~BrowserWindowOsrMac() {} + +void BrowserWindowOsrMac::CreateBrowser( + ClientWindowHandle parent_handle, + const CefRect& rect, + const CefBrowserSettings& settings, + CefRefPtr request_context) { + impl_->CreateBrowser(parent_handle, rect, settings, request_context); +} + +void BrowserWindowOsrMac::GetPopupConfig(CefWindowHandle temp_handle, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings) { + impl_->GetPopupConfig(temp_handle, windowInfo, client, settings); +} + +void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle, + int x, + int y, + size_t width, + size_t height) { + impl_->ShowPopup(parent_handle, x, y, width, height); +} + +void BrowserWindowOsrMac::Show() { + impl_->Show(); +} + +void BrowserWindowOsrMac::Hide() { + impl_->Hide(); +} + +void BrowserWindowOsrMac::SetBounds(int x, int y, size_t width, size_t height) { + impl_->SetBounds(x, y, width, height); +} + +void BrowserWindowOsrMac::SetFocus(bool focus) { + impl_->SetFocus(focus); +} + +void BrowserWindowOsrMac::SetDeviceScaleFactor(float device_scale_factor) { + impl_->SetDeviceScaleFactor(device_scale_factor); +} + +float BrowserWindowOsrMac::GetDeviceScaleFactor() const { + return impl_->GetDeviceScaleFactor(); +} + +ClientWindowHandle BrowserWindowOsrMac::GetWindowHandle() const { + return impl_->GetWindowHandle(); +} + +void BrowserWindowOsrMac::OnAfterCreated(CefRefPtr browser) { + impl_->OnAfterCreated(browser); +} + +void BrowserWindowOsrMac::OnBeforeClose(CefRefPtr browser) { + impl_->OnBeforeClose(browser); +} + +bool BrowserWindowOsrMac::GetRootScreenRect(CefRefPtr browser, + CefRect& rect) { + return impl_->GetRootScreenRect(browser, rect); +} + +void BrowserWindowOsrMac::GetViewRect(CefRefPtr browser, + CefRect& rect) { + impl_->GetViewRect(browser, rect); +} + +bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr browser, + int viewX, + int viewY, + int& screenX, + int& screenY) { + return impl_->GetScreenPoint(browser, viewX, viewY, screenX, screenY); +} + +bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) { + return impl_->GetScreenInfo(browser, screen_info); +} + +void BrowserWindowOsrMac::OnPopupShow(CefRefPtr browser, + bool show) { + impl_->OnPopupShow(browser, show); +} + +void BrowserWindowOsrMac::OnPopupSize(CefRefPtr browser, + const CefRect& rect) { + impl_->OnPopupSize(browser, rect); +} + +void BrowserWindowOsrMac::OnPaint(CefRefPtr browser, + CefRenderHandler::PaintElementType type, + const CefRenderHandler::RectList& dirtyRects, + const void* buffer, + int width, + int height) { + impl_->OnPaint(browser, type, dirtyRects, buffer, width, height); +} + +void BrowserWindowOsrMac::OnCursorChange( + CefRefPtr browser, + CefCursorHandle cursor, + CefRenderHandler::CursorType type, + const CefCursorInfo& custom_cursor_info) { + impl_->OnCursorChange(browser, cursor, type, custom_cursor_info); +} + +bool BrowserWindowOsrMac::StartDragging( + CefRefPtr browser, + CefRefPtr drag_data, + CefRenderHandler::DragOperationsMask allowed_ops, + int x, + int y) { + return impl_->StartDragging(browser, drag_data, allowed_ops, x, y); +} + +void BrowserWindowOsrMac::UpdateDragCursor( + CefRefPtr browser, + CefRenderHandler::DragOperation operation) { + impl_->UpdateDragCursor(browser, operation); +} + +void BrowserWindowOsrMac::OnImeCompositionRangeChanged( + CefRefPtr browser, + const CefRange& selection_range, + const CefRenderHandler::RectList& character_bounds) { + impl_->OnImeCompositionRangeChanged(browser, selection_range, + character_bounds); +} + void BrowserWindowOsrMac::UpdateAccessibilityTree(CefRefPtr value) { - CEF_REQUIRE_UI_THREAD(); - - if (nsview_) { - [GLView(nsview_) UpdateAccessibilityTree:value]; - } + impl_->UpdateAccessibilityTree(value); } void BrowserWindowOsrMac::UpdateAccessibilityLocation( - scoped_refptr value) { - CEF_REQUIRE_UI_THREAD(); - - if (nsview_) { - [GLView(nsview_) UpdateAccessibilityLocation:value]; - } -} - -void BrowserWindowOsrMac::Create(ClientWindowHandle parent_handle, - const CefRect& rect) { - REQUIRE_MAIN_THREAD(); - DCHECK(!nsview_); - - NSRect window_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); - nsview_ = [[BrowserOpenGLView alloc] initWithFrame:window_rect - andBrowserWindow:this - andRenderer:&renderer_]; - [nsview_ setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; - [nsview_ setAutoresizesSubviews:true]; - [parent_handle addSubview:nsview_]; - - // Determine the default scale factor. - [GLView(nsview_) resetDeviceScaleFactor]; - - [[NSNotificationCenter defaultCenter] - addObserver:nsview_ - selector:@selector(windowDidChangeBackingProperties:) - name:NSWindowDidChangeBackingPropertiesNotification - object:[nsview_ window]]; + CefRefPtr value) { + impl_->UpdateAccessibilityLocation(value); } } // namespace client diff --git a/tests/cefclient/browser/browser_window_std_mac.mm b/tests/cefclient/browser/browser_window_std_mac.mm index cc19f5268..f83512457 100644 --- a/tests/cefclient/browser/browser_window_std_mac.mm +++ b/tests/cefclient/browser/browser_window_std_mac.mm @@ -52,11 +52,11 @@ void BrowserWindowStdMac::ShowPopup(ClientWindowHandle parent_handle, size_t height) { REQUIRE_MAIN_THREAD(); - NSView* browser_view = GetWindowHandle(); + NSView* browser_view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(GetWindowHandle()); // Re-parent |browser_view| to |parent_handle|. [browser_view removeFromSuperview]; - [parent_handle addSubview:browser_view]; + [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle) addSubview:browser_view]; NSSize size = NSMakeSize(static_cast(width), static_cast(height)); [browser_view setFrameSize:size]; diff --git a/tests/cefclient/browser/client_types.h b/tests/cefclient/browser/client_types.h index d26a1b4b3..2eefd1901 100644 --- a/tests/cefclient/browser/client_types.h +++ b/tests/cefclient/browser/client_types.h @@ -18,15 +18,12 @@ #endif #if defined(OS_MACOSX) -// Forward declaration of ObjC types used by cefclient and not provided by -// include/internal/cef_types_mac.h. -#ifdef __cplusplus +#define ClientNativeMacWindow void* #ifdef __OBJC__ -@class NSWindow; -#else -class NSWindow; -#endif -#endif +#define CAST_CLIENT_NATIVE_MAC_WINDOW_TO_NSWINDOW(native) \ + (__bridge NSWindow*)native +#define CAST_NSWINDOW_TO_CLIENT_NATIVE_MAC_WINDOW(window) (__bridge void*)window +#endif // __OBJC__ #endif // defined OS_MACOSX #endif // CEF_TESTS_CEFCLIENT_BROWSER_CLIENT_TYPES_H_ diff --git a/tests/cefclient/browser/osr_accessibility_node.h b/tests/cefclient/browser/osr_accessibility_node.h index 329cce408..7c815a48d 100644 --- a/tests/cefclient/browser/osr_accessibility_node.h +++ b/tests/cefclient/browser/osr_accessibility_node.h @@ -11,12 +11,19 @@ #include "include/cef_browser.h" #if defined(OS_MACOSX) -#ifdef __OBJC__ -@class NSObject; -#else -class NSObject; -#endif -typedef NSObject CefNativeAccessible; +typedef void CefNativeAccessible; +#if __OBJC__ +#if __has_feature(objc_arc) +#define CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(accessible) \ + (__bridge NSObject*)accessible +#define CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(object) \ + (__bridge CefNativeAccessible*)object +#else // __has_feature(objc_arc) +#define CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(accessible) (NSObject*)accessible +#define CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(object) \ + (__bridge CefNativeAccessible*)object +#endif // __has_feature(objc_arc) +#endif // __OBJC__ #elif defined(OS_WIN) struct IAccessible; typedef IAccessible CefNativeAccessible; diff --git a/tests/cefclient/browser/osr_accessibility_node_mac.mm b/tests/cefclient/browser/osr_accessibility_node_mac.mm index fbbb01cd2..2fbb8da1c 100644 --- a/tests/cefclient/browser/osr_accessibility_node_mac.mm +++ b/tests/cefclient/browser/osr_accessibility_node_mac.mm @@ -320,7 +320,9 @@ inline int MiddleY(const CefRect& rect) { NSMutableArray* kids = [NSMutableArray arrayWithCapacity:numChilds]; for (int index = 0; index < numChilds; index++) { client::OsrAXNode* child = node_->ChildAtIndex(index); - [kids addObject:child ? child->GetNativeAccessibleObject(node_) : nil]; + [kids addObject:child ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT( + child->GetNativeAccessibleObject(node_)) + : nil]; } return kids; } @@ -332,7 +334,7 @@ inline int MiddleY(const CefRect& rect) { NSPoint origin = NSMakePoint(cef_rect.x, cef_rect.y); NSSize size = NSMakeSize(cef_rect.width, cef_rect.height); - NSView* view = node_->GetWindowHandle(); + NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(node_->GetWindowHandle()); origin.y = NSHeight([view bounds]) - origin.y; NSPoint originInWindow = [view convertPoint:origin toView:nil]; @@ -348,7 +350,7 @@ inline int MiddleY(const CefRect& rect) { CefRect cef_rect = node_->AxLocation(); NSRect rect = NSMakeRect(cef_rect.x, cef_rect.y, cef_rect.width, cef_rect.height); - NSView* view = node_->GetWindowHandle(); + NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(node_->GetWindowHandle()); rect = [[view window] convertRectToScreen:rect]; return rect.size; } @@ -384,6 +386,7 @@ inline int MiddleY(const CefRect& rect) { } - (id)accessibilityAttributeValue:(NSString*)attribute { + NSObject* typed_parent = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(parent_); if (!node_) return nil; if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { @@ -397,16 +400,17 @@ inline int MiddleY(const CefRect& rect) { accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute]; return [NSNumber numberWithBool:[focusedElement isEqual:self]]; } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) { - return NSAccessibilityUnignoredAncestor(parent_); + return NSAccessibilityUnignoredAncestor(typed_parent); } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { return NSAccessibilityUnignoredChildren([self getKids]); } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) { // We're in the same window as our parent. - return [parent_ accessibilityAttributeValue:NSAccessibilityWindowAttribute]; + return [typed_parent + accessibilityAttributeValue:NSAccessibilityWindowAttribute]; } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) { // We're in the same top level element as our parent. - return [parent_ + return [typed_parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) { return [NSValue valueWithPoint:[self position]]; @@ -460,17 +464,18 @@ inline int MiddleY(const CefRect& rect) { namespace client { void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const { + NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(GetWindowHandle()); if (event_type == "focus") { NSAccessibilityPostNotification( - GetWindowHandle(), NSAccessibilityFocusedUIElementChangedNotification); + view, NSAccessibilityFocusedUIElementChangedNotification); } else if (event_type == "textChanged") { - NSAccessibilityPostNotification(GetWindowHandle(), + NSAccessibilityPostNotification(view, NSAccessibilityTitleChangedNotification); } else if (event_type == "valueChanged") { - NSAccessibilityPostNotification(GetWindowHandle(), + NSAccessibilityPostNotification(view, NSAccessibilityValueChangedNotification); } else if (event_type == "textSelectionChanged") { - NSAccessibilityPostNotification(GetWindowHandle(), + NSAccessibilityPostNotification(view, NSAccessibilityValueChangedNotification); } } @@ -478,7 +483,8 @@ void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const { void OsrAXNode::Destroy() { if (platform_accessibility_) { NSAccessibilityPostNotification( - platform_accessibility_, NSAccessibilityUIElementDestroyedNotification); + CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(platform_accessibility_), + NSAccessibilityUIElementDestroyedNotification); } delete this; @@ -488,7 +494,8 @@ void OsrAXNode::Destroy() { CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject( client::OsrAXNode* parent) { if (!platform_accessibility_) { - platform_accessibility_ = [OsrAXNodeObject elementWithNode:this]; + platform_accessibility_ = CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE( + [OsrAXNodeObject elementWithNode:this]); SetParent(parent); } return platform_accessibility_; diff --git a/tests/cefclient/browser/root_window.h b/tests/cefclient/browser/root_window.h index 584449a46..b73fc514a 100644 --- a/tests/cefclient/browser/root_window.h +++ b/tests/cefclient/browser/root_window.h @@ -17,6 +17,10 @@ #include "tests/cefclient/browser/image_cache.h" #include "tests/shared/browser/main_message_loop.h" +#if defined(OS_MACOSX) && __OBJC__ +@class NSWindow; +#endif // defined(OS_MACOSX) && __OBJC__ + namespace client { // Used to configure how a RootWindow is created. @@ -117,7 +121,7 @@ class RootWindow // called on the main thread. static scoped_refptr GetForBrowser(int browser_id); -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) && __OBJC__ // Returns the RootWindow associated with the specified |window|. Must be // called on the main thread. static scoped_refptr GetForNSWindow(NSWindow* window); diff --git a/tests/cefclient/browser/root_window_mac.h b/tests/cefclient/browser/root_window_mac.h index 4f869d21f..ddd76ba6d 100644 --- a/tests/cefclient/browser/root_window_mac.h +++ b/tests/cefclient/browser/root_window_mac.h @@ -12,18 +12,10 @@ #include "tests/cefclient/browser/browser_window.h" #include "tests/cefclient/browser/root_window.h" -#ifdef __OBJC__ -@class NSWindow; -@class NSButton; -@class NSTextField; -#else -class NSWindow; -class NSButton; -class NSTextField; -#endif - namespace client { +class RootWindowMacImpl; + // OS X implementation of a top-level native window in the browser process. // The methods of this class must be called on the main thread unless otherwise // indicated. @@ -33,6 +25,9 @@ class RootWindowMac : public RootWindow, public BrowserWindow::Delegate { RootWindowMac(); ~RootWindowMac(); + BrowserWindow* browser_window() const; + RootWindow::Delegate* delegate() const; + // RootWindow methods. void Init(RootWindow::Delegate* delegate, const RootWindowConfig& config, @@ -55,18 +50,6 @@ class RootWindowMac : public RootWindow, public BrowserWindow::Delegate { bool WithWindowlessRendering() const OVERRIDE; bool WithExtension() const OVERRIDE; - // Called by RootWindowDelegate after the associated NSWindow has been - // destroyed. - void WindowDestroyed(); - - BrowserWindow* browser_window() const { return browser_window_.get(); } - RootWindow::Delegate* delegate() const { return delegate_; } - - private: - void CreateBrowserWindow(const std::string& startup_url); - void CreateRootWindow(const CefBrowserSettings& settings, - bool initially_hidden); - // BrowserWindow::Delegate methods. void OnBrowserCreated(CefRefPtr browser) OVERRIDE; void OnBrowserWindowDestroyed() OVERRIDE; @@ -80,34 +63,14 @@ class RootWindowMac : public RootWindow, public BrowserWindow::Delegate { void OnSetDraggableRegions( const std::vector& regions) OVERRIDE; - void NotifyDestroyedIfDone(); + void OnNativeWindowClosed(); - // After initialization all members are only accessed on the main thread. - // Members set during initialization. - bool with_controls_; - bool with_osr_; - bool with_extension_; - bool is_popup_; - CefRect start_rect_; - scoped_ptr browser_window_; - bool initialized_; - - // Main window. - NSWindow* window_; - - // 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_; + private: + CefRefPtr impl_; DISALLOW_COPY_AND_ASSIGN(RootWindowMac); + + friend class RootWindowMacImpl; }; } // namespace client diff --git a/tests/cefclient/browser/root_window_mac.mm b/tests/cefclient/browser/root_window_mac.mm index 87235014f..5bb23d2e2 100644 --- a/tests/cefclient/browser/root_window_mac.mm +++ b/tests/cefclient/browser/root_window_mac.mm @@ -19,7 +19,7 @@ // Receives notifications from controls and the browser window. Will delete // itself when done. -@interface RootWindowDelegate : NSObject { +@interface RootWindowDelegate : NSObject { @private NSWindow* window_; client::RootWindowMac* root_window_; @@ -36,7 +36,721 @@ - (IBAction)reload:(id)sender; - (IBAction)stopLoading:(id)sender; - (IBAction)takeURLStringValueFrom:(NSTextField*)sender; -@end +@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, + const RootWindowConfig& 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_; + scoped_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, + 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& 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 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 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) { + scoped_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, + 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& 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 @@ -68,8 +782,9 @@ - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; - +#if !__has_feature(objc_arc) [super dealloc]; +#endif // !__has_feature(objc_arc) } - (IBAction)goBack:(id)sender { @@ -165,7 +880,7 @@ // 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:(id)window { +- (BOOL)windowShouldClose:(NSWindow*)window { if (!force_close_) { client::BrowserWindow* browser_window = root_window_->browser_window(); if (browser_window && !browser_window->IsClosing()) { @@ -182,544 +897,33 @@ } } - // Try to make the window go away. + // 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 performSelectorOnMainThread:@selector(cleanup:) - withObject:window - waitUntilDone:NO]; + [self cleanup]; // Allow the close. return YES; } // Deletes itself. -- (void)cleanup:(id)window { - root_window_->WindowDestroyed(); - +- (void)cleanup { // Don't want any more delegate callbacks after we destroy ourselves. - [window setDelegate:nil]; - - [self release]; -} - -@end - -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] autorelease]; - [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 - -RootWindowMac::RootWindowMac() - : 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) {} - -RootWindowMac::~RootWindowMac() { - REQUIRE_MAIN_THREAD(); - - // The window and browser should already have been destroyed. - DCHECK(window_destroyed_); - DCHECK(browser_destroyed_); -} - -void RootWindowMac::Init(RootWindow::Delegate* delegate, - const RootWindowConfig& config, - const CefBrowserSettings& settings) { - DCHECK(delegate); - DCHECK(!initialized_); - - delegate_ = delegate; - 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(&RootWindowMac::CreateRootWindow, this, - settings, config.initially_hidden)); - } -} - -void RootWindowMac::InitAsPopup(RootWindow::Delegate* delegate, - bool with_controls, - bool with_osr, - const CefPopupFeatures& popupFeatures, - CefWindowInfo& windowInfo, - CefRefPtr& client, - CefBrowserSettings& settings) { - DCHECK(delegate); - DCHECK(!initialized_); - - delegate_ = delegate; - 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 RootWindowMac::Show(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 == ShowMinimized && is_minimized) || - (mode == ShowMaximized && is_maximized) || - (mode == 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 == ShowMinimized) - [window_ performMiniaturize:nil]; - else if (mode == ShowMaximized) - [window_ performZoom:nil]; -} - -void RootWindowMac::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 RootWindowMac::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 RootWindowMac::Close(bool force) { - REQUIRE_MAIN_THREAD(); - - if (window_) { - static_cast([window_ delegate]).force_close = force; - [window_ performClose:nil]; - } -} - -void RootWindowMac::SetDeviceScaleFactor(float device_scale_factor) { - REQUIRE_MAIN_THREAD(); - - if (browser_window_ && with_osr_) - browser_window_->SetDeviceScaleFactor(device_scale_factor); -} - -float RootWindowMac::GetDeviceScaleFactor() const { - REQUIRE_MAIN_THREAD(); - - if (browser_window_ && with_osr_) - return browser_window_->GetDeviceScaleFactor(); - - NOTREACHED(); - return 0.0f; -} - -CefRefPtr RootWindowMac::GetBrowser() const { - REQUIRE_MAIN_THREAD(); - - if (browser_window_) - return browser_window_->GetBrowser(); - return NULL; -} - -ClientWindowHandle RootWindowMac::GetWindowHandle() const { - REQUIRE_MAIN_THREAD(); - return [window_ contentView]; -} - -bool RootWindowMac::WithWindowlessRendering() const { - REQUIRE_MAIN_THREAD(); - return with_osr_; -} - -bool RootWindowMac::WithExtension() const { - REQUIRE_MAIN_THREAD(); - return with_extension_; -} - -void RootWindowMac::WindowDestroyed() { + 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; - window_destroyed_ = true; - NotifyDestroyedIfDone(); + root_window_->OnNativeWindowClosed(); } -void RootWindowMac::CreateBrowserWindow(const std::string& startup_url) { - if (with_osr_) { - OsrRendererSettings settings = {}; - MainContext::Get()->PopulateOsrSettings(&settings); - browser_window_.reset(new BrowserWindowOsrMac(this, startup_url, settings)); - } else { - browser_window_.reset(new BrowserWindowStdMac(this, startup_url)); - } -} - -void RootWindowMac::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"]; - - // Create the delegate for control and browser window events. - RootWindowDelegate* delegate = - [[RootWindowDelegate alloc] initWithWindow:window_ andRootWindow:this]; - - // 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:delegate]; - [back_button_ setAction:@selector(goBack:)]; - [back_button_ setEnabled:NO]; - - forward_button_ = MakeButton(&button_rect, @"Forward", contentView); - [forward_button_ setTarget:delegate]; - [forward_button_ setAction:@selector(goForward:)]; - [forward_button_ setEnabled:NO]; - - reload_button_ = MakeButton(&button_rect, @"Reload", contentView); - [reload_button_ setTarget:delegate]; - [reload_button_ setAction:@selector(reload:)]; - [reload_button_ setEnabled:NO]; - - stop_button_ = MakeButton(&button_rect, @"Stop", contentView); - [stop_button_ setTarget: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: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(contentView, CefRect(0, 0, width, height), - settings, - delegate_->GetRequestContext(this)); - } 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(contentView, 0, 0, contentBounds.size.width, - contentBounds.size.height); - } - - if (!initially_hidden) { - // Show the window. - Show(ShowNormal); - - // Size the window. - SetBounds(x, y, width, height); - } -} - -void RootWindowMac::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); - - delegate_->OnBrowserCreated(this, browser); -} - -void RootWindowMac::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 RootWindowMac::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 RootWindowMac::OnSetDraggableRegions( - const std::vector& regions) { - REQUIRE_MAIN_THREAD(); - // TODO(cef): Implement support for draggable regions on this platform. -} - -void RootWindowMac::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 RootWindowMac::OnSetFullscreen(bool fullscreen) { - REQUIRE_MAIN_THREAD(); - - CefRefPtr browser = GetBrowser(); - if (browser) { - scoped_ptr test_runner( - new window_test::WindowTestRunnerMac()); - if (fullscreen) - test_runner->Maximize(browser); - else - test_runner->Restore(browser); - } -} - -void RootWindowMac::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(ShowNormal); -} - -void RootWindowMac::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 RootWindowMac::NotifyDestroyedIfDone() { - // Notify once both the window and the browser have been destroyed. - if (window_destroyed_ && browser_destroyed_) - delegate_->OnRootWindowDestroyed(this); -} - -// static -scoped_refptr RootWindow::GetForNSWindow(NSWindow* window) { - RootWindowDelegate* delegate = - static_cast([window delegate]); - return [delegate root_window]; -} - -} // namespace client +@end // @implementation RootWindowDelegate diff --git a/tests/cefclient/browser/temp_window_mac.h b/tests/cefclient/browser/temp_window_mac.h index 0bec1e0cf..c64062f2c 100644 --- a/tests/cefclient/browser/temp_window_mac.h +++ b/tests/cefclient/browser/temp_window_mac.h @@ -10,6 +10,8 @@ namespace client { +class TempWindowMacImpl; + // Represents a singleton hidden window that acts as a temporary parent for // popup browsers. Only accessed on the UI thread. class TempWindowMac { @@ -26,7 +28,7 @@ class TempWindowMac { TempWindowMac(); ~TempWindowMac(); - NSWindow* window_; + scoped_ptr impl_; DISALLOW_COPY_AND_ASSIGN(TempWindowMac); }; diff --git a/tests/cefclient/browser/temp_window_mac.mm b/tests/cefclient/browser/temp_window_mac.mm index d6091c15e..6aff01cd7 100644 --- a/tests/cefclient/browser/temp_window_mac.mm +++ b/tests/cefclient/browser/temp_window_mac.mm @@ -17,29 +17,43 @@ TempWindowMac* g_temp_window = NULL; } // namespace -TempWindowMac::TempWindowMac() : window_(nil) { - DCHECK(!g_temp_window); - g_temp_window = this; +class TempWindowMacImpl { + public: + TempWindowMacImpl() { + // Create a borderless non-visible 1x1 window. + window_ = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1) + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + CHECK(window_); + } + ~TempWindowMacImpl() { + DCHECK(window_); + [window_ close]; + } - // Create a borderless non-visible 1x1 window. - window_ = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1) - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]; - CHECK(window_); + private: + NSWindow* window_; + + friend class TempWindowMac; +}; + +TempWindowMac::TempWindowMac() { + DCHECK(!g_temp_window); + impl_.reset(new TempWindowMacImpl); + g_temp_window = this; } TempWindowMac::~TempWindowMac() { + impl_.reset(); g_temp_window = NULL; - DCHECK(window_); - - [window_ close]; } // static CefWindowHandle TempWindowMac::GetWindowHandle() { DCHECK(g_temp_window); - return [g_temp_window->window_ contentView]; + return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE( + g_temp_window->impl_->window_.contentView); } } // namespace client diff --git a/tests/cefclient/browser/window_test_runner_mac.mm b/tests/cefclient/browser/window_test_runner_mac.mm index 243d17dda..2a04d09e1 100644 --- a/tests/cefclient/browser/window_test_runner_mac.mm +++ b/tests/cefclient/browser/window_test_runner_mac.mm @@ -15,7 +15,8 @@ namespace window_test { namespace { NSWindow* GetWindow(CefRefPtr browser) { - NSView* view = (NSView*)browser->GetHost()->GetWindowHandle(); + NSView* view = + CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetHost()->GetWindowHandle()); return [view window]; } diff --git a/tests/cefclient/cefclient_mac.mm b/tests/cefclient/cefclient_mac.mm index a1c56f7a8..e7bd659dc 100644 --- a/tests/cefclient/cefclient_mac.mm +++ b/tests/cefclient/cefclient_mac.mm @@ -355,71 +355,77 @@ int RunMain(int argc, char* argv[]) { if (!library_loader.LoadInMain()) return 1; + int result = -1; + CefMainArgs main_args(argc, argv); - // Initialize the AutoRelease pool. - NSAutoreleasePool* autopool = [[NSAutoreleasePool alloc] init]; + @autoreleasepool { + // Initialize the ClientApplication instance. + [ClientApplication sharedApplication]; - // Initialize the ClientApplication instance. - [ClientApplication sharedApplication]; + // Parse command-line arguments. + CefRefPtr command_line = + CefCommandLine::CreateCommandLine(); + command_line->InitFromArgv(argc, argv); - // Parse command-line arguments. - CefRefPtr command_line = CefCommandLine::CreateCommandLine(); - command_line->InitFromArgv(argc, argv); + // Create a ClientApp of the correct type. + CefRefPtr app; + ClientApp::ProcessType process_type = + ClientApp::GetProcessType(command_line); + if (process_type == ClientApp::BrowserProcess) + app = new ClientAppBrowser(); - // Create a ClientApp of the correct type. - CefRefPtr app; - ClientApp::ProcessType process_type = ClientApp::GetProcessType(command_line); - if (process_type == ClientApp::BrowserProcess) - app = new ClientAppBrowser(); + // Create the main context object. + scoped_ptr context( + new MainContextImpl(command_line, true)); - // Create the main context object. - scoped_ptr context(new MainContextImpl(command_line, true)); - - CefSettings settings; + CefSettings settings; // When generating projects with CMake the CEF_USE_SANDBOX value will be defined // automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable // use of the sandbox. #if !defined(CEF_USE_SANDBOX) - settings.no_sandbox = true; + settings.no_sandbox = true; #endif - // Populate the settings based on command line arguments. - context->PopulateSettings(&settings); + // Populate the settings based on command line arguments. + context->PopulateSettings(&settings); - // Create the main message loop object. - scoped_ptr message_loop; - if (settings.external_message_pump) - message_loop = MainMessageLoopExternalPump::Create(); - else - message_loop.reset(new MainMessageLoopStd); + // Create the main message loop object. + scoped_ptr message_loop; + if (settings.external_message_pump) + message_loop = MainMessageLoopExternalPump::Create(); + else + message_loop.reset(new MainMessageLoopStd); - // Initialize CEF. - context->Initialize(main_args, settings, app, NULL); + // Initialize CEF. + context->Initialize(main_args, settings, app, NULL); - // Register scheme handlers. - test_runner::RegisterSchemeHandlers(); + // Register scheme handlers. + test_runner::RegisterSchemeHandlers(); - // Create the application delegate and window. - ClientAppDelegate* delegate = [[ClientAppDelegate alloc] - initWithControls:!command_line->HasSwitch(switches::kHideControls) - andOsr:settings.windowless_rendering_enabled ? true : false]; - [delegate performSelectorOnMainThread:@selector(createApplication:) - withObject:nil - waitUntilDone:NO]; + // Create the application delegate and window. + ClientAppDelegate* delegate = [[ClientAppDelegate alloc] + initWithControls:!command_line->HasSwitch(switches::kHideControls) + andOsr:settings.windowless_rendering_enabled ? true : false]; + [delegate performSelectorOnMainThread:@selector(createApplication:) + withObject:nil + waitUntilDone:NO]; - // Run the message loop. This will block until Quit() is called. - int result = message_loop->Run(); + // Run the message loop. This will block until Quit() is called. + result = message_loop->Run(); - // Shut down CEF. - context->Shutdown(); + // Shut down CEF. + context->Shutdown(); - // Release objects in reverse order of creation. - [delegate release]; - message_loop.reset(); - context.reset(); - [autopool release]; + // Release objects in reverse order of creation. +#if !__has_feature(objc_arc) + [delegate release]; +#endif // !__has_feature(objc_arc) + delegate = nil; + message_loop.reset(); + context.reset(); + } // @autoreleasepool return result; } diff --git a/tests/cefsimple/CMakeLists.txt.in b/tests/cefsimple/CMakeLists.txt.in index 917778444..145327832 100644 --- a/tests/cefsimple/CMakeLists.txt.in +++ b/tests/cefsimple/CMakeLists.txt.in @@ -87,6 +87,16 @@ endif() # if(OS_MACOSX) + option(OPTION_USE_ARC "Build with ARC (automatic Reference Counting) on macOS." ON) + if(OPTION_USE_ARC) + list(APPEND CEF_COMPILER_FLAGS + -fobjc-arc + ) + set_target_properties(${target} PROPERTIES + CLANG_ENABLE_OBJC_ARC "YES" + ) + endif() + # Output paths for the app bundles. set(CEF_APP "${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.app") set(CEF_HELPER_APP "${CEF_TARGET_OUT_DIR}/${CEF_HELPER_OUTPUT_NAME}.app") diff --git a/tests/cefsimple/cefsimple_mac.mm b/tests/cefsimple/cefsimple_mac.mm index a60b53c6f..e5301b4d9 100644 --- a/tests/cefsimple/cefsimple_mac.mm +++ b/tests/cefsimple/cefsimple_mac.mm @@ -12,13 +12,13 @@ #include "tests/cefsimple/simple_handler.h" // Receives notifications from the application. -@interface SimpleAppDelegate : NSObject +@interface SimpleAppDelegate : NSObject - (void)createApplication:(id)object; - (void)tryToTerminateApplication:(NSApplication*)app; @end // Provide the CefAppProtocol implementation required by CEF. -@interface SimpleApplication : NSApplication { +@interface SimpleApplication : NSApplication { @private BOOL handlingSendEvent_; } @@ -119,48 +119,47 @@ int main(int argc, char* argv[]) { // Provide CEF with command-line arguments. CefMainArgs main_args(argc, argv); - // Initialize the AutoRelease pool. - NSAutoreleasePool* autopool = [[NSAutoreleasePool alloc] init]; + @autoreleasepool { + // Initialize the SimpleApplication instance. + [SimpleApplication sharedApplication]; - // Initialize the SimpleApplication instance. - [SimpleApplication sharedApplication]; + // Specify CEF global settings here. + CefSettings settings; - // Specify CEF global settings here. - CefSettings settings; - -// When generating projects with CMake the CEF_USE_SANDBOX value will be defined -// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable -// use of the sandbox. + // When generating projects with CMake the CEF_USE_SANDBOX value will be + // defined automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line + // to disable use of the sandbox. #if !defined(CEF_USE_SANDBOX) - settings.no_sandbox = true; + settings.no_sandbox = true; #endif - // SimpleApp implements application-level callbacks for the browser process. - // It will create the first browser instance in OnContextInitialized() after - // CEF has initialized. - CefRefPtr app(new SimpleApp); + // SimpleApp implements application-level callbacks for the browser process. + // It will create the first browser instance in OnContextInitialized() after + // CEF has initialized. + CefRefPtr app(new SimpleApp); - // Initialize CEF for the browser process. - CefInitialize(main_args, settings, app.get(), NULL); + // Initialize CEF for the browser process. + CefInitialize(main_args, settings, app.get(), NULL); - // Create the application delegate. - NSObject* delegate = [[SimpleAppDelegate alloc] init]; - [delegate performSelectorOnMainThread:@selector(createApplication:) - withObject:nil - waitUntilDone:NO]; + // Create the application delegate. + NSObject* delegate = [[SimpleAppDelegate alloc] init]; + [delegate performSelectorOnMainThread:@selector(createApplication:) + withObject:nil + waitUntilDone:NO]; - // Run the CEF message loop. This will block until CefQuitMessageLoop() is - // called. - CefRunMessageLoop(); + // Run the CEF message loop. This will block until CefQuitMessageLoop() is + // called. + CefRunMessageLoop(); - // Shut down CEF. - CefShutdown(); + // Shut down CEF. + CefShutdown(); - // Release the delegate. - [delegate release]; - - // Release the AutoRelease pool. - [autopool release]; + // Release the delegate. +#if !__has_feature(objc_arc) + [delegate release]; +#endif // !__has_feature(objc_arc) + delegate = nil; + } // @autoreleasepool return 0; } diff --git a/tests/cefsimple/simple_handler_mac.mm b/tests/cefsimple/simple_handler_mac.mm index 5a25c60f4..5a666627c 100644 --- a/tests/cefsimple/simple_handler_mac.mm +++ b/tests/cefsimple/simple_handler_mac.mm @@ -10,7 +10,8 @@ void SimpleHandler::PlatformTitleChange(CefRefPtr browser, const CefString& title) { - NSView* view = (NSView*)browser->GetHost()->GetWindowHandle(); + NSView* view = + CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetHost()->GetWindowHandle()); NSWindow* window = [view window]; std::string titleStr(title); NSString* str = [NSString stringWithUTF8String:titleStr.c_str()]; diff --git a/tests/ceftests/os_rendering_unittest_mac.mm b/tests/ceftests/os_rendering_unittest_mac.mm index 9c05092ef..6e24dc93d 100644 --- a/tests/ceftests/os_rendering_unittest_mac.mm +++ b/tests/ceftests/os_rendering_unittest_mac.mm @@ -12,7 +12,7 @@ CefWindowHandle GetFakeView() { NSScreen* mainScreen = [NSScreen mainScreen]; NSRect screenRect = [mainScreen visibleFrame]; NSView* fakeView = [[NSView alloc] initWithFrame:screenRect]; - return fakeView; + return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(fakeView); } } // namespace osr_unittests diff --git a/tests/shared/browser/main_message_loop_external_pump_mac.mm b/tests/shared/browser/main_message_loop_external_pump_mac.mm index fd1fae10a..f88361b18 100644 --- a/tests/shared/browser/main_message_loop_external_pump_mac.mm +++ b/tests/shared/browser/main_message_loop_external_pump_mac.mm @@ -82,14 +82,21 @@ class MainMessageLoopExternalPumpMac : public MainMessageLoopExternalPump { namespace client { MainMessageLoopExternalPumpMac::MainMessageLoopExternalPumpMac() - : owner_thread_([[NSThread currentThread] retain]), timer_(nil) { - event_handler_ = [[[EventHandler alloc] initWithPump:this] retain]; + : owner_thread_([NSThread currentThread]), timer_(nil) { +#if !__has_feature(objc_arc) + [owner_thread_ retain]; +#endif // !__has_feature(objc_arc) + event_handler_ = [[EventHandler alloc] initWithPump:this]; } MainMessageLoopExternalPumpMac::~MainMessageLoopExternalPumpMac() { KillTimer(); +#if !__has_feature(objc_arc) [owner_thread_ release]; [event_handler_ release]; +#endif // !__has_feature(objc_arc) + owner_thread_ = nil; + event_handler_ = nil; } void MainMessageLoopExternalPumpMac::Quit() { @@ -140,11 +147,14 @@ void MainMessageLoopExternalPumpMac::SetTimer(int64 delay_ms) { DCHECK(!timer_); const double delay_s = static_cast(delay_ms) / 1000.0; - timer_ = [[NSTimer timerWithTimeInterval:delay_s - target:event_handler_ - selector:@selector(timerTimeout:) - userInfo:nil - repeats:NO] retain]; + timer_ = [NSTimer timerWithTimeInterval:delay_s + target:event_handler_ + selector:@selector(timerTimeout:) + userInfo:nil + repeats:NO]; +#if !__has_feature(objc_arc) + [timer_ retain]; +#endif // !__has_feature(objc_arc) // Add the timer to default and tracking runloop modes. NSRunLoop* owner_runloop = [NSRunLoop currentRunLoop]; @@ -155,7 +165,9 @@ void MainMessageLoopExternalPumpMac::SetTimer(int64 delay_ms) { void MainMessageLoopExternalPumpMac::KillTimer() { if (timer_ != nil) { [timer_ invalidate]; +#if !__has_feature(objc_arc) [timer_ release]; +#endif // !__has_feature(objc_arc) timer_ = nil; } }