• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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