diff options
Diffstat (limited to '')
-rw-r--r-- | widget/uikit/GfxInfo.cpp | 174 | ||||
-rw-r--r-- | widget/uikit/GfxInfo.h | 75 | ||||
-rw-r--r-- | widget/uikit/moz.build | 24 | ||||
-rw-r--r-- | widget/uikit/nsAppShell.h | 56 | ||||
-rw-r--r-- | widget/uikit/nsAppShell.mm | 241 | ||||
-rw-r--r-- | widget/uikit/nsLookAndFeel.h | 39 | ||||
-rw-r--r-- | widget/uikit/nsLookAndFeel.mm | 383 | ||||
-rw-r--r-- | widget/uikit/nsScreenManager.h | 60 | ||||
-rw-r--r-- | widget/uikit/nsScreenManager.mm | 98 | ||||
-rw-r--r-- | widget/uikit/nsWidgetFactory.mm | 49 | ||||
-rw-r--r-- | widget/uikit/nsWindow.h | 110 | ||||
-rw-r--r-- | widget/uikit/nsWindow.mm | 728 |
12 files changed, 2037 insertions, 0 deletions
diff --git a/widget/uikit/GfxInfo.cpp b/widget/uikit/GfxInfo.cpp new file mode 100644 index 0000000000..d797e4f4a0 --- /dev/null +++ b/widget/uikit/GfxInfo.cpp @@ -0,0 +1,174 @@ +/* -*- 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; } + +NS_IMETHODIMP +GfxInfo::GetDWriteVersion(nsAString& aDwriteVersion) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDescription2(nsAString& aAdapterDescription) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterRAM(uint32_t* aAdapterRAM) { return NS_ERROR_FAILURE; } + +NS_IMETHODIMP +GfxInfo::GetAdapterRAM2(uint32_t* aAdapterRAM) { return NS_ERROR_FAILURE; } + +NS_IMETHODIMP +GfxInfo::GetAdapterDriver(nsAString& aAdapterDriver) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriver2(nsAString& aAdapterDriver) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion(nsAString& aAdapterDriverVersion) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion2(nsAString& aAdapterDriverVersion) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate(nsAString& aAdapterDriverDate) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate2(nsAString& aAdapterDriverDate) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID(nsAString& aAdapterVendorID) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID2(nsAString& aAdapterVendorID) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID(nsAString& aAdapterDeviceID) { + return NS_ERROR_FAILURE; + return NS_OK; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID2(nsAString& aAdapterDeviceID) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterSubsysID(nsAString& aAdapterSubsysID) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterSubsysID2(nsAString& aAdapterSubsysID) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) { return NS_ERROR_FAILURE; } + +const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { + if (sDriverInfo->IsEmpty()) { + APPEND_TO_DRIVER_BLOCKLIST2( + OperatingSystem::Ios, DeviceFamily::All, + nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_STATUS_OK, + DRIVER_COMPARISON_IGNORED, GfxDriverInfo::allDriverVersions); + } + + return *sDriverInfo; +} + +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 = OperatingSystem::Ios; + + if (sShutdownOccurred) { + return NS_OK; + } + + // OpenGL layers are never blocklisted 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) { + *aStatus = nsIGfxInfo::FEATURE_STATUS_OK; + return NS_OK; + } + + return GfxInfoBase::GetFeatureStatusImpl( + aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aOS); +} + +#ifdef DEBUG + +// Implement nsIGfxInfoDebug + +NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString& aVendorID) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString& aDeviceID) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString& aDriverVersion) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) { + return NS_ERROR_FAILURE; +} + +#endif + +} // namespace widget +} // namespace mozilla diff --git a/widget/uikit/GfxInfo.h b/widget/uikit/GfxInfo.h new file mode 100644 index 0000000000..fe75e28d23 --- /dev/null +++ b/widget/uikit/GfxInfo.h @@ -0,0 +1,75 @@ +/* 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) override; + NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override; + NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override; + NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override; + NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override; + NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override; + NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override; + NS_IMETHOD GetAdapterDeviceID(nsAString& aAdapterDeviceID) override; + NS_IMETHOD GetAdapterSubsysID(nsAString& aAdapterSubsysID) override; + NS_IMETHOD GetAdapterRAM(uint32_t* aAdapterRAM) override; + NS_IMETHOD GetAdapterDriverVersion(nsAString& aAdapterDriverVersion) override; + NS_IMETHOD GetAdapterDriverDate(nsAString& aAdapterDriverDate) override; + NS_IMETHOD GetAdapterDescription2(nsAString& aAdapterDescription) override; + NS_IMETHOD GetAdapterDriver2(nsAString& aAdapterDriver) override; + NS_IMETHOD GetAdapterVendorID2(nsAString& aAdapterVendorID) override; + NS_IMETHOD GetAdapterDeviceID2(nsAString& aAdapterDeviceID) override; + NS_IMETHOD GetAdapterSubsysID2(nsAString& aAdapterSubsysID) override; + NS_IMETHOD GetAdapterRAM2(uint32_t* aAdapterRAM) override; + NS_IMETHOD GetAdapterDriverVersion2( + nsAString& aAdapterDriverVersion) override; + NS_IMETHOD GetAdapterDriverDate2(nsAString& aAdapterDriverDate) override; + NS_IMETHOD GetIsGPU2Active(bool* aIsGPU2Active) override; + using GfxInfoBase::GetFeatureStatus; + using GfxInfoBase::GetFeatureSuggestedDriverVersion; + +#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__ */ diff --git a/widget/uikit/moz.build b/widget/uikit/moz.build new file mode 100644 index 0000000000..6a3c2db994 --- /dev/null +++ b/widget/uikit/moz.build @@ -0,0 +1,24 @@ +# -*- Mode: python; 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Widget") + +SOURCES += [ + "GfxInfo.cpp", + "nsAppShell.mm", + "nsLookAndFeel.mm", + "nsScreenManager.mm", + "nsWidgetFactory.mm", + "nsWindow.mm", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" +LOCAL_INCLUDES += [ + "/widget", +] diff --git a/widget/uikit/nsAppShell.h b/widget/uikit/nsAppShell.h new file mode 100644 index 0000000000..fd46a51025 --- /dev/null +++ b/widget/uikit/nsAppShell.h @@ -0,0 +1,56 @@ +/* -*- 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/. */ + +/* + * 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) override; + + nsAppShell(); + + nsresult Init(); + + NS_IMETHOD Run(void) override; + NS_IMETHOD Exit(void) override; + // 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_ diff --git a/widget/uikit/nsAppShell.mm b/widget/uikit/nsAppShell.mm new file mode 100644 index 0000000000..1fd995f3e8 --- /dev/null +++ b/widget/uikit/nsAppShell.mm @@ -0,0 +1,241 @@ +/* -*- 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 <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:]"); + 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), + 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(); +} diff --git a/widget/uikit/nsLookAndFeel.h b/widget/uikit/nsLookAndFeel.h new file mode 100644 index 0000000000..7de7e0712b --- /dev/null +++ b/widget/uikit/nsLookAndFeel.h @@ -0,0 +1,39 @@ +/* -*- 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/. */ + +#ifndef __nsLookAndFeel +#define __nsLookAndFeel + +#include "nsXPLookAndFeel.h" + +class nsLookAndFeel final : public nsXPLookAndFeel { + public: + nsLookAndFeel(); + virtual ~nsLookAndFeel(); + + void NativeInit() final; + virtual void RefreshImpl(); + nsresult NativeGetImpl(IntID aID, int32_t& aResult) override; + nsresult NativeGetFloat(FloatID aID, float& aResult) override; + nsresult NativeGetColor(ColorID, ColorScheme, nscolor& aResult) override; + bool NativeGetFont(FontID aID, nsString& aFontName, + gfxFontStyle& aFontStyle) override; + virtual char16_t GetPasswordCharacterImpl() { + // unicode value for the bullet character, used for password textfields. + return 0x2022; + } + + static bool UseOverlayScrollbars() { return true; } + + private: + nscolor mColorTextSelectForeground; + nscolor mColorDarkText; + + bool mInitialized; + + void EnsureInit(); +}; + +#endif diff --git a/widget/uikit/nsLookAndFeel.mm b/widget/uikit/nsLookAndFeel.mm new file mode 100644 index 0000000000..5758f93142 --- /dev/null +++ b/widget/uikit/nsLookAndFeel.mm @@ -0,0 +1,383 @@ +/* -*- 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 <UIKit/UIColor.h> +#import <UIKit/UIInterface.h> + +#include "nsLookAndFeel.h" + +#include "mozilla/FontPropertyTypes.h" +#include "nsStyleConsts.h" +#include "gfxFont.h" +#include "gfxFontConstants.h" + +nsLookAndFeel::nsLookAndFeel() : mInitialized(false) {} + +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)); + } + MOZ_ASSERT_UNREACHABLE("Unhandled color space!"); + return 0; +} + +void nsLookAndFeel::NativeInit() { EnsureInit(); } + +void nsLookAndFeel::RefreshImpl() { + nsXPLookAndFeel::RefreshImpl(); + + mInitialized = false; +} + +nsresult nsLookAndFeel::NativeGetColor(ColorID, ColorScheme, nscolor& aResult) { + EnsureInit(); + + nsresult res = NS_OK; + + switch (aID) { + case ColorID::Highlight: + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::MozMenuhover: + aResult = NS_RGB(0xee, 0xee, 0xee); + break; + case ColorID::Highlighttext: + case ColorID::MozMenuhovertext: + aResult = mColorTextSelectForeground; + break; + case ColorID::IMESelectedRawTextBackground: + case ColorID::IMESelectedConvertedTextBackground: + case ColorID::IMERawInputBackground: + case ColorID::IMEConvertedTextBackground: + aResult = NS_TRANSPARENT; + break; + case ColorID::IMESelectedRawTextForeground: + case ColorID::IMESelectedConvertedTextForeground: + case ColorID::IMERawInputForeground: + case ColorID::IMEConvertedTextForeground: + aResult = NS_SAME_AS_FOREGROUND_COLOR; + break; + case ColorID::IMERawInputUnderline: + case ColorID::IMEConvertedTextUnderline: + aResult = NS_40PERCENT_FOREGROUND_COLOR; + break; + case ColorID::IMESelectedRawTextUnderline: + case ColorID::IMESelectedConvertedTextUnderline: + aResult = NS_SAME_AS_FOREGROUND_COLOR; + break; + case ColorID::SpellCheckerUnderline: + aResult = NS_RGB(0xff, 0, 0); + break; + + // + // css2 system colors http://www.w3.org/TR/REC-CSS2/ui.html#system-colors + // + case ColorID::Buttontext: + case ColorID::MozButtonhovertext: + case ColorID::Captiontext: + case ColorID::Menutext: + case ColorID::Infotext: + case ColorID::MozMenubartext: + case ColorID::Windowtext: + aResult = mColorDarkText; + break; + case ColorID::Activecaption: + aResult = NS_RGB(0xff, 0xff, 0xff); + break; + case ColorID::Activeborder: + aResult = NS_RGB(0x00, 0x00, 0x00); + break; + case ColorID::Appworkspace: + aResult = NS_RGB(0xFF, 0xFF, 0xFF); + break; + case ColorID::Background: + aResult = NS_RGB(0x63, 0x63, 0xCE); + break; + case ColorID::Buttonface: + case ColorID::MozButtonhoverface: + aResult = NS_RGB(0xF0, 0xF0, 0xF0); + break; + case ColorID::Buttonhighlight: + aResult = NS_RGB(0xFF, 0xFF, 0xFF); + break; + case ColorID::Buttonshadow: + aResult = NS_RGB(0xDC, 0xDC, 0xDC); + break; + case ColorID::Graytext: + aResult = NS_RGB(0x44, 0x44, 0x44); + break; + case ColorID::Inactiveborder: + aResult = NS_RGB(0xff, 0xff, 0xff); + break; + case ColorID::Inactivecaption: + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::Inactivecaptiontext: + aResult = NS_RGB(0x45, 0x45, 0x45); + break; + case ColorID::Scrollbar: + aResult = NS_RGB(0, 0, 0); // XXX + break; + case ColorID::Threeddarkshadow: + aResult = NS_RGB(0xDC, 0xDC, 0xDC); + break; + case ColorID::Threedshadow: + aResult = NS_RGB(0xE0, 0xE0, 0xE0); + break; + case ColorID::Threedface: + aResult = NS_RGB(0xF0, 0xF0, 0xF0); + break; + case ColorID::Threedhighlight: + aResult = NS_RGB(0xff, 0xff, 0xff); + break; + case ColorID::Threedlightshadow: + aResult = NS_RGB(0xDA, 0xDA, 0xDA); + break; + case ColorID::Menu: + aResult = NS_RGB(0xff, 0xff, 0xff); + break; + case ColorID::Infobackground: + aResult = NS_RGB(0xFF, 0xFF, 0xC7); + break; + case ColorID::Windowframe: + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::Window: + case ColorID::MozField: + case ColorID::MozCombobox: + aResult = NS_RGB(0xff, 0xff, 0xff); + break; + case ColorID::MozFieldtext: + case ColorID::MozComboboxtext: + aResult = mColorDarkText; + break; + case ColorID::MozDialog: + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::MozDialogtext: + case ColorID::MozCellhighlighttext: + case ColorID::Selecteditemtext: + case ColorID::MozColheadertext: + case ColorID::MozColheaderhovertext: + aResult = mColorDarkText; + break; + case ColorID::MozDragtargetzone: + case ColorID::MozMacChromeActive: + case ColorID::MozMacChromeInactive: + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::MozMacFocusring: + aResult = NS_RGB(0x3F, 0x98, 0xDD); + break; + case ColorID::MozMacMenushadow: + aResult = NS_RGB(0xA3, 0xA3, 0xA3); + break; + case ColorID::MozMacMenutextdisable: + aResult = NS_RGB(0x88, 0x88, 0x88); + break; + case ColorID::MozMacMenutextselect: + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::MozMacDisabledtoolbartext: + aResult = NS_RGB(0x3F, 0x3F, 0x3F); + break; + case ColorID::MozMacMenuselect: + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::MozButtondefault: + aResult = NS_RGB(0xDC, 0xDC, 0xDC); + break; + case ColorID::MozCellhighlight: + case ColorID::Selecteditem: + case ColorID::MozMacSecondaryhighlight: + // For inactive list selection + aResult = NS_RGB(0xaa, 0xaa, 0xaa); + break; + case ColorID::MozEventreerow: + // Background color of even list rows. + aResult = NS_RGB(0xff, 0xff, 0xff); + break; + case ColorID::MozOddtreerow: + // Background color of odd list rows. + aResult = NS_TRANSPARENT; + break; + case ColorID::MozNativehyperlinktext: + // There appears to be no available system defined color. HARDCODING to the appropriate color. + aResult = NS_RGB(0x14, 0x4F, 0xAE); + break; + case ColorID::MozNativevisitedhyperlinktext: + // Safari defaults to the MacOS color implementation for visited links, which in turn uses + // systemPurpleColor, so we do the same here. + aResult = GetColorFromUIColor([UIColor systemPurpleColor]); + 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::NativeGetInt(IntID aID, int32_t& aResult) { + nsresult res = NS_OK; + + switch (aID) { + case IntID::ScrollButtonLeftMouseButtonAction: + aResult = 0; + break; + case IntID::ScrollButtonMiddleMouseButtonAction: + case IntID::ScrollButtonRightMouseButtonAction: + aResult = 3; + break; + case IntID::CaretBlinkTime: + aResult = 567; + break; + case IntID::CaretWidth: + aResult = 1; + break; + case IntID::ShowCaretDuringSelection: + aResult = 0; + break; + case IntID::SelectTextfieldsOnKeyFocus: + // Select textfield content when focused by kbd + // used by nsEventStateManager::sTextfieldSelectModel + aResult = 1; + break; + case IntID::SubmenuDelay: + aResult = 200; + break; + case IntID::MenusCanOverlapOSBar: + // xul popups are not allowed to overlap the menubar. + aResult = 0; + break; + case IntID::SkipNavigatingDisabledMenuItem: + aResult = 1; + break; + case IntID::DragThresholdX: + case IntID::DragThresholdY: + aResult = 4; + break; + case IntID::ScrollArrowStyle: + aResult = eScrollArrow_None; + break; + case IntID::TreeOpenDelay: + aResult = 1000; + break; + case IntID::TreeCloseDelay: + aResult = 1000; + break; + case IntID::TreeLazyScrollDelay: + aResult = 150; + break; + case IntID::TreeScrollDelay: + aResult = 100; + break; + case IntID::TreeScrollLinesMax: + aResult = 3; + break; + case IntID::DWMCompositor: + case IntID::WindowsClassic: + case IntID::WindowsDefaultTheme: + aResult = 0; + res = NS_ERROR_NOT_IMPLEMENTED; + break; + case IntID::MacGraphiteTheme: + aResult = 0; + break; + case IntID::TabFocusModel: + aResult = 1; // default to just textboxes + break; + case IntID::ScrollToClick: + aResult = 0; + break; + case IntID::ChosenMenuItemsShouldBlink: + aResult = 1; + break; + case IntID::IMERawInputUnderlineStyle: + case IntID::IMEConvertedTextUnderlineStyle: + case IntID::IMESelectedRawTextUnderlineStyle: + case IntID::IMESelectedConvertedTextUnderline: + aResult = static_cast<int32_t>(StyleTextDecorationStyle::Solid); + break; + case IntID::SpellCheckerUnderlineStyle: + aResult = static_cast<int32_t>(StyleTextDecorationStyle::Dotted); + break; + case IntID::ContextMenuOffsetVertical: + case IntID::ContextMenuOffsetHorizontal: + aResult = 2; + break; + default: + aResult = 0; + res = NS_ERROR_FAILURE; + } + return res; +} + +NS_IMETHODIMP +nsLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) { + nsresult res = NS_OK; + + switch (aID) { + case FloatID::IMEUnderlineRelativeSize: + aResult = 2.0f; + break; + case FloatID::SpellCheckerUnderlineRelativeSize: + aResult = 2.0f; + break; + default: + aResult = -1.0; + res = NS_ERROR_FAILURE; + } + + return res; +} + +bool nsLookAndFeel::NativeGetFont(FontID aID, nsString& aFontName, gfxFontStyle& aFontStyle) { + // hack for now + if (aID == FontID::Window || aID == FontID::Document) { + aFontStyle.style = FontSlantStyle::Normal(); + aFontStyle.weight = FontWeight::Normal(); + aFontStyle.stretch = FontStretch::Normal(); + aFontStyle.size = 14; + aFontStyle.systemFont = true; + + aFontName.AssignLiteral("sans-serif"); + return true; + } + + // TODO: implement more here? + return false; +} + +void nsLookAndFeel::EnsureInit() { + if (mInitialized) { + return; + } + mInitialized = true; + + nscolor color; + GetColor(ColorID::TextSelectBackground, color); + if (color == 0x000000) { + mColorTextSelectForeground = NS_RGB(0xff, 0xff, 0xff); + } else { + mColorTextSelectForeground = NS_SAME_AS_FOREGROUND_COLOR; + } + + mColorDarkText = GetColorFromUIColor([UIColor darkTextColor]); + + RecordTelemetry(); +} diff --git a/widget/uikit/nsScreenManager.h b/widget/uikit/nsScreenManager.h new file mode 100644 index 0000000000..db4d7ed8e9 --- /dev/null +++ b/widget/uikit/nsScreenManager.h @@ -0,0 +1,60 @@ +/* -*- 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/. */ + +#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) override { + *outId = 0; + return NS_OK; + } + + NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight) override; + NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, + int32_t* aHeight) override; + NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, + int32_t* aHeight) override; + NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, + int32_t* aHeight) override; + NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) override; + NS_IMETHOD GetColorDepth(int32_t* aColorDepth) override; + NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override; + NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) override { + return GetContentsScaleFactor(aScaleFactor); + } + + private: + UIScreen* mScreen; +}; + +class UIKitScreenManager : public nsIScreenManager { + public: + UIKitScreenManager(); + + NS_DECL_ISUPPORTS + + NS_DECL_NSISCREENMANAGER + + static LayoutDeviceIntRect GetBounds(); + + private: + virtual ~UIKitScreenManager() {} + // TODO: support >1 screen, iPad supports external displays + nsCOMPtr<nsIScreen> mScreen; +}; + +#endif // nsScreenManager_h_ diff --git a/widget/uikit/nsScreenManager.mm b/widget/uikit/nsScreenManager.mm new file mode 100644 index 0000000000..a9ed0f3d66 --- /dev/null +++ b/widget/uikit/nsScreenManager.mm @@ -0,0 +1,98 @@ +/* -*- 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 <UIKit/UIScreen.h> + +#include "gfxPoint.h" +#include "nsScreenManager.h" +#include "nsAppShell.h" + +static LayoutDeviceIntRect 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])) {} + +LayoutDeviceIntRect 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); +} diff --git a/widget/uikit/nsWidgetFactory.mm b/widget/uikit/nsWidgetFactory.mm new file mode 100644 index 0000000000..7250d6ddad --- /dev/null +++ b/widget/uikit/nsWidgetFactory.mm @@ -0,0 +1,49 @@ +/* -*- 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/. */ + +#include "nsISupports.h" +#include "mozilla/ModuleUtils.h" + +#include "nsWidgetsCID.h" + +#include "nsAppShell.h" +#include "nsAppShellSingleton.h" +#include "nsLookAndFeel.h" +#include "nsScreenManager.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(UIKitScreenManager) + +#include "GfxInfo.h" +namespace mozilla { +namespace widget { +// This constructor should really be shared with all platforms. +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init) +} // namespace widget +} // namespace mozilla + +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_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/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(); +} + +extern const mozilla::Module kWidgetModule = { + mozilla::Module::kVersion, kWidgetCIDs, kWidgetContracts, nullptr, nullptr, nsAppShellInit, + nsWidgetUIKitModuleDtor}; diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h new file mode 100644 index 0000000000..ed36e1df63 --- /dev/null +++ b/widget/uikit/nsWindow.h @@ -0,0 +1,110 @@ +/* -*- 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/. */ + +#ifndef NSWINDOW_H_ +#define NSWINDOW_H_ + +#include "nsBaseWidget.h" +#include "gfxPoint.h" + +#include "nsTArray.h" + +@class UIWindow; +@class UIView; +@class ChildView; + +class nsWindow final : public nsBaseWidget { + typedef nsBaseWidget Inherited; + + public: + nsWindow(); + + NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, Inherited) + + // + // nsIWidget + // + + [[nodiscard]] virtual nsresult Create(nsIWidget* aParent, nsNativeWidget aNativeParent, + const LayoutDeviceIntRect& aRect, + widget::InitData* aInitData = nullptr) override; + virtual void Destroy() override; + virtual void Show(bool aState) override; + virtual void Enable(bool aState) override {} + virtual bool IsEnabled() const override { return true; } + virtual bool IsVisible() const override { return mVisible; } + virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; + virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; + + virtual void SetBackgroundColor(const nscolor& aColor) override; + virtual void* GetNativeData(uint32_t aDataType) override; + + virtual void Move(double aX, double aY) override; + virtual nsSizeMode SizeMode() override { return mSizeMode; } + virtual void SetSizeMode(nsSizeMode aMode) override; + void EnteredFullScreen(bool aFullScreen); + virtual void Resize(double aWidth, double aHeight, bool aRepaint) override; + virtual void Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override; + virtual LayoutDeviceIntRect GetScreenBounds() override; + void ReportMoveEvent(); + void ReportSizeEvent(); + void ReportSizeModeEvent(nsSizeMode 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; + + virtual nsresult SetTitle(const nsAString& aTitle) override { return NS_OK; } + + virtual void Invalidate(const LayoutDeviceIntRect& aRect) override; + virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override; + + void WillPaintWindow(); + bool PaintWindow(LayoutDeviceIntRegion aRegion); + + bool HasModalDescendents() { return false; } + + // virtual nsresult + // NotifyIME(const IMENotification& aIMENotification) override; + virtual void SetInputContext(const InputContext& aContext, const InputContextAction& aAction); + virtual InputContext GetInputContext(); + /* + virtual bool ExecuteNativeKeyBinding( + NativeKeyBindingsType aType, + const mozilla::WidgetKeyboardEvent& aEvent, + DoCommandCallback aCallback, + void* aCallbackData) 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; + nsSizeMode mSizeMode; + nsTArray<nsWindow*> mChildren; + nsWindow* mParent; + InputContext mInputContext; + + void OnSizeChanged(const mozilla::gfx::IntSize& 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_ */ diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm new file mode 100644 index 0000000000..ba360f928e --- /dev/null +++ b/widget/uikit/nsWindow.mm @@ -0,0 +1,728 @@ +/* -*- 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 <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/ProfilerLabels.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/MouseEventBinding.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 LayoutDeviceIntRect& 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:(EventMessage)aType + point:(LayoutDeviceIntPoint)aPoint + widget:(nsWindow*)aWindow; +- (void)handleTap:(UITapGestureRecognizer*)sender; +- (BOOL)isUsingMainThreadOpenGL; +- (void)drawUsingOpenGL; +- (void)drawUsingOpenGLCallback; +- (void)sendTouchEvent:(EventMessage)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:(EventMessage)aType + point:(LayoutDeviceIntPoint)aPoint + widget:(nsWindow*)aWindow { + WidgetMouseEvent event(true, aType, aWindow, WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); + + event.mRefPoint = aPoint; + event.mClickCount = 1; + event.button = MouseButton::ePrimary; + event.mTime = PR_IntervalNow(); + event.inputSource = MouseEvent_Binding::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:eMouseMove point:lp widget:mGeckoChild]; + [self sendMouseEvent:eMouseDown point:lp widget:mGeckoChild]; + [self sendMouseEvent:eMouseUp point:lp widget:mGeckoChild]; + } +} + +- (void)sendTouchEvent:(EventMessage)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.mTime = PR_IntervalNow(); + event.mTouches.SetCapacity(aTouches.count); + for (UITouch* touch in aTouches) { + LayoutDeviceIntPoint loc = + UIKitPointsToDevPixels([touch locationInView:self], [self contentScaleFactor]); + LayoutDeviceIntPoint radius = UIKitPointsToDevPixels([touch majorRadius], [touch majorRadius]); + 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); + RefPtr<Touch> t = new Touch(id, loc, radius, 0.0f, 1.0f); + event.mRefPoint = loc; + event.mTouches.AppendElement(t); + } + aWindow->DispatchInputEvent(&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:eTouchStart touches:[event allTouches] widget:mGeckoChild]; +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { + ALOG("[ChildView[%p] touchesCancelled", self); + [self sendTouchEvent:eTouchCancel 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:eTouchEnd 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:eTouchMove 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 NO; +} + +- (void)drawUsingOpenGL { + ALOG("drawUsingOpenGL"); + AUTO_PROFILER_LABEL("ChildView::drawUsingOpenGL", OTHER); + + if (!mGeckoChild->IsVisible()) return; + + mWaitingForPaint = NO; + + LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); + LayoutDeviceIntRegion 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 + LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); + + 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; + } + AUTO_PROFILER_LABEL("ChildView::drawRect", OTHER); + + // 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; + gfx::IntSize backingSize(viewSize.width * scale, viewSize.height * scale); + + CGContextSaveGState(aContext); + + LayoutDeviceIntRegion region = LayoutDeviceIntRect( + NSToIntRound(aRect.origin.x * scale), NSToIntRound(aRect.origin.y * scale), + NSToIntRound(aRect.size.width * scale), NSToIntRound(aRect.size.height * scale)); + + // Create Cairo objects. + RefPtr<gfxQuartzSurface> targetSurface; + + UniquePtrPtr<gfxContext> targetContext; + 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::CreateDrawTargetForSurface(targetSurface, backingSize); + if (!dt || !dt->IsValid()) { + gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) + << "Window context problem 2 " << backingSize; + return; + } + targetContext = gfxContext::CreateOrNull(dt); + } else { + MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backend"); + } + MOZ_ASSERT(targetContext); // already checked for valid draw targets above + + // Set up the clip region. + targetContext->NewPath(); + for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) { + const LayoutDeviceIntRect& r = iter.Get(); + targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); + } + targetContext->Clip(); + + // nsAutoRetainCocoaObject kungFuDeathGrip(self); + bool painted = false; + 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 + +nsWindow::nsWindow() + : mNativeView(nullptr), mVisible(false), mSizeMode(nsSizeMode_Normal), 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 == WindowType::TopLevel || mWindowType == WindowType::Dialog || + mWindowType == WindowType::Invisible; +} + +// +// nsIWidget +// + +nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, + const LayoutDeviceIntRect& aRect, widget::InitData* 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 = WindowType::TopLevel; + mBorderStyle = BorderStyle::Default; + + Inherited::BaseCreate(aParent, 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; +} + +void nsWindow::Destroy() { + 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; +} + +void 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; + } +} + +void nsWindow::Move(double aX, double aY) { + if (!mNativeView || (mBounds.x == aX && mBounds.y == aY)) return; + + // 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(); +} + +void 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; + + 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(); +} + +void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) { + if (!mNativeView || (mBounds.width == aWidth && mBounds.height == aHeight)) return; + + mBounds.width = aWidth; + mBounds.height = aHeight; + + [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; + + if (mVisible && aRepaint) [mNativeView setNeedsDisplay]; + + ReportSizeEvent(); +} + +void nsWindow::SetSizeMode(nsSizeMode aMode) { + if (aMode == static_cast<int32_t>(mSizeMode)) { + return; + } + + mSizeMode = static_cast<nsSizeMode>(aMode); + if (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen) { + // Resize to fill screen + nsBaseWidget::InfallibleMakeFullScreen(true); + } + ReportSizeModeEvent(aMode); +} + +void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) { + if (!mNativeView || !mVisible) return; + + MOZ_RELEASE_ASSERT(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_WR, + "Shouldn't need to invalidate with accelerated OMTC layers!"); + + [mNativeView setNeedsLayout]; + [mNativeView setNeedsDisplayInRect:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; +} + +void nsWindow::SetFocus(Raise) { + [[mNativeView window] makeKeyWindow]; + [mNativeView becomeFirstResponder]; +} + +void nsWindow::WillPaintWindow() { + if (mWidgetListener) { + mWidgetListener->WillPaintWindow(this); + } +} + +bool nsWindow::PaintWindow(LayoutDeviceIntRegion 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(nsSizeMode 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) { + LayoutDeviceIntRect innerBounds = GetClientBounds(); + mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height); + } +} + +LayoutDeviceIntRect nsWindow::GetScreenBounds() { + return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size()); +} + +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; +} + +nsresult nsWindow::DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) { + aStatus = nsEventStatus_eIgnore; + nsCOMPtr<nsIWidget> kungFuDeathGrip(aEvent->mWidget); + + if (mWidgetListener) aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents); + + return NS_OK; +} + +void nsWindow::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { + // TODO: actually show VKB + mInputContext = aContext; +} + +mozilla::widget::InputContext nsWindow::GetInputContext() { 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: + 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_RAW_NATIVE_IME_CONTEXT: + retVal = GetPseudoIMEContext(); + if (retVal) { + break; + } + retVal = NS_ONLY_ONE_NATIVE_IME_CONTEXT; + 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; +} + +already_AddRefed<nsIWidget> nsIWidget::CreateTopLevelWindow() { + nsCOMPtr<nsIWidget> window = new nsWindow(); + return window.forget(); +} + +already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() { + nsCOMPtr<nsIWidget> window = new nsWindow(); + return window.forget(); +} |