widget/cocoa/nsChildView.mm

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

mercurial