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