Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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, ®ion); |
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__ |