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