1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #import <UIKit/UIEvent.h>
7 #import <UIKit/UIKit.h>
8 #import <UIKit/UIGraphics.h>
9 #import <UIKit/UIInterface.h>
10 #import <UIKit/UIScreen.h>
11 #import <UIKit/UITapGestureRecognizer.h>
12 #import <UIKit/UITouch.h>
13 #import <UIKit/UIView.h>
14 #import <UIKit/UIViewController.h>
15 #import <UIKit/UIWindow.h>
16 #import <QuartzCore/QuartzCore.h>
21 #include "nsAppShell.h"
23 # include "nsAccessibilityService.h"
24 # include "mozilla/a11y/LocalAccessible.h"
27 #include "nsWidgetsCID.h"
28 #include "nsGfxCIID.h"
30 #include "gfxPlatform.h"
31 #include "gfxQuartzSurface.h"
33 #include "gfxImageSurface.h"
34 #include "gfxContext.h"
35 #include "nsObjCExceptions.h"
38 #include "TextInputHandler.h"
39 #include "UIKitUtils.h"
41 #include "mozilla/BasicEvents.h"
42 #include "mozilla/ProfilerLabels.h"
43 #include "mozilla/TouchEvents.h"
44 #include "mozilla/Unused.h"
45 #include "mozilla/dom/MouseEventBinding.h"
46 #include "mozilla/gfx/Logging.h"
48 # include "mozilla/a11y/MUIRootAccessibleProtocol.h"
51 using namespace mozilla;
52 using namespace mozilla::gfx;
53 using namespace mozilla::layers;
54 using mozilla::dom::Touch;
55 using mozilla::widget::UIKitUtils;
57 #define ALOG(args...) \
58 fprintf(stderr, args); \
61 static LayoutDeviceIntPoint UIKitPointsToDevPixels(CGPoint aPoint,
62 CGFloat aBackingScale) {
63 return LayoutDeviceIntPoint(NSToIntRound(aPoint.x * aBackingScale),
64 NSToIntRound(aPoint.y * aBackingScale));
67 static CGRect DevPixelsToUIKitPoints(const LayoutDeviceIntRect& aRect,
68 CGFloat aBackingScale) {
69 return CGRectMake((CGFloat)aRect.x / aBackingScale,
70 (CGFloat)aRect.y / aBackingScale,
71 (CGFloat)aRect.width / aBackingScale,
72 (CGFloat)aRect.height / aBackingScale);
75 // Used to retain a Cocoa object for the remainder of a method's execution.
76 class nsAutoRetainUIKitObject {
78 explicit nsAutoRetainUIKitObject(id anObject) { mObject = [anObject retain]; }
79 ~nsAutoRetainUIKitObject() { [mObject release]; }
82 id mObject; // [STRONG]
86 @interface ChildView : UIView <UIKeyInput, MUIRootAccessibleProtocol> {
88 @interface ChildView : UIView <UIKeyInput> {
91 nsWindow* mGeckoChild; // weak ref
92 BOOL mWaitingForPaint;
93 NSMapTable<UITouch*, NSNumber*>* mTouches;
96 // sets up our view, attaching it to its owning gecko view
97 - (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild;
98 // Our Gecko child was Destroy()ed
99 - (void)widgetDestroyed;
100 // Tear down this ChildView
101 - (void)delayedTearDown;
102 - (void)sendMouseEvent:(EventMessage)aType
103 point:(LayoutDeviceIntPoint)aPoint
104 widget:(nsWindow*)aWindow;
105 - (void)handleTap:(UITapGestureRecognizer*)sender;
106 - (BOOL)isUsingMainThreadOpenGL;
107 - (void)drawUsingOpenGL;
108 - (void)drawUsingOpenGLCallback;
109 - (void)sendTouchEvent:(EventMessage)aType
110 touches:(NSSet*)aTouches
111 widget:(nsWindow*)aWindow;
112 // Event handling (UIResponder)
113 - (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event;
114 - (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event;
115 - (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event;
116 - (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event;
118 - (void)activateWindow:(NSNotification*)notification;
119 - (void)deactivateWindow:(NSNotification*)notification;
123 - (BOOL)hasRepresentedView;
124 - (id)representedView;
127 - (BOOL)isAccessibilityElement;
128 - (NSString*)accessibilityLabel;
129 - (CGRect)accessibilityFrame;
130 - (NSString*)accessibilityValue;
131 - (uint64_t)accessibilityTraits;
132 - (NSInteger)accessibilityElementCount;
133 - (nullable id)accessibilityElementAtIndex:(NSInteger)index;
134 - (NSInteger)indexOfAccessibilityElement:(id)element;
135 - (NSArray* _Nullable)accessibilityElements;
136 - (UIAccessibilityContainerType)accessibilityContainerType;
141 @implementation ChildView
142 + (Class)layerClass {
143 return [CAEAGLLayer class];
146 - (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild {
147 self.multipleTouchEnabled = YES;
148 if ((self = [super initWithFrame:inFrame])) {
149 mGeckoChild = inChild;
151 ALOG("[ChildView[%p] initWithFrame:] (mGeckoChild = %p)", (void*)self,
156 UITapGestureRecognizer* tapRecognizer =
157 [[UITapGestureRecognizer alloc] initWithTarget:self
158 action:@selector(handleTap:)];
159 tapRecognizer.numberOfTapsRequired = 1;
160 [self addGestureRecognizer:tapRecognizer];
162 mTouches = [[NSMapTable alloc] init];
165 // This is managed with weak references by the notification center so that we
166 // do not need to call removeObserver.
167 // https://developer.apple.com/documentation/foundation/nsnotificationcenter/1415360-addobserver#discussion
168 [[NSNotificationCenter defaultCenter]
170 selector:@selector(activateWindow:)
171 name:UIWindowDidBecomeKeyNotification
173 [[NSNotificationCenter defaultCenter]
175 selector:@selector(deactivateWindow:)
176 name:UIWindowDidResignKeyNotification
182 - (void)widgetDestroyed {
183 mGeckoChild = nullptr;
187 - (void)delayedTearDown {
188 [self removeFromSuperview];
192 - (void)activateWindow:(NSNotification*)notification {
193 ALOG("[[ChildView[%p] activateWindow]", (void*)self);
199 if (nsIWidgetListener* listener = mGeckoChild->GetWidgetListener()) {
200 listener->WindowActivated();
204 - (void)deactivateWindow:(NSNotification*)notification {
205 ALOG("[[ChildView[%p] deactivateWindow]", (void*)self);
211 if (nsIWidgetListener* listener = mGeckoChild->GetWidgetListener()) {
212 listener->WindowDeactivated();
216 - (void)sendMouseEvent:(EventMessage)aType
217 point:(LayoutDeviceIntPoint)aPoint
218 widget:(nsWindow*)aWindow {
219 WidgetMouseEvent event(true, aType, aWindow, WidgetMouseEvent::eReal,
220 WidgetMouseEvent::eNormal);
222 event.mRefPoint = aPoint;
223 event.mClickCount = 1;
224 event.mButton = MouseButton::ePrimary;
225 event.mInputSource = mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
227 nsEventStatus status;
228 aWindow->DispatchEvent(&event, status);
231 - (void)handleTap:(UITapGestureRecognizer*)sender {
232 if (sender.state == UIGestureRecognizerStateEnded) {
233 ALOG("[ChildView[%p] handleTap]", self);
234 LayoutDeviceIntPoint lp = UIKitPointsToDevPixels(
235 [sender locationInView:self], [self contentScaleFactor]);
236 [self sendMouseEvent:eMouseMove point:lp widget:mGeckoChild];
237 [self sendMouseEvent:eMouseDown point:lp widget:mGeckoChild];
238 [self sendMouseEvent:eMouseUp point:lp widget:mGeckoChild];
242 - (void)sendTouchEvent:(EventMessage)aType
243 touches:(NSSet*)aTouches
244 widget:(nsWindow*)aWindow {
245 WidgetTouchEvent event(true, aType, aWindow);
246 // XXX: I think nativeEvent.timestamp * 1000 is probably usable here but
247 // I don't care that much right now.
248 event.mTouches.SetCapacity(aTouches.count);
249 for (UITouch* touch in aTouches) {
250 LayoutDeviceIntPoint loc = UIKitPointsToDevPixels(
251 [touch locationInView:self], [self contentScaleFactor]);
252 LayoutDeviceIntPoint radius = UIKitPointsToDevPixels(
253 CGPointMake([touch majorRadius], [touch majorRadius]),
254 [self contentScaleFactor]);
255 NSNumber* value = [mTouches objectForKey:touch];
257 // This shouldn't happen.
258 NS_ASSERTION(false, "Got a touch that we didn't know about");
261 int id = [value intValue];
262 RefPtr<Touch> t = new Touch(id, loc, radius, 0.0f, 1.0f);
263 event.mRefPoint = loc;
264 event.mTouches.AppendElement(t);
266 aWindow->DispatchInputEvent(&event);
269 - (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
270 ALOG("[ChildView[%p] touchesBegan", self);
271 if (!mGeckoChild) return;
273 for (UITouch* touch : touches) {
274 [mTouches setObject:[NSNumber numberWithInt:mNextTouchID] forKey:touch];
277 [self sendTouchEvent:eTouchStart
278 touches:[event allTouches]
282 - (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
283 ALOG("[ChildView[%p] touchesCancelled", self);
284 [self sendTouchEvent:eTouchCancel touches:touches widget:mGeckoChild];
285 for (UITouch* touch : touches) {
286 [mTouches removeObjectForKey:touch];
288 if (mTouches.count == 0) {
293 - (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
294 ALOG("[ChildView[%p] touchesEnded", self);
295 if (!mGeckoChild) return;
297 [self sendTouchEvent:eTouchEnd touches:touches widget:mGeckoChild];
298 for (UITouch* touch : touches) {
299 [mTouches removeObjectForKey:touch];
301 if (mTouches.count == 0) {
306 - (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
307 ALOG("[ChildView[%p] touchesMoved", self);
308 if (!mGeckoChild) return;
310 [self sendTouchEvent:eTouchMove
311 touches:[event allTouches]
315 - (BOOL)canBecomeFirstResponder {
320 if (mGeckoChild->IsVirtualKeyboardDisabled()) {
326 - (void)setNeedsDisplayInRect:(CGRect)aRect {
327 if ([self isUsingMainThreadOpenGL]) {
328 // Draw without calling drawRect. This prevent us from
329 // needing to access the normal window buffer surface unnecessarily, so we
330 // waste less time synchronizing the two surfaces.
331 if (!mWaitingForPaint) {
332 mWaitingForPaint = YES;
333 // Use NSRunLoopCommonModes instead of the default NSDefaultRunLoopMode
334 // so that the timer also fires while a native menu is open.
335 [self performSelector:@selector(drawUsingOpenGLCallback)
338 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
343 - (BOOL)isUsingMainThreadOpenGL {
344 if (!mGeckoChild || ![self window]) return NO;
349 - (void)drawUsingOpenGL {
350 ALOG("drawUsingOpenGL");
351 AUTO_PROFILER_LABEL("ChildView::drawUsingOpenGL", OTHER);
353 if (!mGeckoChild->IsVisible()) return;
355 mWaitingForPaint = NO;
357 LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds();
358 LayoutDeviceIntRegion region(geckoBounds);
360 mGeckoChild->PaintWindow(region);
363 // Called asynchronously after setNeedsDisplay in order to avoid entering the
364 // normal drawing machinery.
365 - (void)drawUsingOpenGLCallback {
366 if (mWaitingForPaint) {
367 [self drawUsingOpenGL];
371 // The display system has told us that a portion of our view is dirty. Tell
373 - (void)drawRect:(CGRect)aRect {
374 CGContextRef cgContext = UIGraphicsGetCurrentContext();
375 [self drawRect:aRect inContext:cgContext];
378 - (void)drawRect:(CGRect)aRect inContext:(CGContextRef)aContext {
380 LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds();
383 "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d "
385 self, mGeckoChild, aRect.origin.x, aRect.origin.y, aRect.size.width,
386 aRect.size.height, aContext, geckoBounds.x, geckoBounds.y,
387 geckoBounds.width, geckoBounds.height);
389 CGAffineTransform xform = CGContextGetCTM(aContext);
390 fprintf(stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b,
391 xform.c, xform.d, xform.tx, xform.ty);
395 // For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is
396 // directly called from a delayed perform callback - without going through
398 // Paints that come through here are triggered by something that Cocoa
399 // controls, for example by window resizing or window focus changes.
401 // Do GL composition and return.
402 [self drawUsingOpenGL];
405 AUTO_PROFILER_LABEL("ChildView::drawRect", OTHER);
407 // The CGContext that drawRect supplies us with comes with a transform that
408 // scales one user space unit to one Cocoa point, which can consist of
409 // multiple dev pixels. But Gecko expects its supplied context to be scaled
410 // to device pixels, so we need to reverse the scaling.
411 double scale = mGeckoChild->BackingScaleFactor();
412 CGContextSaveGState(aContext);
413 CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale);
415 CGSize viewSize = [self bounds].size;
416 gfx::IntSize backingSize(NSToIntRound(viewSize.width * scale),
417 NSToIntRound(viewSize.height * scale));
419 CGContextSaveGState(aContext);
421 LayoutDeviceIntRegion region =
422 LayoutDeviceIntRect(NSToIntRound(aRect.origin.x * scale),
423 NSToIntRound(aRect.origin.y * scale),
424 NSToIntRound(aRect.size.width * scale),
425 NSToIntRound(aRect.size.height * scale));
427 // Create Cairo objects.
428 RefPtr<gfxQuartzSurface> targetSurface;
430 UniquePtr<gfxContext> targetContext;
431 if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(
432 gfx::BackendType::CAIRO)) {
433 // This is dead code unless you mess with prefs, but keep it around for
435 targetSurface = new gfxQuartzSurface(aContext, backingSize);
436 RefPtr<gfx::DrawTarget> dt =
437 gfxPlatform::CreateDrawTargetForSurface(targetSurface, backingSize);
438 if (!dt || !dt->IsValid()) {
439 gfxDevCrash(mozilla::gfx::LogReason::InvalidContext)
440 << "Window context problem 2 " << backingSize;
443 targetContext = gfxContext::CreateOrNull(dt);
445 MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backend");
447 MOZ_ASSERT(targetContext); // already checked for valid draw targets above
449 // Set up the clip region.
450 targetContext->NewPath();
451 for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
452 const LayoutDeviceIntRect& r = iter.Get();
453 targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
455 targetContext->Clip();
457 // nsAutoRetainCocoaObject kungFuDeathGrip(self);
458 bool painted = false;
459 targetContext = nullptr;
460 targetSurface = nullptr;
462 CGContextRestoreGState(aContext);
464 // Undo the scale transform so that from now on the context is in
465 // CocoaPoints again.
466 CGContextRestoreGState(aContext);
467 if (!painted && [self isOpaque]) {
468 // Gecko refused to draw, but we've claimed to be opaque, so we have to
469 // draw something--fill with white.
470 CGContextSetRGBFillColor(aContext, 1, 1, 1, 1);
471 CGContextFillRect(aContext, aRect);
475 fprintf(stderr, "---- update done ----\n");
478 CGContextSetRGBStrokeColor (aContext,
479 ((((unsigned long)self) & 0xff)) / 255.0,
480 ((((unsigned long)self) & 0xff00) >> 8) / 255.0,
481 ((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
484 CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8);
485 CGContextSetLineWidth(aContext, 4.0);
486 CGContextStrokeRect(aContext, aRect);
492 - (void)insertText:(NSString*)text {
493 if (!mGeckoChild || mGeckoChild->Destroyed()) {
496 widget::TextInputHandler* textInputHandler =
497 mGeckoChild->GetTextInputHandler();
498 if (!textInputHandler) {
501 textInputHandler->InsertText(text);
504 - (void)deleteBackward {
505 if (!mGeckoChild || mGeckoChild->Destroyed()) {
508 widget::TextInputHandler* textInputHandler =
509 mGeckoChild->GetTextInputHandler();
510 if (!textInputHandler) {
513 textInputHandler->HandleCommand(Command::DeleteCharBackward);
517 if (!mGeckoChild || mGeckoChild->Destroyed()) {
520 widget::InputContext context = mGeckoChild->GetInputContext();
521 if (context.mIMEState.mEnabled == mozilla::widget::IMEEnabled::Disabled) {
529 - (UIKeyboardType)keyboardType {
530 if (!mGeckoChild || mGeckoChild->Destroyed()) {
531 return UIKeyboardTypeDefault;
533 return UIKitUtils::GetUIKeyboardType(mGeckoChild->GetInputContext());
536 - (UIReturnKeyType)returnKeyType {
537 if (!mGeckoChild || mGeckoChild->Destroyed()) {
538 return UIReturnKeyDefault;
540 return UIKitUtils::GetUIReturnKeyType(mGeckoChild->GetInputContext());
543 - (UITextAutocapitalizationType)autocapitalizationType {
544 if (!mGeckoChild || mGeckoChild->Destroyed()) {
545 return UITextAutocapitalizationTypeNone;
547 return UIKitUtils::GetUITextAutocapitalizationType(
548 mGeckoChild->GetInputContext());
551 - (BOOL)isSecureTextEntry {
552 if (!mGeckoChild || mGeckoChild->Destroyed()) {
555 if (mGeckoChild->GetInputContext().IsPasswordEditor()) {
564 - (id<MUIRootAccessibleProtocol>)accessible {
565 if (!mGeckoChild) return nil;
567 id<MUIRootAccessibleProtocol> nativeAccessible = nil;
569 // nsAutoRetainCocoaObject kungFuDeathGrip(self);
570 RefPtr<nsWindow> geckoChild(mGeckoChild);
571 RefPtr<a11y::LocalAccessible> accessible = geckoChild->GetRootAccessible();
572 if (!accessible) return nil;
574 accessible->GetNativeInterface((void**)&nativeAccessible);
576 return nativeAccessible;
579 - (BOOL)hasRepresentedView {
583 - (id)representedView {
587 - (BOOL)isAccessibilityElement {
588 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
589 return [super isAccessibilityElement];
592 return [[self accessible] isAccessibilityElement];
595 - (NSString*)accessibilityLabel {
596 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
597 return [super accessibilityLabel];
600 return [[self accessible] accessibilityLabel];
603 - (CGRect)accessibilityFrame {
604 // Use the UIView implementation here. We rely on the position of this
605 // frame to place gecko bounds in the right offset.
606 return [super accessibilityFrame];
609 - (NSString*)accessibilityValue {
610 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
611 return [super accessibilityValue];
614 return [[self accessible] accessibilityValue];
617 - (uint64_t)accessibilityTraits {
618 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
619 return [super accessibilityTraits];
622 return [[self accessible] accessibilityTraits];
625 - (NSInteger)accessibilityElementCount {
626 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
627 return [super accessibilityElementCount];
630 return [[self accessible] accessibilityElementCount];
633 - (nullable id)accessibilityElementAtIndex:(NSInteger)index {
634 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
635 return [super accessibilityElementAtIndex:index];
638 return [[self accessible] accessibilityElementAtIndex:index];
641 - (NSInteger)indexOfAccessibilityElement:(id)element {
642 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
643 return [super indexOfAccessibilityElement:element];
646 return [[self accessible] indexOfAccessibilityElement:element];
649 - (NSArray* _Nullable)accessibilityElements {
650 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
651 return [super accessibilityElements];
654 return [[self accessible] accessibilityElements];
657 - (UIAccessibilityContainerType)accessibilityContainerType {
658 if (!mozilla::a11y::ShouldA11yBeEnabled()) {
659 return [super accessibilityContainerType];
662 return [[self accessible] accessibilityContainerType];
669 : mNativeView(nullptr),
671 mSizeMode(nsSizeMode_Normal),
674 nsWindow::~nsWindow() {
675 [mNativeView widgetDestroyed]; // Safe if mNativeView is nil.
676 TearDownView(); // Safe if called twice.
679 void nsWindow::TearDownView() {
680 if (!mNativeView) return;
682 [mNativeView performSelectorOnMainThread:@selector(delayedTearDown)
684 waitUntilDone:false];
688 bool nsWindow::IsTopLevel() {
689 return mWindowType == WindowType::TopLevel ||
690 mWindowType == WindowType::Dialog ||
691 mWindowType == WindowType::Invisible;
698 nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
699 const LayoutDeviceIntRect& aRect,
700 widget::InitData* aInitData) {
701 ALOG("nsWindow[%p]::Create %p/%p [%d %d %d %d]", (void*)this, (void*)aParent,
702 (void*)aNativeParent, aRect.x, aRect.y, aRect.width, aRect.height);
703 nsWindow* parent = (nsWindow*)aParent;
704 ChildView* nativeParent = (ChildView*)aNativeParent;
706 if (parent == nullptr && nativeParent) parent = nativeParent->mGeckoChild;
707 if (parent && nativeParent == nullptr) nativeParent = parent->mNativeView;
711 ALOG("nsWindow[%p]::Create bounds: %d %d %d %d", (void*)this, mBounds.x,
712 mBounds.y, mBounds.width, mBounds.height);
714 // Set defaults which can be overriden from aInitData in BaseCreate
715 mWindowType = WindowType::TopLevel;
716 mBorderStyle = BorderStyle::Default;
718 Inherited::BaseCreate(aParent, aInitData);
720 NS_ASSERTION(IsTopLevel() || parent,
721 "non top level window doesn't have a parent!");
723 mNativeView = [[ChildView alloc]
724 initWithFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())
726 mNativeView.hidden = YES;
729 parent->mChildren.AppendElement(this);
734 [nativeParent addSubview:mNativeView];
735 } else if (nsAppShell::gWindow) {
736 [nsAppShell::gWindow.rootViewController.view addSubview:mNativeView];
738 [nsAppShell::gTopLevelViews addObject:mNativeView];
741 mTextInputHandler = new widget::TextInputHandler(this);
746 void nsWindow::Destroy() {
747 for (uint32_t i = 0; i < mChildren.Length(); ++i) {
748 // why do we still have children?
749 mChildren[i]->SetParent(nullptr);
752 if (mParent) mParent->mChildren.RemoveElement(this);
754 if (mTextInputHandler) {
755 mTextInputHandler->OnDestroyed();
757 mTextInputHandler = nullptr;
759 [mNativeView widgetDestroyed];
761 nsBaseWidget::Destroy();
763 // ReportDestroyEvent();
767 nsBaseWidget::OnDestroy();
770 void nsWindow::Show(bool aState) {
771 if (aState != mVisible) {
772 mNativeView.hidden = aState ? NO : YES;
774 UIView* parentView = mParent
775 ? mParent->mNativeView
776 : nsAppShell::gWindow.rootViewController.view;
777 [parentView bringSubviewToFront:mNativeView];
778 [mNativeView setNeedsDisplay];
784 void nsWindow::Move(double aX, double aY) {
785 if (!mNativeView || (mBounds.x == aX && mBounds.y == aY)) return;
788 // The point we have is in Gecko coordinates (origin top-left). Convert
789 // it to Cocoa ones (origin bottom-left).
793 mNativeView.frame = DevPixelsToUIKitPoints(mBounds, BackingScaleFactor());
795 if (mVisible) [mNativeView setNeedsDisplay];
800 void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
802 BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
803 BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
804 if (!mNativeView || (!isMoving && !isResizing)) return;
811 mBounds.width = aWidth;
812 mBounds.height = aHeight;
815 [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())];
817 if (mVisible && aRepaint) [mNativeView setNeedsDisplay];
819 if (isMoving) ReportMoveEvent();
821 if (isResizing) ReportSizeEvent();
824 void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
825 if (!mNativeView || (mBounds.width == aWidth && mBounds.height == aHeight))
828 mBounds.width = aWidth;
829 mBounds.height = aHeight;
831 [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())];
833 if (mVisible && aRepaint) [mNativeView setNeedsDisplay];
838 void nsWindow::SetSizeMode(nsSizeMode aMode) {
839 if (aMode == static_cast<int32_t>(mSizeMode)) {
843 mSizeMode = static_cast<nsSizeMode>(aMode);
844 if (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen) {
845 // Resize to fill screen
846 nsBaseWidget::InfallibleMakeFullScreen(true);
848 ReportSizeModeEvent(aMode);
851 void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) {
852 if (!mNativeView || !mVisible) return;
854 [mNativeView setNeedsLayout];
855 [mNativeView setNeedsDisplayInRect:DevPixelsToUIKitPoints(
856 mBounds, BackingScaleFactor())];
859 void nsWindow::SetFocus(Raise, mozilla::dom::CallerType) {
860 [[mNativeView window] makeKeyWindow];
861 [mNativeView becomeFirstResponder];
864 void nsWindow::WillPaintWindow() {
865 if (mWidgetListener) {
866 mWidgetListener->WillPaintWindow(this);
870 bool nsWindow::PaintWindow(LayoutDeviceIntRegion aRegion) {
871 if (!mWidgetListener) return false;
873 bool returnValue = false;
874 returnValue = mWidgetListener->PaintWindow(this, aRegion);
876 if (mWidgetListener) {
877 mWidgetListener->DidPaintWindow();
883 void nsWindow::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); }
885 void nsWindow::ReportSizeModeEvent(nsSizeMode aMode) {
886 if (mWidgetListener) {
890 case nsSizeMode_Maximized:
891 theMode = nsSizeMode_Maximized;
893 case nsSizeMode_Fullscreen:
894 theMode = nsSizeMode_Fullscreen;
899 mWidgetListener->SizeModeChanged(theMode);
903 void nsWindow::ReportSizeEvent() {
904 LayoutDeviceIntRect innerBounds = GetClientBounds();
906 if (mWidgetListener) {
907 mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height);
910 if (mAttachedWidgetListener) {
911 mAttachedWidgetListener->WindowResized(this, innerBounds.width,
916 LayoutDeviceIntRect nsWindow::GetScreenBounds() {
917 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
920 LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
921 LayoutDeviceIntPoint offset(0, 0);
923 offset = mParent->WidgetToScreenOffset();
926 CGPoint temp = [mNativeView convertPoint:temp toView:nil];
928 if (!mParent && nsAppShell::gWindow) {
929 // convert to screen coords
930 temp = [nsAppShell::gWindow convertPoint:temp toWindow:nil];
933 offset.x += static_cast<int32_t>(temp.x);
934 offset.y += static_cast<int32_t>(temp.y);
939 nsresult nsWindow::DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
940 nsEventStatus& aStatus) {
941 aStatus = nsEventStatus_eIgnore;
942 nsCOMPtr<nsIWidget> kungFuDeathGrip(aEvent->mWidget);
943 mozilla::Unused << kungFuDeathGrip; // Not used within this function
945 if (mAttachedWidgetListener) {
946 aStatus = mAttachedWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
947 } else if (mWidgetListener) {
948 aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
954 void nsWindow::SetInputContext(const InputContext& aContext,
955 const InputContextAction& aAction) {
956 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
958 const bool changingEnabledState =
959 aContext.IsInputAttributeChanged(mInputContext);
961 mInputContext = aContext;
963 if (IsVirtualKeyboardDisabled()) {
964 [mNativeView resignFirstResponder];
968 [mNativeView becomeFirstResponder];
970 if (aAction.UserMightRequestOpenVKB() || changingEnabledState) {
972 // It is unnecessary to call reloadInputViews with changingEnabledState if
973 // virtual keyboard is disappeared.
974 [mNativeView reloadInputViews];
977 NS_OBJC_END_TRY_IGNORE_BLOCK;
980 widget::InputContext nsWindow::GetInputContext() {
981 if (!mTextInputHandler) {
982 InputContext context;
983 context.mIMEState.mEnabled = IMEEnabled::Disabled;
984 context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
987 return mInputContext;
990 widget::TextEventDispatcherListener*
991 nsWindow::GetNativeTextEventDispatcherListener() {
992 return mTextInputHandler;
995 bool nsWindow::IsVirtualKeyboardDisabled() const {
996 return mInputContext.mIMEState.mEnabled == IMEEnabled::Disabled ||
997 mInputContext.mHTMLInputMode.EqualsLiteral("none");
1000 void nsWindow::SetBackgroundColor(const nscolor& aColor) {
1001 mNativeView.backgroundColor = [UIColor colorWithRed:NS_GET_R(aColor)
1002 green:NS_GET_G(aColor)
1003 blue:NS_GET_B(aColor)
1004 alpha:NS_GET_A(aColor)];
1007 void* nsWindow::GetNativeData(uint32_t aDataType) {
1008 void* retVal = nullptr;
1010 switch (aDataType) {
1011 case NS_NATIVE_WIDGET:
1012 retVal = (void*)mNativeView;
1015 case NS_NATIVE_WINDOW:
1016 retVal = [mNativeView window];
1019 case NS_NATIVE_GRAPHIC:
1020 NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a UIKit child view!");
1023 case NS_NATIVE_OFFSETX:
1027 case NS_NATIVE_OFFSETY:
1031 case NS_RAW_NATIVE_IME_CONTEXT:
1032 retVal = GetPseudoIMEContext();
1036 retVal = NS_ONLY_ONE_NATIVE_IME_CONTEXT;
1043 CGFloat nsWindow::BackingScaleFactor() {
1045 return [mNativeView contentScaleFactor];
1047 return [UIScreen mainScreen].scale;
1050 int32_t nsWindow::RoundsWidgetCoordinatesTo() {
1051 if (BackingScaleFactor() == 2.0) {
1057 already_AddRefed<nsIWidget> nsIWidget::CreateTopLevelWindow() {
1058 nsCOMPtr<nsIWidget> window = new nsWindow();
1059 return window.forget();
1062 already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
1063 nsCOMPtr<nsIWidget> window = new nsWindow();
1064 return window.forget();