bug 1171113 - Add widget/uikit. r=roc (NPOTB)
authorTed Mielczarek <ted@mielczarek.org>
Fri, 12 Jun 2015 08:49:57 -0400
changeset 248753 986edcc945241f9d2a2244a1f747c557afbedd3c
parent 248752 600c02fb4be271987e90f6fc0e61f33c05baa1d7
child 248754 6f894e0e33adda8bc3ed35b3653f29bd4230ee65
push id28904
push userphilringnalda@gmail.com
push dateSun, 14 Jun 2015 02:49:28 +0000
treeherdermozilla-central@c223b8844264 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1171113
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1171113 - Add widget/uikit. r=roc (NPOTB)
modules/libpref/init/all.js
widget/GfxDriverInfo.h
widget/moz.build
widget/nsBaseWidget.cpp
widget/uikit/GfxInfo.cpp
widget/uikit/GfxInfo.h
widget/uikit/moz.build
widget/uikit/nsAppShell.h
widget/uikit/nsAppShell.mm
widget/uikit/nsLookAndFeel.h
widget/uikit/nsLookAndFeel.mm
widget/uikit/nsScreenManager.h
widget/uikit/nsScreenManager.mm
widget/uikit/nsWidgetFactory.mm
widget/uikit/nsWindow.h
widget/uikit/nsWindow.mm
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4211,21 +4211,30 @@ pref("layers.offmainthreadcomposition.en
 // 0  -> full-tilt mode: Recomposite even if not transaction occured.
 pref("layers.offmainthreadcomposition.frame-rate", -1);
 
 // Asynchonous video compositing using the ImageBridge IPDL protocol.
 // requires off-main-thread compositing.
 pref("layers.async-video.enabled", true);
 pref("layers.async-video-oop.enabled",true);
 
+#ifdef MOZ_WIDGET_UIKIT
+pref("layers.async-pan-zoom.enabled", true);
+#endif
+
+#ifdef MOZ_WIDGET_UIKIT
+pref("layers.async-pan-zoom.enabled", true);
+#endif
+
 #ifdef XP_MACOSX
 pref("layers.enable-tiles", true);
 pref("layers.tiled-drawtarget.enabled", true);
 #endif
 
+
 // same effect as layers.offmainthreadcomposition.enabled, but specifically for
 // use with tests.
 pref("layers.offmainthreadcomposition.testing.enabled", false);
 
 // whether to allow use of the basic compositor
 pref("layers.offmainthreadcomposition.force-basic", false);
 
 // Whether to animate simple opacity and transforms on the compositor
--- a/widget/GfxDriverInfo.h
+++ b/widget/GfxDriverInfo.h
@@ -51,16 +51,17 @@ enum OperatingSystem {
   DRIVER_OS_LINUX,
   DRIVER_OS_OS_X_10_5,
   DRIVER_OS_OS_X_10_6,
   DRIVER_OS_OS_X_10_7,
   DRIVER_OS_OS_X_10_8,
   DRIVER_OS_OS_X_10_9,
   DRIVER_OS_OS_X_10_10,
   DRIVER_OS_ANDROID,
+  DRIVER_OS_IOS,
   DRIVER_OS_ALL
 };
 
 enum VersionComparisonOp {
   DRIVER_LESS_THAN,             // driver <  version
   DRIVER_LESS_THAN_OR_EQUAL,    // driver <= version
   DRIVER_GREATER_THAN,          // driver >  version
   DRIVER_GREATER_THAN_OR_EQUAL, // driver >= version
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -1,17 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
 
-if toolkit in ('cocoa', 'qt', 'android', 'gonk'):
+if toolkit in ('cocoa', 'qt', 'android', 'gonk', 'uikit'):
     DIRS += [toolkit]
 if toolkit in ('qt', 'android', 'gonk', 'gtk2', 'gtk3'):
     EXPORTS += ['nsIPrintDialogService.h']
 
 if toolkit == 'windows':
     DIRS += ['windows']
 
     XPIDL_SOURCES += [
@@ -196,18 +196,18 @@ if CONFIG['MOZ_X11']:
         'GfxInfoX11.cpp'
     ]
 
 if toolkit in ('cocoa', 'windows'):
     UNIFIED_SOURCES += [
         'nsBaseClipboard.cpp',
     ]
 
-if toolkit in ('qt', 'gtk2', 'gtk3', 'cocoa', 'windows',
-                                    'android', 'gonk'):
+if toolkit in {'qt', 'gtk2', 'gtk3', 'cocoa', 'windows',
+               'android', 'gonk', 'uikit'}:
     UNIFIED_SOURCES += [
         'nsBaseFilePicker.cpp',
     ]
 
 if toolkit in ('qt', 'gtk2', 'gtk3', 'windows', 'cocoa'):
     UNIFIED_SOURCES += [
         'nsNativeTheme.cpp',
     ]
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -899,17 +899,18 @@ nsBaseWidget::ComputeShouldAccelerate(bo
 #if defined (XP_MACOSX)
   // 10.6.2 and lower have a bug involving textures and pixel buffer objects
   // that caused bug 629016, so we don't allow OpenGL-accelerated layers on
   // those versions of the OS.
   // This will still let full-screen video be accelerated on OpenGL, because
   // that XUL widget opts in to acceleration, but that's probably OK.
   bool accelerateByDefault = nsCocoaFeatures::AccelerateByDefault();
 #elif defined(XP_WIN) || defined(ANDROID) || \
-    defined(MOZ_GL_PROVIDER) || defined(MOZ_WIDGET_QT)
+    defined(MOZ_GL_PROVIDER) || defined(MOZ_WIDGET_QT) || \
+    defined(MOZ_WIDGET_UIKIT)
   bool accelerateByDefault = true;
 #else
   bool accelerateByDefault = false;
 #endif
 
   // If the platform is accelerated by default or the environment
   // variable is set, we accelerate:
   const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED");
new file mode 100644
--- /dev/null
+++ b/widget/uikit/GfxInfo.cpp
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "GfxInfo.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+namespace widget {
+
+
+#ifdef DEBUG
+NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug)
+#endif
+
+GfxInfo::GfxInfo()
+{
+}
+
+GfxInfo::~GfxInfo()
+{
+}
+
+nsresult
+GfxInfo::GetD2DEnabled(bool *aEnabled)
+{
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
+GfxInfo::GetDWriteEnabled(bool *aEnabled)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString DWriteVersion; */
+NS_IMETHODIMP
+GfxInfo::GetDWriteVersion(nsAString & aDwriteVersion)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString cleartypeParameters; */
+NS_IMETHODIMP
+GfxInfo::GetCleartypeParameters(nsAString & aCleartypeParams)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDescription; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDescription(nsAString & aAdapterDescription)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDescription2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDescription2(nsAString & aAdapterDescription)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterRAM; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterRAM2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterRAM2(nsAString & aAdapterRAM)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDriver; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriver(nsAString & aAdapterDriver)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDriver2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriver2(nsAString & aAdapterDriver)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDriverVersion; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDriverVersion2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDriverDate; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverDate(nsAString & aAdapterDriverDate)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDriverDate2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDriverDate2(nsAString & aAdapterDriverDate)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterVendorID; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterVendorID(nsAString & aAdapterVendorID)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterVendorID2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterVendorID2(nsAString & aAdapterVendorID)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterDeviceID; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDeviceID(nsAString & aAdapterDeviceID)
+{
+  return NS_ERROR_FAILURE;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString adapterDeviceID2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterDeviceID2(nsAString & aAdapterDeviceID)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterSubsysID; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterSubsysID(nsAString & aAdapterSubsysID)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute DOMString adapterSubsysID2; */
+NS_IMETHODIMP
+GfxInfo::GetAdapterSubsysID2(nsAString & aAdapterSubsysID)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* readonly attribute boolean isGPU2Active; */
+NS_IMETHODIMP
+GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active)
+{
+  return NS_ERROR_FAILURE;
+}
+
+const nsTArray<GfxDriverInfo>&
+GfxInfo::GetGfxDriverInfo()
+{
+  if (mDriverInfo->IsEmpty()) {
+    APPEND_TO_DRIVER_BLOCKLIST2( DRIVER_OS_ALL,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAll), GfxDriverInfo::allDevices,
+      nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_STATUS_OK,
+      DRIVER_COMPARISON_IGNORED, GfxDriverInfo::allDriverVersions );
+  }
+
+  return *mDriverInfo;
+}
+
+nsresult
+GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
+                              int32_t *aStatus,
+                              nsAString & aSuggestedDriverVersion,
+                              const nsTArray<GfxDriverInfo>& aDriverInfo,
+                              OperatingSystem* aOS /* = nullptr */)
+{
+  NS_ENSURE_ARG_POINTER(aStatus);
+  aSuggestedDriverVersion.SetIsVoid(true);
+  *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
+  if (aOS)
+    *aOS = DRIVER_OS_IOS;
+
+  // OpenGL layers are never blacklisted on iOS.
+  // This early return is so we avoid potentially slow
+  // GLStrings initialization on startup when we initialize GL layers.
+  if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS ||
+      aFeature == nsIGfxInfo::FEATURE_WEBGL_OPENGL ||
+      aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA) {
+    *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
+    return NS_OK;
+  }
+
+  return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aOS);
+}
+
+#ifdef DEBUG
+
+// Implement nsIGfxInfoDebug
+
+/* void spoofVendorID (in DOMString aVendorID); */
+NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* void spoofDeviceID (in unsigned long aDeviceID); */
+NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* void spoofDriverVersion (in DOMString aDriverVersion); */
+NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion)
+{
+  return NS_ERROR_FAILURE;
+}
+
+/* void spoofOSVersion (in unsigned long aVersion); */
+NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion)
+{
+  return NS_ERROR_FAILURE;
+}
+
+#endif
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/widget/uikit/GfxInfo.h
@@ -0,0 +1,78 @@
+/* vim: se cin sw=2 ts=2 et : */
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef __mozilla_widget_GfxInfo_h__
+#define __mozilla_widget_GfxInfo_h__
+
+#include "GfxInfoBase.h"
+#include "GfxDriverInfo.h"
+
+#include "nsString.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+namespace gl {
+class GLContext;
+}
+
+namespace widget {
+
+class GfxInfo : public GfxInfoBase
+{
+private:
+  ~GfxInfo();
+
+public:
+  GfxInfo();
+
+  // We only declare the subset of nsIGfxInfo that we actually implement. The
+  // rest is brought forward from GfxInfoBase.
+  NS_IMETHOD GetD2DEnabled(bool *aD2DEnabled);
+  NS_IMETHOD GetDWriteEnabled(bool *aDWriteEnabled);
+  NS_IMETHOD GetDWriteVersion(nsAString & aDwriteVersion);
+  NS_IMETHOD GetCleartypeParameters(nsAString & aCleartypeParams);
+  NS_IMETHOD GetAdapterDescription(nsAString & aAdapterDescription);
+  NS_IMETHOD GetAdapterDriver(nsAString & aAdapterDriver);
+  NS_IMETHOD GetAdapterVendorID(nsAString & aAdapterVendorID);
+  NS_IMETHOD GetAdapterDeviceID(nsAString & aAdapterDeviceID);
+  NS_IMETHOD GetAdapterSubsysID(nsAString & aAdapterSubsysID);
+  NS_IMETHOD GetAdapterRAM(nsAString & aAdapterRAM);
+  NS_IMETHOD GetAdapterDriverVersion(nsAString & aAdapterDriverVersion);
+  NS_IMETHOD GetAdapterDriverDate(nsAString & aAdapterDriverDate);
+  NS_IMETHOD GetAdapterDescription2(nsAString & aAdapterDescription);
+  NS_IMETHOD GetAdapterDriver2(nsAString & aAdapterDriver);
+  NS_IMETHOD GetAdapterVendorID2(nsAString & aAdapterVendorID);
+  NS_IMETHOD GetAdapterDeviceID2(nsAString & aAdapterDeviceID);
+  NS_IMETHOD GetAdapterSubsysID2(nsAString & aAdapterSubsysID);
+  NS_IMETHOD GetAdapterRAM2(nsAString & aAdapterRAM);
+  NS_IMETHOD GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion);
+  NS_IMETHOD GetAdapterDriverDate2(nsAString & aAdapterDriverDate);
+  NS_IMETHOD GetIsGPU2Active(bool *aIsGPU2Active);
+  using GfxInfoBase::GetFeatureStatus;
+  using GfxInfoBase::GetFeatureSuggestedDriverVersion;
+  using GfxInfoBase::GetWebGLParameter;
+
+#ifdef DEBUG
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIGFXINFODEBUG
+#endif
+
+protected:
+
+  virtual nsresult GetFeatureStatusImpl(int32_t aFeature,
+                                        int32_t *aStatus,
+                                        nsAString & aSuggestedDriverVersion,
+                                        const nsTArray<GfxDriverInfo>& aDriverInfo,
+                                        OperatingSystem* aOS = nullptr);
+  virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo();
+};
+
+} // namespace widget
+} // namespace mozilla
+
+#endif /* __mozilla_widget_GfxInfo_h__ */
new file mode 100644
--- /dev/null
+++ b/widget/uikit/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+    'GfxInfo.cpp',
+    'nsAppShell.mm',
+    'nsLookAndFeel.mm',
+    'nsScreenManager.mm',
+    'nsWidgetFactory.mm',
+    'nsWindow.mm',
+]
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+    '/widget',
+]
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsAppShell.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Runs the main native UIKit run loop, interrupting it as needed to process
+ * Gecko events.
+ */
+
+#ifndef nsAppShell_h_
+#define nsAppShell_h_
+
+#include "nsBaseAppShell.h"
+#include "nsTArray.h"
+
+#include <Foundation/NSAutoreleasePool.h>
+#include <CoreFoundation/CFRunLoop.h>
+#include <UIKit/UIWindow.h>
+
+@class AppShellDelegate;
+
+class nsAppShell : public nsBaseAppShell
+{
+public:
+  NS_IMETHOD ResumeNative(void);
+
+  nsAppShell();
+
+  nsresult Init();
+
+  NS_IMETHOD Run(void);
+  NS_IMETHOD Exit(void);
+  // Called by the application delegate
+  void WillTerminate(void);
+
+  static nsAppShell* gAppShell;
+  static UIWindow* gWindow;
+  static NSMutableArray* gTopLevelViews;
+
+protected:
+  virtual ~nsAppShell();
+
+  static void ProcessGeckoEvents(void* aInfo);
+  virtual void ScheduleNativeEventCallback();
+  virtual bool ProcessNextNativeEvent(bool aMayWait);
+
+  NSAutoreleasePool* mAutoreleasePool;
+  AppShellDelegate*  mDelegate;
+  CFRunLoopRef       mCFRunLoop;
+  CFRunLoopSourceRef mCFRunLoopSource;
+
+  bool               mTerminated;
+  bool               mNotifiedWillTerminate;
+};
+
+#endif // nsAppShell_h_
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsAppShell.mm
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIFile.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsString.h"
+#include "nsIRollupListener.h"
+#include "nsIWidget.h"
+#include "nsThreadUtils.h"
+#include "nsIWindowMediator.h"
+#include "nsMemoryPressure.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIWebBrowserChrome.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:]");
+  NS_DispatchMemoryPressure(MemPressure_New);
+}
+@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;
+
+  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();
+}
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsLookAndFeel.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef __nsLookAndFeel
+#define __nsLookAndFeel
+
+#include "nsXPLookAndFeel.h"
+
+class nsLookAndFeel: public nsXPLookAndFeel
+{
+public:
+    nsLookAndFeel();
+    virtual ~nsLookAndFeel();
+
+    virtual nsresult NativeGetColor(const ColorID aID, nscolor &aResult);
+    virtual nsresult GetIntImpl(IntID aID, int32_t &aResult);
+    virtual nsresult GetFloatImpl(FloatID aID, float &aResult);
+    virtual bool GetFontImpl(FontID aID, nsString& aFontName,
+                             gfxFontStyle& aFontStyle,
+                             float aDevPixPerCSSPixel);
+    virtual char16_t GetPasswordCharacterImpl()
+    {
+        // unicode value for the bullet character, used for password textfields.
+        return 0x2022;
+    }
+
+    static bool UseOverlayScrollbars()
+    {
+        return true;
+    }
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsLookAndFeel.mm
@@ -0,0 +1,397 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/UIColor.h>
+#import <UIKit/UIInterface.h>
+
+#include "nsLookAndFeel.h"
+#include "nsStyleConsts.h"
+#include "gfxFont.h"
+#include "gfxFontConstants.h"
+
+nsLookAndFeel::nsLookAndFeel()
+    : nsXPLookAndFeel()
+{
+}
+
+nsLookAndFeel::~nsLookAndFeel()
+{
+}
+
+static nscolor GetColorFromUIColor(UIColor* aColor)
+{
+    CGColorRef cgColor = [aColor CGColor];
+    CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(cgColor));
+    const CGFloat* components = CGColorGetComponents(cgColor);
+    if (model == kCGColorSpaceModelRGB) {
+        return NS_RGB((unsigned int)(components[0] * 255.0),
+                      (unsigned int)(components[1] * 255.0),
+                      (unsigned int)(components[2] * 255.0));
+    }
+    else if (model == kCGColorSpaceModelMonochrome) {
+        unsigned int val = (unsigned int)(components[0] * 255.0);
+        return NS_RGBA(val, val, val,
+                       (unsigned int)(components[1] * 255.0));
+    }
+    NS_NOTREACHED("Unhandled color space!");
+    return 0;
+}
+
+nsresult
+nsLookAndFeel::NativeGetColor(const ColorID aID, nscolor &aResult)
+{
+  nsresult res = NS_OK;
+
+  switch (aID) {
+    case eColorID_WindowBackground:
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID_WindowForeground:
+      aResult = NS_RGB(0x00,0x00,0x00);
+      break;
+    case eColorID_WidgetBackground:
+      aResult = NS_RGB(0xdd,0xdd,0xdd);
+      break;
+    case eColorID_WidgetForeground:
+      aResult = NS_RGB(0x00,0x00,0x00);
+      break;
+    case eColorID_WidgetSelectBackground:
+      aResult = NS_RGB(0x80,0x80,0x80);
+      break;
+    case eColorID_WidgetSelectForeground:
+      aResult = NS_RGB(0x00,0x00,0x80);
+      break;
+    case eColorID_Widget3DHighlight:
+      aResult = NS_RGB(0xa0,0xa0,0xa0);
+      break;
+    case eColorID_Widget3DShadow:
+      aResult = NS_RGB(0x40,0x40,0x40);
+      break;
+    case eColorID_TextBackground:
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID_TextForeground:
+      aResult = NS_RGB(0x00,0x00,0x00);
+      break;
+    case eColorID_TextSelectBackground:
+    case eColorID_highlight: // CSS2 color
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID__moz_menuhover:
+      aResult = NS_RGB(0xee,0xee,0xee);
+      break;
+    case eColorID_TextSelectForeground:
+    case eColorID_highlighttext:  // CSS2 color
+    case eColorID__moz_menuhovertext:
+      GetColor(eColorID_TextSelectBackground, aResult);
+      if (aResult == 0x000000)
+        aResult = NS_RGB(0xff,0xff,0xff);
+      else
+        aResult = NS_DONT_CHANGE_COLOR;
+      break;
+    case eColorID_IMESelectedRawTextBackground:
+    case eColorID_IMESelectedConvertedTextBackground:
+    case eColorID_IMERawInputBackground:
+    case eColorID_IMEConvertedTextBackground:
+      aResult = NS_TRANSPARENT;
+      break;
+    case eColorID_IMESelectedRawTextForeground:
+    case eColorID_IMESelectedConvertedTextForeground:
+    case eColorID_IMERawInputForeground:
+    case eColorID_IMEConvertedTextForeground:
+      aResult = NS_SAME_AS_FOREGROUND_COLOR;
+      break;
+    case eColorID_IMERawInputUnderline:
+    case eColorID_IMEConvertedTextUnderline:
+      aResult = NS_40PERCENT_FOREGROUND_COLOR;
+      break;
+    case eColorID_IMESelectedRawTextUnderline:
+    case eColorID_IMESelectedConvertedTextUnderline:
+      aResult = NS_SAME_AS_FOREGROUND_COLOR;
+      break;
+    case eColorID_SpellCheckerUnderline:
+      aResult = NS_RGB(0xff, 0, 0);
+      break;
+
+    //
+    // css2 system colors http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
+    //
+    case eColorID_buttontext:
+    case eColorID__moz_buttonhovertext:
+    case eColorID_captiontext:
+    case eColorID_menutext:
+    case eColorID_infotext:
+    case eColorID__moz_menubartext:
+    case eColorID_windowtext:
+      aResult = GetColorFromUIColor([UIColor darkTextColor]);
+      break;
+    case eColorID_activecaption:
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID_activeborder:
+      aResult = NS_RGB(0x00,0x00,0x00);
+      break;
+     case eColorID_appworkspace:
+      aResult = NS_RGB(0xFF,0xFF,0xFF);
+      break;
+    case eColorID_background:
+      aResult = NS_RGB(0x63,0x63,0xCE);
+      break;
+    case eColorID_buttonface:
+    case eColorID__moz_buttonhoverface:
+      aResult = NS_RGB(0xF0,0xF0,0xF0);
+      break;
+    case eColorID_buttonhighlight:
+      aResult = NS_RGB(0xFF,0xFF,0xFF);
+      break;
+    case eColorID_buttonshadow:
+      aResult = NS_RGB(0xDC,0xDC,0xDC);
+      break;
+    case eColorID_graytext:
+      aResult = NS_RGB(0x44,0x44,0x44);
+      break;
+    case eColorID_inactiveborder:
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID_inactivecaption:
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID_inactivecaptiontext:
+      aResult = NS_RGB(0x45,0x45,0x45);
+      break;
+    case eColorID_scrollbar:
+      aResult = NS_RGB(0,0,0); //XXX
+      break;
+    case eColorID_threeddarkshadow:
+      aResult = NS_RGB(0xDC,0xDC,0xDC);
+      break;
+    case eColorID_threedshadow:
+      aResult = NS_RGB(0xE0,0xE0,0xE0);
+      break;
+    case eColorID_threedface:
+      aResult = NS_RGB(0xF0,0xF0,0xF0);
+      break;
+    case eColorID_threedhighlight:
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID_threedlightshadow:
+      aResult = NS_RGB(0xDA,0xDA,0xDA);
+      break;
+    case eColorID_menu:
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID_infobackground:
+      aResult = NS_RGB(0xFF,0xFF,0xC7);
+      break;
+    case eColorID_windowframe:
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID_window:
+    case eColorID__moz_field:
+    case eColorID__moz_combobox:
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID__moz_fieldtext:
+    case eColorID__moz_comboboxtext:
+      aResult = GetColorFromUIColor([UIColor darkTextColor]);
+      break;
+    case eColorID__moz_dialog:
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID__moz_dialogtext:
+    case eColorID__moz_cellhighlighttext:
+    case eColorID__moz_html_cellhighlighttext:
+      aResult = GetColorFromUIColor([UIColor darkTextColor]);
+      break;
+    case eColorID__moz_dragtargetzone:
+    case eColorID__moz_mac_chrome_active:
+    case eColorID__moz_mac_chrome_inactive:
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID__moz_mac_focusring:
+      aResult = NS_RGB(0x3F,0x98,0xDD);
+      break;
+    case eColorID__moz_mac_menushadow:
+      aResult = NS_RGB(0xA3,0xA3,0xA3);
+      break;
+    case eColorID__moz_mac_menutextdisable:
+      aResult = NS_RGB(0x88,0x88,0x88);
+      break;
+    case eColorID__moz_mac_menutextselect:
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID__moz_mac_disabledtoolbartext:
+      aResult = NS_RGB(0x3F,0x3F,0x3F);
+      break;
+    case eColorID__moz_mac_menuselect:
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID__moz_buttondefault:
+      aResult = NS_RGB(0xDC,0xDC,0xDC);
+      break;
+    case eColorID__moz_cellhighlight:
+    case eColorID__moz_html_cellhighlight:
+    case eColorID__moz_mac_secondaryhighlight:
+      // For inactive list selection
+      aResult = NS_RGB(0xaa,0xaa,0xaa);
+      break;
+    case eColorID__moz_eventreerow:
+      // Background color of even list rows.
+      aResult = NS_RGB(0xff,0xff,0xff);
+      break;
+    case eColorID__moz_oddtreerow:
+      // Background color of odd list rows.
+      aResult = NS_TRANSPARENT;
+      break;
+    case eColorID__moz_nativehyperlinktext:
+      // There appears to be no available system defined color. HARDCODING to the appropriate color.
+      aResult = NS_RGB(0x14,0x4F,0xAE);
+      break;
+    default:
+      NS_WARNING("Someone asked nsILookAndFeel for a color I don't know about");
+      aResult = NS_RGB(0xff,0xff,0xff);
+      res = NS_ERROR_FAILURE;
+      break;
+    }
+
+  return res;
+}
+
+NS_IMETHODIMP
+nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
+{
+  nsresult res = nsXPLookAndFeel::GetIntImpl(aID, aResult);
+  if (NS_SUCCEEDED(res))
+    return res;
+  res = NS_OK;
+
+  switch (aID) {
+    case eIntID_CaretBlinkTime:
+      aResult = 567;
+      break;
+    case eIntID_CaretWidth:
+      aResult = 1;
+      break;
+    case eIntID_ShowCaretDuringSelection:
+      aResult = 0;
+      break;
+    case eIntID_SelectTextfieldsOnKeyFocus:
+      // Select textfield content when focused by kbd
+      // used by nsEventStateManager::sTextfieldSelectModel
+      aResult = 1;
+      break;
+    case eIntID_SubmenuDelay:
+      aResult = 200;
+      break;
+    case eIntID_MenusCanOverlapOSBar:
+      // xul popups are not allowed to overlap the menubar.
+      aResult = 0;
+      break;
+    case eIntID_SkipNavigatingDisabledMenuItem:
+      aResult = 1;
+      break;
+    case eIntID_DragThresholdX:
+    case eIntID_DragThresholdY:
+      aResult = 4;
+      break;
+    case eIntID_ScrollArrowStyle:
+      aResult = eScrollArrow_None;
+      break;
+    case eIntID_ScrollSliderStyle:
+      aResult = eScrollThumbStyle_Proportional;
+      break;
+    case eIntID_TreeOpenDelay:
+      aResult = 1000;
+      break;
+    case eIntID_TreeCloseDelay:
+      aResult = 1000;
+      break;
+    case eIntID_TreeLazyScrollDelay:
+      aResult = 150;
+      break;
+    case eIntID_TreeScrollDelay:
+      aResult = 100;
+      break;
+    case eIntID_TreeScrollLinesMax:
+      aResult = 3;
+      break;
+    case eIntID_DWMCompositor:
+    case eIntID_WindowsClassic:
+    case eIntID_WindowsDefaultTheme:
+    case eIntID_TouchEnabled:
+      aResult = 0;
+      res = NS_ERROR_NOT_IMPLEMENTED;
+      break;
+    case eIntID_MacGraphiteTheme:
+      aResult = 0;
+      break;
+    case eIntID_TabFocusModel:
+      aResult = 1;    // default to just textboxes
+      break;
+    case eIntID_ScrollToClick:
+      aResult = 0;
+      break;
+    case eIntID_ChosenMenuItemsShouldBlink:
+      aResult = 1;
+      break;
+    case eIntID_IMERawInputUnderlineStyle:
+    case eIntID_IMEConvertedTextUnderlineStyle:
+    case eIntID_IMESelectedRawTextUnderlineStyle:
+    case eIntID_IMESelectedConvertedTextUnderline:
+      aResult = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
+      break;
+    case eIntID_SpellCheckerUnderlineStyle:
+      aResult = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED;
+      break;
+    default:
+      aResult = 0;
+      res = NS_ERROR_FAILURE;
+  }
+  return res;
+}
+
+NS_IMETHODIMP
+nsLookAndFeel::GetFloatImpl(FloatID aID, float &aResult)
+{
+  nsresult res = nsXPLookAndFeel::GetFloatImpl(aID, aResult);
+  if (NS_SUCCEEDED(res))
+    return res;
+  res = NS_OK;
+
+  switch (aID) {
+    case eFloatID_IMEUnderlineRelativeSize:
+      aResult = 2.0f;
+      break;
+    case eFloatID_SpellCheckerUnderlineRelativeSize:
+      aResult = 2.0f;
+      break;
+    default:
+      aResult = -1.0;
+      res = NS_ERROR_FAILURE;
+  }
+
+  return res;
+}
+
+bool
+nsLookAndFeel::GetFontImpl(FontID aID, nsString &aFontName,
+                           gfxFontStyle &aFontStyle,
+                           float aDevPixPerCSSPixel)
+{
+    // hack for now
+    if (aID == eFont_Window || aID == eFont_Document) {
+        aFontStyle.style      = NS_FONT_STYLE_NORMAL;
+        aFontStyle.weight     = NS_FONT_WEIGHT_NORMAL;
+        aFontStyle.stretch    = NS_FONT_STRETCH_NORMAL;
+        aFontStyle.size       = 14 * aDevPixPerCSSPixel;
+        aFontStyle.systemFont = true;
+
+        aFontName.AssignLiteral("sans-serif");
+        return true;
+    }
+
+    //TODO: implement more here?
+    return false;
+}
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsScreenManager.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef nsScreenManager_h_
+#define nsScreenManager_h_
+
+#include "nsBaseScreen.h"
+#include "nsIScreenManager.h"
+#include "nsCOMPtr.h"
+#include "nsRect.h"
+
+@class UIScreen;
+
+class UIKitScreen : public nsBaseScreen
+{
+public:
+    explicit UIKitScreen (UIScreen* screen);
+    ~UIKitScreen () {}
+
+    NS_IMETHOD GetId(uint32_t* outId) {
+        *outId = 0;
+        return NS_OK;
+    }
+
+    NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
+    NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
+    NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
+    NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
+    NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth);
+    NS_IMETHOD GetColorDepth(int32_t* aColorDepth);
+    NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor);
+
+private:
+    UIScreen* mScreen;
+};
+
+class UIKitScreenManager : public nsIScreenManager
+{
+public:
+    UIKitScreenManager ();
+
+    NS_DECL_ISUPPORTS
+
+    NS_DECL_NSISCREENMANAGER
+
+    static nsIntRect GetBounds();
+
+private:
+    virtual ~UIKitScreenManager () {}
+    //TODO: support >1 screen, iPad supports external displays
+    nsCOMPtr<nsIScreen> mScreen;
+};
+
+#endif // nsScreenManager_h_
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsScreenManager.mm
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/UIScreen.h>
+
+#include "gfxPoint.h"
+#include "nsScreenManager.h"
+#include "nsAppShell.h"
+
+static nsIntRect gScreenBounds;
+static bool gScreenBoundsSet = false;
+
+UIKitScreen::UIKitScreen(UIScreen* aScreen)
+{
+    mScreen = [aScreen retain];
+}
+
+NS_IMETHODIMP
+UIKitScreen::GetRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight)
+{
+    return GetRectDisplayPix(outX, outY, outWidth, outHeight);
+}
+
+NS_IMETHODIMP
+UIKitScreen::GetAvailRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight)
+{
+    return GetAvailRectDisplayPix(outX, outY, outWidth, outHeight);
+}
+
+NS_IMETHODIMP
+UIKitScreen::GetRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight)
+{
+    nsIntRect rect = UIKitScreenManager::GetBounds();
+    *outX = rect.x;
+    *outY = rect.y;
+    *outWidth = rect.width;
+    *outHeight = rect.height;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+UIKitScreen::GetAvailRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight)
+{
+    CGRect rect = [mScreen applicationFrame];
+    CGFloat scale = [mScreen scale];
+
+    *outX = rect.origin.x * scale;
+    *outY = rect.origin.y * scale;
+    *outWidth = rect.size.width * scale;
+    *outHeight = rect.size.height * scale;
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+UIKitScreen::GetPixelDepth(int32_t *aPixelDepth)
+{
+  // Close enough.
+  *aPixelDepth = 24;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UIKitScreen::GetColorDepth(int32_t *aColorDepth)
+{
+  return GetPixelDepth(aColorDepth);
+}
+
+NS_IMETHODIMP
+UIKitScreen::GetContentsScaleFactor(double* aContentsScaleFactor)
+{
+    *aContentsScaleFactor = [mScreen scale];
+    return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(UIKitScreenManager, nsIScreenManager)
+
+UIKitScreenManager::UIKitScreenManager()
+: mScreen(new UIKitScreen([UIScreen mainScreen]))
+{
+}
+
+nsIntRect
+UIKitScreenManager::GetBounds()
+{
+    if (!gScreenBoundsSet) {
+        CGRect rect = [[UIScreen mainScreen] bounds];
+        CGFloat scale = [[UIScreen mainScreen] scale];
+        gScreenBounds.x = rect.origin.x * scale;
+        gScreenBounds.y = rect.origin.y * scale;
+        gScreenBounds.width = rect.size.width * scale;
+        gScreenBounds.height = rect.size.height * scale;
+        gScreenBoundsSet = true;
+    }
+    printf("UIKitScreenManager::GetBounds: %d %d %d %d\n",
+           gScreenBounds.x, gScreenBounds.y, gScreenBounds.width, gScreenBounds.height);
+    return gScreenBounds;
+}
+
+NS_IMETHODIMP
+UIKitScreenManager::GetPrimaryScreen(nsIScreen** outScreen)
+{
+  NS_IF_ADDREF(*outScreen = mScreen.get());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UIKitScreenManager::ScreenForRect(int32_t inLeft,
+                               int32_t inTop,
+                               int32_t inWidth,
+                               int32_t inHeight,
+                               nsIScreen** outScreen)
+{
+  return GetPrimaryScreen(outScreen);
+}
+
+NS_IMETHODIMP
+UIKitScreenManager::ScreenForId(uint32_t id,
+                                nsIScreen** outScreen)
+{
+    return GetPrimaryScreen(outScreen);
+}
+
+NS_IMETHODIMP
+UIKitScreenManager::ScreenForNativeWidget(void* aWidget, nsIScreen** outScreen)
+{
+  return GetPrimaryScreen(outScreen);
+}
+
+NS_IMETHODIMP
+UIKitScreenManager::GetNumberOfScreens(uint32_t* aNumberOfScreens)
+{
+  //TODO: support multiple screens
+  *aNumberOfScreens = 1;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UIKitScreenManager::GetSystemDefaultScale(float* aScale)
+{
+    *aScale = [UIScreen mainScreen].scale;
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsWidgetFactory.mm
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "nsIFactory.h"
+#include "nsISupports.h"
+#include "nsIComponentManager.h"
+#include "mozilla/ModuleUtils.h"
+
+#include "nsWidgetsCID.h"
+
+#include "nsAppShell.h"
+#include "nsAppShellSingleton.h"
+#include "nsLookAndFeel.h"
+#include "nsScreenManager.h"
+#include "nsWindow.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(UIKitScreenManager)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow)
+
+#include "GfxInfo.h"
+namespace mozilla {
+namespace widget {
+// This constructor should really be shared with all platforms.
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
+}
+}
+
+NS_DEFINE_NAMED_CID(NS_WINDOW_CID);
+NS_DEFINE_NAMED_CID(NS_CHILD_CID);
+NS_DEFINE_NAMED_CID(NS_APPSHELL_CID);
+NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID);
+NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
+
+static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
+  { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor },
+  { &kNS_CHILD_CID, false, nullptr, nsWindowConstructor },
+  { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor },
+  { &kNS_SCREENMANAGER_CID, false, nullptr, UIKitScreenManagerConstructor },
+  { &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor },
+  { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
+  { "@mozilla.org/widgets/window/uikit;1", &kNS_WINDOW_CID },
+  { "@mozilla.org/widgets/childwindow/uikit;1", &kNS_CHILD_CID },
+  { "@mozilla.org/widget/appshell/uikit;1", &kNS_APPSHELL_CID },
+  { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID },
+  { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
+  { nullptr }
+};
+
+static void
+nsWidgetUIKitModuleDtor()
+{
+  nsLookAndFeel::Shutdown();
+  nsAppShellShutdown();
+}
+
+static const mozilla::Module kWidgetModule = {
+  mozilla::Module::kVersion,
+  kWidgetCIDs,
+  kWidgetContracts,
+  nullptr,
+  nullptr,
+  nsAppShellInit,
+  nsWidgetUIKitModuleDtor
+};
+
+NSMODULE_DEFN(nsWidgetUIKitModule) = &kWidgetModule;
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsWindow.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef NSWINDOW_H_
+#define NSWINDOW_H_
+
+#include "nsBaseWidget.h"
+#include "gfxPoint.h"
+
+#include "nsTArray.h"
+
+@class UIWindow;
+@class UIView;
+@class ChildView;
+class gfxASurface;
+
+class nsWindow :
+    public nsBaseWidget
+{
+    typedef nsBaseWidget Inherited;
+
+public:
+    nsWindow();
+
+    NS_DECL_ISUPPORTS_INHERITED
+
+    //
+    // nsIWidget
+    //
+
+    NS_IMETHOD Create(nsIWidget *aParent,
+                      nsNativeWidget aNativeParent,
+                      const nsIntRect &aRect,
+                      nsWidgetInitData *aInitData = nullptr) override;
+    NS_IMETHOD Destroy() override;
+    NS_IMETHOD Show(bool aState) override;
+    NS_IMETHOD              Enable(bool aState) override {
+        return NS_OK;
+    }
+    virtual bool            IsEnabled() const override {
+        return true;
+    }
+    NS_IMETHOD              SetModal(bool aState) override;
+    virtual bool            IsVisible() const override {
+        return mVisible;
+    }
+    NS_IMETHOD              SetFocus(bool aState=false) override;
+    virtual mozilla::LayoutDeviceIntPoint WidgetToScreenOffset() override;
+
+    virtual void SetBackgroundColor(const nscolor &aColor) override;
+    virtual void* GetNativeData(uint32_t aDataType) override;
+
+    NS_IMETHOD              ConstrainPosition(bool aAllowSlop,
+                                              int32_t *aX, int32_t *aY) override;
+    NS_IMETHOD              Move(double aX, double aY) override;
+    NS_IMETHOD              PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
+                                        nsIWidget *aWidget, bool aActivate) override;
+    NS_IMETHOD              SetSizeMode(int32_t aMode) override;
+    void                    EnteredFullScreen(bool aFullScreen);
+    NS_IMETHOD              Resize(double aWidth, double aHeight, bool aRepaint) override;
+    NS_IMETHOD              Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override;
+    NS_IMETHOD              GetScreenBounds(nsIntRect &aRect) override;
+    void                    ReportMoveEvent();
+    void                    ReportSizeEvent();
+    void                    ReportSizeModeEvent(int32_t aMode);
+
+    CGFloat                 BackingScaleFactor();
+    void                    BackingScaleFactorChanged();
+    virtual float           GetDPI() override {
+        //XXX: terrible
+        return 326.0f;
+    }
+    virtual double          GetDefaultScaleInternal() override {
+        return BackingScaleFactor();
+    }
+    virtual int32_t         RoundsWidgetCoordinatesTo() override;
+
+    NS_IMETHOD              SetTitle(const nsAString& aTitle) override {
+        return NS_OK;
+    }
+
+    NS_IMETHOD Invalidate(const nsIntRect &aRect) override;
+    virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override;
+    NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
+                             nsEventStatus& aStatus) override;
+    NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener,
+                                   bool aDoCapture) override {
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
+
+    void WillPaintWindow();
+    bool PaintWindow(nsIntRegion aRegion);
+
+    bool HasModalDescendents() { return false; }
+
+    //NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) override;
+    NS_IMETHOD_(void) SetInputContext(
+                        const InputContext& aContext,
+                        const InputContextAction& aAction);
+    NS_IMETHOD_(InputContext) GetInputContext();
+    /*
+    NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
+                        NativeKeyBindingsType aType,
+                        const mozilla::WidgetKeyboardEvent& aEvent,
+                        DoCommandCallback aCallback,
+                        void* aCallbackData) override;
+    */
+
+    NS_IMETHOD         ReparentNativeWidget(nsIWidget* aNewParent) override;
+
+protected:
+    virtual ~nsWindow();
+    void BringToFront();
+    nsWindow *FindTopLevel();
+    bool IsTopLevel();
+    nsresult GetCurrentOffset(uint32_t &aOffset, uint32_t &aLength);
+    nsresult DeleteRange(int aOffset, int aLen);
+
+    void TearDownView();
+
+    ChildView*   mNativeView;
+    bool mVisible;
+    nsTArray<nsWindow*> mChildren;
+    nsWindow* mParent;
+    InputContext         mInputContext;
+
+    void OnSizeChanged(const gfxIntSize& aSize);
+
+    static void DumpWindows();
+    static void DumpWindows(const nsTArray<nsWindow*>& wins, int indent = 0);
+    static void LogWindow(nsWindow *win, int index, int indent);
+};
+
+#endif /* NSWINDOW_H_ */
new file mode 100644
--- /dev/null
+++ b/widget/uikit/nsWindow.mm
@@ -0,0 +1,901 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/UIEvent.h>
+#import <UIKit/UIGraphics.h>
+#import <UIKit/UIInterface.h>
+#import <UIKit/UIScreen.h>
+#import <UIKit/UITapGestureRecognizer.h>
+#import <UIKit/UITouch.h>
+#import <UIKit/UIView.h>
+#import <UIKit/UIViewController.h>
+#import <UIKit/UIWindow.h>
+#import <QuartzCore/QuartzCore.h>
+
+#include <algorithm>
+
+#include "nsWindow.h"
+#include "nsScreenManager.h"
+#include "nsAppShell.h"
+
+#include "nsWidgetsCID.h"
+#include "nsGfxCIID.h"
+
+#include "gfxQuartzSurface.h"
+#include "gfxUtils.h"
+#include "gfxImageSurface.h"
+#include "gfxContext.h"
+#include "nsRegion.h"
+#include "Layers.h"
+#include "nsTArray.h"
+
+#include "mozilla/BasicEvents.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/unused.h"
+
+#include "GeckoProfiler.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::layers;
+
+#define ALOG(args...) fprintf(stderr, args); fprintf(stderr, "\n")
+
+static LayoutDeviceIntPoint
+UIKitPointsToDevPixels(CGPoint aPoint, CGFloat aBackingScale)
+{
+    return LayoutDeviceIntPoint(NSToIntRound(aPoint.x * aBackingScale),
+                                NSToIntRound(aPoint.y * aBackingScale));
+}
+
+static CGRect
+DevPixelsToUIKitPoints(const nsIntRect& aRect, CGFloat aBackingScale)
+{
+    return CGRectMake((CGFloat)aRect.x / aBackingScale,
+                      (CGFloat)aRect.y / aBackingScale,
+                      (CGFloat)aRect.width / aBackingScale,
+                      (CGFloat)aRect.height / aBackingScale);
+}
+
+// Used to retain a Cocoa object for the remainder of a method's execution.
+class nsAutoRetainUIKitObject {
+public:
+nsAutoRetainUIKitObject(id anObject)
+{
+  mObject = [anObject retain];
+}
+~nsAutoRetainUIKitObject()
+{
+  [mObject release];
+}
+private:
+  id mObject;  // [STRONG]
+};
+
+@interface ChildView : UIView
+{
+@public
+    nsWindow* mGeckoChild; // weak ref
+    BOOL mWaitingForPaint;
+    CFMutableDictionaryRef mTouches;
+    int mNextTouchID;
+}
+// sets up our view, attaching it to its owning gecko view
+- (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild;
+// Our Gecko child was Destroy()ed
+- (void)widgetDestroyed;
+// Tear down this ChildView
+- (void)delayedTearDown;
+- (void)sendMouseEvent:(int) aType point:(LayoutDeviceIntPoint)aPoint widget:(nsWindow*)aWindow;
+- (void)handleTap:(UITapGestureRecognizer *)sender;
+- (BOOL)isUsingMainThreadOpenGL;
+- (void)drawUsingOpenGL;
+- (void)drawUsingOpenGLCallback;
+- (void)sendTouchEvent:(int) aType touches:(NSSet*)aTouches widget:(nsWindow*)aWindow;
+// Event handling (UIResponder)
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
+@end
+
+@implementation ChildView
++ (Class)layerClass {
+    return [CAEAGLLayer class];
+}
+
+- (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild
+{
+    self.multipleTouchEnabled = YES;
+    if ((self = [super initWithFrame:inFrame])) {
+      mGeckoChild = inChild;
+    }
+    ALOG("[ChildView[%p] initWithFrame:] (mGeckoChild = %p)", (void*)self, (void*)mGeckoChild);
+    self.opaque = YES;
+    self.alpha = 1.0;
+
+    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
+          initWithTarget:self action:@selector(handleTap:)];
+    tapRecognizer.numberOfTapsRequired = 1;
+    [self addGestureRecognizer:tapRecognizer];
+
+    mTouches = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+    mNextTouchID = 0;
+    return self;
+}
+
+- (void)widgetDestroyed
+{
+  mGeckoChild = nullptr;
+  CFRelease(mTouches);
+}
+
+- (void)delayedTearDown
+{
+  [self removeFromSuperview];
+  [self release];
+}
+
+- (void)sendMouseEvent:(int) aType point:(LayoutDeviceIntPoint)aPoint widget:(nsWindow*)aWindow
+{
+    WidgetMouseEvent event(true, aType, aWindow,
+                           WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
+
+    event.refPoint = aPoint;
+    event.clickCount = 1;
+    event.button = WidgetMouseEvent::eLeftButton;
+    event.time = PR_IntervalNow();
+    event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
+
+    nsEventStatus status;
+    aWindow->DispatchEvent(&event, status);
+}
+
+- (void)handleTap:(UITapGestureRecognizer *)sender
+{
+    if (sender.state == UIGestureRecognizerStateEnded) {
+        ALOG("[ChildView[%p] handleTap]", self);
+        LayoutDeviceIntPoint lp = UIKitPointsToDevPixels([sender locationInView:self], [self contentScaleFactor]);
+        [self sendMouseEvent:NS_MOUSE_MOVE point:lp widget:mGeckoChild];
+        [self sendMouseEvent:NS_MOUSE_BUTTON_DOWN point:lp widget:mGeckoChild];
+        [self sendMouseEvent:NS_MOUSE_BUTTON_UP point:lp widget:mGeckoChild];
+    }
+}
+
+- (void)sendTouchEvent:(int) aType touches:(NSSet*)aTouches widget:(nsWindow*)aWindow
+{
+    WidgetTouchEvent event(true, aType, aWindow);
+    //XXX: I think nativeEvent.timestamp * 1000 is probably usable here but
+    // I don't care that much right now.
+    event.time = PR_IntervalNow();
+    event.touches.SetCapacity(aTouches.count);
+    for (UITouch* touch in aTouches) {
+        LayoutDeviceIntPoint loc = UIKitPointsToDevPixels([touch locationInView:self], [self contentScaleFactor]);
+        void* value;
+        if (!CFDictionaryGetValueIfPresent(mTouches, touch, (const void**)&value)) {
+            // This shouldn't happen.
+            NS_ASSERTION(false, "Got a touch that we didn't know about");
+            continue;
+        }
+        int id = reinterpret_cast<int>(value);
+        nsRefPtr<Touch> t = new Touch(id,
+                                      loc,
+                                      nsIntPoint([touch majorRadius], [touch majorRadius]),
+                                      0.0f,
+                                      1.0f);
+        event.refPoint = loc;
+        event.touches.AppendElement(t);
+    }
+    aWindow->DispatchAPZAwareEvent(&event);
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+    ALOG("[ChildView[%p] touchesBegan", self);
+    if (!mGeckoChild)
+        return;
+
+    for (UITouch* touch : touches) {
+        CFDictionaryAddValue(mTouches, touch, (void*)mNextTouchID);
+        mNextTouchID++;
+    }
+    [self sendTouchEvent:NS_TOUCH_START touches:[event allTouches] widget:mGeckoChild];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+    ALOG("[ChildView[%p] touchesCancelled", self);
+    [self sendTouchEvent:NS_TOUCH_CANCEL touches:touches widget:mGeckoChild];
+    for (UITouch* touch : touches) {
+        CFDictionaryRemoveValue(mTouches, touch);
+    }
+    if (CFDictionaryGetCount(mTouches) == 0) {
+        mNextTouchID = 0;
+    }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+    ALOG("[ChildView[%p] touchesEnded", self);
+    if (!mGeckoChild)
+        return;
+
+    [self sendTouchEvent:NS_TOUCH_END touches:touches widget:mGeckoChild];
+    for (UITouch* touch : touches) {
+        CFDictionaryRemoveValue(mTouches, touch);
+    }
+    if (CFDictionaryGetCount(mTouches) == 0) {
+        mNextTouchID = 0;
+    }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+    ALOG("[ChildView[%p] touchesMoved", self);
+    if (!mGeckoChild)
+        return;
+
+    [self sendTouchEvent:NS_TOUCH_MOVE touches:[event allTouches] widget:mGeckoChild];
+}
+
+- (void)setNeedsDisplayInRect:(CGRect)aRect
+{
+    if ([self isUsingMainThreadOpenGL]) {
+    // Draw without calling drawRect. This prevent us from
+    // needing to access the normal window buffer surface unnecessarily, so we
+    // waste less time synchronizing the two surfaces.
+    if (!mWaitingForPaint) {
+      mWaitingForPaint = YES;
+      // Use NSRunLoopCommonModes instead of the default NSDefaultRunLoopMode
+      // so that the timer also fires while a native menu is open.
+      [self performSelector:@selector(drawUsingOpenGLCallback)
+                 withObject:nil
+                 afterDelay:0
+                    inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
+    }
+  }
+}
+
+- (BOOL)isUsingMainThreadOpenGL
+{
+  if (!mGeckoChild || ![self window])
+    return NO;
+
+  return mGeckoChild->GetLayerManager(nullptr)->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL;
+}
+
+- (void)drawUsingOpenGL
+{
+    ALOG("drawUsingOpenGL");
+  PROFILER_LABEL("ChildView", "drawUsingOpenGL",
+    js::ProfileEntry::Category::GRAPHICS);
+
+  if (!mGeckoChild->IsVisible())
+    return;
+
+  mWaitingForPaint = NO;
+
+  nsIntRect geckoBounds;
+  mGeckoChild->GetBounds(geckoBounds);
+  nsIntRegion region(geckoBounds);
+
+  mGeckoChild->PaintWindow(region);
+}
+
+// Called asynchronously after setNeedsDisplay in order to avoid entering the
+// normal drawing machinery.
+- (void)drawUsingOpenGLCallback
+{
+  if (mWaitingForPaint) {
+    [self drawUsingOpenGL];
+  }
+}
+
+// The display system has told us that a portion of our view is dirty. Tell
+// gecko to paint it
+- (void)drawRect:(CGRect)aRect
+{
+    CGContextRef cgContext = UIGraphicsGetCurrentContext();
+    [self drawRect:aRect inContext:cgContext];
+}
+
+- (void)drawRect:(CGRect)aRect inContext:(CGContextRef)aContext
+{
+#ifdef DEBUG_UPDATE
+  nsIntRect geckoBounds;
+  mGeckoChild->GetBounds(geckoBounds);
+
+  fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n  gecko bounds: [%d %d %d %d]\n",
+           self, mGeckoChild,
+           aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, aContext,
+           geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
+
+  CGAffineTransform xform = CGContextGetCTM(aContext);
+  fprintf (stderr, "  xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
+#endif
+
+  if (true) {
+    // For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is
+    // directly called from a delayed perform callback - without going through
+    // drawRect.
+    // Paints that come through here are triggered by something that Cocoa
+    // controls, for example by window resizing or window focus changes.
+
+    // Do GL composition and return.
+    [self drawUsingOpenGL];
+    return;
+  }
+  PROFILER_LABEL("ChildView", "drawRect",
+    js::ProfileEntry::Category::GRAPHICS);
+
+  // The CGContext that drawRect supplies us with comes with a transform that
+  // scales one user space unit to one Cocoa point, which can consist of
+  // multiple dev pixels. But Gecko expects its supplied context to be scaled
+  // to device pixels, so we need to reverse the scaling.
+  double scale = mGeckoChild->BackingScaleFactor();
+  CGContextSaveGState(aContext);
+  CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale);
+
+  CGSize viewSize = [self bounds].size;
+  nsIntSize backingSize(viewSize.width * scale, viewSize.height * scale);
+
+  CGContextSaveGState(aContext);
+
+  nsIntRegion region = nsIntRect(NSToIntRound(aRect.origin.x * scale),
+                                 NSToIntRound(aRect.origin.y * scale),
+                                 NSToIntRound(aRect.size.width * scale),
+                                 NSToIntRound(aRect.size.height * scale));
+
+  // Create Cairo objects.
+  nsRefPtr<gfxQuartzSurface> targetSurface;
+
+  nsRefPtr<gfxContext> targetContext;
+  if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(gfx::BackendType::COREGRAPHICS)) {
+    RefPtr<gfx::DrawTarget> dt =
+      gfx::Factory::CreateDrawTargetForCairoCGContext(aContext,
+                                                      gfx::IntSize(backingSize.width,
+                                                                   backingSize.height));
+    dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
+    targetContext = new gfxContext(dt);
+  } else if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(gfx::BackendType::CAIRO)) {
+    // This is dead code unless you mess with prefs, but keep it around for
+    // debugging.
+    targetSurface = new gfxQuartzSurface(aContext, backingSize);
+    targetSurface->SetAllowUseAsSource(false);
+    RefPtr<gfx::DrawTarget> dt =
+      gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface,
+                                                             gfx::IntSize(backingSize.width,
+                                                                          backingSize.height));
+    dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
+    targetContext = new gfxContext(dt);
+  } else {
+    MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backed");
+  }
+
+  // Set up the clip region.
+  nsIntRegionRectIterator iter(region);
+  targetContext->NewPath();
+  for (;;) {
+    const nsIntRect* r = iter.Next();
+    if (!r)
+      break;
+    targetContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
+  }
+  targetContext->Clip();
+
+  //nsAutoRetainCocoaObject kungFuDeathGrip(self);
+  bool painted = false;
+  if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
+    nsBaseWidget::AutoLayerManagerSetup
+      setupLayerManager(mGeckoChild, targetContext, BufferMode::BUFFER_NONE);
+    painted = mGeckoChild->PaintWindow(region);
+  } else if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
+    // We only need this so that we actually get DidPaintWindow fired
+    painted = mGeckoChild->PaintWindow(region);
+  }
+
+  targetContext = nullptr;
+  targetSurface = nullptr;
+
+  CGContextRestoreGState(aContext);
+
+  // Undo the scale transform so that from now on the context is in
+  // CocoaPoints again.
+  CGContextRestoreGState(aContext);
+  if (!painted && [self isOpaque]) {
+    // Gecko refused to draw, but we've claimed to be opaque, so we have to
+    // draw something--fill with white.
+    CGContextSetRGBFillColor(aContext, 1, 1, 1, 1);
+    CGContextFillRect(aContext, aRect);
+  }
+
+#ifdef DEBUG_UPDATE
+  fprintf (stderr, "---- update done ----\n");
+
+#if 0
+  CGContextSetRGBStrokeColor (aContext,
+                            ((((unsigned long)self) & 0xff)) / 255.0,
+                            ((((unsigned long)self) & 0xff00) >> 8) / 255.0,
+                            ((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
+                            0.5);
+#endif
+  CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8);
+  CGContextSetLineWidth(aContext, 4.0);
+  CGContextStrokeRect(aContext, aRect);
+#endif
+}
+@end
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, Inherited)
+
+nsWindow::nsWindow()
+: mNativeView(nullptr),
+  mVisible(false),
+  mParent(nullptr)
+{
+}
+
+nsWindow::~nsWindow()
+{
+    [mNativeView widgetDestroyed]; // Safe if mNativeView is nil.
+    TearDownView(); // Safe if called twice.
+}
+
+void nsWindow::TearDownView()
+{
+  if (!mNativeView)
+    return;
+
+  [mNativeView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
+  mNativeView = nil;
+}
+
+bool
+nsWindow::IsTopLevel()
+{
+    return mWindowType == eWindowType_toplevel ||
+        mWindowType == eWindowType_dialog ||
+        mWindowType == eWindowType_invisible;
+}
+
+//
+// nsIWidget
+//
+
+NS_IMETHODIMP
+nsWindow::Create(nsIWidget *aParent,
+                 nsNativeWidget aNativeParent,
+                 const nsIntRect &aRect,
+                 nsWidgetInitData *aInitData)
+{
+    ALOG("nsWindow[%p]::Create %p/%p [%d %d %d %d]", (void*)this, (void*)aParent, (void*)aNativeParent, aRect.x, aRect.y, aRect.width, aRect.height);
+    nsWindow* parent = (nsWindow*) aParent;
+    ChildView* nativeParent = (ChildView*)aNativeParent;
+
+    if (parent == nullptr && nativeParent)
+        parent = nativeParent->mGeckoChild;
+    if (parent && nativeParent == nullptr)
+        nativeParent = parent->mNativeView;
+
+    // for toplevel windows, bounds are fixed to full screen size
+    if (parent == nullptr) {
+        if (nsAppShell::gWindow == nil) {
+            mBounds = UIKitScreenManager::GetBounds();
+        }
+        else {
+            CGRect cgRect = [nsAppShell::gWindow bounds];
+            mBounds.x = cgRect.origin.x;
+            mBounds.y = cgRect.origin.y;
+            mBounds.width = cgRect.size.width;
+            mBounds.height = cgRect.size.height;
+        }
+    }
+    else {
+        mBounds = aRect;
+    }
+
+    ALOG("nsWindow[%p]::Create bounds: %d %d %d %d", (void*)this,
+         mBounds.x, mBounds.y, mBounds.width, mBounds.height);
+
+    // Set defaults which can be overriden from aInitData in BaseCreate
+    mWindowType = eWindowType_toplevel;
+    mBorderStyle = eBorderStyle_default;
+
+    Inherited::BaseCreate(aParent, mBounds, aInitData);
+
+    NS_ASSERTION(IsTopLevel() || parent, "non top level window doesn't have a parent!");
+
+    mNativeView = [[ChildView alloc] initWithFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor()) geckoChild:this];
+    mNativeView.hidden = YES;
+
+    if (parent) {
+        parent->mChildren.AppendElement(this);
+        mParent = parent;
+    }
+
+    if (nativeParent) {
+        [nativeParent addSubview:mNativeView];
+    } else if (nsAppShell::gWindow) {
+        [nsAppShell::gWindow.rootViewController.view addSubview:mNativeView];
+    }
+    else {
+        [nsAppShell::gTopLevelViews addObject:mNativeView];
+    }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Destroy(void)
+{
+    for (uint32_t i = 0; i < mChildren.Length(); ++i) {
+        // why do we still have children?
+        mChildren[i]->SetParent(nullptr);
+    }
+
+    if (mParent)
+        mParent->mChildren.RemoveElement(this);
+
+    [mNativeView widgetDestroyed];
+
+    nsBaseWidget::Destroy();
+
+    //ReportDestroyEvent();
+
+    TearDownView();
+
+    nsBaseWidget::OnDestroy();
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config)
+{
+    for (uint32_t i = 0; i < config.Length(); ++i) {
+        nsWindow *childWin = (nsWindow*) config[i].mChild.get();
+        childWin->Resize(config[i].mBounds.x,
+                         config[i].mBounds.y,
+                         config[i].mBounds.width,
+                         config[i].mBounds.height,
+                         false);
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsWindow::Show(bool aState)
+{
+  if (aState != mVisible) {
+      mNativeView.hidden = aState ? NO : YES;
+      if (aState) {
+          UIView* parentView = mParent ? mParent->mNativeView : nsAppShell::gWindow.rootViewController.view;
+          [parentView bringSubviewToFront:mNativeView];
+          [mNativeView setNeedsDisplay];
+      }
+      mVisible = aState;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetModal(bool aModal)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::ConstrainPosition(bool aAllowSlop,
+                            int32_t *aX,
+                            int32_t *aY)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Move(double aX, double aY)
+{
+  if (!mNativeView || (mBounds.x == aX && mBounds.y == aY))
+    return NS_OK;
+
+  //XXX: handle this
+  // The point we have is in Gecko coordinates (origin top-left). Convert
+  // it to Cocoa ones (origin bottom-left).
+  mBounds.x = aX;
+  mBounds.y = aY;
+
+  mNativeView.frame = DevPixelsToUIKitPoints(mBounds, BackingScaleFactor());
+
+  if (mVisible)
+    [mNativeView setNeedsDisplay];
+
+  ReportMoveEvent();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Resize(double aX, double aY,
+                 double aWidth, double aHeight,
+                 bool aRepaint)
+{
+    BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
+    BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
+    if (!mNativeView || (!isMoving && !isResizing))
+        return NS_OK;
+
+    if (isMoving) {
+        mBounds.x = aX;
+        mBounds.y = aY;
+    }
+    if (isResizing) {
+        mBounds.width  = aWidth;
+        mBounds.height  = aHeight;
+    }
+
+    [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())];
+
+    if (mVisible && aRepaint)
+        [mNativeView setNeedsDisplay];
+
+    if (isMoving)
+        ReportMoveEvent();
+
+    if (isResizing)
+        ReportSizeEvent();
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
+{
+    if (!mNativeView || (mBounds.width == aWidth && mBounds.height == aHeight))
+        return NS_OK;
+
+    mBounds.width  = aWidth;
+    mBounds.height = aHeight;
+
+    [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())];
+
+    if (mVisible && aRepaint)
+        [mNativeView setNeedsDisplay];
+
+    ReportSizeEvent();
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
+                      nsIWidget *aWidget,
+                      bool aActivate)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetSizeMode(int32_t aMode)
+{
+    if (aMode == static_cast<int32_t>(mSizeMode)) {
+        return NS_OK;
+    }
+
+    nsresult rv = NS_OK;
+    mSizeMode = static_cast<nsSizeMode>(aMode);
+    if (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen) {
+        // Resize to fill screen
+        rv = nsBaseWidget::MakeFullScreen(true);
+    }
+    ReportSizeModeEvent(aMode);
+    return rv;
+}
+
+NS_IMETHODIMP
+nsWindow::Invalidate(const nsIntRect &aRect)
+{
+  if (!mNativeView || !mVisible)
+    return NS_OK;
+
+  MOZ_RELEASE_ASSERT(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_CLIENT,
+                     "Shouldn't need to invalidate with accelerated OMTC layers!");
+
+
+  [mNativeView setNeedsLayout];
+  [mNativeView setNeedsDisplayInRect:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())];
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetFocus(bool aRaise)
+{
+  [[mNativeView window] makeKeyWindow];
+  [mNativeView becomeFirstResponder];
+  return NS_OK;
+}
+
+void nsWindow::WillPaintWindow()
+{
+  if (mWidgetListener) {
+    mWidgetListener->WillPaintWindow(this);
+  }
+}
+
+bool nsWindow::PaintWindow(nsIntRegion aRegion)
+{
+  if (!mWidgetListener)
+    return false;
+
+  bool returnValue = false;
+  returnValue = mWidgetListener->PaintWindow(this, aRegion);
+
+  if (mWidgetListener) {
+    mWidgetListener->DidPaintWindow();
+  }
+
+  return returnValue;
+}
+
+void nsWindow::ReportMoveEvent()
+{
+    NotifyWindowMoved(mBounds.x, mBounds.y);
+}
+
+void nsWindow::ReportSizeModeEvent(int32_t aMode)
+{
+    if (mWidgetListener) {
+        // This is terrible.
+        nsSizeMode theMode;
+        switch (aMode) {
+        case nsSizeMode_Maximized:
+            theMode = nsSizeMode_Maximized;
+            break;
+        case nsSizeMode_Fullscreen:
+            theMode = nsSizeMode_Fullscreen;
+            break;
+        default:
+            return;
+        }
+        mWidgetListener->SizeModeChanged(theMode);
+    }
+}
+
+void nsWindow::ReportSizeEvent()
+{
+    if (mWidgetListener) {
+        nsIntRect innerBounds;
+        GetClientBounds(innerBounds);
+        mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height);
+    }
+}
+
+NS_IMETHODIMP
+nsWindow::GetScreenBounds(nsIntRect &aRect)
+{
+    LayoutDeviceIntPoint p = WidgetToScreenOffset();
+
+    aRect.x = p.x;
+    aRect.y = p.y;
+    aRect.width = mBounds.width;
+    aRect.height = mBounds.height;
+
+    return NS_OK;
+}
+
+LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset()
+{
+    LayoutDeviceIntPoint offset(0, 0);
+    if (mParent) {
+        offset = mParent->WidgetToScreenOffset();
+    }
+
+    CGPoint temp = [mNativeView convertPoint:temp toView:nil];
+
+    if (!mParent && nsAppShell::gWindow) {
+        // convert to screen coords
+        temp = [nsAppShell::gWindow convertPoint:temp toWindow:nil];
+    }
+
+    offset.x += temp.x;
+    offset.y += temp.y;
+
+    return offset;
+}
+
+NS_IMETHODIMP
+nsWindow::DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
+                        nsEventStatus& aStatus)
+{
+  aStatus = nsEventStatus_eIgnore;
+  nsCOMPtr<nsIWidget> kungFuDeathGrip = do_QueryInterface(aEvent->widget);
+
+  if (mWidgetListener)
+    aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+nsWindow::SetInputContext(const InputContext& aContext,
+                          const InputContextAction& aAction)
+{
+    //TODO: actually show VKB
+    mInputContext = aContext;
+}
+
+NS_IMETHODIMP_(mozilla::widget::InputContext)
+nsWindow::GetInputContext()
+{
+    mInputContext.mNativeIMEContext = nullptr;
+    return mInputContext;
+}
+
+void
+nsWindow::SetBackgroundColor(const nscolor &aColor)
+{
+    mNativeView.backgroundColor = [UIColor colorWithRed:NS_GET_R(aColor)
+                                   green:NS_GET_G(aColor)
+                                   blue:NS_GET_B(aColor)
+                                   alpha:NS_GET_A(aColor)];
+}
+
+void* nsWindow::GetNativeData(uint32_t aDataType)
+{
+  void* retVal = nullptr;
+
+  switch (aDataType)
+  {
+    case NS_NATIVE_WIDGET:
+    case NS_NATIVE_DISPLAY:
+      retVal = (void*)mNativeView;
+      break;
+
+    case NS_NATIVE_WINDOW:
+      retVal = [mNativeView window];
+      break;
+
+    case NS_NATIVE_GRAPHIC:
+      NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a UIKit child view!");
+      break;
+
+    case NS_NATIVE_OFFSETX:
+      retVal = 0;
+      break;
+
+    case NS_NATIVE_OFFSETY:
+      retVal = 0;
+      break;
+
+    case NS_NATIVE_PLUGIN_PORT:
+        // not implemented
+        break;
+  }
+
+  return retVal;
+}
+
+CGFloat
+nsWindow::BackingScaleFactor()
+{
+    if (mNativeView) {
+        return [mNativeView contentScaleFactor];
+    }
+    return [UIScreen mainScreen].scale;
+}
+
+int32_t
+nsWindow::RoundsWidgetCoordinatesTo()
+{
+  if (BackingScaleFactor() == 2.0) {
+    return 2;
+  }
+  return 1;
+}