Backed out changeset b4a0f8afc02e (bug 1857946) for causing bc failures at browser...
[gecko.git] / widget / cocoa / nsChildView.mm
blobcd5d77b524ecb14b5459b16796b95c838677519f
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"
33 #include "nsArrayUtils.h"
34 #include "nsExceptionHandler.h"
35 #include "nsObjCExceptions.h"
36 #include "nsCOMPtr.h"
37 #include "nsThreadUtils.h"
38 #include "nsToolkit.h"
39 #include "nsCRT.h"
41 #include "nsFontMetrics.h"
42 #include "nsIRollupListener.h"
43 #include "nsViewManager.h"
44 #include "nsIFile.h"
45 #include "nsILocalFileMac.h"
46 #include "nsGfxCIID.h"
47 #include "nsStyleConsts.h"
48 #include "nsIWidgetListener.h"
49 #include "nsIScreen.h"
51 #include "nsDragService.h"
52 #include "nsClipboard.h"
53 #include "nsCursorManager.h"
54 #include "nsWindowMap.h"
55 #include "nsCocoaUtils.h"
56 #include "nsMenuUtilsX.h"
57 #include "nsMenuBarX.h"
58 #include "NativeKeyBindings.h"
59 #include "MacThemeGeometryType.h"
61 #include "gfxContext.h"
62 #include "gfxQuartzSurface.h"
63 #include "gfxUtils.h"
64 #include "nsRegion.h"
65 #include "GfxTexturesReporter.h"
66 #include "GLTextureImage.h"
67 #include "GLContextProvider.h"
68 #include "GLContextCGL.h"
69 #include "OGLShaderProgram.h"
70 #include "ScopedGLHelpers.h"
71 #include "HeapCopyOfStackArray.h"
72 #include "mozilla/layers/IAPZCTreeManager.h"
73 #include "mozilla/layers/APZInputBridge.h"
74 #include "mozilla/layers/APZThreadUtils.h"
75 #include "mozilla/layers/CompositorOGL.h"
76 #include "mozilla/layers/CompositorBridgeParent.h"
77 #include "mozilla/layers/InputAPZContext.h"
78 #include "mozilla/layers/IpcResourceUpdateQueue.h"
79 #include "mozilla/layers/NativeLayerCA.h"
80 #include "mozilla/layers/WebRenderBridgeChild.h"
81 #include "mozilla/layers/WebRenderLayerManager.h"
82 #include "mozilla/webrender/WebRenderAPI.h"
83 #include "mozilla/widget/CompositorWidget.h"
84 #include "mozilla/widget/Screen.h"
85 #include "gfxUtils.h"
86 #include "mozilla/gfx/2D.h"
87 #include "mozilla/gfx/BorrowedContext.h"
88 #ifdef ACCESSIBILITY
89 #  include "nsAccessibilityService.h"
90 #  include "mozilla/a11y/Platform.h"
91 #endif
93 #include "mozilla/Preferences.h"
94 #include "mozilla/StaticPrefs_apz.h"
95 #include "mozilla/StaticPrefs_browser.h"
96 #include "mozilla/StaticPrefs_general.h"
97 #include "mozilla/StaticPrefs_gfx.h"
98 #include "mozilla/StaticPrefs_ui.h"
100 #include <dlfcn.h>
102 #include <ApplicationServices/ApplicationServices.h>
104 #include "GeckoProfiler.h"
106 #include "mozilla/layers/ChromeProcessController.h"
107 #include "nsLayoutUtils.h"
108 #include "InputData.h"
109 #include "VibrancyManager.h"
110 #include "nsNativeThemeCocoa.h"
111 #include "nsIDOMWindowUtils.h"
112 #include "Units.h"
113 #include "UnitTransforms.h"
114 #include "mozilla/UniquePtrExtensions.h"
115 #include "CustomCocoaEvents.h"
116 #include "NativeMenuSupport.h"
118 using namespace mozilla;
119 using namespace mozilla::layers;
120 using namespace mozilla::gl;
121 using namespace mozilla::widget;
123 using mozilla::gfx::Matrix4x4;
125 #undef DEBUG_UPDATE
126 #undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated
128 // Don't put more than this many rects in the dirty region, just fluff
129 // out to the bounding-box if there are more
130 #define MAX_RECTS_IN_REGION 100
132 LazyLogModule sCocoaLog("nsCocoaWidgets");
134 extern "C" {
135 CG_EXTERN void CGContextResetCTM(CGContextRef);
136 CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
137 CG_EXTERN void CGContextResetClip(CGContextRef);
139 typedef CFTypeRef CGSRegionObj;
140 CGError CGSNewRegionWithRect(const CGRect* rect, CGSRegionObj* outRegion);
141 CGError CGSNewRegionWithRectList(const CGRect* rects, int rectCount,
142                                  CGSRegionObj* outRegion);
145 // defined in nsMenuBarX.mm
146 extern NSMenu* sApplicationMenu;  // Application menu shared by all menubars
148 extern nsIArray* gDraggedTransferables;
150 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
151 NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil;
152 NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
153 NSPoint ChildViewMouseTracker::sLastScrollEventScreenLocation = NSZeroPoint;
155 #ifdef INVALIDATE_DEBUGGING
156 static void blinkRect(Rect* r);
157 static void blinkRgn(RgnHandle rgn);
158 #endif
160 bool gUserCancelledDrag = false;
162 uint32_t nsChildView::sLastInputEventCount = 0;
164 static bool sIsTabletPointerActivated = false;
166 static uint32_t sUniqueKeyEventId = 0;
168 // The view that will do our drawing or host our NSOpenGLContext or Core
169 // Animation layer.
170 @interface PixelHostingView : NSView {
173 @end
175 @interface ChildView (Private)
177 // sets up our view, attaching it to its owning gecko view
178 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
180 // set up a gecko mouse event based on a cocoa mouse event
181 - (void)convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
182                        toGeckoEvent:(WidgetWheelEvent*)outWheelEvent;
183 - (void)convertCocoaMouseEvent:(NSEvent*)aMouseEvent
184                   toGeckoEvent:(WidgetInputEvent*)outGeckoEvent;
185 - (void)convertCocoaTabletPointerEvent:(NSEvent*)aMouseEvent
186                           toGeckoEvent:(WidgetMouseEvent*)outGeckoEvent;
187 - (NSMenu*)contextMenu;
189 - (void)markLayerForDisplay;
190 - (CALayer*)rootCALayer;
191 - (void)updateRootCALayer;
193 #ifdef ACCESSIBILITY
194 - (id<mozAccessible>)accessible;
195 #endif
197 - (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint;
198 - (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint;
200 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
201 - (void)updateWindowDraggableState;
203 - (bool)beginOrEndGestureForEventPhase:(NSEvent*)aEvent;
205 @end
207 #pragma mark -
209 // Flips a screen coordinate from a point in the cocoa coordinate system
210 // (bottom-left rect) to a point that is a "flipped" cocoa coordinate system
211 // (starts in the top-left).
212 static inline void FlipCocoaScreenCoordinate(NSPoint& inPoint) {
213   inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
216 #pragma mark -
218 nsChildView::nsChildView()
219     : nsBaseWidget(),
220       mView(nullptr),
221       mParentView(nil),
222       mParentWidget(nullptr),
223       mCompositingLock("ChildViewCompositing"),
224       mBackingScaleFactor(0.0),
225       mVisible(false),
226       mSizeMode(nsSizeMode_Normal),
227       mDrawing(false),
228       mIsDispatchPaint(false) {}
230 nsChildView::~nsChildView() {
231   // Notify the children that we're gone.  childView->ResetParent() can change
232   // our list of children while it's being iterated, so the way we iterate the
233   // list must allow for this.
234   for (nsIWidget* kid = mLastChild; kid;) {
235     nsChildView* childView = static_cast<nsChildView*>(kid);
236     kid = kid->GetPrevSibling();
237     childView->ResetParent();
238   }
240   NS_WARNING_ASSERTION(
241       mOnDestroyCalled,
242       "nsChildView object destroyed without calling Destroy()");
244   if (mContentLayer) {
245     mNativeLayerRoot->RemoveLayer(mContentLayer);  // safe if already removed
246   }
248   DestroyCompositor();
250   // An nsChildView object that was in use can be destroyed without Destroy()
251   // ever being called on it.  So we also need to do a quick, safe cleanup
252   // here (it's too late to just call Destroy(), which can cause crashes).
253   // It's particularly important to make sure widgetDestroyed is called on our
254   // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
255   // mGeckoChild are used throughout the ChildView class to tell if it's safe
256   // to use a ChildView object.
257   [mView widgetDestroyed];  // Safe if mView is nil.
258   mParentWidget = nil;
259   TearDownView();  // Safe if called twice.
262 nsresult nsChildView::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
263                              const LayoutDeviceIntRect& aRect,
264                              widget::InitData* aInitData) {
265   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
267   // Because the hidden window is created outside of an event loop,
268   // we need to provide an autorelease pool to avoid leaking cocoa objects
269   // (see bug 559075).
270   nsAutoreleasePool localPool;
272   mBounds = aRect;
274   // Ensure that the toolkit is created.
275   nsToolkit::GetToolkit();
277   BaseCreate(aParent, aInitData);
279   mParentView = nil;
280   if (aParent) {
281     // This is the popup window case. aParent is the nsCocoaWindow for the
282     // popup window, and mParentView will be its content view.
283     mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET);
284     mParentWidget = aParent;
285   } else {
286     // This is the top-level window case.
287     // aNativeParent will be the contentView of our window, since that's what
288     // nsCocoaWindow returns when asked for an NS_NATIVE_VIEW.
289     // We do not have a direct "parent widget" association with the top level
290     // window's nsCocoaWindow object.
291     mParentView = reinterpret_cast<NSView*>(aNativeParent);
292   }
294   // create our parallel NSView and hook it up to our parent. Recall
295   // that NS_NATIVE_WIDGET is the NSView.
296   CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mParentView);
297   NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(mBounds, scaleFactor);
298   mView = [[ChildView alloc] initWithFrame:r geckoChild:this];
300   mNativeLayerRoot = NativeLayerRootCA::CreateForCALayer([mView rootCALayer]);
301   mNativeLayerRoot->SetBackingScale(scaleFactor);
303   // If this view was created in a Gecko view hierarchy, the initial state
304   // is hidden.  If the view is attached only to a native NSView but has
305   // no Gecko parent (as in embedding), the initial state is visible.
306   if (mParentWidget)
307     [mView setHidden:YES];
308   else
309     mVisible = true;
311   // Hook it up in the NSView hierarchy.
312   if (mParentView) {
313     [mParentView addSubview:mView];
314   }
316   // if this is a ChildView, make sure that our per-window data
317   // is set up
318   if ([mView isKindOfClass:[ChildView class]])
319     [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]];
321   NS_ASSERTION(!mTextInputHandler, "mTextInputHandler has already existed");
322   mTextInputHandler = new TextInputHandler(this, mView);
324   return NS_OK;
326   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
329 void nsChildView::TearDownView() {
330   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
332   if (!mView) return;
334   NSWindow* win = [mView window];
335   NSResponder* responder = [win firstResponder];
337   // We're being unhooked from the view hierarchy, don't leave our view
338   // or a child view as the window first responder.
339   if (responder && [responder isKindOfClass:[NSView class]] &&
340       [(NSView*)responder isDescendantOf:mView]) {
341     [win makeFirstResponder:[mView superview]];
342   }
344   // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
345   // win has retained mView, and will detach it from the view hierarchy and
346   // release it when necessary (when win is itself destroyed (in a call to
347   // [win dealloc])).  So all we need to do here is call [mView release] (to
348   // match the call to [mView retain] in nsChildView::StandardCreate()).
349   // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
350   // mView to be released again and dealloced, while remaining win's
351   // contentView.  So if we do that here, win will (for a short while) have
352   // an invalid contentView (for the consequences see bmo bugs 381087 and
353   // 374260).
354   if ([mView isEqual:[win contentView]]) {
355     [mView release];
356   } else {
357     // Stop NSView hierarchy being changed during [ChildView drawRect:]
358     [mView performSelectorOnMainThread:@selector(delayedTearDown)
359                             withObject:nil
360                          waitUntilDone:false];
361   }
362   mView = nil;
364   NS_OBJC_END_TRY_IGNORE_BLOCK;
367 nsCocoaWindow* nsChildView::GetAppWindowWidget() const {
368   id windowDelegate = [[mView window] delegate];
369   if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
370     return [(WindowDelegate*)windowDelegate geckoWidget];
371   }
372   return nullptr;
375 void nsChildView::Destroy() {
376   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
378   if (mOnDestroyCalled) return;
379   mOnDestroyCalled = true;
381   // Stuff below may delete the last ref to this
382   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
384   {
385     // Make sure that no composition is in progress while disconnecting
386     // ourselves from the view.
387     MutexAutoLock lock(mCompositingLock);
389     [mView widgetDestroyed];
390   }
392   nsBaseWidget::Destroy();
394   NotifyWindowDestroyed();
395   mParentWidget = nil;
397   TearDownView();
399   nsBaseWidget::OnDestroy();
401   NS_OBJC_END_TRY_IGNORE_BLOCK;
404 #pragma mark -
406 #if 0
407 static void PrintViewHierarchy(NSView *view)
409   while (view) {
410     NSLog(@"  view is %x, frame %@", view, NSStringFromRect([view frame]));
411     view = [view superview];
412   }
414 #endif
416 // Return native data according to aDataType
417 void* nsChildView::GetNativeData(uint32_t aDataType) {
418   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
420   void* retVal = nullptr;
422   switch (aDataType) {
423     case NS_NATIVE_WIDGET:
424       retVal = (void*)mView;
425       break;
427     case NS_NATIVE_WINDOW:
428       retVal = [mView window];
429       break;
431     case NS_NATIVE_GRAPHIC:
432       NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
433       retVal = nullptr;
434       break;
436     case NS_NATIVE_OFFSETX:
437       retVal = 0;
438       break;
440     case NS_NATIVE_OFFSETY:
441       retVal = 0;
442       break;
444     case NS_RAW_NATIVE_IME_CONTEXT:
445       retVal = GetPseudoIMEContext();
446       if (retVal) {
447         break;
448       }
449       retVal = [mView inputContext];
450       // If input context isn't available on this widget, we should set |this|
451       // instead of nullptr since if this returns nullptr, IMEStateManager
452       // cannot manage composition with TextComposition instance.  Although,
453       // this case shouldn't occur.
454       if (NS_WARN_IF(!retVal)) {
455         retVal = this;
456       }
457       break;
459     case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID: {
460       NSWindow* win = [mView window];
461       if (win) {
462         retVal = (void*)[win windowNumber];
463       }
464       break;
465     }
466   }
468   return retVal;
470   NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
473 #pragma mark -
475 void nsChildView::SuppressAnimation(bool aSuppress) {
476   GetAppWindowWidget()->SuppressAnimation(aSuppress);
479 bool nsChildView::IsVisible() const {
480   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
482   if (!mVisible) {
483     return mVisible;
484   }
486   if (!GetAppWindowWidget()->IsVisible()) {
487     return false;
488   }
490   // mVisible does not accurately reflect the state of a hidden tabbed view
491   // so verify that the view has a window as well
492   // then check native widget hierarchy visibility
493   return ([mView window] != nil) && !NSIsEmptyRect([mView visibleRect]);
495   NS_OBJC_END_TRY_BLOCK_RETURN(false);
498 // Some NSView methods (e.g. setFrame and setHidden) invalidate the view's
499 // bounds in our window. However, we don't want these invalidations because
500 // they are unnecessary and because they actually slow us down since we
501 // block on the compositor inside drawRect.
502 // When we actually need something invalidated, there will be an explicit call
503 // to Invalidate from Gecko, so turning these automatic invalidations off
504 // won't hurt us in the non-OMTC case.
505 // The invalidations inside these NSView methods happen via a call to the
506 // private method -[NSWindow _setNeedsDisplayInRect:]. Our BaseWindow
507 // implementation of that method is augmented to let us ignore those calls
508 // using -[BaseWindow disable/enableSetNeedsDisplay].
509 static void ManipulateViewWithoutNeedingDisplay(NSView* aView,
510                                                 void (^aCallback)()) {
511   BaseWindow* win = nil;
512   if ([[aView window] isKindOfClass:[BaseWindow class]]) {
513     win = (BaseWindow*)[aView window];
514   }
515   [win disableSetNeedsDisplay];
516   aCallback();
517   [win enableSetNeedsDisplay];
520 // Hide or show this component
521 void nsChildView::Show(bool aState) {
522   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
524   if (aState != mVisible) {
525     // Provide an autorelease pool because this gets called during startup
526     // on the "hidden window", resulting in cocoa object leakage if there's
527     // no pool in place.
528     nsAutoreleasePool localPool;
530     ManipulateViewWithoutNeedingDisplay(mView, ^{
531       [mView setHidden:!aState];
532     });
534     mVisible = aState;
535   }
537   NS_OBJC_END_TRY_IGNORE_BLOCK;
540 // Change the parent of this widget
541 void nsChildView::SetParent(nsIWidget* aNewParent) {
542   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
544   if (mOnDestroyCalled) return;
546   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
548   if (mParentWidget) {
549     mParentWidget->RemoveChild(this);
550   }
552   if (aNewParent) {
553     ReparentNativeWidget(aNewParent);
554   } else {
555     [mView removeFromSuperview];
556     mParentView = nil;
557   }
559   mParentWidget = aNewParent;
561   if (mParentWidget) {
562     mParentWidget->AddChild(this);
563   }
565   NS_OBJC_END_TRY_IGNORE_BLOCK;
568 void nsChildView::ReparentNativeWidget(nsIWidget* aNewParent) {
569   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
571   MOZ_ASSERT(aNewParent, "null widget");
573   if (mOnDestroyCalled) return;
575   NSView<mozView>* newParentView =
576       (NSView<mozView>*)aNewParent->GetNativeData(NS_NATIVE_WIDGET);
577   NS_ENSURE_TRUE_VOID(newParentView);
579   // we hold a ref to mView, so this is safe
580   [mView removeFromSuperview];
581   mParentView = newParentView;
582   [mParentView addSubview:mView];
584   NS_OBJC_END_TRY_IGNORE_BLOCK;
587 void nsChildView::ResetParent() {
588   if (!mOnDestroyCalled) {
589     if (mParentWidget) mParentWidget->RemoveChild(this);
590     if (mView) [mView removeFromSuperview];
591   }
592   mParentWidget = nullptr;
595 nsIWidget* nsChildView::GetParent() { return mParentWidget; }
597 float nsChildView::GetDPI() {
598   float dpi = 96.0;
599   nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
600   if (screen) {
601     screen->GetDpi(&dpi);
602   }
603   return dpi;
606 void nsChildView::Enable(bool aState) {}
608 bool nsChildView::IsEnabled() const { return true; }
610 void nsChildView::SetFocus(Raise, mozilla::dom::CallerType aCallerType) {
611   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
613   NSWindow* window = [mView window];
614   if (window) [window makeFirstResponder:mView];
615   NS_OBJC_END_TRY_IGNORE_BLOCK;
618 // Override to set the cursor on the mac
619 void nsChildView::SetCursor(const Cursor& aCursor) {
620   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
622   if ([mView isDragInProgress]) {
623     return;  // Don't change the cursor during dragging.
624   }
626   nsBaseWidget::SetCursor(aCursor);
628   if (NS_SUCCEEDED([[nsCursorManager sharedInstance]
629             setCustomCursor:aCursor
630           widgetScaleFactor:BackingScaleFactor()])) {
631     return;
632   }
634   [[nsCursorManager sharedInstance] setNonCustomCursor:aCursor];
636   NS_OBJC_END_TRY_IGNORE_BLOCK;
639 #pragma mark -
641 // Get this component dimension
642 LayoutDeviceIntRect nsChildView::GetBounds() {
643   return !mView ? mBounds : CocoaPointsToDevPixels([mView frame]);
646 LayoutDeviceIntRect nsChildView::GetClientBounds() {
647   LayoutDeviceIntRect rect = GetBounds();
648   if (!mParentWidget) {
649     // For top level widgets we want the position on screen, not the position
650     // of this view inside the window.
651     rect.MoveTo(WidgetToScreenOffset());
652   }
653   return rect;
656 LayoutDeviceIntRect nsChildView::GetScreenBounds() {
657   LayoutDeviceIntRect rect = GetBounds();
658   rect.MoveTo(WidgetToScreenOffset());
659   return rect;
662 double nsChildView::GetDefaultScaleInternal() { return BackingScaleFactor(); }
664 CGFloat nsChildView::BackingScaleFactor() const {
665   if (mBackingScaleFactor > 0.0) {
666     return mBackingScaleFactor;
667   }
668   if (!mView) {
669     return 1.0;
670   }
671   mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mView);
672   return mBackingScaleFactor;
675 void nsChildView::BackingScaleFactorChanged() {
676   CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mView);
678   // ignore notification if it hasn't really changed (or maybe we have
679   // disabled HiDPI mode via prefs)
680   if (mBackingScaleFactor == newScale) {
681     return;
682   }
684   SuspendAsyncCATransactions();
685   mBackingScaleFactor = newScale;
686   NSRect frame = [mView frame];
687   mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, newScale);
689   mNativeLayerRoot->SetBackingScale(mBackingScaleFactor);
691   if (mWidgetListener && !mWidgetListener->GetAppWindow()) {
692     if (PresShell* presShell = mWidgetListener->GetPresShell()) {
693       presShell->BackingScaleFactorChanged();
694     }
695   }
698 int32_t nsChildView::RoundsWidgetCoordinatesTo() {
699   if (BackingScaleFactor() == 2.0) {
700     return 2;
701   }
702   return 1;
705 // Move this component, aX and aY are in the parent widget coordinate system
706 void nsChildView::Move(double aX, double aY) {
707   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
709   int32_t x = NSToIntRound(aX);
710   int32_t y = NSToIntRound(aY);
712   if (!mView || (mBounds.x == x && mBounds.y == y)) return;
714   mBounds.x = x;
715   mBounds.y = y;
717   ManipulateViewWithoutNeedingDisplay(mView, ^{
718     [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
719   });
721   ReportMoveEvent();
723   NS_OBJC_END_TRY_IGNORE_BLOCK;
726 void nsChildView::Resize(double aWidth, double aHeight, bool aRepaint) {
727   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
729   int32_t width = NSToIntRound(aWidth);
730   int32_t height = NSToIntRound(aHeight);
732   if (!mView || (mBounds.width == width && mBounds.height == height)) return;
734   SuspendAsyncCATransactions();
735   mBounds.width = width;
736   mBounds.height = height;
738   ManipulateViewWithoutNeedingDisplay(mView, ^{
739     [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
740   });
742   if (mVisible && aRepaint) {
743     [[mView pixelHostingView] setNeedsDisplay:YES];
744   }
746   ReportSizeEvent();
748   NS_OBJC_END_TRY_IGNORE_BLOCK;
751 void nsChildView::Resize(double aX, double aY, double aWidth, double aHeight,
752                          bool aRepaint) {
753   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
755   int32_t x = NSToIntRound(aX);
756   int32_t y = NSToIntRound(aY);
757   int32_t width = NSToIntRound(aWidth);
758   int32_t height = NSToIntRound(aHeight);
760   BOOL isMoving = (mBounds.x != x || mBounds.y != y);
761   BOOL isResizing = (mBounds.width != width || mBounds.height != height);
762   if (!mView || (!isMoving && !isResizing)) return;
764   if (isMoving) {
765     mBounds.x = x;
766     mBounds.y = y;
767   }
768   if (isResizing) {
769     SuspendAsyncCATransactions();
770     mBounds.width = width;
771     mBounds.height = height;
773     CALayer* layer = [mView rootCALayer];
774     double scale = BackingScaleFactor();
775     layer.bounds = CGRectMake(0, 0, width / scale, height / scale);
776   }
778   ManipulateViewWithoutNeedingDisplay(mView, ^{
779     [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
780   });
782   if (mVisible && aRepaint) {
783     [[mView pixelHostingView] setNeedsDisplay:YES];
784   }
786   if (isMoving) {
787     ReportMoveEvent();
788     if (mOnDestroyCalled) return;
789   }
790   if (isResizing) ReportSizeEvent();
792   NS_OBJC_END_TRY_IGNORE_BLOCK;
795 // The following three methods are primarily an attempt to avoid glitches during
796 // window resizing.
797 // Here's some background on how these glitches come to be:
798 // CoreAnimation transactions are per-thread. They don't nest across threads.
799 // If you submit a transaction on the main thread and a transaction on a
800 // different thread, the two will race to the window server and show up on the
801 // screen in the order that they happen to arrive in at the window server.
802 // When the window size changes, there's another event that needs to be
803 // synchronized with: the window "shape" change. Cocoa has built-in
804 // synchronization mechanics that make sure that *main thread* window paints
805 // during window resizes are synchronized properly with the window shape change.
806 // But no such built-in synchronization exists for CATransactions that are
807 // triggered on a non-main thread. To cope with this, we define a "danger zone"
808 // during which we simply avoid triggering any CATransactions on a non-main
809 // thread (called "async" CATransactions here). This danger zone starts at the
810 // earliest opportunity at which we know about the size change, which is
811 // nsChildView::Resize, and ends at a point at which we know for sure that the
812 // paint has been handled completely, which is when we return to the event loop
813 // after layer display.
814 void nsChildView::SuspendAsyncCATransactions() {
815   if (mUnsuspendAsyncCATransactionsRunnable) {
816     mUnsuspendAsyncCATransactionsRunnable->Cancel();
817     mUnsuspendAsyncCATransactionsRunnable = nullptr;
818   }
820   // Make sure that there actually will be a CATransaction on the main thread
821   // during which we get a chance to schedule unsuspension. Otherwise we might
822   // accidentally stay suspended indefinitely.
823   [mView markLayerForDisplay];
825   mNativeLayerRoot->SuspendOffMainThreadCommits();
828 void nsChildView::MaybeScheduleUnsuspendAsyncCATransactions() {
829   if (mNativeLayerRoot->AreOffMainThreadCommitsSuspended() &&
830       !mUnsuspendAsyncCATransactionsRunnable) {
831     mUnsuspendAsyncCATransactionsRunnable = NewCancelableRunnableMethod(
832         "nsChildView::MaybeScheduleUnsuspendAsyncCATransactions", this,
833         &nsChildView::UnsuspendAsyncCATransactions);
834     NS_DispatchToMainThread(mUnsuspendAsyncCATransactionsRunnable);
835   }
838 void nsChildView::UnsuspendAsyncCATransactions() {
839   mUnsuspendAsyncCATransactionsRunnable = nullptr;
841   if (mNativeLayerRoot->UnsuspendOffMainThreadCommits()) {
842     // We need to call mNativeLayerRoot->CommitToScreen() at the next available
843     // opportunity.
844     // The easiest way to handle this request is to mark the layer as needing
845     // display, because this will schedule a main thread CATransaction, during
846     // which HandleMainThreadCATransaction will call CommitToScreen().
847     [mView markLayerForDisplay];
848   }
851 void nsChildView::UpdateFullscreen(bool aFullscreen) {
852   if (mNativeLayerRoot) {
853     mNativeLayerRoot->SetWindowIsFullscreen(aFullscreen);
854   }
857 nsresult nsChildView::SynthesizeNativeKeyEvent(
858     int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
859     uint32_t aModifierFlags, const nsAString& aCharacters,
860     const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) {
861   AutoObserverNotifier notifier(aObserver, "keyevent");
862   return mTextInputHandler->SynthesizeNativeKeyEvent(
863       aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters,
864       aUnmodifiedCharacters);
867 nsresult nsChildView::SynthesizeNativeMouseEvent(
868     LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
869     MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
870     nsIObserver* aObserver) {
871   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
873   AutoObserverNotifier notifier(aObserver, "mouseevent");
875   NSPoint pt =
876       nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
878   // Move the mouse cursor to the requested position and reconnect it to the
879   // mouse.
880   CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
881   CGAssociateMouseAndMouseCursorPosition(true);
883   // aPoint is given with the origin on the top left, but convertScreenToBase
884   // expects a point in a coordinate system that has its origin on the bottom
885   // left.
886   NSPoint screenPoint = NSMakePoint(pt.x, nsCocoaUtils::FlippedScreenY(pt.y));
887   NSPoint windowPoint =
888       nsCocoaUtils::ConvertPointFromScreen([mView window], screenPoint);
889   NSEventModifierFlags modifierFlags =
890       nsCocoaUtils::ConvertWidgetModifiersToMacModifierFlags(aModifierFlags);
892   if (aButton == MouseButton::eX1 || aButton == MouseButton::eX2) {
893     // NSEvent has `buttonNumber` for `NSEventTypeOther*`.  However, it seems
894     // that there is no way to specify it.  Therefore, we should return error
895     // for now.
896     return NS_ERROR_INVALID_ARG;
897   }
899   NSEventType nativeEventType;
900   switch (aNativeMessage) {
901     case NativeMouseMessage::ButtonDown:
902     case NativeMouseMessage::ButtonUp: {
903       switch (aButton) {
904         case MouseButton::ePrimary:
905           nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
906                                 ? NSEventTypeLeftMouseDown
907                                 : NSEventTypeLeftMouseUp;
908           break;
909         case MouseButton::eMiddle:
910           nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
911                                 ? NSEventTypeOtherMouseDown
912                                 : NSEventTypeOtherMouseUp;
913           break;
914         case MouseButton::eSecondary:
915           nativeEventType = aNativeMessage == NativeMouseMessage::ButtonDown
916                                 ? NSEventTypeRightMouseDown
917                                 : NSEventTypeRightMouseUp;
918           break;
919         default:
920           return NS_ERROR_INVALID_ARG;
921       }
922       break;
923     }
924     case NativeMouseMessage::Move:
925       nativeEventType = NSEventTypeMouseMoved;
926       break;
927     case NativeMouseMessage::EnterWindow:
928       nativeEventType = NSEventTypeMouseEntered;
929       break;
930     case NativeMouseMessage::LeaveWindow:
931       nativeEventType = NSEventTypeMouseExited;
932       break;
933   }
935   NSEvent* event =
936       [NSEvent mouseEventWithType:nativeEventType
937                          location:windowPoint
938                     modifierFlags:modifierFlags
939                         timestamp:[[NSProcessInfo processInfo] systemUptime]
940                      windowNumber:[[mView window] windowNumber]
941                           context:nil
942                       eventNumber:0
943                        clickCount:1
944                          pressure:0.0];
946   if (!event) return NS_ERROR_FAILURE;
948   if ([[mView window] isKindOfClass:[BaseWindow class]]) {
949     // Tracking area events don't end up in their tracking areas when sent
950     // through [NSApp sendEvent:], so pass them directly to the right methods.
951     BaseWindow* window = (BaseWindow*)[mView window];
952     if (nativeEventType == NSEventTypeMouseEntered) {
953       [window mouseEntered:event];
954       return NS_OK;
955     }
956     if (nativeEventType == NSEventTypeMouseExited) {
957       [window mouseExited:event];
958       return NS_OK;
959     }
960     if (nativeEventType == NSEventTypeMouseMoved) {
961       [window mouseMoved:event];
962       return NS_OK;
963     }
964   }
966   [NSApp sendEvent:event];
967   return NS_OK;
969   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
972 nsresult nsChildView::SynthesizeNativeMouseScrollEvent(
973     mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
974     double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
975     uint32_t aAdditionalFlags, nsIObserver* aObserver) {
976   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
978   AutoObserverNotifier notifier(aObserver, "mousescrollevent");
980   NSPoint pt =
981       nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
983   // Move the mouse cursor to the requested position and reconnect it to the
984   // mouse.
985   CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
986   CGAssociateMouseAndMouseCursorPosition(true);
988   // Mostly copied from http://stackoverflow.com/a/6130349
989   CGScrollEventUnit units =
990       (aAdditionalFlags & nsIDOMWindowUtils::MOUSESCROLL_SCROLL_LINES)
991           ? kCGScrollEventUnitLine
992           : kCGScrollEventUnitPixel;
993   CGEventRef cgEvent = CGEventCreateScrollWheelEvent(
994       NULL, units, 3, (int32_t)aDeltaY, (int32_t)aDeltaX, (int32_t)aDeltaZ);
995   if (!cgEvent) {
996     return NS_ERROR_FAILURE;
997   }
999   if (aNativeMessage) {
1000     CGEventSetIntegerValueField(cgEvent, kCGScrollWheelEventScrollPhase,
1001                                 aNativeMessage);
1002   }
1004   // On macOS 10.14 and up CGEventPost won't work because of changes in macOS
1005   // to improve security. This code makes an NSEvent corresponding to the
1006   // wheel event and dispatches it directly to the scrollWheel handler. Some
1007   // fiddling is needed with the coordinates in order to simulate what macOS
1008   // would do; this code adapted from the Chromium equivalent function at
1009   // https://chromium.googlesource.com/chromium/src.git/+/62.0.3178.1/ui/events/test/cocoa_test_event_utils.mm#38
1010   CGPoint location = CGEventGetLocation(cgEvent);
1011   location.y += NSMinY([[mView window] frame]);
1012   location.x -= NSMinX([[mView window] frame]);
1013   CGEventSetLocation(cgEvent, location);
1015   uint64_t kNanosPerSec = 1000000000L;
1016   CGEventSetTimestamp(
1017       cgEvent, [[NSProcessInfo processInfo] systemUptime] * kNanosPerSec);
1019   NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
1020   [event setValue:[mView window] forKey:@"_window"];
1021   [mView scrollWheel:event];
1023   CFRelease(cgEvent);
1024   return NS_OK;
1026   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1029 nsresult nsChildView::SynthesizeNativeTouchPoint(
1030     uint32_t aPointerId, TouchPointerState aPointerState,
1031     mozilla::LayoutDeviceIntPoint aPoint, double aPointerPressure,
1032     uint32_t aPointerOrientation, nsIObserver* aObserver) {
1033   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1035   AutoObserverNotifier notifier(aObserver, "touchpoint");
1037   MOZ_ASSERT(NS_IsMainThread());
1038   if (aPointerState == TOUCH_HOVER) {
1039     return NS_ERROR_UNEXPECTED;
1040   }
1042   if (!mSynthesizedTouchInput) {
1043     mSynthesizedTouchInput = MakeUnique<MultiTouchInput>();
1044   }
1046   LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
1047   MultiTouchInput inputToDispatch = UpdateSynthesizedTouchState(
1048       mSynthesizedTouchInput.get(), TimeStamp::Now(), aPointerId, aPointerState,
1049       pointInWindow, aPointerPressure, aPointerOrientation);
1050   DispatchTouchInput(inputToDispatch);
1051   return NS_OK;
1053   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1056 nsresult nsChildView::SynthesizeNativeTouchpadDoubleTap(
1057     mozilla::LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) {
1058   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1060   DispatchDoubleTapGesture(TimeStamp::Now(), aPoint - WidgetToScreenOffset(),
1061                            static_cast<Modifiers>(aModifierFlags));
1063   return NS_OK;
1065   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1068 bool nsChildView::SendEventToNativeMenuSystem(NSEvent* aEvent) {
1069   bool handled = false;
1070   nsCocoaWindow* widget = GetAppWindowWidget();
1071   if (widget) {
1072     nsMenuBarX* mb = widget->GetMenuBar();
1073     if (mb) {
1074       // Check if main menu wants to handle the event.
1075       handled = mb->PerformKeyEquivalent(aEvent);
1076     }
1077   }
1079   if (!handled && sApplicationMenu) {
1080     // Check if application menu wants to handle the event.
1081     handled = [sApplicationMenu performKeyEquivalent:aEvent];
1082   }
1084   return handled;
1087 void nsChildView::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) {
1088   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
1090   // We always allow keyboard events to propagate to keyDown: but if they are
1091   // not handled we give menu items a chance to act. This allows for handling of
1092   // custom shortcuts. Note that existing shortcuts cannot be reassigned yet and
1093   // will have been handled by keyDown: before we get here.
1094   NSMutableDictionary* nativeKeyEventsMap = [ChildView sNativeKeyEventsMap];
1095   NSEvent* cocoaEvent = [nativeKeyEventsMap objectForKey:@(aEvent->mUniqueId)];
1096   if (!cocoaEvent) {
1097     return;
1098   }
1100   // If the escape key is pressed, the expectations are as follows:
1101   // 1. If the page is loading, interrupt loading.
1102   // 2. Give a website an opportunity to handle the event and call
1103   //    preventDefault() on it.
1104   // 3. If the browser is fullscreen and the page isn't loading, exit
1105   //    fullscreen.
1106   // 4. Ignore.
1107   // Case 1 and 2 are handled before we get here. Below, we handle case 3.
1108   if (StaticPrefs::browser_fullscreen_exit_on_escape() &&
1109       [cocoaEvent keyCode] == kVK_Escape &&
1110       [[mView window] styleMask] & NSWindowStyleMaskFullScreen) {
1111     [[mView window] toggleFullScreen:nil];
1112   }
1114   if (SendEventToNativeMenuSystem(cocoaEvent)) {
1115     aEvent->PreventDefault();
1116   }
1117   [nativeKeyEventsMap removeObjectForKey:@(aEvent->mUniqueId)];
1119   NS_OBJC_END_TRY_IGNORE_BLOCK;
1122 // Used for testing native menu system structure and event handling.
1123 nsresult nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString) {
1124   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1126   nsMenuUtilsX::CheckNativeMenuConsistency([NSApp mainMenu]);
1128   NSString* locationString =
1129       [NSString stringWithCharacters:reinterpret_cast<const unichar*>(
1130                                          indexString.BeginReading())
1131                               length:indexString.Length()];
1132   NSMenuItem* item = nsMenuUtilsX::NativeMenuItemWithLocation(
1133       [NSApp mainMenu], locationString, true);
1134   // We can't perform an action on an item with a submenu, that will raise
1135   // an obj-c exception.
1136   if (item && ![item hasSubmenu]) {
1137     NSMenu* parent = [item menu];
1138     if (parent) {
1139       // NSLog(@"Performing action for native menu item titled: %@\n",
1140       //       [[currentSubmenu itemAtIndex:targetIndex] title]);
1141       mozilla::AutoRestore<bool> autoRestore(
1142           nsMenuUtilsX::gIsSynchronouslyActivatingNativeMenuItemDuringTest);
1143       nsMenuUtilsX::gIsSynchronouslyActivatingNativeMenuItemDuringTest = true;
1144       [parent performActionForItemAtIndex:[parent indexOfItem:item]];
1145       return NS_OK;
1146     }
1147   }
1148   return NS_ERROR_FAILURE;
1150   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1153 // Used for testing native menu system structure and event handling.
1154 nsresult nsChildView::ForceUpdateNativeMenuAt(const nsAString& indexString) {
1155   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1157   nsCocoaWindow* widget = GetAppWindowWidget();
1158   if (widget) {
1159     nsMenuBarX* mb = widget->GetMenuBar();
1160     if (mb) {
1161       if (indexString.IsEmpty())
1162         mb->ForceNativeMenuReload();
1163       else
1164         mb->ForceUpdateNativeMenuAt(indexString);
1165     }
1166   }
1167   return NS_OK;
1169   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1172 #pragma mark -
1174 #ifdef INVALIDATE_DEBUGGING
1176 static Boolean KeyDown(const UInt8 theKey) {
1177   KeyMap map;
1178   GetKeys(map);
1179   return ((*((UInt8*)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
1182 static Boolean caps_lock() { return KeyDown(0x39); }
1184 static void blinkRect(Rect* r) {
1185   StRegionFromPool oldClip;
1186   if (oldClip != NULL) ::GetClip(oldClip);
1188   ::ClipRect(r);
1189   ::InvertRect(r);
1190   UInt32 end = ::TickCount() + 5;
1191   while (::TickCount() < end)
1192     ;
1193   ::InvertRect(r);
1195   if (oldClip != NULL) ::SetClip(oldClip);
1198 static void blinkRgn(RgnHandle rgn) {
1199   StRegionFromPool oldClip;
1200   if (oldClip != NULL) ::GetClip(oldClip);
1202   ::SetClip(rgn);
1203   ::InvertRgn(rgn);
1204   UInt32 end = ::TickCount() + 5;
1205   while (::TickCount() < end)
1206     ;
1207   ::InvertRgn(rgn);
1209   if (oldClip != NULL) ::SetClip(oldClip);
1212 #endif
1214 // Invalidate this component's visible area
1215 void nsChildView::Invalidate(const LayoutDeviceIntRect& aRect) {
1216   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
1218   if (!mView || !mVisible) return;
1220   NS_ASSERTION(
1221       GetWindowRenderer()->GetBackendType() != LayersBackend::LAYERS_WR,
1222       "Shouldn't need to invalidate with accelerated OMTC layers!");
1224   EnsureContentLayerForMainThreadPainting();
1225   mContentLayerInvalidRegion.OrWith(aRect.Intersect(GetBounds()));
1226   [mView markLayerForDisplay];
1228   NS_OBJC_END_TRY_IGNORE_BLOCK;
1231 bool nsChildView::WidgetTypeSupportsAcceleration() {
1232   // All widget types support acceleration.
1233   return true;
1236 bool nsChildView::ShouldUseOffMainThreadCompositing() {
1237   // We need to enable OMTC in popups which contain remote layer
1238   // trees, since the remote content won't be rendered at all otherwise.
1239   if (HasRemoteContent()) {
1240     return true;
1241   }
1243   // Don't use OMTC for popup windows, because we do not want context menus to
1244   // pay the overhead of starting up a compositor. With the OpenGL compositor,
1245   // new windows are expensive because of shader re-compilation, and with
1246   // WebRender, new windows are expensive because they create their own threads
1247   // and texture caches.
1248   // Using OMTC with BasicCompositor for context menus would probably be fine
1249   // but isn't a well-tested configuration.
1250   if ([mView window] && [[mView window] isKindOfClass:[PopupWindow class]]) {
1251     // Use main-thread BasicLayerManager for drawing menus.
1252     return false;
1253   }
1255   return nsBaseWidget::ShouldUseOffMainThreadCompositing();
1258 #pragma mark -
1260 // Invokes callback and ProcessEvent methods on Event Listener object
1261 nsresult nsChildView::DispatchEvent(WidgetGUIEvent* event,
1262                                     nsEventStatus& aStatus) {
1263   RefPtr<nsChildView> kungFuDeathGrip(this);
1265 #ifdef DEBUG
1266   debug_DumpEvent(stdout, event->mWidget, event, "something", 0);
1267 #endif
1269   if (event->mFlags.mIsSynthesizedForTests) {
1270     WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent();
1271     if (keyEvent) {
1272       nsresult rv = mTextInputHandler->AttachNativeKeyEvent(*keyEvent);
1273       NS_ENSURE_SUCCESS(rv, rv);
1274     }
1275   }
1277   aStatus = nsEventStatus_eIgnore;
1279   nsIWidgetListener* listener = mWidgetListener;
1281   // If the listener is NULL, check if the parent is a popup. If it is, then
1282   // this child is the popup content view attached to a popup. Get the
1283   // listener from the parent popup instead.
1284   nsCOMPtr<nsIWidget> parentWidget = mParentWidget;
1285   if (!listener && parentWidget) {
1286     if (parentWidget->GetWindowType() == WindowType::Popup) {
1287       // Check just in case event->mWidget isn't this widget
1288       if (event->mWidget) {
1289         listener = event->mWidget->GetWidgetListener();
1290       }
1291       if (!listener) {
1292         event->mWidget = parentWidget;
1293         listener = parentWidget->GetWidgetListener();
1294       }
1295     }
1296   }
1298   if (listener) aStatus = listener->HandleEvent(event, mUseAttachedEvents);
1300   return NS_OK;
1303 nsIWidget* nsChildView::GetWidgetForListenerEvents() {
1304   // If there is no listener, use the parent popup's listener if that exists.
1305   if (!mWidgetListener && mParentWidget &&
1306       mParentWidget->GetWindowType() == WindowType::Popup) {
1307     return mParentWidget;
1308   }
1310   return this;
1313 void nsChildView::WillPaintWindow() {
1314   nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();
1316   nsIWidgetListener* listener = widget->GetWidgetListener();
1317   if (listener) {
1318     listener->WillPaintWindow(widget);
1319   }
1322 bool nsChildView::PaintWindow(LayoutDeviceIntRegion aRegion) {
1323   nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();
1325   nsIWidgetListener* listener = widget->GetWidgetListener();
1326   if (!listener) return false;
1328   bool returnValue = false;
1329   bool oldDispatchPaint = mIsDispatchPaint;
1330   mIsDispatchPaint = true;
1331   returnValue = listener->PaintWindow(widget, aRegion);
1333   listener = widget->GetWidgetListener();
1334   if (listener) {
1335     listener->DidPaintWindow();
1336   }
1338   mIsDispatchPaint = oldDispatchPaint;
1339   return returnValue;
1342 bool nsChildView::PaintWindowInDrawTarget(gfx::DrawTarget* aDT,
1343                                           const LayoutDeviceIntRegion& aRegion,
1344                                           const gfx::IntSize& aSurfaceSize) {
1345   if (!aDT || !aDT->IsValid()) {
1346     return false;
1347   }
1348   gfxContext targetContext(aDT);
1350   // Set up the clip region and clear existing contents in the backing surface.
1351   targetContext.NewPath();
1352   for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
1353     const LayoutDeviceIntRect& r = iter.Get();
1354     targetContext.Rectangle(gfxRect(r.x, r.y, r.width, r.height));
1355     aDT->ClearRect(gfx::Rect(r.ToUnknownRect()));
1356   }
1357   targetContext.Clip();
1359   nsAutoRetainCocoaObject kungFuDeathGrip(mView);
1360   if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) {
1361     nsBaseWidget::AutoLayerManagerSetup setupLayerManager(
1362         this, &targetContext, BufferMode::BUFFER_NONE);
1363     return PaintWindow(aRegion);
1364   }
1365   return false;
1368 void nsChildView::EnsureContentLayerForMainThreadPainting() {
1369   // Ensure we have an mContentLayer of the correct size.
1370   // The content layer gets created on demand for BasicLayers windows. We do
1371   // not create it during widget creation because, for non-BasicLayers windows,
1372   // the compositing layer manager will create any layers it needs.
1373   gfx::IntSize size = GetBounds().Size().ToUnknownSize();
1374   if (mContentLayer && mContentLayer->GetSize() != size) {
1375     mNativeLayerRoot->RemoveLayer(mContentLayer);
1376     mContentLayer = nullptr;
1377   }
1378   if (!mContentLayer) {
1379     mPoolHandle = SurfacePool::Create(0)->GetHandleForGL(nullptr);
1380     RefPtr<NativeLayer> contentLayer =
1381         mNativeLayerRoot->CreateLayer(size, false, mPoolHandle);
1382     mNativeLayerRoot->AppendLayer(contentLayer);
1383     mContentLayer = contentLayer->AsNativeLayerCA();
1384     mContentLayer->SetSurfaceIsFlipped(false);
1385     mContentLayerInvalidRegion = GetBounds();
1386   }
1389 void nsChildView::PaintWindowInContentLayer() {
1390   EnsureContentLayerForMainThreadPainting();
1391   mPoolHandle->OnBeginFrame();
1392   RefPtr<DrawTarget> dt = mContentLayer->NextSurfaceAsDrawTarget(
1393       gfx::IntRect({}, mContentLayer->GetSize()),
1394       mContentLayerInvalidRegion.ToUnknownRegion(), gfx::BackendType::SKIA);
1395   if (!dt) {
1396     return;
1397   }
1399   PaintWindowInDrawTarget(dt, mContentLayerInvalidRegion, dt->GetSize());
1400   mContentLayer->NotifySurfaceReady();
1401   mContentLayerInvalidRegion.SetEmpty();
1402   mPoolHandle->OnEndFrame();
1405 void nsChildView::HandleMainThreadCATransaction() {
1406   WillPaintWindow();
1408   if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) {
1409     // We're in BasicLayers mode, i.e. main thread software compositing.
1410     // Composite the window into our layer's surface.
1411     PaintWindowInContentLayer();
1412   } else {
1413     // Trigger a synchronous OMTC composite. This will call NextSurface and
1414     // NotifySurfaceReady on the compositor thread to update mNativeLayerRoot's
1415     // contents, and the main thread (this thread) will wait inside PaintWindow
1416     // during that time.
1417     PaintWindow(LayoutDeviceIntRegion(GetBounds()));
1418   }
1420   {
1421     // Apply the changes inside mNativeLayerRoot to the underlying CALayers. Now
1422     // is a good time to call this because we know we're currently inside a main
1423     // thread CATransaction, and the lock makes sure that no composition is
1424     // currently in progress, so we won't present half-composited state to the
1425     // screen.
1426     MutexAutoLock lock(mCompositingLock);
1427     mNativeLayerRoot->CommitToScreen();
1428   }
1430   MaybeScheduleUnsuspendAsyncCATransactions();
1433 #pragma mark -
1435 void nsChildView::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); }
1437 void nsChildView::ReportSizeEvent() {
1438   if (mWidgetListener)
1439     mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
1442 #pragma mark -
1444 LayoutDeviceIntPoint nsChildView::GetClientOffset() {
1445   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1447   NSPoint origin = [mView convertPoint:NSMakePoint(0, 0) toView:nil];
1448   origin.y = [[mView window] frame].size.height - origin.y;
1449   return CocoaPointsToDevPixels(origin);
1451   NS_OBJC_END_TRY_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
1454 //    Return the offset between this child view and the screen.
1455 //    @return       -- widget origin in device-pixel coords
1456 LayoutDeviceIntPoint nsChildView::WidgetToScreenOffset() {
1457   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1459   NSPoint origin = NSMakePoint(0, 0);
1461   // 1. First translate view origin point into window coords.
1462   // The returned point is in bottom-left coordinates.
1463   origin = [mView convertPoint:origin toView:nil];
1465   // 2. We turn the window-coord rect's origin into screen (still bottom-left)
1466   // coords.
1467   origin = nsCocoaUtils::ConvertPointToScreen([mView window], origin);
1469   // 3. Since we're dealing in bottom-left coords, we need to make it top-left
1470   // coords
1471   //    before we pass it back to Gecko.
1472   FlipCocoaScreenCoordinate(origin);
1474   // convert to device pixels
1475   return CocoaPointsToDevPixels(origin);
1477   NS_OBJC_END_TRY_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0));
1480 nsresult nsChildView::SetTitle(const nsAString& title) {
1481   // child views don't have titles
1482   return NS_OK;
1485 nsresult nsChildView::GetAttention(int32_t aCycleCount) {
1486   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
1488   [NSApp requestUserAttention:NSInformationalRequest];
1489   return NS_OK;
1491   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
1494 /* static */
1495 bool nsChildView::DoHasPendingInputEvent() {
1496   return sLastInputEventCount != GetCurrentInputEventCount();
1499 /* static */
1500 uint32_t nsChildView::GetCurrentInputEventCount() {
1501   // Can't use kCGAnyInputEventType because that updates too rarely for us (and
1502   // always in increments of 30+!) and because apparently it's sort of broken
1503   // on Tiger.  So just go ahead and query the counters we care about.
1504   static const CGEventType eventTypes[] = {kCGEventLeftMouseDown,
1505                                            kCGEventLeftMouseUp,
1506                                            kCGEventRightMouseDown,
1507                                            kCGEventRightMouseUp,
1508                                            kCGEventMouseMoved,
1509                                            kCGEventLeftMouseDragged,
1510                                            kCGEventRightMouseDragged,
1511                                            kCGEventKeyDown,
1512                                            kCGEventKeyUp,
1513                                            kCGEventScrollWheel,
1514                                            kCGEventTabletPointer,
1515                                            kCGEventOtherMouseDown,
1516                                            kCGEventOtherMouseUp,
1517                                            kCGEventOtherMouseDragged};
1519   uint32_t eventCount = 0;
1520   for (uint32_t i = 0; i < ArrayLength(eventTypes); ++i) {
1521     eventCount += CGEventSourceCounterForEventType(
1522         kCGEventSourceStateCombinedSessionState, eventTypes[i]);
1523   }
1524   return eventCount;
1527 /* static */
1528 void nsChildView::UpdateCurrentInputEventCount() {
1529   sLastInputEventCount = GetCurrentInputEventCount();
1532 bool nsChildView::HasPendingInputEvent() { return DoHasPendingInputEvent(); }
1534 #pragma mark -
1536 void nsChildView::SetInputContext(const InputContext& aContext,
1537                                   const InputContextAction& aAction) {
1538   NS_ENSURE_TRUE_VOID(mTextInputHandler);
1540   if (mTextInputHandler->IsFocused()) {
1541     if (aContext.IsPasswordEditor()) {
1542       TextInputHandler::EnableSecureEventInput();
1543     } else {
1544       TextInputHandler::EnsureSecureEventInputDisabled();
1545     }
1546   }
1548   // IMEInputHandler::IsEditableContent() returns false when both
1549   // IsASCIICableOnly() and IsIMEEnabled() return false.  So, be careful
1550   // when you change the following code.  You might need to change
1551   // IMEInputHandler::IsEditableContent() too.
1552   mInputContext = aContext;
1553   switch (aContext.mIMEState.mEnabled) {
1554     case IMEEnabled::Enabled:
1555       mTextInputHandler->SetASCIICapableOnly(false);
1556       mTextInputHandler->EnableIME(true);
1557       if (mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) {
1558         mTextInputHandler->SetIMEOpenState(mInputContext.mIMEState.mOpen ==
1559                                            IMEState::OPEN);
1560       }
1561       break;
1562     case IMEEnabled::Disabled:
1563       mTextInputHandler->SetASCIICapableOnly(false);
1564       mTextInputHandler->EnableIME(false);
1565       break;
1566     case IMEEnabled::Password:
1567       mTextInputHandler->SetASCIICapableOnly(true);
1568       mTextInputHandler->EnableIME(false);
1569       break;
1570     default:
1571       NS_ERROR("not implemented!");
1572   }
1575 InputContext nsChildView::GetInputContext() {
1576   switch (mInputContext.mIMEState.mEnabled) {
1577     case IMEEnabled::Enabled:
1578       if (mTextInputHandler) {
1579         mInputContext.mIMEState.mOpen = mTextInputHandler->IsIMEOpened()
1580                                             ? IMEState::OPEN
1581                                             : IMEState::CLOSED;
1582         break;
1583       }
1584       // If mTextInputHandler is null, set CLOSED instead...
1585       [[fallthrough]];
1586     default:
1587       mInputContext.mIMEState.mOpen = IMEState::CLOSED;
1588       break;
1589   }
1590   return mInputContext;
1593 TextEventDispatcherListener*
1594 nsChildView::GetNativeTextEventDispatcherListener() {
1595   if (NS_WARN_IF(!mTextInputHandler)) {
1596     return nullptr;
1597   }
1598   return mTextInputHandler;
1601 nsresult nsChildView::AttachNativeKeyEvent(
1602     mozilla::WidgetKeyboardEvent& aEvent) {
1603   NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
1604   return mTextInputHandler->AttachNativeKeyEvent(aEvent);
1607 bool nsChildView::GetEditCommands(NativeKeyBindingsType aType,
1608                                   const WidgetKeyboardEvent& aEvent,
1609                                   nsTArray<CommandInt>& aCommands) {
1610   // Validate the arguments.
1611   if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType, aEvent, aCommands))) {
1612     return false;
1613   }
1615   Maybe<WritingMode> writingMode;
1616   if (aEvent.NeedsToRemapNavigationKey()) {
1617     if (RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher()) {
1618       writingMode = dispatcher->MaybeQueryWritingModeAtSelection();
1619     }
1620   }
1622   NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
1623   keyBindings->GetEditCommands(aEvent, writingMode, aCommands);
1624   return true;
1627 NSView<mozView>* nsChildView::GetEditorView() {
1628   NSView<mozView>* editorView = mView;
1629   // We need to get editor's view. E.g., when the focus is in the bookmark
1630   // dialog, the view is <panel> element of the dialog.  At this time, the key
1631   // events are processed the parent window's view that has native focus.
1632   WidgetQueryContentEvent queryContentState(true, eQueryContentState, this);
1633   // This may be called during creating a menu popup frame due to creating
1634   // widget synchronously and that causes Cocoa asking current window level.
1635   // In this case, it's not safe to flush layout on the document and we don't
1636   // need any layout information right now.
1637   queryContentState.mNeedsToFlushLayout = false;
1638   DispatchWindowEvent(queryContentState);
1639   if (queryContentState.Succeeded() &&
1640       queryContentState.mReply->mFocusedWidget) {
1641     NSView<mozView>* view = static_cast<NSView<mozView>*>(
1642         queryContentState.mReply->mFocusedWidget->GetNativeData(
1643             NS_NATIVE_WIDGET));
1644     if (view) editorView = view;
1645   }
1646   return editorView;
1649 #pragma mark -
1651 void nsChildView::CreateCompositor() {
1652   nsBaseWidget::CreateCompositor();
1653   if (mCompositorBridgeChild) {
1654     [mView setUsingOMTCompositor:true];
1655   }
1658 void nsChildView::ConfigureAPZCTreeManager() {
1659   nsBaseWidget::ConfigureAPZCTreeManager();
1662 void nsChildView::ConfigureAPZControllerThread() {
1663   nsBaseWidget::ConfigureAPZControllerThread();
1666 bool nsChildView::PreRender(WidgetRenderingContext* aContext)
1667     MOZ_NO_THREAD_SAFETY_ANALYSIS {
1668   // The lock makes sure that we don't attempt to tear down the view while
1669   // compositing. That would make us unable to call postRender on it when the
1670   // composition is done, thus keeping the GL context locked forever.
1671   mCompositingLock.Lock();
1673   if (aContext->mGL && gfxPlatform::CanMigrateMacGPUs()) {
1674     GLContextCGL::Cast(aContext->mGL)->MigrateToActiveGPU();
1675   }
1677   return true;
1680 void nsChildView::PostRender(WidgetRenderingContext* aContext)
1681     MOZ_NO_THREAD_SAFETY_ANALYSIS {
1682   mCompositingLock.Unlock();
1685 RefPtr<layers::NativeLayerRoot> nsChildView::GetNativeLayerRoot() {
1686   return mNativeLayerRoot;
1689 static int32_t FindTitlebarBottom(
1690     const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
1691     int32_t aWindowWidth) {
1692   int32_t titlebarBottom = 0;
1693   for (auto& g : aThemeGeometries) {
1694     if (g.mType == eThemeGeometryTypeTitlebar && g.mRect.X() <= 0 &&
1695         g.mRect.XMost() >= aWindowWidth && g.mRect.Y() <= 0) {
1696       titlebarBottom = std::max(titlebarBottom, g.mRect.YMost());
1697     }
1698   }
1699   return titlebarBottom;
1702 static int32_t FindUnifiedToolbarBottom(
1703     const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
1704     int32_t aWindowWidth, int32_t aTitlebarBottom) {
1705   int32_t unifiedToolbarBottom = aTitlebarBottom;
1706   for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) {
1707     const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
1708     if ((g.mType == eThemeGeometryTypeToolbar) && g.mRect.X() <= 0 &&
1709         g.mRect.XMost() >= aWindowWidth && g.mRect.Y() <= aTitlebarBottom) {
1710       unifiedToolbarBottom = std::max(unifiedToolbarBottom, g.mRect.YMost());
1711     }
1712   }
1713   return unifiedToolbarBottom;
1716 static LayoutDeviceIntRect FindFirstRectOfType(
1717     const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
1718     nsITheme::ThemeGeometryType aThemeGeometryType) {
1719   for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) {
1720     const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
1721     if (g.mType == aThemeGeometryType) {
1722       return g.mRect;
1723     }
1724   }
1725   return LayoutDeviceIntRect();
1728 void nsChildView::UpdateThemeGeometries(
1729     const nsTArray<ThemeGeometry>& aThemeGeometries) {
1730   if (![mView window]) return;
1732   UpdateVibrancy(aThemeGeometries);
1734   if (![[mView window] isKindOfClass:[ToolbarWindow class]]) return;
1736   // Update unified toolbar height and sheet attachment position.
1737   int32_t windowWidth = mBounds.width;
1738   int32_t titlebarBottom = FindTitlebarBottom(aThemeGeometries, windowWidth);
1739   int32_t unifiedToolbarBottom =
1740       FindUnifiedToolbarBottom(aThemeGeometries, windowWidth, titlebarBottom);
1741   int32_t toolboxBottom =
1742       FindFirstRectOfType(aThemeGeometries, eThemeGeometryTypeToolbox).YMost();
1744   ToolbarWindow* win = (ToolbarWindow*)[mView window];
1745   int32_t titlebarHeight = [win drawsContentsIntoWindowFrame]
1746                                ? 0
1747                                : CocoaPointsToDevPixels([win titlebarHeight]);
1748   int32_t devUnifiedHeight = titlebarHeight + unifiedToolbarBottom;
1749   [win setUnifiedToolbarHeight:DevPixelsToCocoaPoints(devUnifiedHeight)];
1751   int32_t sheetPositionDevPx = std::max(toolboxBottom, unifiedToolbarBottom);
1752   NSPoint sheetPositionView = {0, DevPixelsToCocoaPoints(sheetPositionDevPx)};
1753   NSPoint sheetPositionWindow = [mView convertPoint:sheetPositionView
1754                                              toView:nil];
1755   [win setSheetAttachmentPosition:sheetPositionWindow.y];
1757   // Update titlebar control offsets.
1758   LayoutDeviceIntRect windowButtonRect =
1759       FindFirstRectOfType(aThemeGeometries, eThemeGeometryTypeWindowButtons);
1760   [win placeWindowButtons:[mView convertRect:DevPixelsToCocoaPoints(
1761                                                  windowButtonRect)
1762                                       toView:nil]];
1765 static Maybe<VibrancyType> ThemeGeometryTypeToVibrancyType(
1766     nsITheme::ThemeGeometryType aThemeGeometryType) {
1767   switch (aThemeGeometryType) {
1768     case eThemeGeometryTypeTooltip:
1769       return Some(VibrancyType::TOOLTIP);
1770     case eThemeGeometryTypeMenu:
1771       return Some(VibrancyType::MENU);
1772     case eThemeGeometryTypeHighlightedMenuItem:
1773       return Some(VibrancyType::HIGHLIGHTED_MENUITEM);
1774     case eThemeGeometryTypeSourceList:
1775       return Some(VibrancyType::SOURCE_LIST);
1776     case eThemeGeometryTypeSourceListSelection:
1777       return Some(VibrancyType::SOURCE_LIST_SELECTION);
1778     case eThemeGeometryTypeActiveSourceListSelection:
1779       return Some(VibrancyType::ACTIVE_SOURCE_LIST_SELECTION);
1780     default:
1781       return Nothing();
1782   }
1785 static LayoutDeviceIntRegion GatherVibrantRegion(
1786     const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
1787     VibrancyType aVibrancyType) {
1788   LayoutDeviceIntRegion region;
1789   for (auto& geometry : aThemeGeometries) {
1790     if (ThemeGeometryTypeToVibrancyType(geometry.mType) ==
1791         Some(aVibrancyType)) {
1792       region.OrWith(geometry.mRect);
1793     }
1794   }
1795   return region;
1798 template <typename Region>
1799 static void MakeRegionsNonOverlappingImpl(Region& aOutUnion) {}
1801 template <typename Region, typename... Regions>
1802 static void MakeRegionsNonOverlappingImpl(Region& aOutUnion, Region& aFirst,
1803                                           Regions&... aRest) {
1804   MakeRegionsNonOverlappingImpl(aOutUnion, aRest...);
1805   aFirst.SubOut(aOutUnion);
1806   aOutUnion.OrWith(aFirst);
1809 // Subtracts parts from regions in such a way that they don't have any overlap.
1810 // Each region in the argument list will have the union of all the regions
1811 // *following* it subtracted from itself. In other words, the arguments are
1812 // sorted low priority to high priority.
1813 template <typename Region, typename... Regions>
1814 static void MakeRegionsNonOverlapping(Region& aFirst, Regions&... aRest) {
1815   Region unionOfAll;
1816   MakeRegionsNonOverlappingImpl(unionOfAll, aFirst, aRest...);
1819 void nsChildView::UpdateVibrancy(
1820     const nsTArray<ThemeGeometry>& aThemeGeometries) {
1821   LayoutDeviceIntRegion menuRegion =
1822       GatherVibrantRegion(aThemeGeometries, VibrancyType::MENU);
1823   LayoutDeviceIntRegion tooltipRegion =
1824       GatherVibrantRegion(aThemeGeometries, VibrancyType::TOOLTIP);
1825   LayoutDeviceIntRegion highlightedMenuItemRegion =
1826       GatherVibrantRegion(aThemeGeometries, VibrancyType::HIGHLIGHTED_MENUITEM);
1827   LayoutDeviceIntRegion sourceListRegion =
1828       GatherVibrantRegion(aThemeGeometries, VibrancyType::SOURCE_LIST);
1829   LayoutDeviceIntRegion sourceListSelectionRegion = GatherVibrantRegion(
1830       aThemeGeometries, VibrancyType::SOURCE_LIST_SELECTION);
1831   LayoutDeviceIntRegion activeSourceListSelectionRegion = GatherVibrantRegion(
1832       aThemeGeometries, VibrancyType::ACTIVE_SOURCE_LIST_SELECTION);
1834   MakeRegionsNonOverlapping(
1835       menuRegion, tooltipRegion, highlightedMenuItemRegion, sourceListRegion,
1836       sourceListSelectionRegion, activeSourceListSelectionRegion);
1838   auto& vm = EnsureVibrancyManager();
1839   bool changed = false;
1840   changed |= vm.UpdateVibrantRegion(VibrancyType::MENU, menuRegion);
1841   changed |= vm.UpdateVibrantRegion(VibrancyType::TOOLTIP, tooltipRegion);
1842   changed |= vm.UpdateVibrantRegion(VibrancyType::HIGHLIGHTED_MENUITEM,
1843                                     highlightedMenuItemRegion);
1844   changed |=
1845       vm.UpdateVibrantRegion(VibrancyType::SOURCE_LIST, sourceListRegion);
1846   changed |= vm.UpdateVibrantRegion(VibrancyType::SOURCE_LIST_SELECTION,
1847                                     sourceListSelectionRegion);
1848   changed |= vm.UpdateVibrantRegion(VibrancyType::ACTIVE_SOURCE_LIST_SELECTION,
1849                                     activeSourceListSelectionRegion);
1851   if (changed) {
1852     SuspendAsyncCATransactions();
1853   }
1856 mozilla::VibrancyManager& nsChildView::EnsureVibrancyManager() {
1857   MOZ_ASSERT(mView, "Only call this once we have a view!");
1858   if (!mVibrancyManager) {
1859     mVibrancyManager =
1860         MakeUnique<VibrancyManager>(*this, [mView vibrancyViewsContainer]);
1861   }
1862   return *mVibrancyManager;
1865 void nsChildView::UpdateBoundsFromView() {
1866   auto oldSize = mBounds.Size();
1867   mBounds = CocoaPointsToDevPixels([mView frame]);
1868   if (mBounds.Size() != oldSize) {
1869     SuspendAsyncCATransactions();
1870   }
1873 @interface NonDraggableView : NSView
1874 @end
1876 @implementation NonDraggableView
1877 - (BOOL)mouseDownCanMoveWindow {
1878   return NO;
1880 - (NSView*)hitTest:(NSPoint)aPoint {
1881   return nil;
1883 - (NSRect)_opaqueRectForWindowMoveWhenInTitlebar {
1884   // In NSWindows that use NSWindowStyleMaskFullSizeContentView, NSViews which
1885   // overlap the titlebar do not disable window dragging in the overlapping
1886   // areas even if they return NO from mouseDownCanMoveWindow. This can have
1887   // unfortunate effects: For example, dragging tabs in a browser window would
1888   // move the window if those tabs are in the titlebar.
1889   // macOS does not seem to offer a documented way to opt-out of the forced
1890   // window dragging in the titlebar.
1891   // Overriding _opaqueRectForWindowMoveWhenInTitlebar is an undocumented way
1892   // of opting out of this behavior. This method was added in 10.11 and is used
1893   // by some NSControl subclasses to prevent window dragging in the titlebar.
1894   // The function which assembles the draggable area of the window calls
1895   // _opaqueRect for the content area and _opaqueRectForWindowMoveWhenInTitlebar
1896   // for the titlebar area, on all visible NSViews. The default implementation
1897   // of _opaqueRect returns [self visibleRect], and the default implementation
1898   // of _opaqueRectForWindowMoveWhenInTitlebar returns NSZeroRect unless it's
1899   // overridden.
1900   //
1901   // Since this view is constructed and used such that its entire bounds is the
1902   // relevant region, we just return our bounds.
1903   return self.bounds;
1905 @end
1907 void nsChildView::UpdateWindowDraggingRegion(
1908     const LayoutDeviceIntRegion& aRegion) {
1909   // mView returns YES from mouseDownCanMoveWindow, so we need to put NSViews
1910   // that return NO from mouseDownCanMoveWindow in the places that shouldn't
1911   // be draggable. We can't do it the other way round because returning
1912   // YES from mouseDownCanMoveWindow doesn't have any effect if there's a
1913   // superview that returns NO.
1914   LayoutDeviceIntRegion nonDraggable;
1915   nonDraggable.Sub(LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height),
1916                    aRegion);
1918   __block bool changed = false;
1920   // Suppress calls to setNeedsDisplay during NSView geometry changes.
1921   ManipulateViewWithoutNeedingDisplay(mView, ^() {
1922     changed = mNonDraggableRegion.UpdateRegion(
1923         nonDraggable, *this, [mView nonDraggableViewsContainer], ^() {
1924           return [[NonDraggableView alloc] initWithFrame:NSZeroRect];
1925         });
1926   });
1928   if (changed) {
1929     // Trigger an update to the window server. This will call
1930     // mouseDownCanMoveWindow.
1931     // Doing this manually is only necessary because we're suppressing
1932     // setNeedsDisplay calls above.
1933     [[mView window] setMovableByWindowBackground:NO];
1934     [[mView window] setMovableByWindowBackground:YES];
1935   }
1938 nsEventStatus nsChildView::DispatchAPZInputEvent(InputData& aEvent) {
1939   APZEventResult result;
1941   if (mAPZC) {
1942     result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
1943   }
1945   if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1946     return result.GetStatus();
1947   }
1949   if (aEvent.mInputType == PINCHGESTURE_INPUT) {
1950     PinchGestureInput& pinchEvent = aEvent.AsPinchGestureInput();
1951     WidgetWheelEvent wheelEvent = pinchEvent.ToWidgetEvent(this);
1952     ProcessUntransformedAPZEvent(&wheelEvent, result);
1953   } else if (aEvent.mInputType == TAPGESTURE_INPUT) {
1954     TapGestureInput& tapEvent = aEvent.AsTapGestureInput();
1955     WidgetSimpleGestureEvent gestureEvent = tapEvent.ToWidgetEvent(this);
1956     ProcessUntransformedAPZEvent(&gestureEvent, result);
1957   } else {
1958     MOZ_ASSERT_UNREACHABLE();
1959   }
1961   return result.GetStatus();
1964 void nsChildView::DispatchAPZWheelInputEvent(InputData& aEvent) {
1965   if (mSwipeTracker && aEvent.mInputType == PANGESTURE_INPUT) {
1966     // Give the swipe tracker a first pass at the event. If a new pan gesture
1967     // has been started since the beginning of the swipe, the swipe tracker
1968     // will know to ignore the event.
1969     nsEventStatus status =
1970         mSwipeTracker->ProcessEvent(aEvent.AsPanGestureInput());
1971     if (status == nsEventStatus_eConsumeNoDefault) {
1972       return;
1973     }
1974   }
1976   WidgetWheelEvent event(true, eWheel, this);
1978   if (mAPZC) {
1979     APZEventResult result;
1981     switch (aEvent.mInputType) {
1982       case PANGESTURE_INPUT: {
1983         result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
1984         if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1985           return;
1986         }
1988         event = MayStartSwipeForAPZ(aEvent.AsPanGestureInput(), result);
1989         break;
1990       }
1991       case SCROLLWHEEL_INPUT: {
1992         // For wheel events on OS X, send it to APZ using the WidgetInputEvent
1993         // variant of ReceiveInputEvent, because the APZInputBridge version of
1994         // that function has special handling (for delta multipliers etc.) that
1995         // we need to run. Using the InputData variant would bypass that and
1996         // go straight to the APZCTreeManager subclass.
1997         event = aEvent.AsScrollWheelInput().ToWidgetEvent(this);
1998         result = mAPZC->InputBridge()->ReceiveInputEvent(event);
1999         if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
2000           return;
2001         }
2002         break;
2003       };
2004       default:
2005         MOZ_CRASH("unsupported event type");
2006         return;
2007     }
2008     if (event.mMessage == eWheel &&
2009         (event.mDeltaX != 0 || event.mDeltaY != 0)) {
2010       ProcessUntransformedAPZEvent(&event, result);
2011     }
2012     return;
2013   }
2015   nsEventStatus status;
2016   switch (aEvent.mInputType) {
2017     case PANGESTURE_INPUT: {
2018       if (MayStartSwipeForNonAPZ(aEvent.AsPanGestureInput())) {
2019         return;
2020       }
2021       event = aEvent.AsPanGestureInput().ToWidgetEvent(this);
2022       break;
2023     }
2024     case SCROLLWHEEL_INPUT: {
2025       event = aEvent.AsScrollWheelInput().ToWidgetEvent(this);
2026       break;
2027     }
2028     default:
2029       MOZ_CRASH("unexpected event type");
2030       return;
2031   }
2032   if (event.mMessage == eWheel && (event.mDeltaX != 0 || event.mDeltaY != 0)) {
2033     DispatchEvent(&event, status);
2034   }
2037 void nsChildView::DispatchDoubleTapGesture(TimeStamp aEventTimeStamp,
2038                                            LayoutDeviceIntPoint aScreenPosition,
2039                                            mozilla::Modifiers aModifiers) {
2040   if (StaticPrefs::apz_mac_enable_double_tap_zoom_touchpad_gesture()) {
2041     TapGestureInput event{
2042         TapGestureInput::TAPGESTURE_DOUBLE, aEventTimeStamp,
2043         ViewAs<ScreenPixel>(
2044             aScreenPosition,
2045             PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent),
2046         aModifiers};
2048     DispatchAPZInputEvent(event);
2049   } else {
2050     // Setup the "double tap" event.
2051     WidgetSimpleGestureEvent geckoEvent(true, eTapGesture, this);
2052     // do what convertCocoaMouseEvent does basically.
2053     geckoEvent.mRefPoint = aScreenPosition;
2054     geckoEvent.mModifiers = aModifiers;
2055     geckoEvent.mTimeStamp = aEventTimeStamp;
2056     geckoEvent.mClickCount = 1;
2058     // Send the event.
2059     DispatchWindowEvent(geckoEvent);
2060   }
2063 void nsChildView::LookUpDictionary(
2064     const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRangeArray,
2065     const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) {
2066   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2068   NSMutableAttributedString* attrStr =
2069       nsCocoaUtils::GetNSMutableAttributedString(
2070           aText, aFontRangeArray, aIsVertical, BackingScaleFactor());
2071   NSPoint pt =
2072       nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
2073   NSDictionary* attributes = [attrStr attributesAtIndex:0 effectiveRange:nil];
2074   NSFont* font = [attributes objectForKey:NSFontAttributeName];
2075   if (font) {
2076     if (aIsVertical) {
2077       pt.x -= [font descender];
2078     } else {
2079       pt.y += [font ascender];
2080     }
2081   }
2083   [mView showDefinitionForAttributedString:attrStr atPoint:pt];
2085   NS_OBJC_END_TRY_IGNORE_BLOCK;
2088 #ifdef ACCESSIBILITY
2089 already_AddRefed<a11y::LocalAccessible> nsChildView::GetDocumentAccessible() {
2090   if (!mozilla::a11y::ShouldA11yBeEnabled()) return nullptr;
2092   // mAccessible might be dead if accessibility was previously disabled and is
2093   // now being enabled again.
2094   if (mAccessible && mAccessible->IsAlive()) {
2095     RefPtr<a11y::LocalAccessible> ret;
2096     CallQueryReferent(mAccessible.get(), static_cast<a11y::LocalAccessible**>(
2097                                              getter_AddRefs(ret)));
2098     return ret.forget();
2099   }
2101   // need to fetch the accessible anew, because it has gone away.
2102   // cache the accessible in our weak ptr
2103   RefPtr<a11y::LocalAccessible> acc = GetRootAccessible();
2104   mAccessible = do_GetWeakReference(acc.get());
2106   return acc.forget();
2108 #endif
2110 class WidgetsReleaserRunnable final : public mozilla::Runnable {
2111  public:
2112   explicit WidgetsReleaserRunnable(nsTArray<nsCOMPtr<nsIWidget>>&& aWidgetArray)
2113       : mozilla::Runnable("WidgetsReleaserRunnable"),
2114         mWidgetArray(std::move(aWidgetArray)) {}
2116   // Do nothing; all this runnable does is hold a reference the widgets in
2117   // mWidgetArray, and those references will be dropped when this runnable
2118   // is destroyed.
2120  private:
2121   nsTArray<nsCOMPtr<nsIWidget>> mWidgetArray;
2124 #pragma mark -
2126 // ViewRegionContainerView is a view class for certain subviews of ChildView
2127 // which contain the NSViews created for ViewRegions (see ViewRegion.h).
2128 // It doesn't do anything interesting, it only acts as a container so that it's
2129 // easier for ChildView to control the z order of its children.
2130 @interface ViewRegionContainerView : NSView {
2132 @end
2134 @implementation ViewRegionContainerView
2136 - (NSView*)hitTest:(NSPoint)aPoint {
2137   return nil;  // Be transparent to mouse events.
2140 - (BOOL)isFlipped {
2141   return [[self superview] isFlipped];
2144 - (BOOL)mouseDownCanMoveWindow {
2145   return [[self superview] mouseDownCanMoveWindow];
2148 @end
2150 @implementation ChildView
2152 // globalDragPboard is non-null during native drag sessions that did not
2153 // originate in our native NSView (it is set in |draggingEntered:|). It is unset
2154 // when the drag session ends for this view, either with the mouse exiting or
2155 // when a drop occurs in this view.
2156 NSPasteboard* globalDragPboard = nil;
2158 // gLastDragView and gLastDragMouseDownEvent are used to communicate information
2159 // to the drag service during drag invocation (starting a drag in from the
2160 // view). gLastDragView is only non-null while a mouse button is pressed, so
2161 // between mouseDown and mouseUp.
2162 NSView* gLastDragView = nil;             // [weak]
2163 NSEvent* gLastDragMouseDownEvent = nil;  // [strong]
2165 + (void)initialize {
2166   static BOOL initialized = NO;
2168   if (!initialized) {
2169     // Inform the OS about the types of services (from the "Services" menu)
2170     // that we can handle.
2171     NSArray* types = @[
2172       [UTIHelper stringFromPboardType:NSPasteboardTypeString],
2173       [UTIHelper stringFromPboardType:NSPasteboardTypeHTML]
2174     ];
2175     [NSApp registerServicesMenuSendTypes:types returnTypes:types];
2176     initialized = YES;
2177   }
2180 + (void)registerViewForDraggedTypes:(NSView*)aView {
2181   [aView
2182       registerForDraggedTypes:
2183           [NSArray
2184               arrayWithObjects:
2185                   [UTIHelper stringFromPboardType:NSFilenamesPboardType],
2186                   [UTIHelper stringFromPboardType:kMozFileUrlsPboardType],
2187                   [UTIHelper stringFromPboardType:NSPasteboardTypeString],
2188                   [UTIHelper stringFromPboardType:NSPasteboardTypeHTML],
2189                   [UTIHelper
2190                       stringFromPboardType:(NSString*)
2191                                                kPasteboardTypeFileURLPromise],
2192                   [UTIHelper stringFromPboardType:kMozWildcardPboardType],
2193                   [UTIHelper stringFromPboardType:kPublicUrlPboardType],
2194                   [UTIHelper stringFromPboardType:kPublicUrlNamePboardType],
2195                   [UTIHelper stringFromPboardType:kUrlsWithTitlesPboardType],
2196                   nil]];
2199 // initWithFrame:geckoChild:
2200 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild {
2201   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
2203   if ((self = [super initWithFrame:inFrame])) {
2204     mGeckoChild = inChild;
2205     mBlockedLastMouseDown = NO;
2206     mExpectingWheelStop = NO;
2208     mLastMouseDownEvent = nil;
2209     mLastKeyDownEvent = nil;
2210     mClickThroughMouseDownEvent = nil;
2211     mDragService = nullptr;
2213     mGestureState = eGestureState_None;
2214     mCumulativeRotation = 0.0;
2216     mIsUpdatingLayer = NO;
2218     [self setFocusRingType:NSFocusRingTypeNone];
2220 #ifdef __LP64__
2221     mCancelSwipeAnimation = nil;
2222 #endif
2224     mNonDraggableViewsContainer =
2225         [[ViewRegionContainerView alloc] initWithFrame:[self bounds]];
2226     mVibrancyViewsContainer =
2227         [[ViewRegionContainerView alloc] initWithFrame:[self bounds]];
2229     [mNonDraggableViewsContainer
2230         setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
2231     [mVibrancyViewsContainer
2232         setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
2234     [self addSubview:mNonDraggableViewsContainer];
2235     [self addSubview:mVibrancyViewsContainer];
2237     mPixelHostingView = [[PixelHostingView alloc] initWithFrame:[self bounds]];
2238     [mPixelHostingView
2239         setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
2241     [self addSubview:mPixelHostingView];
2243     mRootCALayer = [[CALayer layer] retain];
2244     mRootCALayer.position = NSZeroPoint;
2245     mRootCALayer.bounds = NSZeroRect;
2246     mRootCALayer.anchorPoint = NSZeroPoint;
2247     mRootCALayer.contentsGravity = kCAGravityTopLeft;
2248     [[mPixelHostingView layer] addSublayer:mRootCALayer];
2250     mLastPressureStage = 0;
2251   }
2253   // register for things we'll take from other applications
2254   [ChildView registerViewForDraggedTypes:self];
2256   return self;
2258   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
2261 - (NSTextInputContext*)inputContext {
2262   if (!mGeckoChild) {
2263     // -[ChildView widgetDestroyed] has been called, but
2264     // -[ChildView delayedTearDown] has not yet completed.  Accessing
2265     // [super inputContext] now would uselessly recreate a text input context
2266     // for us, under which -[ChildView validAttributesForMarkedText] would
2267     // be called and the assertion checking for mTextInputHandler would fail.
2268     // We return nil to avoid that.
2269     return nil;
2270   }
2271   return [super inputContext];
2274 - (void)installTextInputHandler:(TextInputHandler*)aHandler {
2275   mTextInputHandler = aHandler;
2278 - (void)uninstallTextInputHandler {
2279   mTextInputHandler = nullptr;
2282 - (NSView*)vibrancyViewsContainer {
2283   return mVibrancyViewsContainer;
2286 - (NSView*)nonDraggableViewsContainer {
2287   return mNonDraggableViewsContainer;
2290 - (NSView*)pixelHostingView {
2291   return mPixelHostingView;
2294 - (void)dealloc {
2295   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2297   [mLastMouseDownEvent release];
2298   [mLastKeyDownEvent release];
2299   [mClickThroughMouseDownEvent release];
2300   ChildViewMouseTracker::OnDestroyView(self);
2302   [mVibrancyViewsContainer removeFromSuperview];
2303   [mVibrancyViewsContainer release];
2304   [mNonDraggableViewsContainer removeFromSuperview];
2305   [mNonDraggableViewsContainer release];
2306   [mPixelHostingView removeFromSuperview];
2307   [mPixelHostingView release];
2308   [mRootCALayer release];
2310   if (gLastDragView == self) {
2311     gLastDragView = nil;
2312   }
2314   [super dealloc];
2316   NS_OBJC_END_TRY_IGNORE_BLOCK;
2319 - (void)widgetDestroyed {
2320   if (mTextInputHandler) {
2321     mTextInputHandler->OnDestroyWidget(mGeckoChild);
2322     mTextInputHandler = nullptr;
2323   }
2324   mGeckoChild = nullptr;
2326   // Just in case we're destroyed abruptly and missed the draggingExited
2327   // or performDragOperation message.
2328   NS_IF_RELEASE(mDragService);
2331 // mozView method, return our gecko child view widget. Note this does not
2332 // AddRef.
2333 - (nsIWidget*)widget {
2334   return static_cast<nsIWidget*>(mGeckoChild);
2337 - (NSString*)description {
2338   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
2340   return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@",
2341                                     self, mGeckoChild,
2342                                     NSStringFromRect([self frame])];
2344   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
2347 // Make the origin of this view the topLeft corner (gecko origin) rather
2348 // than the bottomLeft corner (standard cocoa origin).
2349 - (BOOL)isFlipped {
2350   return YES;
2353 // We accept key and mouse events, so don't keep passing them up the chain.
2354 // Allow this to be a 'focused' widget for event dispatch.
2355 - (BOOL)acceptsFirstResponder {
2356   return YES;
2359 // Accept mouse down events on background windows
2360 - (BOOL)acceptsFirstMouse:(NSEvent*)aEvent {
2361   if (![[self window] isKindOfClass:[PopupWindow class]]) {
2362     // We rely on this function to tell us that the mousedown was on a
2363     // background window. Inside mouseDown we can't tell whether we were
2364     // inactive because at that point we've already been made active.
2365     // Unfortunately, acceptsFirstMouse is called for PopupWindows even when
2366     // their parent window is active, so ignore this on them for now.
2367     mClickThroughMouseDownEvent = [aEvent retain];
2368   }
2369   return YES;
2372 - (BOOL)mouseDownCanMoveWindow {
2373   // Return YES so that parts of this view can be draggable. The non-draggable
2374   // parts will be covered by NSViews that return NO from
2375   // mouseDownCanMoveWindow and thus override draggability from the inside.
2376   // These views are assembled in nsChildView::UpdateWindowDraggingRegion.
2377   return YES;
2380 - (void)viewDidChangeBackingProperties {
2381   [super viewDidChangeBackingProperties];
2382   if (mGeckoChild) {
2383     // actually, it could be the color space that's changed,
2384     // but we can't tell the difference here except by retrieving
2385     // the backing scale factor and comparing to the old value
2386     mGeckoChild->BackingScaleFactorChanged();
2387   }
2390 - (BOOL)isCoveringTitlebar {
2391   return [[self window] isKindOfClass:[BaseWindow class]] &&
2392          [(BaseWindow*)[self window] mainChildView] == self &&
2393          [(BaseWindow*)[self window] drawsContentsIntoWindowFrame];
2396 - (void)viewWillStartLiveResize {
2397   nsCocoaWindow* windowWidget =
2398       mGeckoChild ? mGeckoChild->GetAppWindowWidget() : nullptr;
2399   if (windowWidget) {
2400     windowWidget->NotifyLiveResizeStarted();
2401   }
2404 - (void)viewDidEndLiveResize {
2405   // mGeckoChild may legitimately be null here. It should also have been null
2406   // in viewWillStartLiveResize, so there's no problem. However if we run into
2407   // cases where the windowWidget was non-null in viewWillStartLiveResize but
2408   // is null here, that might be problematic because we might get stuck with
2409   // a content process that has the displayport suppressed. If that scenario
2410   // arises (I'm not sure that it does) we will need to handle it gracefully.
2411   nsCocoaWindow* windowWidget =
2412       mGeckoChild ? mGeckoChild->GetAppWindowWidget() : nullptr;
2413   if (windowWidget) {
2414     windowWidget->NotifyLiveResizeStopped();
2415   }
2418 - (void)markLayerForDisplay {
2419   MOZ_RELEASE_ASSERT(NS_IsMainThread());
2420   if (!mIsUpdatingLayer) {
2421     // This call will cause updateRootCALayer to be called during the upcoming
2422     // main thread CoreAnimation transaction. It will also trigger a transaction
2423     // if no transaction is currently pending.
2424     [[mPixelHostingView layer] setNeedsDisplay];
2425   }
2428 - (void)ensureNextCompositeIsAtomicWithMainThreadPaint {
2429   MOZ_RELEASE_ASSERT(NS_IsMainThread());
2430   if (mGeckoChild) {
2431     mGeckoChild->SuspendAsyncCATransactions();
2432   }
2435 - (void)updateRootCALayer {
2436   if (NS_IsMainThread() && mGeckoChild) {
2437     MOZ_RELEASE_ASSERT(!mIsUpdatingLayer, "Re-entrant layer display?");
2438     mIsUpdatingLayer = YES;
2439     mGeckoChild->HandleMainThreadCATransaction();
2440     mIsUpdatingLayer = NO;
2441   }
2444 - (CALayer*)rootCALayer {
2445   return mRootCALayer;
2448 // If we've just created a non-native context menu, we need to mark it as
2449 // such and let the OS (and other programs) know when it opens and closes
2450 // (this is how the OS knows to close other programs' context menus when
2451 // ours open).  We send the initial notification here, but others are sent
2452 // in nsCocoaWindow::Show().
2453 - (void)maybeInitContextMenuTracking {
2454   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2456   if (mozilla::widget::NativeMenuSupport::ShouldUseNativeContextMenus()) {
2457     return;
2458   }
2460   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
2461   NS_ENSURE_TRUE_VOID(rollupListener);
2462   nsCOMPtr<nsIWidget> widget = rollupListener->GetRollupWidget();
2463   NS_ENSURE_TRUE_VOID(widget);
2465   NSWindow* popupWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
2466   if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]]) return;
2468   [[NSDistributedNotificationCenter defaultCenter]
2469       postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
2470                     object:@"org.mozilla.gecko.PopupWindow"];
2471   [(PopupWindow*)popupWindow setIsContextMenu:YES];
2473   NS_OBJC_END_TRY_IGNORE_BLOCK;
2476 // Returns true if the event should no longer be processed, false otherwise.
2477 // This does not return whether or not anything was rolled up.
2478 - (BOOL)maybeRollup:(NSEvent*)theEvent {
2479   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
2481   BOOL consumeEvent = NO;
2483   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
2484   NS_ENSURE_TRUE(rollupListener, false);
2486   BOOL isWheelTypeEvent = [theEvent type] == NSEventTypeScrollWheel ||
2487                           [theEvent type] == NSEventTypeMagnify ||
2488                           [theEvent type] == NSEventTypeSmartMagnify;
2490   if (!isWheelTypeEvent && rollupListener->RollupNativeMenu()) {
2491     // A native menu was rolled up.
2492     // Don't consume this event; if the menu wanted to consume this event it
2493     // would already have done so and we wouldn't even get here. For example, we
2494     // won't get here for left clicks that close native menus (because the
2495     // native menu consumes it), but we will get here for right clicks that
2496     // close native menus, and we do not want to consume those right clicks.
2497     return NO;
2498   }
2500   nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
2501   if (!rollupWidget) {
2502     return consumeEvent;
2503   }
2505   NSWindow* currentPopup =
2506       static_cast<NSWindow*>(rollupWidget->GetNativeData(NS_NATIVE_WINDOW));
2507   if (nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
2508     return consumeEvent;
2509   }
2511   // Check to see if scroll/zoom events should roll up the popup
2512   if (isWheelTypeEvent) {
2513     // consume scroll events that aren't over the popup unless the popup is an
2514     // arrow panel.
2515     consumeEvent = rollupListener->ShouldConsumeOnMouseWheelEvent();
2516     if (!rollupListener->ShouldRollupOnMouseWheelEvent()) {
2517       return consumeEvent;
2518     }
2519   }
2521   // if we're dealing with menus, we probably have submenus and
2522   // we don't want to rollup if the click is in a parent menu of
2523   // the current submenu
2524   uint32_t popupsToRollup = UINT32_MAX;
2525   AutoTArray<nsIWidget*, 5> widgetChain;
2526   uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
2527   for (uint32_t i = 0; i < widgetChain.Length(); i++) {
2528     nsIWidget* widget = widgetChain[i];
2529     NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
2530     if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
2531       // don't roll up if the mouse event occurred within a menu of the
2532       // same type. If the mouse event occurred in a menu higher than
2533       // that, roll up, but pass the number of popups to Rollup so
2534       // that only those of the same type close up.
2535       if (i < sameTypeCount) {
2536         return consumeEvent;
2537       }
2538       popupsToRollup = sameTypeCount;
2539       break;
2540     }
2541   }
2543   LayoutDeviceIntPoint devPoint;
2544   nsIRollupListener::RollupOptions rollupOptions{
2545       popupsToRollup, nsIRollupListener::FlushViews::Yes};
2546   if ([theEvent type] == NSEventTypeLeftMouseDown) {
2547     NSPoint point = [NSEvent mouseLocation];
2548     FlipCocoaScreenCoordinate(point);
2549     devPoint = mGeckoChild->CocoaPointsToDevPixels(point);
2550     rollupOptions.mPoint = &devPoint;
2551   }
2552   consumeEvent = (BOOL)rollupListener->Rollup(rollupOptions);
2553   return consumeEvent;
2555   NS_OBJC_END_TRY_BLOCK_RETURN(NO);
2558 - (void)swipeWithEvent:(NSEvent*)anEvent {
2559   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2561   if (!anEvent || !mGeckoChild) {
2562     return;
2563   }
2565   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2567   float deltaX = [anEvent deltaX];  // left=1.0, right=-1.0
2568   float deltaY = [anEvent deltaY];  // up=1.0, down=-1.0
2570   // Setup the "swipe" event.
2571   WidgetSimpleGestureEvent geckoEvent(true, eSwipeGesture, mGeckoChild);
2572   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2574   // Record the left/right direction.
2575   if (deltaX > 0.0)
2576     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_LEFT;
2577   else if (deltaX < 0.0)
2578     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT;
2580   // Record the up/down direction.
2581   if (deltaY > 0.0)
2582     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_UP;
2583   else if (deltaY < 0.0)
2584     geckoEvent.mDirection |= dom::SimpleGestureEvent_Binding::DIRECTION_DOWN;
2586   // Send the event.
2587   mGeckoChild->DispatchWindowEvent(geckoEvent);
2589   NS_OBJC_END_TRY_IGNORE_BLOCK;
2592 // Pinch zoom gesture.
2593 - (void)magnifyWithEvent:(NSEvent*)anEvent {
2594   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2596   if ([self maybeRollup:anEvent]) {
2597     return;
2598   }
2600   if (!mGeckoChild) {
2601     return;
2602   }
2604   // Instead of calling beginOrEndGestureForEventPhase we basically inline
2605   // the effects of it here, because that function doesn't play too well with
2606   // how we create PinchGestureInput events below. The main point of that
2607   // function is to avoid flip-flopping between rotation/magnify gestures, which
2608   // we can do by checking and setting mGestureState appropriately. A secondary
2609   // result of that function is to send the final eMagnifyGesture event when
2610   // the gesture ends, but APZ takes care of that for us.
2611   if (mGestureState == eGestureState_RotateGesture &&
2612       [anEvent phase] != NSEventPhaseBegan) {
2613     // If we're already in a rotation and not "starting" a magnify, abort.
2614     return;
2615   }
2616   mGestureState = eGestureState_MagnifyGesture;
2618   NSPoint locationInWindow =
2619       nsCocoaUtils::EventLocationForWindow(anEvent, [self window]);
2620   ScreenPoint position = ViewAs<ScreenPixel>(
2621       [self convertWindowCoordinatesRoundDown:locationInWindow],
2622       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
2623   ExternalPoint screenOffset = ViewAs<ExternalPixel>(
2624       mGeckoChild->WidgetToScreenOffset(),
2625       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
2627   TimeStamp eventTimeStamp =
2628       nsCocoaUtils::GetEventTimeStamp([anEvent timestamp]);
2629   NSEventPhase eventPhase = [anEvent phase];
2630   PinchGestureInput::PinchGestureType pinchGestureType;
2632   switch (eventPhase) {
2633     case NSEventPhaseBegan: {
2634       pinchGestureType = PinchGestureInput::PINCHGESTURE_START;
2635       break;
2636     }
2637     case NSEventPhaseChanged: {
2638       pinchGestureType = PinchGestureInput::PINCHGESTURE_SCALE;
2639       break;
2640     }
2641     case NSEventPhaseEnded: {
2642       pinchGestureType = PinchGestureInput::PINCHGESTURE_END;
2643       mGestureState = eGestureState_None;
2644       break;
2645     }
2646     default: {
2647       NS_WARNING("Unexpected phase for pinch gesture event.");
2648       return;
2649     }
2650   }
2652   PinchGestureInput event{pinchGestureType,
2653                           PinchGestureInput::TRACKPAD,
2654                           eventTimeStamp,
2655                           screenOffset,
2656                           position,
2657                           100.0,
2658                           100.0 * (1.0 - [anEvent magnification]),
2659                           nsCocoaUtils::ModifiersForEvent(anEvent)};
2661   mGeckoChild->DispatchAPZInputEvent(event);
2663   NS_OBJC_END_TRY_IGNORE_BLOCK;
2666 // Smart zoom gesture, i.e. two-finger double tap on trackpads.
2667 - (void)smartMagnifyWithEvent:(NSEvent*)anEvent {
2668   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2670   if (!anEvent || !mGeckoChild ||
2671       [self beginOrEndGestureForEventPhase:anEvent]) {
2672     return;
2673   }
2675   if ([self maybeRollup:anEvent]) {
2676     return;
2677   }
2679   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2681   if (StaticPrefs::apz_mac_enable_double_tap_zoom_touchpad_gesture()) {
2682     TimeStamp eventTimeStamp =
2683         nsCocoaUtils::GetEventTimeStamp([anEvent timestamp]);
2684     NSPoint locationInWindow =
2685         nsCocoaUtils::EventLocationForWindow(anEvent, [self window]);
2686     LayoutDevicePoint position =
2687         [self convertWindowCoordinatesRoundDown:locationInWindow];
2689     mGeckoChild->DispatchDoubleTapGesture(
2690         eventTimeStamp, RoundedToInt(position),
2691         nsCocoaUtils::ModifiersForEvent(anEvent));
2692   } else {
2693     // Setup the "double tap" event.
2694     WidgetSimpleGestureEvent geckoEvent(true, eTapGesture, mGeckoChild);
2695     [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2696     geckoEvent.mClickCount = 1;
2698     // Send the event.
2699     mGeckoChild->DispatchWindowEvent(geckoEvent);
2700   }
2702   // Clear the gesture state
2703   mGestureState = eGestureState_None;
2705   NS_OBJC_END_TRY_IGNORE_BLOCK;
2708 - (void)rotateWithEvent:(NSEvent*)anEvent {
2709   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2711   if (!anEvent || !mGeckoChild ||
2712       [self beginOrEndGestureForEventPhase:anEvent]) {
2713     return;
2714   }
2716   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2718   float rotation = [anEvent rotation];
2720   EventMessage msg;
2721   switch (mGestureState) {
2722     case eGestureState_StartGesture:
2723       msg = eRotateGestureStart;
2724       mGestureState = eGestureState_RotateGesture;
2725       break;
2727     case eGestureState_RotateGesture:
2728       msg = eRotateGestureUpdate;
2729       break;
2731     case eGestureState_None:
2732     case eGestureState_MagnifyGesture:
2733     default:
2734       return;
2735   }
2737   // Setup the event.
2738   WidgetSimpleGestureEvent geckoEvent(true, msg, mGeckoChild);
2739   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2740   geckoEvent.mDelta = -rotation;
2741   if (rotation > 0.0) {
2742     geckoEvent.mDirection =
2743         dom::SimpleGestureEvent_Binding::ROTATION_COUNTERCLOCKWISE;
2744   } else {
2745     geckoEvent.mDirection = dom::SimpleGestureEvent_Binding::ROTATION_CLOCKWISE;
2746   }
2748   // Send the event.
2749   mGeckoChild->DispatchWindowEvent(geckoEvent);
2751   // Keep track of the cumulative rotation for the final "rotate" event.
2752   mCumulativeRotation += rotation;
2754   NS_OBJC_END_TRY_IGNORE_BLOCK;
2757 // `beginGestureWithEvent` and `endGestureWithEvent` are not called for
2758 // applications that link against the macOS 10.11 or later SDK when we're
2759 // running on macOS 10.11 or later. For compatibility with all supported macOS
2760 // versions, we have to call {begin,end}GestureWithEvent ourselves based on
2761 // the event phase when we're handling gestures.
2762 - (bool)beginOrEndGestureForEventPhase:(NSEvent*)aEvent {
2763   if (!aEvent) {
2764     return false;
2765   }
2767   if (aEvent.phase == NSEventPhaseBegan) {
2768     [self beginGestureWithEvent:aEvent];
2769     return true;
2770   }
2772   if (aEvent.phase == NSEventPhaseEnded ||
2773       aEvent.phase == NSEventPhaseCancelled) {
2774     [self endGestureWithEvent:aEvent];
2775     return true;
2776   }
2778   return false;
2781 - (void)beginGestureWithEvent:(NSEvent*)aEvent {
2782   if (!aEvent) {
2783     return;
2784   }
2786   mGestureState = eGestureState_StartGesture;
2787   mCumulativeRotation = 0.0;
2790 - (void)endGestureWithEvent:(NSEvent*)anEvent {
2791   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2793   if (!anEvent || !mGeckoChild) {
2794     // Clear the gestures state if we cannot send an event.
2795     mGestureState = eGestureState_None;
2796     mCumulativeRotation = 0.0;
2797     return;
2798   }
2800   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2802   switch (mGestureState) {
2803     case eGestureState_RotateGesture: {
2804       // Setup the "rotate" event.
2805       WidgetSimpleGestureEvent geckoEvent(true, eRotateGesture, mGeckoChild);
2806       [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
2807       geckoEvent.mDelta = -mCumulativeRotation;
2808       if (mCumulativeRotation > 0.0) {
2809         geckoEvent.mDirection =
2810             dom::SimpleGestureEvent_Binding::ROTATION_COUNTERCLOCKWISE;
2811       } else {
2812         geckoEvent.mDirection =
2813             dom::SimpleGestureEvent_Binding::ROTATION_CLOCKWISE;
2814       }
2816       // Send the event.
2817       mGeckoChild->DispatchWindowEvent(geckoEvent);
2818     } break;
2820     case eGestureState_MagnifyGesture:  // APZ handles sending the widget events
2821     case eGestureState_None:
2822     case eGestureState_StartGesture:
2823     default:
2824       break;
2825   }
2827   // Clear the gestures state.
2828   mGestureState = eGestureState_None;
2829   mCumulativeRotation = 0.0;
2831   NS_OBJC_END_TRY_IGNORE_BLOCK;
2834 - (void)setUsingOMTCompositor:(BOOL)aUseOMTC {
2835   mUsingOMTCompositor = aUseOMTC;
2838 // Returning NO from this method only disallows ordering on mousedown - in order
2839 // to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering]
2840 // when handling the mousedown event.
2841 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)aEvent {
2842   // Always using system-provided window ordering for normal windows.
2843   if (![[self window] isKindOfClass:[PopupWindow class]]) return NO;
2845   // Don't reorder when we don't have a parent window, like when we're a
2846   // context menu or a tooltip.
2847   return ![[self window] parentWindow];
2850 - (void)mouseDown:(NSEvent*)theEvent {
2851   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2852   mPerformedDrag = NO;
2854   if ([self shouldDelayWindowOrderingForEvent:theEvent]) {
2855     [NSApp preventWindowOrdering];
2856   }
2858   // If we've already seen this event due to direct dispatch from menuForEvent:
2859   // just bail; if not, remember it.
2860   if (mLastMouseDownEvent == theEvent) {
2861     [mLastMouseDownEvent release];
2862     mLastMouseDownEvent = nil;
2863     return;
2864   } else {
2865     [mLastMouseDownEvent release];
2866     mLastMouseDownEvent = [theEvent retain];
2867   }
2869   [gLastDragMouseDownEvent release];
2870   gLastDragMouseDownEvent = [theEvent retain];
2871   gLastDragView = self;
2873   // We need isClickThrough because at this point the window we're in might
2874   // already have become main, so the check for isMainWindow in
2875   // WindowAcceptsEvent isn't enough. It also has to check isClickThrough.
2876   BOOL isClickThrough = (theEvent == mClickThroughMouseDownEvent);
2877   [mClickThroughMouseDownEvent release];
2878   mClickThroughMouseDownEvent = nil;
2880   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2882   if ([self maybeRollup:theEvent] ||
2883       !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self,
2884                                                  isClickThrough)) {
2885     // Remember blocking because that means we want to block mouseup as well.
2886     mBlockedLastMouseDown = YES;
2887     return;
2888   }
2890   // in order to send gecko events we'll need a gecko widget
2891   if (!mGeckoChild) return;
2892   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2893     return;
2894   }
2896   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild,
2897                               WidgetMouseEvent::eReal);
2898   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2900   NSInteger clickCount = [theEvent clickCount];
2901   if (mBlockedLastMouseDown && clickCount > 1) {
2902     // Don't send a double click if the first click of the double click was
2903     // blocked.
2904     clickCount--;
2905   }
2906   geckoEvent.mClickCount = clickCount;
2908   if (!StaticPrefs::dom_event_treat_ctrl_click_as_right_click_disabled() &&
2909       geckoEvent.IsControl()) {
2910     geckoEvent.mButton = MouseButton::eSecondary;
2911   } else {
2912     geckoEvent.mButton = MouseButton::ePrimary;
2913     // Don't send a click if ctrl key is pressed.
2914     geckoEvent.mClickEventPrevented = geckoEvent.IsControl();
2915   }
2917   mGeckoChild->DispatchInputEvent(&geckoEvent);
2918   mBlockedLastMouseDown = NO;
2920   // XXX maybe call markedTextSelectionChanged:client: here?
2922   NS_OBJC_END_TRY_IGNORE_BLOCK;
2925 - (void)mouseUp:(NSEvent*)theEvent {
2926   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
2928   gLastDragView = nil;
2930   if (!mGeckoChild || mBlockedLastMouseDown || mPerformedDrag) {
2931     // There is case that mouseUp event will be fired right after DnD on OSX. As
2932     // mPerformedDrag will be YES at end of DnD processing, ignore this mouseUp
2933     // event fired right after DnD.
2934     return;
2935   }
2937   if (mTextInputHandler->OnHandleEvent(theEvent)) {
2938     return;
2939   }
2941   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2943   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild,
2944                               WidgetMouseEvent::eReal);
2945   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
2947   if (!StaticPrefs::dom_event_treat_ctrl_click_as_right_click_disabled() &&
2948       ([theEvent modifierFlags] & NSEventModifierFlagControl)) {
2949     geckoEvent.mButton = MouseButton::eSecondary;
2950   } else {
2951     geckoEvent.mButton = MouseButton::ePrimary;
2952   }
2954   // Remember the event's position before calling DispatchInputEvent, because
2955   // that call can mutate it and convert it into a different coordinate space.
2956   LayoutDeviceIntPoint pos = geckoEvent.mRefPoint;
2958   // This might destroy our widget (and null out mGeckoChild).
2959   bool defaultPrevented =
2960       (mGeckoChild->DispatchInputEvent(&geckoEvent).mContentStatus ==
2961        nsEventStatus_eConsumeNoDefault);
2963   if (!mGeckoChild) {
2964     return;
2965   }
2967   // Check to see if we are double-clicking in draggable parts of the window.
2968   if (!defaultPrevented && [theEvent clickCount] == 2 &&
2969       !mGeckoChild->GetNonDraggableRegion().Contains(pos.x, pos.y)) {
2970     if (nsCocoaUtils::ShouldZoomOnTitlebarDoubleClick()) {
2971       [[self window] performZoom:nil];
2972     } else if (nsCocoaUtils::ShouldMinimizeOnTitlebarDoubleClick()) {
2973       [[self window] performMiniaturize:nil];
2974     }
2975   }
2977   NS_OBJC_END_TRY_IGNORE_BLOCK;
2980 - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
2981                             enter:(BOOL)aEnter
2982                          exitFrom:(WidgetMouseEvent::ExitFrom)aExitFrom {
2983   if (!mGeckoChild) return;
2985   NSPoint windowEventLocation =
2986       nsCocoaUtils::EventLocationForWindow(aEvent, [self window]);
2987   NSPoint localEventLocation = [self convertPoint:windowEventLocation
2988                                          fromView:nil];
2990   EventMessage msg = aEnter ? eMouseEnterIntoWidget : eMouseExitFromWidget;
2991   WidgetMouseEvent event(true, msg, mGeckoChild, WidgetMouseEvent::eReal);
2992   event.mRefPoint = mGeckoChild->CocoaPointsToDevPixels(localEventLocation);
2993   if (event.mMessage == eMouseExitFromWidget) {
2994     event.mExitFrom = Some(aExitFrom);
2995   }
2996   nsEventStatus status;  // ignored
2997   mGeckoChild->DispatchEvent(&event, status);
3000 - (void)handleMouseMoved:(NSEvent*)theEvent {
3001   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
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];
3012   mGeckoChild->DispatchInputEvent(&geckoEvent);
3014   NS_OBJC_END_TRY_IGNORE_BLOCK;
3017 - (void)mouseDragged:(NSEvent*)theEvent {
3018   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3020   if (!mGeckoChild) return;
3021   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3022     return;
3023   }
3025   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
3026                               WidgetMouseEvent::eReal);
3027   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3029   mGeckoChild->DispatchInputEvent(&geckoEvent);
3031   // Note, sending the above event might have destroyed our widget since we
3032   // didn't retain. Fine so long as we don't access any local variables from
3033   // here on.
3035   // XXX maybe call markedTextSelectionChanged:client: here?
3037   NS_OBJC_END_TRY_IGNORE_BLOCK;
3040 - (void)rightMouseDown:(NSEvent*)theEvent {
3041   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3042   mPerformedDrag = NO;
3044   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3046   [self maybeRollup:theEvent];
3047   if (!mGeckoChild) return;
3048   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3049     return;
3050   }
3052   // The right mouse went down, fire off a right mouse down event to gecko
3053   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild,
3054                               WidgetMouseEvent::eReal);
3055   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3056   geckoEvent.mButton = MouseButton::eSecondary;
3057   geckoEvent.mClickCount = [theEvent clickCount];
3059   nsIWidget::ContentAndAPZEventStatus eventStatus =
3060       mGeckoChild->DispatchInputEvent(&geckoEvent);
3061   if (!mGeckoChild) return;
3063   if (!StaticPrefs::ui_context_menus_after_mouseup() &&
3064       eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
3065     // Let the superclass do the context menu stuff.
3066     [super rightMouseDown:theEvent];
3067   }
3069   NS_OBJC_END_TRY_IGNORE_BLOCK;
3072 - (void)rightMouseUp:(NSEvent*)theEvent {
3073   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3075   if (!mGeckoChild) return;
3076   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3077     return;
3078   }
3080   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild,
3081                               WidgetMouseEvent::eReal);
3082   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3083   geckoEvent.mButton = MouseButton::eSecondary;
3084   geckoEvent.mClickCount = [theEvent clickCount];
3086   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3087   nsIWidget::ContentAndAPZEventStatus eventStatus =
3088       mGeckoChild->DispatchInputEvent(&geckoEvent);
3089   if (!mGeckoChild) return;
3091   if (StaticPrefs::ui_context_menus_after_mouseup() &&
3092       eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
3093     // Let the superclass do the context menu stuff, but pretend it's
3094     // rightMouseDown.
3095     NSEvent* dupeEvent = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown
3096                                             location:theEvent.locationInWindow
3097                                        modifierFlags:theEvent.modifierFlags
3098                                            timestamp:theEvent.timestamp
3099                                         windowNumber:theEvent.windowNumber
3100                                              context:nil
3101                                          eventNumber:theEvent.eventNumber
3102                                           clickCount:theEvent.clickCount
3103                                             pressure:theEvent.pressure];
3105     [super rightMouseDown:dupeEvent];
3106   }
3108   NS_OBJC_END_TRY_IGNORE_BLOCK;
3111 - (void)rightMouseDragged:(NSEvent*)theEvent {
3112   if (!mGeckoChild) return;
3113   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3114     return;
3115   }
3117   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
3118                               WidgetMouseEvent::eReal);
3119   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3120   geckoEvent.mButton = MouseButton::eSecondary;
3122   // send event into Gecko by going directly to the
3123   // the widget.
3124   mGeckoChild->DispatchInputEvent(&geckoEvent);
3127 static bool ShouldDispatchBackForwardCommandForMouseButton(int16_t aButton) {
3128   return (aButton == MouseButton::eX1 &&
3129           Preferences::GetBool("mousebutton.4th.enabled", true)) ||
3130          (aButton == MouseButton::eX2 &&
3131           Preferences::GetBool("mousebutton.5th.enabled", true));
3134 - (void)otherMouseDown:(NSEvent*)theEvent {
3135   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3136   mPerformedDrag = NO;
3138   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3140   if ([self maybeRollup:theEvent] ||
3141       !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self))
3142     return;
3144   if (!mGeckoChild) return;
3145   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3146     return;
3147   }
3149   int16_t button = nsCocoaUtils::ButtonForEvent(theEvent);
3150   if (ShouldDispatchBackForwardCommandForMouseButton(button)) {
3151     WidgetCommandEvent appCommandEvent(
3152         true,
3153         (button == MouseButton::eX2) ? nsGkAtoms::Forward : nsGkAtoms::Back,
3154         mGeckoChild);
3155     mGeckoChild->DispatchWindowEvent(appCommandEvent);
3156     return;
3157   }
3159   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild,
3160                               WidgetMouseEvent::eReal);
3161   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3162   geckoEvent.mButton = button;
3163   geckoEvent.mClickCount = [theEvent clickCount];
3165   mGeckoChild->DispatchInputEvent(&geckoEvent);
3167   NS_OBJC_END_TRY_IGNORE_BLOCK;
3170 - (void)otherMouseUp:(NSEvent*)theEvent {
3171   if (!mGeckoChild) return;
3172   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3173     return;
3174   }
3176   int16_t button = nsCocoaUtils::ButtonForEvent(theEvent);
3177   if (ShouldDispatchBackForwardCommandForMouseButton(button)) {
3178     return;
3179   }
3181   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild,
3182                               WidgetMouseEvent::eReal);
3183   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3184   geckoEvent.mButton = button;
3186   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3187   mGeckoChild->DispatchInputEvent(&geckoEvent);
3190 - (void)otherMouseDragged:(NSEvent*)theEvent {
3191   if (!mGeckoChild) return;
3192   if (mTextInputHandler->OnHandleEvent(theEvent)) {
3193     return;
3194   }
3196   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild,
3197                               WidgetMouseEvent::eReal);
3198   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3199   int16_t button = nsCocoaUtils::ButtonForEvent(theEvent);
3200   geckoEvent.mButton = button;
3202   // send event into Gecko by going directly to the
3203   // the widget.
3204   mGeckoChild->DispatchInputEvent(&geckoEvent);
3207 - (void)sendWheelStartOrStop:(EventMessage)msg forEvent:(NSEvent*)theEvent {
3208   WidgetWheelEvent wheelEvent(true, msg, mGeckoChild);
3209   [self convertCocoaMouseWheelEvent:theEvent toGeckoEvent:&wheelEvent];
3210   mExpectingWheelStop = (msg == eWheelOperationStart);
3211   mGeckoChild->DispatchInputEvent(wheelEvent.AsInputEvent());
3214 - (void)sendWheelCondition:(BOOL)condition
3215                      first:(EventMessage)first
3216                     second:(EventMessage)second
3217                   forEvent:(NSEvent*)theEvent {
3218   if (mExpectingWheelStop == condition) {
3219     [self sendWheelStartOrStop:first forEvent:theEvent];
3220   }
3221   [self sendWheelStartOrStop:second forEvent:theEvent];
3224 static int32_t RoundUp(double aDouble) {
3225   return aDouble < 0 ? static_cast<int32_t>(floor(aDouble))
3226                      : static_cast<int32_t>(ceil(aDouble));
3229 static gfx::IntPoint GetIntegerDeltaForEvent(NSEvent* aEvent) {
3230   if ([aEvent hasPreciseScrollingDeltas]) {
3231     // Pixel scroll events (events with hasPreciseScrollingDeltas == YES)
3232     // carry pixel deltas in the scrollingDeltaX/Y fields and line scroll
3233     // information in the deltaX/Y fields.
3234     // Prior to 10.12, these line scroll fields would be zero for most pixel
3235     // scroll events and non-zero for some, whenever at least a full line
3236     // worth of pixel scrolling had accumulated. That's the behavior we want.
3237     // Starting with 10.12 however, pixel scroll events no longer accumulate
3238     // deltaX and deltaY; they just report floating point values for every
3239     // single event. So we need to do our own accumulation.
3240     return PanGestureInput::GetIntegerDeltaForEvent(
3241         [aEvent phase] == NSEventPhaseBegan, [aEvent deltaX], [aEvent deltaY]);
3242   }
3244   // For line scrolls, or pre-10.12, just use the rounded up value of deltaX /
3245   // deltaY.
3246   return gfx::IntPoint(RoundUp([aEvent deltaX]), RoundUp([aEvent deltaY]));
3249 - (void)scrollWheel:(NSEvent*)theEvent {
3250   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3252   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3254   ChildViewMouseTracker::MouseScrolled(theEvent);
3256   if ([self maybeRollup:theEvent]) {
3257     return;
3258   }
3260   if (!mGeckoChild) {
3261     return;
3262   }
3264   NSEventPhase phase = [theEvent phase];
3265   // Fire eWheelOperationStart/End events when 2 fingers touch/release the
3266   // touchpad.
3267   if (phase & NSEventPhaseMayBegin) {
3268     [self sendWheelCondition:YES
3269                        first:eWheelOperationEnd
3270                       second:eWheelOperationStart
3271                     forEvent:theEvent];
3272   } else if (phase & (NSEventPhaseEnded | NSEventPhaseCancelled)) {
3273     [self sendWheelCondition:NO
3274                        first:eWheelOperationStart
3275                       second:eWheelOperationEnd
3276                     forEvent:theEvent];
3277   }
3279   if (!mGeckoChild) {
3280     return;
3281   }
3282   RefPtr<nsChildView> geckoChildDeathGrip(mGeckoChild);
3284   NSPoint locationInWindow =
3285       nsCocoaUtils::EventLocationForWindow(theEvent, [self window]);
3287   // Use convertWindowCoordinatesRoundDown when converting the position to
3288   // integer screen pixels in order to ensure that coordinates which are just
3289   // inside the right / bottom edges of the window don't end up outside of the
3290   // window after rounding.
3291   ScreenPoint position = ViewAs<ScreenPixel>(
3292       [self convertWindowCoordinatesRoundDown:locationInWindow],
3293       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
3295   bool usePreciseDeltas =
3296       [theEvent hasPreciseScrollingDeltas] &&
3297       Preferences::GetBool("mousewheel.enable_pixel_scrolling", true);
3298   bool hasPhaseInformation = nsCocoaUtils::EventHasPhaseInformation(theEvent);
3300   gfx::IntPoint lineOrPageDelta = -GetIntegerDeltaForEvent(theEvent);
3302   Modifiers modifiers = nsCocoaUtils::ModifiersForEvent(theEvent);
3304   TimeStamp eventTimeStamp =
3305       nsCocoaUtils::GetEventTimeStamp([theEvent timestamp]);
3307   ScreenPoint preciseDelta;
3308   if (usePreciseDeltas) {
3309     CGFloat pixelDeltaX = [theEvent scrollingDeltaX];
3310     CGFloat pixelDeltaY = [theEvent scrollingDeltaY];
3311     double scale = geckoChildDeathGrip->BackingScaleFactor();
3312     preciseDelta = ScreenPoint(-pixelDeltaX * scale, -pixelDeltaY * scale);
3313   }
3315   if (usePreciseDeltas && hasPhaseInformation) {
3316     PanGestureInput panEvent = nsCocoaUtils::CreatePanGestureEvent(
3317         theEvent, eventTimeStamp, position, preciseDelta, lineOrPageDelta,
3318         modifiers);
3320     geckoChildDeathGrip->DispatchAPZWheelInputEvent(panEvent);
3321   } else if (usePreciseDeltas) {
3322     // This is on 10.6 or old touchpads that don't have any phase information.
3323     ScrollWheelInput wheelEvent(eventTimeStamp, modifiers,
3324                                 ScrollWheelInput::SCROLLMODE_INSTANT,
3325                                 ScrollWheelInput::SCROLLDELTA_PIXEL, position,
3326                                 preciseDelta.x, preciseDelta.y, false,
3327                                 // This parameter is used for wheel delta
3328                                 // adjustment, such as auto-dir scrolling,
3329                                 // but we do't need to do anything special here
3330                                 // since this wheel event is sent to
3331                                 // DispatchAPZWheelInputEvent, which turns this
3332                                 // ScrollWheelInput back into a WidgetWheelEvent
3333                                 // and then it goes through the regular handling
3334                                 // in APZInputBridge. So passing |eNone| won't
3335                                 // pass up the necessary wheel delta adjustment.
3336                                 WheelDeltaAdjustmentStrategy::eNone);
3337     wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x;
3338     wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y;
3339     wheelEvent.mIsMomentum = nsCocoaUtils::IsMomentumScrollEvent(theEvent);
3340     geckoChildDeathGrip->DispatchAPZWheelInputEvent(wheelEvent);
3341   } else {
3342     ScrollWheelInput::ScrollMode scrollMode =
3343         ScrollWheelInput::SCROLLMODE_INSTANT;
3344     if (StaticPrefs::general_smoothScroll() &&
3345         StaticPrefs::general_smoothScroll_mouseWheel()) {
3346       scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
3347     }
3348     ScrollWheelInput wheelEvent(eventTimeStamp, modifiers, scrollMode,
3349                                 ScrollWheelInput::SCROLLDELTA_LINE, position,
3350                                 lineOrPageDelta.x, lineOrPageDelta.y, false,
3351                                 // This parameter is used for wheel delta
3352                                 // adjustment, such as auto-dir scrolling,
3353                                 // but we do't need to do anything special here
3354                                 // since this wheel event is sent to
3355                                 // DispatchAPZWheelInputEvent, which turns this
3356                                 // ScrollWheelInput back into a WidgetWheelEvent
3357                                 // and then it goes through the regular handling
3358                                 // in APZInputBridge. So passing |eNone| won't
3359                                 // pass up the necessary wheel delta adjustment.
3360                                 WheelDeltaAdjustmentStrategy::eNone);
3361     wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x;
3362     wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y;
3363     geckoChildDeathGrip->DispatchAPZWheelInputEvent(wheelEvent);
3364   }
3366   NS_OBJC_END_TRY_IGNORE_BLOCK;
3369 - (NSMenu*)menuForEvent:(NSEvent*)theEvent {
3370   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3372   if (!mGeckoChild) return nil;
3374   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3376   [self maybeRollup:theEvent];
3377   if (!mGeckoChild) return nil;
3379   // Cocoa doesn't always dispatch a mouseDown: for a control-click event,
3380   // depends on what we return from menuForEvent:. Gecko always expects one
3381   // and expects the mouse down event before the context menu event, so
3382   // get that event sent first if this is a left mouse click.
3383   if ([theEvent type] == NSEventTypeLeftMouseDown) {
3384     [self mouseDown:theEvent];
3385     if (!mGeckoChild) return nil;
3386   }
3388   WidgetMouseEvent geckoEvent(true, eContextMenu, mGeckoChild,
3389                               WidgetMouseEvent::eReal);
3390   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3391   if (StaticPrefs::dom_event_treat_ctrl_click_as_right_click_disabled() &&
3392       [theEvent type] == NSEventTypeLeftMouseDown) {
3393     geckoEvent.mContextMenuTrigger = WidgetMouseEvent::eControlClick;
3394     geckoEvent.mButton = MouseButton::ePrimary;
3395   } else {
3396     geckoEvent.mButton = MouseButton::eSecondary;
3397   }
3399   mGeckoChild->DispatchInputEvent(&geckoEvent);
3400   if (!mGeckoChild) return nil;
3402   [self maybeInitContextMenuTracking];
3404   // We never return an actual NSMenu* for the context menu. Gecko might have
3405   // responded to the eContextMenu event by putting up a fake context menu.
3406   return nil;
3408   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
3411 - (void)willOpenMenu:(NSMenu*)aMenu withEvent:(NSEvent*)aEvent {
3412   ChildViewMouseTracker::NativeMenuOpened();
3415 - (void)didCloseMenu:(NSMenu*)aMenu withEvent:(NSEvent*)aEvent {
3416   ChildViewMouseTracker::NativeMenuClosed();
3419 - (void)convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
3420                        toGeckoEvent:(WidgetWheelEvent*)outWheelEvent {
3421   [self convertCocoaMouseEvent:aMouseEvent toGeckoEvent:outWheelEvent];
3423   bool usePreciseDeltas =
3424       [aMouseEvent hasPreciseScrollingDeltas] &&
3425       Preferences::GetBool("mousewheel.enable_pixel_scrolling", true);
3427   outWheelEvent->mDeltaMode = usePreciseDeltas
3428                                   ? dom::WheelEvent_Binding::DOM_DELTA_PIXEL
3429                                   : dom::WheelEvent_Binding::DOM_DELTA_LINE;
3430   outWheelEvent->mIsMomentum = nsCocoaUtils::IsMomentumScrollEvent(aMouseEvent);
3433 - (void)convertCocoaMouseEvent:(NSEvent*)aMouseEvent
3434                   toGeckoEvent:(WidgetInputEvent*)outGeckoEvent {
3435   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3437   NS_ASSERTION(
3438       outGeckoEvent,
3439       "convertCocoaMouseEvent:toGeckoEvent: requires non-null aoutGeckoEvent");
3440   if (!outGeckoEvent) return;
3442   nsCocoaUtils::InitInputEvent(*outGeckoEvent, aMouseEvent);
3444   // convert point to view coordinate system
3445   NSPoint locationInWindow =
3446       nsCocoaUtils::EventLocationForWindow(aMouseEvent, [self window]);
3448   outGeckoEvent->mRefPoint = [self convertWindowCoordinates:locationInWindow];
3450   WidgetMouseEventBase* mouseEvent = outGeckoEvent->AsMouseEventBase();
3451   mouseEvent->mButtons = 0;
3452   NSUInteger mouseButtons = [NSEvent pressedMouseButtons];
3454   if (mouseButtons & 0x01) {
3455     mouseEvent->mButtons |= MouseButtonsFlag::ePrimaryFlag;
3456   }
3457   if (mouseButtons & 0x02) {
3458     mouseEvent->mButtons |= MouseButtonsFlag::eSecondaryFlag;
3459   }
3460   if (mouseButtons & 0x04) {
3461     mouseEvent->mButtons |= MouseButtonsFlag::eMiddleFlag;
3462   }
3463   if (mouseButtons & 0x08) {
3464     mouseEvent->mButtons |= MouseButtonsFlag::e4thFlag;
3465   }
3466   if (mouseButtons & 0x10) {
3467     mouseEvent->mButtons |= MouseButtonsFlag::e5thFlag;
3468   }
3470   switch ([aMouseEvent type]) {
3471     case NSEventTypeLeftMouseDown:
3472     case NSEventTypeLeftMouseUp:
3473     case NSEventTypeLeftMouseDragged:
3474     case NSEventTypeRightMouseDown:
3475     case NSEventTypeRightMouseUp:
3476     case NSEventTypeRightMouseDragged:
3477     case NSEventTypeOtherMouseDown:
3478     case NSEventTypeOtherMouseUp:
3479     case NSEventTypeOtherMouseDragged:
3480     case NSEventTypeMouseMoved:
3481       if ([aMouseEvent subtype] == NSEventSubtypeTabletPoint) {
3482         [self convertCocoaTabletPointerEvent:aMouseEvent
3483                                 toGeckoEvent:mouseEvent->AsMouseEvent()];
3484       }
3485       break;
3487     default:
3488       // Don't check other NSEvents for pressure.
3489       break;
3490   }
3492   NS_OBJC_END_TRY_IGNORE_BLOCK;
3495 - (void)convertCocoaTabletPointerEvent:(NSEvent*)aPointerEvent
3496                           toGeckoEvent:(WidgetMouseEvent*)aOutGeckoEvent {
3497   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
3498   if (!aOutGeckoEvent || !sIsTabletPointerActivated) {
3499     return;
3500   }
3501   if ([aPointerEvent type] != NSEventTypeMouseMoved) {
3502     aOutGeckoEvent->mPressure = [aPointerEvent pressure];
3503     MOZ_ASSERT(aOutGeckoEvent->mPressure >= 0.0 &&
3504                aOutGeckoEvent->mPressure <= 1.0);
3505   }
3506   aOutGeckoEvent->mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
3507   aOutGeckoEvent->tiltX = (int32_t)lround([aPointerEvent tilt].x * 90);
3508   aOutGeckoEvent->tiltY = (int32_t)lround([aPointerEvent tilt].y * 90);
3509   aOutGeckoEvent->tangentialPressure = [aPointerEvent tangentialPressure];
3510   // Make sure the twist value is in the range of 0-359.
3511   int32_t twist = (int32_t)fmod([aPointerEvent rotation], 360);
3512   aOutGeckoEvent->twist = twist >= 0 ? twist : twist + 360;
3513   NS_OBJC_END_TRY_IGNORE_BLOCK;
3516 - (void)tabletProximity:(NSEvent*)theEvent {
3517   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
3518   sIsTabletPointerActivated = [theEvent isEnteringProximity];
3519   NS_OBJC_END_TRY_IGNORE_BLOCK
3522 #pragma mark -
3523 // NSTextInputClient implementation
3525 - (NSRange)markedRange {
3526   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3528   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0));
3529   return mTextInputHandler->MarkedRange();
3531   NS_OBJC_END_TRY_BLOCK_RETURN(NSMakeRange(0, 0));
3534 - (NSRange)selectedRange {
3535   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3537   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0));
3538   return mTextInputHandler->SelectedRange();
3540   NS_OBJC_END_TRY_BLOCK_RETURN(NSMakeRange(0, 0));
3543 - (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex {
3544   NS_ENSURE_TRUE(mTextInputHandler, NO);
3545   if (charIndex == NSNotFound) {
3546     return NO;
3547   }
3548   return mTextInputHandler->DrawsVerticallyForCharacterAtIndex(charIndex);
3551 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
3552   NS_ENSURE_TRUE(mTextInputHandler, 0);
3553   return mTextInputHandler->CharacterIndexForPoint(thePoint);
3556 - (NSArray*)validAttributesForMarkedText {
3557   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3559   NS_ENSURE_TRUE(mTextInputHandler, [NSArray array]);
3560   return mTextInputHandler->GetValidAttributesForMarkedText();
3562   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
3565 - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
3566   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3568   NS_ENSURE_TRUE_VOID(mGeckoChild);
3570   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3572   NSAttributedString* attrStr;
3573   if ([aString isKindOfClass:[NSAttributedString class]]) {
3574     attrStr = static_cast<NSAttributedString*>(aString);
3575   } else {
3576     attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
3577   }
3579   mTextInputHandler->InsertText(attrStr, &replacementRange);
3581   NS_OBJC_END_TRY_IGNORE_BLOCK;
3584 - (void)doCommandBySelector:(SEL)aSelector {
3585   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3587   if (!mGeckoChild || !mTextInputHandler) {
3588     return;
3589   }
3591   const char* sel = reinterpret_cast<const char*>(aSelector);
3592   if (!mTextInputHandler->DoCommandBySelector(sel)) {
3593     [super doCommandBySelector:aSelector];
3594   }
3596   NS_OBJC_END_TRY_IGNORE_BLOCK;
3599 - (void)unmarkText {
3600   NS_ENSURE_TRUE_VOID(mTextInputHandler);
3601   mTextInputHandler->CommitIMEComposition();
3604 - (BOOL)hasMarkedText {
3605   NS_ENSURE_TRUE(mTextInputHandler, NO);
3606   return mTextInputHandler->HasMarkedText();
3609 - (void)setMarkedText:(id)aString
3610         selectedRange:(NSRange)selectedRange
3611      replacementRange:(NSRange)replacementRange {
3612   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3614   NS_ENSURE_TRUE_VOID(mTextInputHandler);
3616   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3618   NSAttributedString* attrStr;
3619   if ([aString isKindOfClass:[NSAttributedString class]]) {
3620     attrStr = static_cast<NSAttributedString*>(aString);
3621   } else {
3622     attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
3623   }
3625   mTextInputHandler->SetMarkedText(attrStr, selectedRange, &replacementRange);
3627   NS_OBJC_END_TRY_IGNORE_BLOCK;
3630 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange
3631                                                actualRange:
3632                                                    (NSRangePointer)actualRange {
3633   NS_ENSURE_TRUE(mTextInputHandler, nil);
3634   return mTextInputHandler->GetAttributedSubstringFromRange(aRange,
3635                                                             actualRange);
3638 - (NSRect)firstRectForCharacterRange:(NSRange)aRange
3639                          actualRange:(NSRangePointer)actualRange {
3640   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRect(0.0, 0.0, 0.0, 0.0));
3641   return mTextInputHandler->FirstRectForCharacterRange(aRange, actualRange);
3644 - (void)quickLookWithEvent:(NSEvent*)event {
3645   // Show dictionary by current point
3646   WidgetContentCommandEvent contentCommandEvent(
3647       true, eContentCommandLookUpDictionary, mGeckoChild);
3648   NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3649   contentCommandEvent.mRefPoint = mGeckoChild->CocoaPointsToDevPixels(point);
3650   mGeckoChild->DispatchWindowEvent(contentCommandEvent);
3651   // The widget might have been destroyed.
3654 - (NSInteger)windowLevel {
3655   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
3657   NS_ENSURE_TRUE(mTextInputHandler, [[self window] level]);
3658   return mTextInputHandler->GetWindowLevel();
3660   NS_OBJC_END_TRY_BLOCK_RETURN(NSNormalWindowLevel);
3663 #pragma mark -
3665 // This is a private API that Cocoa uses.
3666 // Cocoa will call this after the menu system returns "NO" for
3667 // "performKeyEquivalent:". We want all they key events we can get so just
3668 // return YES. In particular, this fixes ctrl-tab - we don't get a "keyDown:"
3669 // call for that without this.
3670 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
3671   return YES;
3674 - (NSEvent*)lastKeyDownEvent {
3675   return mLastKeyDownEvent;
3678 - (void)keyDown:(NSEvent*)theEvent {
3679   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3681   [mLastKeyDownEvent release];
3682   mLastKeyDownEvent = [theEvent retain];
3684   // Weird things can happen on keyboard input if the key window isn't in the
3685   // current space.  For example see bug 1056251.  To get around this, always
3686   // make sure that, if our window is key, it's also made frontmost.  Doing
3687   // this automatically switches to whatever space our window is in.  Safari
3688   // does something similar.  Our window should normally always be key --
3689   // otherwise why is the OS sending us a key down event?  But it's just
3690   // possible we're in Gecko's hidden window, so we check first.
3691   NSWindow* viewWindow = [self window];
3692   if (viewWindow && [viewWindow isKeyWindow]) {
3693     [viewWindow orderWindow:NSWindowAbove relativeTo:0];
3694   }
3696 #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
3697   if (!Preferences::GetBool("intl.allow-insecure-text-input", false) &&
3698       mGeckoChild && mTextInputHandler && mTextInputHandler->IsFocused()) {
3699     NSWindow* window = [self window];
3700     NSString* info = [NSString
3701         stringWithFormat:@"\nview [%@], window [%@], window is key %i, is "
3702                          @"fullscreen %i, app is active %i",
3703                          self, window, [window isKeyWindow],
3704                          ([window styleMask] & NSWindowStyleMaskFullScreen) !=
3705                              0,
3706                          [NSApp isActive]];
3707     nsAutoCString additionalInfo([info UTF8String]);
3709     if (mGeckoChild->GetInputContext().IsPasswordEditor() &&
3710         !TextInputHandler::IsSecureEventInputEnabled()) {
3711 #  define CRASH_MESSAGE \
3712     "A password editor has focus, but not in secure input mode"
3714       CrashReporter::AppendAppNotesToCrashReport(
3715           "\nBug 893973: "_ns + nsLiteralCString(CRASH_MESSAGE));
3716       CrashReporter::AppendAppNotesToCrashReport(additionalInfo);
3718       MOZ_CRASH(CRASH_MESSAGE);
3719 #  undef CRASH_MESSAGE
3720     } else if (!mGeckoChild->GetInputContext().IsPasswordEditor() &&
3721                TextInputHandler::IsSecureEventInputEnabled()) {
3722 #  define CRASH_MESSAGE \
3723     "A non-password editor has focus, but in secure input mode"
3725       CrashReporter::AppendAppNotesToCrashReport(
3726           "\nBug 893973: "_ns + nsLiteralCString(CRASH_MESSAGE));
3727       CrashReporter::AppendAppNotesToCrashReport(additionalInfo);
3729       MOZ_CRASH(CRASH_MESSAGE);
3730 #  undef CRASH_MESSAGE
3731     }
3732   }
3733 #endif  // #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
3735   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3736   if (mGeckoChild) {
3737     if (mTextInputHandler) {
3738       sUniqueKeyEventId++;
3739       NSMutableDictionary* nativeKeyEventsMap = [ChildView sNativeKeyEventsMap];
3740       [nativeKeyEventsMap setObject:theEvent forKey:@(sUniqueKeyEventId)];
3741       // Purge old native events, in case we're still holding on to them. We
3742       // keep at most 10 references to 10 different native events.
3743       [nativeKeyEventsMap removeObjectForKey:@(sUniqueKeyEventId - 10)];
3744       mTextInputHandler->HandleKeyDownEvent(theEvent, sUniqueKeyEventId);
3745     } else {
3746       // There was no text input handler. Offer the event to the native menu
3747       // system to check if there are any registered custom shortcuts for this
3748       // event.
3749       mGeckoChild->SendEventToNativeMenuSystem(theEvent);
3750     }
3751   }
3753   NS_OBJC_END_TRY_IGNORE_BLOCK;
3756 - (void)keyUp:(NSEvent*)theEvent {
3757   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
3759   NS_ENSURE_TRUE(mGeckoChild, );
3761   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3763   mTextInputHandler->HandleKeyUpEvent(theEvent);
3765   NS_OBJC_END_TRY_IGNORE_BLOCK;
3768 - (void)insertNewline:(id)sender {
3769   if (mTextInputHandler) {
3770     mTextInputHandler->HandleCommand(Command::InsertParagraph);
3771   }
3774 - (void)insertLineBreak:(id)sender {
3775   // Ctrl + Enter in the default settings.
3776   if (mTextInputHandler) {
3777     mTextInputHandler->HandleCommand(Command::InsertLineBreak);
3778   }
3781 - (void)deleteBackward:(id)sender {
3782   // Backspace in the default settings.
3783   if (mTextInputHandler) {
3784     mTextInputHandler->HandleCommand(Command::DeleteCharBackward);
3785   }
3788 - (void)deleteBackwardByDecomposingPreviousCharacter:(id)sender {
3789   // Ctrl + Backspace in the default settings.
3790   if (mTextInputHandler) {
3791     mTextInputHandler->HandleCommand(Command::DeleteCharBackward);
3792   }
3795 - (void)deleteWordBackward:(id)sender {
3796   // Alt + Backspace in the default settings.
3797   if (mTextInputHandler) {
3798     mTextInputHandler->HandleCommand(Command::DeleteWordBackward);
3799   }
3802 - (void)deleteToBeginningOfBackward:(id)sender {
3803   // Command + Backspace in the default settings.
3804   if (mTextInputHandler) {
3805     mTextInputHandler->HandleCommand(Command::DeleteToBeginningOfLine);
3806   }
3809 - (void)deleteForward:(id)sender {
3810   // Delete in the default settings.
3811   if (mTextInputHandler) {
3812     mTextInputHandler->HandleCommand(Command::DeleteCharForward);
3813   }
3816 - (void)deleteWordForward:(id)sender {
3817   // Alt + Delete in the default settings.
3818   if (mTextInputHandler) {
3819     mTextInputHandler->HandleCommand(Command::DeleteWordForward);
3820   }
3823 - (void)insertTab:(id)sender {
3824   // Tab in the default settings.
3825   if (mTextInputHandler) {
3826     mTextInputHandler->HandleCommand(Command::InsertTab);
3827   }
3830 - (void)insertBacktab:(id)sender {
3831   // Shift + Tab in the default settings.
3832   if (mTextInputHandler) {
3833     mTextInputHandler->HandleCommand(Command::InsertBacktab);
3834   }
3837 - (void)moveRight:(id)sender {
3838   // RightArrow in the default settings.
3839   if (mTextInputHandler) {
3840     mTextInputHandler->HandleCommand(Command::CharNext);
3841   }
3844 - (void)moveRightAndModifySelection:(id)sender {
3845   // Shift + RightArrow in the default settings.
3846   if (mTextInputHandler) {
3847     mTextInputHandler->HandleCommand(Command::SelectCharNext);
3848   }
3851 - (void)moveWordRight:(id)sender {
3852   // Alt + RightArrow in the default settings.
3853   if (mTextInputHandler) {
3854     mTextInputHandler->HandleCommand(Command::WordNext);
3855   }
3858 - (void)moveWordRightAndModifySelection:(id)sender {
3859   // Alt + Shift + RightArrow in the default settings.
3860   if (mTextInputHandler) {
3861     mTextInputHandler->HandleCommand(Command::SelectWordNext);
3862   }
3865 - (void)moveToRightEndOfLine:(id)sender {
3866   // Command + RightArrow in the default settings.
3867   if (mTextInputHandler) {
3868     mTextInputHandler->HandleCommand(Command::EndLine);
3869   }
3872 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
3873   // Command + Shift + RightArrow in the default settings.
3874   if (mTextInputHandler) {
3875     mTextInputHandler->HandleCommand(Command::SelectEndLine);
3876   }
3879 - (void)moveLeft:(id)sender {
3880   // LeftArrow in the default settings.
3881   if (mTextInputHandler) {
3882     mTextInputHandler->HandleCommand(Command::CharPrevious);
3883   }
3886 - (void)moveLeftAndModifySelection:(id)sender {
3887   // Shift + LeftArrow in the default settings.
3888   if (mTextInputHandler) {
3889     mTextInputHandler->HandleCommand(Command::SelectCharPrevious);
3890   }
3893 - (void)moveWordLeft:(id)sender {
3894   // Alt + LeftArrow in the default settings.
3895   if (mTextInputHandler) {
3896     mTextInputHandler->HandleCommand(Command::WordPrevious);
3897   }
3900 - (void)moveWordLeftAndModifySelection:(id)sender {
3901   // Alt + Shift + LeftArrow in the default settings.
3902   if (mTextInputHandler) {
3903     mTextInputHandler->HandleCommand(Command::SelectWordPrevious);
3904   }
3907 - (void)moveToLeftEndOfLine:(id)sender {
3908   // Command + LeftArrow in the default settings.
3909   if (mTextInputHandler) {
3910     mTextInputHandler->HandleCommand(Command::BeginLine);
3911   }
3914 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
3915   // Command + Shift + LeftArrow in the default settings.
3916   if (mTextInputHandler) {
3917     mTextInputHandler->HandleCommand(Command::SelectBeginLine);
3918   }
3921 - (void)moveUp:(id)sender {
3922   // ArrowUp in the default settings.
3923   if (mTextInputHandler) {
3924     mTextInputHandler->HandleCommand(Command::LinePrevious);
3925   }
3928 - (void)moveUpAndModifySelection:(id)sender {
3929   // Shift + ArrowUp in the default settings.
3930   if (mTextInputHandler) {
3931     mTextInputHandler->HandleCommand(Command::SelectLinePrevious);
3932   }
3935 - (void)moveToBeginningOfDocument:(id)sender {
3936   // Command + ArrowUp in the default settings.
3937   if (mTextInputHandler) {
3938     mTextInputHandler->HandleCommand(Command::MoveTop);
3939   }
3942 - (void)moveToBeginningOfDocumentAndModifySelection:(id)sender {
3943   // Command + Shift + ArrowUp or Shift + Home in the default settings.
3944   if (mTextInputHandler) {
3945     mTextInputHandler->HandleCommand(Command::SelectTop);
3946   }
3949 - (void)moveDown:(id)sender {
3950   // ArrowDown in the default settings.
3951   if (mTextInputHandler) {
3952     mTextInputHandler->HandleCommand(Command::LineNext);
3953   }
3956 - (void)moveDownAndModifySelection:(id)sender {
3957   // Shift + ArrowDown in the default settings.
3958   if (mTextInputHandler) {
3959     mTextInputHandler->HandleCommand(Command::SelectLineNext);
3960   }
3963 - (void)moveToEndOfDocument:(id)sender {
3964   // Command + ArrowDown in the default settings.
3965   if (mTextInputHandler) {
3966     mTextInputHandler->HandleCommand(Command::MoveBottom);
3967   }
3970 - (void)moveToEndOfDocumentAndModifySelection:(id)sender {
3971   // Command + Shift + ArrowDown or Shift + End in the default settings.
3972   if (mTextInputHandler) {
3973     mTextInputHandler->HandleCommand(Command::SelectBottom);
3974   }
3977 - (void)scrollPageUp:(id)sender {
3978   // PageUp in the default settings.
3979   if (mTextInputHandler) {
3980     mTextInputHandler->HandleCommand(Command::ScrollPageUp);
3981   }
3984 - (void)pageUpAndModifySelection:(id)sender {
3985   // Shift + PageUp in the default settings.
3986   if (mTextInputHandler) {
3987     mTextInputHandler->HandleCommand(Command::SelectPageUp);
3988   }
3991 - (void)scrollPageDown:(id)sender {
3992   // PageDown in the default settings.
3993   if (mTextInputHandler) {
3994     mTextInputHandler->HandleCommand(Command::ScrollPageDown);
3995   }
3998 - (void)pageDownAndModifySelection:(id)sender {
3999   // Shift + PageDown in the default settings.
4000   if (mTextInputHandler) {
4001     mTextInputHandler->HandleCommand(Command::SelectPageDown);
4002   }
4005 - (void)scrollToEndOfDocument:(id)sender {
4006   // End in the default settings.
4007   if (mTextInputHandler) {
4008     mTextInputHandler->HandleCommand(Command::ScrollBottom);
4009   }
4012 - (void)scrollToBeginningOfDocument:(id)sender {
4013   // Home in the default settings.
4014   if (mTextInputHandler) {
4015     mTextInputHandler->HandleCommand(Command::ScrollTop);
4016   }
4019 // XXX Don't decleare nor implement calcelOperation: because it
4020 //     causes not calling keyDown: for Command + Period.
4021 //     We need to handle it from doCommandBySelector:.
4023 - (void)complete:(id)sender {
4024   // Alt + Escape or Alt + Shift + Escape in the default settings.
4025   if (mTextInputHandler) {
4026     mTextInputHandler->HandleCommand(Command::Complete);
4027   }
4030 - (void)flagsChanged:(NSEvent*)theEvent {
4031   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4033   NS_ENSURE_TRUE(mGeckoChild, );
4035   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4036   mTextInputHandler->HandleFlagsChanged(theEvent);
4038   NS_OBJC_END_TRY_IGNORE_BLOCK;
4041 - (BOOL)isFirstResponder {
4042   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4044   NSResponder* resp = [[self window] firstResponder];
4045   return (resp == (NSResponder*)self);
4047   NS_OBJC_END_TRY_BLOCK_RETURN(NO);
4050 - (BOOL)isDragInProgress {
4051   if (!mDragService) return NO;
4053   nsCOMPtr<nsIDragSession> dragSession;
4054   mDragService->GetCurrentSession(getter_AddRefs(dragSession));
4055   return dragSession != nullptr;
4058 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent {
4059   // If we're being destroyed assume the default -- return YES.
4060   if (!mGeckoChild) return YES;
4062   WidgetMouseEvent geckoEvent(true, eMouseActivate, mGeckoChild,
4063                               WidgetMouseEvent::eReal);
4064   [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
4065   return (mGeckoChild->DispatchInputEvent(&geckoEvent).mContentStatus !=
4066           nsEventStatus_eConsumeNoDefault);
4069 // We must always call through to our superclass, even when mGeckoChild is
4070 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
4071 - (BOOL)becomeFirstResponder {
4072   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4074   return [super becomeFirstResponder];
4076   NS_OBJC_END_TRY_BLOCK_RETURN(YES);
4079 - (void)viewsWindowDidBecomeKey {
4080   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4082   if (!mGeckoChild) return;
4084   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4086   // check to see if the window implements the mozWindow protocol. This
4087   // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
4088   // which can happen because these activate calls propagate out
4089   // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
4090   BOOL isMozWindow =
4091       [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
4092   if (isMozWindow) [[self window] setSuppressMakeKeyFront:YES];
4094   nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
4095   if (listener) listener->WindowActivated();
4097   if (isMozWindow) [[self window] setSuppressMakeKeyFront:NO];
4099   if (mGeckoChild->GetInputContext().IsPasswordEditor()) {
4100     TextInputHandler::EnableSecureEventInput();
4101   } else {
4102     TextInputHandler::EnsureSecureEventInputDisabled();
4103   }
4105   NS_OBJC_END_TRY_IGNORE_BLOCK;
4108 - (void)viewsWindowDidResignKey {
4109   if (!mGeckoChild) return;
4111   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4113   nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
4114   if (listener) listener->WindowDeactivated();
4116   TextInputHandler::EnsureSecureEventInputDisabled();
4119 // If the call to removeFromSuperview isn't delayed from nsChildView::
4120 // TearDownView(), the NSView hierarchy might get changed during calls to
4121 // [ChildView drawRect:], which leads to "beyond bounds" exceptions in
4122 // NSCFArray.  For more info see bmo bug 373122.  Apple's docs claim that
4123 // removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
4124 // display" (whatever "display" means).  But it's _not_ true that it can be
4125 // safely invoked during calls to [NSView drawRect:].  We use
4126 // removeFromSuperview here because there's no longer any danger of being
4127 // "invoked during display", and because doing do clears up bmo bug 384343.
4128 - (void)delayedTearDown {
4129   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4131   [self removeFromSuperview];
4132   [self release];
4134   NS_OBJC_END_TRY_IGNORE_BLOCK;
4137 #pragma mark -
4139 // drag'n'drop stuff
4140 #define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
4142 - (NSDragOperation)dragOperationFromDragAction:(int32_t)aDragAction {
4143   if (nsIDragService::DRAGDROP_ACTION_LINK & aDragAction)
4144     return NSDragOperationLink;
4145   if (nsIDragService::DRAGDROP_ACTION_COPY & aDragAction)
4146     return NSDragOperationCopy;
4147   if (nsIDragService::DRAGDROP_ACTION_MOVE & aDragAction)
4148     return NSDragOperationGeneric;
4149   return NSDragOperationNone;
4152 - (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint {
4153   if (!mGeckoChild) {
4154     return LayoutDeviceIntPoint(0, 0);
4155   }
4157   NSPoint localPoint = [self convertPoint:aPoint fromView:nil];
4158   return mGeckoChild->CocoaPointsToDevPixels(localPoint);
4161 - (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint {
4162   if (!mGeckoChild) {
4163     return LayoutDeviceIntPoint(0, 0);
4164   }
4166   NSPoint localPoint = [self convertPoint:aPoint fromView:nil];
4167   return mGeckoChild->CocoaPointsToDevPixelsRoundDown(localPoint);
4170 // This is a utility function used by NSView drag event methods
4171 // to send events. It contains all of the logic needed for Gecko
4172 // dragging to work. Returns the appropriate cocoa drag operation code.
4173 - (NSDragOperation)doDragAction:(EventMessage)aMessage sender:(id)aSender {
4174   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4176   if (!mGeckoChild) return NSDragOperationNone;
4178   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView doDragAction: entered\n"));
4180   if (!mDragService) {
4181     CallGetService(kDragServiceContractID, &mDragService);
4182     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
4183     if (!mDragService) return NSDragOperationNone;
4184   }
4186   if (aMessage == eDragEnter) {
4187     mDragService->StartDragSession();
4188   }
4190   nsCOMPtr<nsIDragSession> dragSession;
4191   mDragService->GetCurrentSession(getter_AddRefs(dragSession));
4192   if (dragSession) {
4193     if (aMessage == eDragOver) {
4194       // fire the drag event at the source. Just ignore whether it was
4195       // cancelled or not as there isn't actually a means to stop the drag
4196       nsCOMPtr<nsIDragService> dragService = mDragService;
4197       dragService->FireDragEventAtSource(
4198           eDrag, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
4199       dragSession->SetCanDrop(false);
4200     } else if (aMessage == eDrop) {
4201       // We make the assumption that the dragOver handlers have correctly set
4202       // the |canDrop| property of the Drag Session.
4203       bool canDrop = false;
4204       if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
4205         [self doDragAction:eDragExit sender:aSender];
4207         nsCOMPtr<nsINode> sourceNode;
4208         dragSession->GetSourceNode(getter_AddRefs(sourceNode));
4209         if (!sourceNode) {
4210           nsCOMPtr<nsIDragService> dragService = mDragService;
4211           dragService->EndDragSession(
4212               false, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
4213         }
4214         return NSDragOperationNone;
4215       }
4216     }
4218     unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
4219     uint32_t action = nsIDragService::DRAGDROP_ACTION_MOVE;
4220     // force copy = option, alias = cmd-option, default is move
4221     if (modifierFlags & NSEventModifierFlagOption) {
4222       if (modifierFlags & NSEventModifierFlagCommand)
4223         action = nsIDragService::DRAGDROP_ACTION_LINK;
4224       else
4225         action = nsIDragService::DRAGDROP_ACTION_COPY;
4226     }
4227     dragSession->SetDragAction(action);
4228   }
4230   // set up gecko event
4231   WidgetDragEvent geckoEvent(true, aMessage, mGeckoChild);
4232   nsCocoaUtils::InitInputEvent(geckoEvent, [NSApp currentEvent]);
4234   // Use our own coordinates in the gecko event.
4235   // Convert event from gecko global coords to gecko view coords.
4236   NSPoint draggingLoc = [aSender draggingLocation];
4238   geckoEvent.mRefPoint = [self convertWindowCoordinates:draggingLoc];
4240   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4241   mGeckoChild->DispatchInputEvent(&geckoEvent);
4242   if (!mGeckoChild) return NSDragOperationNone;
4244   if (dragSession) {
4245     switch (aMessage) {
4246       case eDragEnter:
4247       case eDragOver: {
4248         uint32_t dragAction;
4249         dragSession->GetDragAction(&dragAction);
4251         // If TakeChildProcessDragAction returns something other than
4252         // DRAGDROP_ACTION_UNINITIALIZED, it means that the last event was sent
4253         // to the child process and this event is also being sent to the child
4254         // process. In this case, use the last event's action instead.
4255         nsDragService* dragService = static_cast<nsDragService*>(mDragService);
4256         int32_t childDragAction = dragService->TakeChildProcessDragAction();
4257         if (childDragAction != nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) {
4258           dragAction = childDragAction;
4259         }
4261         return [self dragOperationFromDragAction:dragAction];
4262       }
4263       case eDragExit:
4264       case eDrop: {
4265         nsCOMPtr<nsINode> sourceNode;
4266         dragSession->GetSourceNode(getter_AddRefs(sourceNode));
4267         if (!sourceNode) {
4268           // We're leaving a window while doing a drag that was
4269           // initiated in a different app. End the drag session,
4270           // since we're done with it for now (until the user
4271           // drags back into mozilla).
4272           nsCOMPtr<nsIDragService> dragService = mDragService;
4273           dragService->EndDragSession(
4274               false, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
4275         }
4276         break;
4277       }
4278       default:
4279         break;
4280     }
4281   }
4283   return NSDragOperationGeneric;
4285   NS_OBJC_END_TRY_BLOCK_RETURN(NSDragOperationNone);
4288 // NSDraggingDestination
4289 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
4290   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4292   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingEntered: entered\n"));
4294   // there should never be a globalDragPboard when "draggingEntered:" is
4295   // called, but just in case we'll take care of it here.
4296   [globalDragPboard release];
4298   // Set the global drag pasteboard that will be used for this drag session.
4299   // This will be set back to nil when the drag session ends (mouse exits
4300   // the view or a drop happens within the view).
4301   globalDragPboard = [[sender draggingPasteboard] retain];
4303   return [self doDragAction:eDragEnter sender:sender];
4305   NS_OBJC_END_TRY_BLOCK_RETURN(NSDragOperationNone);
4308 // NSDraggingDestination
4309 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
4310   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingUpdated: entered\n"));
4311   return [self doDragAction:eDragOver sender:sender];
4314 // NSDraggingDestination
4315 - (void)draggingExited:(id<NSDraggingInfo>)sender {
4316   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingExited: entered\n"));
4318   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4319   [self doDragAction:eDragExit sender:sender];
4320   NS_IF_RELEASE(mDragService);
4323 // NSDraggingDestination
4324 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
4325   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4326   BOOL handled = [self doDragAction:eDrop sender:sender] != NSDragOperationNone;
4327   NS_IF_RELEASE(mDragService);
4328   return handled;
4331 // NSDraggingSource
4332 // This is just implemented so we comply with the NSDraggingSource protocol.
4333 - (NSDragOperation)draggingSession:(NSDraggingSession*)session
4334     sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
4335   return UINT_MAX;
4338 // NSDraggingSource
4339 - (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession*)session {
4340   return YES;
4343 // NSDraggingSource
4344 - (void)draggingSession:(NSDraggingSession*)aSession
4345            endedAtPoint:(NSPoint)aPoint
4346               operation:(NSDragOperation)aOperation {
4347   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4349 #ifdef NIGHTLY_BUILD
4350   MOZ_RELEASE_ASSERT(NS_IsMainThread());
4351 #endif
4353   gDraggedTransferables = nullptr;
4355   NSEvent* currentEvent = [NSApp currentEvent];
4356   gUserCancelledDrag = ([currentEvent type] == NSEventTypeKeyDown &&
4357                         [currentEvent keyCode] == kVK_Escape);
4359   if (!mDragService) {
4360     CallGetService(kDragServiceContractID, &mDragService);
4361     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
4362   }
4364   if (mDragService) {
4365     RefPtr<nsDragService> dragService =
4366         static_cast<nsDragService*>(mDragService);
4368     // Set the dragend point from the current mouse location
4369     // FIXME(emilio): Weird that we wouldn't use aPoint instead? Seems to work
4370     // locally as well...
4371     // NSPoint pnt = aPoint;
4372     NSPoint pnt = [NSEvent mouseLocation];
4373     NSPoint locationInWindow =
4374         nsCocoaUtils::ConvertPointFromScreen([self window], pnt);
4375     FlipCocoaScreenCoordinate(pnt);
4376     dragService->SetDragEndPoint(
4377         [self convertWindowCoordinates:locationInWindow]);
4379     // XXX: dropEffect should be updated per |aOperation|.
4380     // As things stand though, |aOperation| isn't well handled within "our"
4381     // events, that is, when the drop happens within the window: it is set
4382     // either to NSDragOperationGeneric or to NSDragOperationNone.
4383     // For that reason, it's not yet possible to override dropEffect per the
4384     // given OS value, and it's also unclear what's the correct dropEffect
4385     // value for NSDragOperationGeneric that is passed by other applications.
4386     // All that said, NSDragOperationNone is still reliable.
4387     if (aOperation == NSDragOperationNone) {
4388       RefPtr<dom::DataTransfer> dataTransfer = dragService->GetDataTransfer();
4389       if (dataTransfer) {
4390         dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
4391       }
4392     }
4394     dragService->EndDragSession(true,
4395                                 nsCocoaUtils::ModifiersForEvent(currentEvent));
4396     NS_RELEASE(mDragService);
4397   }
4399   [globalDragPboard release];
4400   globalDragPboard = nil;
4401   [gLastDragMouseDownEvent release];
4402   gLastDragMouseDownEvent = nil;
4403   mPerformedDrag = YES;
4405   NS_OBJC_END_TRY_IGNORE_BLOCK;
4408 // NSDraggingSource
4409 - (void)draggingSession:(NSDraggingSession*)aSession
4410            movedToPoint:(NSPoint)aPoint {
4411   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4413   // Get the drag service if it isn't already cached. The drag service
4414   // isn't cached when dragging over a different application.
4415   nsCOMPtr<nsIDragService> dragService = mDragService;
4416   if (!dragService) {
4417     dragService = do_GetService(kDragServiceContractID);
4418   }
4420   if (dragService) {
4421     nsDragService* ds = static_cast<nsDragService*>(dragService.get());
4422     ds->DragMovedWithView(aSession, aPoint);
4423   }
4425   NS_OBJC_END_TRY_IGNORE_BLOCK;
4428 // NSDraggingSource
4429 - (void)draggingSession:(NSDraggingSession*)aSession
4430        willBeginAtPoint:(NSPoint)aPoint {
4431   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4433   // there should never be a globalDragPboard when "willBeginAtPoint:" is
4434   // called, but just in case we'll take care of it here.
4435   [globalDragPboard release];
4437   // Set the global drag pasteboard that will be used for this drag session.
4438   // This will be set back to nil when the drag session ends (mouse exits
4439   // the view or a drop happens within the view).
4440   globalDragPboard = [[aSession draggingPasteboard] retain];
4442   NS_OBJC_END_TRY_IGNORE_BLOCK;
4445 // Get the paste location from the low level pasteboard.
4446 static CFTypeRefPtr<CFURLRef> GetPasteLocation(NSPasteboard* aPasteboard) {
4447   PasteboardRef pboardRef = nullptr;
4448   PasteboardCreate((CFStringRef)[aPasteboard name], &pboardRef);
4449   if (!pboardRef) {
4450     return nullptr;
4451   }
4453   auto pasteBoard = CFTypeRefPtr<PasteboardRef>::WrapUnderCreateRule(pboardRef);
4454   PasteboardSynchronize(pasteBoard.get());
4456   CFURLRef urlRef = nullptr;
4457   PasteboardCopyPasteLocation(pasteBoard.get(), &urlRef);
4458   return CFTypeRefPtr<CFURLRef>::WrapUnderCreateRule(urlRef);
4461 // NSPasteboardItemDataProvider
4462 - (void)pasteboard:(NSPasteboard*)aPasteboard
4463                   item:(NSPasteboardItem*)aItem
4464     provideDataForType:(NSString*)aType {
4465   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
4467 #ifdef NIGHTLY_BUILD
4468   MOZ_RELEASE_ASSERT(NS_IsMainThread());
4469 #endif
4471   if (!gDraggedTransferables) {
4472     return;
4473   }
4475   uint32_t count = 0;
4476   gDraggedTransferables->GetLength(&count);
4478   for (uint32_t j = 0; j < count; j++) {
4479     nsCOMPtr<nsITransferable> currentTransferable =
4480         do_QueryElementAt(gDraggedTransferables, j);
4481     if (!currentTransferable) {
4482       return;
4483     }
4485     // Transform the transferable to an NSDictionary.
4486     NSDictionary* pasteboardOutputDict =
4487         nsClipboard::PasteboardDictFromTransferable(currentTransferable);
4488     if (!pasteboardOutputDict) {
4489       return;
4490     }
4492     // Write everything out to the pasteboard.
4493     unsigned int typeCount = [pasteboardOutputDict count];
4494     NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1];
4495     [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
4496     [types addObject:[UTIHelper stringFromPboardType:kMozWildcardPboardType]];
4497     for (unsigned int k = 0; k < typeCount; k++) {
4498       NSString* curType = [types objectAtIndex:k];
4499       if ([curType isEqualToString:[UTIHelper stringFromPboardType:
4500                                                   NSPasteboardTypeString]] ||
4501           [curType
4502               isEqualToString:[UTIHelper
4503                                   stringFromPboardType:kPublicUrlPboardType]] ||
4504           [curType isEqualToString:[UTIHelper stringFromPboardType:
4505                                                   kPublicUrlNamePboardType]] ||
4506           [curType
4507               isEqualToString:[UTIHelper
4508                                   stringFromPboardType:(NSString*)
4509                                                            kUTTypeFileURL]]) {
4510         [aPasteboard setString:[pasteboardOutputDict valueForKey:curType]
4511                        forType:curType];
4512       } else if ([curType isEqualToString:[UTIHelper
4513                                               stringFromPboardType:
4514                                                   kUrlsWithTitlesPboardType]]) {
4515         [aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType]
4516                              forType:curType];
4517       } else if ([curType
4518                      isEqualToString:[UTIHelper stringFromPboardType:
4519                                                     NSPasteboardTypeHTML]]) {
4520         [aPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(
4521                                    [pasteboardOutputDict valueForKey:curType]))
4522                        forType:curType];
4523       } else if ([curType
4524                      isEqualToString:[UTIHelper stringFromPboardType:
4525                                                     NSPasteboardTypeTIFF]] ||
4526                  [curType isEqualToString:[UTIHelper
4527                                               stringFromPboardType:
4528                                                   kMozCustomTypesPboardType]]) {
4529         [aPasteboard setData:[pasteboardOutputDict valueForKey:curType]
4530                      forType:curType];
4531       } else if ([curType
4532                      isEqualToString:[UTIHelper stringFromPboardType:
4533                                                     kMozFileUrlsPboardType]]) {
4534         [aPasteboard writeObjects:[pasteboardOutputDict valueForKey:curType]];
4535       } else if ([curType
4536                      isEqualToString:
4537                          [UTIHelper
4538                              stringFromPboardType:
4539                                  (NSString*)kPasteboardTypeFileURLPromise]]) {
4540         nsCOMPtr<nsIFile> targFile;
4541         NS_NewLocalFile(u""_ns, true, getter_AddRefs(targFile));
4542         nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
4543         if (!macLocalFile) {
4544           NS_ERROR("No Mac local file");
4545           continue;
4546         }
4548         CFTypeRefPtr<CFURLRef> url = GetPasteLocation(aPasteboard);
4549         if (!url) {
4550           continue;
4551         }
4553         if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL(url.get()))) {
4554           NS_ERROR("failed InitWithCFURL");
4555           continue;
4556         }
4558         if (!gDraggedTransferables) {
4559           continue;
4560         }
4562         uint32_t transferableCount;
4563         nsresult rv = gDraggedTransferables->GetLength(&transferableCount);
4564         if (NS_FAILED(rv)) {
4565           continue;
4566         }
4568         for (uint32_t i = 0; i < transferableCount; i++) {
4569           nsCOMPtr<nsITransferable> item =
4570               do_QueryElementAt(gDraggedTransferables, i);
4571           if (!item) {
4572             NS_ERROR("no transferable");
4573             continue;
4574           }
4576           item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile);
4578           // Now request the kFilePromiseMime data, which will invoke the data
4579           // provider. If successful, the file will have been created.
4580           nsCOMPtr<nsISupports> fileDataPrimitive;
4581           Unused << item->GetTransferData(kFilePromiseMime,
4582                                           getter_AddRefs(fileDataPrimitive));
4583         }
4585         [aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType]
4586                              forType:curType];
4587       }
4588     }
4589   }
4591   NS_OBJC_END_TRY_IGNORE_BLOCK;
4594 #pragma mark -
4596 // Support for the "Services" menu. We currently only support sending strings
4597 // and HTML to system services.
4598 // This method can be called on any thread (see bug 1751687). We can only
4599 // usefully handle it on the main thread.
4600 - (id)validRequestorForSendType:(NSString*)sendType
4601                      returnType:(NSString*)returnType {
4602   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4604   if (!NS_IsMainThread()) {
4605     // We don't have any thread-safe ways of checking whether we can send
4606     // or receive content. Just say no. In normal cases, we expect this
4607     // method to be called on the main thread.
4608     return [super validRequestorForSendType:sendType returnType:returnType];
4609   }
4611   // sendType contains the type of data that the service would like this
4612   // application to send to it.  sendType is nil if the service is not
4613   // requesting any data.
4614   //
4615   // returnType contains the type of data the the service would like to
4616   // return to this application (e.g., to overwrite the selection).
4617   // returnType is nil if the service will not return any data.
4618   //
4619   // The following condition thus triggers when the service expects a string
4620   // or HTML from us or no data at all AND when the service will either not
4621   // send back any data to us or will send a string or HTML back to us.
4623   id result = nil;
4625   NSString* stringType =
4626       [UTIHelper stringFromPboardType:NSPasteboardTypeString];
4627   NSString* htmlType = [UTIHelper stringFromPboardType:NSPasteboardTypeHTML];
4628   if ((!sendType || [sendType isEqualToString:stringType] ||
4629        [sendType isEqualToString:htmlType]) &&
4630       (!returnType || [returnType isEqualToString:stringType] ||
4631        [returnType isEqualToString:htmlType])) {
4632     if (mGeckoChild) {
4633       // Assume that this object will be able to handle this request.
4634       result = self;
4636       // Keep the ChildView alive during this operation.
4637       nsAutoRetainCocoaObject kungFuDeathGrip(self);
4639       if (sendType) {
4640         // Determine if there is a current selection (chrome/content).
4641         if (!nsClipboard::sSelectionCache) {
4642           result = nil;
4643         }
4644       }
4646       // Determine if we can paste (if receiving data from the service).
4647       if (mGeckoChild && returnType) {
4648         WidgetContentCommandEvent command(
4649             true, eContentCommandPasteTransferable, mGeckoChild, true);
4650         // This might possibly destroy our widget (and null out mGeckoChild).
4651         mGeckoChild->DispatchWindowEvent(command);
4652         if (!mGeckoChild || !command.mSucceeded || !command.mIsEnabled)
4653           result = nil;
4654       }
4655     }
4656   }
4658   // Give the superclass a chance if this object will not handle this request.
4659   if (!result)
4660     result = [super validRequestorForSendType:sendType returnType:returnType];
4662   return result;
4664   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
4667 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
4668   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4670   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4672   // Make sure that the service will accept strings or HTML.
4673   if (![types
4674           containsObject:[UTIHelper stringFromPboardType:NSStringPboardType]] &&
4675       ![types
4676           containsObject:[UTIHelper
4677                              stringFromPboardType:NSPasteboardTypeString]] &&
4678       ![types containsObject:[UTIHelper
4679                                  stringFromPboardType:NSPasteboardTypeHTML]]) {
4680     return NO;
4681   }
4683   // Bail out if there is no Gecko object.
4684   if (!mGeckoChild) return NO;
4686   // Transform the transferable to an NSDictionary.
4687   NSDictionary* pasteboardOutputDict = nullptr;
4689   pasteboardOutputDict =
4690       nsClipboard::PasteboardDictFromTransferable(nsClipboard::sSelectionCache);
4692   if (!pasteboardOutputDict) return NO;
4694   // Declare the pasteboard types.
4695   unsigned int typeCount = [pasteboardOutputDict count];
4696   NSMutableArray* declaredTypes = [NSMutableArray arrayWithCapacity:typeCount];
4697   [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]];
4698   [pboard declareTypes:declaredTypes owner:nil];
4700   // Write the data to the pasteboard.
4701   for (unsigned int i = 0; i < typeCount; i++) {
4702     NSString* currentKey = [declaredTypes objectAtIndex:i];
4703     id currentValue = [pasteboardOutputDict valueForKey:currentKey];
4705     if ([currentKey
4706             isEqualToString:[UTIHelper
4707                                 stringFromPboardType:NSPasteboardTypeString]] ||
4708         [currentKey
4709             isEqualToString:[UTIHelper
4710                                 stringFromPboardType:kPublicUrlPboardType]] ||
4711         [currentKey isEqualToString:[UTIHelper stringFromPboardType:
4712                                                    kPublicUrlNamePboardType]]) {
4713       [pboard setString:currentValue forType:currentKey];
4714     } else if ([currentKey
4715                    isEqualToString:
4716                        [UTIHelper stringFromPboardType:NSPasteboardTypeHTML]]) {
4717       [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
4718                 forType:currentKey];
4719     } else if ([currentKey
4720                    isEqualToString:
4721                        [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF]]) {
4722       [pboard setData:currentValue forType:currentKey];
4723     } else if ([currentKey
4724                    isEqualToString:
4725                        [UTIHelper
4726                            stringFromPboardType:
4727                                (NSString*)kPasteboardTypeFileURLPromise]] ||
4728                [currentKey
4729                    isEqualToString:[UTIHelper stringFromPboardType:
4730                                                   kUrlsWithTitlesPboardType]]) {
4731       [pboard setPropertyList:currentValue forType:currentKey];
4732     }
4733   }
4734   return YES;
4736   NS_OBJC_END_TRY_BLOCK_RETURN(NO);
4739 // Called if the service wants us to replace the current selection.
4740 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
4741   nsresult rv;
4742   nsCOMPtr<nsITransferable> trans =
4743       do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
4744   if (NS_FAILED(rv)) return NO;
4745   trans->Init(nullptr);
4747   trans->AddDataFlavor(kTextMime);
4748   trans->AddDataFlavor(kHTMLMime);
4750   rv = nsClipboard::TransferableFromPasteboard(trans, pboard);
4751   if (NS_FAILED(rv)) return NO;
4753   NS_ENSURE_TRUE(mGeckoChild, false);
4755   WidgetContentCommandEvent command(true, eContentCommandPasteTransferable,
4756                                     mGeckoChild);
4757   command.mTransferable = trans;
4758   mGeckoChild->DispatchWindowEvent(command);
4760   return command.mSucceeded && command.mIsEnabled;
4763 - (void)pressureChangeWithEvent:(NSEvent*)event {
4764   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK
4766   NSInteger stage = [event stage];
4767   if (mLastPressureStage == 1 && stage == 2) {
4768     NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
4769     if ([userDefaults integerForKey:@"com.apple.trackpad.forceClick"] == 1) {
4770       // This is no public API to get configuration for current force click.
4771       // This is filed as radar 29294285.
4772       [self quickLookWithEvent:event];
4773     }
4774   }
4775   mLastPressureStage = stage;
4777   NS_OBJC_END_TRY_IGNORE_BLOCK
4780 nsresult nsChildView::GetSelectionAsPlaintext(nsAString& aResult) {
4781   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4783   if (!nsClipboard::sSelectionCache) {
4784     MOZ_ASSERT(aResult.IsEmpty());
4785     return NS_OK;
4786   }
4788   // Get the current chrome or content selection.
4789   NSDictionary* pasteboardOutputDict = nullptr;
4790   pasteboardOutputDict =
4791       nsClipboard::PasteboardDictFromTransferable(nsClipboard::sSelectionCache);
4793   if (NS_WARN_IF(!pasteboardOutputDict)) {
4794     return NS_ERROR_FAILURE;
4795   }
4797   // Declare the pasteboard types.
4798   unsigned int typeCount = [pasteboardOutputDict count];
4799   NSMutableArray* declaredTypes = [NSMutableArray arrayWithCapacity:typeCount];
4800   [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]];
4801   NSString* currentKey = [declaredTypes objectAtIndex:0];
4802   NSString* currentValue = [pasteboardOutputDict valueForKey:currentKey];
4803   const char* textSelection = [currentValue UTF8String];
4804   aResult = NS_ConvertUTF8toUTF16(textSelection);
4806   return NS_OK;
4808   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
4811 #ifdef DEBUG
4812 nsresult nsChildView::SetHiDPIMode(bool aHiDPI) {
4813   nsCocoaUtils::InvalidateHiDPIState();
4814   Preferences::SetInt("gfx.hidpi.enabled", aHiDPI ? 1 : 0);
4815   BackingScaleFactorChanged();
4816   return NS_OK;
4819 nsresult nsChildView::RestoreHiDPIMode() {
4820   nsCocoaUtils::InvalidateHiDPIState();
4821   Preferences::ClearUser("gfx.hidpi.enabled");
4822   BackingScaleFactorChanged();
4823   return NS_OK;
4825 #endif
4827 #pragma mark -
4829 #ifdef ACCESSIBILITY
4831 /* Every ChildView has a corresponding mozDocAccessible object that is doing all
4832    the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
4833    object.
4835    All ChildView needs to do is to route all accessibility calls (from the
4836    NSAccessibility APIs) down to its object, pretending that they are the same.
4838 - (id<mozAccessible>)accessible {
4839   if (!mGeckoChild) return nil;
4841   id<mozAccessible> nativeAccessible = nil;
4843   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4844   RefPtr<nsChildView> geckoChild(mGeckoChild);
4845   RefPtr<a11y::LocalAccessible> accessible =
4846       geckoChild->GetDocumentAccessible();
4847   if (!accessible) return nil;
4849   accessible->GetNativeInterface((void**)&nativeAccessible);
4851 #  ifdef DEBUG_hakan
4852   NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
4853 #  endif
4855   return nativeAccessible;
4858 /* Implementation of formal mozAccessible formal protocol (enabling mozViews
4859    to talk to mozAccessible objects in the accessibility module). */
4861 - (BOOL)hasRepresentedView {
4862   return YES;
4865 - (id)representedView {
4866   return self;
4869 - (BOOL)isRoot {
4870   return [[self accessible] isRoot];
4873 #  pragma mark -
4875 // general
4877 - (BOOL)isAccessibilityElement {
4878   if (!mozilla::a11y::ShouldA11yBeEnabled())
4879     return [super isAccessibilityElement];
4881   return [[self accessible] isAccessibilityElement];
4884 - (id)accessibilityHitTest:(NSPoint)point {
4885   if (!mozilla::a11y::ShouldA11yBeEnabled())
4886     return [super accessibilityHitTest:point];
4888   return [[self accessible] accessibilityHitTest:point];
4891 - (id)accessibilityFocusedUIElement {
4892   if (!mozilla::a11y::ShouldA11yBeEnabled())
4893     return [super accessibilityFocusedUIElement];
4895   return [[self accessible] accessibilityFocusedUIElement];
4898 // actions
4900 - (NSArray*)accessibilityActionNames {
4901   if (!mozilla::a11y::ShouldA11yBeEnabled())
4902     return [super accessibilityActionNames];
4904   return [[self accessible] accessibilityActionNames];
4907 - (NSString*)accessibilityActionDescription:(NSString*)action {
4908   if (!mozilla::a11y::ShouldA11yBeEnabled())
4909     return [super accessibilityActionDescription:action];
4911   return [[self accessible] accessibilityActionDescription:action];
4914 - (void)accessibilityPerformAction:(NSString*)action {
4915   if (!mozilla::a11y::ShouldA11yBeEnabled())
4916     return [super accessibilityPerformAction:action];
4918   return [[self accessible] accessibilityPerformAction:action];
4921 // attributes
4923 - (NSArray*)accessibilityAttributeNames {
4924   if (!mozilla::a11y::ShouldA11yBeEnabled())
4925     return [super accessibilityAttributeNames];
4927   return [[self accessible] accessibilityAttributeNames];
4930 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
4931   if (!mozilla::a11y::ShouldA11yBeEnabled())
4932     return [super accessibilityIsAttributeSettable:attribute];
4934   return [[self accessible] accessibilityIsAttributeSettable:attribute];
4937 - (id)accessibilityAttributeValue:(NSString*)attribute {
4938   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
4940   if (!mozilla::a11y::ShouldA11yBeEnabled())
4941     return [super accessibilityAttributeValue:attribute];
4943   id<mozAccessible> accessible = [self accessible];
4945   // if we're the root (topmost) accessible, we need to return our native
4946   // AXParent as we traverse outside to the hierarchy of whoever embeds us.
4947   // thus, fall back on NSView's default implementation for this attribute.
4948   if ([attribute isEqualToString:NSAccessibilityParentAttribute] &&
4949       [accessible isRoot]) {
4950     id parentAccessible = [super accessibilityAttributeValue:attribute];
4951     return parentAccessible;
4952   }
4954   return [accessible accessibilityAttributeValue:attribute];
4956   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
4959 #endif /* ACCESSIBILITY */
4961 + (uint32_t)sUniqueKeyEventId {
4962   return sUniqueKeyEventId;
4965 + (NSMutableDictionary*)sNativeKeyEventsMap {
4966   // This dictionary is "leaked".
4967   static NSMutableDictionary* sNativeKeyEventsMap =
4968       [[NSMutableDictionary alloc] init];
4969   return sNativeKeyEventsMap;
4972 @end
4974 @implementation PixelHostingView
4976 - (id)initWithFrame:(NSRect)aRect {
4977   self = [super initWithFrame:aRect];
4979   self.wantsLayer = YES;
4980   self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
4982   return self;
4985 - (BOOL)isFlipped {
4986   return YES;
4989 - (NSView*)hitTest:(NSPoint)aPoint {
4990   return nil;
4993 - (void)drawRect:(NSRect)aRect {
4994   NS_WARNING("Unexpected call to drawRect: This view returns YES from "
4995              "wantsUpdateLayer, so "
4996              "drawRect should not be called.");
4999 - (BOOL)wantsUpdateLayer {
5000   return YES;
5003 - (void)updateLayer {
5004   [(ChildView*)[self superview] updateRootCALayer];
5007 - (BOOL)wantsBestResolutionOpenGLSurface {
5008   return nsCocoaUtils::HiDPIEnabled() ? YES : NO;
5011 @end
5013 #pragma mark -
5015 void ChildViewMouseTracker::OnDestroyView(ChildView* aView) {
5016   if (sLastMouseEventView == aView) {
5017     sLastMouseEventView = nil;
5018     [sLastMouseMoveEvent release];
5019     sLastMouseMoveEvent = nil;
5020   }
5023 void ChildViewMouseTracker::OnDestroyWindow(NSWindow* aWindow) {
5024   if (sWindowUnderMouse == aWindow) {
5025     sWindowUnderMouse = nil;
5026   }
5029 void ChildViewMouseTracker::MouseEnteredWindow(NSEvent* aEvent) {
5030   sWindowUnderMouse = [aEvent window];
5031   ReEvaluateMouseEnterState(aEvent);
5034 void ChildViewMouseTracker::MouseExitedWindow(NSEvent* aEvent) {
5035   if (sWindowUnderMouse == [aEvent window]) {
5036     sWindowUnderMouse = nil;
5037     [sLastMouseMoveEvent release];
5038     sLastMouseMoveEvent = nil;
5039     ReEvaluateMouseEnterState(aEvent);
5040   }
5043 void ChildViewMouseTracker::NativeMenuOpened() {
5044   // Send a mouse exit event now.
5045   // The menu consumes all mouse events while it's open, and we don't want to be
5046   // stuck thinking the mouse is still hovering our window after the mouse has
5047   // already moved. This could result in unintended cursor changes or tooltips.
5048   sWindowUnderMouse = nil;
5049   ReEvaluateMouseEnterState(nil);
5052 void ChildViewMouseTracker::NativeMenuClosed() {
5053   // If a window was hovered before the menu opened, re-enter that window at the
5054   // last known mouse position. After -[NSView didCloseMenu:withEvent:] is
5055   // called, any NSTrackingArea updates that were buffered while the menu was
5056   // open will be replayed.
5057   if (sLastMouseMoveEvent) {
5058     sWindowUnderMouse = sLastMouseMoveEvent.window;
5059     ReEvaluateMouseEnterState(sLastMouseMoveEvent);
5060   }
5063 void ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent,
5064                                                       ChildView* aOldView) {
5065   ChildView* oldView = aOldView ? aOldView : sLastMouseEventView;
5066   sLastMouseEventView = ViewForEvent(aEvent);
5067   if (sLastMouseEventView != oldView) {
5068     // Send enter and / or exit events.
5069     WidgetMouseEvent::ExitFrom exitFrom =
5070         [sLastMouseEventView window] == [oldView window]
5071             ? WidgetMouseEvent::ePlatformChild
5072             : WidgetMouseEvent::ePlatformTopLevel;
5073     [oldView sendMouseEnterOrExitEvent:aEvent enter:NO exitFrom:exitFrom];
5074     // After the cursor exits the window set it to a visible regular arrow
5075     // cursor.
5076     if (exitFrom == WidgetMouseEvent::ePlatformTopLevel) {
5077       [[nsCursorManager sharedInstance]
5078           setNonCustomCursor:nsIWidget::Cursor{eCursor_standard}];
5079     }
5080     [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent
5081                                              enter:YES
5082                                           exitFrom:exitFrom];
5083   }
5086 void ChildViewMouseTracker::ResendLastMouseMoveEvent() {
5087   if (sLastMouseMoveEvent) {
5088     MouseMoved(sLastMouseMoveEvent);
5089   }
5092 void ChildViewMouseTracker::MouseMoved(NSEvent* aEvent) {
5093   MouseEnteredWindow(aEvent);
5094   [sLastMouseEventView handleMouseMoved:aEvent];
5095   if (sLastMouseMoveEvent != aEvent) {
5096     [sLastMouseMoveEvent release];
5097     sLastMouseMoveEvent = [aEvent retain];
5098   }
5101 void ChildViewMouseTracker::MouseScrolled(NSEvent* aEvent) {
5102   if (!nsCocoaUtils::IsMomentumScrollEvent(aEvent)) {
5103     // Store the position so we can pin future momentum scroll events.
5104     sLastScrollEventScreenLocation =
5105         nsCocoaUtils::ScreenLocationForEvent(aEvent);
5106   }
5109 ChildView* ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent) {
5110   NSWindow* window = sWindowUnderMouse;
5111   if (!window) return nil;
5113   NSPoint windowEventLocation =
5114       nsCocoaUtils::EventLocationForWindow(aEvent, window);
5115   NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
5117   if (![view isKindOfClass:[ChildView class]]) return nil;
5119   ChildView* childView = (ChildView*)view;
5120   // If childView is being destroyed return nil.
5121   if (![childView widget]) return nil;
5122   return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
5125 BOOL ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow,
5126                                                NSEvent* aEvent,
5127                                                ChildView* aView,
5128                                                BOOL aIsClickThrough) {
5129   // Right mouse down events may get through to all windows, even to a top level
5130   // window with an open sheet.
5131   if (!aWindow || [aEvent type] == NSEventTypeRightMouseDown) return YES;
5133   id delegate = [aWindow delegate];
5134   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return YES;
5136   nsIWidget* windowWidget = [(WindowDelegate*)delegate geckoWidget];
5137   if (!windowWidget) return YES;
5139   NSWindow* topLevelWindow = nil;
5141   switch (windowWidget->GetWindowType()) {
5142     case WindowType::Popup:
5143       // If this is a context menu, it won't have a parent. So we'll always
5144       // accept mouse move events on context menus even when none of our windows
5145       // is active, which is the right thing to do.
5146       // For panels, the parent window is the XUL window that owns the panel.
5147       return WindowAcceptsEvent([aWindow parentWindow], aEvent, aView,
5148                                 aIsClickThrough);
5150     case WindowType::TopLevel:
5151     case WindowType::Dialog:
5152       if ([aWindow attachedSheet]) return NO;
5154       topLevelWindow = aWindow;
5155       break;
5156     case WindowType::Sheet: {
5157       nsIWidget* parentWidget = windowWidget->GetSheetWindowParent();
5158       if (!parentWidget) return YES;
5160       topLevelWindow = (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW);
5161       break;
5162     }
5164     default:
5165       return YES;
5166   }
5168   if (!topLevelWindow || ([topLevelWindow isMainWindow] && !aIsClickThrough) ||
5169       [aEvent type] == NSEventTypeOtherMouseDown ||
5170       (([aEvent modifierFlags] & NSEventModifierFlagCommand) != 0 &&
5171        [aEvent type] != NSEventTypeMouseMoved))
5172     return YES;
5174   // If we're here then we're dealing with a left click or mouse move on an
5175   // inactive window or something similar. Ask Gecko what to do.
5176   return [aView inactiveWindowAcceptsMouseEvent:aEvent];
5179 #pragma mark -