2018-11-30 22:52:05 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2015-06-12 15:49:57 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#import <UIKit/UIApplication.h>
|
|
|
|
#import <UIKit/UIScreen.h>
|
|
|
|
#import <UIKit/UIWindow.h>
|
|
|
|
#import <UIKit/UIViewController.h>
|
|
|
|
|
|
|
|
#include "nsAppShell.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsDirectoryServiceDefs.h"
|
|
|
|
#include "nsString.h"
|
|
|
|
#include "nsIRollupListener.h"
|
|
|
|
#include "nsIWidget.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "nsMemoryPressure.h"
|
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
|
|
|
|
nsAppShell* nsAppShell::gAppShell = NULL;
|
|
|
|
UIWindow* nsAppShell::gWindow = nil;
|
|
|
|
NSMutableArray* nsAppShell::gTopLevelViews = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
#define ALOG(args...) \
|
|
|
|
fprintf(stderr, args); \
|
|
|
|
fprintf(stderr, "\n")
|
|
|
|
|
|
|
|
// ViewController
|
|
|
|
@interface ViewController : UIViewController
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ViewController
|
|
|
|
|
|
|
|
- (void)loadView {
|
|
|
|
ALOG("[ViewController loadView]");
|
|
|
|
CGRect r = {{0, 0}, {100, 100}};
|
|
|
|
self.view = [[UIView alloc] initWithFrame:r];
|
|
|
|
[self.view setBackgroundColor:[UIColor lightGrayColor]];
|
|
|
|
// add all of the top level views as children
|
|
|
|
for (UIView* v in nsAppShell::gTopLevelViews) {
|
|
|
|
ALOG("[ViewController.view addSubView:%p]", v);
|
|
|
|
[self.view addSubview:v];
|
|
|
|
}
|
|
|
|
[nsAppShell::gTopLevelViews release];
|
|
|
|
nsAppShell::gTopLevelViews = nil;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// AppShellDelegate
|
|
|
|
//
|
|
|
|
// Acts as a delegate for the UIApplication
|
|
|
|
|
|
|
|
@interface AppShellDelegate : NSObject <UIApplicationDelegate> {
|
|
|
|
}
|
|
|
|
@property(strong, nonatomic) UIWindow* window;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation AppShellDelegate
|
|
|
|
|
|
|
|
- (BOOL)application:(UIApplication*)application
|
|
|
|
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
|
|
|
ALOG("[AppShellDelegate application:didFinishLaunchingWithOptions:]");
|
|
|
|
// We only create one window, since we can only display one window at
|
|
|
|
// a time anyway. Also, iOS 4 fails to display UIWindows if you
|
|
|
|
// create them before calling UIApplicationMain, so this makes more sense.
|
|
|
|
nsAppShell::gWindow =
|
|
|
|
[[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] retain];
|
|
|
|
self.window = nsAppShell::gWindow;
|
|
|
|
|
|
|
|
self.window.rootViewController = [[ViewController alloc] init];
|
|
|
|
|
|
|
|
// just to make things more visible for now
|
|
|
|
nsAppShell::gWindow.backgroundColor = [UIColor blueColor];
|
|
|
|
[nsAppShell::gWindow makeKeyAndVisible];
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationWillTerminate:(UIApplication*)application {
|
|
|
|
ALOG("[AppShellDelegate applicationWillTerminate:]");
|
|
|
|
nsAppShell::gAppShell->WillTerminate();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationDidBecomeActive:(UIApplication*)application {
|
|
|
|
ALOG("[AppShellDelegate applicationDidBecomeActive:]");
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationWillResignActive:(UIApplication*)application {
|
|
|
|
ALOG("[AppShellDelegate applicationWillResignActive:]");
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application {
|
|
|
|
ALOG("[AppShellDelegate applicationDidReceiveMemoryWarning:]");
|
2021-07-07 01:30:58 +03:00
|
|
|
NS_NotifyOfMemoryPressure(MemoryPressureState::LowMemory);
|
2015-06-12 15:49:57 +03:00
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// nsAppShell implementation
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAppShell::ResumeNative(void) { return nsBaseAppShell::ResumeNative(); }
|
|
|
|
|
|
|
|
nsAppShell::nsAppShell()
|
|
|
|
: mAutoreleasePool(NULL),
|
|
|
|
mDelegate(NULL),
|
|
|
|
mCFRunLoop(NULL),
|
|
|
|
mCFRunLoopSource(NULL),
|
|
|
|
mTerminated(false),
|
|
|
|
mNotifiedWillTerminate(false) {
|
|
|
|
gAppShell = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAppShell::~nsAppShell() {
|
|
|
|
if (mAutoreleasePool) {
|
|
|
|
[mAutoreleasePool release];
|
|
|
|
mAutoreleasePool = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mCFRunLoop) {
|
|
|
|
if (mCFRunLoopSource) {
|
|
|
|
::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
|
|
|
|
::CFRelease(mCFRunLoopSource);
|
|
|
|
}
|
|
|
|
::CFRelease(mCFRunLoop);
|
|
|
|
}
|
|
|
|
|
|
|
|
gAppShell = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init
|
|
|
|
//
|
|
|
|
// public
|
|
|
|
nsresult nsAppShell::Init() {
|
|
|
|
mAutoreleasePool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
|
|
|
// Add a CFRunLoopSource to the main native run loop. The source is
|
|
|
|
// responsible for interrupting the run loop when Gecko events are ready.
|
|
|
|
|
|
|
|
mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
|
|
|
|
NS_ENSURE_STATE(mCFRunLoop);
|
|
|
|
::CFRetain(mCFRunLoop);
|
|
|
|
|
|
|
|
CFRunLoopSourceContext context;
|
|
|
|
bzero(&context, sizeof(context));
|
|
|
|
// context.version = 0;
|
|
|
|
context.info = this;
|
|
|
|
context.perform = ProcessGeckoEvents;
|
|
|
|
|
|
|
|
mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
|
|
|
|
NS_ENSURE_STATE(mCFRunLoopSource);
|
|
|
|
|
|
|
|
::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
|
|
|
|
|
|
|
|
return nsBaseAppShell::Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessGeckoEvents
|
|
|
|
//
|
|
|
|
// The "perform" target of mCFRunLoop, called when mCFRunLoopSource is
|
|
|
|
// signalled from ScheduleNativeEventCallback.
|
|
|
|
//
|
|
|
|
// protected static
|
|
|
|
void nsAppShell::ProcessGeckoEvents(void* aInfo) {
|
|
|
|
nsAppShell* self = static_cast<nsAppShell*>(aInfo);
|
|
|
|
self->NativeEventCallback();
|
|
|
|
self->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
// WillTerminate
|
|
|
|
//
|
|
|
|
// public
|
|
|
|
void nsAppShell::WillTerminate() {
|
|
|
|
mNotifiedWillTerminate = true;
|
|
|
|
if (mTerminated) return;
|
|
|
|
mTerminated = true;
|
|
|
|
// We won't get another chance to process events
|
|
|
|
NS_ProcessPendingEvents(NS_GetCurrentThread());
|
|
|
|
|
|
|
|
// Unless we call nsBaseAppShell::Exit() here, it might not get called
|
|
|
|
// at all.
|
|
|
|
nsBaseAppShell::Exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ScheduleNativeEventCallback
|
|
|
|
//
|
|
|
|
// protected virtual
|
|
|
|
void nsAppShell::ScheduleNativeEventCallback() {
|
|
|
|
if (mTerminated) return;
|
|
|
|
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
|
|
|
|
// This will invoke ProcessGeckoEvents on the main thread.
|
|
|
|
::CFRunLoopSourceSignal(mCFRunLoopSource);
|
|
|
|
::CFRunLoopWakeUp(mCFRunLoop);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessNextNativeEvent
|
|
|
|
//
|
|
|
|
// protected virtual
|
|
|
|
bool nsAppShell::ProcessNextNativeEvent(bool aMayWait) {
|
|
|
|
if (mTerminated) return false;
|
2019-01-21 20:18:16 +03:00
|
|
|
|
2015-06-12 15:49:57 +03:00
|
|
|
NSString* currentMode = nil;
|
|
|
|
NSDate* waitUntil = nil;
|
|
|
|
if (aMayWait) waitUntil = [NSDate distantFuture];
|
|
|
|
NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
|
|
|
|
|
|
|
|
BOOL eventProcessed = NO;
|
|
|
|
do {
|
|
|
|
currentMode = [currentRunLoop currentMode];
|
|
|
|
if (!currentMode) currentMode = NSDefaultRunLoopMode;
|
|
|
|
|
|
|
|
if (aMayWait)
|
|
|
|
eventProcessed = [currentRunLoop runMode:currentMode beforeDate:waitUntil];
|
|
|
|
else
|
|
|
|
[currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil];
|
|
|
|
} while (eventProcessed && aMayWait);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run
|
|
|
|
//
|
|
|
|
// public
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAppShell::Run(void) {
|
|
|
|
ALOG("nsAppShell::Run");
|
|
|
|
char argv[1][4] = {"app"};
|
|
|
|
UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate");
|
|
|
|
// UIApplicationMain doesn't exit. :-(
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAppShell::Exit(void) {
|
|
|
|
if (mTerminated) return NS_OK;
|
|
|
|
|
|
|
|
mTerminated = true;
|
|
|
|
return nsBaseAppShell::Exit();
|
|
|
|
}
|