cef/tests/shared/browser/main_message_loop_external_pump_mac.mm
Jesper Papmehl-Dufay 019611c764 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.
2019-04-23 17:17:56 +00:00

182 lines
4.8 KiB
Plaintext

// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/shared/browser/main_message_loop_external_pump.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include "include/cef_app.h"
@class EventHandler;
namespace client {
class MainMessageLoopExternalPumpMac : public MainMessageLoopExternalPump {
public:
MainMessageLoopExternalPumpMac();
~MainMessageLoopExternalPumpMac();
// MainMessageLoopStd methods:
void Quit() OVERRIDE;
int Run() OVERRIDE;
// MainMessageLoopExternalPump methods:
void OnScheduleMessagePumpWork(int64 delay_ms) OVERRIDE;
// Internal methods used for processing the event callbacks. They are public
// for simplicity but should not be used directly.
void HandleScheduleWork(int64 delay_ms);
void HandleTimerTimeout();
protected:
// MainMessageLoopExternalPump methods:
void SetTimer(int64 delay_ms) OVERRIDE;
void KillTimer() OVERRIDE;
bool IsTimerPending() OVERRIDE { return timer_ != nil; }
private:
// Owner thread that will run events.
NSThread* owner_thread_;
// Pending work timer.
NSTimer* timer_;
// Used to handle event callbacks on the owner thread.
EventHandler* event_handler_;
};
} // namespace client
// Object that handles event callbacks on the owner thread.
@interface EventHandler : NSObject {
@private
client::MainMessageLoopExternalPumpMac* pump_;
}
- (id)initWithPump:(client::MainMessageLoopExternalPumpMac*)pump;
- (void)scheduleWork:(NSNumber*)delay_ms;
- (void)timerTimeout:(id)obj;
@end
@implementation EventHandler
- (id)initWithPump:(client::MainMessageLoopExternalPumpMac*)pump {
if (self = [super init]) {
pump_ = pump;
}
return self;
}
- (void)scheduleWork:(NSNumber*)delay_ms {
pump_->HandleScheduleWork([delay_ms integerValue]);
}
- (void)timerTimeout:(id)obj {
pump_->HandleTimerTimeout();
}
@end
namespace client {
MainMessageLoopExternalPumpMac::MainMessageLoopExternalPumpMac()
: 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() {
[NSApp stop:nil];
}
int MainMessageLoopExternalPumpMac::Run() {
// Run the message loop.
[NSApp run];
KillTimer();
// We need to run the message pump until it is idle. However we don't have
// that information here so we run the message loop "for a while".
for (int i = 0; i < 10; ++i) {
// Let default runloop observers run.
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, 1);
// Do some work.
CefDoMessageLoopWork();
// Sleep to allow the CEF proc to do work.
[NSThread sleepForTimeInterval:0.05];
}
return 0;
}
void MainMessageLoopExternalPumpMac::OnScheduleMessagePumpWork(int64 delay_ms) {
// This method may be called on any thread.
NSNumber* number = [NSNumber numberWithInt:static_cast<int>(delay_ms)];
[event_handler_ performSelector:@selector(scheduleWork:)
onThread:owner_thread_
withObject:number
waitUntilDone:NO];
}
void MainMessageLoopExternalPumpMac::HandleScheduleWork(int64 delay_ms) {
OnScheduleWork(delay_ms);
}
void MainMessageLoopExternalPumpMac::HandleTimerTimeout() {
OnTimerTimeout();
}
void MainMessageLoopExternalPumpMac::SetTimer(int64 delay_ms) {
DCHECK_GT(delay_ms, 0);
DCHECK(!timer_);
const double delay_s = static_cast<double>(delay_ms) / 1000.0;
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];
[owner_runloop addTimer:timer_ forMode:NSRunLoopCommonModes];
[owner_runloop addTimer:timer_ forMode:NSEventTrackingRunLoopMode];
}
void MainMessageLoopExternalPumpMac::KillTimer() {
if (timer_ != nil) {
[timer_ invalidate];
#if !__has_feature(objc_arc)
[timer_ release];
#endif // !__has_feature(objc_arc)
timer_ = nil;
}
}
// static
scoped_ptr<MainMessageLoopExternalPump> MainMessageLoopExternalPump::Create() {
return scoped_ptr<MainMessageLoopExternalPump>(
new MainMessageLoopExternalPumpMac());
}
} // namespace client