fune/widget/uikit/nsWindow.mm
Gerald Squelart 2416d881e2 Bug 1691589 - Reduce reliance on GeckoProfiler.h when only labels (and maybe markers) are needed - r=necko-reviewers,geckoview-reviewers,sg,agi,florian
There are no code changes, only #include changes.
It was a fairly mechanical process: Search for all "AUTO_PROFILER_LABEL", and in each file, if only labels are used, convert "GeckoProfiler.h" into "ProfilerLabels.h" (or just add that last one where needed).
In some files, there were also some marker calls but no other profiler-related calls, in these cases "GeckoProfiler.h" was replaced with both "ProfilerLabels.h" and "ProfilerMarkers.h", which still helps in reducing the use of the all-encompassing "GeckoProfiler.h".

Differential Revision: https://phabricator.services.mozilla.com/D104588
2021-02-16 04:44:19 +00:00

753 lines
23 KiB
Text

/* -*- 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 mGeckoChild->GetLayerManager(nullptr)->GetBackendType() ==
mozilla::layers::LayersBackend::LAYERS_OPENGL;
}
- (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;
RefPtr<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;
}
dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
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;
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
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
//
nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
const LayoutDeviceIntRect& 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, 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;
}
nsresult 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;
}
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_CLIENT,
"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:
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;
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();
}