1// Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights 2// reserved. Use of this source code is governed by a BSD-style license that 3// can be found in the LICENSE file. 4 5#include "tests/shared/browser/main_message_loop_external_pump.h" 6 7#import <AppKit/AppKit.h> 8#import <Foundation/Foundation.h> 9 10#include <memory> 11 12#include "include/cef_app.h" 13 14@class EventHandler; 15 16namespace client { 17 18class MainMessageLoopExternalPumpMac : public MainMessageLoopExternalPump { 19 public: 20 MainMessageLoopExternalPumpMac(); 21 ~MainMessageLoopExternalPumpMac(); 22 23 // MainMessageLoopStd methods: 24 void Quit() override; 25 int Run() override; 26 27 // MainMessageLoopExternalPump methods: 28 void OnScheduleMessagePumpWork(int64 delay_ms) override; 29 30 // Internal methods used for processing the event callbacks. They are public 31 // for simplicity but should not be used directly. 32 void HandleScheduleWork(int64 delay_ms); 33 void HandleTimerTimeout(); 34 35 protected: 36 // MainMessageLoopExternalPump methods: 37 void SetTimer(int64 delay_ms) override; 38 void KillTimer() override; 39 bool IsTimerPending() override { return timer_ != nil; } 40 41 private: 42 // Owner thread that will run events. 43 NSThread* owner_thread_; 44 45 // Pending work timer. 46 NSTimer* timer_; 47 48 // Used to handle event callbacks on the owner thread. 49 EventHandler* event_handler_; 50}; 51 52} // namespace client 53 54// Object that handles event callbacks on the owner thread. 55@interface EventHandler : NSObject { 56 @private 57 client::MainMessageLoopExternalPumpMac* pump_; 58} 59 60- (id)initWithPump:(client::MainMessageLoopExternalPumpMac*)pump; 61- (void)scheduleWork:(NSNumber*)delay_ms; 62- (void)timerTimeout:(id)obj; 63@end 64 65@implementation EventHandler 66 67- (id)initWithPump:(client::MainMessageLoopExternalPumpMac*)pump { 68 if (self = [super init]) { 69 pump_ = pump; 70 } 71 return self; 72} 73 74- (void)scheduleWork:(NSNumber*)delay_ms { 75 pump_->HandleScheduleWork([delay_ms integerValue]); 76} 77 78- (void)timerTimeout:(id)obj { 79 pump_->HandleTimerTimeout(); 80} 81 82@end 83 84namespace client { 85 86MainMessageLoopExternalPumpMac::MainMessageLoopExternalPumpMac() 87 : owner_thread_([NSThread currentThread]), timer_(nil) { 88#if !__has_feature(objc_arc) 89 [owner_thread_ retain]; 90#endif // !__has_feature(objc_arc) 91 event_handler_ = [[EventHandler alloc] initWithPump:this]; 92} 93 94MainMessageLoopExternalPumpMac::~MainMessageLoopExternalPumpMac() { 95 KillTimer(); 96#if !__has_feature(objc_arc) 97 [owner_thread_ release]; 98 [event_handler_ release]; 99#endif // !__has_feature(objc_arc) 100 owner_thread_ = nil; 101 event_handler_ = nil; 102} 103 104void MainMessageLoopExternalPumpMac::Quit() { 105 [NSApp stop:nil]; 106} 107 108int MainMessageLoopExternalPumpMac::Run() { 109 // Run the message loop. 110 [NSApp run]; 111 112 KillTimer(); 113 114 // We need to run the message pump until it is idle. However we don't have 115 // that information here so we run the message loop "for a while". 116 for (int i = 0; i < 10; ++i) { 117 // Let default runloop observers run. 118 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, 1); 119 120 // Do some work. 121 CefDoMessageLoopWork(); 122 123 // Sleep to allow the CEF proc to do work. 124 [NSThread sleepForTimeInterval:0.05]; 125 } 126 127 return 0; 128} 129 130void MainMessageLoopExternalPumpMac::OnScheduleMessagePumpWork(int64 delay_ms) { 131 // This method may be called on any thread. 132 NSNumber* number = [NSNumber numberWithInt:static_cast<int>(delay_ms)]; 133 [event_handler_ performSelector:@selector(scheduleWork:) 134 onThread:owner_thread_ 135 withObject:number 136 waitUntilDone:NO]; 137} 138 139void MainMessageLoopExternalPumpMac::HandleScheduleWork(int64 delay_ms) { 140 OnScheduleWork(delay_ms); 141} 142 143void MainMessageLoopExternalPumpMac::HandleTimerTimeout() { 144 OnTimerTimeout(); 145} 146 147void MainMessageLoopExternalPumpMac::SetTimer(int64 delay_ms) { 148 DCHECK_GT(delay_ms, 0); 149 DCHECK(!timer_); 150 151 const double delay_s = static_cast<double>(delay_ms) / 1000.0; 152 timer_ = [NSTimer timerWithTimeInterval:delay_s 153 target:event_handler_ 154 selector:@selector(timerTimeout:) 155 userInfo:nil 156 repeats:NO]; 157#if !__has_feature(objc_arc) 158 [timer_ retain]; 159#endif // !__has_feature(objc_arc) 160 161 // Add the timer to default and tracking runloop modes. 162 NSRunLoop* owner_runloop = [NSRunLoop currentRunLoop]; 163 [owner_runloop addTimer:timer_ forMode:NSRunLoopCommonModes]; 164 [owner_runloop addTimer:timer_ forMode:NSEventTrackingRunLoopMode]; 165} 166 167void MainMessageLoopExternalPumpMac::KillTimer() { 168 if (timer_ != nil) { 169 [timer_ invalidate]; 170#if !__has_feature(objc_arc) 171 [timer_ release]; 172#endif // !__has_feature(objc_arc) 173 timer_ = nil; 174 } 175} 176 177// static 178std::unique_ptr<MainMessageLoopExternalPump> 179MainMessageLoopExternalPump::Create() { 180 return std::make_unique<MainMessageLoopExternalPumpMac>(); 181} 182 183} // namespace client 184