/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 #import #import #import #include "gfxPlatform.h" #include "nsAppShell.h" #include "nsCOMPtr.h" #include "nsDirectoryServiceDefs.h" #include "nsObjCExceptions.h" #include "nsString.h" #include "nsIRollupListener.h" #include "nsIWidget.h" #include "nsThreadUtils.h" #include "nsMemoryPressure.h" #include "nsServiceManagerUtils.h" #include "mozilla/widget/ScreenManager.h" #include "ScreenHelperUIKit.h" #include "mozilla/Hal.h" #include "HeadlessScreenHelper.h" using namespace mozilla; using namespace mozilla::widget; nsAppShell* nsAppShell::gAppShell = NULL; UIWindow* nsAppShell::gWindow = nil; MOZ_RUNINIT 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 { } @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:]"); NS_NotifyOfMemoryPressure(MemoryPressureState::LowMemory); } @end // nsAppShell implementation NS_IMETHODIMP nsAppShell::ResumeNative(void) { return nsBaseAppShell::ResumeNative(); } nsAppShell::nsAppShell() : mAutoreleasePool(NULL), mDelegate(NULL), mCFRunLoop(NULL), mCFRunLoopSource(NULL), mRunningEventLoop(false), 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); hal::Init(); if (XRE_IsParentProcess()) { ScreenManager& screenManager = ScreenManager::GetSingleton(); if (gfxPlatform::IsHeadless()) { screenManager.SetHelper(mozilla::MakeUnique()); } else { screenManager.SetHelper(mozilla::MakeUnique()); } } 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(aInfo); if (self->mRunningEventLoop) { self->mRunningEventLoop = false; } 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) { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; if (mTerminated) return false; bool wasRunningEventLoop = mRunningEventLoop; mRunningEventLoop = aMayWait; NSString* currentMode = nil; NSDate* waitUntil = nil; if (aMayWait) waitUntil = [NSDate distantFuture]; NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop]; do { currentMode = [currentRunLoop currentMode]; if (!currentMode) currentMode = NSDefaultRunLoopMode; if (aMayWait) { [currentRunLoop runMode:currentMode beforeDate:waitUntil]; } else { [currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil]; } } while (mRunningEventLoop); mRunningEventLoop = wasRunningEventLoop; NS_OBJC_END_TRY_IGNORE_BLOCK; return false; } // Run // // public NS_IMETHODIMP nsAppShell::Run(void) { ALOG("nsAppShell::Run"); nsresult rv = NS_OK; if (XRE_UseNativeEventProcessing()) { char argv[1][4] = {"app"}; UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate"); // UIApplicationMain doesn't exit. :-( } else { rv = nsBaseAppShell::Run(); } return rv; } NS_IMETHODIMP nsAppShell::Exit(void) { if (mTerminated) return NS_OK; mTerminated = true; return nsBaseAppShell::Exit(); }