|
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, ®ion); |
|
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__ |