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