Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / uikit / nsWindow.mm
blob51b317ee6158c752dc5a54e9de1802a9e460be96
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>
18 #include <algorithm>
20 #include "nsWindow.h"
21 #include "nsAppShell.h"
22 #ifdef ACCESSIBILITY
23 #  include "nsAccessibilityService.h"
24 #  include "mozilla/a11y/LocalAccessible.h"
25 #endif
27 #include "nsWidgetsCID.h"
28 #include "nsGfxCIID.h"
30 #include "gfxPlatform.h"
31 #include "gfxQuartzSurface.h"
32 #include "gfxUtils.h"
33 #include "gfxImageSurface.h"
34 #include "gfxContext.h"
35 #include "nsObjCExceptions.h"
36 #include "nsRegion.h"
37 #include "nsTArray.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"
47 #ifdef ACCESSIBILITY
48 #  include "mozilla/a11y/MUIRootAccessibleProtocol.h"
49 #endif
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); \
59   fprintf(stderr, "\n")
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 {
77  public:
78   explicit nsAutoRetainUIKitObject(id anObject) { mObject = [anObject retain]; }
79   ~nsAutoRetainUIKitObject() { [mObject release]; }
81  private:
82   id mObject;  // [STRONG]
85 #ifdef ACCESSIBILITY
86 @interface ChildView : UIView <UIKeyInput, MUIRootAccessibleProtocol> {
87 #else
88 @interface ChildView : UIView <UIKeyInput> {
89 #endif
90  @public
91   nsWindow* mGeckoChild;  // weak ref
92   BOOL mWaitingForPaint;
93   NSMapTable<UITouch*, NSNumber*>* mTouches;
94   int mNextTouchID;
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;
121 #ifdef ACCESSIBILITY
122 // MUIRootAccessible
123 - (BOOL)hasRepresentedView;
124 - (id)representedView;
126 // MUIAccessible
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;
137 #endif
139 @end
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;
150   }
151   ALOG("[ChildView[%p] initWithFrame:] (mGeckoChild = %p)", (void*)self,
152        (void*)mGeckoChild);
153   self.opaque = YES;
154   self.alpha = 1.0;
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];
163   mNextTouchID = 0;
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]
169       addObserver:self
170          selector:@selector(activateWindow:)
171              name:UIWindowDidBecomeKeyNotification
172            object:nil];
173   [[NSNotificationCenter defaultCenter]
174       addObserver:self
175          selector:@selector(deactivateWindow:)
176              name:UIWindowDidResignKeyNotification
177            object:nil];
179   return self;
182 - (void)widgetDestroyed {
183   mGeckoChild = nullptr;
184   [mTouches release];
187 - (void)delayedTearDown {
188   [self removeFromSuperview];
189   [self release];
192 - (void)activateWindow:(NSNotification*)notification {
193   ALOG("[[ChildView[%p] activateWindow]", (void*)self);
195   if (!mGeckoChild) {
196     return;
197   }
199   if (nsIWidgetListener* listener = mGeckoChild->GetWidgetListener()) {
200     listener->WindowActivated();
201   }
204 - (void)deactivateWindow:(NSNotification*)notification {
205   ALOG("[[ChildView[%p] deactivateWindow]", (void*)self);
207   if (!mGeckoChild) {
208     return;
209   }
211   if (nsIWidgetListener* listener = mGeckoChild->GetWidgetListener()) {
212     listener->WindowDeactivated();
213   }
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];
239   }
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];
256     if (value == nil) {
257       // This shouldn't happen.
258       NS_ASSERTION(false, "Got a touch that we didn't know about");
259       continue;
260     }
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);
265   }
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];
275     mNextTouchID++;
276   }
277   [self sendTouchEvent:eTouchStart
278                touches:[event allTouches]
279                 widget:mGeckoChild];
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];
287   }
288   if (mTouches.count == 0) {
289     mNextTouchID = 0;
290   }
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];
300   }
301   if (mTouches.count == 0) {
302     mNextTouchID = 0;
303   }
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]
312                 widget:mGeckoChild];
315 - (BOOL)canBecomeFirstResponder {
316   if (!mGeckoChild) {
317     return NO;
318   }
320   if (mGeckoChild->IsVirtualKeyboardDisabled()) {
321     return NO;
322   }
323   return YES;
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)
336                  withObject:nil
337                  afterDelay:0
338                     inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
339     }
340   }
343 - (BOOL)isUsingMainThreadOpenGL {
344   if (!mGeckoChild || ![self window]) return NO;
346   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];
368   }
371 // The display system has told us that a portion of our view is dirty. Tell
372 // gecko to paint it
373 - (void)drawRect:(CGRect)aRect {
374   CGContextRef cgContext = UIGraphicsGetCurrentContext();
375   [self drawRect:aRect inContext:cgContext];
378 - (void)drawRect:(CGRect)aRect inContext:(CGContextRef)aContext {
379 #ifdef DEBUG_UPDATE
380   LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds();
382   fprintf(stderr,
383           "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n  gecko bounds: [%d %d "
384           "%d %d]\n",
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);
392 #endif
394   if (true) {
395     // For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is
396     // directly called from a delayed perform callback - without going through
397     // drawRect.
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];
403     return;
404   }
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
434     // debugging.
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;
441       return;
442     }
443     targetContext = gfxContext::CreateOrNull(dt);
444   } else {
445     MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backend");
446   }
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));
454   }
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);
472   }
474 #ifdef DEBUG_UPDATE
475   fprintf(stderr, "---- update done ----\n");
477 #  if 0
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,
482                             0.5);
483 #  endif
484   CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8);
485   CGContextSetLineWidth(aContext, 4.0);
486   CGContextStrokeRect(aContext, aRect);
487 #endif
490 // UIKeyInput
492 - (void)insertText:(NSString*)text {
493   if (!mGeckoChild || mGeckoChild->Destroyed()) {
494     return;
495   }
496   widget::TextInputHandler* textInputHandler =
497       mGeckoChild->GetTextInputHandler();
498   if (!textInputHandler) {
499     return;
500   }
501   textInputHandler->InsertText(text);
504 - (void)deleteBackward {
505   if (!mGeckoChild || mGeckoChild->Destroyed()) {
506     return;
507   }
508   widget::TextInputHandler* textInputHandler =
509       mGeckoChild->GetTextInputHandler();
510   if (!textInputHandler) {
511     return;
512   }
513   textInputHandler->HandleCommand(Command::DeleteCharBackward);
516 - (BOOL)hasText {
517   if (!mGeckoChild || mGeckoChild->Destroyed()) {
518     return NO;
519   }
520   widget::InputContext context = mGeckoChild->GetInputContext();
521   if (context.mIMEState.mEnabled == mozilla::widget::IMEEnabled::Disabled) {
522     return NO;
523   }
524   return YES;
527 // UITextInputTraits
529 - (UIKeyboardType)keyboardType {
530   if (!mGeckoChild || mGeckoChild->Destroyed()) {
531     return UIKeyboardTypeDefault;
532   }
533   return UIKitUtils::GetUIKeyboardType(mGeckoChild->GetInputContext());
536 - (UIReturnKeyType)returnKeyType {
537   if (!mGeckoChild || mGeckoChild->Destroyed()) {
538     return UIReturnKeyDefault;
539   }
540   return UIKitUtils::GetUIReturnKeyType(mGeckoChild->GetInputContext());
543 - (UITextAutocapitalizationType)autocapitalizationType {
544   if (!mGeckoChild || mGeckoChild->Destroyed()) {
545     return UITextAutocapitalizationTypeNone;
546   }
547   return UIKitUtils::GetUITextAutocapitalizationType(
548       mGeckoChild->GetInputContext());
551 - (BOOL)isSecureTextEntry {
552   if (!mGeckoChild || mGeckoChild->Destroyed()) {
553     return NO;
554   }
555   if (mGeckoChild->GetInputContext().IsPasswordEditor()) {
556     return YES;
557   }
558   return NO;
561 #ifdef ACCESSIBILITY
562 // MUIRootAccessible
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 {
580   return YES;
583 - (id)representedView {
584   return self;
587 - (BOOL)isAccessibilityElement {
588   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
589     return [super isAccessibilityElement];
590   }
592   return [[self accessible] isAccessibilityElement];
595 - (NSString*)accessibilityLabel {
596   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
597     return [super accessibilityLabel];
598   }
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];
612   }
614   return [[self accessible] accessibilityValue];
617 - (uint64_t)accessibilityTraits {
618   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
619     return [super accessibilityTraits];
620   }
622   return [[self accessible] accessibilityTraits];
625 - (NSInteger)accessibilityElementCount {
626   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
627     return [super accessibilityElementCount];
628   }
630   return [[self accessible] accessibilityElementCount];
633 - (nullable id)accessibilityElementAtIndex:(NSInteger)index {
634   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
635     return [super accessibilityElementAtIndex:index];
636   }
638   return [[self accessible] accessibilityElementAtIndex:index];
641 - (NSInteger)indexOfAccessibilityElement:(id)element {
642   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
643     return [super indexOfAccessibilityElement:element];
644   }
646   return [[self accessible] indexOfAccessibilityElement:element];
649 - (NSArray* _Nullable)accessibilityElements {
650   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
651     return [super accessibilityElements];
652   }
654   return [[self accessible] accessibilityElements];
657 - (UIAccessibilityContainerType)accessibilityContainerType {
658   if (!mozilla::a11y::ShouldA11yBeEnabled()) {
659     return [super accessibilityContainerType];
660   }
662   return [[self accessible] accessibilityContainerType];
664 #endif
666 @end
668 nsWindow::nsWindow()
669     : mNativeView(nullptr),
670       mVisible(false),
671       mSizeMode(nsSizeMode_Normal),
672       mParent(nullptr) {}
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)
683                                 withObject:nil
684                              waitUntilDone:false];
685   mNativeView = nil;
688 bool nsWindow::IsTopLevel() {
689   return mWindowType == WindowType::TopLevel ||
690          mWindowType == WindowType::Dialog ||
691          mWindowType == WindowType::Invisible;
695 // nsIWidget
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;
709   mBounds = aRect;
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())
725          geckoChild:this];
726   mNativeView.hidden = YES;
728   if (parent) {
729     parent->mChildren.AppendElement(this);
730     mParent = parent;
731   }
733   if (nativeParent) {
734     [nativeParent addSubview:mNativeView];
735   } else if (nsAppShell::gWindow) {
736     [nsAppShell::gWindow.rootViewController.view addSubview:mNativeView];
737   } else {
738     [nsAppShell::gTopLevelViews addObject:mNativeView];
739   }
741   mTextInputHandler = new widget::TextInputHandler(this);
743   return NS_OK;
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);
750   }
752   if (mParent) mParent->mChildren.RemoveElement(this);
754   if (mTextInputHandler) {
755     mTextInputHandler->OnDestroyed();
756   }
757   mTextInputHandler = nullptr;
759   [mNativeView widgetDestroyed];
761   nsBaseWidget::Destroy();
763   // ReportDestroyEvent();
765   TearDownView();
767   nsBaseWidget::OnDestroy();
770 void nsWindow::Show(bool aState) {
771   if (aState != mVisible) {
772     mNativeView.hidden = aState ? NO : YES;
773     if (aState) {
774       UIView* parentView = mParent
775                                ? mParent->mNativeView
776                                : nsAppShell::gWindow.rootViewController.view;
777       [parentView bringSubviewToFront:mNativeView];
778       [mNativeView setNeedsDisplay];
779     }
780     mVisible = aState;
781   }
784 void nsWindow::Move(double aX, double aY) {
785   if (!mNativeView || (mBounds.x == aX && mBounds.y == aY)) return;
787   // XXX: handle this
788   // The point we have is in Gecko coordinates (origin top-left). Convert
789   // it to Cocoa ones (origin bottom-left).
790   mBounds.x = aX;
791   mBounds.y = aY;
793   mNativeView.frame = DevPixelsToUIKitPoints(mBounds, BackingScaleFactor());
795   if (mVisible) [mNativeView setNeedsDisplay];
797   ReportMoveEvent();
800 void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
801                       bool aRepaint) {
802   BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
803   BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
804   if (!mNativeView || (!isMoving && !isResizing)) return;
806   if (isMoving) {
807     mBounds.x = aX;
808     mBounds.y = aY;
809   }
810   if (isResizing) {
811     mBounds.width = aWidth;
812     mBounds.height = aHeight;
813   }
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))
826     return;
828   mBounds.width = aWidth;
829   mBounds.height = aHeight;
831   [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())];
833   if (mVisible && aRepaint) [mNativeView setNeedsDisplay];
835   ReportSizeEvent();
838 void nsWindow::SetSizeMode(nsSizeMode aMode) {
839   if (aMode == static_cast<int32_t>(mSizeMode)) {
840     return;
841   }
843   mSizeMode = static_cast<nsSizeMode>(aMode);
844   if (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen) {
845     // Resize to fill screen
846     nsBaseWidget::InfallibleMakeFullScreen(true);
847   }
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);
867   }
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();
878   }
880   return returnValue;
883 void nsWindow::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); }
885 void nsWindow::ReportSizeModeEvent(nsSizeMode aMode) {
886   if (mWidgetListener) {
887     // This is terrible.
888     nsSizeMode theMode;
889     switch (aMode) {
890       case nsSizeMode_Maximized:
891         theMode = nsSizeMode_Maximized;
892         break;
893       case nsSizeMode_Fullscreen:
894         theMode = nsSizeMode_Fullscreen;
895         break;
896       default:
897         return;
898     }
899     mWidgetListener->SizeModeChanged(theMode);
900   }
903 void nsWindow::ReportSizeEvent() {
904   LayoutDeviceIntRect innerBounds = GetClientBounds();
906   if (mWidgetListener) {
907     mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height);
908   }
910   if (mAttachedWidgetListener) {
911     mAttachedWidgetListener->WindowResized(this, innerBounds.width,
912                                            innerBounds.height);
913   }
916 LayoutDeviceIntRect nsWindow::GetScreenBounds() {
917   return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
920 LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
921   LayoutDeviceIntPoint offset(0, 0);
922   if (mParent) {
923     offset = mParent->WidgetToScreenOffset();
924   }
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];
931   }
933   offset.x += static_cast<int32_t>(temp.x);
934   offset.y += static_cast<int32_t>(temp.y);
936   return offset;
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);
949   }
951   return NS_OK;
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];
965     return;
966   }
968   [mNativeView becomeFirstResponder];
970   if (aAction.UserMightRequestOpenVKB() || changingEnabledState) {
971     // TODO(m_kato):
972     // It is unnecessary to call reloadInputViews with changingEnabledState if
973     // virtual keyboard is disappeared.
974     [mNativeView reloadInputViews];
975   }
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;
985     return context;
986   }
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;
1013       break;
1015     case NS_NATIVE_WINDOW:
1016       retVal = [mNativeView window];
1017       break;
1019     case NS_NATIVE_GRAPHIC:
1020       NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a UIKit child view!");
1021       break;
1023     case NS_NATIVE_OFFSETX:
1024       retVal = 0;
1025       break;
1027     case NS_NATIVE_OFFSETY:
1028       retVal = 0;
1029       break;
1031     case NS_RAW_NATIVE_IME_CONTEXT:
1032       retVal = GetPseudoIMEContext();
1033       if (retVal) {
1034         break;
1035       }
1036       retVal = NS_ONLY_ONE_NATIVE_IME_CONTEXT;
1037       break;
1038   }
1040   return retVal;
1043 CGFloat nsWindow::BackingScaleFactor() {
1044   if (mNativeView) {
1045     return [mNativeView contentScaleFactor];
1046   }
1047   return [UIScreen mainScreen].scale;
1050 int32_t nsWindow::RoundsWidgetCoordinatesTo() {
1051   if (BackingScaleFactor() == 2.0) {
1052     return 2;
1053   }
1054   return 1;
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();