Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / widget / uikit / nsAppShell.mm
blobe3db5ee5a00e4d1636bcea1d28f2ffd4c31a626f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #import <UIKit/UIApplication.h>
7 #import <UIKit/UIScreen.h>
8 #import <UIKit/UIWindow.h>
9 #import <UIKit/UIViewController.h>
11 #include "nsAppShell.h"
12 #include "nsCOMPtr.h"
13 #include "nsDirectoryServiceDefs.h"
14 #include "nsString.h"
15 #include "nsIRollupListener.h"
16 #include "nsIWidget.h"
17 #include "nsThreadUtils.h"
18 #include "nsMemoryPressure.h"
19 #include "nsServiceManagerUtils.h"
21 nsAppShell* nsAppShell::gAppShell = NULL;
22 UIWindow* nsAppShell::gWindow = nil;
23 MOZ_RUNINIT NSMutableArray* nsAppShell::gTopLevelViews =
24     [[NSMutableArray alloc] init];
26 #define ALOG(args...)    \
27   fprintf(stderr, args); \
28   fprintf(stderr, "\n")
30 // ViewController
31 @interface ViewController : UIViewController
32 @end
34 @implementation ViewController
36 - (void)loadView {
37   ALOG("[ViewController loadView]");
38   CGRect r = {{0, 0}, {100, 100}};
39   self.view = [[UIView alloc] initWithFrame:r];
40   [self.view setBackgroundColor:[UIColor lightGrayColor]];
41   // add all of the top level views as children
42   for (UIView* v in nsAppShell::gTopLevelViews) {
43     ALOG("[ViewController.view addSubView:%p]", v);
44     [self.view addSubview:v];
45   }
46   [nsAppShell::gTopLevelViews release];
47   nsAppShell::gTopLevelViews = nil;
49 @end
51 // AppShellDelegate
53 // Acts as a delegate for the UIApplication
55 @interface AppShellDelegate : NSObject <UIApplicationDelegate> {
57 @property(strong, nonatomic) UIWindow* window;
58 @end
60 @implementation AppShellDelegate
62 - (BOOL)application:(UIApplication*)application
63     didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
64   ALOG("[AppShellDelegate application:didFinishLaunchingWithOptions:]");
65   // We only create one window, since we can only display one window at
66   // a time anyway. Also, iOS 4 fails to display UIWindows if you
67   // create them before calling UIApplicationMain, so this makes more sense.
68   nsAppShell::gWindow = [[[UIWindow alloc]
69       initWithFrame:[[UIScreen mainScreen] applicationFrame]] retain];
70   self.window = nsAppShell::gWindow;
72   self.window.rootViewController = [[ViewController alloc] init];
74   // just to make things more visible for now
75   nsAppShell::gWindow.backgroundColor = [UIColor blueColor];
76   [nsAppShell::gWindow makeKeyAndVisible];
78   return YES;
81 - (void)applicationWillTerminate:(UIApplication*)application {
82   ALOG("[AppShellDelegate applicationWillTerminate:]");
83   nsAppShell::gAppShell->WillTerminate();
86 - (void)applicationDidBecomeActive:(UIApplication*)application {
87   ALOG("[AppShellDelegate applicationDidBecomeActive:]");
90 - (void)applicationWillResignActive:(UIApplication*)application {
91   ALOG("[AppShellDelegate applicationWillResignActive:]");
94 - (void)applicationDidReceiveMemoryWarning:(UIApplication*)application {
95   ALOG("[AppShellDelegate applicationDidReceiveMemoryWarning:]");
96   NS_NotifyOfMemoryPressure(MemoryPressureState::LowMemory);
98 @end
100 // nsAppShell implementation
102 NS_IMETHODIMP
103 nsAppShell::ResumeNative(void) { return nsBaseAppShell::ResumeNative(); }
105 nsAppShell::nsAppShell()
106     : mAutoreleasePool(NULL),
107       mDelegate(NULL),
108       mCFRunLoop(NULL),
109       mCFRunLoopSource(NULL),
110       mTerminated(false),
111       mNotifiedWillTerminate(false) {
112   gAppShell = this;
115 nsAppShell::~nsAppShell() {
116   if (mAutoreleasePool) {
117     [mAutoreleasePool release];
118     mAutoreleasePool = NULL;
119   }
121   if (mCFRunLoop) {
122     if (mCFRunLoopSource) {
123       ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource,
124                               kCFRunLoopCommonModes);
125       ::CFRelease(mCFRunLoopSource);
126     }
127     ::CFRelease(mCFRunLoop);
128   }
130   gAppShell = NULL;
133 // Init
135 // public
136 nsresult nsAppShell::Init() {
137   mAutoreleasePool = [[NSAutoreleasePool alloc] init];
139   // Add a CFRunLoopSource to the main native run loop.  The source is
140   // responsible for interrupting the run loop when Gecko events are ready.
142   mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
143   NS_ENSURE_STATE(mCFRunLoop);
144   ::CFRetain(mCFRunLoop);
146   CFRunLoopSourceContext context;
147   bzero(&context, sizeof(context));
148   // context.version = 0;
149   context.info = this;
150   context.perform = ProcessGeckoEvents;
152   mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
153   NS_ENSURE_STATE(mCFRunLoopSource);
155   ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
157   return nsBaseAppShell::Init();
160 // ProcessGeckoEvents
162 // The "perform" target of mCFRunLoop, called when mCFRunLoopSource is
163 // signalled from ScheduleNativeEventCallback.
165 // protected static
166 void nsAppShell::ProcessGeckoEvents(void* aInfo) {
167   nsAppShell* self = static_cast<nsAppShell*>(aInfo);
168   self->NativeEventCallback();
169   self->Release();
172 // WillTerminate
174 // public
175 void nsAppShell::WillTerminate() {
176   mNotifiedWillTerminate = true;
177   if (mTerminated) return;
178   mTerminated = true;
179   // We won't get another chance to process events
180   NS_ProcessPendingEvents(NS_GetCurrentThread());
182   // Unless we call nsBaseAppShell::Exit() here, it might not get called
183   // at all.
184   nsBaseAppShell::Exit();
187 // ScheduleNativeEventCallback
189 // protected virtual
190 void nsAppShell::ScheduleNativeEventCallback() {
191   if (mTerminated) return;
193   NS_ADDREF_THIS();
195   // This will invoke ProcessGeckoEvents on the main thread.
196   ::CFRunLoopSourceSignal(mCFRunLoopSource);
197   ::CFRunLoopWakeUp(mCFRunLoop);
200 // ProcessNextNativeEvent
202 // protected virtual
203 bool nsAppShell::ProcessNextNativeEvent(bool aMayWait) {
204   if (mTerminated) return false;
206   NSString* currentMode = nil;
207   NSDate* waitUntil = nil;
208   if (aMayWait) waitUntil = [NSDate distantFuture];
209   NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
211   BOOL eventProcessed = NO;
212   do {
213     currentMode = [currentRunLoop currentMode];
214     if (!currentMode) currentMode = NSDefaultRunLoopMode;
216     if (aMayWait)
217       eventProcessed = [currentRunLoop runMode:currentMode
218                                     beforeDate:waitUntil];
219     else
220       [currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil];
221   } while (eventProcessed && aMayWait);
223   return false;
226 // Run
228 // public
229 NS_IMETHODIMP
230 nsAppShell::Run(void) {
231   ALOG("nsAppShell::Run");
232   char argv[1][4] = {"app"};
233   UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate");
234   // UIApplicationMain doesn't exit. :-(
235   return NS_OK;
238 NS_IMETHODIMP
239 nsAppShell::Exit(void) {
240   if (mTerminated) return NS_OK;
242   mTerminated = true;
243   return nsBaseAppShell::Exit();