widget/cocoa/nsChildView.mm

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial