widget/cocoa/nsChildView.mm

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* -*- Mode: objc; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/ArrayUtils.h"
     8 #ifdef MOZ_LOGGING
     9 #define FORCE_PR_LOG
    10 #endif
    11 #include "prlog.h"
    13 #include <unistd.h>
    14 #include <math.h>
    16 #include "nsChildView.h"
    17 #include "nsCocoaWindow.h"
    19 #include "mozilla/MiscEvents.h"
    20 #include "mozilla/MouseEvents.h"
    21 #include "mozilla/TextEvents.h"
    22 #include "mozilla/TouchEvents.h"
    24 #include "nsObjCExceptions.h"
    25 #include "nsCOMPtr.h"
    26 #include "nsToolkit.h"
    27 #include "nsCRT.h"
    29 #include "nsFontMetrics.h"
    30 #include "nsIRollupListener.h"
    31 #include "nsViewManager.h"
    32 #include "nsIInterfaceRequestor.h"
    33 #include "nsIFile.h"
    34 #include "nsILocalFileMac.h"
    35 #include "nsGfxCIID.h"
    36 #include "nsIDOMSimpleGestureEvent.h"
    37 #include "nsNPAPIPluginInstance.h"
    38 #include "nsThemeConstants.h"
    39 #include "nsIWidgetListener.h"
    40 #include "nsIPresShell.h"
    42 #include "nsDragService.h"
    43 #include "nsClipboard.h"
    44 #include "nsCursorManager.h"
    45 #include "nsWindowMap.h"
    46 #include "nsCocoaFeatures.h"
    47 #include "nsCocoaUtils.h"
    48 #include "nsMenuUtilsX.h"
    49 #include "nsMenuBarX.h"
    50 #ifdef __LP64__
    51 #include "ComplexTextInputPanel.h"
    52 #endif
    53 #include "NativeKeyBindings.h"
    55 #include "gfxContext.h"
    56 #include "gfxQuartzSurface.h"
    57 #include "gfxUtils.h"
    58 #include "nsRegion.h"
    59 #include "Layers.h"
    60 #include "ClientLayerManager.h"
    61 #include "mozilla/layers/LayerManagerComposite.h"
    62 #include "GLTextureImage.h"
    63 #include "GLContextProvider.h"
    64 #include "GLContextCGL.h"
    65 #include "GLUploadHelpers.h"
    66 #include "ScopedGLHelpers.h"
    67 #include "mozilla/layers/GLManager.h"
    68 #include "mozilla/layers/CompositorOGL.h"
    69 #include "mozilla/layers/BasicCompositor.h"
    70 #include "gfxUtils.h"
    71 #include "mozilla/gfx/2D.h"
    72 #include "mozilla/gfx/BorrowedContext.h"
    73 #ifdef ACCESSIBILITY
    74 #include "nsAccessibilityService.h"
    75 #include "mozilla/a11y/Platform.h"
    76 #endif
    77 #ifdef MOZ_CRASHREPORTER
    78 #include "nsExceptionHandler.h"
    79 #endif
    81 #include "mozilla/Preferences.h"
    83 #include <dlfcn.h>
    85 #include <ApplicationServices/ApplicationServices.h>
    87 #include "GeckoProfiler.h"
    89 #include "nsIDOMWheelEvent.h"
    91 using namespace mozilla;
    92 using namespace mozilla::layers;
    93 using namespace mozilla::gl;
    94 using namespace mozilla::widget;
    96 #undef DEBUG_UPDATE
    97 #undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated
    99 // Don't put more than this many rects in the dirty region, just fluff
   100 // out to the bounding-box if there are more
   101 #define MAX_RECTS_IN_REGION 100
   103 #ifdef PR_LOGGING
   104 PRLogModuleInfo* sCocoaLog = nullptr;
   105 #endif
   107 extern "C" {
   108   CG_EXTERN void CGContextResetCTM(CGContextRef);
   109   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
   110   CG_EXTERN void CGContextResetClip(CGContextRef);
   112   typedef CFTypeRef CGSRegionObj;
   113   CGError CGSNewRegionWithRect(const CGRect *rect, CGSRegionObj *outRegion);
   114 }
   116 // defined in nsMenuBarX.mm
   117 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
   119 bool gChildViewMethodsSwizzled = false;
   121 extern nsISupportsArray *gDraggedTransferables;
   123 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
   124 NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil;
   125 NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
   126 NSPoint ChildViewMouseTracker::sLastScrollEventScreenLocation = NSZeroPoint;
   128 #ifdef INVALIDATE_DEBUGGING
   129 static void blinkRect(Rect* r);
   130 static void blinkRgn(RgnHandle rgn);
   131 #endif
   133 bool gUserCancelledDrag = false;
   135 uint32_t nsChildView::sLastInputEventCount = 0;
   137 @interface ChildView(Private)
   139 // sets up our view, attaching it to its owning gecko view
   140 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
   141 - (void)forceRefreshOpenGL;
   143 // set up a gecko mouse event based on a cocoa mouse event
   144 - (void) convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
   145                         toGeckoEvent:(WidgetWheelEvent*)outWheelEvent;
   146 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent
   147                    toGeckoEvent:(WidgetInputEvent*)outGeckoEvent;
   149 - (NSMenu*)contextMenu;
   151 - (void)setIsPluginView:(BOOL)aIsPlugin;
   152 - (void)setPluginEventModel:(NPEventModel)eventModel;
   153 - (void)setPluginDrawingModel:(NPDrawingModel)drawingModel;
   154 - (NPDrawingModel)pluginDrawingModel;
   156 - (BOOL)isRectObscuredBySubview:(NSRect)inRect;
   158 - (void)processPendingRedraws;
   160 - (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext;
   161 - (nsIntRegion)nativeDirtyRegionWithBoundingRect:(NSRect)aRect;
   162 - (BOOL)isUsingMainThreadOpenGL;
   163 - (BOOL)isUsingOpenGL;
   164 - (void)drawUsingOpenGL;
   165 - (void)drawUsingOpenGLCallback;
   167 - (BOOL)hasRoundedBottomCorners;
   168 - (CGFloat)cornerRadius;
   169 - (void)clearCorners;
   171 // Overlay drawing functions for traditional CGContext drawing
   172 - (void)drawTitleString;
   173 - (void)drawTitlebarHighlight;
   174 - (void)maskTopCornersInContext:(CGContextRef)aContext;
   176 // Called using performSelector:withObject:afterDelay:0 to release
   177 // aWidgetArray (and its contents) the next time through the run loop.
   178 - (void)releaseWidgets:(NSArray*)aWidgetArray;
   180 #if USE_CLICK_HOLD_CONTEXTMENU
   181  // called on a timer two seconds after a mouse down to see if we should display
   182  // a context menu (click-hold)
   183 - (void)clickHoldCallback:(id)inEvent;
   184 #endif
   186 #ifdef ACCESSIBILITY
   187 - (id<mozAccessible>)accessible;
   188 #endif
   190 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
   192 @end
   194 @interface NSView(NSThemeFrameCornerRadius)
   195 - (float)roundedCornerRadius;
   196 @end
   198 // Starting with 10.7 the bottom corners of all windows are rounded.
   199 // Unfortunately, the standard rounding that OS X applies to OpenGL views
   200 // does not use anti-aliasing and looks very crude. Since we want a smooth,
   201 // anti-aliased curve, we'll draw it ourselves.
   202 // Additionally, we need to turn off the OS-supplied rounding because it
   203 // eats into our corner's curve. We do that by overriding an NSSurface method.
   204 @interface NSSurface @end
   206 @implementation NSSurface(DontCutOffCorners)
   207 - (CGSRegionObj)_createRoundedBottomRegionForRect:(CGRect)rect
   208 {
   209   // Create a normal rect region without rounded bottom corners.
   210   CGSRegionObj region;
   211   CGSNewRegionWithRect(&rect, &region);
   212   return region;
   213 }
   214 @end
   216 #pragma mark -
   218 /* Convenience routine to go from a Gecko rect to Cocoa NSRect.
   219  *
   220  * Gecko rects (nsRect) contain an origin (x,y) in a coordinate
   221  * system with (0,0) in the top-left of the screen. Cocoa rects
   222  * (NSRect) contain an origin (x,y) in a coordinate system with
   223  * (0,0) in the bottom-left of the screen. Both nsRect and NSRect
   224  * contain width/height info, with no difference in their use.
   225  * If a Cocoa rect is from a flipped view, there is no need to
   226  * convert coordinate systems.
   227  */
   228 #ifndef __LP64__
   229 static inline void
   230 ConvertGeckoRectToMacRect(const nsIntRect& aRect, Rect& outMacRect)
   231 {
   232   outMacRect.left = aRect.x;
   233   outMacRect.top = aRect.y;
   234   outMacRect.right = aRect.x + aRect.width;
   235   outMacRect.bottom = aRect.y + aRect.height;
   236 }
   237 #endif
   239 // Flips a screen coordinate from a point in the cocoa coordinate system (bottom-left rect) to a point
   240 // that is a "flipped" cocoa coordinate system (starts in the top-left).
   241 static inline void
   242 FlipCocoaScreenCoordinate(NSPoint &inPoint)
   243 {
   244   inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
   245 }
   247 void EnsureLogInitialized()
   248 {
   249 #ifdef PR_LOGGING
   250   if (!sCocoaLog) {
   251     sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
   252   }
   253 #endif // PR_LOGGING
   254 }
   256 namespace {
   258 // Manages a texture which can resize dynamically, binds to the
   259 // LOCAL_GL_TEXTURE_RECTANGLE_ARB texture target and is automatically backed
   260 // by a power-of-two size GL texture. The latter two features are used for
   261 // compatibility with older Mac hardware which we block GL layers on.
   262 // RectTextureImages are used both for accelerated GL layers drawing and for
   263 // OMTC BasicLayers drawing.
   264 class RectTextureImage {
   265 public:
   266   RectTextureImage(GLContext* aGLContext)
   267    : mGLContext(aGLContext)
   268    , mTexture(0)
   269    , mInUpdate(false)
   270   {}
   272   virtual ~RectTextureImage();
   274   TemporaryRef<gfx::DrawTarget>
   275     BeginUpdate(const nsIntSize& aNewSize,
   276                 const nsIntRegion& aDirtyRegion = nsIntRegion());
   277   void EndUpdate(bool aKeepSurface = false);
   279   void UpdateIfNeeded(const nsIntSize& aNewSize,
   280                       const nsIntRegion& aDirtyRegion,
   281                       void (^aCallback)(gfx::DrawTarget*, const nsIntRegion&))
   282   {
   283     RefPtr<gfx::DrawTarget> drawTarget = BeginUpdate(aNewSize, aDirtyRegion);
   284     if (drawTarget) {
   285       aCallback(drawTarget, GetUpdateRegion());
   286       EndUpdate();
   287     }
   288   }
   290   void UpdateFromCGContext(const nsIntSize& aNewSize,
   291                            const nsIntRegion& aDirtyRegion,
   292                            CGContextRef aCGContext);
   294   void UpdateFromDrawTarget(const nsIntSize& aNewSize,
   295                             const nsIntRegion& aDirtyRegion,
   296                             gfx::DrawTarget* aFromDrawTarget);
   298   nsIntRegion GetUpdateRegion() {
   299     MOZ_ASSERT(mInUpdate, "update region only valid during update");
   300     return mUpdateRegion;
   301   }
   303   void Draw(mozilla::layers::GLManager* aManager,
   304             const nsIntPoint& aLocation,
   305             const gfx3DMatrix& aTransform = gfx3DMatrix());
   307   static nsIntSize TextureSizeForSize(const nsIntSize& aSize);
   309 protected:
   311   RefPtr<gfx::DrawTarget> mUpdateDrawTarget;
   312   GLContext* mGLContext;
   313   nsIntRegion mUpdateRegion;
   314   nsIntSize mUsedSize;
   315   nsIntSize mBufferSize;
   316   nsIntSize mTextureSize;
   317   GLuint mTexture;
   318   bool mInUpdate;
   319 };
   321 // Used for OpenGL drawing from the compositor thread for OMTC BasicLayers.
   322 // We need to use OpenGL for this because there seems to be no other robust
   323 // way of drawing from a secondary thread without locking, which would cause
   324 // deadlocks in our setup. See bug 882523.
   325 class GLPresenter : public GLManager
   326 {
   327 public:
   328   static GLPresenter* CreateForWindow(nsIWidget* aWindow)
   329   {
   330     nsRefPtr<GLContext> context = gl::GLContextProvider::CreateForWindow(aWindow);
   331     return context ? new GLPresenter(context) : nullptr;
   332   }
   334   GLPresenter(GLContext* aContext);
   335   virtual ~GLPresenter();
   337   virtual GLContext* gl() const MOZ_OVERRIDE { return mGLContext; }
   338   virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) MOZ_OVERRIDE
   339   {
   340     MOZ_ASSERT(aTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
   341     MOZ_ASSERT(aFormat == gfx::SurfaceFormat::R8G8B8A8);
   342     return mRGBARectProgram;
   343   }
   344   virtual const gfx::Matrix4x4& GetProjMatrix() const MOZ_OVERRIDE
   345   {
   346     return mProjMatrix;
   347   }
   348   virtual void BindAndDrawQuad(ShaderProgramOGL *aProg) MOZ_OVERRIDE;
   350   void BeginFrame(nsIntSize aRenderSize);
   351   void EndFrame();
   353   NSOpenGLContext* GetNSOpenGLContext()
   354   {
   355     return GLContextCGL::Cast(mGLContext)->GetNSOpenGLContext();
   356   }
   358 protected:
   359   nsRefPtr<mozilla::gl::GLContext> mGLContext;
   360   nsAutoPtr<mozilla::layers::ShaderProgramOGL> mRGBARectProgram;
   361   gfx::Matrix4x4 mProjMatrix;
   362   GLuint mQuadVBO;
   363 };
   365 } // unnamed namespace
   367 #pragma mark -
   369 nsChildView::nsChildView() : nsBaseWidget()
   370 , mView(nullptr)
   371 , mParentView(nullptr)
   372 , mParentWidget(nullptr)
   373 , mViewTearDownLock("ChildViewTearDown")
   374 , mEffectsLock("WidgetEffects")
   375 , mShowsResizeIndicator(false)
   376 , mHasRoundedBottomCorners(false)
   377 , mIsCoveringTitlebar(false)
   378 , mIsFullscreen(false)
   379 , mTitlebarCGContext(nullptr)
   380 , mBackingScaleFactor(0.0)
   381 , mVisible(false)
   382 , mDrawing(false)
   383 , mPluginDrawing(false)
   384 , mIsDispatchPaint(false)
   385 , mPluginInstanceOwner(nullptr)
   386 {
   387   EnsureLogInitialized();
   389   memset(&mPluginCGContext, 0, sizeof(mPluginCGContext));
   390 }
   392 nsChildView::~nsChildView()
   393 {
   394   ReleaseTitlebarCGContext();
   396   // Notify the children that we're gone.  childView->ResetParent() can change
   397   // our list of children while it's being iterated, so the way we iterate the
   398   // list must allow for this.
   399   for (nsIWidget* kid = mLastChild; kid;) {
   400     nsChildView* childView = static_cast<nsChildView*>(kid);
   401     kid = kid->GetPrevSibling();
   402     childView->ResetParent();
   403   }
   405   NS_WARN_IF_FALSE(mOnDestroyCalled, "nsChildView object destroyed without calling Destroy()");
   407   DestroyCompositor();
   409   // An nsChildView object that was in use can be destroyed without Destroy()
   410   // ever being called on it.  So we also need to do a quick, safe cleanup
   411   // here (it's too late to just call Destroy(), which can cause crashes).
   412   // It's particularly important to make sure widgetDestroyed is called on our
   413   // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
   414   // mGeckoChild are used throughout the ChildView class to tell if it's safe
   415   // to use a ChildView object.
   416   [mView widgetDestroyed]; // Safe if mView is nil.
   417   mParentWidget = nil;
   418   TearDownView(); // Safe if called twice.
   419 }
   421 void
   422 nsChildView::ReleaseTitlebarCGContext()
   423 {
   424   if (mTitlebarCGContext) {
   425     CGContextRelease(mTitlebarCGContext);
   426     mTitlebarCGContext = nullptr;
   427   }
   428 }
   430 NS_IMPL_ISUPPORTS_INHERITED(nsChildView, nsBaseWidget, nsIPluginWidget)
   432 nsresult nsChildView::Create(nsIWidget *aParent,
   433                              nsNativeWidget aNativeParent,
   434                              const nsIntRect &aRect,
   435                              nsDeviceContext *aContext,
   436                              nsWidgetInitData *aInitData)
   437 {
   438   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   440   // Because the hidden window is created outside of an event loop,
   441   // we need to provide an autorelease pool to avoid leaking cocoa objects
   442   // (see bug 559075).
   443   nsAutoreleasePool localPool;
   445   // See NSView (MethodSwizzling) below.
   446   if (!gChildViewMethodsSwizzled) {
   447     nsToolkit::SwizzleMethods([NSView class], @selector(mouseDownCanMoveWindow),
   448                               @selector(nsChildView_NSView_mouseDownCanMoveWindow));
   449 #ifdef __LP64__
   450     if (nsCocoaFeatures::OnLionOrLater()) {
   451       nsToolkit::SwizzleMethods([NSEvent class], @selector(addLocalMonitorForEventsMatchingMask:handler:),
   452                                 @selector(nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:handler:),
   453                                 true);
   454       nsToolkit::SwizzleMethods([NSEvent class], @selector(removeMonitor:),
   455                                 @selector(nsChildView_NSEvent_removeMonitor:), true);
   456     }
   457 #else
   458     TextInputHandler::SwizzleMethods();
   459 #endif
   460     gChildViewMethodsSwizzled = true;
   461   }
   463   mBounds = aRect;
   465   // Ensure that the toolkit is created.
   466   nsToolkit::GetToolkit();
   468   BaseCreate(aParent, aRect, aContext, aInitData);
   470   // inherit things from the parent view and create our parallel
   471   // NSView in the Cocoa display system
   472   mParentView = nil;
   473   if (aParent) {
   474     // inherit the top-level window. NS_NATIVE_WIDGET is always a NSView
   475     // regardless of if we're asking a window or a view (for compatibility
   476     // with windows).
   477     mParentView = (NSView<mozView>*)aParent->GetNativeData(NS_NATIVE_WIDGET);
   478     mParentWidget = aParent;
   479   } else {
   480     // This is the normal case. When we're the root widget of the view hiararchy,
   481     // aNativeParent will be the contentView of our window, since that's what
   482     // nsCocoaWindow returns when asked for an NS_NATIVE_VIEW.
   483     mParentView = reinterpret_cast<NSView<mozView>*>(aNativeParent);
   484   }
   486   // create our parallel NSView and hook it up to our parent. Recall
   487   // that NS_NATIVE_WIDGET is the NSView.
   488   CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mParentView);
   489   NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(mBounds, scaleFactor);
   490   mView = [(NSView<mozView>*)CreateCocoaView(r) retain];
   491   if (!mView) {
   492     return NS_ERROR_FAILURE;
   493   }
   495   [(ChildView*)mView setIsPluginView:(mWindowType == eWindowType_plugin)];
   497   // If this view was created in a Gecko view hierarchy, the initial state
   498   // is hidden.  If the view is attached only to a native NSView but has
   499   // no Gecko parent (as in embedding), the initial state is visible.
   500   if (mParentWidget)
   501     [mView setHidden:YES];
   502   else
   503     mVisible = true;
   505   // Hook it up in the NSView hierarchy.
   506   if (mParentView) {
   507     [mParentView addSubview:mView];
   508   }
   510   // if this is a ChildView, make sure that our per-window data
   511   // is set up
   512   if ([mView isKindOfClass:[ChildView class]])
   513     [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]];
   515   NS_ASSERTION(!mTextInputHandler, "mTextInputHandler has already existed");
   516   mTextInputHandler = new TextInputHandler(this, mView);
   518   return NS_OK;
   520   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   521 }
   523 // Creates the appropriate child view. Override to create something other than
   524 // our |ChildView| object. Autoreleases, so caller must retain.
   525 NSView*
   526 nsChildView::CreateCocoaView(NSRect inFrame)
   527 {
   528   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
   530   return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this] autorelease];
   532   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
   533 }
   535 void nsChildView::TearDownView()
   536 {
   537   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   539   if (!mView)
   540     return;
   542   NSWindow* win = [mView window];
   543   NSResponder* responder = [win firstResponder];
   545   // We're being unhooked from the view hierarchy, don't leave our view
   546   // or a child view as the window first responder.
   547   if (responder && [responder isKindOfClass:[NSView class]] &&
   548       [(NSView*)responder isDescendantOf:mView]) {
   549     [win makeFirstResponder:[mView superview]];
   550   }
   552   // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
   553   // win has retained mView, and will detach it from the view hierarchy and
   554   // release it when necessary (when win is itself destroyed (in a call to
   555   // [win dealloc])).  So all we need to do here is call [mView release] (to
   556   // match the call to [mView retain] in nsChildView::StandardCreate()).
   557   // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
   558   // mView to be released again and dealloced, while remaining win's
   559   // contentView.  So if we do that here, win will (for a short while) have
   560   // an invalid contentView (for the consequences see bmo bugs 381087 and
   561   // 374260).
   562   if ([mView isEqual:[win contentView]]) {
   563     [mView release];
   564   } else {
   565     // Stop NSView hierarchy being changed during [ChildView drawRect:]
   566     [mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
   567   }
   568   mView = nil;
   570   NS_OBJC_END_TRY_ABORT_BLOCK;
   571 }
   573 nsCocoaWindow*
   574 nsChildView::GetXULWindowWidget()
   575 {
   576   id windowDelegate = [[mView window] delegate];
   577   if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
   578     return [(WindowDelegate *)windowDelegate geckoWidget];
   579   }
   580   return nullptr;
   581 }
   583 NS_IMETHODIMP nsChildView::Destroy()
   584 {
   585   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   587   // Make sure that no composition is in progress while disconnecting
   588   // ourselves from the view.
   589   MutexAutoLock lock(mViewTearDownLock);
   591   if (mOnDestroyCalled)
   592     return NS_OK;
   593   mOnDestroyCalled = true;
   595   [mView widgetDestroyed];
   597   nsBaseWidget::Destroy();
   599   NotifyWindowDestroyed();
   600   mParentWidget = nil;
   602   TearDownView();
   604   nsBaseWidget::OnDestroy();
   606   return NS_OK;
   608   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   609 }
   611 #pragma mark -
   613 #if 0
   614 static void PrintViewHierarchy(NSView *view)
   615 {
   616   while (view) {
   617     NSLog(@"  view is %x, frame %@", view, NSStringFromRect([view frame]));
   618     view = [view superview];
   619   }
   620 }
   621 #endif
   623 // Return native data according to aDataType
   624 void* nsChildView::GetNativeData(uint32_t aDataType)
   625 {
   626   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
   628   void* retVal = nullptr;
   630   switch (aDataType)
   631   {
   632     case NS_NATIVE_WIDGET:
   633     case NS_NATIVE_DISPLAY:
   634       retVal = (void*)mView;
   635       break;
   637     case NS_NATIVE_WINDOW:
   638       retVal = [mView window];
   639       break;
   641     case NS_NATIVE_GRAPHIC:
   642       NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
   643       retVal = nullptr;
   644       break;
   646     case NS_NATIVE_OFFSETX:
   647       retVal = 0;
   648       break;
   650     case NS_NATIVE_OFFSETY:
   651       retVal = 0;
   652       break;
   654     case NS_NATIVE_PLUGIN_PORT:
   655     case NS_NATIVE_PLUGIN_PORT_CG:
   656     {
   657       // The NP_CGContext pointer should always be NULL in the Cocoa event model.
   658       if ([(ChildView*)mView pluginEventModel] == NPEventModelCocoa)
   659         return nullptr;
   661       UpdatePluginPort();
   662       retVal = (void*)&mPluginCGContext;
   663       break;
   664     }
   665   }
   667   return retVal;
   669   NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
   670 }
   672 #pragma mark -
   674 nsTransparencyMode nsChildView::GetTransparencyMode()
   675 {
   676   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   678   nsCocoaWindow* windowWidget = GetXULWindowWidget();
   679   return windowWidget ? windowWidget->GetTransparencyMode() : eTransparencyOpaque;
   681   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque);
   682 }
   684 // This is called by nsContainerFrame on the root widget for all window types
   685 // except popup windows (when nsCocoaWindow::SetTransparencyMode is used instead).
   686 void nsChildView::SetTransparencyMode(nsTransparencyMode aMode)
   687 {
   688   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   690   nsCocoaWindow* windowWidget = GetXULWindowWidget();
   691   if (windowWidget) {
   692     windowWidget->SetTransparencyMode(aMode);
   693   }
   695   NS_OBJC_END_TRY_ABORT_BLOCK;
   696 }
   698 bool nsChildView::IsVisible() const
   699 {
   700   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   702   if (!mVisible) {
   703     return mVisible;
   704   }
   706   // mVisible does not accurately reflect the state of a hidden tabbed view
   707   // so verify that the view has a window as well
   708   // then check native widget hierarchy visibility
   709   return ([mView window] != nil) && !NSIsEmptyRect([mView visibleRect]);
   711   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
   712 }
   714 void nsChildView::HidePlugin()
   715 {
   716   NS_ASSERTION(mWindowType == eWindowType_plugin,
   717                "HidePlugin called on non-plugin view");
   718 }
   720 void nsChildView::UpdatePluginPort()
   721 {
   722   NS_ASSERTION(mWindowType == eWindowType_plugin,
   723                "UpdatePluginPort called on non-plugin view");
   725   // [NSGraphicsContext currentContext] is supposed to "return the
   726   // current graphics context of the current thread."  But sometimes
   727   // (when called while mView isn't focused for drawing) it returns a
   728   // graphics context for the wrong window.  [window graphicsContext]
   729   // (which "provides the graphics context associated with the window
   730   // for the current thread") seems always to return the "right"
   731   // graphics context.  See bug 500130.
   732   mPluginCGContext.context = NULL;
   733   mPluginCGContext.window = NULL;
   734 }
   736 static void HideChildPluginViews(NSView* aView)
   737 {
   738   NSArray* subviews = [aView subviews];
   740   for (unsigned int i = 0; i < [subviews count]; ++i) {
   741     NSView* view = [subviews objectAtIndex: i];
   743     if (![view isKindOfClass:[ChildView class]])
   744       continue;
   746     ChildView* childview = static_cast<ChildView*>(view);
   747     if ([childview isPluginView]) {
   748       nsChildView* widget = static_cast<nsChildView*>([childview widget]);
   749       if (widget) {
   750         widget->HidePlugin();
   751       }
   752     } else {
   753       HideChildPluginViews(view);
   754     }
   755   }
   756 }
   758 // Some NSView methods (e.g. setFrame and setHidden) invalidate the view's
   759 // bounds in our window. However, we don't want these invalidations because
   760 // they are unnecessary and because they actually slow us down since we
   761 // block on the compositor inside drawRect.
   762 // When we actually need something invalidated, there will be an explicit call
   763 // to Invalidate from Gecko, so turning these automatic invalidations off
   764 // won't hurt us in the non-OMTC case.
   765 // The invalidations inside these NSView methods happen via a call to the
   766 // private method -[NSWindow _setNeedsDisplayInRect:]. Our BaseWindow
   767 // implementation of that method is augmented to let us ignore those calls
   768 // using -[BaseWindow disable/enableSetNeedsDisplay].
   769 static void
   770 ManipulateViewWithoutNeedingDisplay(NSView* aView, void (^aCallback)())
   771 {
   772   BaseWindow* win = nil;
   773   if ([[aView window] isKindOfClass:[BaseWindow class]]) {
   774     win = (BaseWindow*)[aView window];
   775   }
   776   [win disableSetNeedsDisplay];
   777   aCallback();
   778   [win enableSetNeedsDisplay];
   779 }
   781 // Hide or show this component
   782 NS_IMETHODIMP nsChildView::Show(bool aState)
   783 {
   784   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   786   if (aState != mVisible) {
   787     // Provide an autorelease pool because this gets called during startup
   788     // on the "hidden window", resulting in cocoa object leakage if there's
   789     // no pool in place.
   790     nsAutoreleasePool localPool;
   792     ManipulateViewWithoutNeedingDisplay(mView, ^{
   793       [mView setHidden:!aState];
   794     });
   796     mVisible = aState;
   797     if (!mVisible && IsPluginView())
   798       HidePlugin();
   799   }
   800   return NS_OK;
   802   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   803 }
   805 // Change the parent of this widget
   806 NS_IMETHODIMP
   807 nsChildView::SetParent(nsIWidget* aNewParent)
   808 {
   809   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   811   if (mOnDestroyCalled)
   812     return NS_OK;
   814   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
   816   if (mParentWidget) {
   817     mParentWidget->RemoveChild(this);
   818   }
   820   if (aNewParent) {
   821     ReparentNativeWidget(aNewParent);
   822   } else {
   823     [mView removeFromSuperview];
   824     mParentView = nil;
   825   }
   827   mParentWidget = aNewParent;
   829   if (mParentWidget) {
   830     mParentWidget->AddChild(this);
   831   }
   833   return NS_OK;
   835   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   836 }
   838 NS_IMETHODIMP
   839 nsChildView::ReparentNativeWidget(nsIWidget* aNewParent)
   840 {
   841   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   843   NS_PRECONDITION(aNewParent, "");
   845   if (mOnDestroyCalled)
   846     return NS_OK;
   848   NSView<mozView>* newParentView =
   849    (NSView<mozView>*)aNewParent->GetNativeData(NS_NATIVE_WIDGET);
   850   NS_ENSURE_TRUE(newParentView, NS_ERROR_FAILURE);
   852   // we hold a ref to mView, so this is safe
   853   [mView removeFromSuperview];
   854   mParentView = newParentView;
   855   [mParentView addSubview:mView];
   856   return NS_OK;
   858   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   859 }
   861 void nsChildView::ResetParent()
   862 {
   863   if (!mOnDestroyCalled) {
   864     if (mParentWidget)
   865       mParentWidget->RemoveChild(this);
   866     if (mView)
   867       [mView removeFromSuperview];
   868   }
   869   mParentWidget = nullptr;
   870 }
   872 nsIWidget*
   873 nsChildView::GetParent()
   874 {
   875   return mParentWidget;
   876 }
   878 float
   879 nsChildView::GetDPI()
   880 {
   881   NSWindow* window = [mView window];
   882   if (window && [window isKindOfClass:[BaseWindow class]]) {
   883     return [(BaseWindow*)window getDPI];
   884   }
   886   return 96.0;
   887 }
   889 NS_IMETHODIMP nsChildView::Enable(bool aState)
   890 {
   891   return NS_OK;
   892 }
   894 bool nsChildView::IsEnabled() const
   895 {
   896   return true;
   897 }
   899 NS_IMETHODIMP nsChildView::SetFocus(bool aRaise)
   900 {
   901   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   903   NSWindow* window = [mView window];
   904   if (window)
   905     [window makeFirstResponder:mView];
   906   return NS_OK;
   908   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   909 }
   911 // Override to set the cursor on the mac
   912 NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
   913 {
   914   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   916   if ([mView isDragInProgress])
   917     return NS_OK; // Don't change the cursor during dragging.
   919   nsBaseWidget::SetCursor(aCursor);
   920   return [[nsCursorManager sharedInstance] setCursor:aCursor];
   922   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   923 }
   925 // implement to fix "hidden virtual function" warning
   926 NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
   927                                       uint32_t aHotspotX, uint32_t aHotspotY)
   928 {
   929   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   931   nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
   932   return [[nsCursorManager sharedInstance] setCursorWithImage:aCursor hotSpotX:aHotspotX hotSpotY:aHotspotY scaleFactor:BackingScaleFactor()];
   934   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   935 }
   937 #pragma mark -
   939 // Get this component dimension
   940 NS_IMETHODIMP nsChildView::GetBounds(nsIntRect &aRect)
   941 {
   942   if (!mView) {
   943     aRect = mBounds;
   944   } else {
   945     aRect = CocoaPointsToDevPixels([mView frame]);
   946   }
   947   return NS_OK;
   948 }
   950 NS_IMETHODIMP nsChildView::GetClientBounds(nsIntRect &aRect)
   951 {
   952   GetBounds(aRect);
   953   if (!mParentWidget) {
   954     // For top level widgets we want the position on screen, not the position
   955     // of this view inside the window.
   956     MOZ_ASSERT(mWindowType != eWindowType_plugin, "plugin widgets should have parents");
   957     aRect.MoveTo(WidgetToScreenOffset());
   958   }
   959   return NS_OK;
   960 }
   962 NS_IMETHODIMP nsChildView::GetScreenBounds(nsIntRect &aRect)
   963 {
   964   GetBounds(aRect);
   965   aRect.MoveTo(WidgetToScreenOffset());
   966   return NS_OK;
   967 }
   969 double
   970 nsChildView::GetDefaultScaleInternal()
   971 {
   972   return BackingScaleFactor();
   973 }
   975 CGFloat
   976 nsChildView::BackingScaleFactor()
   977 {
   978   if (mBackingScaleFactor > 0.0) {
   979     return mBackingScaleFactor;
   980   }
   981   if (!mView) {
   982     return 1.0;
   983   }
   984   mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mView);
   985   return mBackingScaleFactor;
   986 }
   988 void
   989 nsChildView::BackingScaleFactorChanged()
   990 {
   991   CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mView);
   993   // ignore notification if it hasn't really changed (or maybe we have
   994   // disabled HiDPI mode via prefs)
   995   if (mBackingScaleFactor == newScale) {
   996     return;
   997   }
   999   mBackingScaleFactor = newScale;
  1001   if (mWidgetListener && !mWidgetListener->GetXULWindow()) {
  1002     nsIPresShell* presShell = mWidgetListener->GetPresShell();
  1003     if (presShell) {
  1004       presShell->BackingScaleFactorChanged();
  1008   if (IsPluginView()) {
  1009     nsEventStatus status = nsEventStatus_eIgnore;
  1010     WidgetGUIEvent guiEvent(true, NS_PLUGIN_RESOLUTION_CHANGED, this);
  1011     guiEvent.time = PR_IntervalNow();
  1012     DispatchEvent(&guiEvent, status);
  1016 int32_t
  1017 nsChildView::RoundsWidgetCoordinatesTo()
  1019   if (BackingScaleFactor() == 2.0) {
  1020     return 2;
  1022   return 1;
  1025 NS_IMETHODIMP nsChildView::ConstrainPosition(bool aAllowSlop,
  1026                                              int32_t *aX, int32_t *aY)
  1028   return NS_OK;
  1031 // Move this component, aX and aY are in the parent widget coordinate system
  1032 NS_IMETHODIMP nsChildView::Move(double aX, double aY)
  1034   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1036   int32_t x = NSToIntRound(aX);
  1037   int32_t y = NSToIntRound(aY);
  1039   if (!mView || (mBounds.x == x && mBounds.y == y))
  1040     return NS_OK;
  1042   mBounds.x = x;
  1043   mBounds.y = y;
  1045   ManipulateViewWithoutNeedingDisplay(mView, ^{
  1046     [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
  1047   });
  1049   NotifyRollupGeometryChange();
  1050   ReportMoveEvent();
  1052   return NS_OK;
  1054   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1057 NS_IMETHODIMP nsChildView::Resize(double aWidth, double aHeight, bool aRepaint)
  1059   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1061   int32_t width = NSToIntRound(aWidth);
  1062   int32_t height = NSToIntRound(aHeight);
  1064   if (!mView || (mBounds.width == width && mBounds.height == height))
  1065     return NS_OK;
  1067   mBounds.width  = width;
  1068   mBounds.height = height;
  1070   ManipulateViewWithoutNeedingDisplay(mView, ^{
  1071     [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
  1072   });
  1074   if (mVisible && aRepaint)
  1075     [mView setNeedsDisplay:YES];
  1077   NotifyRollupGeometryChange();
  1078   ReportSizeEvent();
  1080   return NS_OK;
  1082   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1085 NS_IMETHODIMP nsChildView::Resize(double aX, double aY,
  1086                                   double aWidth, double aHeight, bool aRepaint)
  1088   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1090   int32_t x = NSToIntRound(aX);
  1091   int32_t y = NSToIntRound(aY);
  1092   int32_t width = NSToIntRound(aWidth);
  1093   int32_t height = NSToIntRound(aHeight);
  1095   BOOL isMoving = (mBounds.x != x || mBounds.y != y);
  1096   BOOL isResizing = (mBounds.width != width || mBounds.height != height);
  1097   if (!mView || (!isMoving && !isResizing))
  1098     return NS_OK;
  1100   if (isMoving) {
  1101     mBounds.x = x;
  1102     mBounds.y = y;
  1104   if (isResizing) {
  1105     mBounds.width  = width;
  1106     mBounds.height = height;
  1109   ManipulateViewWithoutNeedingDisplay(mView, ^{
  1110     [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
  1111   });
  1113   if (mVisible && aRepaint)
  1114     [mView setNeedsDisplay:YES];
  1116   NotifyRollupGeometryChange();
  1117   if (isMoving) {
  1118     ReportMoveEvent();
  1119     if (mOnDestroyCalled)
  1120       return NS_OK;
  1122   if (isResizing)
  1123     ReportSizeEvent();
  1125   return NS_OK;
  1127   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1130 static const int32_t resizeIndicatorWidth = 15;
  1131 static const int32_t resizeIndicatorHeight = 15;
  1132 bool nsChildView::ShowsResizeIndicator(nsIntRect* aResizerRect)
  1134   NSView *topLevelView = mView, *superView = nil;
  1135   while ((superView = [topLevelView superview]))
  1136     topLevelView = superView;
  1138   if (![[topLevelView window] showsResizeIndicator] ||
  1139       !([[topLevelView window] styleMask] & NSResizableWindowMask))
  1140     return false;
  1142   if (aResizerRect) {
  1143     NSSize bounds = [topLevelView bounds].size;
  1144     NSPoint corner = NSMakePoint(bounds.width, [topLevelView isFlipped] ? bounds.height : 0);
  1145     corner = [topLevelView convertPoint:corner toView:mView];
  1146     aResizerRect->SetRect(NSToIntRound(corner.x) - resizeIndicatorWidth,
  1147                           NSToIntRound(corner.y) - resizeIndicatorHeight,
  1148                           resizeIndicatorWidth, resizeIndicatorHeight);
  1150   return true;
  1153 // In QuickDraw mode the coordinate system used here should be that of the
  1154 // browser window's content region (defined as everything but the 22-pixel
  1155 // high titlebar).  But in CoreGraphics mode the coordinate system should be
  1156 // that of the browser window as a whole (including its titlebar).  Both
  1157 // coordinate systems have a top-left origin.  See bmo bug 474491.
  1158 //
  1159 // There's a bug in this method's code -- it currently uses the QuickDraw
  1160 // coordinate system for both the QuickDraw and CoreGraphics drawing modes.
  1161 // This bug is fixed by the patch for bug 474491.  But the Flash plugin (both
  1162 // version 10.0.12.36 from Adobe and version 9.0 r151 from Apple) has Mozilla-
  1163 // specific code to work around this bug, which breaks when we fix it (see bmo
  1164 // bug 477077).  So we'll need to coordinate releasing a fix for this bug with
  1165 // Adobe and other major plugin vendors that support the CoreGraphics mode.
  1166 //
  1167 // outClipRect and outOrigin are in display pixels, not device pixels.
  1168 NS_IMETHODIMP nsChildView::GetPluginClipRect(nsIntRect& outClipRect, nsIntPoint& outOrigin, bool& outWidgetVisible)
  1170   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1172   NS_ASSERTION(mWindowType == eWindowType_plugin,
  1173                "GetPluginClipRect must only be called on a plugin widget");
  1174   if (mWindowType != eWindowType_plugin) return NS_ERROR_FAILURE;
  1176   NSWindow* window = [mView window];
  1177   if (!window) return NS_ERROR_FAILURE;
  1179   NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
  1180   NSRect frame = [[window contentView] frame];
  1181   viewOrigin.y = frame.size.height - viewOrigin.y;
  1183   // set up the clipping region for plugins.
  1184   NSRect visibleBounds = [mView visibleRect];
  1185   NSPoint clipOrigin   = [mView convertPoint:visibleBounds.origin toView:nil];
  1187   // Convert from cocoa to QuickDraw coordinates
  1188   clipOrigin.y = frame.size.height - clipOrigin.y;
  1190   outClipRect.x = NSToIntRound(clipOrigin.x);
  1191   outClipRect.y = NSToIntRound(clipOrigin.y);
  1193   // need to convert view's origin to window coordinates.
  1194   // then, encode as "SetOrigin" ready values.
  1195   outOrigin.x = -NSToIntRound(viewOrigin.x);
  1196   outOrigin.y = -NSToIntRound(viewOrigin.y);
  1198   if (IsVisible() && [mView window] != nil) {
  1199     outClipRect.width  = NSToIntRound(visibleBounds.origin.x + visibleBounds.size.width) - NSToIntRound(visibleBounds.origin.x);
  1200     outClipRect.height = NSToIntRound(visibleBounds.origin.y + visibleBounds.size.height) - NSToIntRound(visibleBounds.origin.y);
  1202     if (mClipRects) {
  1203       nsIntRect clipBounds;
  1204       for (uint32_t i = 0; i < mClipRectCount; ++i) {
  1205         NSRect cocoaPoints = DevPixelsToCocoaPoints(mClipRects[i]);
  1206         clipBounds.UnionRect(clipBounds, nsIntRect(NSToIntRound(cocoaPoints.origin.x),
  1207                                                    NSToIntRound(cocoaPoints.origin.y),
  1208                                                    NSToIntRound(cocoaPoints.size.width),
  1209                                                    NSToIntRound(cocoaPoints.size.height)));
  1211       outClipRect.IntersectRect(outClipRect, clipBounds - outOrigin);
  1214     // XXXroc should this be !outClipRect.IsEmpty()?
  1215     outWidgetVisible = true;
  1217   else {
  1218     outClipRect.width = 0;
  1219     outClipRect.height = 0;
  1220     outWidgetVisible = false;
  1223   return NS_OK;
  1225   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1228 NS_IMETHODIMP nsChildView::StartDrawPlugin()
  1230   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1232   NS_ASSERTION(mWindowType == eWindowType_plugin,
  1233                "StartDrawPlugin must only be called on a plugin widget");
  1234   if (mWindowType != eWindowType_plugin) return NS_ERROR_FAILURE;
  1236   // This code is necessary for CoreGraphics in 32-bit builds.
  1237   // See comments below about why. In 64-bit CoreGraphics mode we will not keep
  1238   // this region up to date, plugins should not depend on it.
  1239 #ifndef __LP64__
  1240   NSWindow* window = [mView window];
  1241   if (!window)
  1242     return NS_ERROR_FAILURE;
  1244   // In QuickDraw drawing mode we used to prevent reentrant handling of any
  1245   // plugin event. But in CoreGraphics drawing mode only do this if the current
  1246   // plugin event isn't an update/paint event.  This allows popupcontextmenu()
  1247   // to work properly from a plugin that supports the Cocoa event model,
  1248   // without regressing bug 409615.  See bug 435041.  (StartDrawPlugin() and
  1249   // EndDrawPlugin() wrap every call to nsIPluginInstance::HandleEvent() --
  1250   // not just calls that "draw" or paint.)
  1251   if (mIsDispatchPaint && mPluginDrawing) {
  1252     return NS_ERROR_FAILURE;
  1255   // It appears that the WindowRef from which we get the plugin port undergoes the
  1256   // traditional BeginUpdate/EndUpdate cycle, which, if you recall, sets the visible
  1257   // region to the intersection of the visible region and the update region. Since
  1258   // we don't know here if we're being drawn inside a BeginUpdate/EndUpdate pair
  1259   // (which seem to occur in [NSWindow display]), and we don't want to have the burden
  1260   // of correctly doing Carbon invalidates of the plugin rect, we manually set the
  1261   // visible region to be the entire port every time. It is necessary to set up our
  1262   // window's port even for CoreGraphics plugins, because they may still use Carbon
  1263   // internally (see bug #420527 for details).
  1264   //
  1265   // Don't use this code if any of the QuickDraw APIs it currently requires are
  1266   // missing (as they probably will be on OS X 10.8 and up).
  1267   if (::NewRgn && ::GetQDGlobalsThePort && ::GetGWorld && ::SetGWorld &&
  1268       ::IsPortOffscreen && ::GetMainDevice && ::SetOrigin && ::RectRgn &&
  1269       ::SetPortVisibleRegion && ::SetPortClipRegion && ::DisposeRgn) {
  1270     RgnHandle pluginRegion = ::NewRgn();
  1271     if (pluginRegion) {
  1272       CGrafPtr port = ::GetWindowPort(WindowRef([window windowRef]));
  1273       bool portChanged = (port != CGrafPtr(::GetQDGlobalsThePort()));
  1274       CGrafPtr oldPort;
  1275       GDHandle oldDevice;
  1277       if (portChanged) {
  1278         ::GetGWorld(&oldPort, &oldDevice);
  1279         ::SetGWorld(port, ::IsPortOffscreen(port) ? nullptr : ::GetMainDevice());
  1282       ::SetOrigin(0, 0);
  1284       nsIntRect clipRect; // this is in native window coordinates
  1285       nsIntPoint origin;
  1286       bool visible;
  1287       GetPluginClipRect(clipRect, origin, visible);
  1289       // XXX if we're not visible, set an empty clip region?
  1290       Rect pluginRect;
  1291       ConvertGeckoRectToMacRect(clipRect, pluginRect);
  1293       ::RectRgn(pluginRegion, &pluginRect);
  1294       ::SetPortVisibleRegion(port, pluginRegion);
  1295       ::SetPortClipRegion(port, pluginRegion);
  1297       // now set up the origin for the plugin
  1298       ::SetOrigin(origin.x, origin.y);
  1300       ::DisposeRgn(pluginRegion);
  1302       if (portChanged) {
  1303         ::SetGWorld(oldPort, oldDevice);
  1307 #endif
  1309   mPluginDrawing = true;
  1310   return NS_OK;
  1312   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1315 NS_IMETHODIMP nsChildView::EndDrawPlugin()
  1317   NS_ASSERTION(mWindowType == eWindowType_plugin,
  1318                "EndDrawPlugin must only be called on a plugin widget");
  1319   if (mWindowType != eWindowType_plugin) return NS_ERROR_FAILURE;
  1321   mPluginDrawing = false;
  1322   return NS_OK;
  1325 NS_IMETHODIMP nsChildView::SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner)
  1327   mPluginInstanceOwner = aInstanceOwner;
  1329   return NS_OK;
  1332 NS_IMETHODIMP nsChildView::SetPluginEventModel(int inEventModel)
  1334   [(ChildView*)mView setPluginEventModel:(NPEventModel)inEventModel];
  1335   return NS_OK;
  1338 NS_IMETHODIMP nsChildView::GetPluginEventModel(int* outEventModel)
  1340   *outEventModel = [(ChildView*)mView pluginEventModel];
  1341   return NS_OK;
  1344 NS_IMETHODIMP nsChildView::SetPluginDrawingModel(int inDrawingModel)
  1346   [(ChildView*)mView setPluginDrawingModel:(NPDrawingModel)inDrawingModel];
  1347   return NS_OK;
  1350 NS_IMETHODIMP nsChildView::StartComplexTextInputForCurrentEvent()
  1352   return mTextInputHandler->StartComplexTextInputForCurrentEvent();
  1355 nsresult nsChildView::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
  1356                                                int32_t aNativeKeyCode,
  1357                                                uint32_t aModifierFlags,
  1358                                                const nsAString& aCharacters,
  1359                                                const nsAString& aUnmodifiedCharacters)
  1361   return mTextInputHandler->SynthesizeNativeKeyEvent(aNativeKeyboardLayout,
  1362                                                      aNativeKeyCode,
  1363                                                      aModifierFlags,
  1364                                                      aCharacters,
  1365                                                      aUnmodifiedCharacters);
  1368 nsresult nsChildView::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
  1369                                                  uint32_t aNativeMessage,
  1370                                                  uint32_t aModifierFlags)
  1372   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1374   NSPoint pt =
  1375     nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
  1377   // Move the mouse cursor to the requested position and reconnect it to the mouse.
  1378   CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
  1379   CGAssociateMouseAndMouseCursorPosition(true);
  1381   // aPoint is given with the origin on the top left, but convertScreenToBase
  1382   // expects a point in a coordinate system that has its origin on the bottom left.
  1383   NSPoint screenPoint = NSMakePoint(pt.x, nsCocoaUtils::FlippedScreenY(pt.y));
  1384   NSPoint windowPoint = [[mView window] convertScreenToBase:screenPoint];
  1386   NSEvent* event = [NSEvent mouseEventWithType:(NSEventType)aNativeMessage
  1387                                       location:windowPoint
  1388                                  modifierFlags:aModifierFlags
  1389                                      timestamp:[NSDate timeIntervalSinceReferenceDate]
  1390                                   windowNumber:[[mView window] windowNumber]
  1391                                        context:nil
  1392                                    eventNumber:0
  1393                                     clickCount:1
  1394                                       pressure:0.0];
  1396   if (!event)
  1397     return NS_ERROR_FAILURE;
  1399   if ([[mView window] isKindOfClass:[BaseWindow class]]) {
  1400     // Tracking area events don't end up in their tracking areas when sent
  1401     // through [NSApp sendEvent:], so pass them directly to the right methods.
  1402     BaseWindow* window = (BaseWindow*)[mView window];
  1403     if (aNativeMessage == NSMouseEntered) {
  1404       [window mouseEntered:event];
  1405       return NS_OK;
  1407     if (aNativeMessage == NSMouseExited) {
  1408       [window mouseExited:event];
  1409       return NS_OK;
  1411     if (aNativeMessage == NSMouseMoved) {
  1412       [window mouseMoved:event];
  1413       return NS_OK;
  1417   [NSApp sendEvent:event];
  1418   return NS_OK;
  1420   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1423 // First argument has to be an NSMenu representing the application's top-level
  1424 // menu bar. The returned item is *not* retained.
  1425 static NSMenuItem* NativeMenuItemWithLocation(NSMenu* menubar, NSString* locationString)
  1427   NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
  1428   unsigned int indexCount = [indexes count];
  1429   if (indexCount == 0)
  1430     return nil;
  1432   NSMenu* currentSubmenu = [NSApp mainMenu];
  1433   for (unsigned int i = 0; i < indexCount; i++) {
  1434     int targetIndex;
  1435     // We remove the application menu from consideration for the top-level menu
  1436     if (i == 0)
  1437       targetIndex = [[indexes objectAtIndex:i] intValue] + 1;
  1438     else
  1439       targetIndex = [[indexes objectAtIndex:i] intValue];
  1440     int itemCount = [currentSubmenu numberOfItems];
  1441     if (targetIndex < itemCount) {
  1442       NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
  1443       // if this is the last index just return the menu item
  1444       if (i == (indexCount - 1))
  1445         return menuItem;
  1446       // if this is not the last index find the submenu and keep going
  1447       if ([menuItem hasSubmenu])
  1448         currentSubmenu = [menuItem submenu];
  1449       else
  1450         return nil;
  1454   return nil;
  1457 // Used for testing native menu system structure and event handling.
  1458 NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString)
  1460   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1462   NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
  1463                                                      length:indexString.Length()];
  1464   NSMenuItem* item = NativeMenuItemWithLocation([NSApp mainMenu], locationString);
  1465   // We can't perform an action on an item with a submenu, that will raise
  1466   // an obj-c exception.
  1467   if (item && ![item hasSubmenu]) {
  1468     NSMenu* parent = [item menu];
  1469     if (parent) {
  1470       // NSLog(@"Performing action for native menu item titled: %@\n",
  1471       //       [[currentSubmenu itemAtIndex:targetIndex] title]);
  1472       [parent performActionForItemAtIndex:[parent indexOfItem:item]];
  1473       return NS_OK;
  1476   return NS_ERROR_FAILURE;
  1478   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1481 // Used for testing native menu system structure and event handling.
  1482 NS_IMETHODIMP nsChildView::ForceUpdateNativeMenuAt(const nsAString& indexString)
  1484   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1486   nsCocoaWindow *widget = GetXULWindowWidget();
  1487   if (widget) {
  1488     nsMenuBarX* mb = widget->GetMenuBar();
  1489     if (mb) {
  1490       if (indexString.IsEmpty())
  1491         mb->ForceNativeMenuReload();
  1492       else
  1493         mb->ForceUpdateNativeMenuAt(indexString);
  1496   return NS_OK;
  1498   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1501 #pragma mark -
  1503 #ifdef INVALIDATE_DEBUGGING
  1505 static Boolean KeyDown(const UInt8 theKey)
  1507   KeyMap map;
  1508   GetKeys(map);
  1509   return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
  1512 static Boolean caps_lock()
  1514   return KeyDown(0x39);
  1517 static void blinkRect(Rect* r)
  1519   StRegionFromPool oldClip;
  1520   if (oldClip != NULL)
  1521     ::GetClip(oldClip);
  1523   ::ClipRect(r);
  1524   ::InvertRect(r);
  1525   UInt32 end = ::TickCount() + 5;
  1526   while (::TickCount() < end) ;
  1527   ::InvertRect(r);
  1529   if (oldClip != NULL)
  1530     ::SetClip(oldClip);
  1533 static void blinkRgn(RgnHandle rgn)
  1535   StRegionFromPool oldClip;
  1536   if (oldClip != NULL)
  1537     ::GetClip(oldClip);
  1539   ::SetClip(rgn);
  1540   ::InvertRgn(rgn);
  1541   UInt32 end = ::TickCount() + 5;
  1542   while (::TickCount() < end) ;
  1543   ::InvertRgn(rgn);
  1545   if (oldClip != NULL)
  1546     ::SetClip(oldClip);
  1549 #endif
  1551 // Invalidate this component's visible area
  1552 NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect)
  1554   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1556   if (!mView || !mVisible)
  1557     return NS_OK;
  1559   NS_ASSERTION(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_CLIENT,
  1560                "Shouldn't need to invalidate with accelerated OMTC layers!");
  1562   if ([NSView focusView]) {
  1563     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
  1564     // don't lose it.
  1565     [mView setNeedsPendingDisplayInRect:DevPixelsToCocoaPoints(aRect)];
  1567   else {
  1568     [mView setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)];
  1571   return NS_OK;
  1573   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1576 bool
  1577 nsChildView::ComputeShouldAccelerate(bool aDefault)
  1579   // Don't use OpenGL for transparent windows or for popup windows.
  1580   if (!mView || ![[mView window] isOpaque] ||
  1581       [[mView window] isKindOfClass:[PopupWindow class]])
  1582     return false;
  1584   return nsBaseWidget::ComputeShouldAccelerate(aDefault);
  1587 bool
  1588 nsChildView::ShouldUseOffMainThreadCompositing()
  1590   // Don't use OMTC for transparent windows or for popup windows.
  1591   if (!mView || ![[mView window] isOpaque] ||
  1592       [[mView window] isKindOfClass:[PopupWindow class]])
  1593     return false;
  1595   return nsBaseWidget::ShouldUseOffMainThreadCompositing();
  1598 inline uint16_t COLOR8TOCOLOR16(uint8_t color8)
  1600   // return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
  1601   return (color8 << 8) | color8;  /* (color8 * 257) == (color8 * 0x0101) */
  1604 #pragma mark -
  1606 nsresult nsChildView::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
  1608   for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
  1609     const Configuration& config = aConfigurations[i];
  1610     nsChildView* child = static_cast<nsChildView*>(config.mChild);
  1611 #ifdef DEBUG
  1612     nsWindowType kidType = child->WindowType();
  1613 #endif
  1614     NS_ASSERTION(kidType == eWindowType_plugin,
  1615                  "Configured widget is not a plugin type");
  1616     NS_ASSERTION(child->GetParent() == this,
  1617                  "Configured widget is not a child of the right widget");
  1619     // nsIWidget::Show() doesn't get called on plugin widgets unless we call
  1620     // it from here.  See bug 592563.
  1621     child->Show(!config.mClipRegion.IsEmpty());
  1623     child->Resize(
  1624         config.mBounds.x, config.mBounds.y,
  1625         config.mBounds.width, config.mBounds.height,
  1626         false);
  1628     // Store the clip region here in case GetPluginClipRect needs it.
  1629     child->StoreWindowClipRegion(config.mClipRegion);
  1631   return NS_OK;
  1634 // Invokes callback and ProcessEvent methods on Event Listener object
  1635 NS_IMETHODIMP nsChildView::DispatchEvent(WidgetGUIEvent* event,
  1636                                          nsEventStatus& aStatus)
  1638 #ifdef DEBUG
  1639   debug_DumpEvent(stdout, event->widget, event, nsAutoCString("something"), 0);
  1640 #endif
  1642   NS_ASSERTION(!(mTextInputHandler && mTextInputHandler->IsIMEComposing() &&
  1643                  event->HasKeyEventMessage()),
  1644     "Any key events should not be fired during IME composing");
  1646   if (event->mFlags.mIsSynthesizedForTests) {
  1647     WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent();
  1648     if (keyEvent) {
  1649       nsresult rv = mTextInputHandler->AttachNativeKeyEvent(*keyEvent);
  1650       NS_ENSURE_SUCCESS(rv, rv);
  1654   aStatus = nsEventStatus_eIgnore;
  1656   nsIWidgetListener* listener = mWidgetListener;
  1658   // If the listener is NULL, check if the parent is a popup. If it is, then
  1659   // this child is the popup content view attached to a popup. Get the
  1660   // listener from the parent popup instead.
  1661   nsCOMPtr<nsIWidget> kungFuDeathGrip = do_QueryInterface(mParentWidget ? mParentWidget : this);
  1662   if (!listener && mParentWidget) {
  1663     if (mParentWidget->WindowType() == eWindowType_popup) {
  1664       // Check just in case event->widget isn't this widget
  1665       if (event->widget)
  1666         listener = event->widget->GetWidgetListener();
  1667       if (!listener) {
  1668         event->widget = mParentWidget;
  1669         listener = mParentWidget->GetWidgetListener();
  1674   if (listener)
  1675     aStatus = listener->HandleEvent(event, mUseAttachedEvents);
  1677   return NS_OK;
  1680 bool nsChildView::DispatchWindowEvent(WidgetGUIEvent& event)
  1682   nsEventStatus status;
  1683   DispatchEvent(&event, status);
  1684   return ConvertStatus(status);
  1687 nsIWidget*
  1688 nsChildView::GetWidgetForListenerEvents()
  1690   // If there is no listener, use the parent popup's listener if that exists.
  1691   if (!mWidgetListener && mParentWidget &&
  1692       mParentWidget->WindowType() == eWindowType_popup) {
  1693     return mParentWidget;
  1696   return this;
  1699 void nsChildView::WillPaintWindow()
  1701   nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();
  1703   nsIWidgetListener* listener = widget->GetWidgetListener();
  1704   if (listener) {
  1705     listener->WillPaintWindow(widget);
  1709 bool nsChildView::PaintWindow(nsIntRegion aRegion)
  1711   nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents();
  1713   nsIWidgetListener* listener = widget->GetWidgetListener();
  1714   if (!listener)
  1715     return false;
  1717   bool returnValue = false;
  1718   bool oldDispatchPaint = mIsDispatchPaint;
  1719   mIsDispatchPaint = true;
  1720   returnValue = listener->PaintWindow(widget, aRegion);
  1722   listener = widget->GetWidgetListener();
  1723   if (listener) {
  1724     listener->DidPaintWindow();
  1727   mIsDispatchPaint = oldDispatchPaint;
  1728   return returnValue;
  1731 #pragma mark -
  1733 void nsChildView::ReportMoveEvent()
  1735    NotifyWindowMoved(mBounds.x, mBounds.y);
  1738 void nsChildView::ReportSizeEvent()
  1740   if (mWidgetListener)
  1741     mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
  1744 #pragma mark -
  1746 //    Return the offset between this child view and the screen.
  1747 //    @return       -- widget origin in device-pixel coords
  1748 nsIntPoint nsChildView::WidgetToScreenOffset()
  1750   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1752   NSPoint origin = NSMakePoint(0, 0);
  1754   // 1. First translate view origin point into window coords.
  1755   // The returned point is in bottom-left coordinates.
  1756   origin = [mView convertPoint:origin toView:nil];
  1758   // 2. We turn the window-coord rect's origin into screen (still bottom-left) coords.
  1759   origin = [[mView window] convertBaseToScreen:origin];
  1761   // 3. Since we're dealing in bottom-left coords, we need to make it top-left coords
  1762   //    before we pass it back to Gecko.
  1763   FlipCocoaScreenCoordinate(origin);
  1765   // convert to device pixels
  1766   return CocoaPointsToDevPixels(origin);
  1768   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0,0));
  1771 NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener,
  1772                                                bool aDoCapture)
  1774   // this never gets called, only top-level windows can be rollup widgets
  1775   return NS_OK;
  1778 NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
  1780   // child views don't have titles
  1781   return NS_OK;
  1784 NS_IMETHODIMP nsChildView::GetAttention(int32_t aCycleCount)
  1786   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1788   [NSApp requestUserAttention:NSInformationalRequest];
  1789   return NS_OK;
  1791   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1794 /* static */
  1795 bool nsChildView::DoHasPendingInputEvent()
  1797   return sLastInputEventCount != GetCurrentInputEventCount();
  1800 /* static */
  1801 uint32_t nsChildView::GetCurrentInputEventCount()
  1803   // Can't use kCGAnyInputEventType because that updates too rarely for us (and
  1804   // always in increments of 30+!) and because apparently it's sort of broken
  1805   // on Tiger.  So just go ahead and query the counters we care about.
  1806   static const CGEventType eventTypes[] = {
  1807     kCGEventLeftMouseDown,
  1808     kCGEventLeftMouseUp,
  1809     kCGEventRightMouseDown,
  1810     kCGEventRightMouseUp,
  1811     kCGEventMouseMoved,
  1812     kCGEventLeftMouseDragged,
  1813     kCGEventRightMouseDragged,
  1814     kCGEventKeyDown,
  1815     kCGEventKeyUp,
  1816     kCGEventScrollWheel,
  1817     kCGEventTabletPointer,
  1818     kCGEventOtherMouseDown,
  1819     kCGEventOtherMouseUp,
  1820     kCGEventOtherMouseDragged
  1821   };
  1823   uint32_t eventCount = 0;
  1824   for (uint32_t i = 0; i < ArrayLength(eventTypes); ++i) {
  1825     eventCount +=
  1826       CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState,
  1827                                        eventTypes[i]);
  1829   return eventCount;
  1832 /* static */
  1833 void nsChildView::UpdateCurrentInputEventCount()
  1835   sLastInputEventCount = GetCurrentInputEventCount();
  1838 bool nsChildView::HasPendingInputEvent()
  1840   return DoHasPendingInputEvent();
  1843 #pragma mark -
  1845 NS_IMETHODIMP
  1846 nsChildView::NotifyIME(const IMENotification& aIMENotification)
  1848   switch (aIMENotification.mMessage) {
  1849     case REQUEST_TO_COMMIT_COMPOSITION:
  1850       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
  1851       mTextInputHandler->CommitIMEComposition();
  1852       return NS_OK;
  1853     case REQUEST_TO_CANCEL_COMPOSITION:
  1854       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
  1855       mTextInputHandler->CancelIMEComposition();
  1856       return NS_OK;
  1857     case NOTIFY_IME_OF_FOCUS:
  1858       if (mInputContext.IsPasswordEditor()) {
  1859         TextInputHandler::EnableSecureEventInput();
  1862       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
  1863       mTextInputHandler->OnFocusChangeInGecko(true);
  1864       return NS_OK;
  1865     case NOTIFY_IME_OF_BLUR:
  1866       // When we're going to be deactive, we must disable the secure event input
  1867       // mode, see the Carbon Event Manager Reference.
  1868       TextInputHandler::EnsureSecureEventInputDisabled();
  1870       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
  1871       mTextInputHandler->OnFocusChangeInGecko(false);
  1872       return NS_OK;
  1873     case NOTIFY_IME_OF_SELECTION_CHANGE:
  1874       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
  1875       mTextInputHandler->OnSelectionChange();
  1876     default:
  1877       return NS_ERROR_NOT_IMPLEMENTED;
  1881 NS_IMETHODIMP_(void)
  1882 nsChildView::SetInputContext(const InputContext& aContext,
  1883                              const InputContextAction& aAction)
  1885   NS_ENSURE_TRUE_VOID(mTextInputHandler);
  1887   if (mTextInputHandler->IsFocused()) {
  1888     if (aContext.IsPasswordEditor()) {
  1889       TextInputHandler::EnableSecureEventInput();
  1890     } else {
  1891       TextInputHandler::EnsureSecureEventInputDisabled();
  1895   mInputContext = aContext;
  1896   switch (aContext.mIMEState.mEnabled) {
  1897     case IMEState::ENABLED:
  1898     case IMEState::PLUGIN:
  1899       mTextInputHandler->SetASCIICapableOnly(false);
  1900       mTextInputHandler->EnableIME(true);
  1901       if (mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) {
  1902         mTextInputHandler->SetIMEOpenState(
  1903           mInputContext.mIMEState.mOpen == IMEState::OPEN);
  1905       break;
  1906     case IMEState::DISABLED:
  1907       mTextInputHandler->SetASCIICapableOnly(false);
  1908       mTextInputHandler->EnableIME(false);
  1909       break;
  1910     case IMEState::PASSWORD:
  1911       mTextInputHandler->SetASCIICapableOnly(true);
  1912       mTextInputHandler->EnableIME(false);
  1913       break;
  1914     default:
  1915       NS_ERROR("not implemented!");
  1919 NS_IMETHODIMP_(InputContext)
  1920 nsChildView::GetInputContext()
  1922   switch (mInputContext.mIMEState.mEnabled) {
  1923     case IMEState::ENABLED:
  1924     case IMEState::PLUGIN:
  1925       if (mTextInputHandler) {
  1926         mInputContext.mIMEState.mOpen =
  1927           mTextInputHandler->IsIMEOpened() ? IMEState::OPEN : IMEState::CLOSED;
  1928         break;
  1930       // If mTextInputHandler is null, set CLOSED instead...
  1931     default:
  1932       mInputContext.mIMEState.mOpen = IMEState::CLOSED;
  1933       break;
  1935   mInputContext.mNativeIMEContext = [mView inputContext];
  1936   // If input context isn't available on this widget, we should set |this|
  1937   // instead of nullptr since nullptr means that the platform has only one
  1938   // context per process.
  1939   if (!mInputContext.mNativeIMEContext) {
  1940     mInputContext.mNativeIMEContext = this;
  1942   return mInputContext;
  1945 NS_IMETHODIMP
  1946 nsChildView::AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent)
  1948   NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
  1949   return mTextInputHandler->AttachNativeKeyEvent(aEvent);
  1952 NS_IMETHODIMP_(bool)
  1953 nsChildView::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
  1954                                      const WidgetKeyboardEvent& aEvent,
  1955                                      DoCommandCallback aCallback,
  1956                                      void* aCallbackData)
  1958   NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
  1959   return keyBindings->Execute(aEvent, aCallback, aCallbackData);
  1962 nsIMEUpdatePreference
  1963 nsChildView::GetIMEUpdatePreference()
  1965   return nsIMEUpdatePreference(nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE);
  1968 NS_IMETHODIMP nsChildView::GetToggledKeyState(uint32_t aKeyCode,
  1969                                               bool* aLEDState)
  1971   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1973   NS_ENSURE_ARG_POINTER(aLEDState);
  1974   uint32_t key;
  1975   switch (aKeyCode) {
  1976     case NS_VK_CAPS_LOCK:
  1977       key = alphaLock;
  1978       break;
  1979     case NS_VK_NUM_LOCK:
  1980       key = kEventKeyModifierNumLockMask;
  1981       break;
  1982     // Mac doesn't support SCROLL_LOCK state.
  1983     default:
  1984       return NS_ERROR_NOT_IMPLEMENTED;
  1986   uint32_t modifierFlags = ::GetCurrentKeyModifiers();
  1987   *aLEDState = (modifierFlags & key) != 0;
  1988   return NS_OK;
  1990   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1993 NSView<mozView>* nsChildView::GetEditorView()
  1995   NSView<mozView>* editorView = mView;
  1996   // We need to get editor's view. E.g., when the focus is in the bookmark
  1997   // dialog, the view is <panel> element of the dialog.  At this time, the key
  1998   // events are processed the parent window's view that has native focus.
  1999   WidgetQueryContentEvent textContent(true, NS_QUERY_TEXT_CONTENT, this);
  2000   textContent.InitForQueryTextContent(0, 0);
  2001   DispatchWindowEvent(textContent);
  2002   if (textContent.mSucceeded && textContent.mReply.mFocusedWidget) {
  2003     NSView<mozView>* view = static_cast<NSView<mozView>*>(
  2004       textContent.mReply.mFocusedWidget->GetNativeData(NS_NATIVE_WIDGET));
  2005     if (view)
  2006       editorView = view;
  2008   return editorView;
  2011 #pragma mark -
  2013 void
  2014 nsChildView::CreateCompositor()
  2016   nsBaseWidget::CreateCompositor();
  2017   if (mCompositorChild) {
  2018     [(ChildView *)mView setUsingOMTCompositor:true];
  2022 gfxASurface*
  2023 nsChildView::GetThebesSurface()
  2025   if (!mTempThebesSurface) {
  2026     mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxImageFormat::ARGB32);
  2029   return mTempThebesSurface;
  2032 void
  2033 nsChildView::NotifyDirtyRegion(const nsIntRegion& aDirtyRegion)
  2035   if ([(ChildView*)mView isCoveringTitlebar]) {
  2036     // We store the dirty region so that we know what to repaint in the titlebar.
  2037     mDirtyTitlebarRegion.Or(mDirtyTitlebarRegion, aDirtyRegion);
  2041 nsIntRect
  2042 nsChildView::RectContainingTitlebarControls()
  2044   // Start with a thin strip at the top of the window for the highlight line.
  2045   NSRect rect = NSMakeRect(0, 0, [mView bounds].size.width,
  2046                            [(ChildView*)mView cornerRadius]);
  2048   // Add the rects of the titlebar controls.
  2049   for (id view in [(BaseWindow*)[mView window] titlebarControls]) {
  2050     rect = NSUnionRect(rect, [mView convertRect:[view bounds] fromView:view]);
  2052   return CocoaPointsToDevPixels(rect);
  2055 void
  2056 nsChildView::PrepareWindowEffects()
  2058   MutexAutoLock lock(mEffectsLock);
  2059   mShowsResizeIndicator = ShowsResizeIndicator(&mResizeIndicatorRect);
  2060   mHasRoundedBottomCorners = [(ChildView*)mView hasRoundedBottomCorners];
  2061   CGFloat cornerRadius = [(ChildView*)mView cornerRadius];
  2062   mDevPixelCornerRadius = cornerRadius * BackingScaleFactor();
  2063   mIsCoveringTitlebar = [(ChildView*)mView isCoveringTitlebar];
  2064   mIsFullscreen = ([[mView window] styleMask] & NSFullScreenWindowMask) != 0;
  2065   if (mIsCoveringTitlebar) {
  2066     mTitlebarRect = RectContainingTitlebarControls();
  2067     UpdateTitlebarCGContext();
  2071 void
  2072 nsChildView::CleanupWindowEffects()
  2074   mResizerImage = nullptr;
  2075   mCornerMaskImage = nullptr;
  2076   mTitlebarImage = nullptr;
  2079 bool
  2080 nsChildView::PreRender(LayerManagerComposite* aManager)
  2082   nsAutoPtr<GLManager> manager(GLManager::CreateGLManager(aManager));
  2083   if (!manager) {
  2084     return true;
  2087   // The lock makes sure that we don't attempt to tear down the view while
  2088   // compositing. That would make us unable to call postRender on it when the
  2089   // composition is done, thus keeping the GL context locked forever.
  2090   mViewTearDownLock.Lock();
  2092   NSOpenGLContext *glContext = GLContextCGL::Cast(manager->gl())->GetNSOpenGLContext();
  2094   if (![(ChildView*)mView preRender:glContext]) {
  2095     mViewTearDownLock.Unlock();
  2096     return false;
  2098   return true;
  2101 void
  2102 nsChildView::PostRender(LayerManagerComposite* aManager)
  2104   nsAutoPtr<GLManager> manager(GLManager::CreateGLManager(aManager));
  2105   if (!manager) {
  2106     return;
  2108   NSOpenGLContext *glContext = GLContextCGL::Cast(manager->gl())->GetNSOpenGLContext();
  2109   [(ChildView*)mView postRender:glContext];
  2110   mViewTearDownLock.Unlock();
  2113 void
  2114 nsChildView::DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect)
  2116   nsAutoPtr<GLManager> manager(GLManager::CreateGLManager(aManager));
  2117   if (manager) {
  2118     DrawWindowOverlay(manager, aRect);
  2122 void
  2123 nsChildView::DrawWindowOverlay(GLManager* aManager, nsIntRect aRect)
  2125   GLContext* gl = aManager->gl();
  2126   ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false);
  2128   MaybeDrawTitlebar(aManager, aRect);
  2129   MaybeDrawResizeIndicator(aManager, aRect);
  2130   MaybeDrawRoundedCorners(aManager, aRect);
  2133 static void
  2134 ClearRegion(gfx::DrawTarget *aDT, nsIntRegion aRegion)
  2136   gfxUtils::ClipToRegion(aDT, aRegion);
  2137   aDT->ClearRect(gfx::Rect(0, 0, aDT->GetSize().width, aDT->GetSize().height));
  2138   aDT->PopClip();
  2141 static void
  2142 DrawResizer(CGContextRef aCtx)
  2144   CGContextSetShouldAntialias(aCtx, false);
  2145   CGPoint points[6];
  2146   points[0] = CGPointMake(13.0f, 4.0f);
  2147   points[1] = CGPointMake(3.0f, 14.0f);
  2148   points[2] = CGPointMake(13.0f, 8.0f);
  2149   points[3] = CGPointMake(7.0f, 14.0f);
  2150   points[4] = CGPointMake(13.0f, 12.0f);
  2151   points[5] = CGPointMake(11.0f, 14.0f);
  2152   CGContextSetRGBStrokeColor(aCtx, 0.00f, 0.00f, 0.00f, 0.15f);
  2153   CGContextStrokeLineSegments(aCtx, points, 6);
  2155   points[0] = CGPointMake(13.0f, 5.0f);
  2156   points[1] = CGPointMake(4.0f, 14.0f);
  2157   points[2] = CGPointMake(13.0f, 9.0f);
  2158   points[3] = CGPointMake(8.0f, 14.0f);
  2159   points[4] = CGPointMake(13.0f, 13.0f);
  2160   points[5] = CGPointMake(12.0f, 14.0f);
  2161   CGContextSetRGBStrokeColor(aCtx, 0.13f, 0.13f, 0.13f, 0.54f);
  2162   CGContextStrokeLineSegments(aCtx, points, 6);
  2164   points[0] = CGPointMake(13.0f, 6.0f);
  2165   points[1] = CGPointMake(5.0f, 14.0f);
  2166   points[2] = CGPointMake(13.0f, 10.0f);
  2167   points[3] = CGPointMake(9.0f, 14.0f);
  2168   points[5] = CGPointMake(13.0f, 13.9f);
  2169   points[4] = CGPointMake(13.0f, 14.0f);
  2170   CGContextSetRGBStrokeColor(aCtx, 0.84f, 0.84f, 0.84f, 0.55f);
  2171   CGContextStrokeLineSegments(aCtx, points, 6);
  2174 void
  2175 nsChildView::MaybeDrawResizeIndicator(GLManager* aManager, const nsIntRect& aRect)
  2177   MutexAutoLock lock(mEffectsLock);
  2178   if (!mShowsResizeIndicator) {
  2179     return;
  2182   if (!mResizerImage) {
  2183     mResizerImage = new RectTextureImage(aManager->gl());
  2186   nsIntSize size = mResizeIndicatorRect.Size();
  2187   mResizerImage->UpdateIfNeeded(size, nsIntRegion(), ^(gfx::DrawTarget* drawTarget, const nsIntRegion& updateRegion) {
  2188     ClearRegion(drawTarget, updateRegion);
  2189     gfx::BorrowedCGContext borrow(drawTarget);
  2190     DrawResizer(borrow.cg);
  2191     borrow.Finish();
  2192   });
  2194   mResizerImage->Draw(aManager, mResizeIndicatorRect.TopLeft());
  2197 // Draw the highlight line at the top of the titlebar.
  2198 // This function draws into the current NSGraphicsContext and assumes flippedness.
  2199 static void
  2200 DrawTitlebarHighlight(NSSize aWindowSize, CGFloat aRadius, CGFloat aDevicePixelWidth)
  2202   [NSGraphicsContext saveGraphicsState];
  2204   // Set up the clip path. We start with the outer rectangle and cut out a
  2205   // slightly smaller inner rectangle with rounded corners.
  2206   // The outer corners of the resulting path will be square, but they will be
  2207   // masked away in a later step.
  2208   NSBezierPath* path = [NSBezierPath bezierPath];
  2209   [path setWindingRule:NSEvenOddWindingRule];
  2210   NSRect pathRect = NSMakeRect(0, 0, aWindowSize.width, aRadius + 2);
  2211   [path appendBezierPathWithRect:pathRect];
  2212   pathRect = NSInsetRect(pathRect, aDevicePixelWidth, aDevicePixelWidth);
  2213   CGFloat innerRadius = aRadius - aDevicePixelWidth;
  2214   [path appendBezierPathWithRoundedRect:pathRect xRadius:innerRadius yRadius:innerRadius];
  2215   [path addClip];
  2217   // Now we fill the path with a subtle highlight gradient.
  2218   // We don't use NSGradient because it's 5x to 15x slower than the manual fill,
  2219   // as indicated by the performance test in bug 880620.
  2220   for (CGFloat y = 0; y < aRadius; y += aDevicePixelWidth) {
  2221     CGFloat t = y / aRadius;
  2222     [[NSColor colorWithDeviceWhite:1.0 alpha:0.4 * (1.0 - t)] set];
  2223     NSRectFill(NSMakeRect(0, y, aWindowSize.width, aDevicePixelWidth));
  2226   [NSGraphicsContext restoreGraphicsState];
  2229 static CGContextRef
  2230 CreateCGContext(const nsIntSize& aSize)
  2232   CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
  2233   CGContextRef ctx =
  2234     CGBitmapContextCreate(NULL,
  2235                           aSize.width,
  2236                           aSize.height,
  2237                           8 /* bitsPerComponent */,
  2238                           aSize.width * 4,
  2239                           cs,
  2240                           kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
  2241   CGColorSpaceRelease(cs);
  2243   CGContextTranslateCTM(ctx, 0, aSize.height);
  2244   CGContextScaleCTM(ctx, 1, -1);
  2245   CGContextSetInterpolationQuality(ctx, kCGInterpolationLow);
  2247   return ctx;
  2250 // When this method is entered, mEffectsLock is already being held.
  2251 void
  2252 nsChildView::UpdateTitlebarCGContext()
  2254   nsIntRegion dirtyTitlebarRegion;
  2255   dirtyTitlebarRegion.And(mDirtyTitlebarRegion, mTitlebarRect);
  2256   mDirtyTitlebarRegion.SetEmpty();
  2258   nsIntSize texSize = RectTextureImage::TextureSizeForSize(mTitlebarRect.Size());
  2259   if (!mTitlebarCGContext ||
  2260       CGBitmapContextGetWidth(mTitlebarCGContext) != size_t(texSize.width) ||
  2261       CGBitmapContextGetHeight(mTitlebarCGContext) != size_t(texSize.height)) {
  2262     dirtyTitlebarRegion = mTitlebarRect;
  2264     ReleaseTitlebarCGContext();
  2266     mTitlebarCGContext = CreateCGContext(texSize);
  2269   if (dirtyTitlebarRegion.IsEmpty())
  2270     return;
  2272   CGContextRef ctx = mTitlebarCGContext;
  2274   CGContextSaveGState(ctx);
  2276   std::vector<CGRect> rects;
  2277   nsIntRegionRectIterator iter(dirtyTitlebarRegion);
  2278   for (;;) {
  2279     const nsIntRect* r = iter.Next();
  2280     if (!r)
  2281       break;
  2282     rects.push_back(CGRectMake(r->x, r->y, r->width, r->height));
  2284   CGContextClipToRects(ctx, rects.data(), rects.size());
  2286   CGContextClearRect(ctx, CGRectMake(0, 0, texSize.width, texSize.height));
  2288   double scale = BackingScaleFactor();
  2289   CGContextScaleCTM(ctx, scale, scale);
  2290   NSGraphicsContext* oldContext = [NSGraphicsContext currentContext];
  2292   CGContextSaveGState(ctx);
  2294   BaseWindow* window = (BaseWindow*)[mView window];
  2295   NSView* frameView = [[window contentView] superview];
  2296   if (![frameView isFlipped]) {
  2297     CGContextTranslateCTM(ctx, 0, [frameView bounds].size.height);
  2298     CGContextScaleCTM(ctx, 1, -1);
  2300   NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:[frameView isFlipped]];
  2301   [NSGraphicsContext setCurrentContext:context];
  2303   // Draw the title string.
  2304   if ([window wantsTitleDrawn] && [frameView respondsToSelector:@selector(_drawTitleBar:)]) {
  2305     [frameView _drawTitleBar:[frameView bounds]];
  2308   // Draw the titlebar controls into the titlebar image.
  2309   for (id view in [window titlebarControls]) {
  2310     NSRect viewFrame = [view frame];
  2311     nsIntRect viewRect = CocoaPointsToDevPixels([mView convertRect:viewFrame fromView:frameView]);
  2312     if (!dirtyTitlebarRegion.Intersects(viewRect)) {
  2313       continue;
  2315     // All of the titlebar controls we're interested in are subclasses of
  2316     // NSButton.
  2317     if (![view isKindOfClass:[NSButton class]]) {
  2318       continue;
  2320     NSButton *button = (NSButton *) view;
  2321     id cellObject = [button cell];
  2322     if (![cellObject isKindOfClass:[NSCell class]]) {
  2323       continue;
  2325     NSCell *cell = (NSCell *) cellObject;
  2327     CGContextSaveGState(ctx);
  2328     CGContextTranslateCTM(ctx, viewFrame.origin.x, viewFrame.origin.y);
  2330     if ([context isFlipped] != [view isFlipped]) {
  2331       CGContextTranslateCTM(ctx, 0, viewFrame.size.height);
  2332       CGContextScaleCTM(ctx, 1, -1);
  2335     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:[view isFlipped]]];
  2337     [cell drawWithFrame:[button bounds] inView:button];
  2339     [NSGraphicsContext setCurrentContext:context];
  2340     CGContextRestoreGState(ctx);
  2343   CGContextRestoreGState(ctx);
  2345   DrawTitlebarHighlight([frameView bounds].size, [(ChildView*)mView cornerRadius],
  2346                         DevPixelsToCocoaPoints(1));
  2348   [NSGraphicsContext setCurrentContext:oldContext];
  2350   CGContextRestoreGState(ctx);
  2352   mUpdatedTitlebarRegion.Or(mUpdatedTitlebarRegion, dirtyTitlebarRegion);
  2355 // This method draws an overlay in the top of the window which contains the
  2356 // titlebar controls (e.g. close, min, zoom, fullscreen) and the titlebar
  2357 // highlight effect.
  2358 // This is necessary because the real titlebar controls are covered by our
  2359 // OpenGL context. Note that in terms of the NSView hierarchy, our ChildView
  2360 // is actually below the titlebar controls - that's why hovering and clicking
  2361 // them works as expected - but their visual representation is only drawn into
  2362 // the normal window buffer, and the window buffer surface lies below the
  2363 // GLContext surface. In order to make the titlebar controls visible, we have
  2364 // to redraw them inside the OpenGL context surface.
  2365 void
  2366 nsChildView::MaybeDrawTitlebar(GLManager* aManager, const nsIntRect& aRect)
  2368   MutexAutoLock lock(mEffectsLock);
  2369   if (!mIsCoveringTitlebar || mIsFullscreen) {
  2370     return;
  2373   nsIntRegion updatedTitlebarRegion;
  2374   updatedTitlebarRegion.And(mUpdatedTitlebarRegion, mTitlebarRect);
  2375   mUpdatedTitlebarRegion.SetEmpty();
  2377   if (!mTitlebarImage) {
  2378     mTitlebarImage = new RectTextureImage(aManager->gl());
  2381   mTitlebarImage->UpdateFromCGContext(mTitlebarRect.Size(),
  2382                                       updatedTitlebarRegion,
  2383                                       mTitlebarCGContext);
  2385   mTitlebarImage->Draw(aManager, mTitlebarRect.TopLeft());
  2388 static void
  2389 DrawTopLeftCornerMask(CGContextRef aCtx, int aRadius)
  2391   CGContextSetRGBFillColor(aCtx, 1.0, 1.0, 1.0, 1.0);
  2392   CGContextFillEllipseInRect(aCtx, CGRectMake(0, 0, aRadius * 2, aRadius * 2));
  2395 void
  2396 nsChildView::MaybeDrawRoundedCorners(GLManager* aManager, const nsIntRect& aRect)
  2398   MutexAutoLock lock(mEffectsLock);
  2400   if (!mCornerMaskImage) {
  2401     mCornerMaskImage = new RectTextureImage(aManager->gl());
  2404   nsIntSize size(mDevPixelCornerRadius, mDevPixelCornerRadius);
  2405   mCornerMaskImage->UpdateIfNeeded(size, nsIntRegion(), ^(gfx::DrawTarget* drawTarget, const nsIntRegion& updateRegion) {
  2406     ClearRegion(drawTarget, updateRegion);
  2407     RefPtr<gfx::PathBuilder> builder = drawTarget->CreatePathBuilder();
  2408     builder->Arc(gfx::Point(mDevPixelCornerRadius, mDevPixelCornerRadius), mDevPixelCornerRadius, 0, 2.0f * M_PI);
  2409     RefPtr<gfx::Path> path = builder->Finish();
  2410     drawTarget->Fill(path,
  2411                      gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)),
  2412                      gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE));
  2413   });
  2415   // Use operator destination in: multiply all 4 channels with source alpha.
  2416   aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA,
  2417                                      LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA);
  2419   gfx3DMatrix flipX = gfx3DMatrix::ScalingMatrix(-1, 1, 1);
  2420   gfx3DMatrix flipY = gfx3DMatrix::ScalingMatrix(1, -1, 1);
  2422   if (mIsCoveringTitlebar && !mIsFullscreen) {
  2423     // Mask the top corners.
  2424     mCornerMaskImage->Draw(aManager, aRect.TopLeft());
  2425     mCornerMaskImage->Draw(aManager, aRect.TopRight(), flipX);
  2428   if (mHasRoundedBottomCorners && !mIsFullscreen) {
  2429     // Mask the bottom corners.
  2430     mCornerMaskImage->Draw(aManager, aRect.BottomLeft(), flipY);
  2431     mCornerMaskImage->Draw(aManager, aRect.BottomRight(), flipY * flipX);
  2434   // Reset blend mode.
  2435   aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
  2436                                      LOCAL_GL_ONE, LOCAL_GL_ONE);
  2439 static int32_t
  2440 FindTitlebarBottom(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
  2441                    int32_t aWindowWidth)
  2443   int32_t titlebarBottom = 0;
  2444   for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) {
  2445     const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
  2446     if ((g.mWidgetType == NS_THEME_WINDOW_TITLEBAR) &&
  2447         g.mRect.X() <= 0 &&
  2448         g.mRect.XMost() >= aWindowWidth &&
  2449         g.mRect.Y() <= 0) {
  2450       titlebarBottom = std::max(titlebarBottom, g.mRect.YMost());
  2453   return titlebarBottom;
  2456 static int32_t
  2457 FindUnifiedToolbarBottom(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
  2458                          int32_t aWindowWidth, int32_t aTitlebarBottom)
  2460   int32_t unifiedToolbarBottom = aTitlebarBottom;
  2461   for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) {
  2462     const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
  2463     if ((g.mWidgetType == NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR ||
  2464          g.mWidgetType == NS_THEME_TOOLBAR) &&
  2465         g.mRect.X() <= 0 &&
  2466         g.mRect.XMost() >= aWindowWidth &&
  2467         g.mRect.Y() <= aTitlebarBottom) {
  2468       unifiedToolbarBottom = std::max(unifiedToolbarBottom, g.mRect.YMost());
  2471   return unifiedToolbarBottom;
  2474 static nsIntRect
  2475 FindFirstRectOfType(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
  2476                     uint8_t aWidgetType)
  2478   for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) {
  2479     const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
  2480     if (g.mWidgetType == aWidgetType) {
  2481       return g.mRect;
  2484   return nsIntRect();
  2487 void
  2488 nsChildView::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries)
  2490   if (![mView window] || ![[mView window] isKindOfClass:[ToolbarWindow class]])
  2491     return;
  2493   // Update unified toolbar height.
  2494   int32_t windowWidth = mBounds.width;
  2495   int32_t titlebarBottom = FindTitlebarBottom(aThemeGeometries, windowWidth);
  2496   int32_t unifiedToolbarBottom =
  2497     FindUnifiedToolbarBottom(aThemeGeometries, windowWidth, titlebarBottom);
  2499   ToolbarWindow* win = (ToolbarWindow*)[mView window];
  2500   bool drawsContentsIntoWindowFrame = [win drawsContentsIntoWindowFrame];
  2501   int32_t titlebarHeight = CocoaPointsToDevPixels([win titlebarHeight]);
  2502   int32_t contentOffset = drawsContentsIntoWindowFrame ? titlebarHeight : 0;
  2503   int32_t devUnifiedHeight = titlebarHeight + unifiedToolbarBottom - contentOffset;
  2504   [win setUnifiedToolbarHeight:DevPixelsToCocoaPoints(devUnifiedHeight)];
  2506   // Update titlebar control offsets.
  2507   nsIntRect windowButtonRect = FindFirstRectOfType(aThemeGeometries, NS_THEME_WINDOW_BUTTON_BOX);
  2508   [win placeWindowButtons:[mView convertRect:DevPixelsToCocoaPoints(windowButtonRect) toView:nil]];
  2509   nsIntRect fullScreenButtonRect = FindFirstRectOfType(aThemeGeometries, NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON);
  2510   [win placeFullScreenButton:[mView convertRect:DevPixelsToCocoaPoints(fullScreenButtonRect) toView:nil]];
  2513 TemporaryRef<gfx::DrawTarget>
  2514 nsChildView::StartRemoteDrawing()
  2516   if (!mGLPresenter) {
  2517     mGLPresenter = GLPresenter::CreateForWindow(this);
  2519     if (!mGLPresenter) {
  2520       return nullptr;
  2524   nsIntRegion dirtyRegion = mBounds;
  2525   nsIntSize renderSize = mBounds.Size();
  2527   if (!mBasicCompositorImage) {
  2528     mBasicCompositorImage = new RectTextureImage(mGLPresenter->gl());
  2531   RefPtr<gfx::DrawTarget> drawTarget =
  2532     mBasicCompositorImage->BeginUpdate(renderSize, dirtyRegion);
  2534   if (!drawTarget) {
  2535     // Composite unchanged textures.
  2536     DoRemoteComposition(mBounds);
  2537     return nullptr;
  2540   return drawTarget;
  2543 void
  2544 nsChildView::EndRemoteDrawing()
  2546   mBasicCompositorImage->EndUpdate(true);
  2547   DoRemoteComposition(mBounds);
  2550 void
  2551 nsChildView::CleanupRemoteDrawing()
  2553   mBasicCompositorImage = nullptr;
  2554   mCornerMaskImage = nullptr;
  2555   mResizerImage = nullptr;
  2556   mTitlebarImage = nullptr;
  2557   mGLPresenter = nullptr;
  2560 void
  2561 nsChildView::DoRemoteComposition(const nsIntRect& aRenderRect)
  2563   if (![(ChildView*)mView preRender:mGLPresenter->GetNSOpenGLContext()]) {
  2564     return;
  2566   mGLPresenter->BeginFrame(aRenderRect.Size());
  2568   // Draw the result from the basic compositor.
  2569   mBasicCompositorImage->Draw(mGLPresenter, nsIntPoint(0, 0));
  2571   // DrawWindowOverlay doesn't do anything for non-GL, so it didn't paint
  2572   // anything during the basic compositor transaction. Draw the overlay now.
  2573   DrawWindowOverlay(mGLPresenter, aRenderRect);
  2575   mGLPresenter->EndFrame();
  2577   [(ChildView*)mView postRender:mGLPresenter->GetNSOpenGLContext()];
  2580 #ifdef ACCESSIBILITY
  2581 already_AddRefed<a11y::Accessible>
  2582 nsChildView::GetDocumentAccessible()
  2584   if (!mozilla::a11y::ShouldA11yBeEnabled())
  2585     return nullptr;
  2587   if (mAccessible) {
  2588     nsRefPtr<a11y::Accessible> ret;
  2589     CallQueryReferent(mAccessible.get(),
  2590                       static_cast<a11y::Accessible**>(getter_AddRefs(ret)));
  2591     return ret.forget();
  2594   // need to fetch the accessible anew, because it has gone away.
  2595   // cache the accessible in our weak ptr
  2596   nsRefPtr<a11y::Accessible> acc = GetRootAccessible();
  2597   mAccessible = do_GetWeakReference(static_cast<nsIAccessible *>(acc.get()));
  2599   return acc.forget();
  2601 #endif
  2603 // RectTextureImage implementation
  2605 RectTextureImage::~RectTextureImage()
  2607   if (mTexture) {
  2608     mGLContext->MakeCurrent();
  2609     mGLContext->fDeleteTextures(1, &mTexture);
  2610     mTexture = 0;
  2614 nsIntSize
  2615 RectTextureImage::TextureSizeForSize(const nsIntSize& aSize)
  2617   return nsIntSize(gfx::NextPowerOfTwo(aSize.width),
  2618                    gfx::NextPowerOfTwo(aSize.height));
  2621 TemporaryRef<gfx::DrawTarget>
  2622 RectTextureImage::BeginUpdate(const nsIntSize& aNewSize,
  2623                               const nsIntRegion& aDirtyRegion)
  2625   MOZ_ASSERT(!mInUpdate, "Beginning update during update!");
  2626   mUpdateRegion = aDirtyRegion;
  2627   if (aNewSize != mUsedSize) {
  2628     mUsedSize = aNewSize;
  2629     mUpdateRegion = nsIntRect(nsIntPoint(0, 0), aNewSize);
  2632   if (mUpdateRegion.IsEmpty()) {
  2633     return nullptr;
  2636   nsIntSize neededBufferSize = TextureSizeForSize(mUsedSize);
  2637   if (!mUpdateDrawTarget || mBufferSize != neededBufferSize) {
  2638     gfx::IntSize size(neededBufferSize.width, neededBufferSize.height);
  2639     mUpdateDrawTarget =
  2640       gfx::Factory::CreateDrawTarget(gfx::BackendType::COREGRAPHICS, size,
  2641                                      gfx::SurfaceFormat::B8G8R8A8);
  2642     mBufferSize = neededBufferSize;
  2645   mInUpdate = true;
  2647   RefPtr<gfx::DrawTarget> drawTarget = mUpdateDrawTarget;
  2648   return drawTarget;
  2651 #define NSFoundationVersionWithProperStrideSupportForSubtextureUpload NSFoundationVersionNumber10_6_3
  2653 static bool
  2654 CanUploadSubtextures()
  2656   return NSFoundationVersionNumber >= NSFoundationVersionWithProperStrideSupportForSubtextureUpload;
  2659 void
  2660 RectTextureImage::EndUpdate(bool aKeepSurface)
  2662   MOZ_ASSERT(mInUpdate, "Ending update while not in update");
  2664   bool overwriteTexture = false;
  2665   nsIntRegion updateRegion = mUpdateRegion;
  2666   if (!mTexture || (mTextureSize != mBufferSize)) {
  2667     overwriteTexture = true;
  2668     mTextureSize = mBufferSize;
  2671   if (overwriteTexture || !CanUploadSubtextures()) {
  2672     updateRegion = nsIntRect(nsIntPoint(0, 0), mTextureSize);
  2675   RefPtr<gfx::SourceSurface> snapshot = mUpdateDrawTarget->Snapshot();
  2676   RefPtr<gfx::DataSourceSurface> dataSnapshot = snapshot->GetDataSurface();
  2678   UploadSurfaceToTexture(mGLContext,
  2679                          dataSnapshot,
  2680                          updateRegion,
  2681                          mTexture,
  2682                          overwriteTexture,
  2683                          updateRegion.GetBounds().TopLeft(),
  2684                          false,
  2685                          LOCAL_GL_TEXTURE0,
  2686                          LOCAL_GL_TEXTURE_RECTANGLE_ARB);
  2688   if (!aKeepSurface) {
  2689     mUpdateDrawTarget = nullptr;
  2692   mInUpdate = false;
  2695 void
  2696 RectTextureImage::UpdateFromCGContext(const nsIntSize& aNewSize,
  2697                                       const nsIntRegion& aDirtyRegion,
  2698                                       CGContextRef aCGContext)
  2700   gfx::IntSize size = gfx::IntSize(CGBitmapContextGetWidth(aCGContext),
  2701                                    CGBitmapContextGetHeight(aCGContext));
  2702   mBufferSize.SizeTo(size.width, size.height);
  2703   RefPtr<gfx::DrawTarget> dt = BeginUpdate(aNewSize, aDirtyRegion);
  2704   if (dt) {
  2705     gfx::Rect rect(0, 0, size.width, size.height);
  2706     gfxUtils::ClipToRegion(dt, GetUpdateRegion());
  2707     RefPtr<gfx::SourceSurface> sourceSurface =
  2708       dt->CreateSourceSurfaceFromData(static_cast<uint8_t *>(CGBitmapContextGetData(aCGContext)),
  2709                                       size,
  2710                                       CGBitmapContextGetBytesPerRow(aCGContext),
  2711                                       gfx::SurfaceFormat::B8G8R8A8);
  2712     dt->DrawSurface(sourceSurface, rect, rect,
  2713                     gfx::DrawSurfaceOptions(),
  2714                     gfx::DrawOptions(1.0, gfx::CompositionOp::OP_SOURCE));
  2715     dt->PopClip();
  2716     EndUpdate();
  2720 void
  2721 RectTextureImage::UpdateFromDrawTarget(const nsIntSize& aNewSize,
  2722                                        const nsIntRegion& aDirtyRegion,
  2723                                        gfx::DrawTarget* aFromDrawTarget)
  2725   mUpdateDrawTarget = aFromDrawTarget;
  2726   mBufferSize.SizeTo(aFromDrawTarget->GetSize().width, aFromDrawTarget->GetSize().height);
  2727   RefPtr<gfx::DrawTarget> drawTarget = BeginUpdate(aNewSize, aDirtyRegion);
  2728   if (drawTarget) {
  2729     if (drawTarget != aFromDrawTarget) {
  2730       RefPtr<gfx::SourceSurface> source = aFromDrawTarget->Snapshot();
  2731       gfx::Rect rect(0, 0, aFromDrawTarget->GetSize().width, aFromDrawTarget->GetSize().height);
  2732       gfxUtils::ClipToRegion(drawTarget, GetUpdateRegion());
  2733       drawTarget->DrawSurface(source, rect, rect,
  2734                               gfx::DrawSurfaceOptions(),
  2735                               gfx::DrawOptions(1.0, gfx::CompositionOp::OP_SOURCE));
  2736       drawTarget->PopClip();
  2738     EndUpdate();
  2740   mUpdateDrawTarget = nullptr;
  2743 void
  2744 RectTextureImage::Draw(GLManager* aManager,
  2745                        const nsIntPoint& aLocation,
  2746                        const gfx3DMatrix& aTransform)
  2748   ShaderProgramOGL* program = aManager->GetProgram(LOCAL_GL_TEXTURE_RECTANGLE_ARB,
  2749                                                    gfx::SurfaceFormat::R8G8B8A8);
  2751   aManager->gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, mTexture);
  2753   program->Activate();
  2754   program->SetProjectionMatrix(aManager->GetProjMatrix());
  2755   program->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0), mUsedSize));
  2756   gfx::Matrix4x4 transform;
  2757   gfx::ToMatrix4x4(aTransform, transform);
  2758   program->SetLayerTransform(transform * gfx::Matrix4x4().Translate(aLocation.x, aLocation.y, 0));
  2759   program->SetTextureTransform(gfx::Matrix4x4());
  2760   program->SetRenderOffset(nsIntPoint(0, 0));
  2761   program->SetTexCoordMultiplier(mUsedSize.width, mUsedSize.height);
  2762   program->SetTextureUnit(0);
  2764   aManager->BindAndDrawQuad(program);
  2766   aManager->gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
  2769 // GLPresenter implementation
  2771 GLPresenter::GLPresenter(GLContext* aContext)
  2772  : mGLContext(aContext)
  2774   mGLContext->MakeCurrent();
  2775   ShaderConfigOGL config;
  2776   config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
  2777   mRGBARectProgram = new ShaderProgramOGL(mGLContext,
  2778     ProgramProfileOGL::GetProfileFor(config));
  2780   // Create mQuadVBO.
  2781   mGLContext->fGenBuffers(1, &mQuadVBO);
  2782   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
  2784   GLfloat vertices[] = {
  2785     /* First quad vertices */
  2786     0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
  2787     /* Then quad texcoords */
  2788     0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
  2789   };
  2790   mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER, sizeof(vertices), vertices, LOCAL_GL_STATIC_DRAW);
  2791   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
  2794 GLPresenter::~GLPresenter()
  2796   if (mQuadVBO) {
  2797     mGLContext->MakeCurrent();
  2798     mGLContext->fDeleteBuffers(1, &mQuadVBO);
  2799     mQuadVBO = 0;
  2803 void
  2804 GLPresenter::BindAndDrawQuad(ShaderProgramOGL *aProgram)
  2806   mGLContext->MakeCurrent();
  2808   GLuint vertAttribIndex = aProgram->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
  2809   GLuint texCoordAttribIndex = aProgram->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
  2811   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
  2812   mGLContext->fVertexAttribPointer(vertAttribIndex, 2,
  2813                                    LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
  2814                                    (GLvoid*)0);
  2815   mGLContext->fEnableVertexAttribArray(vertAttribIndex);
  2816   mGLContext->fVertexAttribPointer(texCoordAttribIndex, 2,
  2817                                    LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
  2818                                    (GLvoid*) (sizeof(float)*4*2));
  2819   mGLContext->fEnableVertexAttribArray(texCoordAttribIndex);
  2820   mGLContext->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
  2821   mGLContext->fDisableVertexAttribArray(vertAttribIndex);
  2822   mGLContext->fDisableVertexAttribArray(texCoordAttribIndex);
  2825 void
  2826 GLPresenter::BeginFrame(nsIntSize aRenderSize)
  2828   mGLContext->MakeCurrent();
  2830   mGLContext->fViewport(0, 0, aRenderSize.width, aRenderSize.height);
  2832   // Matrix to transform (0, 0, width, height) to viewport space (-1.0, 1.0,
  2833   // 2, 2) and flip the contents.
  2834   gfx::Matrix viewMatrix;
  2835   viewMatrix.Translate(-1.0, 1.0);
  2836   viewMatrix.Scale(2.0f / float(aRenderSize.width), 2.0f / float(aRenderSize.height));
  2837   viewMatrix.Scale(1.0f, -1.0f);
  2839   gfx::Matrix4x4 matrix3d = gfx::Matrix4x4::From2D(viewMatrix);
  2840   matrix3d._33 = 0.0f;
  2842   // set the projection matrix for the next time the program is activated
  2843   mProjMatrix = matrix3d;
  2845   // Default blend function implements "OVER"
  2846   mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
  2847                                  LOCAL_GL_ONE, LOCAL_GL_ONE);
  2848   mGLContext->fEnable(LOCAL_GL_BLEND);
  2850   mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
  2851   mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
  2853   mGLContext->fEnable(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
  2856 void
  2857 GLPresenter::EndFrame()
  2859   mGLContext->SwapBuffers();
  2860   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
  2863 #pragma mark -
  2865 @implementation ChildView
  2867 // globalDragPboard is non-null during native drag sessions that did not originate
  2868 // in our native NSView (it is set in |draggingEntered:|). It is unset when the
  2869 // drag session ends for this view, either with the mouse exiting or when a drop
  2870 // occurs in this view.
  2871 NSPasteboard* globalDragPboard = nil;
  2873 // gLastDragView and gLastDragMouseDownEvent are used to communicate information
  2874 // to the drag service during drag invocation (starting a drag in from the view).
  2875 // gLastDragView is only non-null while mouseDragged is on the call stack.
  2876 NSView* gLastDragView = nil;
  2877 NSEvent* gLastDragMouseDownEvent = nil;
  2879 + (void)initialize
  2881   static BOOL initialized = NO;
  2883   if (!initialized) {
  2884     // Inform the OS about the types of services (from the "Services" menu)
  2885     // that we can handle.
  2887     NSArray *sendTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil];
  2888     NSArray *returnTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil];
  2890     [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes];
  2892     [sendTypes release];
  2893     [returnTypes release];
  2895     initialized = YES;
  2899 + (void)registerViewForDraggedTypes:(NSView*)aView
  2901   [aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
  2902                                                            NSStringPboardType,
  2903                                                            NSHTMLPboardType,
  2904                                                            NSURLPboardType,
  2905                                                            NSFilesPromisePboardType,
  2906                                                            kWildcardPboardType,
  2907                                                            kCorePboardType_url,
  2908                                                            kCorePboardType_urld,
  2909                                                            kCorePboardType_urln,
  2910                                                            nil]];
  2913 // initWithFrame:geckoChild:
  2914 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
  2916   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  2918   if ((self = [super initWithFrame:inFrame])) {
  2919     mGeckoChild = inChild;
  2920     mIsPluginView = NO;
  2921 #ifndef NP_NO_CARBON
  2922     // We don't support the Carbon event model but it's still the default
  2923     // model for i386 per NPAPI.
  2924     mPluginEventModel = NPEventModelCarbon;
  2925 #else
  2926     mPluginEventModel = NPEventModelCocoa;
  2927 #endif
  2928 #ifndef NP_NO_QUICKDRAW
  2929     // We don't support the Quickdraw drawing model any more but it's still
  2930     // the default model for i386 per NPAPI.
  2931     mPluginDrawingModel = NPDrawingModelQuickDraw;
  2932 #else
  2933     mPluginDrawingModel = NPDrawingModelCoreGraphics;
  2934 #endif
  2935     mPendingDisplay = NO;
  2936     mBlockedLastMouseDown = NO;
  2937     mExpectingWheelStop = NO;
  2939     mLastMouseDownEvent = nil;
  2940     mClickThroughMouseDownEvent = nil;
  2941     mDragService = nullptr;
  2943     mGestureState = eGestureState_None;
  2944     mCumulativeMagnification = 0.0;
  2945     mCumulativeRotation = 0.0;
  2947     // We can't call forceRefreshOpenGL here because, in order to work around
  2948     // the bug, it seems we need to have a draw already happening. Therefore,
  2949     // we call it in drawRect:inContext:, when we know that a draw is in
  2950     // progress.
  2951     mDidForceRefreshOpenGL = NO;
  2953     [self setFocusRingType:NSFocusRingTypeNone];
  2955 #ifdef __LP64__
  2956     mCancelSwipeAnimation = nil;
  2957     mCurrentSwipeDir = 0;
  2958 #endif
  2960     mTopLeftCornerMask = NULL;
  2963   // register for things we'll take from other applications
  2964   [ChildView registerViewForDraggedTypes:self];
  2966   [[NSNotificationCenter defaultCenter] addObserver:self
  2967                                            selector:@selector(windowBecameMain:)
  2968                                                name:NSWindowDidBecomeMainNotification
  2969                                              object:nil];
  2970   [[NSNotificationCenter defaultCenter] addObserver:self
  2971                                            selector:@selector(windowResignedMain:)
  2972                                                name:NSWindowDidResignMainNotification
  2973                                              object:nil];
  2974   [[NSNotificationCenter defaultCenter] addObserver:self
  2975                                            selector:@selector(systemMetricsChanged)
  2976                                                name:NSControlTintDidChangeNotification
  2977                                              object:nil];
  2978   [[NSNotificationCenter defaultCenter] addObserver:self
  2979                                            selector:@selector(systemMetricsChanged)
  2980                                                name:NSSystemColorsDidChangeNotification
  2981                                              object:nil];
  2982   // TODO: replace the string with the constant once we build with the 10.7 SDK
  2983   [[NSNotificationCenter defaultCenter] addObserver:self
  2984                                            selector:@selector(scrollbarSystemMetricChanged)
  2985                                                name:@"NSPreferredScrollerStyleDidChangeNotification"
  2986                                              object:nil];
  2987   [[NSDistributedNotificationCenter defaultCenter] addObserver:self
  2988                                                       selector:@selector(systemMetricsChanged)
  2989                                                           name:@"AppleAquaScrollBarVariantChanged"
  2990                                                         object:nil
  2991                                             suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
  2992   [[NSNotificationCenter defaultCenter] addObserver:self
  2993                                            selector:@selector(_surfaceNeedsUpdate:)
  2994                                                name:NSViewGlobalFrameDidChangeNotification
  2995                                              object:self];
  2997   return self;
  2999   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  3002 - (void)installTextInputHandler:(TextInputHandler*)aHandler
  3004   mTextInputHandler = aHandler;
  3007 - (void)uninstallTextInputHandler
  3009   mTextInputHandler = nullptr;
  3012 // Work around bug 603134.
  3013 // OS X has a bug that causes new OpenGL windows to only paint once or twice,
  3014 // then stop painting altogether. By clearing the drawable from the GL context,
  3015 // and then resetting the view to ourselves, we convince OS X to start updating
  3016 // again.
  3017 // This can cause a flash in new windows - bug 631339 - but it's very hard to
  3018 // fix that while maintaining this workaround.
  3019 - (void)forceRefreshOpenGL
  3021   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3023   [mGLContext clearDrawable];
  3024   [self updateGLContext];
  3026   NS_OBJC_END_TRY_ABORT_BLOCK;
  3029 - (void)setGLContext:(NSOpenGLContext *)aGLContext
  3031   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3033   mGLContext = aGLContext;
  3034   [mGLContext retain];
  3036   NS_OBJC_END_TRY_ABORT_BLOCK;
  3039 - (bool)preRender:(NSOpenGLContext *)aGLContext
  3041   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3043   if (![self window] ||
  3044       ([[self window] isKindOfClass:[BaseWindow class]] &&
  3045        ![(BaseWindow*)[self window] isVisibleOrBeingShown])) {
  3046     // Before the window is shown, our GL context's front FBO is not
  3047     // framebuffer complete, so we refuse to render.
  3048     return false;
  3051   if (!mGLContext) {
  3052     [self setGLContext:aGLContext];
  3053     [self updateGLContext];
  3056   CGLLockContext((CGLContextObj)[aGLContext CGLContextObj]);
  3058   return true;
  3060   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
  3063 - (void)postRender:(NSOpenGLContext *)aGLContext
  3065   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3067   CGLUnlockContext((CGLContextObj)[aGLContext CGLContextObj]);
  3069   NS_OBJC_END_TRY_ABORT_BLOCK;
  3072 - (void)dealloc
  3074   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3076   [mGLContext release];
  3077   [mPendingDirtyRects release];
  3078   [mLastMouseDownEvent release];
  3079   [mClickThroughMouseDownEvent release];
  3080   CGImageRelease(mTopLeftCornerMask);
  3081   ChildViewMouseTracker::OnDestroyView(self);
  3083   [[NSNotificationCenter defaultCenter] removeObserver:self];
  3084   [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
  3086   [super dealloc];
  3088   NS_OBJC_END_TRY_ABORT_BLOCK;
  3091 - (void)updatePluginTopLevelWindowStatus:(BOOL)hasMain
  3093   if (!mGeckoChild)
  3094     return;
  3096   WidgetPluginEvent pluginEvent(true, NS_PLUGIN_FOCUS_EVENT, mGeckoChild);
  3097   NPCocoaEvent cocoaEvent;
  3098   nsCocoaUtils::InitNPCocoaEvent(&cocoaEvent);
  3099   cocoaEvent.type = NPCocoaEventWindowFocusChanged;
  3100   cocoaEvent.data.focus.hasFocus = hasMain;
  3101   nsCocoaUtils::InitPluginEvent(pluginEvent, cocoaEvent);
  3102   mGeckoChild->DispatchWindowEvent(pluginEvent);
  3105 - (void)windowBecameMain:(NSNotification*)inNotification
  3107   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3109   if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) {
  3110     if ((NSWindow*)[inNotification object] == [self window]) {
  3111       [self updatePluginTopLevelWindowStatus:YES];
  3115   NS_OBJC_END_TRY_ABORT_BLOCK;
  3118 - (void)windowResignedMain:(NSNotification*)inNotification
  3120   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3122   if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) {
  3123     if ((NSWindow*)[inNotification object] == [self window]) {
  3124       [self updatePluginTopLevelWindowStatus:NO];
  3128   NS_OBJC_END_TRY_ABORT_BLOCK;
  3131 - (void)widgetDestroyed
  3133   if (mTextInputHandler) {
  3134     mTextInputHandler->OnDestroyWidget(mGeckoChild);
  3135     mTextInputHandler = nullptr;
  3137   mGeckoChild = nullptr;
  3139   // Just in case we're destroyed abruptly and missed the draggingExited
  3140   // or performDragOperation message.
  3141   NS_IF_RELEASE(mDragService);
  3144 // mozView method, return our gecko child view widget. Note this does not AddRef.
  3145 - (nsIWidget*) widget
  3147   return static_cast<nsIWidget*>(mGeckoChild);
  3150 - (void)systemMetricsChanged
  3152   if (mGeckoChild)
  3153     mGeckoChild->NotifyThemeChanged();
  3156 - (void)scrollbarSystemMetricChanged
  3158   [self systemMetricsChanged];
  3160   if (mGeckoChild) {
  3161     nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
  3162     if (listener) {
  3163       listener->GetPresShell()->ReconstructFrames();
  3168 - (void)setNeedsPendingDisplay
  3170   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3172   mPendingFullDisplay = YES;
  3173   if (!mPendingDisplay) {
  3174     [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
  3175     mPendingDisplay = YES;
  3178   NS_OBJC_END_TRY_ABORT_BLOCK;
  3181 - (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect
  3183   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3185   if (!mPendingDirtyRects)
  3186     mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1];
  3187   [mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]];
  3188   if (!mPendingDisplay) {
  3189     [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
  3190     mPendingDisplay = YES;
  3193   NS_OBJC_END_TRY_ABORT_BLOCK;
  3196 // Clears the queue of any pending invalides
  3197 - (void)processPendingRedraws
  3199   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3201   if (mPendingFullDisplay) {
  3202     [self setNeedsDisplay:YES];
  3204   else if (mPendingDirtyRects) {
  3205     unsigned int count = [mPendingDirtyRects count];
  3206     for (unsigned int i = 0; i < count; ++i) {
  3207       [self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]];
  3210   mPendingFullDisplay = NO;
  3211   mPendingDisplay = NO;
  3212   [mPendingDirtyRects release];
  3213   mPendingDirtyRects = nil;
  3215   NS_OBJC_END_TRY_ABORT_BLOCK;
  3218 - (void)setNeedsDisplayInRect:(NSRect)aRect
  3220   if (![self isUsingOpenGL]) {
  3221     [super setNeedsDisplayInRect:aRect];
  3222     return;
  3225   if ([[self window] isVisible] && [self isUsingMainThreadOpenGL]) {
  3226     // Draw without calling drawRect. This prevent us from
  3227     // needing to access the normal window buffer surface unnecessarily, so we
  3228     // waste less time synchronizing the two surfaces. (These synchronizations
  3229     // show up in a profiler as CGSDeviceLock / _CGSLockWindow /
  3230     // _CGSSynchronizeWindowBackingStore.) It also means that Cocoa doesn't
  3231     // have any potentially expensive invalid rect management for us.
  3232     if (!mWaitingForPaint) {
  3233       mWaitingForPaint = YES;
  3234       // Use NSRunLoopCommonModes instead of the default NSDefaultRunLoopMode
  3235       // so that the timer also fires while a native menu is open.
  3236       [self performSelector:@selector(drawUsingOpenGLCallback)
  3237                  withObject:nil
  3238                  afterDelay:0
  3239                     inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
  3244 - (NSString*)description
  3246   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  3248   return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])];
  3250   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  3253 // Make the origin of this view the topLeft corner (gecko origin) rather
  3254 // than the bottomLeft corner (standard cocoa origin).
  3255 - (BOOL)isFlipped
  3257   return YES;
  3260 - (BOOL)isOpaque
  3262   return [[self window] isOpaque] && !mIsPluginView;
  3265 -(void)setIsPluginView:(BOOL)aIsPlugin
  3267   mIsPluginView = aIsPlugin;
  3270 -(BOOL)isPluginView
  3272   return mIsPluginView;
  3275 - (NSView *)hitTest:(NSPoint)aPoint
  3277   NSView* target = [super hitTest:aPoint];
  3278   if ((target == self) && [self isPluginView] && mGeckoChild) {
  3279     nsAutoRetainCocoaObject kungFuDeathGrip(self);
  3281     NSPoint cocoaLoc = [[self superview] convertPoint:aPoint toView:self];
  3282     LayoutDeviceIntPoint widgetLoc = LayoutDeviceIntPoint::FromUntyped(
  3283       mGeckoChild->CocoaPointsToDevPixels(cocoaLoc));
  3285     WidgetQueryContentEvent hitTest(true, NS_QUERY_DOM_WIDGET_HITTEST,
  3286                                     mGeckoChild);
  3287     hitTest.InitForQueryDOMWidgetHittest(widgetLoc);
  3288     // This might destroy our widget.
  3289     mGeckoChild->DispatchWindowEvent(hitTest);
  3290     if (!mGeckoChild) {
  3291       return nil;
  3293     if (hitTest.mSucceeded && !hitTest.mReply.mWidgetIsHit) {
  3294       return nil;
  3297   return target;
  3300 // Are we processing an NSLeftMouseDown event that will fail to click through?
  3301 // If so, we shouldn't focus or unfocus a plugin.
  3302 - (BOOL)isInFailingLeftClickThrough
  3304   if (!mGeckoChild)
  3305     return NO;
  3307   if (!mClickThroughMouseDownEvent ||
  3308       [mClickThroughMouseDownEvent type] != NSLeftMouseDown)
  3309     return NO;
  3311   BOOL retval =
  3312     !ChildViewMouseTracker::WindowAcceptsEvent([self window],
  3313                                                mClickThroughMouseDownEvent,
  3314                                                self, true);
  3316   // If we return YES here, this will result in us not being focused,
  3317   // which will stop us receiving mClickThroughMouseDownEvent in
  3318   // [ChildView mouseDown:].  So we need to release and null-out
  3319   // mClickThroughMouseDownEvent here.
  3320   if (retval) {
  3321     [mClickThroughMouseDownEvent release];
  3322     mClickThroughMouseDownEvent = nil;
  3325   return retval;
  3328 - (void)setPluginEventModel:(NPEventModel)eventModel
  3330   mPluginEventModel = eventModel;
  3333 - (void)setPluginDrawingModel:(NPDrawingModel)drawingModel
  3335   mPluginDrawingModel = drawingModel;
  3338 - (NPEventModel)pluginEventModel
  3340   return mPluginEventModel;
  3343 - (NPDrawingModel)pluginDrawingModel
  3345   return mPluginDrawingModel;
  3348 - (void)sendFocusEvent:(uint32_t)eventType
  3350   if (!mGeckoChild)
  3351     return;
  3353   nsEventStatus status = nsEventStatus_eIgnore;
  3354   WidgetGUIEvent focusGuiEvent(true, eventType, mGeckoChild);
  3355   focusGuiEvent.time = PR_IntervalNow();
  3356   mGeckoChild->DispatchEvent(&focusGuiEvent, status);
  3359 // We accept key and mouse events, so don't keep passing them up the chain. Allow
  3360 // this to be a 'focused' widget for event dispatch.
  3361 - (BOOL)acceptsFirstResponder
  3363   return YES;
  3366 // Accept mouse down events on background windows
  3367 - (BOOL)acceptsFirstMouse:(NSEvent*)aEvent
  3369   if (![[self window] isKindOfClass:[PopupWindow class]]) {
  3370     // We rely on this function to tell us that the mousedown was on a
  3371     // background window. Inside mouseDown we can't tell whether we were
  3372     // inactive because at that point we've already been made active.
  3373     // Unfortunately, acceptsFirstMouse is called for PopupWindows even when
  3374     // their parent window is active, so ignore this on them for now.
  3375     mClickThroughMouseDownEvent = [aEvent retain];
  3377   return YES;
  3380 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
  3382   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3384   if (!newWindow)
  3385     HideChildPluginViews(self);
  3387   [super viewWillMoveToWindow:newWindow];
  3389   NS_OBJC_END_TRY_ABORT_BLOCK;
  3392 - (void)viewDidMoveToWindow
  3394   if (mPluginEventModel == NPEventModelCocoa &&
  3395       [self window] && [self isPluginView] && mGeckoChild) {
  3396     mGeckoChild->UpdatePluginPort();
  3399   [super viewDidMoveToWindow];
  3402 - (void)scrollRect:(NSRect)aRect by:(NSSize)offset
  3404   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3406   // Update any pending dirty rects to reflect the new scroll position
  3407   if (mPendingDirtyRects) {
  3408     unsigned int count = [mPendingDirtyRects count];
  3409     for (unsigned int i = 0; i < count; ++i) {
  3410       NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue];
  3411       NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height);
  3412       [mPendingDirtyRects replaceObjectAtIndex:i
  3413                                     withObject:[NSValue valueWithRect:newRect]];
  3416   [super scrollRect:aRect by:offset];
  3418   NS_OBJC_END_TRY_ABORT_BLOCK;
  3421 - (BOOL)mouseDownCanMoveWindow
  3423   return [[self window] isMovableByWindowBackground];
  3426 -(void)updateGLContext
  3428   if (mGLContext) {
  3429     CGLLockContext((CGLContextObj)[mGLContext CGLContextObj]);
  3430     [mGLContext setView:self];
  3431     [mGLContext update];
  3432     CGLUnlockContext((CGLContextObj)[mGLContext CGLContextObj]);
  3436 - (void)_surfaceNeedsUpdate:(NSNotification*)notification
  3438    [self updateGLContext];
  3441 - (BOOL)wantsBestResolutionOpenGLSurface
  3443   return nsCocoaUtils::HiDPIEnabled() ? YES : NO;
  3446 - (void)viewDidChangeBackingProperties
  3448   [super viewDidChangeBackingProperties];
  3449   if (mGeckoChild) {
  3450     // actually, it could be the color space that's changed,
  3451     // but we can't tell the difference here except by retrieving
  3452     // the backing scale factor and comparing to the old value
  3453     mGeckoChild->BackingScaleFactorChanged();
  3457 - (BOOL)isCoveringTitlebar
  3459   return [[self window] isKindOfClass:[BaseWindow class]] &&
  3460          [(BaseWindow*)[self window] mainChildView] == self &&
  3461          [(BaseWindow*)[self window] drawsContentsIntoWindowFrame];
  3464 - (nsIntRegion)nativeDirtyRegionWithBoundingRect:(NSRect)aRect
  3466   nsIntRect boundingRect = mGeckoChild->CocoaPointsToDevPixels(aRect);
  3467   const NSRect *rects;
  3468   NSInteger count;
  3469   [self getRectsBeingDrawn:&rects count:&count];
  3471   if (count > MAX_RECTS_IN_REGION) {
  3472     return boundingRect;
  3475   nsIntRegion region;
  3476   for (NSInteger i = 0; i < count; ++i) {
  3477     region.Or(region, mGeckoChild->CocoaPointsToDevPixels(rects[i]));
  3479   region.And(region, boundingRect);
  3480   return region;
  3483 // The display system has told us that a portion of our view is dirty. Tell
  3484 // gecko to paint it
  3485 - (void)drawRect:(NSRect)aRect
  3487   CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
  3488   [self drawRect:aRect inContext:cgContext];
  3490   // If we're a transparent window and our contents have changed, we need
  3491   // to make sure the shadow is updated to the new contents.
  3492   if ([[self window] isKindOfClass:[BaseWindow class]]) {
  3493     [(BaseWindow*)[self window] deferredInvalidateShadow];
  3497 - (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext
  3499   if (!mGeckoChild || !mGeckoChild->IsVisible())
  3500     return;
  3502   // Don't ever draw plugin views explicitly; they'll be drawn as part of their parent widget.
  3503   if (mIsPluginView)
  3504     return;
  3506 #ifdef DEBUG_UPDATE
  3507   nsIntRect geckoBounds;
  3508   mGeckoChild->GetBounds(geckoBounds);
  3510   fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n  gecko bounds: [%d %d %d %d]\n",
  3511            self, mGeckoChild,
  3512            aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, aContext,
  3513            geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
  3515   CGAffineTransform xform = CGContextGetCTM(aContext);
  3516   fprintf (stderr, "  xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
  3517 #endif
  3519   if ([self isUsingOpenGL]) {
  3520     // For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is
  3521     // directly called from a delayed perform callback - without going through
  3522     // drawRect.
  3523     // Paints that come through here are triggered by something that Cocoa
  3524     // controls, for example by window resizing or window focus changes.
  3526     // Since this view is usually declared as opaque, the window's pixel
  3527     // buffer may now contain garbage which we need to prevent from reaching
  3528     // the screen. The only place where garbage can show is in the window
  3529     // corners - the rest of the window is covered by opaque content in our
  3530     // OpenGL surface.
  3531     // So we need to clear the pixel buffer contents in the corners.
  3532     [self clearCorners];
  3534     // Do GL composition and return.
  3535     [self drawUsingOpenGL];
  3536     return;
  3539   PROFILER_LABEL("widget", "ChildView::drawRect");
  3541   // The CGContext that drawRect supplies us with comes with a transform that
  3542   // scales one user space unit to one Cocoa point, which can consist of
  3543   // multiple dev pixels. But Gecko expects its supplied context to be scaled
  3544   // to device pixels, so we need to reverse the scaling.
  3545   double scale = mGeckoChild->BackingScaleFactor();
  3546   CGContextSaveGState(aContext);
  3547   CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale);
  3549   NSSize viewSize = [self bounds].size;
  3550   nsIntSize backingSize(viewSize.width * scale, viewSize.height * scale);
  3552   CGContextSaveGState(aContext);
  3554   nsIntRegion region = [self nativeDirtyRegionWithBoundingRect:aRect];
  3556   // Create Cairo objects.
  3557   nsRefPtr<gfxQuartzSurface> targetSurface =
  3558     new gfxQuartzSurface(aContext, backingSize);
  3559   targetSurface->SetAllowUseAsSource(false);
  3561   nsRefPtr<gfxContext> targetContext;
  3562   if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(gfx::BackendType::CAIRO)) {
  3563     RefPtr<gfx::DrawTarget> dt =
  3564       gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface,
  3565                                                              gfx::IntSize(backingSize.width,
  3566                                                                           backingSize.height));
  3567     dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
  3568     targetContext = new gfxContext(dt);
  3569   } else if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(gfx::BackendType::COREGRAPHICS)) {
  3570     RefPtr<gfx::DrawTarget> dt =
  3571       gfx::Factory::CreateDrawTargetForCairoCGContext(aContext,
  3572                                                       gfx::IntSize(backingSize.width,
  3573                                                                    backingSize.height));
  3574     dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
  3575     targetContext = new gfxContext(dt);
  3576   } else {
  3577     targetContext = new gfxContext(targetSurface);
  3580   // Set up the clip region.
  3581   nsIntRegionRectIterator iter(region);
  3582   targetContext->NewPath();
  3583   for (;;) {
  3584     const nsIntRect* r = iter.Next();
  3585     if (!r)
  3586       break;
  3587     targetContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
  3589   targetContext->Clip();
  3591   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  3592   bool painted = false;
  3593   if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
  3594     nsBaseWidget::AutoLayerManagerSetup
  3595       setupLayerManager(mGeckoChild, targetContext, BufferMode::BUFFER_NONE);
  3596     painted = mGeckoChild->PaintWindow(region);
  3597   } else if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
  3598     // We only need this so that we actually get DidPaintWindow fired
  3599     painted = mGeckoChild->PaintWindow(region);
  3602   targetContext = nullptr;
  3603   targetSurface = nullptr;
  3605   CGContextRestoreGState(aContext);
  3607   // Undo the scale transform so that from now on the context is in
  3608   // CocoaPoints again.
  3609   CGContextRestoreGState(aContext);
  3611   if (!painted && [self isOpaque]) {
  3612     // Gecko refused to draw, but we've claimed to be opaque, so we have to
  3613     // draw something--fill with white.
  3614     CGContextSetRGBFillColor(aContext, 1, 1, 1, 1);
  3615     CGContextFillRect(aContext, NSRectToCGRect(aRect));
  3618   if ([self isCoveringTitlebar]) {
  3619     [self drawTitleString];
  3620     [self drawTitlebarHighlight];
  3621     [self maskTopCornersInContext:aContext];
  3624 #ifdef DEBUG_UPDATE
  3625   fprintf (stderr, "---- update done ----\n");
  3627 #if 0
  3628   CGContextSetRGBStrokeColor (aContext,
  3629                             ((((unsigned long)self) & 0xff)) / 255.0,
  3630                             ((((unsigned long)self) & 0xff00) >> 8) / 255.0,
  3631                             ((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
  3632                             0.5);
  3633 #endif
  3634   CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8);
  3635   CGContextSetLineWidth(aContext, 4.0);
  3636   CGContextStrokeRect(aContext, NSRectToCGRect(aRect));
  3637 #endif
  3640 - (BOOL)isUsingMainThreadOpenGL
  3642   if (!mGeckoChild || ![self window])
  3643     return NO;
  3645   return mGeckoChild->GetLayerManager(nullptr)->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL;
  3648 - (BOOL)isUsingOpenGL
  3650   if (!mGeckoChild || ![self window])
  3651     return NO;
  3653   return mGLContext || mUsingOMTCompositor || [self isUsingMainThreadOpenGL];
  3656 - (void)drawUsingOpenGL
  3658   PROFILER_LABEL("widget", "ChildView::drawUsingOpenGL");
  3660   if (![self isUsingOpenGL] || !mGeckoChild->IsVisible())
  3661     return;
  3663   mWaitingForPaint = NO;
  3665   nsIntRect geckoBounds;
  3666   mGeckoChild->GetBounds(geckoBounds);
  3667   nsIntRegion region(geckoBounds);
  3669   mGeckoChild->PaintWindow(region);
  3671   // Force OpenGL to refresh the very first time we draw. This works around a
  3672   // Mac OS X bug that stops windows updating on OS X when we use OpenGL.
  3673   if (!mDidForceRefreshOpenGL) {
  3674     [self performSelector:@selector(forceRefreshOpenGL) withObject:nil afterDelay:0];
  3675     mDidForceRefreshOpenGL = YES;
  3679 // Called asynchronously after setNeedsDisplay in order to avoid entering the
  3680 // normal drawing machinery.
  3681 - (void)drawUsingOpenGLCallback
  3683   if (mWaitingForPaint) {
  3684     [self drawUsingOpenGL];
  3688 - (BOOL)hasRoundedBottomCorners
  3690   return [[self window] respondsToSelector:@selector(bottomCornerRounded)] &&
  3691   [[self window] bottomCornerRounded];
  3694 - (CGFloat)cornerRadius
  3696   NSView* frameView = [[[self window] contentView] superview];
  3697   if (!frameView || ![frameView respondsToSelector:@selector(roundedCornerRadius)])
  3698     return 4.0f;
  3699   return [frameView roundedCornerRadius];
  3702 // Accelerated windows have two NSSurfaces:
  3703 //  (1) The window's pixel buffer in the back and
  3704 //  (2) the OpenGL view in the front.
  3705 // These two surfaces are composited by the window manager. Drawing into the
  3706 // CGContext which is provided by drawRect ends up in (1).
  3707 // When our window has rounded corners, the OpenGL view has transparent pixels
  3708 // in the corners. In these places the contents of the window's pixel buffer
  3709 // can show through. So we need to make sure that the pixel buffer is
  3710 // transparent in the corners so that no garbage reaches the screen.
  3711 // The contents of the pixel buffer in the rest of the window don't matter
  3712 // because they're covered by opaque pixels of the OpenGL context.
  3713 // Making the corners transparent works even though our window is
  3714 // declared "opaque" (in the NSWindow's isOpaque method).
  3715 - (void)clearCorners
  3717   CGFloat radius = [self cornerRadius];
  3718   CGFloat w = [self bounds].size.width, h = [self bounds].size.height;
  3719   [[NSColor clearColor] set];
  3721   if ([self isCoveringTitlebar]) {
  3722     NSRectFill(NSMakeRect(0, 0, radius, radius));
  3723     NSRectFill(NSMakeRect(w - radius, 0, radius, radius));
  3726   if ([self hasRoundedBottomCorners]) {
  3727     NSRectFill(NSMakeRect(0, h - radius, radius, radius));
  3728     NSRectFill(NSMakeRect(w - radius, h - radius, radius, radius));
  3732 // This is the analog of nsChildView::MaybeDrawRoundedCorners for CGContexts.
  3733 // We only need to mask the top corners here because Cocoa does the masking
  3734 // for the window's bottom corners automatically (starting with 10.7).
  3735 - (void)maskTopCornersInContext:(CGContextRef)aContext
  3737   CGFloat radius = [self cornerRadius];
  3738   int32_t devPixelCornerRadius = mGeckoChild->CocoaPointsToDevPixels(radius);
  3740   // First make sure that mTopLeftCornerMask is set up.
  3741   if (!mTopLeftCornerMask ||
  3742       int32_t(CGImageGetWidth(mTopLeftCornerMask)) != devPixelCornerRadius) {
  3743     CGImageRelease(mTopLeftCornerMask);
  3744     CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
  3745     CGContextRef imgCtx = CGBitmapContextCreate(NULL,
  3746                                                 devPixelCornerRadius,
  3747                                                 devPixelCornerRadius,
  3748                                                 8, devPixelCornerRadius * 4,
  3749                                                 rgb, kCGImageAlphaPremultipliedFirst);
  3750     CGColorSpaceRelease(rgb);
  3751     DrawTopLeftCornerMask(imgCtx, devPixelCornerRadius);
  3752     mTopLeftCornerMask = CGBitmapContextCreateImage(imgCtx);
  3753     CGContextRelease(imgCtx);
  3756   // kCGBlendModeDestinationIn is the secret sauce which allows us to erase
  3757   // already painted pixels. It's defined as R = D * Sa: multiply all channels
  3758   // of the destination pixel with the alpha of the source pixel. In our case,
  3759   // the source is mTopLeftCornerMask.
  3760   CGContextSaveGState(aContext);
  3761   CGContextSetBlendMode(aContext, kCGBlendModeDestinationIn);
  3763   CGRect destRect = CGRectMake(0, 0, radius, radius);
  3765   // Erase the top left corner...
  3766   CGContextDrawImage(aContext, destRect, mTopLeftCornerMask);
  3768   // ... and the top right corner.
  3769   CGContextTranslateCTM(aContext, [self bounds].size.width, 0);
  3770   CGContextScaleCTM(aContext, -1, 1);
  3771   CGContextDrawImage(aContext, destRect, mTopLeftCornerMask);
  3773   CGContextRestoreGState(aContext);
  3776 - (void)drawTitleString
  3778   BaseWindow* window = (BaseWindow*)[self window];
  3779   if (![window wantsTitleDrawn]) {
  3780     return;
  3783   NSView* frameView = [[window contentView] superview];
  3784   if (![frameView respondsToSelector:@selector(_drawTitleBar:)]) {
  3785     return;
  3788   NSGraphicsContext* oldContext = [NSGraphicsContext currentContext];
  3789   CGContextRef ctx = (CGContextRef)[oldContext graphicsPort];
  3790   CGContextSaveGState(ctx);
  3791   if ([oldContext isFlipped] != [frameView isFlipped]) {
  3792     CGContextTranslateCTM(ctx, 0, [self bounds].size.height);
  3793     CGContextScaleCTM(ctx, 1, -1);
  3795   [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:[frameView isFlipped]]];
  3796   [frameView _drawTitleBar:[frameView bounds]];
  3797   CGContextRestoreGState(ctx);
  3798   [NSGraphicsContext setCurrentContext:oldContext];
  3801 - (void)drawTitlebarHighlight
  3803   DrawTitlebarHighlight([self bounds].size, [self cornerRadius],
  3804                         mGeckoChild->DevPixelsToCocoaPoints(1));
  3807 - (void)releaseWidgets:(NSArray*)aWidgetArray
  3809   if (!aWidgetArray) {
  3810     return;
  3812   NSInteger count = [aWidgetArray count];
  3813   for (NSInteger i = 0; i < count; ++i) {
  3814     NSNumber* pointer = (NSNumber*) [aWidgetArray objectAtIndex:i];
  3815     nsIWidget* widget = (nsIWidget*) [pointer unsignedIntegerValue];
  3816     NS_RELEASE(widget);
  3820 - (void)viewWillDraw
  3822   if (mGeckoChild) {
  3823     // The OS normally *will* draw our NSWindow, no matter what we do here.
  3824     // But Gecko can delete our parent widget(s) (along with mGeckoChild)
  3825     // while processing a paint request, which closes our NSWindow and
  3826     // makes the OS throw an NSInternalInconsistencyException assertion when
  3827     // it tries to draw it.  Sometimes the OS also aborts the browser process.
  3828     // So we need to retain our parent(s) here and not release it/them until
  3829     // the next time through the main thread's run loop.  When we do this we
  3830     // also need to retain and release mGeckoChild, which holds a strong
  3831     // reference to us (otherwise we might have been deleted by the time
  3832     // releaseWidgets: is called on us).  See bug 550392.
  3833     nsIWidget* parent = mGeckoChild->GetParent();
  3834     if (parent) {
  3835       NSMutableArray* widgetArray = [NSMutableArray arrayWithCapacity:3];
  3836       while (parent) {
  3837         NS_ADDREF(parent);
  3838         [widgetArray addObject:[NSNumber numberWithUnsignedInteger:(NSUInteger)parent]];
  3839         parent = parent->GetParent();
  3841       NS_ADDREF(mGeckoChild);
  3842       [widgetArray addObject:[NSNumber numberWithUnsignedInteger:(NSUInteger)mGeckoChild]];
  3843       [self performSelector:@selector(releaseWidgets:)
  3844                  withObject:widgetArray
  3845                  afterDelay:0];
  3848     if ([self isUsingOpenGL]) {
  3849       // When our view covers the titlebar, we need to repaint the titlebar
  3850       // texture buffer when, for example, the window buttons are hovered.
  3851       // So we notify our nsChildView about any areas needing repainting.
  3852       mGeckoChild->NotifyDirtyRegion([self nativeDirtyRegionWithBoundingRect:[self bounds]]);
  3854       if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
  3855         ClientLayerManager *manager = static_cast<ClientLayerManager*>(mGeckoChild->GetLayerManager());
  3856         manager->AsShadowForwarder()->WindowOverlayChanged();
  3860     mGeckoChild->WillPaintWindow();
  3862   [super viewWillDraw];
  3865 #if USE_CLICK_HOLD_CONTEXTMENU
  3866 //
  3867 // -clickHoldCallback:
  3868 //
  3869 // called from a timer two seconds after a mouse down to see if we should display
  3870 // a context menu (click-hold). |anEvent| is the original mouseDown event. If we're
  3871 // still in that mouseDown by this time, put up the context menu, otherwise just
  3872 // fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback
  3873 // fires so we're ok there.
  3874 //
  3875 // This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314)
  3876 // so removing it until we get it straightened out.
  3877 //
  3878 - (void)clickHoldCallback:(id)theEvent;
  3880   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3882   if( theEvent == [NSApp currentEvent] ) {
  3883     // we're still in the middle of the same mousedown event here, activate
  3884     // click-hold context menu by triggering the right mouseDown action.
  3885     NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown
  3886                                                   location:[theEvent locationInWindow]
  3887                                              modifierFlags:[theEvent modifierFlags]
  3888                                                  timestamp:[theEvent timestamp]
  3889                                               windowNumber:[theEvent windowNumber]
  3890                                                    context:[theEvent context]
  3891                                                eventNumber:[theEvent eventNumber]
  3892                                                 clickCount:[theEvent clickCount]
  3893                                                   pressure:[theEvent pressure]];
  3894     [self rightMouseDown:clickHoldEvent];
  3897   NS_OBJC_END_TRY_ABORT_BLOCK;
  3899 #endif
  3901 // If we've just created a non-native context menu, we need to mark it as
  3902 // such and let the OS (and other programs) know when it opens and closes
  3903 // (this is how the OS knows to close other programs' context menus when
  3904 // ours open).  We send the initial notification here, but others are sent
  3905 // in nsCocoaWindow::Show().
  3906 - (void)maybeInitContextMenuTracking
  3908   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3910 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
  3911   return;
  3912 #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
  3914   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
  3915   NS_ENSURE_TRUE_VOID(rollupListener);
  3916   nsCOMPtr<nsIWidget> widget = rollupListener->GetRollupWidget();
  3917   NS_ENSURE_TRUE_VOID(widget);
  3919   NSWindow *popupWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
  3920   if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
  3921     return;
  3923   [[NSDistributedNotificationCenter defaultCenter]
  3924     postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
  3925                   object:@"org.mozilla.gecko.PopupWindow"];
  3926   [(PopupWindow*)popupWindow setIsContextMenu:YES];
  3928   NS_OBJC_END_TRY_ABORT_BLOCK;
  3931 // Returns true if the event should no longer be processed, false otherwise.
  3932 // This does not return whether or not anything was rolled up.
  3933 - (BOOL)maybeRollup:(NSEvent*)theEvent
  3935   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3937   BOOL consumeEvent = NO;
  3939   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
  3940   NS_ENSURE_TRUE(rollupListener, false);
  3941   nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
  3942   if (rollupWidget) {
  3943     NSWindow* currentPopup = static_cast<NSWindow*>(rollupWidget->GetNativeData(NS_NATIVE_WINDOW));
  3944     if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
  3945       // event is not over the rollup window, default is to roll up
  3946       bool shouldRollup = true;
  3948       // check to see if scroll events should roll up the popup
  3949       if ([theEvent type] == NSScrollWheel) {
  3950         shouldRollup = rollupListener->ShouldRollupOnMouseWheelEvent();
  3951         // consume scroll events that aren't over the popup
  3952         // unless the popup is an arrow panel
  3953         consumeEvent = rollupListener->ShouldConsumeOnMouseWheelEvent();
  3956       // if we're dealing with menus, we probably have submenus and
  3957       // we don't want to rollup if the click is in a parent menu of
  3958       // the current submenu
  3959       uint32_t popupsToRollup = UINT32_MAX;
  3960       nsAutoTArray<nsIWidget*, 5> widgetChain;
  3961       uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
  3962       for (uint32_t i = 0; i < widgetChain.Length(); i++) {
  3963         nsIWidget* widget = widgetChain[i];
  3964         NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
  3965         if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
  3966           // don't roll up if the mouse event occurred within a menu of the
  3967           // same type. If the mouse event occurred in a menu higher than
  3968           // that, roll up, but pass the number of popups to Rollup so
  3969           // that only those of the same type close up.
  3970           if (i < sameTypeCount) {
  3971             shouldRollup = false;
  3973           else {
  3974             popupsToRollup = sameTypeCount;
  3976           break;
  3980       if (shouldRollup) {
  3981         if ([theEvent type] == NSLeftMouseDown) {
  3982           NSPoint point = [NSEvent mouseLocation];
  3983           FlipCocoaScreenCoordinate(point);
  3984           nsIntPoint pos(point.x, point.y);
  3985           consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, &pos, nullptr);
  3987         else {
  3988           consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, nullptr, nullptr);
  3994   return consumeEvent;
  3996   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  3999 /*
  4000  * In OS X Mountain Lion and above, smart zoom gestures are implemented in
  4001  * smartMagnifyWithEvent. In OS X Lion, they are implemented in
  4002  * magnifyWithEvent. See inline comments for more info.
  4004  * The prototypes swipeWithEvent, beginGestureWithEvent, magnifyWithEvent,
  4005  * smartMagnifyWithEvent, rotateWithEvent, and endGestureWithEvent were
  4006  * obtained from the following links:
  4007  * https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSResponder_Class/Reference/Reference.html
  4008  * https://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html
  4009  */
  4011 - (void)swipeWithEvent:(NSEvent *)anEvent
  4013   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4015   if (!anEvent || !mGeckoChild)
  4016     return;
  4018   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4020   float deltaX = [anEvent deltaX];  // left=1.0, right=-1.0
  4021   float deltaY = [anEvent deltaY];  // up=1.0, down=-1.0
  4023   // Setup the "swipe" event.
  4024   WidgetSimpleGestureEvent geckoEvent(true, NS_SIMPLE_GESTURE_SWIPE,
  4025                                       mGeckoChild);
  4026   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
  4028   // Record the left/right direction.
  4029   if (deltaX > 0.0)
  4030     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
  4031   else if (deltaX < 0.0)
  4032     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
  4034   // Record the up/down direction.
  4035   if (deltaY > 0.0)
  4036     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_UP;
  4037   else if (deltaY < 0.0)
  4038     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_DOWN;
  4040   // Send the event.
  4041   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4043   NS_OBJC_END_TRY_ABORT_BLOCK;
  4046 - (void)beginGestureWithEvent:(NSEvent *)anEvent
  4048   if (!anEvent)
  4049     return;
  4051   mGestureState = eGestureState_StartGesture;
  4052   mCumulativeMagnification = 0;
  4053   mCumulativeRotation = 0.0;
  4056 - (void)magnifyWithEvent:(NSEvent *)anEvent
  4058   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4060   if (!anEvent || !mGeckoChild)
  4061     return;
  4063   /*
  4064    * In OS X 10.7.* (Lion), smart zoom events come through magnifyWithEvent,
  4065    * instead of smartMagnifyWithEvent. See bug 863841.
  4066    */
  4067   if ([ChildView isLionSmartMagnifyEvent: anEvent]) {
  4068     [self smartMagnifyWithEvent: anEvent];
  4069     return;
  4072   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4074   float deltaZ = [anEvent deltaZ];
  4076   uint32_t msg;
  4077   switch (mGestureState) {
  4078   case eGestureState_StartGesture:
  4079     msg = NS_SIMPLE_GESTURE_MAGNIFY_START;
  4080     mGestureState = eGestureState_MagnifyGesture;
  4081     break;
  4083   case eGestureState_MagnifyGesture:
  4084     msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE;
  4085     break;
  4087   case eGestureState_None:
  4088   case eGestureState_RotateGesture:
  4089   default:
  4090     return;
  4093   // Setup the event.
  4094   WidgetSimpleGestureEvent geckoEvent(true, msg, mGeckoChild);
  4095   geckoEvent.delta = deltaZ;
  4096   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
  4098   // Send the event.
  4099   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4101   // Keep track of the cumulative magnification for the final "magnify" event.
  4102   mCumulativeMagnification += deltaZ;
  4104   NS_OBJC_END_TRY_ABORT_BLOCK;
  4107 - (void)smartMagnifyWithEvent:(NSEvent *)anEvent
  4109   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4111   if (!anEvent || !mGeckoChild) {
  4112     return;
  4115   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4117   // Setup the "double tap" event.
  4118   WidgetSimpleGestureEvent geckoEvent(true, NS_SIMPLE_GESTURE_TAP,
  4119                                       mGeckoChild);
  4120   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
  4121   geckoEvent.clickCount = 1;
  4123   // Send the event.
  4124   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4126   // Clear the gesture state
  4127   mGestureState = eGestureState_None;
  4129   NS_OBJC_END_TRY_ABORT_BLOCK;
  4132 - (void)rotateWithEvent:(NSEvent *)anEvent
  4134   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4136   if (!anEvent || !mGeckoChild)
  4137     return;
  4139   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4141   float rotation = [anEvent rotation];
  4143   uint32_t msg;
  4144   switch (mGestureState) {
  4145   case eGestureState_StartGesture:
  4146     msg = NS_SIMPLE_GESTURE_ROTATE_START;
  4147     mGestureState = eGestureState_RotateGesture;
  4148     break;
  4150   case eGestureState_RotateGesture:
  4151     msg = NS_SIMPLE_GESTURE_ROTATE_UPDATE;
  4152     break;
  4154   case eGestureState_None:
  4155   case eGestureState_MagnifyGesture:
  4156   default:
  4157     return;
  4160   // Setup the event.
  4161   WidgetSimpleGestureEvent geckoEvent(true, msg, mGeckoChild);
  4162   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
  4163   geckoEvent.delta = -rotation;
  4164   if (rotation > 0.0) {
  4165     geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE;
  4166   } else {
  4167     geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE;
  4170   // Send the event.
  4171   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4173   // Keep track of the cumulative rotation for the final "rotate" event.
  4174   mCumulativeRotation += rotation;
  4176   NS_OBJC_END_TRY_ABORT_BLOCK;
  4179 - (void)endGestureWithEvent:(NSEvent *)anEvent
  4181   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4183   if (!anEvent || !mGeckoChild) {
  4184     // Clear the gestures state if we cannot send an event.
  4185     mGestureState = eGestureState_None;
  4186     mCumulativeMagnification = 0.0;
  4187     mCumulativeRotation = 0.0;
  4188     return;
  4191   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4193   switch (mGestureState) {
  4194   case eGestureState_MagnifyGesture:
  4196       // Setup the "magnify" event.
  4197       WidgetSimpleGestureEvent geckoEvent(true, NS_SIMPLE_GESTURE_MAGNIFY,
  4198                                           mGeckoChild);
  4199       geckoEvent.delta = mCumulativeMagnification;
  4200       [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
  4202       // Send the event.
  4203       mGeckoChild->DispatchWindowEvent(geckoEvent);
  4205     break;
  4207   case eGestureState_RotateGesture:
  4209       // Setup the "rotate" event.
  4210       WidgetSimpleGestureEvent geckoEvent(true, NS_SIMPLE_GESTURE_ROTATE,
  4211                                           mGeckoChild);
  4212       [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
  4213       geckoEvent.delta = -mCumulativeRotation;
  4214       if (mCumulativeRotation > 0.0) {
  4215         geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE;
  4216       } else {
  4217         geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE;
  4220       // Send the event.
  4221       mGeckoChild->DispatchWindowEvent(geckoEvent);
  4223     break;
  4225   case eGestureState_None:
  4226   case eGestureState_StartGesture:
  4227   default:
  4228     break;
  4231   // Clear the gestures state.
  4232   mGestureState = eGestureState_None;
  4233   mCumulativeMagnification = 0.0;
  4234   mCumulativeRotation = 0.0;
  4236   NS_OBJC_END_TRY_ABORT_BLOCK;
  4239 + (BOOL)isLionSmartMagnifyEvent:(NSEvent*)anEvent
  4241   /*
  4242    * On Lion, smart zoom events have type NSEventTypeGesture, subtype 0x16,
  4243    * whereas pinch zoom events have type NSEventTypeMagnify. So, use that to
  4244    * discriminate between the two. Smart zoom gestures do not call
  4245    * beginGestureWithEvent or endGestureWithEvent, so mGestureState is not
  4246    * changed. Documentation couldn't be found for the meaning of the subtype
  4247    * 0x16, but it will probably never change. See bug 863841.
  4248    */
  4249   return nsCocoaFeatures::OnLionOrLater() &&
  4250          !nsCocoaFeatures::OnMountainLionOrLater() &&
  4251          [anEvent type] == NSEventTypeGesture &&
  4252          [anEvent subtype] == 0x16;
  4255 #ifdef __LP64__
  4256 - (bool)sendSwipeEvent:(NSEvent*)aEvent
  4257                 withKind:(uint32_t)aMsg
  4258        allowedDirections:(uint32_t*)aAllowedDirections
  4259                direction:(uint32_t)aDirection
  4260                    delta:(double)aDelta
  4262   if (!mGeckoChild)
  4263     return false;
  4265   WidgetSimpleGestureEvent geckoEvent(true, aMsg, mGeckoChild);
  4266   geckoEvent.direction = aDirection;
  4267   geckoEvent.delta = aDelta;
  4268   geckoEvent.allowedDirections = *aAllowedDirections;
  4269   [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
  4270   bool eventCancelled = mGeckoChild->DispatchWindowEvent(geckoEvent);
  4271   *aAllowedDirections = geckoEvent.allowedDirections;
  4272   return eventCancelled; // event cancelled == swipe should start
  4275 - (void)sendSwipeEndEvent:(NSEvent *)anEvent
  4276         allowedDirections:(uint32_t)aAllowedDirections
  4278     // Tear down animation overlay by sending a swipe end event.
  4279     uint32_t allowedDirectionsCopy = aAllowedDirections;
  4280     [self sendSwipeEvent:anEvent
  4281                 withKind:NS_SIMPLE_GESTURE_SWIPE_END
  4282        allowedDirections:&allowedDirectionsCopy
  4283                direction:0
  4284                    delta:0.0];
  4287 // Support fluid swipe tracking on OS X 10.7 and higher. We must be careful
  4288 // to only invoke this support on a two-finger gesture that really
  4289 // is a swipe (and not a scroll) -- in other words, the app is responsible
  4290 // for deciding which is which. But once the decision is made, the OS tracks
  4291 // the swipe until it has finished, and decides whether or not it succeeded.
  4292 // A horizontal swipe has the same functionality as the Back and Forward
  4293 // buttons.
  4294 // This method is partly based on Apple sample code available at
  4295 // developer.apple.com/library/mac/#releasenotes/Cocoa/AppKitOlderNotes.html
  4296 // (under Fluid Swipe Tracking API).
  4297 - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
  4298                      scrollOverflowX:(double)anOverflowX
  4299                      scrollOverflowY:(double)anOverflowY
  4300               viewPortIsOverscrolled:(BOOL)aViewPortIsOverscrolled
  4302   if (!nsCocoaFeatures::OnLionOrLater()) {
  4303     return;
  4306   // This method checks whether the AppleEnableSwipeNavigateWithScrolls global
  4307   // preference is set.  If it isn't, fluid swipe tracking is disabled, and a
  4308   // horizontal two-finger gesture is always a scroll (even in Safari).  This
  4309   // preference can't (currently) be set from the Preferences UI -- only using
  4310   // 'defaults write'.
  4311   if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) {
  4312     return;
  4315   // We should only track scroll events as swipe if the viewport is being
  4316   // overscrolled.
  4317   if (!aViewPortIsOverscrolled) {
  4318     return;
  4321   // Verify that this is a scroll wheel event with proper phase to be tracked
  4322   // by the OS.
  4323   if ([anEvent type] != NSScrollWheel || [anEvent phase] == NSEventPhaseNone) {
  4324     return;
  4327   // Only initiate tracking if the user has tried to scroll past the edge of
  4328   // the current page (as indicated by 'anOverflowX' or 'anOverflowY' being
  4329   // non-zero). Gecko only sets WidgetMouseScrollEvent.scrollOverflow when it's
  4330   // processing NS_MOUSE_PIXEL_SCROLL events (not NS_MOUSE_SCROLL events).
  4331   if (anOverflowX == 0.0 && anOverflowY == 0.0) {
  4332     return;
  4335   CGFloat deltaX, deltaY;
  4336   if ([anEvent hasPreciseScrollingDeltas]) {
  4337     deltaX = [anEvent scrollingDeltaX];
  4338     deltaY = [anEvent scrollingDeltaY];
  4339   } else {
  4340     return;
  4343   uint32_t vDirs = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_DOWN |
  4344                    (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_UP;
  4345   uint32_t direction = 0;
  4347   // Only initiate horizontal tracking for events whose horizontal element is
  4348   // at least eight times larger than its vertical element. This minimizes
  4349   // performance problems with vertical scrolls (by minimizing the possibility
  4350   // that they'll be misinterpreted as horizontal swipes), while still
  4351   // tolerating a small vertical element to a true horizontal swipe.  The number
  4352   // '8' was arrived at by trial and error.
  4353   if (anOverflowX != 0.0 && deltaX != 0.0 &&
  4354       fabsf(deltaX) > fabsf(deltaY) * 8) {
  4355     // Only initiate horizontal tracking for gestures that have just begun --
  4356     // otherwise a scroll to one side of the page can have a swipe tacked on
  4357     // to it.
  4358     if ([anEvent phase] != NSEventPhaseBegan) {
  4359       return;
  4362     if (deltaX < 0.0) {
  4363       direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
  4364     } else {
  4365       direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
  4368   // Only initiate vertical tracking for events whose vertical element is
  4369   // at least two times larger than its horizontal element. This minimizes
  4370   // performance problems. The number '2' was arrived at by trial and error.
  4371   else if (anOverflowY != 0.0 && deltaY != 0.0 &&
  4372            fabsf(deltaY) > fabsf(deltaX) * 2) {
  4373     if (deltaY < 0.0) {
  4374       direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_DOWN;
  4375     } else {
  4376       direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_UP;
  4379     if ((mCurrentSwipeDir & vDirs) && (mCurrentSwipeDir != direction)) {
  4380       // If a swipe is currently being tracked kill it -- it's been interrupted
  4381       // by another gesture event.
  4382       if (mCancelSwipeAnimation && *mCancelSwipeAnimation == NO) {
  4383         *mCancelSwipeAnimation = YES;
  4384         mCancelSwipeAnimation = nil;
  4385         [self sendSwipeEndEvent:anEvent allowedDirections:0];
  4387       return;
  4389   } else {
  4390     return;
  4393   // Track the direction we're going in.
  4394   mCurrentSwipeDir = direction;
  4396   uint32_t allowedDirections = 0;
  4397   // We're ready to start the animation. Tell Gecko about it, and at the same
  4398   // time ask it if it really wants to start an animation for this event.
  4399   // This event also reports back the directions that we can swipe in.
  4400   bool shouldStartSwipe = [self sendSwipeEvent:anEvent
  4401                                       withKind:NS_SIMPLE_GESTURE_SWIPE_START
  4402                              allowedDirections:&allowedDirections
  4403                                      direction:direction
  4404                                          delta:0.0];
  4406   if (!shouldStartSwipe) {
  4407     return;
  4410   // If a swipe is currently being tracked kill it -- it's been interrupted
  4411   // by another gesture event.
  4412   if (mCancelSwipeAnimation && *mCancelSwipeAnimation == NO) {
  4413     *mCancelSwipeAnimation = YES;
  4414     mCancelSwipeAnimation = nil;
  4417   CGFloat min = 0.0;
  4418   CGFloat max = 0.0;
  4419   if (!(direction & vDirs)) {
  4420     min = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ?
  4421           -1.0 : 0.0;
  4422     max = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ?
  4423           1.0 : 0.0;
  4426   __block BOOL animationCanceled = NO;
  4427   __block BOOL geckoSwipeEventSent = NO;
  4428   // At this point, anEvent is the first scroll wheel event in a two-finger
  4429   // horizontal gesture that we've decided to treat as a swipe.  When we call
  4430   // [NSEvent trackSwipeEventWithOptions:...], the OS interprets all
  4431   // subsequent scroll wheel events that are part of this gesture as a swipe,
  4432   // and stops sending them to us.  The OS calls the trackingHandler "block"
  4433   // multiple times, asynchronously (sometimes after [NSEvent
  4434   // maybeTrackScrollEventAsSwipe:...] has returned).  The OS determines when
  4435   // the gesture has finished, and whether or not it was "successful" -- this
  4436   // information is passed to trackingHandler.  We must be careful to only
  4437   // call [NSEvent maybeTrackScrollEventAsSwipe:...] on a "real" swipe --
  4438   // otherwise two-finger scrolling performance will suffer significantly.
  4439   // Note that we use anEvent inside the block. This extends the lifetime of
  4440   // the anEvent object because it's retained by the block, see bug 682445.
  4441   // The block will release it when the block goes away at the end of the
  4442   // animation, or when the animation is canceled.
  4443   [anEvent trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection |
  4444                                       NSEventSwipeTrackingClampGestureAmount
  4445              dampenAmountThresholdMin:min
  4446                                   max:max
  4447                          usingHandler:^(CGFloat gestureAmount,
  4448                                         NSEventPhase phase,
  4449                                         BOOL isComplete,
  4450                                         BOOL *stop) {
  4451     uint32_t allowedDirectionsCopy = allowedDirections;
  4452     // Since this tracking handler can be called asynchronously, mGeckoChild
  4453     // might have become NULL here (our child widget might have been
  4454     // destroyed).
  4455     // Checking for gestureAmount == 0.0 also works around bug 770626, which
  4456     // happens when DispatchWindowEvent() triggers a modal dialog, which spins
  4457     // the event loop and confuses the OS. This results in several re-entrant
  4458     // calls to this handler.
  4459     if (animationCanceled || !mGeckoChild || gestureAmount == 0.0) {
  4460       *stop = YES;
  4461       animationCanceled = YES;
  4462       if (gestureAmount == 0.0 ||
  4463           ((direction & vDirs) && (direction != mCurrentSwipeDir))) {
  4464         if (mCancelSwipeAnimation)
  4465           *mCancelSwipeAnimation = YES;
  4466         mCancelSwipeAnimation = nil;
  4467         [self sendSwipeEndEvent:anEvent
  4468               allowedDirections:allowedDirectionsCopy];
  4470       mCurrentSwipeDir = 0;
  4471       return;
  4474     // Update animation overlay to match gestureAmount.
  4475     [self sendSwipeEvent:anEvent
  4476                 withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE
  4477        allowedDirections:&allowedDirectionsCopy
  4478                direction:0.0
  4479                    delta:gestureAmount];
  4481     if (phase == NSEventPhaseEnded && !geckoSwipeEventSent) {
  4482       // The result of the swipe is now known, so the main event can be sent.
  4483       // The animation might continue even after this event was sent, so
  4484       // don't tear down the animation overlay yet.
  4486       uint32_t directionCopy = direction;
  4488       // gestureAmount is documented to be '-1', '0' or '1' when isComplete
  4489       // is TRUE, but the docs don't say anything about its value at other
  4490       // times.  However, tests show that, when phase == NSEventPhaseEnded,
  4491       // gestureAmount is negative when it will be '-1' at isComplete, and
  4492       // positive when it will be '1'.  And phase is never equal to
  4493       // NSEventPhaseEnded when gestureAmount will be '0' at isComplete.
  4494       geckoSwipeEventSent = YES;
  4495       [self sendSwipeEvent:anEvent
  4496                   withKind:NS_SIMPLE_GESTURE_SWIPE
  4497          allowedDirections:&allowedDirectionsCopy
  4498                  direction:directionCopy
  4499                      delta:0.0];
  4502     if (isComplete) {
  4503       [self sendSwipeEndEvent:anEvent allowedDirections:allowedDirectionsCopy];
  4504       mCurrentSwipeDir = 0;
  4505       mCancelSwipeAnimation = nil;
  4507   }];
  4509   mCancelSwipeAnimation = &animationCanceled;
  4511 #endif // #ifdef __LP64__
  4513 - (void)setUsingOMTCompositor:(BOOL)aUseOMTC
  4515   mUsingOMTCompositor = aUseOMTC;
  4519 // Returning NO from this method only disallows ordering on mousedown - in order
  4520 // to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering]
  4521 // when handling the mousedown event.
  4522 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)aEvent
  4524   // Always using system-provided window ordering for normal windows.
  4525   if (![[self window] isKindOfClass:[PopupWindow class]])
  4526     return NO;
  4528   // Don't reorder when we don't have a parent window, like when we're a
  4529   // context menu or a tooltip.
  4530   return ![[self window] parentWindow];
  4533 - (void)mouseDown:(NSEvent*)theEvent
  4535   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4537   if ([self shouldDelayWindowOrderingForEvent:theEvent]) {
  4538     [NSApp preventWindowOrdering];
  4541   // If we've already seen this event due to direct dispatch from menuForEvent:
  4542   // just bail; if not, remember it.
  4543   if (mLastMouseDownEvent == theEvent) {
  4544     [mLastMouseDownEvent release];
  4545     mLastMouseDownEvent = nil;
  4546     return;
  4548   else {
  4549     [mLastMouseDownEvent release];
  4550     mLastMouseDownEvent = [theEvent retain];
  4553   [gLastDragMouseDownEvent release];
  4554   gLastDragMouseDownEvent = [theEvent retain];
  4556   // We need isClickThrough because at this point the window we're in might
  4557   // already have become main, so the check for isMainWindow in
  4558   // WindowAcceptsEvent isn't enough. It also has to check isClickThrough.
  4559   BOOL isClickThrough = (theEvent == mClickThroughMouseDownEvent);
  4560   [mClickThroughMouseDownEvent release];
  4561   mClickThroughMouseDownEvent = nil;
  4563   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4565   if ([self maybeRollup:theEvent] ||
  4566       !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self, isClickThrough)) {
  4567     // Remember blocking because that means we want to block mouseup as well.
  4568     mBlockedLastMouseDown = YES;
  4569     return;
  4572 #if USE_CLICK_HOLD_CONTEXTMENU
  4573   // fire off timer to check for click-hold after two seconds. retains |theEvent|
  4574   [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
  4575 #endif
  4577   // in order to send gecko events we'll need a gecko widget
  4578   if (!mGeckoChild)
  4579     return;
  4581   NSUInteger modifierFlags = [theEvent modifierFlags];
  4583   WidgetMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_DOWN, mGeckoChild,
  4584                               WidgetMouseEvent::eReal);
  4585   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4587   NSInteger clickCount = [theEvent clickCount];
  4588   if (mBlockedLastMouseDown && clickCount > 1) {
  4589     // Don't send a double click if the first click of the double click was
  4590     // blocked.
  4591     clickCount--;
  4593   geckoEvent.clickCount = clickCount;
  4595   if (modifierFlags & NSControlKeyMask)
  4596     geckoEvent.button = WidgetMouseEvent::eRightButton;
  4597   else
  4598     geckoEvent.button = WidgetMouseEvent::eLeftButton;
  4600   // Create event for use by plugins.
  4601   // This is going to our child view so we don't need to look up the destination
  4602   // event type.
  4603   NPCocoaEvent cocoaEvent;
  4604   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4605                                            NPCocoaEventMouseDown,
  4606                                            &cocoaEvent);
  4607   // Don't lose possible changes made above to clickCount
  4608   cocoaEvent.data.mouse.clickCount = clickCount;
  4610   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4611   mBlockedLastMouseDown = NO;
  4613   // XXX maybe call markedTextSelectionChanged:client: here?
  4615   NS_OBJC_END_TRY_ABORT_BLOCK;
  4618 - (void)mouseUp:(NSEvent *)theEvent
  4620   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4622   if (!mGeckoChild || mBlockedLastMouseDown)
  4623     return;
  4625   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4627   NPCocoaEvent cocoaEvent;
  4629   WidgetMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_UP, mGeckoChild,
  4630                               WidgetMouseEvent::eReal);
  4631   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4632   if ([theEvent modifierFlags] & NSControlKeyMask)
  4633     geckoEvent.button = WidgetMouseEvent::eRightButton;
  4634   else
  4635     geckoEvent.button = WidgetMouseEvent::eLeftButton;
  4637   // Create event for use by plugins.
  4638   // This is going to our child view so we don't need to look up the destination
  4639   // event type.
  4640   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4641                                            NPCocoaEventMouseUp,
  4642                                            &cocoaEvent);
  4644   // This might destroy our widget (and null out mGeckoChild).
  4645   bool defaultPrevented = mGeckoChild->DispatchWindowEvent(geckoEvent);
  4647   // Check to see if we are double-clicking in the titlebar.
  4648   CGFloat locationInTitlebar = [[self window] frame].size.height - [theEvent locationInWindow].y;
  4649   if (!defaultPrevented && [theEvent clickCount] == 2 &&
  4650       [[self window] isMovableByWindowBackground] &&
  4651       [self shouldMinimizeOnTitlebarDoubleClick] &&
  4652       [[self window] isKindOfClass:[ToolbarWindow class]] &&
  4653       (locationInTitlebar < [(ToolbarWindow*)[self window] titlebarHeight] ||
  4654        locationInTitlebar < [(ToolbarWindow*)[self window] unifiedToolbarHeight])) {
  4656     NSButton *minimizeButton = [[self window] standardWindowButton:NSWindowMiniaturizeButton];
  4657     [minimizeButton performClick:self];
  4660   // If our mouse-up event's location is over some other object (as might
  4661   // happen if it came at the end of a dragging operation), also send our
  4662   // Gecko frame a mouse-exit event.
  4663   if (mGeckoChild && mIsPluginView) {
  4664     ChildViewMouseTracker::ReEvaluateMouseEnterState(theEvent, self);
  4667   NS_OBJC_END_TRY_ABORT_BLOCK;
  4670 - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
  4671                             enter:(BOOL)aEnter
  4672                              type:(WidgetMouseEvent::exitType)aType
  4674   if (!mGeckoChild)
  4675     return;
  4677   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, [self window]);
  4678   NSPoint localEventLocation = [self convertPoint:windowEventLocation fromView:nil];
  4680   uint32_t msg = aEnter ? NS_MOUSE_ENTER : NS_MOUSE_EXIT;
  4681   WidgetMouseEvent event(true, msg, mGeckoChild, WidgetMouseEvent::eReal);
  4682   event.refPoint = LayoutDeviceIntPoint::FromUntyped(
  4683     mGeckoChild->CocoaPointsToDevPixels(localEventLocation));
  4685   // Create event for use by plugins.
  4686   // This is going to our child view so we don't need to look up the destination
  4687   // event type.
  4688   NPCocoaEvent cocoaEvent;
  4689   ChildViewMouseTracker::AttachPluginEvent(event, self, aEvent,
  4690                                            (msg == NS_MOUSE_ENTER) ?
  4691                                              NPCocoaEventMouseEntered : NPCocoaEventMouseExited,
  4692                                            &cocoaEvent);
  4694   event.exit = aType;
  4696   nsEventStatus status; // ignored
  4697   mGeckoChild->DispatchEvent(&event, status);
  4700 - (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent
  4702   if (!theEvent || !mGeckoChild) {
  4703     return;
  4706   nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
  4707   if (!windowWidget) {
  4708     return;
  4711   // We assume later on that sending a hit test event won't cause widget destruction.
  4712   WidgetMouseEvent hitTestEvent(true, NS_MOUSE_MOZHITTEST, mGeckoChild,
  4713                                 WidgetMouseEvent::eReal);
  4714   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&hitTestEvent];
  4715   bool result = mGeckoChild->DispatchWindowEvent(hitTestEvent);
  4717   [windowWidget->GetCocoaWindow() setMovableByWindowBackground:result];
  4720 - (void)handleMouseMoved:(NSEvent*)theEvent
  4722   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4724   if (!mGeckoChild)
  4725     return;
  4727   WidgetMouseEvent geckoEvent(true, NS_MOUSE_MOVE, mGeckoChild,
  4728                               WidgetMouseEvent::eReal);
  4729   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4731   // Create event for use by plugins.
  4732   // This is going to our child view so we don't need to look up the destination
  4733   // event type.
  4734   NPCocoaEvent cocoaEvent;
  4735   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4736                                            NPCocoaEventMouseMoved,
  4737                                            &cocoaEvent);
  4738   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4740   NS_OBJC_END_TRY_ABORT_BLOCK;
  4743 - (void)mouseDragged:(NSEvent*)theEvent
  4745   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4747   if (!mGeckoChild)
  4748     return;
  4750   gLastDragView = self;
  4752   NPCocoaEvent cocoaEvent;
  4754   WidgetMouseEvent geckoEvent(true, NS_MOUSE_MOVE, mGeckoChild,
  4755                               WidgetMouseEvent::eReal);
  4756   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4758   // create event for use by plugins
  4759   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4760                                            NPCocoaEventMouseDragged,
  4761                                            &cocoaEvent);
  4763   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4765   // Note, sending the above event might have destroyed our widget since we didn't retain.
  4766   // Fine so long as we don't access any local variables from here on.
  4767   gLastDragView = nil;
  4769   // XXX maybe call markedTextSelectionChanged:client: here?
  4771   NS_OBJC_END_TRY_ABORT_BLOCK;
  4774 - (void)rightMouseDown:(NSEvent *)theEvent
  4776   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4778   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4780   [self maybeRollup:theEvent];
  4781   if (!mGeckoChild)
  4782     return;
  4784   // The right mouse went down, fire off a right mouse down event to gecko
  4785   WidgetMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_DOWN, mGeckoChild,
  4786                               WidgetMouseEvent::eReal);
  4787   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4788   geckoEvent.button = WidgetMouseEvent::eRightButton;
  4789   geckoEvent.clickCount = [theEvent clickCount];
  4791   // create event for use by plugins
  4792   NPCocoaEvent cocoaEvent;
  4793   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4794                                            NPCocoaEventMouseDown,
  4795                                            &cocoaEvent);
  4797   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4798   if (!mGeckoChild)
  4799     return;
  4801   // Let the superclass do the context menu stuff.
  4802   [super rightMouseDown:theEvent];
  4804   NS_OBJC_END_TRY_ABORT_BLOCK;
  4807 - (void)rightMouseUp:(NSEvent *)theEvent
  4809   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4811   if (!mGeckoChild)
  4812     return;
  4814   NPCocoaEvent cocoaEvent;
  4816   WidgetMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_UP, mGeckoChild,
  4817                               WidgetMouseEvent::eReal);
  4818   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4819   geckoEvent.button = WidgetMouseEvent::eRightButton;
  4820   geckoEvent.clickCount = [theEvent clickCount];
  4822   // create event for use by plugins
  4823   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4824                                            NPCocoaEventMouseUp,
  4825                                            &cocoaEvent);
  4827   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4828   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4830   // If our mouse-up event's location is over some other object (as might
  4831   // happen if it came at the end of a dragging operation), also send our
  4832   // Gecko frame a mouse-exit event.
  4833   if (mGeckoChild && mIsPluginView) {
  4834     ChildViewMouseTracker::ReEvaluateMouseEnterState(theEvent, self);
  4837   NS_OBJC_END_TRY_ABORT_BLOCK;
  4840 - (void)rightMouseDragged:(NSEvent*)theEvent
  4842   if (!mGeckoChild)
  4843     return;
  4845   WidgetMouseEvent geckoEvent(true, NS_MOUSE_MOVE, mGeckoChild,
  4846                               WidgetMouseEvent::eReal);
  4847   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4848   geckoEvent.button = WidgetMouseEvent::eRightButton;
  4850   // create event for use by plugins
  4851   NPCocoaEvent cocoaEvent;
  4852   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4853                                            NPCocoaEventMouseDragged,
  4854                                            &cocoaEvent);
  4856   // send event into Gecko by going directly to the
  4857   // the widget.
  4858   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4861 - (void)otherMouseDown:(NSEvent *)theEvent
  4863   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4865   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4867   if ([self maybeRollup:theEvent] ||
  4868       !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self))
  4869     return;
  4871   if (!mGeckoChild)
  4872     return;
  4874   WidgetMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_DOWN, mGeckoChild,
  4875                               WidgetMouseEvent::eReal);
  4876   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4877   geckoEvent.button = WidgetMouseEvent::eMiddleButton;
  4878   geckoEvent.clickCount = [theEvent clickCount];
  4880   // create event for use by plugins
  4881   NPCocoaEvent cocoaEvent;
  4882   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4883                                            NPCocoaEventMouseDown,
  4884                                            &cocoaEvent);
  4886   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4888   NS_OBJC_END_TRY_ABORT_BLOCK;
  4891 - (void)otherMouseUp:(NSEvent *)theEvent
  4893   if (!mGeckoChild)
  4894     return;
  4896   WidgetMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_UP, mGeckoChild,
  4897                               WidgetMouseEvent::eReal);
  4898   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4899   geckoEvent.button = WidgetMouseEvent::eMiddleButton;
  4901   // create event for use by plugins
  4902   NPCocoaEvent cocoaEvent;
  4903   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4904                                            NPCocoaEventMouseUp,
  4905                                            &cocoaEvent);
  4907   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4908   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4910   // If our mouse-up event's location is over some other object (as might
  4911   // happen if it came at the end of a dragging operation), also send our
  4912   // Gecko frame a mouse-exit event.
  4913   if (mGeckoChild && mIsPluginView) {
  4914     ChildViewMouseTracker::ReEvaluateMouseEnterState(theEvent, self);
  4918 - (void)otherMouseDragged:(NSEvent*)theEvent
  4920   if (!mGeckoChild)
  4921     return;
  4923   WidgetMouseEvent geckoEvent(true, NS_MOUSE_MOVE, mGeckoChild,
  4924                               WidgetMouseEvent::eReal);
  4925   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  4926   geckoEvent.button = WidgetMouseEvent::eMiddleButton;
  4928   // create event for use by plugins
  4929   NPCocoaEvent cocoaEvent;
  4930   ChildViewMouseTracker::AttachPluginEvent(geckoEvent, self, theEvent,
  4931                                            NPCocoaEventMouseDragged,
  4932                                            &cocoaEvent);
  4934   // send event into Gecko by going directly to the
  4935   // the widget.
  4936   mGeckoChild->DispatchWindowEvent(geckoEvent);
  4939 static int32_t RoundUp(double aDouble)
  4941   return aDouble < 0 ? static_cast<int32_t>(floor(aDouble)) :
  4942                        static_cast<int32_t>(ceil(aDouble));
  4945 - (void)sendWheelStartOrStop:(uint32_t)msg forEvent:(NSEvent *)theEvent
  4947   WidgetWheelEvent wheelEvent(true, msg, mGeckoChild);
  4948   [self convertCocoaMouseWheelEvent:theEvent toGeckoEvent:&wheelEvent];
  4949   mExpectingWheelStop = (msg == NS_WHEEL_START);
  4950   mGeckoChild->DispatchWindowEvent(wheelEvent);
  4953 - (void)sendWheelCondition:(BOOL)condition first:(uint32_t)first second:(uint32_t)second forEvent:(NSEvent *)theEvent
  4955   if (mExpectingWheelStop == condition) {
  4956     [self sendWheelStartOrStop:first forEvent:theEvent];
  4958   [self sendWheelStartOrStop:second forEvent:theEvent];
  4961 - (void)scrollWheel:(NSEvent*)theEvent
  4963   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4965   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4967   ChildViewMouseTracker::MouseScrolled(theEvent);
  4969   if ([self maybeRollup:theEvent]) {
  4970     return;
  4973   if (!mGeckoChild) {
  4974     return;
  4977   if (nsCocoaFeatures::OnLionOrLater()) {
  4978     NSEventPhase phase = [theEvent phase];
  4979     // Fire NS_WHEEL_START/STOP events when 2 fingers touch/release the touchpad.
  4980     if (phase & NSEventPhaseMayBegin) {
  4981       [self sendWheelCondition:YES first:NS_WHEEL_STOP second:NS_WHEEL_START forEvent:theEvent];
  4982       return;
  4985     if (phase & (NSEventPhaseEnded | NSEventPhaseCancelled)) {
  4986       [self sendWheelCondition:NO first:NS_WHEEL_START second:NS_WHEEL_STOP forEvent:theEvent];
  4987       return;
  4991   WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, mGeckoChild);
  4992   [self convertCocoaMouseWheelEvent:theEvent toGeckoEvent:&wheelEvent];
  4994   wheelEvent.lineOrPageDeltaX = RoundUp(-[theEvent deltaX]);
  4995   wheelEvent.lineOrPageDeltaY = RoundUp(-[theEvent deltaY]);
  4997   if (wheelEvent.deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
  4998     // Some scrolling devices supports pixel scrolling, e.g. a Macbook
  4999     // touchpad or a Mighty Mouse. On those devices, [theEvent deviceDeltaX/Y]
  5000     // contains the amount of pixels to scroll. Since Lion this has changed
  5001     // to [theEvent scrollingDeltaX/Y].
  5002     double scale = mGeckoChild->BackingScaleFactor();
  5003     if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
  5004       wheelEvent.deltaX = -[theEvent scrollingDeltaX] * scale;
  5005       wheelEvent.deltaY = -[theEvent scrollingDeltaY] * scale;
  5006     } else {
  5007       wheelEvent.deltaX = -[theEvent deviceDeltaX] * scale;
  5008       wheelEvent.deltaY = -[theEvent deviceDeltaY] * scale;
  5010   } else {
  5011     wheelEvent.deltaX = -[theEvent deltaX];
  5012     wheelEvent.deltaY = -[theEvent deltaY];
  5015   // TODO: We should not set deltaZ for now because we're not sure if we should
  5016   //       revert the sign.
  5017   // wheelEvent.deltaZ = [theEvent deltaZ];
  5019   if (!wheelEvent.deltaX && !wheelEvent.deltaY && !wheelEvent.deltaZ) {
  5020     // No sense in firing off a Gecko event.
  5021     return;
  5024   NPCocoaEvent cocoaEvent;
  5025   ChildViewMouseTracker::AttachPluginEvent(wheelEvent, self, theEvent,
  5026                                            NPCocoaEventScrollWheel,
  5027                                            &cocoaEvent);
  5029   mGeckoChild->DispatchWindowEvent(wheelEvent);
  5030   if (!mGeckoChild) {
  5031     return;
  5034 #ifdef __LP64__
  5035   // overflowDeltaX and overflowDeltaY tell us when the user has tried to
  5036   // scroll past the edge of a page (in those cases it's non-zero).
  5037   if ((wheelEvent.deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) &&
  5038       (wheelEvent.deltaX != 0.0 || wheelEvent.deltaY != 0.0)) {
  5039     [self maybeTrackScrollEventAsSwipe:theEvent
  5040                        scrollOverflowX:wheelEvent.overflowDeltaX
  5041                        scrollOverflowY:wheelEvent.overflowDeltaY
  5042                 viewPortIsOverscrolled:wheelEvent.mViewPortIsOverscrolled];
  5044 #endif // #ifdef __LP64__
  5046   NS_OBJC_END_TRY_ABORT_BLOCK;
  5049 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
  5051   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  5053   if (!mGeckoChild || [self isPluginView])
  5054     return nil;
  5056   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5058   [self maybeRollup:theEvent];
  5059   if (!mGeckoChild)
  5060     return nil;
  5062   // Cocoa doesn't always dispatch a mouseDown: for a control-click event,
  5063   // depends on what we return from menuForEvent:. Gecko always expects one
  5064   // and expects the mouse down event before the context menu event, so
  5065   // get that event sent first if this is a left mouse click.
  5066   if ([theEvent type] == NSLeftMouseDown) {
  5067     [self mouseDown:theEvent];
  5068     if (!mGeckoChild)
  5069       return nil;
  5072   WidgetMouseEvent geckoEvent(true, NS_CONTEXTMENU, mGeckoChild,
  5073                               WidgetMouseEvent::eReal);
  5074   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  5075   geckoEvent.button = WidgetMouseEvent::eRightButton;
  5076   mGeckoChild->DispatchWindowEvent(geckoEvent);
  5077   if (!mGeckoChild)
  5078     return nil;
  5080   [self maybeInitContextMenuTracking];
  5082   // Go up our view chain to fetch the correct menu to return.
  5083   return [self contextMenu];
  5085   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  5088 - (NSMenu*)contextMenu
  5090   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  5092   NSView* superView = [self superview];
  5093   if ([superView respondsToSelector:@selector(contextMenu)])
  5094     return [(NSView<mozView>*)superView contextMenu];
  5096   return nil;
  5098   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  5101 - (void) convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
  5102                         toGeckoEvent:(WidgetWheelEvent*)outWheelEvent
  5104   [self convertCocoaMouseEvent:aMouseEvent toGeckoEvent:outWheelEvent];
  5105   outWheelEvent->deltaMode =
  5106     Preferences::GetBool("mousewheel.enable_pixel_scrolling", true) ?
  5107       nsIDOMWheelEvent::DOM_DELTA_PIXEL : nsIDOMWheelEvent::DOM_DELTA_LINE;
  5109   // Calling deviceDeltaX or deviceDeltaY on theEvent will trigger a Cocoa
  5110   // assertion and an Objective-C NSInternalInconsistencyException if the
  5111   // underlying "Carbon" event doesn't contain pixel scrolling information.
  5112   // For these events, carbonEventKind is kEventMouseWheelMoved instead of
  5113   // kEventMouseScroll.
  5114   if (outWheelEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
  5115     EventRef theCarbonEvent = [aMouseEvent _eventRef];
  5116     UInt32 carbonEventKind = theCarbonEvent ? ::GetEventKind(theCarbonEvent) : 0;
  5117     if (carbonEventKind != kEventMouseScroll) {
  5118       outWheelEvent->deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
  5121   outWheelEvent->isMomentum = nsCocoaUtils::IsMomentumScrollEvent(aMouseEvent);
  5124 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent
  5125                    toGeckoEvent:(WidgetInputEvent*)outGeckoEvent
  5127   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5129   NS_ASSERTION(outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null aoutGeckoEvent");
  5130   if (!outGeckoEvent)
  5131     return;
  5133   nsCocoaUtils::InitInputEvent(*outGeckoEvent, aMouseEvent);
  5135   // convert point to view coordinate system
  5136   NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(aMouseEvent, [self window]);
  5137   NSPoint localPoint = [self convertPoint:locationInWindow fromView:nil];
  5139   outGeckoEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(
  5140     mGeckoChild->CocoaPointsToDevPixels(localPoint));
  5142   WidgetMouseEventBase* mouseEvent = outGeckoEvent->AsMouseEventBase();
  5143   mouseEvent->buttons = 0;
  5144   NSUInteger mouseButtons = [NSEvent pressedMouseButtons];
  5146   if (mouseButtons & 0x01) {
  5147     mouseEvent->buttons |= WidgetMouseEvent::eLeftButtonFlag;
  5149   if (mouseButtons & 0x02) {
  5150     mouseEvent->buttons |= WidgetMouseEvent::eRightButtonFlag;
  5152   if (mouseButtons & 0x04) {
  5153     mouseEvent->buttons |= WidgetMouseEvent::eMiddleButtonFlag;
  5155   if (mouseButtons & 0x08) {
  5156     mouseEvent->buttons |= WidgetMouseEvent::e4thButtonFlag;
  5158   if (mouseButtons & 0x10) {
  5159     mouseEvent->buttons |= WidgetMouseEvent::e5thButtonFlag;
  5162   switch ([aMouseEvent type]) {
  5163     case NSLeftMouseDown:
  5164     case NSLeftMouseUp:
  5165     case NSLeftMouseDragged:
  5166     case NSRightMouseDown:
  5167     case NSRightMouseUp:
  5168     case NSRightMouseDragged:
  5169     case NSOtherMouseDown:
  5170     case NSOtherMouseUp:
  5171     case NSOtherMouseDragged:
  5172       if ([aMouseEvent subtype] == NSTabletPointEventSubtype) {
  5173         mouseEvent->pressure = [aMouseEvent pressure];
  5175       break;
  5178   NS_OBJC_END_TRY_ABORT_BLOCK;
  5182 #pragma mark -
  5183 // NSTextInput implementation
  5185 - (void)insertText:(id)insertString
  5187   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5189   NS_ENSURE_TRUE_VOID(mGeckoChild);
  5191   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5193   NSAttributedString* attrStr;
  5194   if ([insertString isKindOfClass:[NSAttributedString class]]) {
  5195     attrStr = static_cast<NSAttributedString*>(insertString);
  5196   } else {
  5197     attrStr =
  5198       [[[NSAttributedString alloc] initWithString:insertString] autorelease];
  5201   mTextInputHandler->InsertText(attrStr);
  5203   NS_OBJC_END_TRY_ABORT_BLOCK;
  5206 - (void)insertNewline:(id)sender
  5208   [self insertText:@"\n"];
  5211 - (void) doCommandBySelector:(SEL)aSelector
  5213   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5215   if (!mGeckoChild || !mTextInputHandler) {
  5216     return;
  5219   const char* sel = reinterpret_cast<const char*>(aSelector);
  5220   if (!mTextInputHandler->DoCommandBySelector(sel)) {
  5221     [super doCommandBySelector:aSelector];
  5224   NS_OBJC_END_TRY_ABORT_BLOCK;
  5227 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
  5229   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5231   NS_ENSURE_TRUE_VOID(mTextInputHandler);
  5233   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5235   NSAttributedString* attrStr;
  5236   if ([aString isKindOfClass:[NSAttributedString class]]) {
  5237     attrStr = static_cast<NSAttributedString*>(aString);
  5238   } else {
  5239     attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
  5242   mTextInputHandler->SetMarkedText(attrStr, selRange);
  5244   NS_OBJC_END_TRY_ABORT_BLOCK;
  5247 - (void) unmarkText
  5249   NS_ENSURE_TRUE(mTextInputHandler, );
  5250   mTextInputHandler->CommitIMEComposition();
  5253 - (BOOL) hasMarkedText
  5255   NS_ENSURE_TRUE(mTextInputHandler, NO);
  5256   return mTextInputHandler->HasMarkedText();
  5259 - (BOOL)shouldMinimizeOnTitlebarDoubleClick
  5261   NSString *MDAppleMiniaturizeOnDoubleClickKey =
  5262                                       @"AppleMiniaturizeOnDoubleClick";
  5263   NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  5264   bool shouldMinimize = [[userDefaults
  5265           objectForKey:MDAppleMiniaturizeOnDoubleClickKey] boolValue];
  5267   return shouldMinimize;
  5270 - (NSInteger) conversationIdentifier
  5272   NS_ENSURE_TRUE(mTextInputHandler, reinterpret_cast<NSInteger>(self));
  5273   return mTextInputHandler->ConversationIdentifier();
  5276 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
  5278   NS_ENSURE_TRUE(mTextInputHandler, nil);
  5279   return mTextInputHandler->GetAttributedSubstringFromRange(theRange);
  5282 - (NSRange) markedRange
  5284   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5286   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0));
  5287   return mTextInputHandler->MarkedRange();
  5289   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
  5292 - (NSRange) selectedRange
  5294   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5296   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0));
  5297   return mTextInputHandler->SelectedRange();
  5299   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
  5302 - (NSRect) firstRectForCharacterRange:(NSRange)theRange
  5304   NSRect rect;
  5305   NS_ENSURE_TRUE(mTextInputHandler, rect);
  5306   return mTextInputHandler->FirstRectForCharacterRange(theRange);
  5309 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
  5311   NS_ENSURE_TRUE(mTextInputHandler, 0);
  5312   return mTextInputHandler->CharacterIndexForPoint(thePoint);
  5315 - (NSArray*) validAttributesForMarkedText
  5317   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  5319   NS_ENSURE_TRUE(mTextInputHandler, [NSArray array]);
  5320   return mTextInputHandler->GetValidAttributesForMarkedText();
  5322   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  5325 #pragma mark -
  5326 // NSTextInputClient implementation
  5328 - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
  5330   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5332   NS_ENSURE_TRUE_VOID(mGeckoChild);
  5334   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5336   NSAttributedString* attrStr;
  5337   if ([aString isKindOfClass:[NSAttributedString class]]) {
  5338     attrStr = static_cast<NSAttributedString*>(aString);
  5339   } else {
  5340     attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
  5343   mTextInputHandler->InsertText(attrStr, &replacementRange);
  5345   NS_OBJC_END_TRY_ABORT_BLOCK;
  5348 - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange
  5349                                replacementRange:(NSRange)replacementRange
  5351   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5353   NS_ENSURE_TRUE_VOID(mTextInputHandler);
  5355   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5357   NSAttributedString* attrStr;
  5358   if ([aString isKindOfClass:[NSAttributedString class]]) {
  5359     attrStr = static_cast<NSAttributedString*>(aString);
  5360   } else {
  5361     attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
  5364   mTextInputHandler->SetMarkedText(attrStr, selectedRange, &replacementRange);
  5366   NS_OBJC_END_TRY_ABORT_BLOCK;
  5369 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange
  5370                                         actualRange:(NSRangePointer)actualRange
  5372   NS_ENSURE_TRUE(mTextInputHandler, nil);
  5373   return mTextInputHandler->GetAttributedSubstringFromRange(aRange,
  5374                                                             actualRange);
  5377 - (NSRect)firstRectForCharacterRange:(NSRange)aRange
  5378                          actualRange:(NSRangePointer)actualRange
  5380   NS_ENSURE_TRUE(mTextInputHandler, NSMakeRect(0.0, 0.0, 0.0, 0.0));
  5381   return mTextInputHandler->FirstRectForCharacterRange(aRange, actualRange);
  5384 - (NSInteger)windowLevel
  5386   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5388   NS_ENSURE_TRUE(mTextInputHandler, [[self window] level]);
  5389   return mTextInputHandler->GetWindowLevel();
  5391   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNormalWindowLevel);
  5394 #pragma mark -
  5396 #ifdef __LP64__
  5397 - (NSTextInputContext *)inputContext
  5399   if (mIsPluginView && mPluginEventModel == NPEventModelCocoa)
  5400     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
  5401   else
  5402     return [super inputContext];
  5404 #endif
  5406 // This is a private API that Cocoa uses.
  5407 // Cocoa will call this after the menu system returns "NO" for "performKeyEquivalent:".
  5408 // We want all they key events we can get so just return YES. In particular, this fixes
  5409 // ctrl-tab - we don't get a "keyDown:" call for that without this.
  5410 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event
  5412   return YES;
  5415 - (void)keyDown:(NSEvent*)theEvent
  5417   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5419 #if !defined(RELEASE_BUILD) || defined(DEBUG)
  5420   if (mGeckoChild && mTextInputHandler && mTextInputHandler->IsFocused()) {
  5421 #ifdef MOZ_CRASHREPORTER
  5422     NSWindow* window = [self window];
  5423     NSString* info = [NSString stringWithFormat:@"\nview [%@], window [%@], key event [%@], window is key %i, is fullscreen %i, app is active %i",
  5424                       self, window, theEvent, [window isKeyWindow], ([window styleMask] & (1 << 14)) != 0,
  5425                       [NSApp isActive]];
  5426     nsAutoCString additionalInfo([info UTF8String]);
  5427 #endif
  5428     if (mIsPluginView) {
  5429       if (TextInputHandler::IsSecureEventInputEnabled()) {
  5430         #define CRASH_MESSAGE "While a plugin has focus, we must not be in secure mode"
  5431 #ifdef MOZ_CRASHREPORTER
  5432         CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("\nBug 893973: ") +
  5433                                                    NS_LITERAL_CSTRING(CRASH_MESSAGE));
  5434         CrashReporter::AppendAppNotesToCrashReport(additionalInfo);
  5435 #endif
  5436         MOZ_CRASH(CRASH_MESSAGE);
  5437         #undef CRASH_MESSAGE
  5439     } else if (mGeckoChild->GetInputContext().IsPasswordEditor() &&
  5440                !TextInputHandler::IsSecureEventInputEnabled()) {
  5441       #define CRASH_MESSAGE "A password editor has focus, but not in secure input mode"
  5442 #ifdef MOZ_CRASHREPORTER
  5443       CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("\nBug 893973: ") +
  5444                                                  NS_LITERAL_CSTRING(CRASH_MESSAGE));
  5445       CrashReporter::AppendAppNotesToCrashReport(additionalInfo);
  5446 #endif
  5447       MOZ_CRASH(CRASH_MESSAGE);
  5448       #undef CRASH_MESSAGE
  5449     } else if (!mGeckoChild->GetInputContext().IsPasswordEditor() &&
  5450                TextInputHandler::IsSecureEventInputEnabled()) {
  5451       #define CRASH_MESSAGE "A non-password editor has focus, but in secure input mode"
  5452 #ifdef MOZ_CRASHREPORTER
  5453       CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("\nBug 893973: ") +
  5454                                                  NS_LITERAL_CSTRING(CRASH_MESSAGE));
  5455       CrashReporter::AppendAppNotesToCrashReport(additionalInfo);
  5456 #endif
  5457       MOZ_CRASH(CRASH_MESSAGE);
  5458       #undef CRASH_MESSAGE
  5461 #endif // #if !defined(RELEASE_BUILD) || defined(DEBUG)
  5463   if (mGeckoChild && mTextInputHandler && mIsPluginView) {
  5464     mTextInputHandler->HandleKeyDownEventForPlugin(theEvent);
  5465     return;
  5468   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5469   bool handled = false;
  5470   if (mGeckoChild && mTextInputHandler) {
  5471     handled = mTextInputHandler->HandleKeyDownEvent(theEvent);
  5474   // We always allow keyboard events to propagate to keyDown: but if they are not
  5475   // handled we give special Application menu items a chance to act.
  5476   if (!handled && sApplicationMenu) {
  5477     [sApplicationMenu performKeyEquivalent:theEvent];
  5480   NS_OBJC_END_TRY_ABORT_BLOCK;
  5483 - (void)keyUp:(NSEvent*)theEvent
  5485   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5487   NS_ENSURE_TRUE(mGeckoChild, );
  5489   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5491   if (mIsPluginView) {
  5492     mTextInputHandler->HandleKeyUpEventForPlugin(theEvent);
  5493     return;
  5496   mTextInputHandler->HandleKeyUpEvent(theEvent);
  5498   NS_OBJC_END_TRY_ABORT_BLOCK;
  5501 - (void)flagsChanged:(NSEvent*)theEvent
  5503   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5505   NS_ENSURE_TRUE(mGeckoChild, );
  5507   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5508   mTextInputHandler->HandleFlagsChanged(theEvent);
  5510   NS_OBJC_END_TRY_ABORT_BLOCK;
  5513 - (BOOL) isFirstResponder
  5515   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5517   NSResponder* resp = [[self window] firstResponder];
  5518   return (resp == (NSResponder*)self);
  5520   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  5523 - (BOOL)isDragInProgress
  5525   if (!mDragService)
  5526     return NO;
  5528   nsCOMPtr<nsIDragSession> dragSession;
  5529   mDragService->GetCurrentSession(getter_AddRefs(dragSession));
  5530   return dragSession != nullptr;
  5533 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent
  5535   // If we're being destroyed assume the default -- return YES.
  5536   if (!mGeckoChild)
  5537     return YES;
  5539   WidgetMouseEvent geckoEvent(true, NS_MOUSE_ACTIVATE, mGeckoChild,
  5540                               WidgetMouseEvent::eReal);
  5541   [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
  5542   return !mGeckoChild->DispatchWindowEvent(geckoEvent);
  5545 // Returns NO if the plugin shouldn't be focused/unfocused.
  5546 - (BOOL)updatePluginFocusStatus:(BOOL)getFocus
  5548   if (!mGeckoChild)
  5549     return NO;
  5551   if (mPluginEventModel == NPEventModelCocoa) {
  5552     WidgetPluginEvent pluginEvent(true, NS_PLUGIN_FOCUS_EVENT, mGeckoChild);
  5553     NPCocoaEvent cocoaEvent;
  5554     nsCocoaUtils::InitNPCocoaEvent(&cocoaEvent);
  5555     cocoaEvent.type = NPCocoaEventFocusChanged;
  5556     cocoaEvent.data.focus.hasFocus = getFocus;
  5557     nsCocoaUtils::InitPluginEvent(pluginEvent, cocoaEvent);
  5558     mGeckoChild->DispatchWindowEvent(pluginEvent);
  5560     if (getFocus)
  5561       [self sendFocusEvent:NS_PLUGIN_FOCUS];
  5564   return YES;
  5567 // We must always call through to our superclass, even when mGeckoChild is
  5568 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
  5569 - (BOOL)becomeFirstResponder
  5571   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5573   if (mIsPluginView) {
  5574     if (![self updatePluginFocusStatus:YES])
  5575       return NO;
  5578   return [super becomeFirstResponder];
  5580   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES);
  5583 // We must always call through to our superclass, even when mGeckoChild is
  5584 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
  5585 - (BOOL)resignFirstResponder
  5587   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5589   if (mIsPluginView) {
  5590     if (![self updatePluginFocusStatus:NO])
  5591       return NO;
  5594   return [super resignFirstResponder];
  5596   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES);
  5599 - (void)viewsWindowDidBecomeKey
  5601   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5603   if (!mGeckoChild)
  5604     return;
  5606   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5608   // check to see if the window implements the mozWindow protocol. This
  5609   // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
  5610   // which can happen because these activate calls propagate out
  5611   // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
  5612   BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
  5613   if (isMozWindow)
  5614     [[self window] setSuppressMakeKeyFront:YES];
  5616   nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
  5617   if (listener)
  5618     listener->WindowActivated();
  5620   if (isMozWindow)
  5621     [[self window] setSuppressMakeKeyFront:NO];
  5623   NS_OBJC_END_TRY_ABORT_BLOCK;
  5626 - (void)viewsWindowDidResignKey
  5628   if (!mGeckoChild)
  5629     return;
  5631   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5633   nsIWidgetListener* listener = mGeckoChild->GetWidgetListener();
  5634   if (listener)
  5635     listener->WindowDeactivated();
  5638 // If the call to removeFromSuperview isn't delayed from nsChildView::
  5639 // TearDownView(), the NSView hierarchy might get changed during calls to
  5640 // [ChildView drawRect:], which leads to "beyond bounds" exceptions in
  5641 // NSCFArray.  For more info see bmo bug 373122.  Apple's docs claim that
  5642 // removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
  5643 // display" (whatever "display" means).  But it's _not_ true that it can be
  5644 // safely invoked during calls to [NSView drawRect:].  We use
  5645 // removeFromSuperview here because there's no longer any danger of being
  5646 // "invoked during display", and because doing do clears up bmo bug 384343.
  5647 - (void)delayedTearDown
  5649   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5651   [self removeFromSuperview];
  5652   [self release];
  5654   NS_OBJC_END_TRY_ABORT_BLOCK;
  5657 #pragma mark -
  5659 // drag'n'drop stuff
  5660 #define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
  5662 - (NSDragOperation)dragOperationForSession:(nsIDragSession*)aDragSession
  5664   uint32_t dragAction;
  5665   aDragSession->GetDragAction(&dragAction);
  5666   if (nsIDragService::DRAGDROP_ACTION_LINK & dragAction)
  5667     return NSDragOperationLink;
  5668   if (nsIDragService::DRAGDROP_ACTION_COPY & dragAction)
  5669     return NSDragOperationCopy;
  5670   if (nsIDragService::DRAGDROP_ACTION_MOVE & dragAction)
  5671     return NSDragOperationGeneric;
  5672   return NSDragOperationNone;
  5675 // This is a utility function used by NSView drag event methods
  5676 // to send events. It contains all of the logic needed for Gecko
  5677 // dragging to work. Returns the appropriate cocoa drag operation code.
  5678 - (NSDragOperation)doDragAction:(uint32_t)aMessage sender:(id)aSender
  5680   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5682   if (!mGeckoChild)
  5683     return NSDragOperationNone;
  5685   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView doDragAction: entered\n"));
  5687   if (!mDragService) {
  5688     CallGetService(kDragServiceContractID, &mDragService);
  5689     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
  5690     if (!mDragService)
  5691       return NSDragOperationNone;
  5694   if (aMessage == NS_DRAGDROP_ENTER)
  5695     mDragService->StartDragSession();
  5697   nsCOMPtr<nsIDragSession> dragSession;
  5698   mDragService->GetCurrentSession(getter_AddRefs(dragSession));
  5699   if (dragSession) {
  5700     if (aMessage == NS_DRAGDROP_OVER) {
  5701       // fire the drag event at the source. Just ignore whether it was
  5702       // cancelled or not as there isn't actually a means to stop the drag
  5703       mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
  5704       dragSession->SetCanDrop(false);
  5706     else if (aMessage == NS_DRAGDROP_DROP) {
  5707       // We make the assumption that the dragOver handlers have correctly set
  5708       // the |canDrop| property of the Drag Session.
  5709       bool canDrop = false;
  5710       if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
  5711         [self doDragAction:NS_DRAGDROP_EXIT sender:aSender];
  5713         nsCOMPtr<nsIDOMNode> sourceNode;
  5714         dragSession->GetSourceNode(getter_AddRefs(sourceNode));
  5715         if (!sourceNode) {
  5716           mDragService->EndDragSession(false);
  5718         return NSDragOperationNone;
  5722     unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
  5723     uint32_t action = nsIDragService::DRAGDROP_ACTION_MOVE;
  5724     // force copy = option, alias = cmd-option, default is move
  5725     if (modifierFlags & NSAlternateKeyMask) {
  5726       if (modifierFlags & NSCommandKeyMask)
  5727         action = nsIDragService::DRAGDROP_ACTION_LINK;
  5728       else
  5729         action = nsIDragService::DRAGDROP_ACTION_COPY;
  5731     dragSession->SetDragAction(action);
  5734   // set up gecko event
  5735   WidgetDragEvent geckoEvent(true, aMessage, mGeckoChild);
  5736   nsCocoaUtils::InitInputEvent(geckoEvent, [NSApp currentEvent]);
  5738   // Use our own coordinates in the gecko event.
  5739   // Convert event from gecko global coords to gecko view coords.
  5740   NSPoint draggingLoc = [aSender draggingLocation];
  5741   NSPoint localPoint = [self convertPoint:draggingLoc fromView:nil];
  5743   geckoEvent.refPoint = LayoutDeviceIntPoint::FromUntyped(
  5744     mGeckoChild->CocoaPointsToDevPixels(localPoint));
  5746   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5747   mGeckoChild->DispatchWindowEvent(geckoEvent);
  5748   if (!mGeckoChild)
  5749     return NSDragOperationNone;
  5751   if (dragSession) {
  5752     switch (aMessage) {
  5753       case NS_DRAGDROP_ENTER:
  5754       case NS_DRAGDROP_OVER:
  5755         return [self dragOperationForSession:dragSession];
  5756       case NS_DRAGDROP_EXIT:
  5757       case NS_DRAGDROP_DROP: {
  5758         nsCOMPtr<nsIDOMNode> sourceNode;
  5759         dragSession->GetSourceNode(getter_AddRefs(sourceNode));
  5760         if (!sourceNode) {
  5761           // We're leaving a window while doing a drag that was
  5762           // initiated in a different app. End the drag session,
  5763           // since we're done with it for now (until the user
  5764           // drags back into mozilla).
  5765           mDragService->EndDragSession(false);
  5771   return NSDragOperationGeneric;
  5773   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
  5776 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
  5778   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  5780   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingEntered: entered\n"));
  5782   // there should never be a globalDragPboard when "draggingEntered:" is
  5783   // called, but just in case we'll take care of it here.
  5784   [globalDragPboard release];
  5786   // Set the global drag pasteboard that will be used for this drag session.
  5787   // This will be set back to nil when the drag session ends (mouse exits
  5788   // the view or a drop happens within the view).
  5789   globalDragPboard = [[sender draggingPasteboard] retain];
  5791   return [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
  5793   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
  5796 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
  5798   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingUpdated: entered\n"));
  5800   return [self doDragAction:NS_DRAGDROP_OVER sender:sender];
  5803 - (void)draggingExited:(id <NSDraggingInfo>)sender
  5805   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingExited: entered\n"));
  5807   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5808   [self doDragAction:NS_DRAGDROP_EXIT sender:sender];
  5809   NS_IF_RELEASE(mDragService);
  5812 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
  5814   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5815   BOOL handled = [self doDragAction:NS_DRAGDROP_DROP sender:sender] != NSDragOperationNone;
  5816   NS_IF_RELEASE(mDragService);
  5817   return handled;
  5820 // NSDraggingSource
  5821 - (void)draggedImage:(NSImage *)anImage movedTo:(NSPoint)aPoint
  5823   // Get the drag service if it isn't already cached. The drag service
  5824   // isn't cached when dragging over a different application.
  5825   nsCOMPtr<nsIDragService> dragService = mDragService;
  5826   if (!dragService) {
  5827     dragService = do_GetService(kDragServiceContractID);
  5830   if (dragService) {
  5831     NSPoint pnt = [NSEvent mouseLocation];
  5832     FlipCocoaScreenCoordinate(pnt);
  5833     dragService->DragMoved(NSToIntRound(pnt.x), NSToIntRound(pnt.y));
  5837 // NSDraggingSource
  5838 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
  5840   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  5842   gDraggedTransferables = nullptr;
  5844   NSEvent *currentEvent = [NSApp currentEvent];
  5845   gUserCancelledDrag = ([currentEvent type] == NSKeyDown &&
  5846                         [currentEvent keyCode] == kVK_Escape);
  5848   if (!mDragService) {
  5849     CallGetService(kDragServiceContractID, &mDragService);
  5850     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
  5853   if (mDragService) {
  5854     // set the dragend point from the current mouse location
  5855     nsDragService* dragService = static_cast<nsDragService *>(mDragService);
  5856     NSPoint pnt = [NSEvent mouseLocation];
  5857     FlipCocoaScreenCoordinate(pnt);
  5858     dragService->SetDragEndPoint(nsIntPoint(NSToIntRound(pnt.x), NSToIntRound(pnt.y)));
  5860     // XXX: dropEffect should be updated per |operation|.
  5861     // As things stand though, |operation| isn't well handled within "our"
  5862     // events, that is, when the drop happens within the window: it is set
  5863     // either to NSDragOperationGeneric or to NSDragOperationNone.
  5864     // For that reason, it's not yet possible to override dropEffect per the
  5865     // given OS value, and it's also unclear what's the correct dropEffect
  5866     // value for NSDragOperationGeneric that is passed by other applications.
  5867     // All that said, NSDragOperationNone is still reliable.
  5868     if (operation == NSDragOperationNone) {
  5869       nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
  5870       dragService->GetDataTransfer(getter_AddRefs(dataTransfer));
  5871       if (dataTransfer)
  5872         dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
  5875     mDragService->EndDragSession(true);
  5876     NS_RELEASE(mDragService);
  5879   [globalDragPboard release];
  5880   globalDragPboard = nil;
  5881   [gLastDragMouseDownEvent release];
  5882   gLastDragMouseDownEvent = nil;
  5884   NS_OBJC_END_TRY_ABORT_BLOCK;
  5887 // NSDraggingSource
  5888 // this is just implemented so we comply with the NSDraggingSource informal protocol
  5889 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
  5891   return UINT_MAX;
  5894 // This method is a callback typically invoked in response to a drag ending on the desktop
  5895 // or a Findow folder window; the argument passed is a path to the drop location, to be used
  5896 // in constructing a complete pathname for the file(s) we want to create as a result of
  5897 // the drag.
  5898 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination
  5900   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  5902   nsresult rv;
  5904   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n"));
  5906   nsCOMPtr<nsIFile> targFile;
  5907   NS_NewLocalFile(EmptyString(), true, getter_AddRefs(targFile));
  5908   nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
  5909   if (!macLocalFile) {
  5910     NS_ERROR("No Mac local file");
  5911     return nil;
  5914   if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
  5915     NS_ERROR("failed InitWithCFURL");
  5916     return nil;
  5919   if (!gDraggedTransferables)
  5920     return nil;
  5922   uint32_t transferableCount;
  5923   rv = gDraggedTransferables->Count(&transferableCount);
  5924   if (NS_FAILED(rv))
  5925     return nil;
  5927   for (uint32_t i = 0; i < transferableCount; i++) {
  5928     nsCOMPtr<nsISupports> genericItem;
  5929     gDraggedTransferables->GetElementAt(i, getter_AddRefs(genericItem));
  5930     nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
  5931     if (!item) {
  5932       NS_ERROR("no transferable");
  5933       return nil;
  5936     item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsIFile*));
  5938     // now request the kFilePromiseMime data, which will invoke the data provider
  5939     // If successful, the returned data is a reference to the resulting file.
  5940     nsCOMPtr<nsISupports> fileDataPrimitive;
  5941     uint32_t dataSize = 0;
  5942     item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
  5945   NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
  5946   NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"];
  5947   NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  5948   NSArray* rslt = [NSArray arrayWithObject:name];
  5950   [name release];
  5952   return rslt;
  5954   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  5957 #pragma mark -
  5959 // Support for the "Services" menu. We currently only support sending strings
  5960 // and HTML to system services.
  5962 - (id)validRequestorForSendType:(NSString *)sendType
  5963                      returnType:(NSString *)returnType
  5965   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  5967   // sendType contains the type of data that the service would like this
  5968   // application to send to it.  sendType is nil if the service is not
  5969   // requesting any data.
  5970   //
  5971   // returnType contains the type of data the the service would like to
  5972   // return to this application (e.g., to overwrite the selection).
  5973   // returnType is nil if the service will not return any data.
  5974   //
  5975   // The following condition thus triggers when the service expects a string
  5976   // or HTML from us or no data at all AND when the service will either not
  5977   // send back any data to us or will send a string or HTML back to us.
  5979 #define IsSupportedType(typeStr) ([typeStr isEqual:NSStringPboardType] || [typeStr isEqual:NSHTMLPboardType])
  5981   id result = nil;
  5983   if ((!sendType || IsSupportedType(sendType)) &&
  5984       (!returnType || IsSupportedType(returnType))) {
  5985     if (mGeckoChild) {
  5986       // Assume that this object will be able to handle this request.
  5987       result = self;
  5989       // Keep the ChildView alive during this operation.
  5990       nsAutoRetainCocoaObject kungFuDeathGrip(self);
  5992       // Determine if there is a selection (if sending to the service).
  5993       if (sendType) {
  5994         WidgetQueryContentEvent event(true, NS_QUERY_CONTENT_STATE,
  5995                                       mGeckoChild);
  5996         // This might destroy our widget (and null out mGeckoChild).
  5997         mGeckoChild->DispatchWindowEvent(event);
  5998         if (!mGeckoChild || !event.mSucceeded || !event.mReply.mHasSelection)
  5999           result = nil;
  6002       // Determine if we can paste (if receiving data from the service).
  6003       if (mGeckoChild && returnType) {
  6004         WidgetContentCommandEvent command(true,
  6005                                           NS_CONTENT_COMMAND_PASTE_TRANSFERABLE,
  6006                                           mGeckoChild, true);
  6007         // This might possibly destroy our widget (and null out mGeckoChild).
  6008         mGeckoChild->DispatchWindowEvent(command);
  6009         if (!mGeckoChild || !command.mSucceeded || !command.mIsEnabled)
  6010           result = nil;
  6015 #undef IsSupportedType
  6017   // Give the superclass a chance if this object will not handle this request.
  6018   if (!result)
  6019     result = [super validRequestorForSendType:sendType returnType:returnType];
  6021   return result;
  6023   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  6026 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
  6027                              types:(NSArray *)types
  6029   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  6031   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  6033   // Make sure that the service will accept strings or HTML.
  6034   if ([types containsObject:NSStringPboardType] == NO &&
  6035       [types containsObject:NSHTMLPboardType] == NO)
  6036     return NO;
  6038   // Bail out if there is no Gecko object.
  6039   if (!mGeckoChild)
  6040     return NO;
  6042   // Obtain the current selection.
  6043   WidgetQueryContentEvent event(true,
  6044                                 NS_QUERY_SELECTION_AS_TRANSFERABLE,
  6045                                 mGeckoChild);
  6046   mGeckoChild->DispatchWindowEvent(event);
  6047   if (!event.mSucceeded || !event.mReply.mTransferable)
  6048     return NO;
  6050   // Transform the transferable to an NSDictionary.
  6051   NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(event.mReply.mTransferable);
  6052   if (!pasteboardOutputDict)
  6053     return NO;
  6055   // Declare the pasteboard types.
  6056   unsigned int typeCount = [pasteboardOutputDict count];
  6057   NSMutableArray * types = [NSMutableArray arrayWithCapacity:typeCount];
  6058   [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
  6059   [pboard declareTypes:types owner:nil];
  6061   // Write the data to the pasteboard.
  6062   for (unsigned int i = 0; i < typeCount; i++) {
  6063     NSString* currentKey = [types objectAtIndex:i];
  6064     id currentValue = [pasteboardOutputDict valueForKey:currentKey];
  6066     if (currentKey == NSStringPboardType ||
  6067         currentKey == kCorePboardType_url ||
  6068         currentKey == kCorePboardType_urld ||
  6069         currentKey == kCorePboardType_urln) {
  6070       [pboard setString:currentValue forType:currentKey];
  6071     } else if (currentKey == NSHTMLPboardType) {
  6072       [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) forType:currentKey];
  6073     } else if (currentKey == NSTIFFPboardType) {
  6074       [pboard setData:currentValue forType:currentKey];
  6075     } else if (currentKey == NSFilesPromisePboardType) {
  6076       [pboard setPropertyList:currentValue forType:currentKey];
  6080   return YES;
  6082   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  6085 // Called if the service wants us to replace the current selection.
  6086 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard
  6088   nsresult rv;
  6089   nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
  6090   if (NS_FAILED(rv))
  6091     return NO;
  6092   trans->Init(nullptr);
  6094   trans->AddDataFlavor(kUnicodeMime);
  6095   trans->AddDataFlavor(kHTMLMime);
  6097   rv = nsClipboard::TransferableFromPasteboard(trans, pboard);
  6098   if (NS_FAILED(rv))
  6099     return NO;
  6101   NS_ENSURE_TRUE(mGeckoChild, false);
  6103   WidgetContentCommandEvent command(true,
  6104                                     NS_CONTENT_COMMAND_PASTE_TRANSFERABLE,
  6105                                     mGeckoChild);
  6106   command.mTransferable = trans;
  6107   mGeckoChild->DispatchWindowEvent(command);
  6109   return command.mSucceeded && command.mIsEnabled;
  6112 #pragma mark -
  6114 #ifdef ACCESSIBILITY
  6116 /* Every ChildView has a corresponding mozDocAccessible object that is doing all
  6117    the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
  6118    object.
  6120    All ChildView needs to do is to route all accessibility calls (from the NSAccessibility APIs)
  6121    down to its object, pretending that they are the same.
  6122 */
  6123 - (id<mozAccessible>)accessible
  6125   if (!mGeckoChild)
  6126     return nil;
  6128   id<mozAccessible> nativeAccessible = nil;
  6130   nsAutoRetainCocoaObject kungFuDeathGrip(self);
  6131   nsCOMPtr<nsIWidget> kungFuDeathGrip2(mGeckoChild);
  6132   nsRefPtr<a11y::Accessible> accessible = mGeckoChild->GetDocumentAccessible();
  6133   if (!accessible)
  6134     return nil;
  6136   accessible->GetNativeInterface((void**)&nativeAccessible);
  6138 #ifdef DEBUG_hakan
  6139   NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
  6140 #endif
  6142   return nativeAccessible;
  6145 /* Implementation of formal mozAccessible formal protocol (enabling mozViews
  6146    to talk to mozAccessible objects in the accessibility module). */
  6148 - (BOOL)hasRepresentedView
  6150   return YES;
  6153 - (id)representedView
  6155   return self;
  6158 - (BOOL)isRoot
  6160   return [[self accessible] isRoot];
  6163 #ifdef DEBUG
  6164 - (void)printHierarchy
  6166   [[self accessible] printHierarchy];
  6168 #endif
  6170 #pragma mark -
  6172 // general
  6174 - (BOOL)accessibilityIsIgnored
  6176   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6177     return [super accessibilityIsIgnored];
  6179   return [[self accessible] accessibilityIsIgnored];
  6182 - (id)accessibilityHitTest:(NSPoint)point
  6184   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6185     return [super accessibilityHitTest:point];
  6187   return [[self accessible] accessibilityHitTest:point];
  6190 - (id)accessibilityFocusedUIElement
  6192   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6193     return [super accessibilityFocusedUIElement];
  6195   return [[self accessible] accessibilityFocusedUIElement];
  6198 // actions
  6200 - (NSArray*)accessibilityActionNames
  6202   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6203     return [super accessibilityActionNames];
  6205   return [[self accessible] accessibilityActionNames];
  6208 - (NSString*)accessibilityActionDescription:(NSString*)action
  6210   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6211     return [super accessibilityActionDescription:action];
  6213   return [[self accessible] accessibilityActionDescription:action];
  6216 - (void)accessibilityPerformAction:(NSString*)action
  6218   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6219     return [super accessibilityPerformAction:action];
  6221   return [[self accessible] accessibilityPerformAction:action];
  6224 // attributes
  6226 - (NSArray*)accessibilityAttributeNames
  6228   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6229     return [super accessibilityAttributeNames];
  6231   return [[self accessible] accessibilityAttributeNames];
  6234 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
  6236   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6237     return [super accessibilityIsAttributeSettable:attribute];
  6239   return [[self accessible] accessibilityIsAttributeSettable:attribute];
  6242 - (id)accessibilityAttributeValue:(NSString*)attribute
  6244   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  6246   if (!mozilla::a11y::ShouldA11yBeEnabled())
  6247     return [super accessibilityAttributeValue:attribute];
  6249   id<mozAccessible> accessible = [self accessible];
  6251   // if we're the root (topmost) accessible, we need to return our native AXParent as we
  6252   // traverse outside to the hierarchy of whoever embeds us. thus, fall back on NSView's
  6253   // default implementation for this attribute.
  6254   if ([attribute isEqualToString:NSAccessibilityParentAttribute] && [accessible isRoot]) {
  6255     id parentAccessible = [super accessibilityAttributeValue:attribute];
  6256     return parentAccessible;
  6259   return [accessible accessibilityAttributeValue:attribute];
  6261   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  6264 #endif /* ACCESSIBILITY */
  6266 @end
  6268 #pragma mark -
  6270 void
  6271 ChildViewMouseTracker::OnDestroyView(ChildView* aView)
  6273   if (sLastMouseEventView == aView) {
  6274     sLastMouseEventView = nil;
  6275     [sLastMouseMoveEvent release];
  6276     sLastMouseMoveEvent = nil;
  6280 void
  6281 ChildViewMouseTracker::OnDestroyWindow(NSWindow* aWindow)
  6283   if (sWindowUnderMouse == aWindow) {
  6284     sWindowUnderMouse = nil;
  6288 void
  6289 ChildViewMouseTracker::MouseEnteredWindow(NSEvent* aEvent)
  6291   sWindowUnderMouse = [aEvent window];
  6292   ReEvaluateMouseEnterState(aEvent);
  6295 void
  6296 ChildViewMouseTracker::MouseExitedWindow(NSEvent* aEvent)
  6298   if (sWindowUnderMouse == [aEvent window]) {
  6299     sWindowUnderMouse = nil;
  6300     ReEvaluateMouseEnterState(aEvent);
  6304 void
  6305 ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent, ChildView* aOldView)
  6307   ChildView* oldView = aOldView ? aOldView : sLastMouseEventView;
  6308   sLastMouseEventView = ViewForEvent(aEvent);
  6309   if (sLastMouseEventView != oldView) {
  6310     // Send enter and / or exit events.
  6311     WidgetMouseEvent::exitType type =
  6312       [sLastMouseEventView window] == [oldView window] ?
  6313         WidgetMouseEvent::eChild : WidgetMouseEvent::eTopLevel;
  6314     [oldView sendMouseEnterOrExitEvent:aEvent enter:NO type:type];
  6315     // After the cursor exits the window set it to a visible regular arrow cursor.
  6316     if (type == WidgetMouseEvent::eTopLevel) {
  6317       [[nsCursorManager sharedInstance] setCursor:eCursor_standard];
  6319     [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent enter:YES type:type];
  6323 void
  6324 ChildViewMouseTracker::ResendLastMouseMoveEvent()
  6326   if (sLastMouseMoveEvent) {
  6327     MouseMoved(sLastMouseMoveEvent);
  6331 void
  6332 ChildViewMouseTracker::MouseMoved(NSEvent* aEvent)
  6334   MouseEnteredWindow(aEvent);
  6335   [sLastMouseEventView handleMouseMoved:aEvent];
  6336   if (sLastMouseMoveEvent != aEvent) {
  6337     [sLastMouseMoveEvent release];
  6338     sLastMouseMoveEvent = [aEvent retain];
  6342 void
  6343 ChildViewMouseTracker::MouseScrolled(NSEvent* aEvent)
  6345   if (!nsCocoaUtils::IsMomentumScrollEvent(aEvent)) {
  6346     // Store the position so we can pin future momentum scroll events.
  6347     sLastScrollEventScreenLocation = nsCocoaUtils::ScreenLocationForEvent(aEvent);
  6351 ChildView*
  6352 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
  6354   NSWindow* window = sWindowUnderMouse;
  6355   if (!window)
  6356     return nil;
  6358   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
  6359   NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
  6361   if (![view isKindOfClass:[ChildView class]])
  6362     return nil;
  6364   ChildView* childView = (ChildView*)view;
  6365   // If childView is being destroyed return nil.
  6366   if (![childView widget])
  6367     return nil;
  6368   return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
  6371 void
  6372 ChildViewMouseTracker::AttachPluginEvent(WidgetMouseEventBase& aMouseEvent,
  6373                                          ChildView* aView,
  6374                                          NSEvent* aNativeMouseEvent,
  6375                                          int aPluginEventType,
  6376                                          void* aPluginEventHolder)
  6378   if (![aView isPluginView] ||
  6379       [aView pluginEventModel] != NPEventModelCocoa) {
  6380     return;
  6383   NPCocoaEvent* cocoaEvent = (NPCocoaEvent*)aPluginEventHolder;
  6384   nsCocoaUtils::InitNPCocoaEvent(cocoaEvent);
  6385   NSPoint point = [aView convertPoint:[aNativeMouseEvent locationInWindow]
  6386                              fromView:nil];
  6387   NPCocoaEventType type = (NPCocoaEventType)aPluginEventType;
  6388   // XXX We should consider dropping this code, which causes data.mouse.pluginX
  6389   // and data.mouse.pluginY to be set to '5' for mouse-entered and mouse-exited
  6390   // events.  But note that it's been in the tree since the Cocoa NPAPI event
  6391   // model was first implemented four years ago, without any known complaints.
  6392   //
  6393   // Similar code first appeared (without explanation) in a very early version
  6394   // ("fix 0.3") of the patch for bug 435041 ("Implement Cocoa NPAPI event
  6395   // model for Mac OS X").  But there's no trace of it in the WebKit code that
  6396   // was used as a model for much of that patch.
  6397 #if (0)
  6398   if (type == NPCocoaEventMouseEntered ||
  6399       type == NPCocoaEventMouseExited) {
  6400     point.x = point.y = 5;
  6402 #endif
  6403   NSUInteger clickCount = 0;
  6404   if (type != NPCocoaEventMouseEntered &&
  6405       type != NPCocoaEventMouseExited &&
  6406       type != NPCocoaEventScrollWheel) {
  6407     clickCount = [aNativeMouseEvent clickCount];
  6409   cocoaEvent->type = type;
  6410   cocoaEvent->data.mouse.modifierFlags = [aNativeMouseEvent modifierFlags];
  6411   cocoaEvent->data.mouse.pluginX = point.x;
  6412   cocoaEvent->data.mouse.pluginY = point.y;
  6413   cocoaEvent->data.mouse.buttonNumber = [aNativeMouseEvent buttonNumber];
  6414   cocoaEvent->data.mouse.clickCount = clickCount;
  6415   cocoaEvent->data.mouse.deltaX = [aNativeMouseEvent deltaX];
  6416   cocoaEvent->data.mouse.deltaY = [aNativeMouseEvent deltaY];
  6417   cocoaEvent->data.mouse.deltaZ = [aNativeMouseEvent deltaZ];
  6418   aMouseEvent.pluginEvent = cocoaEvent;
  6421 BOOL
  6422 ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
  6423                                           ChildView* aView, BOOL aIsClickThrough)
  6425   // Right mouse down events may get through to all windows, even to a top level
  6426   // window with an open sheet.
  6427   if (!aWindow || [aEvent type] == NSRightMouseDown)
  6428     return YES;
  6430   id delegate = [aWindow delegate];
  6431   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
  6432     return YES;
  6434   nsIWidget *windowWidget = [(WindowDelegate *)delegate geckoWidget];
  6435   if (!windowWidget)
  6436     return YES;
  6438   NSWindow* topLevelWindow = nil;
  6440   switch (windowWidget->WindowType()) {
  6441     case eWindowType_popup:
  6442       // If this is a context menu, it won't have a parent. So we'll always
  6443       // accept mouse move events on context menus even when none of our windows
  6444       // is active, which is the right thing to do.
  6445       // For panels, the parent window is the XUL window that owns the panel.
  6446       return WindowAcceptsEvent([aWindow parentWindow], aEvent, aView, aIsClickThrough);
  6448     case eWindowType_toplevel:
  6449     case eWindowType_dialog:
  6450       if ([aWindow attachedSheet])
  6451         return NO;
  6453       topLevelWindow = aWindow;
  6454       break;
  6455     case eWindowType_sheet: {
  6456       nsIWidget* parentWidget = windowWidget->GetSheetWindowParent();
  6457       if (!parentWidget)
  6458         return YES;
  6460       topLevelWindow = (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW);
  6461       break;
  6464     default:
  6465       return YES;
  6468   if (!topLevelWindow ||
  6469       ([topLevelWindow isMainWindow] && !aIsClickThrough) ||
  6470       [aEvent type] == NSOtherMouseDown ||
  6471       (([aEvent modifierFlags] & NSCommandKeyMask) != 0 &&
  6472        [aEvent type] != NSMouseMoved))
  6473     return YES;
  6475   // If we're here then we're dealing with a left click or mouse move on an
  6476   // inactive window or something similar. Ask Gecko what to do.
  6477   return [aView inactiveWindowAcceptsMouseEvent:aEvent];
  6480 #pragma mark -
  6482 @interface NSView (MethodSwizzling)
  6483 - (BOOL)nsChildView_NSView_mouseDownCanMoveWindow;
  6484 @end
  6486 @implementation NSView (MethodSwizzling)
  6488 // All top-level browser windows belong to the ToolbarWindow class and have
  6489 // NSTexturedBackgroundWindowMask turned on in their "style" (see particularly
  6490 // [ToolbarWindow initWithContentRect:...] in nsCocoaWindow.mm).  This style
  6491 // normally means the window "may be moved by clicking and dragging anywhere
  6492 // in the window background", but we've suppressed this by giving the
  6493 // ChildView class a mouseDownCanMoveWindow method that always returns NO.
  6494 // Normally a ToolbarWindow's contentView (not a ChildView) returns YES when
  6495 // NSTexturedBackgroundWindowMask is turned on.  But normally this makes no
  6496 // difference.  However, under some (probably very unusual) circumstances
  6497 // (and only on Leopard) it *does* make a difference -- for example it
  6498 // triggers bmo bugs 431902 and 476393.  So here we make sure that a
  6499 // ToolbarWindow's contentView always returns NO from the
  6500 // mouseDownCanMoveWindow method.
  6501 - (BOOL)nsChildView_NSView_mouseDownCanMoveWindow
  6503   NSWindow *ourWindow = [self window];
  6504   NSView *contentView = [ourWindow contentView];
  6505   if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView))
  6506     return [ourWindow isMovableByWindowBackground];
  6507   return [self nsChildView_NSView_mouseDownCanMoveWindow];
  6510 @end
  6512 #ifdef __LP64__
  6513 // When using blocks, at least on OS X 10.7, the OS sometimes calls
  6514 // +[NSEvent removeMonitor:] more than once on a single event monitor, which
  6515 // causes crashes.  See bug 678607.  We hook these methods to work around
  6516 // the problem.
  6517 @interface NSEvent (MethodSwizzling)
  6518 + (id)nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:(unsigned long long)mask handler:(id)block;
  6519 + (void)nsChildView_NSEvent_removeMonitor:(id)eventMonitor;
  6520 @end
  6522 // This is a local copy of the AppKit frameworks sEventObservers hashtable.
  6523 // It only stores "local monitors".  We use it to ensure that +[NSEvent
  6524 // removeMonitor:] is never called more than once on the same local monitor.
  6525 static NSHashTable *sLocalEventObservers = nil;
  6527 @implementation NSEvent (MethodSwizzling)
  6529 + (id)nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:(unsigned long long)mask handler:(id)block
  6531   if (!sLocalEventObservers) {
  6532     sLocalEventObservers = [[NSHashTable hashTableWithOptions:
  6533       NSHashTableStrongMemory | NSHashTableObjectPointerPersonality] retain];
  6535   id retval =
  6536     [self nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:mask handler:block];
  6537   if (sLocalEventObservers && retval && ![sLocalEventObservers containsObject:retval]) {
  6538     [sLocalEventObservers addObject:retval];
  6540   return retval;
  6543 + (void)nsChildView_NSEvent_removeMonitor:(id)eventMonitor
  6545   if (sLocalEventObservers && [eventMonitor isKindOfClass: ::NSClassFromString(@"_NSLocalEventObserver")]) {
  6546     if (![sLocalEventObservers containsObject:eventMonitor]) {
  6547       return;
  6549     [sLocalEventObservers removeObject:eventMonitor];
  6551   [self nsChildView_NSEvent_removeMonitor:eventMonitor];
  6554 @end
  6555 #endif // #ifdef __LP64__

mercurial