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