Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / cocoa / nsChildView.mm
blobe8757f47bf7cbacc66522ea3987f280237ad81f5
1 /* -*- Mode: objc; tab-width: 2; 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 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Logging.h"
9 #include "mozilla/Unused.h"
11 #include <unistd.h>
12 #include <math.h>
14 #include "nsChildView.h"
15 #include "nsCocoaWindow.h"
17 #include "mozilla/Maybe.h"
18 #include "mozilla/MiscEvents.h"
19 #include "mozilla/MouseEvents.h"
20 #include "mozilla/NativeKeyBindingsType.h"
21 #include "mozilla/PresShell.h"
22 #include "mozilla/SwipeTracker.h"
23 #include "mozilla/TextEventDispatcher.h"
24 #include "mozilla/TextEvents.h"
25 #include "mozilla/TouchEvents.h"
26 #include "mozilla/WheelHandlingHelper.h"  // for WheelDeltaAdjustmentStrategy
27 #include "mozilla/WritingModes.h"
28 #include "mozilla/dom/DataTransfer.h"
29 #include "mozilla/dom/MouseEventBinding.h"
30 #include "mozilla/dom/SimpleGestureEventBinding.h"
31 #include "mozilla/dom/WheelEventBinding.h"
32 #include "mozilla/layers/CompositorBridgeChild.h"
34 #include "nsArrayUtils.h"
35 #include "nsExceptionHandler.h"
36 #include "nsObjCExceptions.h"
37 #include "nsCOMPtr.h"
38 #include "nsThreadUtils.h"
39 #include "nsToolkit.h"
40 #include "nsCRT.h"
42 #include "nsFontMetrics.h"
43 #include "nsIRollupListener.h"
44 #include "nsViewManager.h"
45 #include "nsIFile.h"
46 #include "nsILocalFileMac.h"
47 #include "nsGfxCIID.h"
48 #include "nsStyleConsts.h"
49 #include "nsIWidgetListener.h"
50 #include "nsIScreen.h"
52 #include "nsDragService.h"
53 #include "nsClipboard.h"
54 #include "nsCursorManager.h"
55 #include "nsWindowMap.h"
56 #include "nsCocoaUtils.h"
57 #include "nsMenuUtilsX.h"
58 #include "nsMenuBarX.h"
59 #include "NativeKeyBindings.h"
60 #include "MacThemeGeometryType.h"
62 #include "gfxContext.h"
63 #include "gfxQuartzSurface.h"
64 #include "gfxUtils.h"
65 #include "nsRegion.h"
66 #include "GfxTexturesReporter.h"
67 #include "GLTextureImage.h"
68 #include "GLContextProvider.h"
69 #include "GLContextCGL.h"
70 #include "OGLShaderProgram.h"
71 #include "ScopedGLHelpers.h"
72 #include "HeapCopyOfStackArray.h"
73 #include "mozilla/layers/IAPZCTreeManager.h"
74 #include "mozilla/layers/APZInputBridge.h"
75 #include "mozilla/layers/APZThreadUtils.h"
76 #include "mozilla/layers/CompositorOGL.h"
77 #include "mozilla/layers/CompositorBridgeParent.h"
78 #include "mozilla/layers/InputAPZContext.h"
79 #include "mozilla/layers/IpcResourceUpdateQueue.h"
80 #include "mozilla/layers/NativeLayerCA.h"
81 #include "mozilla/layers/WebRenderBridgeChild.h"
82 #include "mozilla/layers/WebRenderLayerManager.h"
83 #include "mozilla/webrender/WebRenderAPI.h"
84 #include "mozilla/widget/CompositorWidget.h"
85 #include "mozilla/widget/Screen.h"
86 #include "gfxUtils.h"
87 #include "mozilla/gfx/2D.h"
88 #include "mozilla/gfx/BorrowedContext.h"
89 #ifdef ACCESSIBILITY
90 #  include "nsAccessibilityService.h"
91 #  include "mozilla/a11y/Platform.h"
92 #endif
94 #include "mozilla/Preferences.h"
95 #include "mozilla/StaticPrefs_apz.h"
96 #include "mozilla/StaticPrefs_browser.h"
97 #include "mozilla/StaticPrefs_dom.h"
98 #include "mozilla/StaticPrefs_general.h"
99 #include "mozilla/StaticPrefs_gfx.h"
100 #include "mozilla/StaticPrefs_ui.h"
102 #include <dlfcn.h>
104 #include <ApplicationServices/ApplicationServices.h>
106 #include "GeckoProfiler.h"
108 #include "mozilla/layers/ChromeProcessController.h"
109 #include "nsLayoutUtils.h"
110 #include "InputData.h"
111 #include "VibrancyManager.h"
112 #include "nsNativeThemeCocoa.h"
113 #include "nsIDOMWindowUtils.h"
114 #include "Units.h"
115 #include "UnitTransforms.h"
116 #include "mozilla/UniquePtrExtensions.h"
117 #include "CustomCocoaEvents.h"
118 #include "NativeMenuSupport.h"
120 using namespace mozilla;
121 using namespace mozilla::layers;
122 using namespace mozilla::gl;
123 using namespace mozilla::widget;
125 using mozilla::gfx::Matrix4x4;
127 #undef DEBUG_UPDATE
128 #undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated
130 // Don't put more than this many rects in the dirty region, just fluff
131 // out to the bounding-box if there are more
132 #define MAX_RECTS_IN_REGION 100
134 LazyLogModule sCocoaLog("nsCocoaWidgets");
136 extern "C" {
137 CG_EXTERN void CGContextResetCTM(CGContextRef);
138 CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
139 CG_EXTERN void CGContextResetClip(CGContextRef);
141 typedef CFTypeRef CGSRegionObj;
142 CGError CGSNewRegionWithRect(const CGRect* rect, CGSRegionObj* outRegion);
143 CGError CGSNewRegionWithRectList(const CGRect* rects, int rectCount,
144                                  CGSRegionObj* outRegion);
147 // defined in nsMenuBarX.mm
148 extern NSMenu* sApplicationMenu;  // Application menu shared by all menubars
150 extern nsIArray* gDraggedTransferables;
152 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
153 NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil;
154 NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
155 MOZ_RUNINIT NSPoint ChildViewMouseTracker::sLastScrollEventScreenLocation =
156     NSZeroPoint;
158 #ifdef INVALIDATE_DEBUGGING
159 static void blinkRect(Rect* r);
160 static void blinkRgn(RgnHandle rgn);
161 #endif
163 bool gUserCancelledDrag = false;
165 uint32_t nsChildView::sLastInputEventCount = 0;
167 static bool sIsTabletPointerActivated = false;
169 static uint32_t sUniqueKeyEventId = 0;
171 // The view that will do our drawing or host our NSOpenGLContext or Core
172 // Animation layer.
173 @interface PixelHostingView : NSView {
176 @end
178 @interface ChildView (Private)
180 // sets up our view, attaching it to its owning gecko view
181 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
183 // set up a gecko mouse event based on a cocoa mouse event
184 - (void)convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
185                        toGeckoEvent:(WidgetWheelEvent*)outWheelEvent;
186 - (void)convertCocoaMouseEvent:(NSEvent*)aMouseEvent
187                   toGeckoEvent:(WidgetInputEvent*)outGeckoEvent;
188 - (void)convertCocoaTabletPointerEvent:(NSEvent*)aMouseEvent
189                           toGeckoEvent:(WidgetMouseEvent*)outGeckoEvent;
190 - (NSMenu*)contextMenu;
192 - (void)markLayerForDisplay;
193 - (CALayer*)rootCALayer;
194 - (void)updateRootCALayer;
196 #ifdef ACCESSIBILITY
197 - (id<mozAccessible>)accessible;
198 #endif
200 - (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint;
201 - (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint;
203 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
204 - (void)updateWindowDraggableState;
206 - (bool)beginOrEndGestureForEventPhase:(NSEvent*)aEvent;
208 @end
210 #pragma mark -
212 // Flips a screen coordinate from a point in the cocoa coordinate system
213 // (bottom-left rect) to a point that is a "flipped" cocoa coordinate system
214 // (starts in the top-left).
215 static inline void FlipCocoaScreenCoordinate(NSPoint& inPoint) {
216   inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
219 #pragma mark -
221 nsChildView::nsChildView()
222     : mView(nullptr),
223       mParentView(nil),
224       mCompositingLock("ChildViewCompositing"),
225       mBackingScaleFactor(0.0),
226       mVisible(false),
227       mSizeMode(nsSizeMode_Normal),
228       mDrawing(false),
229       mIsDispatchPaint(false) {}
231 nsChildView::~nsChildView() {
232   RemoveAllChildren();
234   NS_WARNING_ASSERTION(
235       mOnDestroyCalled,
236       "nsChildView object destroyed without calling Destroy()");
238   if (mContentLayer) {
239     mNativeLayerRoot->RemoveLayer(mContentLayer);  // safe if already removed
240   }
242   DestroyCompositor();
244   // An nsChildView object that was in use can be destroyed without Destroy()
245   // ever being called on it.  So we also need to do a quick, safe cleanup
246   // here (it's too late to just call Destroy(), which can cause crashes).
247   // It's particularly important to make sure widgetDestroyed is called on our
248   // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
249   // mGeckoChild are used throughout the ChildView class to tell if it's safe
250   // to use a ChildView object.
251   [mView widgetDestroyed];  // Safe if mView is nil.
252   ClearParent();
253   TearDownView();  // Safe if called twice.
256 nsresult nsChildView::Create(nsIWidget* aParent,
257                              const LayoutDeviceIntRect& aRect,
258                              widget::InitData* aInitData) {
259   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
261   // Because the hidden window is created outside of an event loop,
262   // we need to provide an autorelease pool to avoid leaking cocoa objects
263   // (see bug 559075).
264   nsAutoreleasePool localPool;
266   mBounds = aRect;
268   // Ensure that the toolkit is created.
269   nsToolkit::GetToolkit();
271   BaseCreate(aParent, aInitData);
273   mParentView =
274       mParent ? (NSView*)mParent->GetNativeData(NS_NATIVE_WIDGET) : nullptr;
276   // create our parallel NSView and hook it up to our parent. Recall
277   // that NS_NATIVE_WIDGET is the NSView.
278   CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mParentView);
279   NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(mBounds, scaleFactor);
280   mView = [[ChildView alloc] initWithFrame:r geckoChild:this];
282   mNativeLayerRoot = NativeLayerRootCA::CreateForCALayer([mView rootCALayer]);
283   mNativeLayerRoot->SetBackingScale(scaleFactor);
285   // If this view was created in a Gecko view hierarchy, the initial state
286   // is hidden.  If the view is attached only to a native NSView but has
287   // no Gecko parent (as in embedding), the initial state is visible.
288   if (mParent) {
289     [mView setHidden:YES];
290   } else {
291     mVisible = true;
292   }
294   // Hook it up in the NSView hierarchy.
295   if (mParentView) {
296     [mParentView addSubview:mView];
297   }
299   // if this is a ChildView, make sure that our per-window data
300   // is set up
301   if ([mView isKindOfClass:[ChildView class]]) {
302     [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]];
303   }
305   NS_ASSERTION(!mTextInputHandler, "mTextInputHandler has already existed");
306   mTextInputHandler = new TextInputHandler(this, mView);
308   return NS_OK;
310   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
313 void nsChildView::TearDownView() {
314   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
316   if (!mView) return;
318   NSWindow* win = [mView window];
319   NSResponder* responder = [win firstResponder];
321   // We're being unhooked from the view hierarchy, don't leave our view
322   // or a child view as the window first responder.
323   if (responder && [responder isKindOfClass:[NSView class]] &&
324       [(NSView*)responder isDescendantOf:mView]) {
325     [win makeFirstResponder:[mView superview]];
326   }
328   // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
329   // win has retained mView, and will detach it from the view hierarchy and
330   // release it when necessary (when win is itself destroyed (in a call to
331   // [win dealloc])).  So all we need to do here is call [mView release] (to
332   // match the call to [mView retain] in nsChildView::StandardCreate()).
333   // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
334   // mView to be released again and dealloced, while remaining win's
335   // contentView.  So if we do that here, win will (for a short while) have
336   // an invalid contentView (for the consequences see bmo bugs 381087 and
337   // 374260).
338   if ([mView isEqual:[win contentView]]) {
339     [mView release];
340   } else {
341     // Stop NSView hierarchy being changed during [ChildView drawRect:]
342     [mView performSelectorOnMainThread:@selector(delayedTearDown)
343                             withObject:nil
344                          waitUntilDone:false];
345   }
346   mView = nil;
348   NS_OBJC_END_TRY_IGNORE_BLOCK;
351 nsCocoaWindow* nsChildView::GetAppWindowWidget() const {
352   id windowDelegate = [[mView window] delegate];
353   if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
354     return [(WindowDelegate*)windowDelegate geckoWidget];
355   }
356   return nullptr;
359 void nsChildView::Destroy() {
360   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
362   if (mOnDestroyCalled) {
363     return;
364   }
365   mOnDestroyCalled = true;
367   // Stuff below may delete the last ref to this
368   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
370   {
371     // Make sure that no composition is in progress while disconnecting
372     // ourselves from the view.
373     MutexAutoLock lock(mCompositingLock);
375     [mView widgetDestroyed];
376   }
378   nsBaseWidget::Destroy();
380   NotifyWindowDestroyed();
382   TearDownView();
384   nsBaseWidget::OnDestroy();
386   NS_OBJC_END_TRY_IGNORE_BLOCK;
389 #pragma mark -
391 #if 0
392 static void PrintViewHierarchy(NSView *view)
394   while (view) {
395     NSLog(@"  view is %x, frame %@", view, NSStringFromRect([view frame]));
396     view = [view superview];
397   }
399 #endif
401 // Return native data according to aDataType
402 void* nsChildView::GetNativeData(uint32_t aDataType) {
403   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
405   void* retVal = nullptr;
407   switch (aDataType) {
408     case NS_NATIVE_WIDGET:
409       retVal = (void*)mView;
410       break;
412     case NS_NATIVE_WINDOW:
413       retVal = [mView window];
414       break;
416     case NS_NATIVE_GRAPHIC:
417       NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
418       retVal = nullptr;
419       break;
421     case NS_NATIVE_OFFSETX:
422       retVal = 0;
423       break;
425     case NS_NATIVE_OFFSETY:
426       retVal = 0;
427       break;
429     case NS_RAW_NATIVE_IME_CONTEXT:
430       retVal = GetPseudoIMEContext();
431       if (retVal) {
432         break;
433       }
434       retVal = [mView inputContext];
435       // If input context isn't available on this widget, we should set |this|
436       // instead of nullptr since if this returns nullptr, IMEStateManager
437       // cannot manage composition with TextComposition instance.  Although,
438       // this case shouldn't occur.
439       if (NS_WARN_IF(!retVal)) {
440         retVal = this;
441       }
442       break;
444     case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID: {
445       NSWindow* win = [mView window];
446       if (win) {
447         retVal = (void*)[win windowNumber];
448       }
449       break;
450     }
451   }
453   return retVal;
455   NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
458 #pragma mark -
460 void nsChildView::SuppressAnimation(bool aSuppress) {
461   if (nsCocoaWindow* widget = GetAppWindowWidget()) {
462     widget->SuppressAnimation(aSuppress);
463   }
466 bool nsChildView::IsVisible() const {
467   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
469   if (!mVisible) {
470     return false;
471   }
473   nsCocoaWindow* widget = GetAppWindowWidget();
474   if (NS_WARN_IF(!widget) || !widget->IsVisible()) {
475     return false;
476   }
478   // mVisible does not accurately reflect the state of a hidden tabbed view
479   // so verify that the view has a window as well
480   // then check native widget hierarchy visibility
481   return ([mView window] != nil) && !NSIsEmptyRect([mView visibleRect]);
483   NS_OBJC_END_TRY_BLOCK_RETURN(false);
486 // Some NSView methods (e.g. setFrame and setHidden) invalidate the view's
487 // bounds in our window. However, we don't want these invalidations because
488 // they are unnecessary and because they actually slow us down since we
489 // block on the compositor inside drawRect.
490 // When we actually need something invalidated, there will be an explicit call
491 // to Invalidate from Gecko, so turning these automatic invalidations off
492 // won't hurt us in the non-OMTC case.
493 // The invalidations inside these NSView methods happen via a call to the
494 // private method -[NSWindow _setNeedsDisplayInRect:]. Our BaseWindow
495 // implementation of that method is augmented to let us ignore those calls
496 // using -[BaseWindow disable/enableSetNeedsDisplay].
497 static void ManipulateViewWithoutNeedingDisplay(NSView* aView,
498                                                 void (^aCallback)()) {
499   BaseWindow* win = nil;
500   if ([[aView window] isKindOfClass:[BaseWindow class]]) {
501     win = (BaseWindow*)[aView window];
502   }
503   [win disableSetNeedsDisplay];
504   aCallback();
505   [win enableSetNeedsDisplay];
508 // Hide or show this component
509 void nsChildView::Show(bool aState) {
510   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
512   if (aState != mVisible) {
513     // Provide an autorelease pool because this gets called during startup
514     // on the "hidden window", resulting in cocoa object leakage if there's
515     // no pool in place.
516     nsAutoreleasePool localPool;
518     ManipulateViewWithoutNeedingDisplay(mView, ^{
519       [mView setHidden:!aState];
520     });
522     mVisible = aState;
523   }
525   NS_OBJC_END_TRY_IGNORE_BLOCK;
528 // Change the parent of this widget
529 void nsChildView::DidClearParent(nsIWidget*) {
530   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
532   if (mOnDestroyCalled) {
533     return;
534   }
536   // we hold a ref to mView, so this is safe
537   [mView removeFromSuperview];
539   NS_OBJC_END_TRY_IGNORE_BLOCK;
542 float nsChildView::GetDPI() {
543   float dpi = 96.0;
544   nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
545   if (screen) {
546     screen->GetDpi(&dpi);
547   }
548   return dpi;
551 void nsChildView::Enable(bool aState) {}
553 bool nsChildView::IsEnabled() const { return true; }
555 void nsChildView::SetFocus(Raise, mozilla::dom::CallerType aCallerType) {
556   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
558   NSWindow* window = [mView window];
559   if (window) [window makeFirstResponder:mView];
560   NS_OBJC_END_TRY_IGNORE_BLOCK;
563 // Override to set the cursor on the mac
564 void nsChildView::SetCursor(const Cursor& aCursor) {
565   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
567   if ([mView isDragInProgress]) {
568     return;  // Don't change the cursor during dragging.
569   }
571   nsBaseWidget::SetCursor(aCursor);
573   bool forceUpdate = mUpdateCursor;
574   mUpdateCursor = false;
575   if (mCustomCursorAllowed && NS_SUCCEEDED([[nsCursorManager sharedInstance]
576                                     setCustomCursor:aCursor
577                                   widgetScaleFactor:BackingScaleFactor()
578                                         forceUpdate:forceUpdate])) {
579     return;
580   }
582   [[nsCursorManager sharedInstance] setNonCustomCursor:aCursor];
584   NS_OBJC_END_TRY_IGNORE_BLOCK;
587 #pragma mark -
589 // Get this component dimension
590 LayoutDeviceIntRect nsChildView::GetBounds() {
591   return !mView ? mBounds : CocoaPointsToDevPixels([mView frame]);
594 LayoutDeviceIntRect nsChildView::GetClientBounds() {
595   LayoutDeviceIntRect rect = GetBounds();
596   if (!mParent) {
597     // For top level widgets we want the position on screen, not the position
598     // of this view inside the window.
599     rect.MoveTo(WidgetToScreenOffset());
600   }
601   return rect;
604 LayoutDeviceIntRect nsChildView::GetScreenBounds() {
605   LayoutDeviceIntRect rect = GetBounds();
606   rect.MoveTo(WidgetToScreenOffset());
607   return rect;
610 double nsChildView::GetDefaultScaleInternal() { return BackingScaleFactor(); }
612 CGFloat nsChildView::BackingScaleFactor() const {
613   if (mBackingScaleFactor > 0.0) {
614     return mBackingScaleFactor;
615   }
616   if (!mView) {
617     return 1.0;
618   }
619   mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mView);
620   return mBackingScaleFactor;
623 void nsChildView::BackingScaleFactorChanged() {
624   CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mView);
626   // ignore notification if it hasn't really changed (or maybe we have
627   // disabled HiDPI mode via prefs)
628   if (mBackingScaleFactor == newScale) {
629     return;
630   }
632   SuspendAsyncCATransactions();
633   mBackingScaleFactor = newScale;
634   NSRect frame = mView.frame;
635   mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, newScale);
637   mNativeLayerRoot->SetBackingScale(mBackingScaleFactor);
639   if (mWidgetListener && !mWidgetListener->GetAppWindow()) {
640     if (PresShell* presShell = mWidgetListener->GetPresShell()) {
641       presShell->BackingScaleFactorChanged();
642     }
643   }
646 int32_t nsChildView::RoundsWidgetCoordinatesTo() {
647   if (BackingScaleFactor() == 2.0) {
648     return 2;
649   }
650   return 1;
653 // Move this component, aX and aY are in the parent widget coordinate system
654 void nsChildView::Move(double aX, double aY) {
655   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
657   int32_t x = NSToIntRound(aX);
658   int32_t y = NSToIntRound(aY);
660   if (!mView || (mBounds.x == x && mBounds.y == y)) return;
662   mBounds.x = x;
663   mBounds.y = y;
665   ManipulateViewWithoutNeedingDisplay(mView, ^{
666     [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
667   });
669   ReportMoveEvent();
671   NS_OBJC_END_TRY_IGNORE_BLOCK;
674 void nsChildView::Resize(double aWidth, double aHeight, bool aRepaint) {
675   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
677   int32_t width = NSToIntRound(aWidth);
678   int32_t height = NSToIntRound(aHeight);
680   if (!mView || (mBounds.width == width && mBounds.height == height)) return;
682   SuspendAsyncCATransactions();
683   mBounds.width = width;
684   mBounds.height = height;
686   ManipulateViewWithoutNeedingDisplay(mView, ^{
687     mView.frame = DevPixelsToCocoaPoints(mBounds);
688   });
690   if (mVisible && aRepaint) {
691     mView.pixelHostingView.needsDisplay = YES;
692   }
694   ReportSizeEvent();
696   NS_OBJC_END_TRY_IGNORE_BLOCK;
699 void nsChildView::Resize(double aX, double aY, double aWidth, double aHeight,
700                          bool aRepaint) {
701   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
703   int32_t x = NSToIntRound(aX);
704   int32_t y = NSToIntRound(aY);
705   int32_t width = NSToIntRound(aWidth);
706   int32_t height = NSToIntRound(aHeight);
708   BOOL isMoving = (mBounds.x != x || mBounds.y != y);
709   BOOL isResizing = (mBounds.width != width || mBounds.height != height);
710   if (!mView || (!isMoving && !isResizing)) return;
712   if (isMoving) {
713     mBounds.x = x;
714     mBounds.y = y;
715   }
716   if (isResizing) {
717     SuspendAsyncCATransactions();
718     mBounds.width = width;
719     mBounds.height = height;
721     CALayer* layer = [mView rootCALayer];
722     double scale = BackingScaleFactor();
723     layer.bounds = CGRectMake(0, 0, width / scale, height / scale);
724   }
726   ManipulateViewWithoutNeedingDisplay(mView, ^{
727     mView.frame = DevPixelsToCocoaPoints(mBounds);
728   });
730   if (mVisible && aRepaint) {
731     mView.pixelHostingView.needsDisplay = YES;
732   }
734   if (isMoving) {
735     ReportMoveEvent();
736     if (mOnDestroyCalled) return;
737   }
738   if (isResizing) ReportSizeEvent();
740   NS_OBJC_END_TRY_IGNORE_BLOCK;
743 // The following three methods are primarily an attempt to avoid glitches during
744 // window resizing.
745 // Here's some background on how these glitches come to be:
746 // CoreAnimation transactions are per-thread. They don't nest across threads.
747 // If you submit a transaction on the main thread and a transaction on a
748 // different thread, the two will race to the window server and show up on the
749 // screen in the order that they happen to arrive in at the window server.
750 // When the window size changes, there's another event that needs to be
751 // synchronized with: the window "shape" change. Cocoa has built-in
752 // synchronization mechanics that make sure that *main thread* window paints
753 // during window resizes are synchronized properly with the window shape change.
754 // But no such built-in synchronization exists for CATransactions that are
755 // triggered on a non-main thread. To cope with this, we define a "danger zone"
756 // during which we simply avoid triggering any CATransactions on a non-main
757 // thread (called "async" CATransactions here). This danger zone starts at the
758 // earliest opportunity at which we know about the size change, which is
759 // nsChildView::Resize, and ends at a point at which we know for sure that the
760 // paint has been handled completely, which is when we return to the event loop
761 // after layer display.
762 void nsChildView::SuspendAsyncCATransactions() {
763   if (mUnsuspendAsyncCATransactionsRunnable) {
764     mUnsuspendAsyncCATransactionsRunnable->Cancel();
765     mUnsuspendAsyncCATransactionsRunnable = nullptr;
766   }
768   // Make sure that there actually will be a CATransaction on the main thread
769   // during which we get a chance to schedule unsuspension. Otherwise we might
770   // accidentally stay suspended indefinitely.
771   [mView markLayerForDisplay];
773   // Ensure that whatever we are going to do does sync flushes of the
774   // rendering pipeline, giving us smooth animation.
775   if (mCompositorBridgeChild) {
776     mCompositorBridgeChild->SetForceSyncFlushRendering(true);
777   }
778   mNativeLayerRoot->SuspendOffMainThreadCommits();
781 void nsChildView::MaybeScheduleUnsuspendAsyncCATransactions() {
782   if (mNativeLayerRoot->AreOffMainThreadCommitsSuspended() &&
783       !mUnsuspendAsyncCATransactionsRunnable) {
784     mUnsuspendAsyncCATransactionsRunnable = NewCancelableRunnableMethod(
785         "nsChildView::MaybeScheduleUnsuspendAsyncCATransactions", this,
786         &nsChildView::UnsuspendAsyncCATransactions);
787     NS_DispatchToMainThread(mUnsuspendAsyncCATransactionsRunnable);
788   }
791 void nsChildView::UnsuspendAsyncCATransactions() {
792   mUnsuspendAsyncCATransactionsRunnable = nullptr;
794   if (mNativeLayerRoot->UnsuspendOffMainThreadCommits()) {
795     // We need to call mNativeLayerRoot->CommitToScreen() at the next available
796     // opportunity.
797     // The easiest way to handle this request is to mark the layer as needing
798     // display, because this will schedule a main thread CATransaction, during
799     // which HandleMainThreadCATransaction will call CommitToScreen().
800     [mView markLayerForDisplay];
801   }
803   // We're done with our critical animation, so allow aysnc flushes again.
804   if (mCompositorBridgeChild) {
805     mCompositorBridgeChild->SetForceSyncFlushRendering(false);
806   }
809 void nsChildView::UpdateFullscreen(bool aFullscreen) {
810   if (mNativeLayerRoot) {
811     mNativeLayerRoot->SetWindowIsFullscreen(aFullscreen);
812   }
815 nsresult nsChildView::SynthesizeNativeKeyEvent(
816     int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
817     uint32_t aModifierFlags, const nsAString& aCharacters,
818     const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) {
819   AutoObserverNotifier notifier(aObserver, "keyevent");
820   return mTextInputHandler->SynthesizeNativeKeyEvent(
821       aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters,
822       aUnmodifiedCharacters);
825 nsresult nsChildView::SynthesizeNativeMouseEvent(
826     LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
827     MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
828     nsIObserver* aObserver) {
829   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
831   AutoObserverNotifier notifier(aObserver, "mouseevent");
833   NSPoint pt =
834       nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
836   // Move the mouse cursor to the requested position and reconnect it to the
837   // mouse.
838   CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
839   CGAssociateMouseAndMouseCursorPosition(true);
841   // aPoint is given with the origin on the top left, but convertScreenToBase
842   // expects a point in a coordinate system that has its origin on the bottom
843   // left.
844   NSPoint screenPoint = NSMakePoint(pt.x, nsCocoaUtils::FlippedScreenY(pt.y));
845   NSPoint windowPoint =
846       nsCocoaUtils::ConvertPointFromScreen([mView window], screenPoint);
847   NSEventModifierFlags modifierFlags =
848       nsCocoaUtils::ConvertWidgetModifiersToMacModifierFlags(aModifierFlags);
850   if (aButton == MouseButton::eX1 || aButton == MouseButton::eX2) {
851     // NSEvent has `buttonNumber` for `NSEventTypeOther*`.  However, it seems
852     // that there is no way to specify it.  Therefore, we should return error
853     // for now.
854     return NS_ERROR_INVALID_ARG;
855   }
857   NSEventType nativeEventType;
858   switch (aNativeMessage) {
859     case NativeMouseMessage::ButtonDown:
860     case NativeMouseMessage::ButtonUp: {
861       switch (aButton) {
862         case MouseButton::ePrimary:
863           nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
864                                 ? NSEventTypeLeftMouseDown
865                                 : NSEventTypeLeftMouseUp;
866           break;
867         case MouseButton::eMiddle:
868           nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
869                                 ? NSEventTypeOtherMouseDown
870                                 : NSEventTypeOtherMouseUp;
871           break;
872         case MouseButton::eSecondary:
873           nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
874                                 ? NSEventTypeRightMouseDown
875                                 : NSEventTypeRightMouseUp;
876           break;
877         default:
878           return NS_ERROR_INVALID_ARG;
879       }
880       break;
881     }
882     case NativeMouseMessage::Move:
883       nativeEventType = NSEventTypeMouseMoved;
884       break;
885     case NativeMouseMessage::EnterWindow:
886       nativeEventType = NSEventTypeMouseEntered;
887       break;
888     case NativeMouseMessage::LeaveWindow:
889       nativeEventType = NSEventTypeMouseExited;
890       break;
891   }
893   NSEvent* event =
894       [NSEvent mouseEventWithType:nativeEventType
895                          location:windowPoint
896                     modifierFlags:modifierFlags
897                         timestamp:[[NSProcessInfo processInfo] systemUptime]
898                      windowNumber:[[mView window] windowNumber]
899                           context:nil
900                       eventNumber:0
901                        clickCount:1
902                          pressure:0.0];
904   if (!event) return NS_ERROR_FAILURE;
906   if ([[mView window] isKindOfClass:[BaseWindow class]]) {
907     // Tracking area events don't end up in their tracking areas when sent
908     // through [NSApp sendEvent:], so pass them directly to the right methods.
909     BaseWindow* window = (BaseWindow*)[mView window];
910     if (nativeEventType == NSEventTypeMouseEntered) {
911       [window mouseEntered:event];
912       return NS_OK;
913     }
914     if (nativeEventType == NSEventTypeMouseExited) {
915       [window mouseExited:event];
916       return NS_OK;
917     }
918     if (nativeEventType == NSEventTypeMouseMoved) {
919       [window mouseMoved:event];
920       return NS_OK;
921     }
922   }
924   [NSApp sendEvent:event];
925   return NS_OK;
927   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
930 nsresult nsChildView::SynthesizeNativeMouseScrollEvent(
931     mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
932     double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
933     uint32_t aAdditionalFlags, nsIObserver* aObserver) {
934   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
936   AutoObserverNotifier notifier(aObserver, "mousescrollevent");
938   NSPoint pt =
939       nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
941   // Move the mouse cursor to the requested position and reconnect it to the
942   // mouse.
943   CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
944   CGAssociateMouseAndMouseCursorPosition(true);
946   // Mostly copied from http://stackoverflow.com/a/6130349
947   CGScrollEventUnit units =
948       (aAdditionalFlags & nsIDOMWindowUtils::MOUSESCROLL_SCROLL_LINES)
949           ? kCGScrollEventUnitLine
950           : kCGScrollEventUnitPixel;
951   CGEventRef cgEvent = CGEventCreateScrollWheelEvent(
952       NULL, units, 3, (int32_t)aDeltaY, (int32_t)aDeltaX, (int32_t)aDeltaZ);
953   if (!cgEvent) {
954     return NS_ERROR_FAILURE;
955   }
957   if (aNativeMessage) {
958     CGEventSetIntegerValueField(cgEvent, kCGScrollWheelEventScrollPhase,
959                                 aNativeMessage);
960   }
962   // On macOS 10.14 and up CGEventPost won't work because of changes in macOS
963   // to improve security. This code makes an NSEvent corresponding to the
964   // wheel event and dispatches it directly to the scrollWheel handler. Some
965   // fiddling is needed with the coordinates in order to simulate what macOS
966   // would do; this code adapted from the Chromium equivalent function at
967   // https://chromium.googlesource.com/chromium/src.git/+/62.0.3178.1/ui/events/test/cocoa_test_event_utils.mm#38
968   CGPoint location = CGEventGetLocation(cgEvent);
969   location.y += NSMinY([[mView window] frame]);
970   location.x -= NSMinX([[mView window] frame]);
971   CGEventSetLocation(cgEvent, location);
973   uint64_t kNanosPerSec = 1000000000L;
974   CGEventSetTimestamp(
975       cgEvent, [[NSProcessInfo processInfo] systemUptime] * kNanosPerSec);
977   NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
978   [event setValue:[mView window] forKey:@"_window"];
979   [mView scrollWheel:event];
981   CFRelease(cgEvent);
982   return NS_OK;
984   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
987 nsresult nsChildView::SynthesizeNativeTouchPoint(
988     uint32_t aPointerId, TouchPointerState aPointerState,
989     mozilla::LayoutDeviceIntPoint aPoint, double aPointerPressure,
990     uint32_t aPointerOrientation, nsIObserver* aObserver) {
991   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
993   AutoObserverNotifier notifier(aObserver, "touchpoint");
995   MOZ_ASSERT(NS_IsMainThread());
996   if (aPointerState == TOUCH_HOVER) {
997     return NS_ERROR_UNEXPECTED;
998   }
1000   if (!mSynthesizedTouchInput) {
1001     mSynthesizedTouchInput = MakeUnique<MultiTouchInput>();
1002   }
1004   LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
1005   MultiTouchInput inputToDispatch = UpdateSynthesizedTouchState(
1006       mSynthesizedTouchInput.get(), TimeStamp::Now(), aPointerId, aPointerState,
1007       pointInWindow, aPointerPressure, aPointerOrientation);
1008   DispatchTouchInput(inputToDispatch);
1009   return NS_OK;
1011   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1014 nsresult nsChildView::SynthesizeNativeTouchpadDoubleTap(
1015     mozilla::LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) {
1016   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1018   DispatchDoubleTapGesture(TimeStamp::Now(), aPoint - WidgetToScreenOffset(),
1019                            static_cast<Modifiers>(aModifierFlags));
1021   return NS_OK;
1023   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1026 bool nsChildView::SendEventToNativeMenuSystem(NSEvent* aEvent) {
1027   bool handled = false;
1028   if (nsCocoaWindow* widget = GetAppWindowWidget()) {
1029     if (nsMenuBarX* mb = widget->GetMenuBar()) {
1030       // Check if main menu wants to handle the event.
1031       handled = mb->PerformKeyEquivalent(aEvent);
1032     }
1033   }
1035   if (!handled && sApplicationMenu) {
1036     // Check if application menu wants to handle the event.
1037     handled = [sApplicationMenu performKeyEquivalent:aEvent];
1038   }
1040   return handled;
1043 void nsChildView::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) {
1044   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
1046   // We always allow keyboard events to propagate to keyDown: but if they are
1047   // not handled we give menu items a chance to act. This allows for handling of
1048   // custom shortcuts. Note that existing shortcuts cannot be reassigned yet and
1049   // will have been handled by keyDown: before we get here.
1050   NSMutableDictionary* nativeKeyEventsMap = [ChildView sNativeKeyEventsMap];
1051   NSEvent* cocoaEvent = [nativeKeyEventsMap objectForKey:@(aEvent->mUniqueId)];
1052   if (!cocoaEvent) {
1053     return;
1054   }
1056   // If the escape key is pressed, the expectations are as follows:
1057   // 1. If the page is loading, interrupt loading.
1058   // 2. Give a website an opportunity to handle the event and call
1059   //    preventDefault() on it.
1060   // 3. If the browser is fullscreen and the page isn't loading, exit
1061   //    fullscreen.
1062   // 4. Ignore.
1063   // Case 1 and 2 are handled before we get here. Below, we handle case 3.
1064   if (StaticPrefs::browser_fullscreen_exit_on_escape() &&
1065       [cocoaEvent keyCode] == kVK_Escape &&
1066       [[mView window] styleMask] & NSWindowStyleMaskFullScreen) {
1067     [[mView window] toggleFullScreen:nil];
1068   }
1070   if (SendEventToNativeMenuSystem(cocoaEvent)) {
1071     aEvent->PreventDefault();
1072   }
1073   [nativeKeyEventsMap removeObjectForKey:@(aEvent->mUniqueId)];
1075   NS_OBJC_END_TRY_IGNORE_BLOCK;
1078 // Used for testing native menu system structure and event handling.
1079 nsresult nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString) {
1080   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1082   nsMenuUtilsX::CheckNativeMenuConsistency([NSApp mainMenu]);
1084   NSString* locationString =
1085       [NSString stringWithCharacters:reinterpret_cast<const unichar*>(
1086                                          indexString.BeginReading())
1087                               length:indexString.Length()];
1088   NSMenuItem* item = nsMenuUtilsX::NativeMenuItemWithLocation(
1089       [NSApp mainMenu], locationString, true);
1090   // We can't perform an action on an item with a submenu, that will raise
1091   // an obj-c exception.
1092   if (item && ![item hasSubmenu]) {
1093     NSMenu* parent = [item menu];
1094     if (parent) {
1095       // NSLog(@"Performing action for native menu item titled: %@\n",
1096       //       [[currentSubmenu itemAtIndex:targetIndex] title]);
1097       mozilla::AutoRestore<bool> autoRestore(
1098           nsMenuUtilsX::gIsSynchronouslyActivatingNativeMenuItemDuringTest);
1099       nsMenuUtilsX::gIsSynchronouslyActivatingNativeMenuItemDuringTest = true;
1100       [parent performActionForItemAtIndex:[parent indexOfItem:item]];
1101       return NS_OK;
1102     }
1103   }
1104   return NS_ERROR_FAILURE;
1106   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1109 // Used for testing native menu system structure and event handling.
1110 nsresult nsChildView::ForceUpdateNativeMenuAt(const nsAString& indexString) {
1111   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1113   if (nsCocoaWindow* widget = GetAppWindowWidget()) {
1114     if (nsMenuBarX* mb = widget->GetMenuBar()) {
1115       if (indexString.IsEmpty())
1116         mb->ForceNativeMenuReload();
1117       else
1118         mb->ForceUpdateNativeMenuAt(indexString);
1119     }
1120   }
1121   return NS_OK;
1123   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1126 #pragma mark -
1128 #ifdef INVALIDATE_DEBUGGING
1130 static Boolean KeyDown(const UInt8 theKey) {
1131   KeyMap map;
1132   GetKeys(map);
1133   return ((*((UInt8*)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
1136 static Boolean caps_lock() { return KeyDown(0x39); }
1138 static void blinkRect(Rect* r) {
1139   StRegionFromPool oldClip;
1140   if (oldClip != NULL) ::GetClip(oldClip);
1142   ::ClipRect(r);
1143   ::InvertRect(r);
1144   UInt32 end = ::TickCount() + 5;
1145   while (::TickCount() < end);
1146   ::InvertRect(r);
1148   if (oldClip != NULL) ::SetClip(oldClip);
1151 static void blinkRgn(RgnHandle rgn) {
1152   StRegionFromPool oldClip;
1153   if (oldClip != NULL) ::GetClip(oldClip);
1155   ::SetClip(rgn);
1156   ::InvertRgn(rgn);
1157   UInt32 end = ::TickCount() + 5;
1158   while (::TickCount() < end);
1159   ::InvertRgn(rgn);
1161   if (oldClip != NULL) ::SetClip(oldClip);
1164 #endif
1166 // Invalidate this component's visible area
1167 void nsChildView::Invalidate(const LayoutDeviceIntRect& aRect) {
1168   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
1170   if (!mView || !mVisible) return;
1172   NS_ASSERTION(
1173       GetWindowRenderer()->GetBackendType() != LayersBackend::LAYERS_WR,
1174       "Shouldn't need to invalidate with accelerated OMTC layers!");
1176   EnsureContentLayerForMainThreadPainting();
1177   mContentLayerInvalidRegion.OrWith(aRect.Intersect(GetBounds()));
1178   [mView markLayerForDisplay];
1180   NS_OBJC_END_TRY_IGNORE_BLOCK;
1183 bool nsChildView::WidgetTypeSupportsAcceleration() {
1184   // All widget types support acceleration.
1185   return true;
1188 bool nsChildView::ShouldUseOffMainThreadCompositing() {
1189   // We need to enable OMTC in popups which contain remote layer
1190   // trees, since the remote content won't be rendered at all otherwise.
1191   if (HasRemoteContent()) {
1192     return true;
1193   }
1195   // Don't use OMTC for popup windows, because we do not want context menus to
1196   // pay the overhead of starting up a compositor. With the OpenGL compositor,
1197   // new windows are expensive because of shader re-compilation, and with
1198   // WebRender, new windows are expensive because they create their own threads
1199   // and texture caches.
1200   // Using OMTC with BasicCompositor for context menus would probably be fine
1201   // but isn't a well-tested configuration.
1202   if ([mView window] && [[mView window] isKindOfClass:[PopupWindow class]]) {
1203     // Use main-thread BasicLayerManager for drawing menus.
1204     return false;
1205   }
1207   return nsBaseWidget::ShouldUseOffMainThreadCompositing();
1210 #pragma mark -
1212 // Invokes callback and ProcessEvent methods on Event Listener object
1213 nsresult nsChildView::DispatchEvent(WidgetGUIEvent* event,
1214                                     nsEventStatus& aStatus) {
1215   RefPtr<nsChildView> kungFuDeathGrip(this);
1217 #ifdef DEBUG
1218   debug_DumpEvent(stdout, event->mWidget, event, "something", 0);
1219 #endif
1221   if (event->mFlags.mIsSynthesizedForTests) {
1222     WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent();
1223     if (keyEvent) {
1224       nsresult rv = mTextInputHandler->AttachNativeKeyEvent(*keyEvent);
1225       NS_ENSURE_SUCCESS(rv, rv);
1226     }
1227   }
1229   aStatus = nsEventStatus_eIgnore;
1231   nsIWidgetListener* listener = mWidgetListener;
1233   // If the listener is NULL, check if the parent is a popup. If it is, then
1234   // this child is the popup content view attached to a popup. Get the
1235   // listener from the parent popup instead.
1236   nsCOMPtr<nsIWidget> parentWidget = mParent;
1237   if (!listener && parentWidget) {
1238     if (parentWidget->GetWindowType() == WindowType::Popup) {
1239       // Check just in case event->mWidget isn't this widget
1240       if (event->mWidget) {
1241         listener = event->mWidget->GetWidgetListener();
1242       }
1243       if (!listener) {
1244         event->mWidget = parentWidget;
1245         listener = parentWidget->GetWidgetListener();
1246       }
1247     }
1248   }
1250   if (listener) aStatus = listener->HandleEvent(event, mUseAttachedEvents);
1252   return NS_OK;
1255 nsIWidget* nsChildView::GetWidgetForListenerEvents() {
1256   // If there is no listener, use the parent popup's listener if that exists.
1257   if (!mWidgetListener && mParent &&
1258       mParent->GetWindowType() == WindowType::Popup) {
1259     return mParent;
1260   }
1261   return this;
1264 void nsChildView::WillPaintWindow() {
1265   nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();
1267   nsIWidgetListener* listener = widget->GetWidgetListener();
1268   if (listener) {
1269     listener->WillPaintWindow(widget);
1270   }
1273 bool nsChildView::PaintWindow(LayoutDeviceIntRegion aRegion) {
1274   nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();
1276   nsIWidgetListener* listener = widget->GetWidgetListener();
1277   if (!listener) return false;
1279   bool returnValue = false;
1280   bool oldDispatchPaint = mIsDispatchPaint;
1281   mIsDispatchPaint = true;
1282   returnValue = listener->PaintWindow(widget, aRegion);
1284   listener = widget->GetWidgetListener();
1285   if (listener) {
1286     listener->DidPaintWindow();
1287   }
1289   mIsDispatchPaint = oldDispatchPaint;
1290   return returnValue;
1293 bool nsChildView::PaintWindowInDrawTarget(gfx::DrawTarget* aDT,
1294                                           const LayoutDeviceIntRegion& aRegion,
1295                                           const gfx::IntSize& aSurfaceSize) {
1296   if (!aDT || !aDT->IsValid()) {
1297     return false;
1298   }
1299   gfxContext targetContext(aDT);
1301   // Set up the clip region and clear existing contents in the backing surface.
1302   targetContext.NewPath();
1303   for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
1304     const LayoutDeviceIntRect& r = iter.Get();
1305     targetContext.Rectangle(gfxRect(r.x, r.y, r.width, r.height));
1306     aDT->ClearRect(gfx::Rect(r.ToUnknownRect()));
1307   }
1308   targetContext.Clip();
1310   nsAutoRetainCocoaObject kungFuDeathGrip(mView);
1311   if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) {
1312     nsBaseWidget::AutoLayerManagerSetup setupLayerManager(
1313         this, &targetContext, BufferMode::BUFFER_NONE);
1314     return PaintWindow(aRegion);
1315   }
1316   return false;
1319 void nsChildView::EnsureContentLayerForMainThreadPainting() {
1320   // Ensure we have an mContentLayer of the correct size.
1321   // The content layer gets created on demand for BasicLayers windows. We do
1322   // not create it during widget creation because, for non-BasicLayers windows,
1323   // the compositing layer manager will create any layers it needs.
1324   gfx::IntSize size = GetBounds().Size().ToUnknownSize();
1325   if (mContentLayer && mContentLayer->GetSize() != size) {
1326     mNativeLayerRoot->RemoveLayer(mContentLayer);
1327     mContentLayer = nullptr;
1328   }
1329   if (!mContentLayer) {
1330     mPoolHandle = SurfacePool::Create(0)->GetHandleForGL(nullptr);
1331     RefPtr<NativeLayer> contentLayer =
1332         mNativeLayerRoot->CreateLayer(size, false, mPoolHandle);
1333     mNativeLayerRoot->AppendLayer(contentLayer);
1334     mContentLayer = contentLayer->AsNativeLayerCA();
1335     mContentLayer->SetSurfaceIsFlipped(false);
1336     mContentLayerInvalidRegion = GetBounds();
1337   }
1340 void nsChildView::PaintWindowInContentLayer() {
1341   EnsureContentLayerForMainThreadPainting();
1342   mPoolHandle->OnBeginFrame();
1343   RefPtr<DrawTarget> dt = mContentLayer->NextSurfaceAsDrawTarget(
1344       gfx::IntRect({}, mContentLayer->GetSize()),
1345       mContentLayerInvalidRegion.ToUnknownRegion(), gfx::BackendType::SKIA);
1346   if (!dt) {
1347     return;
1348   }
1350   PaintWindowInDrawTarget(dt, mContentLayerInvalidRegion, dt->GetSize());
1351   mContentLayer->NotifySurfaceReady();
1352   mContentLayerInvalidRegion.SetEmpty();
1353   mPoolHandle->OnEndFrame();
1356 void nsChildView::HandleMainThreadCATransaction() {
1357   WillPaintWindow();
1359   if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) {
1360     // We're in BasicLayers mode, i.e. main thread software compositing.
1361     // Composite the window into our layer's surface.
1362     PaintWindowInContentLayer();
1363   } else {
1364     // Trigger a synchronous OMTC composite. This will call NextSurface and
1365     // NotifySurfaceReady on the compositor thread to update mNativeLayerRoot's
1366     // contents, and the main thread (this thread) will wait inside PaintWindow
1367     // during that time.
1368     PaintWindow(LayoutDeviceIntRegion(GetBounds()));
1369   }
1371   {
1372     // Apply the changes inside mNativeLayerRoot to the underlying CALayers. Now
1373     // is a good time to call this because we know we're currently inside a main
1374     // thread CATransaction, and the lock makes sure that no composition is
1375     // currently in progress, so we won't present half-composited state to the
1376     // screen.
1377     MutexAutoLock lock(mCompositingLock);
1378     mNativeLayerRoot->CommitToScreen();
1379   }
1381   MaybeScheduleUnsuspendAsyncCATransactions();
1384 #pragma mark -
1386 void nsChildView::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); }
1388 void nsChildView::ReportSizeEvent() {
1389   if (mWidgetListener)
1390     mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
1393 #pragma mark -
1395 LayoutDeviceIntPoint nsChildView::GetClientOffset() {
1396   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1398   NSPoint origin = [mView convertPoint:NSMakePoint(0, 0) toView:nil];
1399   origin.y = [[mView window] frame].size.height - origin.y;
1400   return CocoaPointsToDevPixels(origin);
1402   NS_OBJC_END_TRY_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
1405 //    Return the offset between this child view and the screen.
1406 //    @return       -- widget origin in device-pixel coords
1407 LayoutDeviceIntPoint nsChildView::WidgetToScreenOffset() {
1408   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1410   NSPoint origin = NSMakePoint(0, 0);
1412   // 1. First translate view origin point into window coords.
1413   // The returned point is in bottom-left coordinates.
1414   origin = [mView convertPoint:origin toView:nil];
1416   // 2. We turn the window-coord rect's origin into screen (still bottom-left)
1417   // coords.
1418   origin = nsCocoaUtils::ConvertPointToScreen([mView window], origin);
1420   // 3. Since we're dealing in bottom-left coords, we need to make it top-left
1421   // coords
1422   //    before we pass it back to Gecko.
1423   FlipCocoaScreenCoordinate(origin);
1425   // convert to device pixels
1426   return CocoaPointsToDevPixels(origin);
1428   NS_OBJC_END_TRY_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
1431 nsresult nsChildView::SetTitle(const nsAString& title) {
1432   // child views don't have titles
1433   return NS_OK;
1436 nsresult nsChildView::GetAttention(int32_t aCycleCount) {
1437   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1439   [NSApp requestUserAttention:NSInformationalRequest];
1440   return NS_OK;
1442   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1445 /* static */
1446 bool nsChildView::DoHasPendingInputEvent() {
1447   return sLastInputEventCount != GetCurrentInputEventCount();
1450 /* static */
1451 uint32_t nsChildView::GetCurrentInputEventCount() {
1452   // Can't use kCGAnyInputEventType because that updates too rarely for us (and
1453   // always in increments of 30+!) and because apparently it's sort of broken
1454   // on Tiger.  So just go ahead and query the counters we care about.
1455   static const CGEventType eventTypes[] = {kCGEventLeftMouseDown,
1456                                            kCGEventLeftMouseUp,
1457                                            kCGEventRightMouseDown,
1458                                            kCGEventRightMouseUp,
1459                                            kCGEventMouseMoved,
1460                                            kCGEventLeftMouseDragged,
1461                                            kCGEventRightMouseDragged,
1462                                            kCGEventKeyDown,
1463                                            kCGEventKeyUp,
1464                                            kCGEventScrollWheel,
1465                                            kCGEventTabletPointer,
1466                                            kCGEventOtherMouseDown,
1467                                            kCGEventOtherMouseUp,
1468                                            kCGEventOtherMouseDragged};
1470   uint32_t eventCount = 0;
1471   for (uint32_t i = 0; i < std::size(eventTypes); ++i) {
1472     eventCount += CGEventSourceCounterForEventType(
1473         kCGEventSourceStateCombinedSessionState, eventTypes[i]);
1474   }
1475   return eventCount;
1478 /* static */
1479 void nsChildView::UpdateCurrentInputEventCount() {
1480   sLastInputEventCount = GetCurrentInputEventCount();
1483 bool nsChildView::HasPendingInputEvent() { return DoHasPendingInputEvent(); }
1485 #pragma mark -
1487 void nsChildView::SetInputContext(const InputContext& aContext,
1488                                   const InputContextAction& aAction) {
1489   NS_ENSURE_TRUE_VOID(mTextInputHandler);
1491   if (mTextInputHandler->IsFocused()) {
1492     if (aContext.IsPasswordEditor()) {
1493       TextInputHandler::EnableSecureEventInput();
1494     } else {
1495       TextInputHandler::EnsureSecureEventInputDisabled();
1496     }
1497   }
1499   // IMEInputHandler::IsEditableContent() returns false when both
1500   // IsASCIICableOnly() and IsIMEEnabled() return false.  So, be careful
1501   // when you change the following code.  You might need to change
1502   // IMEInputHandler::IsEditableContent() too.
1503   mInputContext = aContext;
1505   switch (aContext.mIMEState.mEnabled) {
1506     case IMEEnabled::Enabled:
1507       mTextInputHandler->SetASCIICapableOnly(false);
1508       mTextInputHandler->EnableIME(true);
1509       if (mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) {
1510         mTextInputHandler->SetIMEOpenState(mInputContext.mIMEState.mOpen ==
1511                                            IMEState::OPEN);
1512       }
1513       mTextInputHandler->EnableTextSubstitution(aContext.mAutocorrect);
1514       break;
1515     case IMEEnabled::Disabled:
1516       mTextInputHandler->SetASCIICapableOnly(false);
1517       mTextInputHandler->EnableIME(false);
1518       mTextInputHandler->EnableTextSubstitution(false);
1519       break;
1520     case IMEEnabled::Password:
1521       mTextInputHandler->SetASCIICapableOnly(true);
1522       mTextInputHandler->EnableIME(false);
1523       mTextInputHandler->EnableTextSubstitution(aContext.mAutocorrect);
1524       break;
1525     default:
1526       NS_ERROR("not implemented!");
1527   }
1530 InputContext nsChildView::GetInputContext() {
1531   switch (mInputContext.mIMEState.mEnabled) {
1532     case IMEEnabled::Enabled:
1533       if (mTextInputHandler) {
1534         mInputContext.mIMEState.mOpen = mTextInputHandler->IsIMEOpened()
1535                                             ? IMEState::OPEN
1536                                             : IMEState::CLOSED;
1537         break;
1538       }
1539       // If mTextInputHandler is null, set CLOSED instead...
1540       [[fallthrough]];
1541     default:
1542       mInputContext.mIMEState.mOpen = IMEState::CLOSED;
1543       break;
1544   }
1545   return mInputContext;
1548 TextEventDispatcherListener*
1549 nsChildView::GetNativeTextEventDispatcherListener() {
1550   if (NS_WARN_IF(!mTextInputHandler)) {
1551     return nullptr;
1552   }
1553   return mTextInputHandler;
1556 nsresult nsChildView::AttachNativeKeyEvent(
1557     mozilla::WidgetKeyboardEvent& aEvent) {
1558   NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
1559   return mTextInputHandler->AttachNativeKeyEvent(aEvent);
1562 bool nsChildView::GetEditCommands(NativeKeyBindingsType aType,
1563                                   const WidgetKeyboardEvent& aEvent,
1564                                   nsTArray<CommandInt>& aCommands) {
1565   // Validate the arguments.
1566   if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType, aEvent, aCommands))) {
1567     return false;
1568   }
1570   Maybe<WritingMode> writingMode;
1571   if (aEvent.NeedsToRemapNavigationKey()) {
1572     if (RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher()) {
1573       writingMode = dispatcher->MaybeQueryWritingModeAtSelection();
1574     }
1575   }
1577   NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
1578   keyBindings->GetEditCommands(aEvent, writingMode, aCommands);
1579   return true;
1582 NSView<mozView>* nsChildView::GetEditorView() {
1583   NSView<mozView>* editorView = mView;
1584   // We need to get editor's view. E.g., when the focus is in the bookmark
1585   // dialog, the view is <panel> element of the dialog.  At this time, the key
1586   // events are processed the parent window's view that has native focus.
1587   WidgetQueryContentEvent queryContentState(true, eQueryContentState, this);
1588   // This may be called during creating a menu popup frame due to creating
1589   // widget synchronously and that causes Cocoa asking current window level.
1590   // In this case, it's not safe to flush layout on the document and we don't
1591   // need any layout information right now.
1592   queryContentState.mNeedsToFlushLayout = false;
1593   DispatchWindowEvent(queryContentState);
1594   if (queryContentState.Succeeded() &&
1595       queryContentState.mReply->mFocusedWidget) {
1596     NSView<mozView>* view = static_cast<NSView<mozView>*>(
1597         queryContentState.mReply->mFocusedWidget->GetNativeData(
1598             NS_NATIVE_WIDGET));
1599     if (view) editorView = view;
1600   }
1601   return editorView;
1604 #pragma mark -
1606 void nsChildView::CreateCompositor() {
1607   nsBaseWidget::CreateCompositor();
1608   if (mCompositorBridgeChild) {
1609     [mView setUsingOMTCompositor:true];
1610   }
1613 void nsChildView::ConfigureAPZCTreeManager() {
1614   nsBaseWidget::ConfigureAPZCTreeManager();
1617 void nsChildView::ConfigureAPZControllerThread() {
1618   nsBaseWidget::ConfigureAPZControllerThread();
1621 bool nsChildView::PreRender(WidgetRenderingContext* aContext)
1622     MOZ_NO_THREAD_SAFETY_ANALYSIS {
1623   // The lock makes sure that we don't attempt to tear down the view while
1624   // compositing. That would make us unable to call postRender on it when the
1625   // composition is done, thus keeping the GL context locked forever.
1626   mCompositingLock.Lock();
1628   if (aContext->mGL && gfxPlatform::CanMigrateMacGPUs()) {
1629     GLContextCGL::Cast(aContext->mGL)->MigrateToActiveGPU();
1630   }
1632   return true;
1635 void nsChildView::PostRender(WidgetRenderingContext* aContext)
1636     MOZ_NO_THREAD_SAFETY_ANALYSIS {
1637   mCompositingLock.Unlock();
1640 RefPtr<layers::NativeLayerRoot> nsChildView::GetNativeLayerRoot() {
1641   return mNativeLayerRoot;
1644 static LayoutDeviceIntRect FindFirstRectOfType(
1645     const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
1646     nsITheme::ThemeGeometryType aThemeGeometryType) {
1647   for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) {
1648     const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
1649     if (g.mType == aThemeGeometryType) {
1650       return g.mRect;
1651     }
1652   }
1653   return LayoutDeviceIntRect();
1656 void nsChildView::UpdateThemeGeometries(
1657     const nsTArray<ThemeGeometry>& aThemeGeometries) {
1658   if (!mView.window) {
1659     return;
1660   }
1662   UpdateVibrancy(aThemeGeometries);
1664   if (![mView.window isKindOfClass:[ToolbarWindow class]]) {
1665     return;
1666   }
1668   ToolbarWindow* win = (ToolbarWindow*)[mView window];
1670   // Update titlebar control offsets.
1671   LayoutDeviceIntRect windowButtonRect =
1672       FindFirstRectOfType(aThemeGeometries, eThemeGeometryTypeWindowButtons);
1673   [win placeWindowButtons:[mView convertRect:DevPixelsToCocoaPoints(
1674                                                  windowButtonRect)
1675                                       toView:nil]];
1678 static Maybe<VibrancyType> ThemeGeometryTypeToVibrancyType(
1679     nsITheme::ThemeGeometryType aThemeGeometryType) {
1680   switch (aThemeGeometryType) {
1681     case eThemeGeometryTypeSidebar:
1682       return Some(VibrancyType::Sidebar);
1683     case eThemeGeometryTypeTitlebar:
1684       return Some(VibrancyType::Titlebar);
1685     default:
1686       return Nothing();
1687   }
1690 static EnumeratedArray<VibrancyType, LayoutDeviceIntRegion>
1691 GatherVibrantRegions(Span<const nsIWidget::ThemeGeometry> aThemeGeometries) {
1692   EnumeratedArray<VibrancyType, LayoutDeviceIntRegion> regions;
1693   for (const auto& geometry : aThemeGeometries) {
1694     auto vibrancyType = ThemeGeometryTypeToVibrancyType(geometry.mType);
1695     if (!vibrancyType) {
1696       continue;
1697     }
1698     regions[*vibrancyType].OrWith(geometry.mRect);
1699   }
1700   return regions;
1703 // Subtracts parts from regions in such a way that they don't have any overlap.
1704 // Each region in the argument list will have the union of all the regions
1705 // *following* it subtracted from itself. In other words, the arguments are
1706 // treated as low priority to high priority.
1707 static void MakeRegionsNonOverlapping(Span<LayoutDeviceIntRegion> aRegions) {
1708   LayoutDeviceIntRegion unionOfAll;
1709   for (auto& region : aRegions) {
1710     region.SubOut(unionOfAll);
1711     unionOfAll.OrWith(region);
1712   }
1715 void nsChildView::UpdateVibrancy(
1716     const nsTArray<ThemeGeometry>& aThemeGeometries) {
1717   auto regions = GatherVibrantRegions(aThemeGeometries);
1718   MakeRegionsNonOverlapping(regions);
1720   auto& vm = EnsureVibrancyManager();
1721   bool changed = false;
1723   // EnumeratedArray doesn't have an iterator that also yields the enum type,
1724   // but we rely on VibrancyType being contiguous and starting at 0, so we can
1725   // do that manually.
1726   size_t i = 0;
1727   for (const auto& region : regions) {
1728     changed |= vm.UpdateVibrantRegion(VibrancyType(i++), region);
1729   }
1731   if (changed) {
1732     SuspendAsyncCATransactions();
1733   }
1736 mozilla::VibrancyManager& nsChildView::EnsureVibrancyManager() {
1737   MOZ_ASSERT(mView, "Only call this once we have a view!");
1738   if (!mVibrancyManager) {
1739     mVibrancyManager =
1740         MakeUnique<VibrancyManager>(*this, mView.vibrancyViewsContainer);
1741   }
1742   return *mVibrancyManager;
1745 void nsChildView::UpdateBoundsFromView() {
1746   auto oldSize = mBounds.Size();
1747   mBounds = CocoaPointsToDevPixels([mView frame]);
1748   if (mBounds.Size() != oldSize) {
1749     SuspendAsyncCATransactions();
1750   }
1753 @interface NonDraggableView : NSView
1754 @end
1756 @implementation NonDraggableView
1757 - (BOOL)mouseDownCanMoveWindow {
1758   return NO;
1760 - (NSView*)hitTest:(NSPoint)aPoint {
1761   return nil;
1763 - (NSRect)_opaqueRectForWindowMoveWhenInTitlebar {
1764   // In NSWindows that use NSWindowStyleMaskFullSizeContentView, NSViews which
1765   // overlap the titlebar do not disable window dragging in the overlapping
1766   // areas even if they return NO from mouseDownCanMoveWindow. This can have
1767   // unfortunate effects: For example, dragging tabs in a browser window would
1768   // move the window if those tabs are in the titlebar.
1769   // macOS does not seem to offer a documented way to opt-out of the forced
1770   // window dragging in the titlebar.
1771   // Overriding _opaqueRectForWindowMoveWhenInTitlebar is an undocumented way
1772   // of opting out of this behavior. This method was added in 10.11 and is used
1773   // by some NSControl subclasses to prevent window dragging in the titlebar.
1774   // The function which assembles the draggable area of the window calls
1775   // _opaqueRect for the content area and _opaqueRectForWindowMoveWhenInTitlebar
1776   // for the titlebar area, on all visible NSViews. The default implementation
1777   // of _opaqueRect returns [self visibleRect], and the default implementation
1778   // of _opaqueRectForWindowMoveWhenInTitlebar returns NSZeroRect unless it's
1779   // overridden.
1780   //
1781   // Since this view is constructed and used such that its entire bounds is the
1782   // relevant region, we just return our bounds.
1783   return self.bounds;
1785 @end
1787 void nsChildView::UpdateWindowDraggingRegion(
1788     const LayoutDeviceIntRegion& aRegion) {
1789   // mView returns YES from mouseDownCanMoveWindow, so we need to put NSViews
1790   // that return NO from mouseDownCanMoveWindow in the places that shouldn't
1791   // be draggable. We can't do it the other way round because returning
1792   // YES from mouseDownCanMoveWindow doesn't have any effect if there's a
1793   // superview that returns NO.
1794   LayoutDeviceIntRegion nonDraggable;
1795   nonDraggable.Sub(LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height),
1796                    aRegion);
1798   __block bool changed = false;
1800   // Suppress calls to setNeedsDisplay during NSView geometry changes.
1801   ManipulateViewWithoutNeedingDisplay(mView, ^() {
1802     changed = mNonDraggableRegion.UpdateRegion(
1803         nonDraggable, *this, mView.nonDraggableViewsContainer, ^() {
1804           return [[NonDraggableView alloc] initWithFrame:NSZeroRect];
1805         });
1806   });
1808   if (changed) {
1809     // Trigger an update to the window server. This will call
1810     // mouseDownCanMoveWindow.
1811     // Doing this manually is only necessary because we're suppressing
1812     // setNeedsDisplay calls above.
1813     [[mView window] setMovableByWindowBackground:NO];
1814     [[mView window] setMovableByWindowBackground:YES];
1815   }
1818 nsEventStatus nsChildView::DispatchAPZInputEvent(InputData& aEvent) {
1819   APZEventResult result;
1821   if (mAPZC) {
1822     result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
1823   }
1825   if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1826     return result.GetStatus();
1827   }
1829   if (aEvent.mInputType == PINCHGESTURE_INPUT) {
1830     PinchGestureInput& pinchEvent = aEvent.AsPinchGestureInput();
1831     WidgetWheelEvent wheelEvent = pinchEvent.ToWidgetEvent(this);
1832     ProcessUntransformedAPZEvent(&wheelEvent, result);
1833   } else if (aEvent.mInputType == TAPGESTURE_INPUT) {
1834     TapGestureInput& tapEvent = aEvent.AsTapGestureInput();
1835     WidgetSimpleGestureEvent gestureEvent = tapEvent.ToWidgetEvent(this);
1836     ProcessUntransformedAPZEvent(&gestureEvent, result);
1837   } else {
1838     MOZ_ASSERT_UNREACHABLE();
1839   }
1841   return result.GetStatus();
1844 void nsChildView::DispatchAPZWheelInputEvent(InputData& aEvent) {
1845   if (mSwipeTracker && aEvent.mInputType == PANGESTURE_INPUT) {
1846     // Give the swipe tracker a first pass at the event. If a new pan gesture
1847     // has been started since the beginning of the swipe, the swipe tracker
1848     // will know to ignore the event.
1849     nsEventStatus status =
1850         mSwipeTracker->ProcessEvent(aEvent.AsPanGestureInput());
1851     if (status == nsEventStatus_eConsumeNoDefault) {
1852       return;
1853     }
1854   }
1856   WidgetWheelEvent event(true, eWheel, this);
1858   if (mAPZC) {
1859     APZEventResult result;
1861     switch (aEvent.mInputType) {
1862       case PANGESTURE_INPUT: {
1863         result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
1864         if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1865           return;
1866         }
1868         event = MayStartSwipeForAPZ(aEvent.AsPanGestureInput(), result);
1869         break;
1870       }
1871       case SCROLLWHEEL_INPUT: {
1872         // For wheel events on OS X, send it to APZ using the WidgetInputEvent
1873         // variant of ReceiveInputEvent, because the APZInputBridge version of
1874         // that function has special handling (for delta multipliers etc.) that
1875         // we need to run. Using the InputData variant would bypass that and
1876         // go straight to the APZCTreeManager subclass.
1877         event = aEvent.AsScrollWheelInput().ToWidgetEvent(this);
1878         result = mAPZC->InputBridge()->ReceiveInputEvent(event);
1879         if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1880           return;
1881         }
1882         break;
1883       };
1884       default:
1885         MOZ_CRASH("unsupported event type");
1886         return;
1887     }
1888     if (event.mMessage == eWheel &&
1889         (event.mDeltaX != 0 || event.mDeltaY != 0)) {
1890       ProcessUntransformedAPZEvent(&event, result);
1891     }
1892     return;
1893   }
1895   nsEventStatus status;
1896   switch (aEvent.mInputType) {
1897     case PANGESTURE_INPUT: {
1898       if (MayStartSwipeForNonAPZ(aEvent.AsPanGestureInput())) {
1899         return;
1900       }
1901       event = aEvent.AsPanGestureInput().ToWidgetEvent(this);
1902       break;
1903     }
1904     case SCROLLWHEEL_INPUT: {
1905       event = aEvent.AsScrollWheelInput().ToWidgetEvent(this);
1906       break;
1907     }
1908     default:
1909       MOZ_CRASH("unexpected event type");
1910       return;
1911   }
1912   if (event.mMessage == eWheel && (event.mDeltaX != 0 || event.mDeltaY != 0)) {
1913     DispatchEvent(&event, status);
1914   }
1917 void nsChildView::DispatchDoubleTapGesture(TimeStamp aEventTimeStamp,
1918                                            LayoutDeviceIntPoint aScreenPosition,
1919                                            mozilla::Modifiers aModifiers) {
1920   if (StaticPrefs::apz_mac_enable_double_tap_zoom_touchpad_gesture()) {
1921     TapGestureInput event{
1922         TapGestureInput::TAPGESTURE_DOUBLE, aEventTimeStamp,
1923         ViewAs<ScreenPixel>(
1924             aScreenPosition,
1925             PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent),
1926         aModifiers};
1928     DispatchAPZInputEvent(event);
1929   } else {
1930     // Setup the "double tap" event.
1931     WidgetSimpleGestureEvent geckoEvent(true, eTapGesture, this);
1932     // do what convertCocoaMouseEvent does basically.
1933     geckoEvent.mRefPoint = aScreenPosition;
1934     geckoEvent.mModifiers = aModifiers;
1935     geckoEvent.mTimeStamp = aEventTimeStamp;
1936     geckoEvent.mClickCount = 1;
1938     // Send the event.
1939     DispatchWindowEvent(geckoEvent);
1940   }
1943 void nsChildView::LookUpDictionary(
1944     const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRangeArray,
1945     const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) {
1946   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
1948   NSMutableAttributedString* attrStr =
1949       nsCocoaUtils::GetNSMutableAttributedString(
1950           aText, aFontRangeArray, aIsVertical, BackingScaleFactor());
1951   NSPoint pt =
1952       nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
1953   NSDictionary* attributes = [attrStr attributesAtIndex:0 effectiveRange:nil];
1954   NSFont* font = [attributes objectForKey:NSFontAttributeName];
1955   if (font) {
1956     if (aIsVertical) {
1957       pt.x -= [font descender];
1958     } else {
1959       pt.y += [font ascender];
1960     }
1961   }
1963   [mView showDefinitionForAttributedString:attrStr atPoint:pt];
1965   NS_OBJC_END_TRY_IGNORE_BLOCK;
1968 #ifdef ACCESSIBILITY
1969 already_AddRefed<a11y::LocalAccessible> nsChildView::GetDocumentAccessible() {
1970   if (!mozilla::a11y::ShouldA11yBeEnabled()) return nullptr;
1972   // mAccessible might be dead if accessibility was previously disabled and is
1973   // now being enabled again.
1974   if (mAccessible && mAccessible->IsAlive()) {
1975     RefPtr<a11y::LocalAccessible> ret;
1976     CallQueryReferent(mAccessible.get(), static_cast<a11y::LocalAccessible**>(
1977                                              getter_AddRefs(ret)));
1978     return ret.forget();
1979   }
1981   // need to fetch the accessible anew, because it has gone away.
1982   // cache the accessible in our weak ptr
1983   RefPtr<a11y::LocalAccessible> acc = GetRootAccessible();
1984   mAccessible = do_GetWeakReference(acc.get());
1986   return acc.forget();
1988 #endif
1990 class WidgetsReleaserRunnable final : public mozilla::Runnable {
1991  public:
1992   explicit WidgetsReleaserRunnable(nsTArray<nsCOMPtr<nsIWidget>>&& aWidgetArray)
1993       : mozilla::Runnable("WidgetsReleaserRunnable"),
1994         mWidgetArray(std::move(aWidgetArray)) {}
1996   // Do nothing; all this runnable does is hold a reference the widgets in
1997   // mWidgetArray, and those references will be dropped when this runnable
1998   // is destroyed.
2000  private:
2001   nsTArray<nsCOMPtr<nsIWidget>> mWidgetArray;
2004 #pragma mark -
2006 // ViewRegionContainerView is a view class for certain subviews of ChildView
2007 // which contain the NSViews created for ViewRegions (see ViewRegion.h).
2008 // It doesn't do anything interesting, it only acts as a container so that it's
2009 // easier for ChildView to control the z order of its children.
2010 @interface ViewRegionContainerView : NSView {
2012 @end
2014 @implementation ViewRegionContainerView
2016 - (NSView*)hitTest:(NSPoint)aPoint {
2017   return nil;  // Be transparent to mouse events.
2020 - (BOOL)isFlipped {
2021   return [[self superview] isFlipped];
2024 - (BOOL)mouseDownCanMoveWindow {
2025   return [[self superview] mouseDownCanMoveWindow];
2028 @end
2030 @implementation ChildView
2032 // globalDragPboard is non-null during native drag sessions that did not
2033 // originate in our native NSView (it is set in |draggingEntered:|). It is unset
2034 // when the drag session ends for this view, either with the mouse exiting or
2035 // when a drop occurs in this view.
2036 NSPasteboard* globalDragPboard = nil;
2038 // gLastDragView and gLastDragMouseDownEvent are used to communicate information
2039 // to the drag service during drag invocation (starting a drag in from the
2040 // view). gLastDragView is only non-null while a mouse button is pressed, so
2041 // between mouseDown and mouseUp.
2042 NSView* gLastDragView = nil;             // [weak]
2043 NSEvent* gLastDragMouseDownEvent = nil;  // [strong]
2045 + (void)initialize {
2046   static BOOL initialized = NO;
2048   if (!initialized) {
2049     // Inform the OS about the types of services (from the "Services" menu)
2050     // that we can handle.
2051     NSArray* types = @[
2052       [UTIHelper stringFromPboardType:NSPasteboardTypeString],
2053       [UTIHelper stringFromPboardType:NSPasteboardTypeHTML]
2054     ];
2055     [NSApp registerServicesMenuSendTypes:types returnTypes:types];
2056     initialized = YES;
2057   }
2060 + (void)registerViewForDraggedTypes:(NSView*)aView {
2061   [aView
2062       registerForDraggedTypes:
2063           [NSArray
2064               arrayWithObjects:
2065                   [UTIHelper stringFromPboardType:NSFilenamesPboardType],
2066                   [UTIHelper stringFromPboardType:kMozFileUrlsPboardType],
2067                   [UTIHelper stringFromPboardType:NSPasteboardTypeString],
2068                   [UTIHelper stringFromPboardType:NSPasteboardTypeHTML],
2069                   [UTIHelper
2070                       stringFromPboardType:(NSString*)
2071                                                kPasteboardTypeFileURLPromise],
2072                   [UTIHelper stringFromPboardType:kMozWildcardPboardType],
2073                   [UTIHelper stringFromPboardType:kPublicUrlPboardType],
2074                   [UTIHelper stringFromPboardType:kPublicUrlNamePboardType],
2075                   [UTIHelper stringFromPboardType:kUrlsWithTitlesPboardType],
2076                   nil]];
2079 // initWithFrame:geckoChild:
2080 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild {
2081   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
2083   if ((self = [super initWithFrame:inFrame])) {
2084     mGeckoChild = inChild;
2085     mBlockedLastMouseDown = NO;
2086     mExpectingWheelStop = NO;
2088     mLastMouseDownEvent = nil;
2089     mLastKeyDownEvent = nil;
2090     mClickThroughMouseDownEvent = nil;
2091     mDragService = nullptr;
2093     mGestureState = eGestureState_None;
2094     mCumulativeRotation = 0.0;
2096     mIsUpdatingLayer = NO;
2098     [self setFocusRingType:NSFocusRingTypeNone];
2100 #ifdef __LP64__
2101     mCancelSwipeAnimation = nil;
2102 #endif
2104     auto bounds = self.bounds;
2105     mNonDraggableViewsContainer =
2106         [[ViewRegionContainerView alloc] initWithFrame:bounds];
2107     mVibrancyViewsContainer =
2108         [[ViewRegionContainerView alloc] initWithFrame:bounds];
2110     mNonDraggableViewsContainer.autoresizingMask =
2111         mVibrancyViewsContainer.autoresizingMask =
2112             NSViewWidthSizable | NSViewHeightSizable;
2114     [self addSubview:mNonDraggableViewsContainer];
2115     [self addSubview:mVibrancyViewsContainer];
2117     mPixelHostingView = [[PixelHostingView alloc] initWithFrame:bounds];
2118     mPixelHostingView.autoresizingMask =
2119         NSViewWidthSizable | NSViewHeightSizable;
2121     [self addSubview:mPixelHostingView];
2123     mRootCALayer = [[CALayer layer] retain];
2124     mRootCALayer.position = NSZeroPoint;
2125     mRootCALayer.bounds = NSZeroRect;
2126     mRootCALayer.anchorPoint = NSZeroPoint;
2127     mRootCALayer.contentsGravity = kCAGravityTopLeft;
2128     [mPixelHostingView.layer addSublayer:mRootCALayer];
2130     mLastPressureStage = 0;
2131   }
2133   // register for things we'll take from other applications
2134   [ChildView registerViewForDraggedTypes:self];
2136   return self;
2138   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
2141 - (NSTextInputContext*)inputContext {
2142   if (!mGeckoChild) {
2143     // -[ChildView widgetDestroyed] has been called, but
2144     // -[ChildView delayedTearDown] has not yet completed.  Accessing
2145     // [super inputContext] now would uselessly recreate a text input context
2146     // for us, under which -[ChildView validAttributesForMarkedText] would
2147     // be called and the assertion checking for mTextInputHandler would fail.
2148     // We return nil to avoid that.
2149     return nil;
2150   }
2151   return [super inputContext];
2154 - (void)installTextInputHandler:(TextInputHandler*)aHandler {
2155   mTextInputHandler = aHandler;
2158 - (void)uninstallTextInputHandler {
2159   mTextInputHandler = nullptr;
2162 - (NSView*)vibrancyViewsContainer {
2163   return mVibrancyViewsContainer;
2166 - (NSView*)nonDraggableViewsContainer {
2167   return mNonDraggableViewsContainer;
2170 - (NSView*)pixelHostingView {
2171   return mPixelHostingView;
2174 - (void)dealloc {
2175   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2177   [mLastMouseDownEvent release];
2178   [mLastKeyDownEvent release];
2179   [mClickThroughMouseDownEvent release];
2180   ChildViewMouseTracker::OnDestroyView(self);
2182   [mVibrancyViewsContainer removeFromSuperview];
2183   [mVibrancyViewsContainer release];
2184   [mNonDraggableViewsContainer removeFromSuperview];
2185   [mNonDraggableViewsContainer release];
2186   [mPixelHostingView removeFromSuperview];
2187   [mPixelHostingView release];
2188   [mRootCALayer release];
2190   if (gLastDragView == self) {
2191     gLastDragView = nil;
2192   }
2194   [super dealloc];
2196   NS_OBJC_END_TRY_IGNORE_BLOCK;
2199 - (void)widgetDestroyed {
2200   if (mTextInputHandler) {
2201     mTextInputHandler->OnDestroyWidget(mGeckoChild);
2202     mTextInputHandler = nullptr;
2203   }
2204   mGeckoChild = nullptr;
2206   // Just in case we're destroyed abruptly and missed the draggingExited
2207   // or performDragOperation message.
2208   NS_IF_RELEASE(mDragService);
2211 // mozView method, return our gecko child view widget. Note this does not
2212 // AddRef.
2213 - (nsIWidget*)widget {
2214   return static_cast<nsIWidget*>(mGeckoChild);
2217 - (NSString*)description {
2218   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
2220   return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@",
2221                                     self, mGeckoChild,
2222                                     NSStringFromRect([self frame])];
2224   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
2227 // Make the origin of this view the topLeft corner (gecko origin) rather
2228 // than the bottomLeft corner (standard cocoa origin).
2229 - (BOOL)isFlipped {
2230   return YES;
2233 // We accept key and mouse events, so don't keep passing them up the chain.
2234 // Allow this to be a 'focused' widget for event dispatch.
2235 - (BOOL)acceptsFirstResponder {
2236   return YES;
2239 // Accept mouse down events on background windows
2240 - (BOOL)acceptsFirstMouse:(NSEvent*)aEvent {
2241   if (![[self window] isKindOfClass:[PopupWindow class]]) {
2242     // We rely on this function to tell us that the mousedown was on a
2243     // background window. Inside mouseDown we can't tell whether we were
2244     // inactive because at that point we've already been made active.
2245     // Unfortunately, acceptsFirstMouse is called for PopupWindows even when
2246     // their parent window is active, so ignore this on them for now.
2247     mClickThroughMouseDownEvent = [aEvent retain];
2248   }
2249   return YES;
2252 - (BOOL)mouseDownCanMoveWindow {
2253   // Return YES so that parts of this view can be draggable. The non-draggable
2254   // parts will be covered by NSViews that return NO from
2255   // mouseDownCanMoveWindow and thus override draggability from the inside.
2256   // These views are assembled in nsChildView::UpdateWindowDraggingRegion.
2257   return YES;
2260 - (void)viewDidChangeBackingProperties {
2261   [super viewDidChangeBackingProperties];
2262   if (mGeckoChild) {
2263     // actually, it could be the color space that's changed,
2264     // but we can't tell the difference here except by retrieving
2265     // the backing scale factor and comparing to the old value
2266     mGeckoChild->BackingScaleFactorChanged();
2267   }
2270 - (BOOL)isCoveringTitlebar {
2271   return [[self window] isKindOfClass:[BaseWindow class]] &&
2272          [(BaseWindow*)[self window] mainChildView] == self &&
2273          [(BaseWindow*)[self window] drawsContentsIntoWindowFrame];
2276 - (void)showContextMenuForSelection:(id)sender {
2277   if (!mGeckoChild) {
2278     return;
2279   }
2280   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2281   WidgetPointerEvent geckoEvent(true, eContextMenu, mGeckoChild,
2282                                 WidgetMouseEvent::eContextMenuKey);
2283   geckoEvent.mRefPoint = {};
2284   mGeckoChild->DispatchInputEvent(&geckoEvent);
2287 - (void)viewWillStartLiveResize {
2288   nsCocoaWindow* windowWidget =
2289       mGeckoChild ? mGeckoChild->GetAppWindowWidget() : nullptr;
2290   if (windowWidget) {
2291     windowWidget->NotifyLiveResizeStarted();
2292   }
2295 - (void)viewDidEndLiveResize {
2296   // mGeckoChild may legitimately be null here. It should also have been null
2297   // in viewWillStartLiveResize, so there's no problem. However if we run into
2298   // cases where the windowWidget was non-null in viewWillStartLiveResize but
2299   // is null here, that might be problematic because we might get stuck with
2300   // a content process that has the displayport suppressed. If that scenario
2301   // arises (I'm not sure that it does) we will need to handle it gracefully.
2302   nsCocoaWindow* windowWidget =
2303       mGeckoChild ? mGeckoChild->GetAppWindowWidget() : nullptr;
2304   if (windowWidget) {
2305     windowWidget->NotifyLiveResizeStopped();
2306   }
2309 - (void)markLayerForDisplay {
2310   MOZ_RELEASE_ASSERT(NS_IsMainThread());
2311   if (!mIsUpdatingLayer) {
2312     // This call will cause updateRootCALayer to be called during the upcoming
2313     // main thread CoreAnimation transaction. It will also trigger a transaction
2314     // if no transaction is currently pending.
2315     [mPixelHostingView.layer setNeedsDisplay];
2316   }
2319 - (void)ensureNextCompositeIsAtomicWithMainThreadPaint {
2320   MOZ_RELEASE_ASSERT(NS_IsMainThread());
2321   if (mGeckoChild) {
2322     mGeckoChild->SuspendAsyncCATransactions();
2323   }
2326 - (void)updateRootCALayer {
2327   if (NS_IsMainThread() && mGeckoChild) {
2328     MOZ_RELEASE_ASSERT(!mIsUpdatingLayer, "Re-entrant layer display?");
2329     mIsUpdatingLayer = YES;
2330     mGeckoChild->HandleMainThreadCATransaction();
2331     mIsUpdatingLayer = NO;
2332   }
2335 - (CALayer*)rootCALayer {
2336   return mRootCALayer;
2339 // If we've just created a non-native context menu, we need to mark it as
2340 // such and let the OS (and other programs) know when it opens and closes
2341 // (this is how the OS knows to close other programs' context menus when
2342 // ours open).  We send the initial notification here, but others are sent
2343 // in nsCocoaWindow::Show().
2344 - (void)maybeInitContextMenuTracking {
2345   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2347   if (mozilla::widget::NativeMenuSupport::ShouldUseNativeContextMenus()) {
2348     return;
2349   }
2351   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
2352   NS_ENSURE_TRUE_VOID(rollupListener);
2353   nsCOMPtr<nsIWidget> widget = rollupListener->GetRollupWidget();
2354   NS_ENSURE_TRUE_VOID(widget);
2356   NSWindow* popupWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
2357   if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]]) return;
2359   [[NSDistributedNotificationCenter defaultCenter]
2360       postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
2361                     object:@"org.mozilla.gecko.PopupWindow"];
2362   [(PopupWindow*)popupWindow setIsContextMenu:YES];
2364   NS_OBJC_END_TRY_IGNORE_BLOCK;
2367 // Returns true if the event should no longer be processed, false otherwise.
2368 // This does not return whether or not anything was rolled up.
2369 - (BOOL)maybeRollup:(NSEvent*)theEvent {
2370   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
2372   BOOL consumeEvent = NO;
2374   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
2375   NS_ENSURE_TRUE(rollupListener, false);
2377   BOOL isWheelTypeEvent = [theEvent type] == NSEventTypeScrollWheel ||
2378                           [theEvent type] == NSEventTypeMagnify ||
2379                           [theEvent type] == NSEventTypeSmartMagnify;
2381   if (!isWheelTypeEvent && rollupListener->RollupNativeMenu()) {
2382     // A native menu was rolled up.
2383     // Don't consume this event; if the menu wanted to consume this event it
2384     // would already have done so and we wouldn't even get here. For example, we
2385     // won't get here for left clicks that close native menus (because the
2386     // native menu consumes it), but we will get here for right clicks that
2387     // close native menus, and we do not want to consume those right clicks.
2388     return NO;
2389   }
2391   nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
2392   if (!rollupWidget) {
2393     return consumeEvent;
2394   }
2396   NSWindow* currentPopup =
2397       static_cast<NSWindow*>(rollupWidget->GetNativeData(NS_NATIVE_WINDOW));
2398   if (nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
2399     return consumeEvent;
2400   }
2402   // Check to see if scroll/zoom events should roll up the popup
2403   if (isWheelTypeEvent) {
2404     // consume scroll events that aren't over the popup unless the popup is an
2405     // arrow panel.
2406     consumeEvent = rollupListener->ShouldConsumeOnMouseWheelEvent();
2407     if (!rollupListener->ShouldRollupOnMouseWheelEvent()) {
2408       return consumeEvent;
2409     }
2410   }
2412   // if we're dealing with menus, we probably have submenus and
2413   // we don't want to rollup if the click is in a parent menu of
2414   // the current submenu
2415   uint32_t popupsToRollup = UINT32_MAX;
2416   AutoTArray<nsIWidget*, 5> widgetChain;
2417   uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
2418   for (uint32_t i = 0; i < widgetChain.Length(); i++) {
2419     nsIWidget* widget = widgetChain[i];
2420     NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
2421     if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
2422       // don't roll up if the mouse event occurred within a menu of the
2423       // same type. If the mouse event occurred in a menu higher than
2424       // that, roll up, but pass the number of popups to Rollup so
2425       // that only those of the same type close up.
2426       if (i < sameTypeCount) {
2427         return consumeEvent;
2428       }
2429       popupsToRollup = sameTypeCount;
2430       break;
2431     }
2432   }
2434   LayoutDeviceIntPoint devPoint;
2435   nsIRollupListener::RollupOptions rollupOptions{
2436       popupsToRollup, nsIRollupListener::FlushViews::Yes};
2437   if ([theEvent type] == NSEventTypeLeftMouseDown) {
2438     NSPoint point = [NSEvent mouseLocation];
2439     FlipCocoaScreenCoordinate(point);
2440     devPoint = mGeckoChild->CocoaPointsToDevPixels(point);
2441     rollupOptions.mPoint = &devPoint;
2442   }
2443   consumeEvent = (BOOL)rollupListener->Rollup(rollupOptions);
2444   return consumeEvent;
2446   NS_OBJC_END_TRY_BLOCK_RETURN(NO);
2449 - (void)swipeWithEvent:(NSEvent*)anEvent {
2450   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2452   if (!anEvent || !mGeckoChild) {
2453     return;
2454   }
2456   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2458   float deltaX = [anEvent deltaX];  // left=1.0, right=-1.0
2459   float deltaY = [anEvent deltaY];  // up=1.0, down=-1.0
2461   // Setup the "swipe" event.
2462   WidgetSimpleGestureEvent geckoEvent(true, eSwipeGesture, mGeckoChild);
2463   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2465   // Record the left/right direction.
2466   if (deltaX > 0.0)
2467     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_LEFT;
2468   else if (deltaX < 0.0)
2469     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT;
2471   // Record the up/down direction.
2472   if (deltaY > 0.0)
2473     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_UP;
2474   else if (deltaY < 0.0)
2475     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_DOWN;
2477   // Send the event.
2478   mGeckoChild->DispatchWindowEvent(geckoEvent);
2480   NS_OBJC_END_TRY_IGNORE_BLOCK;
2483 // Pinch zoom gesture.
2484 - (void)magnifyWithEvent:(NSEvent*)anEvent {
2485   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2487   if ([self maybeRollup:anEvent]) {
2488     return;
2489   }
2491   if (!mGeckoChild) {
2492     return;
2493   }
2495   // Instead of calling beginOrEndGestureForEventPhase we basically inline
2496   // the effects of it here, because that function doesn't play too well with
2497   // how we create PinchGestureInput events below. The main point of that
2498   // function is to avoid flip-flopping between rotation/magnify gestures, which
2499   // we can do by checking and setting mGestureState appropriately. A secondary
2500   // result of that function is to send the final eMagnifyGesture event when
2501   // the gesture ends, but APZ takes care of that for us.
2502   if (mGestureState == eGestureState_RotateGesture &&
2503       [anEvent phase] != NSEventPhaseBegan) {
2504     // If we're already in a rotation and not "starting" a magnify, abort.
2505     return;
2506   }
2507   mGestureState = eGestureState_MagnifyGesture;
2509   NSPoint locationInWindow =
2510       nsCocoaUtils::EventLocationForWindow(anEvent, [self window]);
2511   ScreenPoint position = ViewAs<ScreenPixel>(
2512       [self convertWindowCoordinatesRoundDown:locationInWindow],
2513       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
2514   ExternalPoint screenOffset = ViewAs<ExternalPixel>(
2515       mGeckoChild->WidgetToScreenOffset(),
2516       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
2518   TimeStamp eventTimeStamp =
2519       nsCocoaUtils::GetEventTimeStamp([anEvent timestamp]);
2520   NSEventPhase eventPhase = [anEvent phase];
2521   PinchGestureInput::PinchGestureType pinchGestureType;
2523   switch (eventPhase) {
2524     case NSEventPhaseBegan: {
2525       pinchGestureType = PinchGestureInput::PINCHGESTURE_START;
2526       break;
2527     }
2528     case NSEventPhaseChanged: {
2529       pinchGestureType = PinchGestureInput::PINCHGESTURE_SCALE;
2530       break;
2531     }
2532     case NSEventPhaseEnded: {
2533       pinchGestureType = PinchGestureInput::PINCHGESTURE_END;
2534       mGestureState = eGestureState_None;
2535       break;
2536     }
2537     default: {
2538       NS_WARNING("Unexpected phase for pinch gesture event.");
2539       return;
2540     }
2541   }
2543   PinchGestureInput event{pinchGestureType,
2544                           PinchGestureInput::TRACKPAD,
2545                           eventTimeStamp,
2546                           screenOffset,
2547                           position,
2548                           100.0,
2549                           100.0 * (1.0 - [anEvent magnification]),
2550                           nsCocoaUtils::ModifiersForEvent(anEvent)};
2552   mGeckoChild->DispatchAPZInputEvent(event);
2554   NS_OBJC_END_TRY_IGNORE_BLOCK;
2557 // Smart zoom gesture, i.e. two-finger double tap on trackpads.
2558 - (void)smartMagnifyWithEvent:(NSEvent*)anEvent {
2559   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2561   if (!anEvent || !mGeckoChild ||
2562       [self beginOrEndGestureForEventPhase:anEvent]) {
2563     return;
2564   }
2566   if ([self maybeRollup:anEvent]) {
2567     return;
2568   }
2570   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2572   if (StaticPrefs::apz_mac_enable_double_tap_zoom_touchpad_gesture()) {
2573     TimeStamp eventTimeStamp =
2574         nsCocoaUtils::GetEventTimeStamp([anEvent timestamp]);
2575     NSPoint locationInWindow =
2576         nsCocoaUtils::EventLocationForWindow(anEvent, [self window]);
2577     LayoutDevicePoint position =
2578         [self convertWindowCoordinatesRoundDown:locationInWindow];
2580     mGeckoChild->DispatchDoubleTapGesture(
2581         eventTimeStamp, RoundedToInt(position),
2582         nsCocoaUtils::ModifiersForEvent(anEvent));
2583   } else {
2584     // Setup the "double tap" event.
2585     WidgetSimpleGestureEvent geckoEvent(true, eTapGesture, mGeckoChild);
2586     [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2587     geckoEvent.mClickCount = 1;
2589     // Send the event.
2590     mGeckoChild->DispatchWindowEvent(geckoEvent);
2591   }
2593   // Clear the gesture state
2594   mGestureState = eGestureState_None;
2596   NS_OBJC_END_TRY_IGNORE_BLOCK;
2599 - (void)rotateWithEvent:(NSEvent*)anEvent {
2600   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2602   if (!anEvent || !mGeckoChild ||
2603       [self beginOrEndGestureForEventPhase:anEvent]) {
2604     return;
2605   }
2607   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2609   float rotation = [anEvent rotation];
2611   EventMessage msg;
2612   switch (mGestureState) {
2613     case eGestureState_StartGesture:
2614       msg = eRotateGestureStart;
2615       mGestureState = eGestureState_RotateGesture;
2616       break;
2618     case eGestureState_RotateGesture:
2619       msg = eRotateGestureUpdate;
2620       break;
2622     case eGestureState_None:
2623     case eGestureState_MagnifyGesture:
2624     default:
2625       return;
2626   }
2628   // Setup the event.
2629   WidgetSimpleGestureEvent geckoEvent(true, msg, mGeckoChild);
2630   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2631   geckoEvent.mDelta = -rotation;
2632   if (rotation > 0.0) {
2633     geckoEvent.mDirection =
2634         dom::SimpleGestureEvent_Binding::ROTATION_COUNTERCLOCKWISE;
2635   } else {
2636     geckoEvent.mDirection = dom::SimpleGestureEvent_Binding::ROTATION_CLOCKWISE;
2637   }
2639   // Send the event.
2640   mGeckoChild->DispatchWindowEvent(geckoEvent);
2642   // Keep track of the cumulative rotation for the final "rotate" event.
2643   mCumulativeRotation += rotation;
2645   NS_OBJC_END_TRY_IGNORE_BLOCK;
2648 // `beginGestureWithEvent` and `endGestureWithEvent` are not called for
2649 // applications that link against the macOS 10.11 or later SDK when we're
2650 // running on macOS 10.11 or later. For compatibility with all supported macOS
2651 // versions, we have to call {begin,end}GestureWithEvent ourselves based on
2652 // the event phase when we're handling gestures.
2653 - (bool)beginOrEndGestureForEventPhase:(NSEvent*)aEvent {
2654   if (!aEvent) {
2655     return false;
2656   }
2658   if (aEvent.phase == NSEventPhaseBegan) {
2659     [self beginGestureWithEvent:aEvent];
2660     return true;
2661   }
2663   if (aEvent.phase == NSEventPhaseEnded ||
2664       aEvent.phase == NSEventPhaseCancelled) {
2665     [self endGestureWithEvent:aEvent];
2666     return true;
2667   }
2669   return false;
2672 - (void)beginGestureWithEvent:(NSEvent*)aEvent {
2673   if (!aEvent) {
2674     return;
2675   }
2677   mGestureState = eGestureState_StartGesture;
2678   mCumulativeRotation = 0.0;
2681 - (void)endGestureWithEvent:(NSEvent*)anEvent {
2682   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2684   if (!anEvent || !mGeckoChild) {
2685     // Clear the gestures state if we cannot send an event.
2686     mGestureState = eGestureState_None;
2687     mCumulativeRotation = 0.0;
2688     return;
2689   }
2691   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2693   switch (mGestureState) {
2694     case eGestureState_RotateGesture: {
2695       // Setup the "rotate" event.
2696       WidgetSimpleGestureEvent geckoEvent(true, eRotateGesture, mGeckoChild);
2697       [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2698       geckoEvent.mDelta = -mCumulativeRotation;
2699       if (mCumulativeRotation > 0.0) {
2700         geckoEvent.mDirection =
2701             dom::SimpleGestureEvent_Binding::ROTATION_COUNTERCLOCKWISE;
2702       } else {
2703         geckoEvent.mDirection =
2704             dom::SimpleGestureEvent_Binding::ROTATION_CLOCKWISE;
2705       }
2707       // Send the event.
2708       mGeckoChild->DispatchWindowEvent(geckoEvent);
2709     } break;
2711     case eGestureState_MagnifyGesture:  // APZ handles sending the widget events
2712     case eGestureState_None:
2713     case eGestureState_StartGesture:
2714     default:
2715       break;
2716   }
2718   // Clear the gestures state.
2719   mGestureState = eGestureState_None;
2720   mCumulativeRotation = 0.0;
2722   NS_OBJC_END_TRY_IGNORE_BLOCK;
2725 - (void)setUsingOMTCompositor:(BOOL)aUseOMTC {
2726   mUsingOMTCompositor = aUseOMTC;
2729 // Returning NO from this method only disallows ordering on mousedown - in order
2730 // to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering]
2731 // when handling the mousedown event.
2732 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)aEvent {
2733   // Always using system-provided window ordering for normal windows.
2734   if (![[self window] isKindOfClass:[PopupWindow class]]) return NO;
2736   // Don't reorder when we don't have a parent window, like when we're a
2737   // context menu or a tooltip.
2738   return ![[self window] parentWindow];
2741 - (void)mouseDown:(NSEvent*)theEvent {
2742   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2743   mPerformedDrag = NO;
2745   if ([self shouldDelayWindowOrderingForEvent:theEvent]) {
2746     [NSApp preventWindowOrdering];
2747   }
2749   // If we've already seen this event due to direct dispatch from menuForEvent:
2750   // just bail; if not, remember it.
2751   if (mLastMouseDownEvent == theEvent) {
2752     [mLastMouseDownEvent release];
2753     mLastMouseDownEvent = nil;
2754     return;
2755   } else {
2756     [mLastMouseDownEvent release];
2757     mLastMouseDownEvent = [theEvent retain];
2758   }
2760   [gLastDragMouseDownEvent release];
2761   gLastDragMouseDownEvent = [theEvent retain];
2762   gLastDragView = self;
2764   // We need isClickThrough because at this point the window we're in might
2765   // already have become main, so the check for isMainWindow in
2766   // WindowAcceptsEvent isn't enough. It also has to check isClickThrough.
2767   BOOL isClickThrough = (theEvent == mClickThroughMouseDownEvent);
2768   [mClickThroughMouseDownEvent release];
2769   mClickThroughMouseDownEvent = nil;
2771   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2773   if ([self maybeRollup:theEvent] ||
2774       !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self,
2775                                                  isClickThrough)) {
2776     // Remember blocking because that means we want to block mouseup as well.
2777     mBlockedLastMouseDown = YES;
2778     return;
2779   }
2781   // in order to send gecko events we'll need a gecko widget
2782   if (!mGeckoChild) return;
2783   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2784     return;
2785   }
2787   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild,
2788                               WidgetMouseEvent::eReal);
2789   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2791   NSInteger clickCount = [theEvent clickCount];
2792   if (mBlockedLastMouseDown && clickCount > 1) {
2793     // Don't send a double click if the first click of the double click was
2794     // blocked.
2795     clickCount--;
2796   }
2797   geckoEvent.mClickCount = clickCount;
2799   if (!StaticPrefs::dom_event_treat_ctrl_click_as_right_click_disabled() &&
2800       geckoEvent.IsControl()) {
2801     geckoEvent.mButton = MouseButton::eSecondary;
2802   } else {
2803     geckoEvent.mButton = MouseButton::ePrimary;
2804     // Don't send a click if ctrl key is pressed.
2805     geckoEvent.mClickEventPrevented = geckoEvent.IsControl();
2806   }
2808   mGeckoChild->DispatchInputEvent(&geckoEvent);
2809   mBlockedLastMouseDown = NO;
2811   // XXX maybe call markedTextSelectionChanged:client: here?
2813   NS_OBJC_END_TRY_IGNORE_BLOCK;
2816 - (void)mouseUp:(NSEvent*)theEvent {
2817   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2819   gLastDragView = nil;
2821   if (!mGeckoChild || mBlockedLastMouseDown || mPerformedDrag) {
2822     // There is case that mouseUp event will be fired right after DnD on OSX. As
2823     // mPerformedDrag will be YES at end of DnD processing, ignore this mouseUp
2824     // event fired right after DnD.
2825     return;
2826   }
2828   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2829     return;
2830   }
2832   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2834   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild,
2835                               WidgetMouseEvent::eReal);
2836   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2838   if (!StaticPrefs::dom_event_treat_ctrl_click_as_right_click_disabled() &&
2839       ([theEvent modifierFlags] & NSEventModifierFlagControl)) {
2840     geckoEvent.mButton = MouseButton::eSecondary;
2841   } else {
2842     geckoEvent.mButton = MouseButton::ePrimary;
2843   }
2845   // Remember the event's position before calling DispatchInputEvent, because
2846   // that call can mutate it and convert it into a different coordinate space.
2847   LayoutDeviceIntPoint pos = geckoEvent.mRefPoint;
2849   // This might destroy our widget (and null out mGeckoChild).
2850   bool defaultPrevented =
2851       (mGeckoChild->DispatchInputEvent(&geckoEvent).mContentStatus ==
2852        nsEventStatus_eConsumeNoDefault);
2854   if (!mGeckoChild) {
2855     return;
2856   }
2858   // Check to see if we are double-clicking in draggable parts of the window.
2859   if (!defaultPrevented && [theEvent clickCount] == 2 &&
2860       !mGeckoChild->GetNonDraggableRegion().Contains(pos.x, pos.y)) {
2861     if (nsCocoaUtils::ShouldZoomOnTitlebarDoubleClick()) {
2862       [[self window] performZoom:nil];
2863     } else if (nsCocoaUtils::ShouldMinimizeOnTitlebarDoubleClick()) {
2864       [[self window] performMiniaturize:nil];
2865     }
2866   }
2868   NS_OBJC_END_TRY_IGNORE_BLOCK;
2871 - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
2872                             enter:(BOOL)aEnter
2873                          exitFrom:(WidgetMouseEvent::ExitFrom)aExitFrom {
2874   if (!mGeckoChild) return;
2876   NSPoint windowEventLocation =
2877       nsCocoaUtils::EventLocationForWindow(aEvent, [self window]);
2878   NSPoint localEventLocation = [self convertPoint:windowEventLocation
2879                                          fromView:nil];
2881   EventMessage msg = aEnter ? eMouseEnterIntoWidget : eMouseExitFromWidget;
2882   WidgetMouseEvent event(true, msg, mGeckoChild, WidgetMouseEvent::eReal);
2883   event.mRefPoint = mGeckoChild->CocoaPointsToDevPixels(localEventLocation);
2884   if (event.mMessage == eMouseExitFromWidget) {
2885     event.mExitFrom = Some(aExitFrom);
2886   }
2887   nsEventStatus status;  // ignored
2888   mGeckoChild->DispatchEvent(&event, status);
2891 - (void)handleMouseMoved:(NSEvent*)theEvent {
2892   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2894   if (!mGeckoChild) return;
2895   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2896     return;
2897   }
2899   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
2900                               WidgetMouseEvent::eReal);
2901   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2903   mGeckoChild->DispatchInputEvent(&geckoEvent);
2905   NS_OBJC_END_TRY_IGNORE_BLOCK;
2908 - (void)mouseDragged:(NSEvent*)theEvent {
2909   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2911   if (!mGeckoChild) return;
2912   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2913     return;
2914   }
2916   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
2917                               WidgetMouseEvent::eReal);
2918   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2920   mGeckoChild->DispatchInputEvent(&geckoEvent);
2922   // Note, sending the above event might have destroyed our widget since we
2923   // didn't retain. Fine so long as we don't access any local variables from
2924   // here on.
2926   // XXX maybe call markedTextSelectionChanged:client: here?
2928   NS_OBJC_END_TRY_IGNORE_BLOCK;
2931 - (void)rightMouseDown:(NSEvent*)theEvent {
2932   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2933   mPerformedDrag = NO;
2935   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2937   [self maybeRollup:theEvent];
2938   if (!mGeckoChild) return;
2939   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2940     return;
2941   }
2943   // The right mouse went down, fire off a right mouse down event to gecko
2944   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild,
2945                               WidgetMouseEvent::eReal);
2946   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2947   geckoEvent.mButton = MouseButton::eSecondary;
2948   geckoEvent.mClickCount = [theEvent clickCount];
2950   nsIWidget::ContentAndAPZEventStatus eventStatus =
2951       mGeckoChild->DispatchInputEvent(&geckoEvent);
2952   if (!mGeckoChild) return;
2954   if (!StaticPrefs::ui_context_menus_after_mouseup() &&
2955       eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
2956     // Let the superclass do the context menu stuff.
2957     [super rightMouseDown:theEvent];
2958   }
2960   NS_OBJC_END_TRY_IGNORE_BLOCK;
2963 - (void)rightMouseUp:(NSEvent*)theEvent {
2964   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2966   if (!mGeckoChild) return;
2967   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2968     return;
2969   }
2971   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild,
2972                               WidgetMouseEvent::eReal);
2973   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2974   geckoEvent.mButton = MouseButton::eSecondary;
2975   geckoEvent.mClickCount = [theEvent clickCount];
2977   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2978   nsIWidget::ContentAndAPZEventStatus eventStatus =
2979       mGeckoChild->DispatchInputEvent(&geckoEvent);
2980   if (!mGeckoChild) return;
2982   if (StaticPrefs::ui_context_menus_after_mouseup() &&
2983       eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
2984     // Let the superclass do the context menu stuff, but pretend it's
2985     // rightMouseDown.
2986     NSEvent* dupeEvent = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown
2987                                             location:theEvent.locationInWindow
2988                                        modifierFlags:theEvent.modifierFlags
2989                                            timestamp:theEvent.timestamp
2990                                         windowNumber:theEvent.windowNumber
2991                                              context:nil
2992                                          eventNumber:theEvent.eventNumber
2993                                           clickCount:theEvent.clickCount
2994                                             pressure:theEvent.pressure];
2996     [super rightMouseDown:dupeEvent];
2997   }
2999   NS_OBJC_END_TRY_IGNORE_BLOCK;
3002 - (void)rightMouseDragged:(NSEvent*)theEvent {
3003   if (!mGeckoChild) return;
3004   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3005     return;
3006   }
3008   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
3009                               WidgetMouseEvent::eReal);
3010   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3011   geckoEvent.mButton = MouseButton::eSecondary;
3013   // send event into Gecko by going directly to the
3014   // the widget.
3015   mGeckoChild->DispatchInputEvent(&geckoEvent);
3018 static bool ShouldDispatchBackForwardCommandForMouseButton(int16_t aButton) {
3019   return (aButton == MouseButton::eX1 &&
3020           Preferences::GetBool("mousebutton.4th.enabled", true)) ||
3021          (aButton == MouseButton::eX2 &&
3022           Preferences::GetBool("mousebutton.5th.enabled", true));
3025 - (void)otherMouseDown:(NSEvent*)theEvent {
3026   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3027   mPerformedDrag = NO;
3029   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3031   if ([self maybeRollup:theEvent] ||
3032       !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self))
3033     return;
3035   if (!mGeckoChild) return;
3036   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3037     return;
3038   }
3040   int16_t button = nsCocoaUtils::ButtonForEvent(theEvent);
3041   if (ShouldDispatchBackForwardCommandForMouseButton(button)) {
3042     WidgetCommandEvent appCommandEvent(
3043         true,
3044         (button == MouseButton::eX2) ? nsGkAtoms::Forward : nsGkAtoms::Back,
3045         mGeckoChild);
3046     mGeckoChild->DispatchWindowEvent(appCommandEvent);
3047     return;
3048   }
3050   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild,
3051                               WidgetMouseEvent::eReal);
3052   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3053   geckoEvent.mButton = button;
3054   geckoEvent.mClickCount = [theEvent clickCount];
3056   mGeckoChild->DispatchInputEvent(&geckoEvent);
3058   NS_OBJC_END_TRY_IGNORE_BLOCK;
3061 - (void)otherMouseUp:(NSEvent*)theEvent {
3062   if (!mGeckoChild) return;
3063   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3064     return;
3065   }
3067   int16_t button = nsCocoaUtils::ButtonForEvent(theEvent);
3068   if (ShouldDispatchBackForwardCommandForMouseButton(button)) {
3069     return;
3070   }
3072   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild,
3073                               WidgetMouseEvent::eReal);
3074   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3075   geckoEvent.mButton = button;
3077   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3078   mGeckoChild->DispatchInputEvent(&geckoEvent);
3081 - (void)otherMouseDragged:(NSEvent*)theEvent {
3082   if (!mGeckoChild) return;
3083   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3084     return;
3085   }
3087   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
3088                               WidgetMouseEvent::eReal);
3089   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3090   int16_t button = nsCocoaUtils::ButtonForEvent(theEvent);
3091   geckoEvent.mButton = button;
3093   // send event into Gecko by going directly to the
3094   // the widget.
3095   mGeckoChild->DispatchInputEvent(&geckoEvent);
3098 - (void)sendWheelStartOrStop:(EventMessage)msg forEvent:(NSEvent*)theEvent {
3099   WidgetWheelEvent wheelEvent(true, msg, mGeckoChild);
3100   [self convertCocoaMouseWheelEvent:theEvent toGeckoEvent:&wheelEvent];
3101   mExpectingWheelStop = (msg == eWheelOperationStart);
3102   mGeckoChild->DispatchInputEvent(wheelEvent.AsInputEvent());
3105 - (void)sendWheelCondition:(BOOL)condition
3106                      first:(EventMessage)first
3107                     second:(EventMessage)second
3108                   forEvent:(NSEvent*)theEvent {
3109   if (mExpectingWheelStop == condition) {
3110     [self sendWheelStartOrStop:first forEvent:theEvent];
3111   }
3112   [self sendWheelStartOrStop:second forEvent:theEvent];
3115 static int32_t RoundUp(double aDouble) {
3116   return aDouble < 0 ? static_cast<int32_t>(floor(aDouble))
3117                      : static_cast<int32_t>(ceil(aDouble));
3120 static gfx::IntPoint GetIntegerDeltaForEvent(NSEvent* aEvent) {
3121   if ([aEvent hasPreciseScrollingDeltas]) {
3122     // Pixel scroll events (events with hasPreciseScrollingDeltas == YES)
3123     // carry pixel deltas in the scrollingDeltaX/Y fields and line scroll
3124     // information in the deltaX/Y fields.
3125     // Prior to 10.12, these line scroll fields would be zero for most pixel
3126     // scroll events and non-zero for some, whenever at least a full line
3127     // worth of pixel scrolling had accumulated. That's the behavior we want.
3128     // Starting with 10.12 however, pixel scroll events no longer accumulate
3129     // deltaX and deltaY; they just report floating point values for every
3130     // single event. So we need to do our own accumulation.
3131     return PanGestureInput::GetIntegerDeltaForEvent(
3132         [aEvent phase] == NSEventPhaseBegan, [aEvent deltaX], [aEvent deltaY]);
3133   }
3135   // For line scrolls, or pre-10.12, just use the rounded up value of deltaX /
3136   // deltaY.
3137   return gfx::IntPoint(RoundUp([aEvent deltaX]), RoundUp([aEvent deltaY]));
3140 - (void)scrollWheel:(NSEvent*)theEvent {
3141   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3143   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3145   ChildViewMouseTracker::MouseScrolled(theEvent);
3147   if ([self maybeRollup:theEvent]) {
3148     return;
3149   }
3151   if (!mGeckoChild) {
3152     return;
3153   }
3155   NSEventPhase phase = [theEvent phase];
3156   // Fire eWheelOperationStart/End events when 2 fingers touch/release the
3157   // touchpad.
3158   if (phase & NSEventPhaseMayBegin) {
3159     [self sendWheelCondition:YES
3160                        first:eWheelOperationEnd
3161                       second:eWheelOperationStart
3162                     forEvent:theEvent];
3163   } else if (phase & (NSEventPhaseEnded | NSEventPhaseCancelled)) {
3164     [self sendWheelCondition:NO
3165                        first:eWheelOperationStart
3166                       second:eWheelOperationEnd
3167                     forEvent:theEvent];
3168   }
3170   if (!mGeckoChild) {
3171     return;
3172   }
3173   RefPtr<nsChildView> geckoChildDeathGrip(mGeckoChild);
3175   NSPoint locationInWindow =
3176       nsCocoaUtils::EventLocationForWindow(theEvent, [self window]);
3178   // Use convertWindowCoordinatesRoundDown when converting the position to
3179   // integer screen pixels in order to ensure that coordinates which are just
3180   // inside the right / bottom edges of the window don't end up outside of the
3181   // window after rounding.
3182   ScreenPoint position = ViewAs<ScreenPixel>(
3183       [self convertWindowCoordinatesRoundDown:locationInWindow],
3184       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
3186   bool usePreciseDeltas =
3187       [theEvent hasPreciseScrollingDeltas] &&
3188       Preferences::GetBool("mousewheel.enable_pixel_scrolling", true);
3189   bool hasPhaseInformation = nsCocoaUtils::EventHasPhaseInformation(theEvent);
3191   gfx::IntPoint lineOrPageDelta = -GetIntegerDeltaForEvent(theEvent);
3193   Modifiers modifiers = nsCocoaUtils::ModifiersForEvent(theEvent);
3195   TimeStamp eventTimeStamp =
3196       nsCocoaUtils::GetEventTimeStamp([theEvent timestamp]);
3198   ScreenPoint preciseDelta;
3199   if (usePreciseDeltas) {
3200     CGFloat pixelDeltaX = [theEvent scrollingDeltaX];
3201     CGFloat pixelDeltaY = [theEvent scrollingDeltaY];
3202     double scale = geckoChildDeathGrip->BackingScaleFactor();
3203     preciseDelta = ScreenPoint(-pixelDeltaX * scale, -pixelDeltaY * scale);
3204   }
3206   if (usePreciseDeltas && hasPhaseInformation) {
3207     PanGestureInput panEvent = nsCocoaUtils::CreatePanGestureEvent(
3208         theEvent, eventTimeStamp, position, preciseDelta, lineOrPageDelta,
3209         modifiers);
3211     geckoChildDeathGrip->DispatchAPZWheelInputEvent(panEvent);
3212   } else if (usePreciseDeltas) {
3213     // This is on 10.6 or old touchpads that don't have any phase information.
3214     ScrollWheelInput wheelEvent(eventTimeStamp, modifiers,
3215                                 ScrollWheelInput::SCROLLMODE_INSTANT,
3216                                 ScrollWheelInput::SCROLLDELTA_PIXEL, position,
3217                                 preciseDelta.x, preciseDelta.y, false,
3218                                 // This parameter is used for wheel delta
3219                                 // adjustment, such as auto-dir scrolling,
3220                                 // but we do't need to do anything special here
3221                                 // since this wheel event is sent to
3222                                 // DispatchAPZWheelInputEvent, which turns this
3223                                 // ScrollWheelInput back into a WidgetWheelEvent
3224                                 // and then it goes through the regular handling
3225                                 // in APZInputBridge. So passing |eNone| won't
3226                                 // pass up the necessary wheel delta adjustment.
3227                                 WheelDeltaAdjustmentStrategy::eNone);
3228     wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x;
3229     wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y;
3230     wheelEvent.mIsMomentum = nsCocoaUtils::IsMomentumScrollEvent(theEvent);
3231     geckoChildDeathGrip->DispatchAPZWheelInputEvent(wheelEvent);
3232   } else {
3233     ScrollWheelInput::ScrollMode scrollMode =
3234         ScrollWheelInput::SCROLLMODE_INSTANT;
3235     if (nsLayoutUtils::IsSmoothScrollingEnabled() &&
3236         StaticPrefs::general_smoothScroll_mouseWheel()) {
3237       scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
3238     }
3239     ScrollWheelInput wheelEvent(eventTimeStamp, modifiers, scrollMode,
3240                                 ScrollWheelInput::SCROLLDELTA_LINE, position,
3241                                 lineOrPageDelta.x, lineOrPageDelta.y, false,
3242                                 // This parameter is used for wheel delta
3243                                 // adjustment, such as auto-dir scrolling,
3244                                 // but we do't need to do anything special here
3245                                 // since this wheel event is sent to
3246                                 // DispatchAPZWheelInputEvent, which turns this
3247                                 // ScrollWheelInput back into a WidgetWheelEvent
3248                                 // and then it goes through the regular handling
3249                                 // in APZInputBridge. So passing |eNone| won't
3250                                 // pass up the necessary wheel delta adjustment.
3251                                 WheelDeltaAdjustmentStrategy::eNone);
3252     wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x;
3253     wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y;
3254     geckoChildDeathGrip->DispatchAPZWheelInputEvent(wheelEvent);
3255   }
3257   NS_OBJC_END_TRY_IGNORE_BLOCK;
3260 - (NSMenu*)menuForEvent:(NSEvent*)theEvent {
3261   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3263   if (!mGeckoChild) return nil;
3265   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3267   [self maybeRollup:theEvent];
3268   if (!mGeckoChild) return nil;
3270   // Cocoa doesn't always dispatch a mouseDown: for a control-click event,
3271   // depends on what we return from menuForEvent:. Gecko always expects one
3272   // and expects the mouse down event before the context menu event, so
3273   // get that event sent first if this is a left mouse click.
3274   if ([theEvent type] == NSEventTypeLeftMouseDown) {
3275     [self mouseDown:theEvent];
3276     if (!mGeckoChild) return nil;
3277   }
3279   WidgetPointerEvent geckoEvent(true, eContextMenu, mGeckoChild);
3280   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3281   if (StaticPrefs::dom_event_treat_ctrl_click_as_right_click_disabled() &&
3282       [theEvent type] == NSEventTypeLeftMouseDown) {
3283     geckoEvent.mContextMenuTrigger = WidgetMouseEvent::eControlClick;
3284     geckoEvent.mButton = MouseButton::ePrimary;
3285   } else {
3286     geckoEvent.mButton = MouseButton::eSecondary;
3287   }
3289   mGeckoChild->DispatchInputEvent(&geckoEvent);
3290   if (!mGeckoChild) return nil;
3292   [self maybeInitContextMenuTracking];
3294   // We never return an actual NSMenu* for the context menu. Gecko might have
3295   // responded to the eContextMenu event by putting up a fake context menu.
3296   return nil;
3298   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
3301 - (void)willOpenMenu:(NSMenu*)aMenu withEvent:(NSEvent*)aEvent {
3302   ChildViewMouseTracker::NativeMenuOpened();
3305 - (void)didCloseMenu:(NSMenu*)aMenu withEvent:(NSEvent*)aEvent {
3306   ChildViewMouseTracker::NativeMenuClosed();
3309 - (void)convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
3310                        toGeckoEvent:(WidgetWheelEvent*)outWheelEvent {
3311   [self convertCocoaMouseEvent:aMouseEvent toGeckoEvent:outWheelEvent];
3313   bool usePreciseDeltas =
3314       [aMouseEvent hasPreciseScrollingDeltas] &&
3315       Preferences::GetBool("mousewheel.enable_pixel_scrolling", true);
3317   outWheelEvent->mDeltaMode = usePreciseDeltas
3318                                   ? dom::WheelEvent_Binding::DOM_DELTA_PIXEL
3319                                   : dom::WheelEvent_Binding::DOM_DELTA_LINE;
3320   outWheelEvent->mIsMomentum = nsCocoaUtils::IsMomentumScrollEvent(aMouseEvent);
3323 - (void)convertCocoaMouseEvent:(NSEvent*)aMouseEvent
3324                   toGeckoEvent:(WidgetInputEvent*)outGeckoEvent {
3325   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3327   NS_ASSERTION(
3328       outGeckoEvent,
3329       "convertCocoaMouseEvent:toGeckoEvent: requires non-null aoutGeckoEvent");
3330   if (!outGeckoEvent) return;
3332   nsCocoaUtils::InitInputEvent(*outGeckoEvent, aMouseEvent);
3334   // convert point to view coordinate system
3335   NSPoint locationInWindow =
3336       nsCocoaUtils::EventLocationForWindow(aMouseEvent, [self window]);
3338   outGeckoEvent->mRefPoint = [self convertWindowCoordinates:locationInWindow];
3340   WidgetMouseEventBase* mouseEvent = outGeckoEvent->AsMouseEventBase();
3341   mouseEvent->mButtons = 0;
3342   NSUInteger mouseButtons = [NSEvent pressedMouseButtons];
3344   if (mouseButtons & 0x01) {
3345     mouseEvent->mButtons |= MouseButtonsFlag::ePrimaryFlag;
3346   }
3347   if (mouseButtons & 0x02) {
3348     mouseEvent->mButtons |= MouseButtonsFlag::eSecondaryFlag;
3349   }
3350   if (mouseButtons & 0x04) {
3351     mouseEvent->mButtons |= MouseButtonsFlag::eMiddleFlag;
3352   }
3353   if (mouseButtons & 0x08) {
3354     mouseEvent->mButtons |= MouseButtonsFlag::e4thFlag;
3355   }
3356   if (mouseButtons & 0x10) {
3357     mouseEvent->mButtons |= MouseButtonsFlag::e5thFlag;
3358   }
3360   switch ([aMouseEvent type]) {
3361     case NSEventTypeLeftMouseDown:
3362     case NSEventTypeLeftMouseUp:
3363     case NSEventTypeLeftMouseDragged:
3364     case NSEventTypeRightMouseDown:
3365     case NSEventTypeRightMouseUp:
3366     case NSEventTypeRightMouseDragged:
3367     case NSEventTypeOtherMouseDown:
3368     case NSEventTypeOtherMouseUp:
3369     case NSEventTypeOtherMouseDragged:
3370     case NSEventTypeMouseMoved:
3371       if ([aMouseEvent subtype] == NSEventSubtypeTabletPoint) {
3372         [self convertCocoaTabletPointerEvent:aMouseEvent
3373                                 toGeckoEvent:mouseEvent->AsMouseEvent()];
3374       }
3375       break;
3377     default:
3378       // Don't check other NSEvents for pressure.
3379       break;
3380   }
3382   NS_OBJC_END_TRY_IGNORE_BLOCK;
3385 - (void)convertCocoaTabletPointerEvent:(NSEvent*)aPointerEvent
3386                           toGeckoEvent:(WidgetMouseEvent*)aOutGeckoEvent {
3387   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
3388   if (!aOutGeckoEvent || !sIsTabletPointerActivated) {
3389     return;
3390   }
3391   if ([aPointerEvent type] != NSEventTypeMouseMoved) {
3392     aOutGeckoEvent->mPressure = [aPointerEvent pressure];
3393     MOZ_ASSERT(aOutGeckoEvent->mPressure >= 0.0 &&
3394                aOutGeckoEvent->mPressure <= 1.0);
3395   }
3396   aOutGeckoEvent->mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
3397   aOutGeckoEvent->tiltX = (int32_t)lround([aPointerEvent tilt].x * 90);
3398   aOutGeckoEvent->tiltY = (int32_t)lround([aPointerEvent tilt].y * 90);
3399   aOutGeckoEvent->tangentialPressure = [aPointerEvent tangentialPressure];
3400   // Make sure the twist value is in the range of 0-359.
3401   int32_t twist = (int32_t)fmod([aPointerEvent rotation], 360);
3402   aOutGeckoEvent->twist = twist >= 0 ? twist : twist + 360;
3403   NS_OBJC_END_TRY_IGNORE_BLOCK;
3406 - (void)tabletProximity:(NSEvent*)theEvent {
3407   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
3408   sIsTabletPointerActivated = [theEvent isEnteringProximity];
3409   NS_OBJC_END_TRY_IGNORE_BLOCK
3412 #pragma mark -
3413 // NSTextInputClient implementation
3415 - (NSRange)markedRange {
3416   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3418   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0));
3419   return mTextInputHandler->MarkedRange();
3421   NS_OBJC_END_TRY_BLOCK_RETURN(NSMakeRange(0, 0));
3424 - (NSRange)selectedRange {
3425   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3427   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0));
3428   return mTextInputHandler->SelectedRange();
3430   NS_OBJC_END_TRY_BLOCK_RETURN(NSMakeRange(0, 0));
3433 - (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex {
3434   NS_ENSURE_TRUE(mTextInputHandler, NO);
3435   if (charIndex == NSNotFound) {
3436     return NO;
3437   }
3438   return mTextInputHandler->DrawsVerticallyForCharacterAtIndex(charIndex);
3441 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
3442   NS_ENSURE_TRUE(mTextInputHandler, 0);
3443   return mTextInputHandler->CharacterIndexForPoint(thePoint);
3446 - (NSArray*)validAttributesForMarkedText {
3447   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3449   NS_ENSURE_TRUE(mTextInputHandler, [NSArray array]);
3450   return mTextInputHandler->GetValidAttributesForMarkedText();
3452   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
3455 - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
3456   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3458   NS_ENSURE_TRUE_VOID(mGeckoChild);
3460   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3462   NSString* str;
3463   if ([aString isKindOfClass:[NSAttributedString class]]) {
3464     str = [aString string];
3465   } else {
3466     str = aString;
3467   }
3469   mTextInputHandler->InsertText(str, &replacementRange);
3471   NS_OBJC_END_TRY_IGNORE_BLOCK;
3474 - (void)doCommandBySelector:(SEL)aSelector {
3475   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3477   if (!mGeckoChild || !mTextInputHandler) {
3478     return;
3479   }
3481   const char* sel = reinterpret_cast<const char*>(aSelector);
3482   if (!mTextInputHandler->DoCommandBySelector(sel)) {
3483     [super doCommandBySelector:aSelector];
3484   }
3486   NS_OBJC_END_TRY_IGNORE_BLOCK;
3489 - (void)unmarkText {
3490   NS_ENSURE_TRUE_VOID(mTextInputHandler);
3491   mTextInputHandler->CommitIMEComposition();
3494 - (BOOL)hasMarkedText {
3495   NS_ENSURE_TRUE(mTextInputHandler, NO);
3496   return mTextInputHandler->HasMarkedText();
3499 - (void)setMarkedText:(id)aString
3500         selectedRange:(NSRange)selectedRange
3501      replacementRange:(NSRange)replacementRange {
3502   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3504   NS_ENSURE_TRUE_VOID(mTextInputHandler);
3506   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3508   NSAttributedString* attrStr;
3509   if ([aString isKindOfClass:[NSAttributedString class]]) {
3510     attrStr = static_cast<NSAttributedString*>(aString);
3511   } else {
3512     attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
3513   }
3515   mTextInputHandler->SetMarkedText(attrStr, selectedRange, &replacementRange);
3517   NS_OBJC_END_TRY_IGNORE_BLOCK;
3520 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange
3521                                                actualRange:
3522                                                    (NSRangePointer)actualRange {
3523   NS_ENSURE_TRUE(mTextInputHandler, nil);
3524   return mTextInputHandler->GetAttributedSubstringFromRange(aRange,
3525                                                             actualRange);
3528 - (NSRect)firstRectForCharacterRange:(NSRange)aRange
3529                          actualRange:(NSRangePointer)actualRange {
3530   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRect(0.0, 0.0, 0.0, 0.0));
3531   return mTextInputHandler->FirstRectForCharacterRange(aRange, actualRange);
3534 - (void)quickLookWithEvent:(NSEvent*)event {
3535   // Show dictionary by current point
3536   WidgetContentCommandEvent contentCommandEvent(
3537       true, eContentCommandLookUpDictionary, mGeckoChild);
3538   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3539   contentCommandEvent.mRefPoint = mGeckoChild->CocoaPointsToDevPixels(point);
3540   mGeckoChild->DispatchWindowEvent(contentCommandEvent);
3541   // The widget might have been destroyed.
3544 - (NSInteger)windowLevel {
3545   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3547   NS_ENSURE_TRUE(mTextInputHandler, [[self window] level]);
3548   return mTextInputHandler->GetWindowLevel();
3550   NS_OBJC_END_TRY_BLOCK_RETURN(NSNormalWindowLevel);
3553 #pragma mark -
3555 // This is a private API that Cocoa uses.
3556 // Cocoa will call this after the menu system returns "NO" for
3557 // "performKeyEquivalent:". We want all they key events we can get so just
3558 // return YES. In particular, this fixes ctrl-tab - we don't get a "keyDown:"
3559 // call for that without this.
3560 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
3561   return YES;
3564 - (NSEvent*)lastKeyDownEvent {
3565   return mLastKeyDownEvent;
3568 - (void)keyDown:(NSEvent*)theEvent {
3569   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3571   [mLastKeyDownEvent release];
3572   mLastKeyDownEvent = [theEvent retain];
3574   // Weird things can happen on keyboard input if the key window isn't in the
3575   // current space.  For example see bug 1056251.  To get around this, always
3576   // make sure that, if our window is key, it's also made frontmost.  Doing
3577   // this automatically switches to whatever space our window is in.  Safari
3578   // does something similar.  Our window should normally always be key --
3579   // otherwise why is the OS sending us a key down event?  But it's just
3580   // possible we're in Gecko's hidden window, so we check first.
3581   NSWindow* viewWindow = [self window];
3582   if (viewWindow && [viewWindow isKeyWindow]) {
3583     [viewWindow orderWindow:NSWindowAbove relativeTo:0];
3584   }
3586 #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
3587   if (!Preferences::GetBool("intl.allow-insecure-text-input", false) &&
3588       mGeckoChild && mTextInputHandler && mTextInputHandler->IsFocused()) {
3589     NSWindow* window = [self window];
3590     NSString* info = [NSString
3591         stringWithFormat:@"\nview [%@], window [%@], window is key %i, is "
3592                          @"fullscreen %i, app is active %i",
3593                          self, window, [window isKeyWindow],
3594                          ([window styleMask] & NSWindowStyleMaskFullScreen) !=
3595                              0,
3596                          [NSApp isActive]];
3597     nsAutoCString additionalInfo([info UTF8String]);
3599     if (mGeckoChild->GetInputContext().IsPasswordEditor() &&
3600         !TextInputHandler::IsSecureEventInputEnabled()) {
3601 #  define CRASH_MESSAGE \
3602     "A password editor has focus, but not in secure input mode"
3604       CrashReporter::AppendAppNotesToCrashReport(
3605           "\nBug 893973: "_ns + nsLiteralCString(CRASH_MESSAGE));
3606       CrashReporter::AppendAppNotesToCrashReport(additionalInfo);
3608       MOZ_CRASH(CRASH_MESSAGE);
3609 #  undef CRASH_MESSAGE
3610     } else if (!mGeckoChild->GetInputContext().IsPasswordEditor() &&
3611                TextInputHandler::IsSecureEventInputEnabled()) {
3612 #  define CRASH_MESSAGE \
3613     "A non-password editor has focus, but in secure input mode"
3615       CrashReporter::AppendAppNotesToCrashReport(
3616           "\nBug 893973: "_ns + nsLiteralCString(CRASH_MESSAGE));
3617       CrashReporter::AppendAppNotesToCrashReport(additionalInfo);
3619       MOZ_CRASH(CRASH_MESSAGE);
3620 #  undef CRASH_MESSAGE
3621     }
3622   }
3623 #endif  // #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
3625   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3626   if (mGeckoChild) {
3627     if (mTextInputHandler) {
3628       sUniqueKeyEventId++;
3629       NSMutableDictionary* nativeKeyEventsMap = [ChildView sNativeKeyEventsMap];
3630       [nativeKeyEventsMap setObject:theEvent forKey:@(sUniqueKeyEventId)];
3631       // Purge old native events, in case we're still holding on to them. We
3632       // keep at most 10 references to 10 different native events.
3633       [nativeKeyEventsMap removeObjectForKey:@(sUniqueKeyEventId - 10)];
3634       mTextInputHandler->HandleKeyDownEvent(theEvent, sUniqueKeyEventId);
3635     } else {
3636       // There was no text input handler. Offer the event to the native menu
3637       // system to check if there are any registered custom shortcuts for this
3638       // event.
3639       mGeckoChild->SendEventToNativeMenuSystem(theEvent);
3640     }
3641   }
3643   NS_OBJC_END_TRY_IGNORE_BLOCK;
3646 - (void)keyUp:(NSEvent*)theEvent {
3647   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3649   NS_ENSURE_TRUE(mGeckoChild, );
3651   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3653   mTextInputHandler->HandleKeyUpEvent(theEvent);
3655   NS_OBJC_END_TRY_IGNORE_BLOCK;
3658 - (void)insertNewline:(id)sender {
3659   if (mTextInputHandler) {
3660     mTextInputHandler->HandleCommand(Command::InsertParagraph);
3661   }
3664 - (void)insertLineBreak:(id)sender {
3665   // Ctrl + Enter in the default settings.
3666   if (mTextInputHandler) {
3667     mTextInputHandler->HandleCommand(Command::InsertLineBreak);
3668   }
3671 - (void)deleteBackward:(id)sender {
3672   // Backspace in the default settings.
3673   if (mTextInputHandler) {
3674     mTextInputHandler->HandleCommand(Command::DeleteCharBackward);
3675   }
3678 - (void)deleteBackwardByDecomposingPreviousCharacter:(id)sender {
3679   // Ctrl + Backspace in the default settings.
3680   if (mTextInputHandler) {
3681     mTextInputHandler->HandleCommand(Command::DeleteCharBackward);
3682   }
3685 - (void)deleteWordBackward:(id)sender {
3686   // Alt + Backspace in the default settings.
3687   if (mTextInputHandler) {
3688     mTextInputHandler->HandleCommand(Command::DeleteWordBackward);
3689   }
3692 - (void)deleteToBeginningOfBackward:(id)sender {
3693   // Command + Backspace in the default settings.
3694   if (mTextInputHandler) {
3695     mTextInputHandler->HandleCommand(Command::DeleteToBeginningOfLine);
3696   }
3699 - (void)deleteForward:(id)sender {
3700   // Delete in the default settings.
3701   if (mTextInputHandler) {
3702     mTextInputHandler->HandleCommand(Command::DeleteCharForward);
3703   }
3706 - (void)deleteWordForward:(id)sender {
3707   // Alt + Delete in the default settings.
3708   if (mTextInputHandler) {
3709     mTextInputHandler->HandleCommand(Command::DeleteWordForward);
3710   }
3713 - (void)insertTab:(id)sender {
3714   // Tab in the default settings.
3715   if (mTextInputHandler) {
3716     mTextInputHandler->HandleCommand(Command::InsertTab);
3717   }
3720 - (void)insertBacktab:(id)sender {
3721   // Shift + Tab in the default settings.
3722   if (mTextInputHandler) {
3723     mTextInputHandler->HandleCommand(Command::InsertBacktab);
3724   }
3727 - (void)moveRight:(id)sender {
3728   // RightArrow in the default settings.
3729   if (mTextInputHandler) {
3730     mTextInputHandler->HandleCommand(Command::CharNext);
3731   }
3734 - (void)moveRightAndModifySelection:(id)sender {
3735   // Shift + RightArrow in the default settings.
3736   if (mTextInputHandler) {
3737     mTextInputHandler->HandleCommand(Command::SelectCharNext);
3738   }
3741 - (void)moveWordRight:(id)sender {
3742   // Alt + RightArrow in the default settings.
3743   if (mTextInputHandler) {
3744     mTextInputHandler->HandleCommand(Command::WordNext);
3745   }
3748 - (void)moveWordRightAndModifySelection:(id)sender {
3749   // Alt + Shift + RightArrow in the default settings.
3750   if (mTextInputHandler) {
3751     mTextInputHandler->HandleCommand(Command::SelectWordNext);
3752   }
3755 - (void)moveToRightEndOfLine:(id)sender {
3756   // Command + RightArrow in the default settings.
3757   if (mTextInputHandler) {
3758     mTextInputHandler->HandleCommand(Command::EndLine);
3759   }
3762 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
3763   // Command + Shift + RightArrow in the default settings.
3764   if (mTextInputHandler) {
3765     mTextInputHandler->HandleCommand(Command::SelectEndLine);
3766   }
3769 - (void)moveLeft:(id)sender {
3770   // LeftArrow in the default settings.
3771   if (mTextInputHandler) {
3772     mTextInputHandler->HandleCommand(Command::CharPrevious);
3773   }
3776 - (void)moveLeftAndModifySelection:(id)sender {
3777   // Shift + LeftArrow in the default settings.
3778   if (mTextInputHandler) {
3779     mTextInputHandler->HandleCommand(Command::SelectCharPrevious);
3780   }
3783 - (void)moveWordLeft:(id)sender {
3784   // Alt + LeftArrow in the default settings.
3785   if (mTextInputHandler) {
3786     mTextInputHandler->HandleCommand(Command::WordPrevious);
3787   }
3790 - (void)moveWordLeftAndModifySelection:(id)sender {
3791   // Alt + Shift + LeftArrow in the default settings.
3792   if (mTextInputHandler) {
3793     mTextInputHandler->HandleCommand(Command::SelectWordPrevious);
3794   }
3797 - (void)moveToLeftEndOfLine:(id)sender {
3798   // Command + LeftArrow in the default settings.
3799   if (mTextInputHandler) {
3800     mTextInputHandler->HandleCommand(Command::BeginLine);
3801   }
3804 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
3805   // Command + Shift + LeftArrow in the default settings.
3806   if (mTextInputHandler) {
3807     mTextInputHandler->HandleCommand(Command::SelectBeginLine);
3808   }
3811 - (void)moveUp:(id)sender {
3812   // ArrowUp in the default settings.
3813   if (mTextInputHandler) {
3814     mTextInputHandler->HandleCommand(Command::LinePrevious);
3815   }
3818 - (void)moveUpAndModifySelection:(id)sender {
3819   // Shift + ArrowUp in the default settings.
3820   if (mTextInputHandler) {
3821     mTextInputHandler->HandleCommand(Command::SelectLinePrevious);
3822   }
3825 - (void)moveToBeginningOfDocument:(id)sender {
3826   // Command + ArrowUp in the default settings.
3827   if (mTextInputHandler) {
3828     mTextInputHandler->HandleCommand(Command::MoveTop);
3829   }
3832 - (void)moveToBeginningOfDocumentAndModifySelection:(id)sender {
3833   // Command + Shift + ArrowUp or Shift + Home in the default settings.
3834   if (mTextInputHandler) {
3835     mTextInputHandler->HandleCommand(Command::SelectTop);
3836   }
3839 - (void)moveDown:(id)sender {
3840   // ArrowDown in the default settings.
3841   if (mTextInputHandler) {
3842     mTextInputHandler->HandleCommand(Command::LineNext);
3843   }
3846 - (void)moveDownAndModifySelection:(id)sender {
3847   // Shift + ArrowDown in the default settings.
3848   if (mTextInputHandler) {
3849     mTextInputHandler->HandleCommand(Command::SelectLineNext);
3850   }
3853 - (void)moveToEndOfDocument:(id)sender {
3854   // Command + ArrowDown in the default settings.
3855   if (mTextInputHandler) {
3856     mTextInputHandler->HandleCommand(Command::MoveBottom);
3857   }
3860 - (void)moveToEndOfDocumentAndModifySelection:(id)sender {
3861   // Command + Shift + ArrowDown or Shift + End in the default settings.
3862   if (mTextInputHandler) {
3863     mTextInputHandler->HandleCommand(Command::SelectBottom);
3864   }
3867 - (void)scrollPageUp:(id)sender {
3868   // PageUp in the default settings.
3869   if (mTextInputHandler) {
3870     mTextInputHandler->HandleCommand(Command::ScrollPageUp);
3871   }
3874 - (void)pageUpAndModifySelection:(id)sender {
3875   // Shift + PageUp in the default settings.
3876   if (mTextInputHandler) {
3877     mTextInputHandler->HandleCommand(Command::SelectPageUp);
3878   }
3881 - (void)scrollPageDown:(id)sender {
3882   // PageDown in the default settings.
3883   if (mTextInputHandler) {
3884     mTextInputHandler->HandleCommand(Command::ScrollPageDown);
3885   }
3888 - (void)pageDownAndModifySelection:(id)sender {
3889   // Shift + PageDown in the default settings.
3890   if (mTextInputHandler) {
3891     mTextInputHandler->HandleCommand(Command::SelectPageDown);
3892   }
3895 - (void)scrollToEndOfDocument:(id)sender {
3896   // End in the default settings.
3897   if (mTextInputHandler) {
3898     mTextInputHandler->HandleCommand(Command::ScrollBottom);
3899   }
3902 - (void)scrollToBeginningOfDocument:(id)sender {
3903   // Home in the default settings.
3904   if (mTextInputHandler) {
3905     mTextInputHandler->HandleCommand(Command::ScrollTop);
3906   }
3909 // XXX Don't decleare nor implement calcelOperation: because it
3910 //     causes not calling keyDown: for Command + Period.
3911 //     We need to handle it from doCommandBySelector:.
3913 - (void)complete:(id)sender {
3914   // Alt + Escape or Alt + Shift + Escape in the default settings.
3915   if (mTextInputHandler) {
3916     mTextInputHandler->HandleCommand(Command::Complete);
3917   }
3920 - (void)flagsChanged:(NSEvent*)theEvent {
3921   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3923   NS_ENSURE_TRUE(mGeckoChild, );
3925   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3926   mTextInputHandler->HandleFlagsChanged(theEvent);
3928   NS_OBJC_END_TRY_IGNORE_BLOCK;
3931 - (BOOL)isFirstResponder {
3932   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3934   NSResponder* resp = [[self window] firstResponder];
3935   return (resp == (NSResponder*)self);
3937   NS_OBJC_END_TRY_BLOCK_RETURN(NO);
3940 - (BOOL)isDragInProgress {
3941   if (!mDragService) return NO;
3943   nsCOMPtr<nsIDragSession> dragSession =
3944       mDragService->GetCurrentSession(mGeckoChild);
3945   return dragSession != nullptr;
3948 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent {
3949   // If we're being destroyed assume the default -- return YES.
3950   if (!mGeckoChild) return YES;
3952   WidgetMouseEvent geckoEvent(true, eMouseActivate, mGeckoChild,
3953                               WidgetMouseEvent::eReal);
3954   [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
3955   return (mGeckoChild->DispatchInputEvent(&geckoEvent).mContentStatus !=
3956           nsEventStatus_eConsumeNoDefault);
3959 // We must always call through to our superclass, even when mGeckoChild is
3960 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
3961 - (BOOL)becomeFirstResponder {
3962   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3964   return [super becomeFirstResponder];
3966   NS_OBJC_END_TRY_BLOCK_RETURN(YES);
3969 - (void)viewsWindowDidBecomeKey {
3970   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3972   if (!mGeckoChild) return;
3974   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3976   // check to see if the window implements the mozWindow protocol. This
3977   // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
3978   // which can happen because these activate calls propagate out
3979   // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
3980   BOOL isMozWindow =
3981       [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
3982   if (isMozWindow) [[self window] setSuppressMakeKeyFront:YES];
3984   nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
3985   if (listener) listener->WindowActivated();
3987   if (isMozWindow) [[self window] setSuppressMakeKeyFront:NO];
3989   if (mGeckoChild->GetInputContext().IsPasswordEditor()) {
3990     TextInputHandler::EnableSecureEventInput();
3991   } else {
3992     TextInputHandler::EnsureSecureEventInputDisabled();
3993   }
3995   NS_OBJC_END_TRY_IGNORE_BLOCK;
3998 - (void)viewsWindowDidResignKey {
3999   if (!mGeckoChild) return;
4001   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4003   nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
4004   if (listener) listener->WindowDeactivated();
4006   TextInputHandler::EnsureSecureEventInputDisabled();
4009 // If the call to removeFromSuperview isn't delayed from nsChildView::
4010 // TearDownView(), the NSView hierarchy might get changed during calls to
4011 // [ChildView drawRect:], which leads to "beyond bounds" exceptions in
4012 // NSCFArray.  For more info see bmo bug 373122.  Apple's docs claim that
4013 // removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
4014 // display" (whatever "display" means).  But it's _not_ true that it can be
4015 // safely invoked during calls to [NSView drawRect:].  We use
4016 // removeFromSuperview here because there's no longer any danger of being
4017 // "invoked during display", and because doing do clears up bmo bug 384343.
4018 - (void)delayedTearDown {
4019   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4021   [self removeFromSuperview];
4022   [self release];
4024   NS_OBJC_END_TRY_IGNORE_BLOCK;
4027 #pragma mark -
4029 // drag'n'drop stuff
4030 #define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
4032 - (NSDragOperation)dragOperationFromDragAction:(int32_t)aDragAction {
4033   if (nsIDragService::DRAGDROP_ACTION_LINK & aDragAction)
4034     return NSDragOperationLink;
4035   if (nsIDragService::DRAGDROP_ACTION_COPY & aDragAction)
4036     return NSDragOperationCopy;
4037   if (nsIDragService::DRAGDROP_ACTION_MOVE & aDragAction)
4038     return NSDragOperationGeneric;
4039   return NSDragOperationNone;
4042 - (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint {
4043   if (!mGeckoChild) {
4044     return LayoutDeviceIntPoint(0, 0);
4045   }
4047   NSPoint localPoint = [self convertPoint:aPoint fromView:nil];
4048   return mGeckoChild->CocoaPointsToDevPixels(localPoint);
4051 - (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint {
4052   if (!mGeckoChild) {
4053     return LayoutDeviceIntPoint(0, 0);
4054   }
4056   NSPoint localPoint = [self convertPoint:aPoint fromView:nil];
4057   return mGeckoChild->CocoaPointsToDevPixelsRoundDown(localPoint);
4060 // This is a utility function used by NSView drag event methods
4061 // to send events. It contains all of the logic needed for Gecko
4062 // dragging to work. Returns the appropriate cocoa drag operation code.
4063 - (NSDragOperation)doDragAction:(EventMessage)aMessage sender:(id)aSender {
4064   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4066   if (!mGeckoChild) return NSDragOperationNone;
4068   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView doDragAction: entered\n"));
4070   if (!mDragService) {
4071     CallGetService(kDragServiceContractID, &mDragService);
4072     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
4073     if (!mDragService) return NSDragOperationNone;
4074   }
4076   nsCOMPtr<nsIDragSession> dragSession;
4077   if (aMessage == eDragEnter) {
4078     nsIWidget* widget = mGeckoChild;
4079     dragSession = mDragService->StartDragSession(widget);
4080   } else {
4081     dragSession = mDragService->GetCurrentSession(mGeckoChild);
4082   }
4084   if (dragSession) {
4085     if (aMessage == eDragOver) {
4086       // fire the drag event at the source. Just ignore whether it was
4087       // cancelled or not as there isn't actually a means to stop the drag
4088       dragSession->FireDragEventAtSource(
4089           eDrag, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
4090       dragSession->SetCanDrop(false);
4091     } else if (aMessage == eDrop) {
4092       // We make the assumption that the dragOver handlers have correctly set
4093       // the |canDrop| property of the Drag Session.
4094       bool canDrop = false;
4095       if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
4096         [self doDragAction:eDragExit sender:aSender];
4098         nsCOMPtr<nsINode> sourceNode;
4099         dragSession->GetSourceNode(getter_AddRefs(sourceNode));
4100         if (!sourceNode) {
4101           dragSession->EndDragSession(
4102               false, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
4103         }
4104         return NSDragOperationNone;
4105       }
4106     }
4108     unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
4109     uint32_t action = nsIDragService::DRAGDROP_ACTION_MOVE;
4110     // force copy = option, alias = cmd-option, default is move
4111     if (modifierFlags & NSEventModifierFlagOption) {
4112       if (modifierFlags & NSEventModifierFlagCommand)
4113         action = nsIDragService::DRAGDROP_ACTION_LINK;
4114       else
4115         action = nsIDragService::DRAGDROP_ACTION_COPY;
4116     }
4117     dragSession->SetDragAction(action);
4118   }
4120   // set up gecko event
4121   WidgetDragEvent geckoEvent(true, aMessage, mGeckoChild);
4122   nsCocoaUtils::InitInputEvent(geckoEvent, [NSApp currentEvent]);
4124   // Use our own coordinates in the gecko event.
4125   // Convert event from gecko global coords to gecko view coords.
4126   NSPoint draggingLoc = [aSender draggingLocation];
4128   geckoEvent.mRefPoint = [self convertWindowCoordinates:draggingLoc];
4130   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4131   mGeckoChild->DispatchInputEvent(&geckoEvent);
4132   if (!mGeckoChild) return NSDragOperationNone;
4134   if (dragSession) {
4135     switch (aMessage) {
4136       case eDragEnter:
4137       case eDragOver: {
4138         uint32_t dragAction;
4139         dragSession->GetDragAction(&dragAction);
4141         // If TakeChildProcessDragAction returns something other than
4142         // DRAGDROP_ACTION_UNINITIALIZED, it means that the last event was sent
4143         // to the child process and this event is also being sent to the child
4144         // process. In this case, use the last event's action instead.
4145         nsDragSession* ds = static_cast<nsDragSession*>(dragSession.get());
4146         int32_t childDragAction = ds->TakeChildProcessDragAction();
4147         if (childDragAction != nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) {
4148           dragAction = childDragAction;
4149         }
4151         return [self dragOperationFromDragAction:dragAction];
4152       }
4153       case eDragExit:
4154       case eDrop: {
4155         nsCOMPtr<nsINode> sourceNode;
4156         dragSession->GetSourceNode(getter_AddRefs(sourceNode));
4157         if (!sourceNode) {
4158           // We're leaving a window while doing a drag that was
4159           // initiated in a different app. End the drag session,
4160           // since we're done with it for now (until the user
4161           // drags back into mozilla).
4162           dragSession->EndDragSession(
4163               false, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
4164         }
4165         break;
4166       }
4167       default:
4168         break;
4169     }
4170   }
4172   return NSDragOperationGeneric;
4174   NS_OBJC_END_TRY_BLOCK_RETURN(NSDragOperationNone);
4177 // NSDraggingDestination
4178 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
4179   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4181   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingEntered: entered\n"));
4183   // there should never be a globalDragPboard when "draggingEntered:" is
4184   // called, but just in case we'll take care of it here.
4185   [globalDragPboard release];
4187   // Set the global drag pasteboard that will be used for this drag session.
4188   // This will be set back to nil when the drag session ends (mouse exits
4189   // the view or a drop happens within the view).
4190   globalDragPboard = [[sender draggingPasteboard] retain];
4192   return [self doDragAction:eDragEnter sender:sender];
4194   NS_OBJC_END_TRY_BLOCK_RETURN(NSDragOperationNone);
4197 // NSDraggingDestination
4198 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
4199   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingUpdated: entered\n"));
4200   return [self doDragAction:eDragOver sender:sender];
4203 // NSDraggingDestination
4204 - (void)draggingExited:(id<NSDraggingInfo>)sender {
4205   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingExited: entered\n"));
4207   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4208   [self doDragAction:eDragExit sender:sender];
4209   NS_IF_RELEASE(mDragService);
4212 // NSDraggingDestination
4213 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
4214   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4215   BOOL handled = [self doDragAction:eDrop sender:sender] != NSDragOperationNone;
4216   NS_IF_RELEASE(mDragService);
4217   return handled;
4220 // NSDraggingSource
4221 // This is just implemented so we comply with the NSDraggingSource protocol.
4222 - (NSDragOperation)draggingSession:(NSDraggingSession*)session
4223     sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
4224   return UINT_MAX;
4227 // NSDraggingSource
4228 - (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession*)session {
4229   return YES;
4232 // NSDraggingSource
4233 - (void)draggingSession:(NSDraggingSession*)aSession
4234            endedAtPoint:(NSPoint)aPoint
4235               operation:(NSDragOperation)aOperation {
4236   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4238 #ifdef NIGHTLY_BUILD
4239   MOZ_RELEASE_ASSERT(NS_IsMainThread());
4240 #endif
4242   gDraggedTransferables = nullptr;
4244   NSEvent* currentEvent = [NSApp currentEvent];
4245   gUserCancelledDrag = ([currentEvent type] == NSEventTypeKeyDown &&
4246                         [currentEvent keyCode] == kVK_Escape);
4248   if (!mDragService) {
4249     CallGetService(kDragServiceContractID, &mDragService);
4250     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
4251   }
4253   nsCOMPtr<nsIDragSession> session =
4254       mDragService->GetCurrentSession(mGeckoChild);
4255   if (session) {
4256     // Set the dragend point from the current mouse location
4257     // FIXME(emilio): Weird that we wouldn't use aPoint instead? Seems to work
4258     // locally as well...
4259     // NSPoint pnt = aPoint;
4260     NSPoint pnt = [NSEvent mouseLocation];
4261     NSPoint locationInWindow =
4262         nsCocoaUtils::ConvertPointFromScreen([self window], pnt);
4263     FlipCocoaScreenCoordinate(pnt);
4264     LayoutDeviceIntPoint pt = [self convertWindowCoordinates:locationInWindow];
4265     session->SetDragEndPoint(pt.x, pt.y);
4267     // XXX: dropEffect should be updated per |aOperation|.
4268     // As things stand though, |aOperation| isn't well handled within "our"
4269     // events, that is, when the drop happens within the window: it is set
4270     // either to NSDragOperationGeneric or to NSDragOperationNone.
4271     // For that reason, it's not yet possible to override dropEffect per the
4272     // given OS value, and it's also unclear what's the correct dropEffect
4273     // value for NSDragOperationGeneric that is passed by other applications.
4274     // All that said, NSDragOperationNone is still reliable.
4275     if (aOperation == NSDragOperationNone) {
4276       if (RefPtr dataTransfer = session->GetDataTransfer()) {
4277         dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
4278       }
4279     }
4281     session->EndDragSession(true,
4282                             nsCocoaUtils::ModifiersForEvent(currentEvent));
4283   }
4285   session = nullptr;
4286   NS_IF_RELEASE(mDragService);
4288   [globalDragPboard release];
4289   globalDragPboard = nil;
4290   [gLastDragMouseDownEvent release];
4291   gLastDragMouseDownEvent = nil;
4292   mPerformedDrag = YES;
4294   NS_OBJC_END_TRY_IGNORE_BLOCK;
4297 // NSDraggingSource
4298 - (void)draggingSession:(NSDraggingSession*)aSession
4299            movedToPoint:(NSPoint)aPoint {
4300   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4302   nsCOMPtr<nsIDragService> dragService = mDragService;
4303   if (!dragService) {
4304     dragService = do_GetService(kDragServiceContractID);
4305   }
4306   if (dragService) {
4307     RefPtr<nsIDragSession> dragSession;
4308     nsIWidget* widget = mGeckoChild;
4309     dragService->GetCurrentSession(widget, getter_AddRefs(dragSession));
4310     if (dragSession) {
4311       MOZ_ASSERT(aSession == static_cast<nsDragSession*>(dragSession.get())
4312                                  ->GetNSDraggingSession());
4313       dragSession->DragMoved(aPoint.x, aPoint.y);
4314     }
4315   }
4317   NS_OBJC_END_TRY_IGNORE_BLOCK;
4320 // NSDraggingSource
4321 - (void)draggingSession:(NSDraggingSession*)aSession
4322        willBeginAtPoint:(NSPoint)aPoint {
4323   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4325   // there should never be a globalDragPboard when "willBeginAtPoint:" is
4326   // called, but just in case we'll take care of it here.
4327   [globalDragPboard release];
4329   // Set the global drag pasteboard that will be used for this drag session.
4330   // This will be set back to nil when the drag session ends (mouse exits
4331   // the view or a drop happens within the view).
4332   globalDragPboard = [[aSession draggingPasteboard] retain];
4334   NS_OBJC_END_TRY_IGNORE_BLOCK;
4337 // Get the paste location from the low level pasteboard.
4338 static CFTypeRefPtr<CFURLRef> GetPasteLocation(NSPasteboard* aPasteboard) {
4339   PasteboardRef pboardRef = nullptr;
4340   PasteboardCreate((CFStringRef)[aPasteboard name], &pboardRef);
4341   if (!pboardRef) {
4342     return nullptr;
4343   }
4345   auto pasteBoard = CFTypeRefPtr<PasteboardRef>::WrapUnderCreateRule(pboardRef);
4346   PasteboardSynchronize(pasteBoard.get());
4348   CFURLRef urlRef = nullptr;
4349   PasteboardCopyPasteLocation(pasteBoard.get(), &urlRef);
4350   return CFTypeRefPtr<CFURLRef>::WrapUnderCreateRule(urlRef);
4353 // NSPasteboardItemDataProvider
4354 - (void)pasteboard:(NSPasteboard*)aPasteboard
4355                   item:(NSPasteboardItem*)aItem
4356     provideDataForType:(NSString*)aType {
4357   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4359 #ifdef NIGHTLY_BUILD
4360   MOZ_RELEASE_ASSERT(NS_IsMainThread());
4361 #endif
4363   if (!gDraggedTransferables) {
4364     return;
4365   }
4367   uint32_t count = 0;
4368   gDraggedTransferables->GetLength(&count);
4370   for (uint32_t j = 0; j < count; j++) {
4371     nsCOMPtr<nsITransferable> currentTransferable =
4372         do_QueryElementAt(gDraggedTransferables, j);
4373     if (!currentTransferable) {
4374       return;
4375     }
4377     // Transform the transferable to an NSDictionary.
4378     NSDictionary* pasteboardOutputDict =
4379         nsClipboard::PasteboardDictFromTransferable(currentTransferable);
4380     if (!pasteboardOutputDict) {
4381       return;
4382     }
4384     // Write everything out to the pasteboard.
4385     unsigned int typeCount = [pasteboardOutputDict count];
4386     NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1];
4387     [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
4388     [types addObject:[UTIHelper stringFromPboardType:kMozWildcardPboardType]];
4389     for (unsigned int k = 0; k < typeCount; k++) {
4390       NSString* curType = [types objectAtIndex:k];
4391       if ([curType isEqualToString:[UTIHelper stringFromPboardType:
4392                                                   NSPasteboardTypeString]] ||
4393           [curType
4394               isEqualToString:[UTIHelper
4395                                   stringFromPboardType:kPublicUrlPboardType]] ||
4396           [curType isEqualToString:[UTIHelper stringFromPboardType:
4397                                                   kPublicUrlNamePboardType]] ||
4398           [curType
4399               isEqualToString:[UTIHelper
4400                                   stringFromPboardType:(NSString*)
4401                                                            kUTTypeFileURL]]) {
4402         [aPasteboard setString:[pasteboardOutputDict valueForKey:curType]
4403                        forType:curType];
4404       } else if ([curType isEqualToString:[UTIHelper
4405                                               stringFromPboardType:
4406                                                   kUrlsWithTitlesPboardType]]) {
4407         [aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType]
4408                              forType:curType];
4409       } else if ([curType
4410                      isEqualToString:[UTIHelper stringFromPboardType:
4411                                                     NSPasteboardTypeHTML]]) {
4412         [aPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(
4413                                    [pasteboardOutputDict valueForKey:curType]))
4414                        forType:curType];
4415       } else if ([curType
4416                      isEqualToString:[UTIHelper stringFromPboardType:
4417                                                     NSPasteboardTypeTIFF]] ||
4418                  [curType isEqualToString:[UTIHelper
4419                                               stringFromPboardType:
4420                                                   kMozCustomTypesPboardType]]) {
4421         [aPasteboard setData:[pasteboardOutputDict valueForKey:curType]
4422                      forType:curType];
4423       } else if ([curType
4424                      isEqualToString:[UTIHelper stringFromPboardType:
4425                                                     kMozFileUrlsPboardType]]) {
4426         [aPasteboard writeObjects:[pasteboardOutputDict valueForKey:curType]];
4427       } else if ([curType
4428                      isEqualToString:
4429                          [UTIHelper
4430                              stringFromPboardType:
4431                                  (NSString*)kPasteboardTypeFileURLPromise]]) {
4432         CFTypeRefPtr<CFURLRef> url = GetPasteLocation(aPasteboard);
4433         if (!url) {
4434           continue;
4435         }
4437         nsCOMPtr<nsILocalFileMac> macLocalFile;
4438         if (NS_FAILED(NS_NewLocalFileWithCFURL(url.get(),
4439                                                getter_AddRefs(macLocalFile)))) {
4440           NS_ERROR("failed NS_NewLocalFileWithCFURL");
4441           continue;
4442         }
4444         if (!gDraggedTransferables) {
4445           continue;
4446         }
4448         uint32_t transferableCount;
4449         nsresult rv = gDraggedTransferables->GetLength(&transferableCount);
4450         if (NS_FAILED(rv)) {
4451           continue;
4452         }
4454         for (uint32_t i = 0; i < transferableCount; i++) {
4455           nsCOMPtr<nsITransferable> item =
4456               do_QueryElementAt(gDraggedTransferables, i);
4457           if (!item) {
4458             NS_ERROR("no transferable");
4459             continue;
4460           }
4462           item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile);
4464           // Now request the kFilePromiseMime data, which will invoke the data
4465           // provider. If successful, the file will have been created.
4466           nsCOMPtr<nsISupports> fileDataPrimitive;
4467           Unused << item->GetTransferData(kFilePromiseMime,
4468                                           getter_AddRefs(fileDataPrimitive));
4469         }
4471         [aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType]
4472                              forType:curType];
4473       }
4474     }
4475   }
4477   NS_OBJC_END_TRY_IGNORE_BLOCK;
4480 #pragma mark -
4482 // Support for the "Services" menu. We currently only support sending strings
4483 // and HTML to system services.
4484 // This method can be called on any thread (see bug 1751687). We can only
4485 // usefully handle it on the main thread.
4486 - (id)validRequestorForSendType:(NSString*)sendType
4487                      returnType:(NSString*)returnType {
4488   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4490   if (!NS_IsMainThread()) {
4491     // We don't have any thread-safe ways of checking whether we can send
4492     // or receive content. Just say no. In normal cases, we expect this
4493     // method to be called on the main thread.
4494     return [super validRequestorForSendType:sendType returnType:returnType];
4495   }
4497   // sendType contains the type of data that the service would like this
4498   // application to send to it.  sendType is nil if the service is not
4499   // requesting any data.
4500   //
4501   // returnType contains the type of data the the service would like to
4502   // return to this application (e.g., to overwrite the selection).
4503   // returnType is nil if the service will not return any data.
4504   //
4505   // The following condition thus triggers when the service expects a string
4506   // or HTML from us or no data at all AND when the service will either not
4507   // send back any data to us or will send a string or HTML back to us.
4509   id result = nil;
4511   NSString* stringType =
4512       [UTIHelper stringFromPboardType:NSPasteboardTypeString];
4513   NSString* htmlType = [UTIHelper stringFromPboardType:NSPasteboardTypeHTML];
4514   if ((!sendType || [sendType isEqualToString:stringType] ||
4515        [sendType isEqualToString:htmlType]) &&
4516       (!returnType || [returnType isEqualToString:stringType] ||
4517        [returnType isEqualToString:htmlType])) {
4518     if (mGeckoChild) {
4519       // Assume that this object will be able to handle this request.
4520       result = self;
4522       // Keep the ChildView alive during this operation.
4523       nsAutoRetainCocoaObject kungFuDeathGrip(self);
4525       if (sendType) {
4526         // Determine if there is a current selection (chrome/content).
4527         if (!nsClipboard::sSelectionCache) {
4528           result = nil;
4529         }
4530       }
4532       // Determine if we can paste (if receiving data from the service).
4533       if (mGeckoChild && returnType) {
4534         WidgetContentCommandEvent command(
4535             true, eContentCommandPasteTransferable, mGeckoChild, true);
4536         // This might possibly destroy our widget (and null out mGeckoChild).
4537         mGeckoChild->DispatchWindowEvent(command);
4538         if (!mGeckoChild || !command.mSucceeded || !command.mIsEnabled)
4539           result = nil;
4540       }
4541     }
4542   }
4544   // Give the superclass a chance if this object will not handle this request.
4545   if (!result)
4546     result = [super validRequestorForSendType:sendType returnType:returnType];
4548   return result;
4550   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
4553 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
4554   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4556   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4558   // Make sure that the service will accept strings or HTML.
4559   if (![types
4560           containsObject:[UTIHelper stringFromPboardType:NSStringPboardType]] &&
4561       ![types
4562           containsObject:[UTIHelper
4563                              stringFromPboardType:NSPasteboardTypeString]] &&
4564       ![types containsObject:[UTIHelper
4565                                  stringFromPboardType:NSPasteboardTypeHTML]]) {
4566     return NO;
4567   }
4569   // Bail out if there is no Gecko object.
4570   if (!mGeckoChild) return NO;
4572   // Transform the transferable to an NSDictionary.
4573   NSDictionary* pasteboardOutputDict = nullptr;
4575   pasteboardOutputDict =
4576       nsClipboard::PasteboardDictFromTransferable(nsClipboard::sSelectionCache);
4578   if (!pasteboardOutputDict) return NO;
4580   // Declare the pasteboard types.
4581   unsigned int typeCount = [pasteboardOutputDict count];
4582   NSMutableArray* declaredTypes = [NSMutableArray arrayWithCapacity:typeCount];
4583   [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]];
4584   [pboard declareTypes:declaredTypes owner:nil];
4586   // Write the data to the pasteboard.
4587   for (unsigned int i = 0; i < typeCount; i++) {
4588     NSString* currentKey = [declaredTypes objectAtIndex:i];
4589     id currentValue = [pasteboardOutputDict valueForKey:currentKey];
4591     if ([currentKey
4592             isEqualToString:[UTIHelper
4593                                 stringFromPboardType:NSPasteboardTypeString]] ||
4594         [currentKey
4595             isEqualToString:[UTIHelper
4596                                 stringFromPboardType:kPublicUrlPboardType]] ||
4597         [currentKey isEqualToString:[UTIHelper stringFromPboardType:
4598                                                    kPublicUrlNamePboardType]]) {
4599       [pboard setString:currentValue forType:currentKey];
4600     } else if ([currentKey
4601                    isEqualToString:
4602                        [UTIHelper stringFromPboardType:NSPasteboardTypeHTML]]) {
4603       [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
4604                 forType:currentKey];
4605     } else if ([currentKey
4606                    isEqualToString:
4607                        [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF]]) {
4608       [pboard setData:currentValue forType:currentKey];
4609     } else if ([currentKey
4610                    isEqualToString:
4611                        [UTIHelper
4612                            stringFromPboardType:
4613                                (NSString*)kPasteboardTypeFileURLPromise]] ||
4614                [currentKey
4615                    isEqualToString:[UTIHelper stringFromPboardType:
4616                                                   kUrlsWithTitlesPboardType]]) {
4617       [pboard setPropertyList:currentValue forType:currentKey];
4618     }
4619   }
4620   return YES;
4622   NS_OBJC_END_TRY_BLOCK_RETURN(NO);
4625 // Called if the service wants us to replace the current selection.
4626 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
4627   nsresult rv;
4628   nsCOMPtr<nsITransferable> trans =
4629       do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
4630   if (NS_FAILED(rv)) return NO;
4631   trans->Init(nullptr);
4633   trans->AddDataFlavor(kTextMime);
4634   trans->AddDataFlavor(kHTMLMime);
4636   rv = nsClipboard::TransferableFromPasteboard(trans, pboard);
4637   if (NS_FAILED(rv)) return NO;
4639   NS_ENSURE_TRUE(mGeckoChild, false);
4641   WidgetContentCommandEvent command(true, eContentCommandPasteTransferable,
4642                                     mGeckoChild);
4643   command.mTransferable = trans;
4644   mGeckoChild->DispatchWindowEvent(command);
4646   return command.mSucceeded && command.mIsEnabled;
4649 - (void)pressureChangeWithEvent:(NSEvent*)event {
4650   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK
4652   NSInteger stage = [event stage];
4653   if (mLastPressureStage == 1 && stage == 2) {
4654     NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
4655     if ([userDefaults integerForKey:@"com.apple.trackpad.forceClick"] == 1) {
4656       // This is no public API to get configuration for current force click.
4657       // This is filed as radar 29294285.
4658       [self quickLookWithEvent:event];
4659     }
4660   }
4661   mLastPressureStage = stage;
4663   NS_OBJC_END_TRY_IGNORE_BLOCK
4666 nsresult nsChildView::GetSelectionAsPlaintext(nsAString& aResult) {
4667   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4669   if (!nsClipboard::sSelectionCache) {
4670     MOZ_ASSERT(aResult.IsEmpty());
4671     return NS_OK;
4672   }
4674   // Get the current chrome or content selection.
4675   NSDictionary* pasteboardOutputDict = nullptr;
4676   pasteboardOutputDict =
4677       nsClipboard::PasteboardDictFromTransferable(nsClipboard::sSelectionCache);
4679   if (NS_WARN_IF(!pasteboardOutputDict)) {
4680     return NS_ERROR_FAILURE;
4681   }
4683   // Declare the pasteboard types.
4684   unsigned int typeCount = [pasteboardOutputDict count];
4685   NSMutableArray* declaredTypes = [NSMutableArray arrayWithCapacity:typeCount];
4686   [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]];
4687   NSString* currentKey = [declaredTypes objectAtIndex:0];
4688   NSString* currentValue = [pasteboardOutputDict valueForKey:currentKey];
4689   const char* textSelection = [currentValue UTF8String];
4690   aResult = NS_ConvertUTF8toUTF16(textSelection);
4692   return NS_OK;
4694   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
4697 #ifdef DEBUG
4698 nsresult nsChildView::SetHiDPIMode(bool aHiDPI) {
4699   nsCocoaUtils::InvalidateHiDPIState();
4700   Preferences::SetInt("gfx.hidpi.enabled", aHiDPI ? 1 : 0);
4701   BackingScaleFactorChanged();
4702   return NS_OK;
4705 nsresult nsChildView::RestoreHiDPIMode() {
4706   nsCocoaUtils::InvalidateHiDPIState();
4707   Preferences::ClearUser("gfx.hidpi.enabled");
4708   BackingScaleFactorChanged();
4709   return NS_OK;
4711 #endif
4713 #pragma mark -
4715 #ifdef ACCESSIBILITY
4717 /* Every ChildView has a corresponding mozDocAccessible object that is doing all
4718    the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
4719    object.
4721    All ChildView needs to do is to route all accessibility calls (from the
4722    NSAccessibility APIs) down to its object, pretending that they are the same.
4724 - (id<mozAccessible>)accessible {
4725   if (!mGeckoChild) return nil;
4727   id<mozAccessible> nativeAccessible = nil;
4729   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4730   RefPtr<nsChildView> geckoChild(mGeckoChild);
4731   RefPtr<a11y::LocalAccessible> accessible =
4732       geckoChild->GetDocumentAccessible();
4733   if (!accessible) return nil;
4735   accessible->GetNativeInterface((void**)&nativeAccessible);
4737 #  ifdef DEBUG_hakan
4738   NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
4739 #  endif
4741   return nativeAccessible;
4744 /* Implementation of formal mozAccessible formal protocol (enabling mozViews
4745    to talk to mozAccessible objects in the accessibility module). */
4747 - (BOOL)hasRepresentedView {
4748   return YES;
4751 - (id)representedView {
4752   return self;
4755 - (BOOL)isRoot {
4756   return [[self accessible] isRoot];
4759 #  pragma mark -
4761 // general
4763 - (BOOL)isAccessibilityElement {
4764   if (!mozilla::a11y::ShouldA11yBeEnabled())
4765     return [super isAccessibilityElement];
4767   return [[self accessible] isAccessibilityElement];
4770 - (id)accessibilityHitTest:(NSPoint)point {
4771   if (!mozilla::a11y::ShouldA11yBeEnabled())
4772     return [super accessibilityHitTest:point];
4774   return [[self accessible] accessibilityHitTest:point];
4777 - (id)accessibilityFocusedUIElement {
4778   if (!mozilla::a11y::ShouldA11yBeEnabled())
4779     return [super accessibilityFocusedUIElement];
4781   return [[self accessible] accessibilityFocusedUIElement];
4784 // actions
4786 - (NSArray*)accessibilityActionNames {
4787   if (!mozilla::a11y::ShouldA11yBeEnabled())
4788     return [super accessibilityActionNames];
4790   return [[self accessible] accessibilityActionNames];
4793 - (NSString*)accessibilityActionDescription:(NSString*)action {
4794   if (!mozilla::a11y::ShouldA11yBeEnabled())
4795     return [super accessibilityActionDescription:action];
4797   return [[self accessible] accessibilityActionDescription:action];
4800 - (void)accessibilityPerformAction:(NSString*)action {
4801   if (!mozilla::a11y::ShouldA11yBeEnabled())
4802     return [super accessibilityPerformAction:action];
4804   return [[self accessible] accessibilityPerformAction:action];
4807 // attributes
4809 - (NSArray*)accessibilityAttributeNames {
4810   if (!mozilla::a11y::ShouldA11yBeEnabled())
4811     return [super accessibilityAttributeNames];
4813   return [[self accessible] accessibilityAttributeNames];
4816 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
4817   if (!mozilla::a11y::ShouldA11yBeEnabled())
4818     return [super accessibilityIsAttributeSettable:attribute];
4820   return [[self accessible] accessibilityIsAttributeSettable:attribute];
4823 - (id)accessibilityAttributeValue:(NSString*)attribute {
4824   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4826   if (!mozilla::a11y::ShouldA11yBeEnabled())
4827     return [super accessibilityAttributeValue:attribute];
4829   id<mozAccessible> accessible = [self accessible];
4831   // if we're the root (topmost) accessible, we need to return our native
4832   // AXParent as we traverse outside to the hierarchy of whoever embeds us.
4833   // thus, fall back on NSView's default implementation for this attribute.
4834   if ([attribute isEqualToString:NSAccessibilityParentAttribute] &&
4835       [accessible isRoot]) {
4836     id parentAccessible = [super accessibilityAttributeValue:attribute];
4837     return parentAccessible;
4838   }
4840   return [accessible accessibilityAttributeValue:attribute];
4842   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
4845 #endif /* ACCESSIBILITY */
4847 + (uint32_t)sUniqueKeyEventId {
4848   return sUniqueKeyEventId;
4851 + (NSMutableDictionary*)sNativeKeyEventsMap {
4852   // This dictionary is "leaked".
4853   MOZ_RUNINIT static NSMutableDictionary* sNativeKeyEventsMap =
4854       [[NSMutableDictionary alloc] init];
4855   return sNativeKeyEventsMap;
4858 @end
4860 @implementation PixelHostingView
4862 - (id)initWithFrame:(NSRect)aRect {
4863   self = [super initWithFrame:aRect];
4865   self.wantsLayer = YES;
4866   self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
4868   return self;
4871 - (BOOL)isFlipped {
4872   return YES;
4875 - (NSView*)hitTest:(NSPoint)aPoint {
4876   return nil;
4879 - (void)drawRect:(NSRect)aRect {
4880   NS_WARNING("Unexpected call to drawRect: This view returns YES from "
4881              "wantsUpdateLayer, so "
4882              "drawRect should not be called.");
4885 - (BOOL)wantsUpdateLayer {
4886   return YES;
4889 - (void)updateLayer {
4890   [(ChildView*)[self superview] updateRootCALayer];
4893 - (BOOL)wantsBestResolutionOpenGLSurface {
4894   return nsCocoaUtils::HiDPIEnabled() ? YES : NO;
4897 @end
4899 #pragma mark -
4901 void ChildViewMouseTracker::OnDestroyView(ChildView* aView) {
4902   if (sLastMouseEventView == aView) {
4903     sLastMouseEventView = nil;
4904     [sLastMouseMoveEvent release];
4905     sLastMouseMoveEvent = nil;
4906   }
4909 void ChildViewMouseTracker::OnDestroyWindow(NSWindow* aWindow) {
4910   if (sWindowUnderMouse == aWindow) {
4911     sWindowUnderMouse = nil;
4912   }
4915 void ChildViewMouseTracker::MouseEnteredWindow(NSEvent* aEvent) {
4916   NSWindow* window = aEvent.window;
4917   if (!window.ignoresMouseEvents) {
4918     sWindowUnderMouse = window;
4919     ReEvaluateMouseEnterState(aEvent);
4920   }
4923 void ChildViewMouseTracker::MouseExitedWindow(NSEvent* aEvent) {
4924   if (sWindowUnderMouse == aEvent.window) {
4925     sWindowUnderMouse = nil;
4926     [sLastMouseMoveEvent release];
4927     sLastMouseMoveEvent = nil;
4928     ReEvaluateMouseEnterState(aEvent);
4929   }
4932 void ChildViewMouseTracker::NativeMenuOpened() {
4933   // Send a mouse exit event now.
4934   // The menu consumes all mouse events while it's open, and we don't want to be
4935   // stuck thinking the mouse is still hovering our window after the mouse has
4936   // already moved. This could result in unintended cursor changes or tooltips.
4937   sWindowUnderMouse = nil;
4938   ReEvaluateMouseEnterState(nil);
4941 void ChildViewMouseTracker::NativeMenuClosed() {
4942   // If a window was hovered before the menu opened, re-enter that window at the
4943   // last known mouse position. After -[NSView didCloseMenu:withEvent:] is
4944   // called, any NSTrackingArea updates that were buffered while the menu was
4945   // open will be replayed.
4946   if (sLastMouseMoveEvent) {
4947     sWindowUnderMouse = sLastMouseMoveEvent.window;
4948     ReEvaluateMouseEnterState(sLastMouseMoveEvent);
4949   }
4952 void ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent,
4953                                                       ChildView* aOldView) {
4954   ChildView* oldView = aOldView ? aOldView : sLastMouseEventView;
4955   sLastMouseEventView = ViewForEvent(aEvent);
4956   if (sLastMouseEventView != oldView) {
4957     // Send enter and / or exit events.
4958     WidgetMouseEvent::ExitFrom exitFrom =
4959         [sLastMouseEventView window] == [oldView window]
4960             ? WidgetMouseEvent::ePlatformChild
4961             : WidgetMouseEvent::ePlatformTopLevel;
4962     [oldView sendMouseEnterOrExitEvent:aEvent enter:NO exitFrom:exitFrom];
4963     // After the cursor exits the window set it to a visible regular arrow
4964     // cursor.
4965     if (exitFrom == WidgetMouseEvent::ePlatformTopLevel) {
4966       [[nsCursorManager sharedInstance]
4967           setNonCustomCursor:nsIWidget::Cursor{eCursor_standard}];
4968     }
4969     [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent
4970                                              enter:YES
4971                                           exitFrom:exitFrom];
4972   }
4975 void ChildViewMouseTracker::ResendLastMouseMoveEvent() {
4976   if (sLastMouseMoveEvent) {
4977     MouseMoved(sLastMouseMoveEvent);
4978   }
4981 void ChildViewMouseTracker::MouseMoved(NSEvent* aEvent) {
4982   MouseEnteredWindow(aEvent);
4983   [sLastMouseEventView handleMouseMoved:aEvent];
4984   if (sLastMouseMoveEvent != aEvent) {
4985     [sLastMouseMoveEvent release];
4986     sLastMouseMoveEvent = [aEvent retain];
4987   }
4990 void ChildViewMouseTracker::MouseScrolled(NSEvent* aEvent) {
4991   if (!nsCocoaUtils::IsMomentumScrollEvent(aEvent)) {
4992     // Store the position so we can pin future momentum scroll events.
4993     sLastScrollEventScreenLocation =
4994         nsCocoaUtils::ScreenLocationForEvent(aEvent);
4995   }
4998 ChildView* ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent) {
4999   NSWindow* window = sWindowUnderMouse;
5000   if (!window) return nil;
5002   NSPoint windowEventLocation =
5003       nsCocoaUtils::EventLocationForWindow(aEvent, window);
5004   NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
5006   if (![view isKindOfClass:[ChildView class]]) return nil;
5008   ChildView* childView = (ChildView*)view;
5009   // If childView is being destroyed return nil.
5010   if (![childView widget]) return nil;
5011   return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
5014 BOOL ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow,
5015                                                NSEvent* aEvent,
5016                                                ChildView* aView,
5017                                                BOOL aIsClickThrough) {
5018   // Right mouse down events may get through to all windows, even to a top level
5019   // window with an open sheet.
5020   if (!aWindow || [aEvent type] == NSEventTypeRightMouseDown) return YES;
5022   id delegate = [aWindow delegate];
5023   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return YES;
5025   nsIWidget* windowWidget = [(WindowDelegate*)delegate geckoWidget];
5026   if (!windowWidget) return YES;
5028   NSWindow* topLevelWindow = nil;
5030   switch (windowWidget->GetWindowType()) {
5031     case WindowType::Popup:
5032       // If this is a context menu, it won't have a parent. So we'll always
5033       // accept mouse move events on context menus even when none of our windows
5034       // is active, which is the right thing to do.
5035       // For panels, the parent window is the XUL window that owns the panel.
5036       return WindowAcceptsEvent([aWindow parentWindow], aEvent, aView,
5037                                 aIsClickThrough);
5039     case WindowType::TopLevel:
5040     case WindowType::Dialog:
5041       if (aWindow.attachedSheet) {
5042         return NO;
5043       }
5045       topLevelWindow = aWindow;
5046       break;
5047     default:
5048       return YES;
5049   }
5051   if (!topLevelWindow || ([topLevelWindow isMainWindow] && !aIsClickThrough) ||
5052       [aEvent type] == NSEventTypeOtherMouseDown ||
5053       (([aEvent modifierFlags] & NSEventModifierFlagCommand) != 0 &&
5054        [aEvent type] != NSEventTypeMouseMoved))
5055     return YES;
5057   // If we're here then we're dealing with a left click or mouse move on an
5058   // inactive window or something similar. Ask Gecko what to do.
5059   return [aView inactiveWindowAcceptsMouseEvent:aEvent];
5062 #pragma mark -