mac: Enable ARC in libcef (fixes #3496)

Also enables debugging of zombie Objective-C objects.
This commit is contained in:
Marshall Greenblatt 2023-05-05 14:17:14 +03:00
parent e5c4b27366
commit e5334a5a18
10 changed files with 105 additions and 70 deletions

View File

@ -1156,6 +1156,8 @@ source_set("libcef_static") {
"//chrome/app/chrome_main_mac.h",
"//chrome/app/chrome_main_mac.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
}
if (ozone_platform_x11) {
@ -1602,6 +1604,7 @@ if (is_mac) {
configs += [
":libcef_autogen_config",
"//build/config/compiler:enable_arc",
]
# We don't link the framework so just use the path from the main executable.

View File

@ -7,6 +7,12 @@
#include "libcef/browser/native/browser_platform_delegate_native.h"
#if defined(__OBJC__)
@class CefWindowDelegate;
#else
class CefWindowDelegate;
#endif
namespace content {
class RenderWidgetHostViewMac;
}
@ -67,7 +73,9 @@ class CefBrowserPlatformDelegateNativeMac
content::RenderWidgetHostViewMac* GetHostView() const;
// True if the host window has been created.
bool host_window_created_;
bool host_window_created_ = false;
CefWindowDelegate* __strong window_delegate_;
};
#endif // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_MAC_H_

View File

@ -7,6 +7,7 @@
#import <Cocoa/Cocoa.h>
#import <CoreServices/CoreServices.h>
#include "include/internal/cef_types_mac.h"
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "libcef/browser/context.h"
#include "libcef/browser/native/javascript_dialog_runner_mac.h"
@ -28,6 +29,10 @@
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/gfx/geometry/rect.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Wrapper NSView for the native view. Necessary to destroy the browser when
// the view is deleted.
@interface CefBrowserHostView : NSView {
@ -49,8 +54,6 @@
// PlatformCreateWindow().
static_cast<AlloyBrowserHostImpl*>(browser_)->WindowDestroyed();
}
[super dealloc];
}
@end
@ -59,7 +62,7 @@
@interface CefWindowDelegate : NSObject <NSWindowDelegate> {
@private
CefBrowserHostBase* browser_; // weak
NSWindow* window_;
NSWindow* __strong window_;
}
- (id)initWithWindow:(NSWindow*)window andBrowser:(CefBrowserHostBase*)browser;
@end
@ -70,16 +73,12 @@
if (self = [super init]) {
window_ = window;
browser_ = browser;
[window_ setDelegate:self];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (BOOL)windowShouldClose:(id)window {
@ -88,6 +87,11 @@
return NO;
}
// For an NSWindow object, the default is to be released on |close|. We
// instead want it to remain valid until all strong references are released
// via |cleanup:| and |BrowserDestroyed|.
((NSWindow*)window).releasedWhenClosed = NO;
// Clean ourselves up after clearing the stack of anything that might have the
// window on it.
[self performSelectorOnMainThread:@selector(cleanup:)
@ -100,7 +104,7 @@
- (void)cleanup:(id)window {
[window_ setDelegate:nil];
[self release];
window_ = nil;
}
@end
@ -239,16 +243,16 @@ void GetNSBoundsInDisplay(const gfx::Rect& dip_bounds,
CefBrowserPlatformDelegateNativeMac::CefBrowserPlatformDelegateNativeMac(
const CefWindowInfo& window_info,
SkColor background_color)
: CefBrowserPlatformDelegateNative(window_info, background_color),
host_window_created_(false) {}
: CefBrowserPlatformDelegateNative(window_info, background_color) {}
void CefBrowserPlatformDelegateNativeMac::BrowserDestroyed(
CefBrowserHostBase* browser) {
CefBrowserPlatformDelegateNative::BrowserDestroyed(browser);
if (host_window_created_) {
// Release the reference added in CreateHostWindow().
// Release the references added in CreateHostWindow().
browser->Release();
window_delegate_ = nil;
}
}
@ -285,12 +289,15 @@ bool CefBrowserPlatformDelegateNativeMac::CreateHostWindow() {
defer:NO];
// Create the delegate for control and browser window events.
[[CefWindowDelegate alloc] initWithWindow:new_window andBrowser:browser_];
// Add a reference that will be released in BrowserDestroyed().
window_delegate_ = [[CefWindowDelegate alloc] initWithWindow:new_window
andBrowser:browser_];
[new_window setDelegate:window_delegate_];
parent_view = [new_window contentView];
browser_view_rect = [parent_view bounds];
window_info_.parent_view = parent_view;
window_info_.parent_view = CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(parent_view);
// 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
@ -314,7 +321,6 @@ bool CefBrowserPlatformDelegateNativeMac::CreateHostWindow() {
[parent_view addSubview:browser_view];
[browser_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[browser_view setNeedsDisplay:YES];
[browser_view release];
// Parent the WebContents to the browser view.
const NSRect bounds = [browser_view bounds];
@ -324,7 +330,7 @@ bool CefBrowserPlatformDelegateNativeMac::CreateHostWindow() {
[native_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[native_view setNeedsDisplay:YES];
window_info_.view = browser_view;
window_info_.view = CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(browser_view);
if (new_window != nil && !window_info_.hidden) {
// Show the window.
@ -461,7 +467,7 @@ void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {}
CefEventHandle CefBrowserPlatformDelegateNativeMac::GetEventHandle(
const content::NativeWebKeyboardEvent& event) const {
return event.os_event;
return CAST_NSEVENT_TO_CEF_EVENT_HANDLE(event.os_event);
}
std::unique_ptr<CefJavaScriptDialogRunner>
@ -507,11 +513,10 @@ CefBrowserPlatformDelegateNativeMac::TranslateWebKeyEvent(
}
NSString* charactersIgnoringModifiers =
[[[NSString alloc] initWithCharacters:&key_event.unmodified_character
length:1] autorelease];
[[NSString alloc] initWithCharacters:&key_event.unmodified_character
length:1];
NSString* characters =
[[[NSString alloc] initWithCharacters:&key_event.character
length:1] autorelease];
[[NSString alloc] initWithCharacters:&key_event.character length:1];
NSEvent* synthetic_event =
[NSEvent keyEventWithType:event_type

View File

@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
#include "include/internal/cef_types_mac.h"
#include "libcef/browser/native/cursor_util.h"
#import "base/mac/scoped_nsobject.h"
#import "ui/base/cocoa/cursor_utils.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace cursor_util {
namespace {
@ -15,14 +19,16 @@ class ScopedCursorHandleImpl : public ScopedCursorHandle {
public:
explicit ScopedCursorHandleImpl(NSCursor* native_cursor) {
if (native_cursor) {
cursor_.reset([native_cursor retain]);
cursor_ = native_cursor;
}
}
cef_cursor_handle_t GetCursorHandle() override { return cursor_.get(); }
cef_cursor_handle_t GetCursorHandle() override {
return CAST_NSCURSOR_TO_CEF_CURSOR_HANDLE(cursor_);
}
private:
base::scoped_nsobject<NSCursor> cursor_;
NSCursor* __strong cursor_;
};
} // namespace

View File

@ -10,7 +10,6 @@
#include "libcef/browser/javascript_dialog_runner.h"
#include "base/functional/callback.h"
#include "base/mac/scoped_nsobject.h"
#include "base/memory/weak_ptr.h"
#if __OBJC__
@ -40,7 +39,7 @@ class CefJavaScriptDialogRunnerMac : public CefJavaScriptDialogRunner {
private:
DialogClosedCallback callback_;
base::scoped_nsobject<CefJavaScriptDialogHelper> helper_;
CefJavaScriptDialogHelper* __strong helper_;
// Must be the last member.
base::WeakPtrFactory<CefJavaScriptDialogRunnerMac> weak_ptr_factory_;

View File

@ -12,12 +12,16 @@
#include "base/strings/utf_string_conversions.h"
#include "components/url_formatter/elide_url.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Helper object that receives the notification that the dialog/sheet is
// going away. Is responsible for cleaning itself up.
@interface CefJavaScriptDialogHelper : NSObject <NSAlertDelegate> {
@private
base::scoped_nsobject<NSAlert> alert_;
NSTextField* textField_; // WEAK; owned by alert_
NSAlert* __strong alert_;
NSTextField* __weak textField_;
// Copies of the fields in CefJavaScriptDialog because they're private.
CefJavaScriptDialogRunner::DialogClosedCallback callback_;
@ -46,18 +50,20 @@
}
- (NSAlert*)alert {
alert_.reset([[NSAlert alloc] init]);
alert_ = [[NSAlert alloc] init];
return alert_;
}
- (NSTextField*)textField {
textField_ = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 22)];
[[textField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail];
[alert_ setAccessoryView:textField_];
[[alert_ window] setInitialFirstResponder:textField_];
[textField_ release];
NSTextField* textField =
[[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 22)];
textField.cell.lineBreakMode = NSLineBreakByTruncatingTail;
return textField_;
alert_.accessoryView = textField;
alert_.window.initialFirstResponder = textField;
textField_ = textField;
return textField;
}
- (void)alertDidEnd:(NSAlert*)alert
@ -70,15 +76,15 @@
bool success = returnCode == NSAlertFirstButtonReturn;
std::u16string input;
if (textField_) {
input = base::SysNSStringToUTF16([textField_ stringValue]);
input = base::SysNSStringToUTF16(textField_.stringValue);
}
std::move(callback_).Run(success, input);
}
- (void)cancel {
[NSApp endSheet:[alert_ window]];
alert_.reset();
[NSApp endSheet:alert_.window];
alert_ = nil;
}
@end
@ -97,26 +103,26 @@ void CefJavaScriptDialogRunnerMac::Run(
const std::u16string& message_text,
const std::u16string& default_prompt_text,
DialogClosedCallback callback) {
DCHECK(!helper_.get());
DCHECK(!helper_);
callback_ = std::move(callback);
bool text_field = message_type == content::JAVASCRIPT_DIALOG_TYPE_PROMPT;
bool one_button = message_type == content::JAVASCRIPT_DIALOG_TYPE_ALERT;
helper_.reset([[CefJavaScriptDialogHelper alloc]
helper_ = [[CefJavaScriptDialogHelper alloc]
initHelperWithCallback:base::BindOnce(
&CefJavaScriptDialogRunnerMac::DialogClosed,
weak_ptr_factory_.GetWeakPtr())]);
weak_ptr_factory_.GetWeakPtr())];
// Show the modal dialog.
NSAlert* alert = [helper_ alert];
NSTextField* field = nil;
if (text_field) {
field = [helper_ textField];
[field setStringValue:base::SysUTF16ToNSString(default_prompt_text)];
field.stringValue = base::SysUTF16ToNSString(default_prompt_text);
}
[alert setDelegate:helper_];
[alert setInformativeText:base::SysUTF16ToNSString(message_text)];
alert.delegate = helper_;
alert.informativeText = base::SysUTF16ToNSString(message_text);
std::u16string label;
switch (message_type) {
@ -137,12 +143,12 @@ void CefJavaScriptDialogRunnerMac::Run(
label += u" - " + display_url;
}
[alert setMessageText:base::SysUTF16ToNSString(label)];
alert.messageText = base::SysUTF16ToNSString(label);
[alert addButtonWithTitle:@"OK"];
if (!one_button) {
NSButton* other = [alert addButtonWithTitle:@"Cancel"];
[other setKeyEquivalent:@"\e"];
other.keyEquivalent = @"\e";
}
// Calling beginSheetModalForWindow:nil is wrong API usage. For now work
@ -168,21 +174,21 @@ void CefJavaScriptDialogRunnerMac::Run(
void CefJavaScriptDialogRunnerMac::Handle(
bool accept,
const std::u16string* prompt_override) {
if (helper_.get()) {
if (helper_) {
DialogClosed(accept, prompt_override ? *prompt_override : std::u16string());
}
}
void CefJavaScriptDialogRunnerMac::Cancel() {
if (helper_.get()) {
if (helper_) {
[helper_ cancel];
helper_.reset(nil);
helper_ = nil;
}
}
void CefJavaScriptDialogRunnerMac::DialogClosed(
bool success,
const std::u16string& user_input) {
helper_.reset(nil);
helper_ = nil;
std::move(callback_).Run(success, user_input);
}

View File

@ -8,8 +8,6 @@
#include "libcef/browser/menu_runner.h"
#include "base/mac/scoped_nsobject.h"
#if __OBJC__
@class MenuControllerCocoa;
#else
@ -28,7 +26,7 @@ class CefMenuRunnerMac : public CefMenuRunner {
void CancelContextMenu() override;
private:
base::scoped_nsobject<MenuControllerCocoa> menu_controller_;
MenuControllerCocoa* __strong menu_controller_;
};
#endif // CEF_LIBCEF_BROWSER_NATIVE_MENU_RUNNER_MAC_H_

View File

@ -11,6 +11,10 @@
#import "ui/base/cocoa/menu_controller.h"
#include "ui/gfx/geometry/point.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
CefMenuRunnerMac::CefMenuRunnerMac() {}
CefMenuRunnerMac::~CefMenuRunnerMac() {}
@ -20,17 +24,12 @@ bool CefMenuRunnerMac::RunContextMenu(
CefMenuModelImpl* model,
const content::ContextMenuParams& params) {
// Create a menu controller based on the model.
menu_controller_.reset([[MenuControllerCocoa alloc]
initWithModel:model->model()
delegate:nil
useWithPopUpButtonCell:NO]);
MenuControllerCocoa* menu_controller =
[[MenuControllerCocoa alloc] initWithModel:model->model()
delegate:nil
useWithPopUpButtonCell:NO];
// Keep the menu controller alive (by adding an additional retain) until after
// the menu has been dismissed. Otherwise it will crash if the browser is
// destroyed (and consequently the menu controller is destroyed) while the
// menu is still pending.
base::scoped_nsobject<MenuControllerCocoa> menu_controller_ref(
menu_controller_);
menu_controller_ = menu_controller;
// Make sure events can be pumped while the menu is up.
base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow;
@ -86,7 +85,7 @@ bool CefMenuRunnerMac::RunContextMenu(
}
void CefMenuRunnerMac::CancelContextMenu() {
if (menu_controller_.get()) {
[menu_controller_ cancel];
if (menu_controller_) {
menu_controller_ = nil;
}
}

View File

@ -17,6 +17,10 @@
#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
#import "ui/views/cocoa/native_widget_mac_ns_window_host.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
AppShimHost* GetHostForBrowser(Browser* browser) {
@ -133,9 +137,8 @@ void CefNativeWidgetMac::OnWindowInitialized() {
// From BrowserFrameMac::OnWindowInitialized.
if (auto* bridge = GetInProcessNSWindowBridge()) {
bridge->SetCommandDispatcher(
[[[ChromeCommandDispatcherDelegate alloc] init] autorelease],
[[[BrowserWindowCommandHandler alloc] init] autorelease]);
bridge->SetCommandDispatcher([[ChromeCommandDispatcherDelegate alloc] init],
[[BrowserWindowCommandHandler alloc] init]);
} else {
if (auto* host = GetHostForBrowser(browser_view_->browser())) {
host->GetAppShim()->CreateCommandDispatcherForWidget(

View File

@ -49,6 +49,7 @@
#include "ui/base/ui_base_switches.h"
#if BUILDFLAG(IS_MAC)
#include "components/crash/core/common/objc_zombie.h"
#include "libcef/common/util_mac.h"
#elif BUILDFLAG(IS_POSIX)
#include "libcef/common/util_linux.h"
@ -94,7 +95,8 @@ absl::optional<int> AlloyMainDelegate::BasicStartupComplete() {
crash_reporting::BasicStartupComplete(command_line);
#endif
if (process_type.empty()) {
const bool is_browser = process_type.empty();
if (is_browser) {
// In the browser process. Populate the global command-line object.
if (settings_->command_line_args_disabled) {
// Remove any existing command-line arguments.
@ -293,6 +295,12 @@ absl::optional<int> AlloyMainDelegate::BasicStartupComplete() {
std::ignore = commandLinePtr->Detach(nullptr);
}
#if BUILDFLAG(IS_MAC)
// Turns all deallocated Objective-C objects into zombies. Give the browser
// process a longer treadmill, since crashes there have more impact.
ObjcEvilDoers::ZombieEnable(true, is_browser ? 10000 : 1000);
#endif
// Initialize logging.
logging::LoggingSettings log_settings;