1 /* -*- Mode: objc; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License
6 * Version 1.1 (the "License"); you may not use this file except in
7 * compliance with the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Josh Aas <josh@mozilla.com>
24 * Mark Mentovai <mark@moxienet.com>
25 * HÃ¥kan Waara <hwaara@gmail.com>
26 * Stuart Morgan <stuart.morgan@alumni.case.edu>
27 * Mats Palmgren <matspal@gmail.com>
28 * Thomas K. Dyas <tdyas@zecador.org>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either the GNU General Public License Version 2 or later (the "GPL"), or
32 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
51 #include "nsChildView.h"
52 #include "nsCocoaWindow.h"
54 #include "nsObjCExceptions.h"
56 #include "nsToolkit.h"
58 #include "nsIPrefService.h"
59 #include "nsIPrefBranch.h"
61 #include "nsIFontMetrics.h"
62 #include "nsIDeviceContext.h"
63 #include "nsIRegion.h"
64 #include "nsIRollupListener.h"
65 #include "nsIViewManager.h"
66 #include "nsIInterfaceRequestor.h"
67 #include "nsIServiceManager.h"
68 #include "nsILocalFile.h"
69 #include "nsILocalFileMac.h"
70 #include "nsGfxCIID.h"
71 #include "nsIMenuRollup.h"
72 #include "nsIDOMSimpleGestureEvent.h"
73 #include "nsIPluginInstance.h"
74 #include "nsThemeConstants.h"
76 #include "nsDragService.h"
77 #include "nsClipboard.h"
78 #include "nsCursorManager.h"
79 #include "nsWindowMap.h"
80 #include "nsCocoaUtils.h"
81 #include "nsMenuUtilsX.h"
82 #include "nsMenuBarX.h"
84 #include "ComplexTextInputPanel.h"
87 #include "gfxContext.h"
88 #include "gfxQuartzSurface.h"
91 #include "LayerManagerOGL.h"
95 #include <ApplicationServices/ApplicationServices.h>
97 using namespace mozilla::layers;
100 #undef INVALIDATE_DEBUGGING // flash areas as they are invalidated
102 // Don't put more than this many rects in the dirty region, just fluff
103 // out to the bounding-box if there are more
104 #define MAX_RECTS_IN_REGION 100
107 PRLogModuleInfo* sCocoaLog = nsnull;
111 CG_EXTERN void CGContextResetCTM(CGContextRef);
112 CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
113 CG_EXTERN void CGContextResetClip(CGContextRef);
116 typedef NSInteger CGSConnection;
117 typedef NSInteger CGSWindow;
118 extern CGSConnection _CGSDefaultConnection();
119 extern CGError CGSGetScreenRectForWindow(const CGSConnection cid, CGSWindow wid, CGRect *outRect);
120 extern CGError CGSGetWindowLevel(const CGSConnection cid, CGSWindow wid, CGWindowLevel *level);
121 extern CGError CGSGetWindowAlpha(const CGSConnection cid, const CGSWindow wid, float* alpha);
124 // defined in nsMenuBarX.mm
125 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
127 // these are defined in nsCocoaWindow.mm
128 extern PRBool gConsumeRollupEvent;
130 PRBool gChildViewMethodsSwizzled = PR_FALSE;
132 extern nsISupportsArray *gDraggedTransferables;
134 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
136 static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
137 #ifdef INVALIDATE_DEBUGGING
138 static void blinkRect(Rect* r);
139 static void blinkRgn(RgnHandle rgn);
142 nsIRollupListener * gRollupListener = nsnull;
143 nsIMenuRollup * gMenuRollup = nsnull;
144 nsIWidget * gRollupWidget = nsnull;
146 PRUint32 gLastModifierState = 0;
148 PRBool gUserCancelledDrag = PR_FALSE;
150 PRUint32 nsChildView::sLastInputEventCount = 0;
152 @interface ChildView(Private)
154 // sets up our view, attaching it to its owning gecko view
155 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
156 - (void)forceRefreshOpenGL;
158 // sends gecko an ime composition event
159 - (void) sendCompositionEvent:(PRInt32)aEventType;
161 // sends gecko an ime text event
162 - (void) sendTextEvent:(PRUnichar*) aBuffer
163 attributedString:(NSAttributedString*) aString
164 selectedRange:(NSRange)selRange
165 markedRange:(NSRange)markRange
166 doCommit:(BOOL)doCommit;
168 // do generic gecko event setup with a generic cocoa event. accepts nil inEvent.
169 - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
171 // set up a gecko mouse event based on a cocoa mouse event
172 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
174 // set up a gecko key event based on a cocoa key event
175 - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent;
177 - (NSMenu*)contextMenu;
179 - (void)setIsPluginView:(BOOL)aIsPlugin;
180 - (BOOL)isPluginView;
181 - (void)setPluginEventModel:(NPEventModel)eventModel;
182 - (void)setPluginDrawingModel:(NPDrawingModel)drawingModel;
183 - (NPEventModel)pluginEventModel;
184 - (NPDrawingModel)pluginDrawingModel;
187 - (void)setPluginTSMInComposition:(BOOL)inComposition;
190 - (BOOL)isRectObscuredBySubview:(NSRect)inRect;
192 - (void)processPendingRedraws;
194 - (void)maybeInitContextMenuTracking;
196 + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
198 - (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext;
200 // Called using performSelector:withObject:afterDelay:0 to release
201 // aWidgetArray (and its contents) the next time through the run loop.
202 - (void)releaseWidgets:(NSArray*)aWidgetArray;
204 #if USE_CLICK_HOLD_CONTEXTMENU
205 // called on a timer two seconds after a mouse down to see if we should display
206 // a context menu (click-hold)
207 - (void)clickHoldCallback:(id)inEvent;
211 - (id<mozAccessible>)accessible;
214 - (BOOL)isFirstResponder;
216 - (BOOL)isDragInProgress;
218 - (void)fireKeyEventForFlagsChanged:(NSEvent*)theEvent keyDown:(BOOL)isKeyDown;
220 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
226 // Key code constants
229 kEscapeKeyCode = 0x35,
230 kRCommandKeyCode = 0x36, // right command key
231 kCommandKeyCode = 0x37,
232 kShiftKeyCode = 0x38,
233 kCapsLockKeyCode = 0x39,
234 kOptionkeyCode = 0x3A,
235 kControlKeyCode = 0x3B,
236 kRShiftKeyCode = 0x3C, // right shift key
237 kROptionKeyCode = 0x3D, // right option key
238 kRControlKeyCode = 0x3E, // right control key
239 kClearKeyCode = 0x47,
258 kPrintScreenKeyCode = kF13KeyCode,
259 kScrollLockKeyCode = kF14KeyCode,
260 kPauseKeyCode = kF15KeyCode,
263 kKeypad0KeyCode = 0x52,
264 kKeypad1KeyCode = 0x53,
265 kKeypad2KeyCode = 0x54,
266 kKeypad3KeyCode = 0x55,
267 kKeypad4KeyCode = 0x56,
268 kKeypad5KeyCode = 0x57,
269 kKeypad6KeyCode = 0x58,
270 kKeypad7KeyCode = 0x59,
271 kKeypad8KeyCode = 0x5B,
272 kKeypad9KeyCode = 0x5C,
274 kKeypadMultiplyKeyCode = 0x43,
275 kKeypadAddKeyCode = 0x45,
276 kKeypadSubtractKeyCode = 0x4E,
277 kKeypadDecimalKeyCode = 0x41,
278 kKeypadDivideKeyCode = 0x4B,
279 kKeypadEqualsKeyCode = 0x51, // no correpsonding gecko key code
280 kEnterKeyCode = 0x4C,
281 kReturnKeyCode = 0x24,
282 kPowerbookEnterKeyCode = 0x34, // Enter on Powerbook's keyboard is different
284 kInsertKeyCode = 0x72, // also help key
285 kDeleteKeyCode = 0x75, // also forward delete key
287 kTildeKeyCode = 0x32,
288 kBackspaceKeyCode = 0x33,
291 kPageUpKeyCode = 0x74,
292 kPageDownKeyCode = 0x79,
293 kLeftArrowKeyCode = 0x7B,
294 kRightArrowKeyCode = 0x7C,
295 kUpArrowKeyCode = 0x7E,
296 kDownArrowKeyCode = 0x7D
299 /* Convenience routines to go from a gecko rect to cocoa NSRects and back
301 * Gecko rects (nsRect) contain an origin (x,y) in a coordinate
302 * system with (0,0) in the top-left of the screen. Cocoa rects
303 * (NSRect) contain an origin (x,y) in a coordinate system with
304 * (0,0) in the bottom-left of the screen. Both nsRect and NSRect
305 * contain width/height info, with no difference in their use.
306 * If a Cocoa rect is from a flipped view, there is no need to
307 * convert coordinate systems.
311 GeckoRectToNSRect(const nsIntRect & inGeckoRect, NSRect & outCocoaRect)
313 outCocoaRect.origin.x = inGeckoRect.x;
314 outCocoaRect.origin.y = inGeckoRect.y;
315 outCocoaRect.size.width = inGeckoRect.width;
316 outCocoaRect.size.height = inGeckoRect.height;
320 NSRectToGeckoRect(const NSRect & inCocoaRect, nsIntRect & outGeckoRect)
322 outGeckoRect.x = NSToIntRound(inCocoaRect.origin.x);
323 outGeckoRect.y = NSToIntRound(inCocoaRect.origin.y);
324 outGeckoRect.width = NSToIntRound(inCocoaRect.origin.x + inCocoaRect.size.width) - outGeckoRect.x;
325 outGeckoRect.height = NSToIntRound(inCocoaRect.origin.y + inCocoaRect.size.height) - outGeckoRect.y;
329 ConvertGeckoRectToMacRect(const nsIntRect& aRect, Rect& outMacRect)
331 outMacRect.left = aRect.x;
332 outMacRect.top = aRect.y;
333 outMacRect.right = aRect.x + aRect.width;
334 outMacRect.bottom = aRect.y + aRect.height;
337 // Flips a screen coordinate from a point in the cocoa coordinate system (bottom-left rect) to a point
338 // that is a "flipped" cocoa coordinate system (starts in the top-left).
340 FlipCocoaScreenCoordinate(NSPoint &inPoint)
342 inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
346 InitNPCocoaEvent(NPCocoaEvent* event)
348 memset(event, 0, sizeof(NPCocoaEvent));
352 UnderlineAttributeToTextRangeType(PRUint32 aUnderlineStyle, NSRange selRange)
355 NSLog(@"****in underlineAttributeToTextRangeType = %d", aUnderlineStyle);
358 // For more info on the underline attribute, please see:
359 // http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/AttributedStrings/Tasks/AccessingAttrs.html
360 // We are not clear where the define for value 2 is right now.
361 // To see this value in japanese ime, type 'aaaaaaaaa' and hit space to make the
362 // ime send you some part of text in 1 (NSSingleUnderlineStyle) and some part in 2.
363 // ftang will ask apple for more details
365 // It probably means show 1-pixel thickness underline vs 2-pixel thickness.
368 if (selRange.length == 0) {
369 switch (aUnderlineStyle) {
371 attr = NS_TEXTRANGE_RAWINPUT;
375 attr = NS_TEXTRANGE_SELECTEDRAWTEXT;
380 switch (aUnderlineStyle) {
382 attr = NS_TEXTRANGE_CONVERTEDTEXT;
386 attr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
394 CountRanges(NSAttributedString *aString)
396 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
398 // Iterate through aString for the NSUnderlineStyleAttributeName and count the
399 // different segments adjusting limitRange as we go.
401 NSRange effectiveRange;
402 NSRange limitRange = NSMakeRange(0, [aString length]);
403 while (limitRange.length > 0) {
404 [aString attribute:NSUnderlineStyleAttributeName
405 atIndex:limitRange.location
406 longestEffectiveRange:&effectiveRange
408 limitRange = NSMakeRange(NSMaxRange(effectiveRange),
409 NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
414 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
418 ConvertAttributeToGeckoRange(NSAttributedString *aString, NSRange markRange, NSRange selRange, PRUint32 inCount, nsTextRange* aRanges)
420 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
422 // Convert the Cocoa range into the nsTextRange Array used in Gecko.
423 // Iterate through the attributed string and map the underline attribute to Gecko IME textrange attributes.
424 // We may need to change the code here if we change the implementation of validAttributesForMarkedText.
426 NSRange effectiveRange;
427 NSRange limitRange = NSMakeRange(0, [aString length]);
428 while ((limitRange.length > 0) && (i < inCount)) {
429 id attributeValue = [aString attribute:NSUnderlineStyleAttributeName
430 atIndex:limitRange.location
431 longestEffectiveRange:&effectiveRange
433 aRanges[i].mStartOffset = effectiveRange.location;
434 aRanges[i].mEndOffset = NSMaxRange(effectiveRange);
435 aRanges[i].mRangeType = UnderlineAttributeToTextRangeType([attributeValue intValue], selRange);
436 limitRange = NSMakeRange(NSMaxRange(effectiveRange),
437 NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
440 // Get current caret position.
441 aRanges[i].mStartOffset = selRange.location + selRange.length;
442 aRanges[i].mEndOffset = aRanges[i].mStartOffset;
443 aRanges[i].mRangeType = NS_TEXTRANGE_CARETPOSITION;
445 NS_OBJC_END_TRY_ABORT_BLOCK;
449 FillTextRangeInTextEvent(nsTextEvent *aTextEvent, NSAttributedString* aString, NSRange markRange, NSRange selRange)
451 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
453 // Count the number of segments in the attributed string and add one more count for sending current caret position to Gecko.
454 // Allocate the right size of nsTextRange and draw caret at right position.
455 // Convert the attributed string into an array of nsTextRange and get current caret position by calling above functions.
456 PRUint32 count = CountRanges(aString) + 1;
457 aTextEvent->rangeArray = new nsTextRange[count];
458 if (aTextEvent->rangeArray) {
459 aTextEvent->rangeCount = count;
460 ConvertAttributeToGeckoRange(aString, markRange, selRange, aTextEvent->rangeCount, aTextEvent->rangeArray);
463 NS_OBJC_END_TRY_ABORT_BLOCK;
466 #if defined(DEBUG) && defined(PR_LOGGING)
468 static void DebugPrintAllKeyboardLayouts()
470 nsCocoaTextInputHandler::DebugPrintAllKeyboardLayouts(sCocoaLog);
471 nsCocoaIMEHandler::DebugPrintAllIMEModes(sCocoaLog);
474 #endif // defined(DEBUG) && defined(PR_LOGGING)
478 nsChildView::nsChildView() : nsBaseWidget()
480 , mParentView(nsnull)
481 , mParentWidget(nsnull)
484 , mPluginDrawing(PR_FALSE)
485 , mIsDispatchPaint(PR_FALSE)
486 , mPluginInstanceOwner(nsnull)
490 sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
492 DebugPrintAllKeyboardLayouts();
497 memset(&mPluginCGContext, 0, sizeof(mPluginCGContext));
498 #ifndef NP_NO_QUICKDRAW
499 memset(&mPluginQDPort, 0, sizeof(mPluginQDPort));
502 SetBackgroundColor(NS_RGB(255, 255, 255));
503 SetForegroundColor(NS_RGB(0, 0, 0));
506 nsChildView::~nsChildView()
508 // Notify the children that we're gone. childView->ResetParent() can change
509 // our list of children while it's being iterated, so the way we iterate the
510 // list must allow for this.
511 for (nsIWidget* kid = mLastChild; kid;) {
512 nsChildView* childView = static_cast<nsChildView*>(kid);
513 kid = kid->GetPrevSibling();
514 childView->ResetParent();
517 NS_WARN_IF_FALSE(mOnDestroyCalled, "nsChildView object destroyed without calling Destroy()");
519 // An nsChildView object that was in use can be destroyed without Destroy()
520 // ever being called on it. So we also need to do a quick, safe cleanup
521 // here (it's too late to just call Destroy(), which can cause crashes).
522 // It's particularly important to make sure widgetDestroyed is called on our
523 // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
524 // mGeckoChild are used throughout the ChildView class to tell if it's safe
525 // to use a ChildView object.
526 [mView widgetDestroyed]; // Safe if mView is nil.
528 TearDownView(); // Safe if called twice.
531 NS_IMPL_ISUPPORTS_INHERITED1(nsChildView, nsBaseWidget, nsIPluginWidget)
533 nsresult nsChildView::Create(nsIWidget *aParent,
534 nsNativeWidget aNativeParent,
535 const nsIntRect &aRect,
536 EVENT_CALLBACK aHandleEventFunction,
537 nsIDeviceContext *aContext,
538 nsIAppShell *aAppShell,
539 nsIToolkit *aToolkit,
540 nsWidgetInitData *aInitData)
542 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
544 // Because the hidden window is created outside of an event loop,
545 // we need to provide an autorelease pool to avoid leaking cocoa objects
547 nsAutoreleasePool localPool;
549 // See NSView (MethodSwizzling) below.
550 if (!gChildViewMethodsSwizzled) {
551 nsToolkit::SwizzleMethods([NSView class], @selector(mouseDownCanMoveWindow),
552 @selector(nsChildView_NSView_mouseDownCanMoveWindow));
554 Class IMKInputSessionClass = ::NSClassFromString(@"IMKInputSession");
555 nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(handleEvent:),
556 @selector(nsChildView_IMKInputSession_handleEvent:));
557 nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(commitComposition),
558 @selector(nsChildView_IMKInputSession_commitComposition));
559 nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(finishSession),
560 @selector(nsChildView_IMKInputSession_finishSession));
562 gChildViewMethodsSwizzled = PR_TRUE;
567 BaseCreate(aParent, aRect, aHandleEventFunction,
568 aContext, aAppShell, aToolkit, aInitData);
570 // inherit things from the parent view and create our parallel
571 // NSView in the Cocoa display system
574 // This is the case when we're the popup content view of a popup window.
575 SetBackgroundColor(aParent->GetBackgroundColor());
576 SetForegroundColor(aParent->GetForegroundColor());
578 // inherit the top-level window. NS_NATIVE_WIDGET is always a NSView
579 // regardless of if we're asking a window or a view (for compatibility
581 mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET);
582 mParentWidget = aParent;
584 // This is the normal case. When we're the root widget of the view hiararchy,
585 // aNativeParent will be the contentView of our window, since that's what
586 // nsCocoaWindow returns when asked for an NS_NATIVE_VIEW.
587 mParentView = reinterpret_cast<NSView*>(aNativeParent);
590 // create our parallel NSView and hook it up to our parent. Recall
591 // that NS_NATIVE_WIDGET is the NSView.
593 GeckoRectToNSRect(mBounds, r);
594 mView = [CreateCocoaView(r) retain];
595 if (!mView) return NS_ERROR_FAILURE;
597 [(ChildView*)mView setIsPluginView:(mWindowType == eWindowType_plugin)];
599 // If this view was created in a Gecko view hierarchy, the initial state
600 // is hidden. If the view is attached only to a native NSView but has
601 // no Gecko parent (as in embedding), the initial state is visible.
603 [mView setHidden:YES];
607 // Hook it up in the NSView hierarchy.
609 [mParentView addSubview:mView];
612 // if this is a ChildView, make sure that our per-window data
614 if ([mView isKindOfClass:[ChildView class]])
615 [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]];
617 mTextInputHandler.Init(this);
621 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
624 // Creates the appropriate child view. Override to create something other than
625 // our |ChildView| object. Autoreleases, so caller must retain.
627 nsChildView::CreateCocoaView(NSRect inFrame)
629 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
631 return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this] autorelease];
633 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
636 void nsChildView::TearDownView()
638 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
643 NSWindow* win = [mView window];
644 NSResponder* responder = [win firstResponder];
646 // We're being unhooked from the view hierarchy, don't leave our view
647 // or a child view as the window first responder.
648 if (responder && [responder isKindOfClass:[NSView class]] &&
649 [(NSView*)responder isDescendantOf:mView]) {
650 [win makeFirstResponder:[mView superview]];
653 // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
654 // win has retained mView, and will detach it from the view hierarchy and
655 // release it when necessary (when win is itself destroyed (in a call to
656 // [win dealloc])). So all we need to do here is call [mView release] (to
657 // match the call to [mView retain] in nsChildView::StandardCreate()).
658 // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
659 // mView to be released again and dealloced, while remaining win's
660 // contentView. So if we do that here, win will (for a short while) have
661 // an invalid contentView (for the consequences see bmo bugs 381087 and
663 if ([mView isEqual:[win contentView]]) {
666 // Stop NSView hierarchy being changed during [ChildView drawRect:]
667 [mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
671 NS_OBJC_END_TRY_ABORT_BLOCK;
675 nsChildView::GetXULWindowWidget()
677 id windowDelegate = [[mView window] delegate];
678 if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
679 return [(WindowDelegate *)windowDelegate geckoWidget];
684 NS_IMETHODIMP nsChildView::Destroy()
686 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
688 if (mOnDestroyCalled)
690 mOnDestroyCalled = PR_TRUE;
692 [mView widgetDestroyed];
694 nsBaseWidget::Destroy();
696 ReportDestroyEvent();
701 nsBaseWidget::OnDestroy();
705 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
711 static void PrintViewHierarchy(NSView *view)
714 NSLog(@" view is %x, frame %@", view, NSStringFromRect([view frame]));
715 view = [view superview];
720 // Return native data according to aDataType
721 void* nsChildView::GetNativeData(PRUint32 aDataType)
723 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
725 void* retVal = nsnull;
729 case NS_NATIVE_WIDGET:
730 case NS_NATIVE_DISPLAY:
731 retVal = (void*)mView;
734 case NS_NATIVE_WINDOW:
735 retVal = [mView window];
738 case NS_NATIVE_GRAPHIC:
739 NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
743 case NS_NATIVE_OFFSETX:
747 case NS_NATIVE_OFFSETY:
751 case NS_NATIVE_PLUGIN_PORT:
752 case NS_NATIVE_PLUGIN_PORT_QD:
753 case NS_NATIVE_PLUGIN_PORT_CG:
755 // The NP_CGContext pointer should always be NULL in the Cocoa event model.
756 if ([(ChildView*)mView pluginEventModel] == NPEventModelCocoa)
760 #ifndef NP_NO_QUICKDRAW
761 if (aDataType != NS_NATIVE_PLUGIN_PORT_CG) {
762 retVal = (void*)&mPluginQDPort;
766 retVal = (void*)&mPluginCGContext;
773 NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
778 nsTransparencyMode nsChildView::GetTransparencyMode()
780 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
782 nsCocoaWindow* windowWidget = GetXULWindowWidget();
783 return windowWidget ? windowWidget->GetTransparencyMode() : eTransparencyOpaque;
785 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque);
788 // This is called by nsContainerFrame on the root widget for all window types
789 // except popup windows (when nsCocoaWindow::SetTransparencyMode is used instead).
790 void nsChildView::SetTransparencyMode(nsTransparencyMode aMode)
792 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
794 nsCocoaWindow* windowWidget = GetXULWindowWidget();
796 windowWidget->SetTransparencyMode(aMode);
799 NS_OBJC_END_TRY_ABORT_BLOCK;
802 NS_IMETHODIMP nsChildView::IsVisible(PRBool& outState)
804 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
810 // mVisible does not accurately reflect the state of a hidden tabbed view
811 // so verify that the view has a window as well
812 outState = ([mView window] != nil);
813 // now check native widget hierarchy visibility
814 if (outState && NSIsEmptyRect([mView visibleRect])) {
821 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
824 void nsChildView::HidePlugin()
826 NS_ASSERTION(mWindowType == eWindowType_plugin,
827 "HidePlugin called on non-plugin view");
829 #ifndef NP_NO_QUICKDRAW
830 if (mPluginInstanceOwner && mView &&
831 [(ChildView*)mView pluginDrawingModel] == NPDrawingModelQuickDraw) {
833 mPluginInstanceOwner->GetWindow(window);
834 nsCOMPtr<nsIPluginInstance> instance;
835 mPluginInstanceOwner->GetInstance(*getter_AddRefs(instance));
836 if (window && instance) {
837 window->clipRect.top = 0;
838 window->clipRect.left = 0;
839 window->clipRect.bottom = 0;
840 window->clipRect.right = 0;
841 instance->SetWindow(window);
847 void nsChildView::UpdatePluginPort()
849 NS_ASSERTION(mWindowType == eWindowType_plugin,
850 "UpdatePluginPort called on non-plugin view");
852 #if !defined(NP_NO_CARBON) || !defined(NP_NO_QUICKDRAW)
853 NSWindow* cocoaWindow = [mView window];
854 WindowRef carbonWindow = cocoaWindow ? (WindowRef)[cocoaWindow windowRef] : NULL;
858 #ifndef NP_NO_QUICKDRAW
859 || [(ChildView*)mView pluginDrawingModel] != NPDrawingModelQuickDraw
862 // [NSGraphicsContext currentContext] is supposed to "return the
863 // current graphics context of the current thread." But sometimes
864 // (when called while mView isn't focused for drawing) it returns a
865 // graphics context for the wrong window. [window graphicsContext]
866 // (which "provides the graphics context associated with the window
867 // for the current thread") seems always to return the "right"
868 // graphics context. See bug 500130.
869 mPluginCGContext.context = NULL;
870 mPluginCGContext.window = NULL;
873 mPluginCGContext.context = (CGContextRef)[[cocoaWindow graphicsContext] graphicsPort];
874 mPluginCGContext.window = carbonWindow;
878 #ifndef NP_NO_QUICKDRAW
881 mPluginQDPort.port = ::GetWindowPort(carbonWindow);
883 NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
884 NSRect frame = [[cocoaWindow contentView] frame];
885 viewOrigin.y = frame.size.height - viewOrigin.y;
887 // need to convert view's origin to window coordinates.
888 // then, encode as "SetOrigin" ready values.
889 mPluginQDPort.portx = (PRInt32)-viewOrigin.x;
890 mPluginQDPort.porty = (PRInt32)-viewOrigin.y;
892 mPluginQDPort.port = NULL;
898 static void HideChildPluginViews(NSView* aView)
900 NSArray* subviews = [aView subviews];
902 for (unsigned int i = 0; i < [subviews count]; ++i) {
903 NSView* view = [subviews objectAtIndex: i];
905 if (![view isKindOfClass:[ChildView class]])
908 ChildView* childview = static_cast<ChildView*>(view);
909 if ([childview isPluginView]) {
910 nsChildView* widget = static_cast<nsChildView*>([childview widget]);
912 widget->HidePlugin();
915 HideChildPluginViews(view);
920 // Hide or show this component
921 NS_IMETHODIMP nsChildView::Show(PRBool aState)
923 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
925 if (aState != mVisible) {
926 // Provide an autorelease pool because this gets called during startup
927 // on the "hidden window", resulting in cocoa object leakage if there's
929 nsAutoreleasePool localPool;
931 [mView setHidden:!aState];
933 if (!mVisible && IsPluginView())
938 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
941 // Change the parent of this widget
943 nsChildView::SetParent(nsIWidget* aNewParent)
945 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
947 NS_ENSURE_ARG(aNewParent);
949 if (mOnDestroyCalled)
952 // make sure we stay alive
953 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
955 // remove us from our existing parent
957 mParentWidget->RemoveChild(this);
959 nsresult rv = ReparentNativeWidget(aNewParent);
960 if (NS_SUCCEEDED(rv))
961 mParentWidget = aNewParent;
963 // add us to the new parent
964 mParentWidget->AddChild(this);
967 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
971 nsChildView::ReparentNativeWidget(nsIWidget* aNewParent)
973 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
975 NS_PRECONDITION(aNewParent, "");
977 if (mOnDestroyCalled)
980 NSView<mozView>* newParentView =
981 (NSView*)aNewParent->GetNativeData(NS_NATIVE_WIDGET);
982 NS_ENSURE_TRUE(newParentView, NS_ERROR_FAILURE);
984 // we hold a ref to mView, so this is safe
985 [mView removeFromSuperview];
986 mParentView = newParentView;
987 [mParentView addSubview:mView];
990 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
993 void nsChildView::ResetParent()
995 if (!mOnDestroyCalled) {
997 mParentWidget->RemoveChild(this);
999 [mView removeFromSuperview];
1001 mParentWidget = nsnull;
1005 nsChildView::GetParent()
1007 return mParentWidget;
1011 nsChildView::GetDPI()
1013 NSWindow* window = [mView window];
1014 if (window && [window isKindOfClass:[BaseWindow class]]) {
1015 return [(BaseWindow*)window getDPI];
1021 NS_IMETHODIMP nsChildView::Enable(PRBool aState)
1026 NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
1034 NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
1036 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1038 NSWindow* window = [mView window];
1040 [window makeFirstResponder:mView];
1043 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1046 // Override to set the cursor on the mac
1047 NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
1049 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1051 if ([mView isDragInProgress])
1052 return NS_OK; // Don't change the cursor during dragging.
1054 nsBaseWidget::SetCursor(aCursor);
1055 return [[nsCursorManager sharedInstance] setCursor:aCursor];
1057 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1060 // implement to fix "hidden virtual function" warning
1061 NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
1062 PRUint32 aHotspotX, PRUint32 aHotspotY)
1064 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1066 nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
1067 return [[nsCursorManager sharedInstance] setCursorWithImage:aCursor hotSpotX:aHotspotX hotSpotY:aHotspotY];
1069 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1074 // Get this component dimension
1075 NS_IMETHODIMP nsChildView::GetBounds(nsIntRect &aRect)
1080 NSRect frame = [mView frame];
1081 NSRectToGeckoRect(frame, aRect);
1086 NS_IMETHODIMP nsChildView::ConstrainPosition(PRBool aAllowSlop,
1087 PRInt32 *aX, PRInt32 *aY)
1092 // Move this component, aX and aY are in the parent widget coordinate system
1093 NS_IMETHODIMP nsChildView::Move(PRInt32 aX, PRInt32 aY)
1095 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1097 if (!mView || (mBounds.x == aX && mBounds.y == aY))
1104 GeckoRectToNSRect(mBounds, r);
1108 [mView setNeedsDisplay:YES];
1114 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1117 NS_IMETHODIMP nsChildView::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1119 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1121 if (!mView || (mBounds.width == aWidth && mBounds.height == aHeight))
1124 mBounds.width = aWidth;
1125 mBounds.height = aHeight;
1128 GeckoRectToNSRect(mBounds, r);
1131 if (mVisible && aRepaint)
1132 [mView setNeedsDisplay:YES];
1138 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1141 NS_IMETHODIMP nsChildView::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1143 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1145 BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
1146 BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
1147 if (!mView || (!isMoving && !isResizing))
1155 mBounds.width = aWidth;
1156 mBounds.height = aHeight;
1160 GeckoRectToNSRect(mBounds, r);
1163 if (mVisible && aRepaint)
1164 [mView setNeedsDisplay:YES];
1168 if (mOnDestroyCalled)
1176 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1179 static const PRInt32 resizeIndicatorWidth = 15;
1180 static const PRInt32 resizeIndicatorHeight = 15;
1181 PRBool nsChildView::ShowsResizeIndicator(nsIntRect* aResizerRect)
1183 NSView *topLevelView = mView, *superView = nil;
1184 while ((superView = [topLevelView superview]))
1185 topLevelView = superView;
1187 if (![[topLevelView window] showsResizeIndicator] ||
1188 !([[topLevelView window] styleMask] & NSResizableWindowMask))
1192 NSSize bounds = [topLevelView bounds].size;
1193 NSPoint corner = NSMakePoint(bounds.width, [topLevelView isFlipped] ? bounds.height : 0);
1194 corner = [topLevelView convertPoint:corner toView:mView];
1195 aResizerRect->SetRect(NSToIntRound(corner.x) - resizeIndicatorWidth,
1196 NSToIntRound(corner.y) - resizeIndicatorHeight,
1197 resizeIndicatorWidth, resizeIndicatorHeight);
1202 // In QuickDraw mode the coordinate system used here should be that of the
1203 // browser window's content region (defined as everything but the 22-pixel
1204 // high titlebar). But in CoreGraphics mode the coordinate system should be
1205 // that of the browser window as a whole (including its titlebar). Both
1206 // coordinate systems have a top-left origin. See bmo bug 474491.
1208 // There's a bug in this method's code -- it currently uses the QuickDraw
1209 // coordinate system for both the QuickDraw and CoreGraphics drawing modes.
1210 // This bug is fixed by the patch for bug 474491. But the Flash plugin (both
1211 // version 10.0.12.36 from Adobe and version 9.0 r151 from Apple) has Mozilla-
1212 // specific code to work around this bug, which breaks when we fix it (see bmo
1213 // bug 477077). So we'll need to coordinate releasing a fix for this bug with
1214 // Adobe and other major plugin vendors that support the CoreGraphics mode.
1215 NS_IMETHODIMP nsChildView::GetPluginClipRect(nsIntRect& outClipRect, nsIntPoint& outOrigin, PRBool& outWidgetVisible)
1217 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1219 NS_ASSERTION(mWindowType == eWindowType_plugin,
1220 "GetPluginClipRect must only be called on a plugin widget");
1221 if (mWindowType != eWindowType_plugin) return NS_ERROR_FAILURE;
1223 NSWindow* window = [mView window];
1224 if (!window) return NS_ERROR_FAILURE;
1226 NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
1227 NSRect frame = [[window contentView] frame];
1228 viewOrigin.y = frame.size.height - viewOrigin.y;
1230 // set up the clipping region for plugins.
1231 NSRect visibleBounds = [mView visibleRect];
1232 NSPoint clipOrigin = [mView convertPoint:visibleBounds.origin toView:nil];
1234 // Convert from cocoa to QuickDraw coordinates
1235 clipOrigin.y = frame.size.height - clipOrigin.y;
1237 outClipRect.x = NSToIntRound(clipOrigin.x);
1238 outClipRect.y = NSToIntRound(clipOrigin.y);
1240 // need to convert view's origin to window coordinates.
1241 // then, encode as "SetOrigin" ready values.
1242 outOrigin.x = -NSToIntRound(viewOrigin.x);
1243 outOrigin.y = -NSToIntRound(viewOrigin.y);
1246 IsVisible(isVisible);
1247 if (isVisible && [mView window] != nil) {
1248 outClipRect.width = NSToIntRound(visibleBounds.origin.x + visibleBounds.size.width) - NSToIntRound(visibleBounds.origin.x);
1249 outClipRect.height = NSToIntRound(visibleBounds.origin.y + visibleBounds.size.height) - NSToIntRound(visibleBounds.origin.y);
1252 nsIntRect clipBounds;
1253 for (PRInt32 i = 0; i < mClipRectCount; ++i) {
1254 clipBounds.UnionRect(clipBounds, mClipRects[i]);
1256 outClipRect.IntersectRect(outClipRect, clipBounds - outOrigin);
1259 // XXXroc should this be !outClipRect.IsEmpty()?
1260 outWidgetVisible = PR_TRUE;
1263 outClipRect.width = 0;
1264 outClipRect.height = 0;
1265 outWidgetVisible = PR_FALSE;
1270 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1273 #ifndef NP_NO_CARBON
1274 static void InitializeEventRecord(EventRecord* event, Point* aMousePosition)
1276 memset(event, 0, sizeof(EventRecord));
1277 if (aMousePosition) {
1278 event->where = *aMousePosition;
1280 ::GetGlobalMouse(&event->where);
1282 event->when = ::TickCount();
1283 event->modifiers = ::GetCurrentKeyModifiers();
1287 void nsChildView::PaintQD()
1289 #ifndef NP_NO_CARBON
1290 void *pluginPort = this->GetNativeData(NS_NATIVE_PLUGIN_PORT_QD);
1291 void *window = ::GetWindowFromPort(static_cast<NP_Port*>(pluginPort)->port);
1293 NS_SUCCEEDED(StartDrawPlugin());
1294 EventRecord updateEvent;
1295 InitializeEventRecord(&updateEvent, nsnull);
1296 updateEvent.what = updateEvt;
1297 updateEvent.message = UInt32(window);
1299 nsCOMPtr<nsIPluginInstance> instance;
1300 mPluginInstanceOwner->GetInstance(*getter_AddRefs(instance));
1302 instance->HandleEvent(&updateEvent, nsnull);
1307 NS_IMETHODIMP nsChildView::StartDrawPlugin()
1309 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1311 NS_ASSERTION(mWindowType == eWindowType_plugin,
1312 "StartDrawPlugin must only be called on a plugin widget");
1313 if (mWindowType != eWindowType_plugin) return NS_ERROR_FAILURE;
1315 // This code is necessary for both Quickdraw and CoreGraphics in 32-bit builds.
1316 // See comments below about why. In 64-bit CoreGraphics mode we will not keep
1317 // this region up to date, plugins should not depend on it.
1319 NSWindow* window = [mView window];
1321 return NS_ERROR_FAILURE;
1323 // In QuickDraw drawing mode, prevent reentrant handling of any plugin event
1324 // (this emulates behavior on the 1.8 branch, where only QuickDraw mode is
1325 // supported). But in CoreGraphics drawing mode only do this if the current
1326 // plugin event isn't an update/paint event. This allows popupcontextmenu()
1327 // to work properly from a plugin that supports the Cocoa event model,
1328 // without regressing bug 409615. See bug 435041. (StartDrawPlugin() and
1329 // EndDrawPlugin() wrap every call to nsIPluginInstance::HandleEvent() --
1330 // not just calls that "draw" or paint.)
1331 PRBool isQDPlugin = [(ChildView*)mView pluginDrawingModel] == NPDrawingModelQuickDraw;
1332 if (isQDPlugin || mIsDispatchPaint) {
1334 return NS_ERROR_FAILURE;
1337 // It appears that the WindowRef from which we get the plugin port undergoes the
1338 // traditional BeginUpdate/EndUpdate cycle, which, if you recall, sets the visible
1339 // region to the intersection of the visible region and the update region. Since
1340 // we don't know here if we're being drawn inside a BeginUpdate/EndUpdate pair
1341 // (which seem to occur in [NSWindow display]), and we don't want to have the burden
1342 // of correctly doing Carbon invalidates of the plugin rect, we manually set the
1343 // visible region to be the entire port every time. It is necessary to set up our
1344 // window's port even for CoreGraphics plugins, because they may still use Carbon
1345 // internally (see bug #420527 for details).
1346 CGrafPtr port = ::GetWindowPort(WindowRef([window windowRef]));
1348 port = mPluginQDPort.port;
1351 RgnHandle pluginRegion = ::NewRgn();
1353 PRBool portChanged = (port != CGrafPtr(GetQDGlobalsThePort()));
1358 ::GetGWorld(&oldPort, &oldDevice);
1359 ::SetGWorld(port, ::IsPortOffscreen(port) ? nsnull : ::GetMainDevice());
1364 nsIntRect clipRect; // this is in native window coordinates
1367 GetPluginClipRect(clipRect, origin, visible);
1369 // XXX if we're not visible, set an empty clip region?
1371 ConvertGeckoRectToMacRect(clipRect, pluginRect);
1373 ::RectRgn(pluginRegion, &pluginRect);
1374 ::SetPortVisibleRegion(port, pluginRegion);
1375 ::SetPortClipRegion(port, pluginRegion);
1377 // now set up the origin for the plugin
1378 ::SetOrigin(origin.x, origin.y);
1380 ::DisposeRgn(pluginRegion);
1383 ::SetGWorld(oldPort, oldDevice);
1387 mPluginDrawing = PR_TRUE;
1390 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1393 NS_IMETHODIMP nsChildView::EndDrawPlugin()
1395 NS_ASSERTION(mWindowType == eWindowType_plugin,
1396 "EndDrawPlugin must only be called on a plugin widget");
1397 if (mWindowType != eWindowType_plugin) return NS_ERROR_FAILURE;
1399 mPluginDrawing = PR_FALSE;
1403 NS_IMETHODIMP nsChildView::SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner)
1405 mPluginInstanceOwner = aInstanceOwner;
1410 NS_IMETHODIMP nsChildView::SetPluginEventModel(int inEventModel)
1412 [(ChildView*)mView setPluginEventModel:(NPEventModel)inEventModel];
1416 NS_IMETHODIMP nsChildView::GetPluginEventModel(int* outEventModel)
1418 *outEventModel = [(ChildView*)mView pluginEventModel];
1422 NS_IMETHODIMP nsChildView::SetPluginDrawingModel(int inDrawingModel)
1424 [(ChildView*)mView setPluginDrawingModel:(NPDrawingModel)inDrawingModel];
1428 NS_IMETHODIMP nsChildView::StartComplexTextInputForCurrentEvent()
1430 [(ChildView*)mView pluginRequestsComplexTextInputForCurrentEvent];
1434 static NSString* ToNSString(const nsAString& aString)
1436 return [NSString stringWithCharacters:aString.BeginReading()
1437 length:aString.Length()];
1440 struct KeyboardLayoutOverride {
1441 PRInt32 mKeyboardLayout;
1442 PRBool mOverrideEnabled;
1445 static KeyboardLayoutOverride gOverrideKeyboardLayout;
1447 static const PRUint32 sModifierFlagMap[][2] = {
1448 { nsIWidget::CAPS_LOCK, NSAlphaShiftKeyMask },
1449 { nsIWidget::SHIFT_L, NSShiftKeyMask },
1450 { nsIWidget::SHIFT_R, NSShiftKeyMask },
1451 { nsIWidget::CTRL_L, NSControlKeyMask },
1452 { nsIWidget::CTRL_R, NSControlKeyMask },
1453 { nsIWidget::ALT_L, NSAlternateKeyMask },
1454 { nsIWidget::ALT_R, NSAlternateKeyMask },
1455 { nsIWidget::COMMAND_L, NSCommandKeyMask },
1456 { nsIWidget::COMMAND_R, NSCommandKeyMask },
1457 { nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask },
1458 { nsIWidget::HELP, NSHelpKeyMask },
1459 { nsIWidget::FUNCTION, NSFunctionKeyMask }
1461 nsresult nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
1462 PRInt32 aNativeKeyCode,
1463 PRUint32 aModifierFlags,
1464 const nsAString& aCharacters,
1465 const nsAString& aUnmodifiedCharacters)
1467 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1469 PRUint32 modifierFlags = 0;
1470 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sModifierFlagMap); ++i) {
1471 if (aModifierFlags & sModifierFlagMap[i][0]) {
1472 modifierFlags |= sModifierFlagMap[i][1];
1475 int windowNumber = [[mView window] windowNumber];
1476 BOOL sendFlagsChangedEvent = NO;
1477 switch (aNativeKeyCode) {
1478 case kCapsLockKeyCode:
1479 case kRCommandKeyCode:
1480 case kCommandKeyCode:
1482 case kOptionkeyCode:
1483 case kControlKeyCode:
1484 case kRShiftKeyCode:
1485 case kROptionKeyCode:
1486 case kRControlKeyCode:
1487 sendFlagsChangedEvent = YES;
1489 NSEventType eventType = sendFlagsChangedEvent ? NSFlagsChanged : NSKeyDown;
1490 NSEvent* downEvent = [NSEvent keyEventWithType:eventType
1491 location:NSMakePoint(0,0)
1492 modifierFlags:modifierFlags
1494 windowNumber:windowNumber
1495 context:[NSGraphicsContext currentContext]
1496 characters:ToNSString(aCharacters)
1497 charactersIgnoringModifiers:ToNSString(aUnmodifiedCharacters)
1499 keyCode:aNativeKeyCode];
1501 NSEvent* upEvent = sendFlagsChangedEvent ? nil :
1502 [ChildView makeNewCocoaEventWithType:NSKeyUp
1503 fromEvent:downEvent];
1505 if (downEvent && (sendFlagsChangedEvent || upEvent)) {
1506 KeyboardLayoutOverride currentLayout = gOverrideKeyboardLayout;
1507 gOverrideKeyboardLayout.mKeyboardLayout = aNativeKeyboardLayout;
1508 gOverrideKeyboardLayout.mOverrideEnabled = PR_TRUE;
1509 [NSApp sendEvent:downEvent];
1511 [NSApp sendEvent:upEvent];
1512 // processKeyDownEvent and keyUp block exceptions so we're sure to
1513 // reach here to restore gOverrideKeyboardLayout
1514 gOverrideKeyboardLayout = currentLayout;
1519 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1522 nsresult nsChildView::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
1523 PRUint32 aNativeMessage,
1524 PRUint32 aModifierFlags)
1526 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1528 // Move the mouse cursor to the requested position and reconnect it to the mouse.
1529 CGWarpMouseCursorPosition(CGPointMake(aPoint.x, aPoint.y));
1530 CGAssociateMouseAndMouseCursorPosition(true);
1532 // aPoint is given with the origin on the top left, but convertScreenToBase
1533 // expects a point in a coordinate system that has its origin on the bottom left.
1534 NSPoint screenPoint = NSMakePoint(aPoint.x, [[NSScreen mainScreen] frame].size.height - aPoint.y);
1535 NSPoint windowPoint = [[mView window] convertScreenToBase:screenPoint];
1537 NSEvent* event = [NSEvent mouseEventWithType:aNativeMessage
1538 location:windowPoint
1539 modifierFlags:aModifierFlags
1540 timestamp:[NSDate timeIntervalSinceReferenceDate]
1541 windowNumber:[[mView window] windowNumber]
1548 return NS_ERROR_FAILURE;
1550 [NSApp sendEvent:event];
1553 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1556 // First argument has to be an NSMenu representing the application's top-level
1557 // menu bar. The returned item is *not* retained.
1558 static NSMenuItem* NativeMenuItemWithLocation(NSMenu* menubar, NSString* locationString)
1560 NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
1561 unsigned int indexCount = [indexes count];
1562 if (indexCount == 0)
1565 NSMenu* currentSubmenu = [NSApp mainMenu];
1566 for (unsigned int i = 0; i < indexCount; i++) {
1568 // We remove the application menu from consideration for the top-level menu
1570 targetIndex = [[indexes objectAtIndex:i] intValue] + 1;
1572 targetIndex = [[indexes objectAtIndex:i] intValue];
1573 int itemCount = [currentSubmenu numberOfItems];
1574 if (targetIndex < itemCount) {
1575 NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
1576 // if this is the last index just return the menu item
1577 if (i == (indexCount - 1))
1579 // if this is not the last index find the submenu and keep going
1580 if ([menuItem hasSubmenu])
1581 currentSubmenu = [menuItem submenu];
1590 // Used for testing native menu system structure and event handling.
1591 NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString)
1593 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1595 NSString* locationString = [NSString stringWithCharacters:indexString.BeginReading() length:indexString.Length()];
1596 NSMenuItem* item = NativeMenuItemWithLocation([NSApp mainMenu], locationString);
1597 // We can't perform an action on an item with a submenu, that will raise
1598 // an obj-c exception.
1599 if (item && ![item hasSubmenu]) {
1600 NSMenu* parent = [item menu];
1602 // NSLog(@"Performing action for native menu item titled: %@\n",
1603 // [[currentSubmenu itemAtIndex:targetIndex] title]);
1604 [parent performActionForItemAtIndex:[parent indexOfItem:item]];
1608 return NS_ERROR_FAILURE;
1610 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1613 // Used for testing native menu system structure and event handling.
1614 NS_IMETHODIMP nsChildView::ForceUpdateNativeMenuAt(const nsAString& indexString)
1616 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1618 nsCocoaWindow *widget = GetXULWindowWidget();
1620 nsMenuBarX* mb = widget->GetMenuBar();
1622 if (indexString.IsEmpty())
1623 mb->ForceNativeMenuReload();
1625 mb->ForceUpdateNativeMenuAt(indexString);
1630 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1635 #ifdef INVALIDATE_DEBUGGING
1637 static Boolean KeyDown(const UInt8 theKey)
1641 return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
1644 static Boolean caps_lock()
1646 return KeyDown(0x39);
1649 static void blinkRect(Rect* r)
1651 StRegionFromPool oldClip;
1652 if (oldClip != NULL)
1657 UInt32 end = ::TickCount() + 5;
1658 while (::TickCount() < end) ;
1661 if (oldClip != NULL)
1665 static void blinkRgn(RgnHandle rgn)
1667 StRegionFromPool oldClip;
1668 if (oldClip != NULL)
1673 UInt32 end = ::TickCount() + 5;
1674 while (::TickCount() < end) ;
1677 if (oldClip != NULL)
1683 // Invalidate this component's visible area
1684 NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect, PRBool aIsSynchronous)
1686 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1688 if (!mView || !mVisible)
1692 GeckoRectToNSRect(aRect, r);
1694 if (aIsSynchronous) {
1695 [mView displayRect:r];
1697 else if ([NSView focusView]) {
1698 // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
1700 [mView setNeedsPendingDisplayInRect:r];
1703 [mView setNeedsDisplayInRect:r];
1708 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1712 nsChildView::GetShouldAccelerate()
1714 // Don't use OpenGL for transparent windows or for popup windows.
1715 if (!mView || ![[mView window] isOpaque] ||
1716 [[mView window] isKindOfClass:[PopupWindow class]])
1719 return nsBaseWidget::GetShouldAccelerate();
1722 inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8)
1724 // return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
1725 return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */
1728 // The OS manages repaints well enough on its own, so we don't have to
1729 // flush them out here. In other words, the OS will automatically call
1730 // displayIfNeeded at the appropriate times, so we don't need to do it
1731 // ourselves. See bmo bug 459319.
1732 NS_IMETHODIMP nsChildView::Update()
1739 nsresult nsChildView::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
1741 for (PRUint32 i = 0; i < aConfigurations.Length(); ++i) {
1742 const Configuration& config = aConfigurations[i];
1743 nsChildView* child = static_cast<nsChildView*>(config.mChild);
1745 nsWindowType kidType;
1746 child->GetWindowType(kidType);
1748 NS_ASSERTION(kidType == eWindowType_plugin,
1749 "Configured widget is not a plugin type");
1750 NS_ASSERTION(child->GetParent() == this,
1751 "Configured widget is not a child of the right widget");
1753 // nsIWidget::Show() doesn't get called on plugin widgets unless we call
1754 // it from here. See bug 592563.
1755 child->Show(!config.mClipRegion.IsEmpty());
1757 PRBool repaint = PR_FALSE;
1758 #ifndef NP_NO_QUICKDRAW
1759 repaint = child->mView &&
1760 [(ChildView*)child->mView pluginDrawingModel] == NPDrawingModelQuickDraw;
1763 config.mBounds.x, config.mBounds.y,
1764 config.mBounds.width, config.mBounds.height,
1767 // Store the clip region here in case GetPluginClipRect needs it.
1768 child->StoreWindowClipRegion(config.mClipRegion);
1773 // Invokes callback and ProcessEvent methods on Event Listener object
1774 NS_IMETHODIMP nsChildView::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
1777 debug_DumpEvent(stdout, event->widget, event, nsCAutoString("something"), 0);
1780 NS_ASSERTION(!(mTextInputHandler.IsIMEComposing() && NS_IS_KEY_EVENT(event)),
1781 "Any key events should not be fired during IME composing");
1783 aStatus = nsEventStatus_eIgnore;
1785 nsCOMPtr<nsIWidget> kungFuDeathGrip = do_QueryInterface(mParentWidget ? mParentWidget : this);
1786 if (mParentWidget) {
1788 mParentWidget->GetWindowType(type);
1789 if (type == eWindowType_popup) {
1790 // use the parent popup's widget if there is no view
1791 void* clientData = nsnull;
1793 event->widget->GetClientData(clientData);
1795 event->widget = mParentWidget;
1799 PRBool restoreIsDispatchPaint = mIsDispatchPaint;
1800 mIsDispatchPaint = mIsDispatchPaint || event->eventStructType == NS_PAINT_EVENT;
1803 aStatus = (*mEventCallback)(event);
1805 mIsDispatchPaint = restoreIsDispatchPaint;
1810 PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event)
1812 nsEventStatus status;
1813 DispatchEvent(&event, status);
1814 return ConvertStatus(status);
1819 PRBool nsChildView::ReportDestroyEvent()
1821 nsGUIEvent event(PR_TRUE, NS_DESTROY, this);
1822 event.time = PR_IntervalNow();
1823 return DispatchWindowEvent(event);
1826 PRBool nsChildView::ReportMoveEvent()
1828 nsGUIEvent moveEvent(PR_TRUE, NS_MOVE, this);
1829 moveEvent.refPoint.x = mBounds.x;
1830 moveEvent.refPoint.y = mBounds.y;
1831 moveEvent.time = PR_IntervalNow();
1832 return DispatchWindowEvent(moveEvent);
1835 PRBool nsChildView::ReportSizeEvent()
1837 nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
1838 sizeEvent.time = PR_IntervalNow();
1839 sizeEvent.windowSize = &mBounds;
1840 sizeEvent.mWinWidth = mBounds.width;
1841 sizeEvent.mWinHeight = mBounds.height;
1842 return DispatchWindowEvent(sizeEvent);
1847 // Return the offset between this child view and the screen.
1848 // @return -- widget origin in screen coordinates
1849 nsIntPoint nsChildView::WidgetToScreenOffset()
1851 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
1857 // 1. First translate this point into window coords. The returned point is always in
1858 // bottom-left coordinates.
1859 temp = [mView convertPoint:temp toView:nil];
1861 // 2. We turn the window-coord rect's origin into screen (still bottom-left) coords.
1862 temp = [[mView window] convertBaseToScreen:temp];
1864 // 3. Since we're dealing in bottom-left coords, we need to make it top-left coords
1865 // before we pass it back to Gecko.
1866 FlipCocoaScreenCoordinate(temp);
1868 return nsIntPoint(NSToIntRound(temp.x), NSToIntRound(temp.y));
1870 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0,0));
1873 NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener,
1874 nsIMenuRollup * aMenuRollup,
1876 PRBool aConsumeRollupEvent)
1878 // this never gets called, only top-level windows can be rollup widgets
1882 NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
1884 // child views don't have titles
1888 NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
1890 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1892 [NSApp requestUserAttention:NSInformationalRequest];
1895 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1899 PRBool nsChildView::DoHasPendingInputEvent()
1901 return sLastInputEventCount != GetCurrentInputEventCount();
1905 PRUint32 nsChildView::GetCurrentInputEventCount()
1907 // Can't use kCGAnyInputEventType because that updates too rarely for us (and
1908 // always in increments of 30+!) and because apparently it's sort of broken
1909 // on Tiger. So just go ahead and query the counters we care about.
1910 static const CGEventType eventTypes[] = {
1911 kCGEventLeftMouseDown,
1912 kCGEventLeftMouseUp,
1913 kCGEventRightMouseDown,
1914 kCGEventRightMouseUp,
1916 kCGEventLeftMouseDragged,
1917 kCGEventRightMouseDragged,
1920 kCGEventScrollWheel,
1921 kCGEventTabletPointer,
1922 kCGEventOtherMouseDown,
1923 kCGEventOtherMouseUp,
1924 kCGEventOtherMouseDragged
1927 PRUint32 eventCount = 0;
1928 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(eventTypes); ++i) {
1930 CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState,
1937 void nsChildView::UpdateCurrentInputEventCount()
1939 sLastInputEventCount = GetCurrentInputEventCount();
1942 PRBool nsChildView::HasPendingInputEvent()
1944 return DoHasPendingInputEvent();
1949 // Force Input Method Editor to commit the uncommitted input
1950 // Note that this and other IME methods don't necessarily
1951 // get called on the same ChildView that input is going through.
1952 NS_IMETHODIMP nsChildView::ResetInputState()
1955 NSLog(@"**** ResetInputState");
1958 mTextInputHandler.CommitIMEComposition();
1962 // 'open' means that it can take non-ASCII chars
1963 NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
1966 NSLog(@"**** SetIMEOpenState aState = %d", aState);
1969 mTextInputHandler.SetIMEOpenState(aState);
1973 // 'open' means that it can take non-ASCII chars
1974 NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
1977 NSLog(@"**** GetIMEOpenState");
1980 *aState = mTextInputHandler.IsIMEOpened();
1984 NS_IMETHODIMP nsChildView::SetInputMode(const IMEContext& aContext)
1987 NSLog(@"**** SetInputMode mStatus = %d", aContext.mStatus);
1990 mIMEContext = aContext;
1991 switch (aContext.mStatus) {
1992 case nsIWidget::IME_STATUS_ENABLED:
1993 case nsIWidget::IME_STATUS_PLUGIN:
1994 mTextInputHandler.SetASCIICapableOnly(PR_FALSE);
1995 mTextInputHandler.EnableIME(PR_TRUE);
1997 case nsIWidget::IME_STATUS_DISABLED:
1998 mTextInputHandler.SetASCIICapableOnly(PR_FALSE);
1999 mTextInputHandler.EnableIME(PR_FALSE);
2001 case nsIWidget::IME_STATUS_PASSWORD:
2002 mTextInputHandler.SetASCIICapableOnly(PR_TRUE);
2003 mTextInputHandler.EnableIME(PR_FALSE);
2006 NS_ERROR("not implemented!");
2011 NS_IMETHODIMP nsChildView::GetInputMode(IMEContext& aContext)
2014 NSLog(@"**** GetInputMode");
2017 aContext = mIMEContext;
2021 // Destruct and don't commit the IME composition string.
2022 NS_IMETHODIMP nsChildView::CancelIMEComposition()
2025 NSLog(@"**** CancelIMEComposition");
2028 mTextInputHandler.CancelIMEComposition();
2032 NS_IMETHODIMP nsChildView::GetToggledKeyState(PRUint32 aKeyCode,
2035 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2038 NSLog(@"**** GetToggledKeyState");
2040 NS_ENSURE_ARG_POINTER(aLEDState);
2043 case NS_VK_CAPS_LOCK:
2046 case NS_VK_NUM_LOCK:
2047 key = kEventKeyModifierNumLockMask;
2049 // Mac doesn't support SCROLL_LOCK state.
2051 return NS_ERROR_NOT_IMPLEMENTED;
2053 PRUint32 modifierFlags = ::GetCurrentKeyModifiers();
2054 *aLEDState = (modifierFlags & key) != 0;
2057 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2060 NS_IMETHODIMP nsChildView::OnIMEFocusChange(PRBool aFocus)
2062 mTextInputHandler.OnFocusChangeInGecko(aFocus);
2063 // XXX Return NS_ERROR_NOT_IMPLEMENTED, see bug 496360.
2064 return NS_ERROR_NOT_IMPLEMENTED;
2067 NSView<mozView>* nsChildView::GetEditorView()
2069 NSView<mozView>* editorView = mView;
2070 // We need to get editor's view. E.g., when the focus is in the bookmark
2071 // dialog, the view is <panel> element of the dialog. At this time, the key
2072 // events are processed the parent window's view that has native focus.
2073 nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, this);
2074 textContent.InitForQueryTextContent(0, 0);
2075 DispatchWindowEvent(textContent);
2076 if (textContent.mSucceeded && textContent.mReply.mFocusedWidget) {
2077 NSView<mozView>* view = static_cast<NSView<mozView>*>(
2078 textContent.mReply.mFocusedWidget->GetNativeData(NS_NATIVE_WIDGET));
2088 nsChildView::GetThebesSurface()
2090 if (!mTempThebesSurface) {
2091 mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxASurface::ImageFormatARGB32);
2094 return mTempThebesSurface;
2098 nsChildView::DrawOver(LayerManager* aManager, nsIntRect aRect)
2100 nsCocoaWindow *cocoaWindow = GetXULWindowWidget();
2102 cocoaWindow->DrawOver(aManager, aRect);
2107 nsChildView::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries)
2109 NSWindow* win = [mView window];
2110 if (!win || ![win isKindOfClass:[ToolbarWindow class]])
2113 float unifiedToolbarHeight = 0;
2114 nsIntRect topPixelStrip(0, 0, [win frame].size.width, 1);
2116 for (PRUint32 i = 0; i < aThemeGeometries.Length(); ++i) {
2117 const ThemeGeometry& g = aThemeGeometries[i];
2118 if ((g.mWidgetType == NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR ||
2119 g.mWidgetType == NS_THEME_TOOLBAR) &&
2120 g.mRect.Contains(topPixelStrip)) {
2121 unifiedToolbarHeight = g.mRect.YMost();
2124 [(ToolbarWindow*)win setUnifiedToolbarHeight:unifiedToolbarHeight];
2128 nsChildView::BeginSecureKeyboardInput()
2130 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2132 nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
2133 if (NS_SUCCEEDED(rv)) {
2134 ::EnableSecureEventInput();
2138 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2142 nsChildView::EndSecureKeyboardInput()
2144 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2146 nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
2147 if (NS_SUCCEEDED(rv)) {
2148 ::DisableSecureEventInput();
2152 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2155 #ifdef ACCESSIBILITY
2156 already_AddRefed<nsAccessible>
2157 nsChildView::GetDocumentAccessible()
2159 nsAccessible *docAccessible = nsnull;
2161 CallQueryReferent(mAccessible.get(), &docAccessible);
2162 return docAccessible;
2165 // need to fetch the accessible anew, because it has gone away.
2166 nsEventStatus status;
2167 nsAccessibleEvent event(PR_TRUE, NS_GETACCESSIBLE, this);
2168 DispatchEvent(&event, status);
2170 // cache the accessible in our weak ptr
2172 do_GetWeakReference(static_cast<nsIAccessible*>(event.mAccessible));
2174 NS_IF_ADDREF(event.mAccessible);
2175 return event.mAccessible;
2181 @implementation ChildView
2183 // globalDragPboard is non-null during native drag sessions that did not originate
2184 // in our native NSView (it is set in |draggingEntered:|). It is unset when the
2185 // drag session ends for this view, either with the mouse exiting or when a drop
2186 // occurs in this view.
2187 NSPasteboard* globalDragPboard = nil;
2189 // gLastDragView and gLastDragMouseDownEvent are used to communicate information
2190 // to the drag service during drag invocation (starting a drag in from the view).
2191 // gLastDragView is only non-null while mouseDragged is on the call stack.
2192 NSView* gLastDragView = nil;
2193 NSEvent* gLastDragMouseDownEvent = nil;
2197 static BOOL initialized = NO;
2200 // Inform the OS about the types of services (from the "Services" menu)
2201 // that we can handle.
2203 NSArray *sendTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil];
2204 NSArray *returnTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil];
2206 [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes];
2208 [sendTypes release];
2209 [returnTypes release];
2215 // initWithFrame:geckoChild:
2216 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
2218 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2220 if ((self = [super initWithFrame:inFrame])) {
2221 mGeckoChild = inChild;
2223 #ifndef NP_NO_CARBON
2224 mPluginEventModel = NPEventModelCarbon;
2226 mPluginEventModel = NPEventModelCocoa;
2228 #ifndef NP_NO_QUICKDRAW
2229 mPluginDrawingModel = NPDrawingModelQuickDraw;
2231 mPluginDrawingModel = NPDrawingModelCoreGraphics;
2234 mKeyDownHandled = PR_FALSE;
2235 mKeyPressHandled = NO;
2237 mPendingDisplay = NO;
2238 mBlockedLastMouseDown = NO;
2240 // initialization for NSTextInput
2241 mMarkedRange.location = NSNotFound;
2242 mMarkedRange.length = 0;
2244 mLastMouseDownEvent = nil;
2245 mClickThroughMouseDownEvent = nil;
2246 mDragService = nsnull;
2248 #ifndef NP_NO_CARBON
2249 mPluginTSMDoc = nil;
2250 mPluginTSMInComposition = NO;
2252 mPluginComplexTextInputRequested = NO;
2254 mIgnoreNextKeyUpEvent = NO;
2256 mGestureState = eGestureState_None;
2257 mCumulativeMagnification = 0.0;
2258 mCumulativeRotation = 0.0;
2260 // We can't call forceRefreshOpenGL here because, in order to work around
2261 // the bug, it seems we need to have a draw already happening. Therefore,
2262 // we call it in drawRect:inContext:, when we know that a draw is in
2264 mDidForceRefreshOpenGL = NO;
2266 [self setFocusRingType:NSFocusRingTypeNone];
2269 // register for things we'll take from other applications
2270 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
2271 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
2275 NSFilesPromisePboardType,
2276 kWildcardPboardType,
2277 kCorePboardType_url,
2278 kCorePboardType_urld,
2279 kCorePboardType_urln,
2281 [[NSNotificationCenter defaultCenter] addObserver:self
2282 selector:@selector(windowBecameMain:)
2283 name:NSWindowDidBecomeMainNotification
2285 [[NSNotificationCenter defaultCenter] addObserver:self
2286 selector:@selector(windowResignedMain:)
2287 name:NSWindowDidResignMainNotification
2289 [[NSNotificationCenter defaultCenter] addObserver:self
2290 selector:@selector(systemMetricsChanged)
2291 name:NSControlTintDidChangeNotification
2293 [[NSNotificationCenter defaultCenter] addObserver:self
2294 selector:@selector(systemMetricsChanged)
2295 name:NSSystemColorsDidChangeNotification
2297 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
2298 selector:@selector(systemMetricsChanged)
2299 name:@"AppleAquaScrollBarVariantChanged"
2301 suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
2302 [[NSNotificationCenter defaultCenter] addObserver:self
2303 selector:@selector(_surfaceNeedsUpdate:)
2304 name:NSViewGlobalFrameDidChangeNotification
2309 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2312 // Work around bug 603134.
2313 // OS X has a bug that causes new OpenGL windows to only paint once or twice,
2314 // then stop painting altogether. By clearing the drawable from the GL context,
2315 // and then resetting the view to ourselves, we convince OS X to start updating
2317 // This can cause a flash in new windows - bug 631339 - but it's very hard to
2318 // fix that while maintaining this workaround.
2319 - (void)forceRefreshOpenGL
2321 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2323 [mGLContext clearDrawable];
2324 [mGLContext setView:self];
2326 NS_OBJC_END_TRY_ABORT_BLOCK;
2331 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2333 [mGLContext release];
2334 [mPendingDirtyRects release];
2335 [mLastMouseDownEvent release];
2336 [mClickThroughMouseDownEvent release];
2337 ChildViewMouseTracker::OnDestroyView(self);
2338 #ifndef NP_NO_CARBON
2340 ::DeleteTSMDocument(mPluginTSMDoc);
2343 [[NSNotificationCenter defaultCenter] removeObserver:self];
2344 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
2348 #ifndef NP_NO_QUICKDRAW
2349 // This sets the current port to _savePort.
2350 // todo: Only do if a Quickdraw plugin is present in the hierarchy!
2354 NS_OBJC_END_TRY_ABORT_BLOCK;
2357 - (void)updatePluginTopLevelWindowStatus:(BOOL)hasMain
2362 nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
2363 NPCocoaEvent cocoaEvent;
2364 InitNPCocoaEvent(&cocoaEvent);
2365 cocoaEvent.type = NPCocoaEventWindowFocusChanged;
2366 cocoaEvent.data.focus.hasFocus = hasMain;
2367 pluginEvent.pluginEvent = &cocoaEvent;
2368 mGeckoChild->DispatchWindowEvent(pluginEvent);
2371 - (void)windowBecameMain:(NSNotification*)inNotification
2373 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2375 if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) {
2376 if ((NSWindow*)[inNotification object] == [self window]) {
2377 [self updatePluginTopLevelWindowStatus:YES];
2381 NS_OBJC_END_TRY_ABORT_BLOCK;
2384 - (void)windowResignedMain:(NSNotification*)inNotification
2386 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2388 if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) {
2389 if ((NSWindow*)[inNotification object] == [self window]) {
2390 [self updatePluginTopLevelWindowStatus:NO];
2394 NS_OBJC_END_TRY_ABORT_BLOCK;
2397 - (void)widgetDestroyed
2399 mGeckoChild->TextInputHandler()->OnDestroyView(self);
2400 mGeckoChild = nsnull;
2402 // Just in case we're destroyed abruptly and missed the draggingExited
2403 // or performDragOperation message.
2404 NS_IF_RELEASE(mDragService);
2407 // mozView method, return our gecko child view widget. Note this does not AddRef.
2408 - (nsIWidget*) widget
2410 return static_cast<nsIWidget*>(mGeckoChild);
2413 - (void)systemMetricsChanged
2418 nsGUIEvent guiEvent(PR_TRUE, NS_THEMECHANGED, mGeckoChild);
2419 mGeckoChild->DispatchWindowEvent(guiEvent);
2422 - (void)setNeedsPendingDisplay
2424 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2426 mPendingFullDisplay = YES;
2427 if (!mPendingDisplay) {
2428 [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
2429 mPendingDisplay = YES;
2432 NS_OBJC_END_TRY_ABORT_BLOCK;
2435 - (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect
2437 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2439 if (!mPendingDirtyRects)
2440 mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1];
2441 [mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]];
2442 if (!mPendingDisplay) {
2443 [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
2444 mPendingDisplay = YES;
2447 NS_OBJC_END_TRY_ABORT_BLOCK;
2450 // Clears the queue of any pending invalides
2451 - (void)processPendingRedraws
2453 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2455 if (mPendingFullDisplay) {
2456 [self setNeedsDisplay:YES];
2458 else if (mPendingDirtyRects) {
2459 unsigned int count = [mPendingDirtyRects count];
2460 for (unsigned int i = 0; i < count; ++i) {
2461 [self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]];
2464 mPendingFullDisplay = NO;
2465 mPendingDisplay = NO;
2466 [mPendingDirtyRects release];
2467 mPendingDirtyRects = nil;
2469 NS_OBJC_END_TRY_ABORT_BLOCK;
2472 - (void)setNeedsDisplayInRect:(NSRect)aRect
2474 [super setNeedsDisplayInRect:aRect];
2476 if ([[self window] isKindOfClass:[ToolbarWindow class]]) {
2477 ToolbarWindow* window = (ToolbarWindow*)[self window];
2478 if ([window drawsContentsIntoWindowFrame]) {
2479 // Tell it to mark the rect in the titlebar as dirty.
2480 NSView* borderView = [[window contentView] superview];
2481 [window setTitlebarNeedsDisplayInRect:[self convertRect:aRect toView:borderView]];
2486 - (NSString*)description
2488 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2490 return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])];
2492 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2495 // Make the origin of this view the topLeft corner (gecko origin) rather
2496 // than the bottomLeft corner (standard cocoa origin).
2504 return [[self window] isOpaque] && !mIsPluginView;
2507 -(void)setIsPluginView:(BOOL)aIsPlugin
2509 mIsPluginView = aIsPlugin;
2514 return mIsPluginView;
2517 // Are we processing an NSLeftMouseDown event that will fail to click through?
2518 // If so, we shouldn't focus or unfocus a plugin.
2519 - (BOOL)isInFailingLeftClickThrough
2524 if (!mClickThroughMouseDownEvent ||
2525 [mClickThroughMouseDownEvent type] != NSLeftMouseDown)
2529 !ChildViewMouseTracker::WindowAcceptsEvent([self window],
2530 mClickThroughMouseDownEvent,
2533 // If we return YES here, this will result in us not being focused,
2534 // which will stop us receiving mClickThroughMouseDownEvent in
2535 // [ChildView mouseDown:]. So we need to release and null-out
2536 // mClickThroughMouseDownEvent here.
2538 [mClickThroughMouseDownEvent release];
2539 mClickThroughMouseDownEvent = nil;
2545 - (void)setPluginEventModel:(NPEventModel)eventModel
2547 mPluginEventModel = eventModel;
2550 - (void)setPluginDrawingModel:(NPDrawingModel)drawingModel
2552 mPluginDrawingModel = drawingModel;
2555 - (NPEventModel)pluginEventModel
2557 return mPluginEventModel;
2560 - (NPDrawingModel)pluginDrawingModel
2562 return mPluginDrawingModel;
2565 #ifndef NP_NO_CARBON
2566 - (void)setPluginTSMInComposition:(BOOL)inComposition
2568 mPluginTSMInComposition = inComposition;
2572 - (void)sendFocusEvent:(PRUint32)eventType
2577 nsEventStatus status = nsEventStatus_eIgnore;
2578 nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoChild);
2579 focusGuiEvent.time = PR_IntervalNow();
2580 mGeckoChild->DispatchEvent(&focusGuiEvent, status);
2583 // We accept key and mouse events, so don't keep passing them up the chain. Allow
2584 // this to be a 'focused' widget for event dispatch.
2585 - (BOOL)acceptsFirstResponder
2590 // Accept mouse down events on background windows
2591 - (BOOL)acceptsFirstMouse:(NSEvent*)aEvent
2593 if (![[self window] isKindOfClass:[PopupWindow class]]) {
2594 // We rely on this function to tell us that the mousedown was on a
2595 // background window. Inside mouseDown we can't tell whether we were
2596 // inactive because at that point we've already been made active.
2597 // Unfortunately, acceptsFirstMouse is called for PopupWindows even when
2598 // their parent window is active, so ignore this on them for now.
2599 mClickThroughMouseDownEvent = [aEvent retain];
2604 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
2606 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2609 HideChildPluginViews(self);
2611 [super viewWillMoveToWindow:newWindow];
2613 NS_OBJC_END_TRY_ABORT_BLOCK;
2616 - (void)viewDidMoveToWindow
2618 if (mPluginEventModel == NPEventModelCocoa &&
2619 [self window] && [self isPluginView] && mGeckoChild) {
2620 mGeckoChild->UpdatePluginPort();
2623 [super viewDidMoveToWindow];
2626 - (void)scrollRect:(NSRect)aRect by:(NSSize)offset
2628 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2630 // Update any pending dirty rects to reflect the new scroll position
2631 if (mPendingDirtyRects) {
2632 unsigned int count = [mPendingDirtyRects count];
2633 for (unsigned int i = 0; i < count; ++i) {
2634 NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue];
2635 NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height);
2636 [mPendingDirtyRects replaceObjectAtIndex:i
2637 withObject:[NSValue valueWithRect:newRect]];
2640 [super scrollRect:aRect by:offset];
2642 NS_OBJC_END_TRY_ABORT_BLOCK;
2645 - (BOOL)mouseDownCanMoveWindow
2652 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2654 #ifndef NP_NO_QUICKDRAW
2655 // Set the current GrafPort to a "safe" port before calling [NSQuickDrawView lockFocus],
2656 // so that the NSQuickDrawView stashes a pointer to this known-good port internally.
2657 // It will set the port back to this port on destruction.
2658 ::SetPort(NULL); // todo: only do if a Quickdraw plugin is present in the hierarchy!
2664 if ([mGLContext view] != self) {
2665 [mGLContext setView:self];
2668 [mGLContext makeCurrentContext];
2671 NS_OBJC_END_TRY_ABORT_BLOCK;
2677 [mGLContext update];
2681 - (void) _surfaceNeedsUpdate:(NSNotification*)notification
2686 // The display system has told us that a portion of our view is dirty. Tell
2687 // gecko to paint it
2688 - (void)drawRect:(NSRect)aRect
2690 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2691 [self drawRect:aRect inContext:cgContext];
2693 // If we're a transparent window and our contents have changed, we need
2694 // to make sure the shadow is updated to the new contents.
2695 if ([[self window] isKindOfClass:[BaseWindow class]]) {
2696 [(BaseWindow*)[self window] deferredInvalidateShadow];
2700 - (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext
2705 // Title bar drawing only works if we really draw into aContext, which only
2706 // the basic layer manager will do.
2707 nsBaseWidget::AutoUseBasicLayerManager setupLayerManager(mGeckoChild);
2708 [self drawRect:aRect inContext:aContext];
2711 - (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext
2714 if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) ||
2718 #ifndef NP_NO_QUICKDRAW
2719 if (mIsPluginView && mPluginDrawingModel == NPDrawingModelQuickDraw) {
2720 mGeckoChild->PaintQD();
2725 // Don't ever draw non-QuickDraw plugin views explicitly; they'll be drawn as
2726 // part of their parent widget.
2731 nsIntRect geckoBounds;
2732 mGeckoChild->GetBounds(geckoBounds);
2734 fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d %d %d]\n",
2736 aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, aContext,
2737 geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
2739 CGAffineTransform xform = CGContextGetCTM(aContext);
2740 fprintf (stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
2742 // Create the event so we can fill in its region
2743 nsPaintEvent paintEvent(PR_TRUE, NS_PAINT, mGeckoChild);
2745 nsIntRect boundingRect =
2746 nsIntRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
2747 const NSRect *rects;
2749 [[NSView focusView] getRectsBeingDrawn:&rects count:&count];
2750 if (count < MAX_RECTS_IN_REGION) {
2751 for (i = 0; i < count; ++i) {
2752 // Add the rect to the region.
2753 const NSRect& r = [self convertRect:rects[i] fromView:[NSView focusView]];
2754 paintEvent.region.Or(paintEvent.region,
2755 nsIntRect(r.origin.x, r.origin.y, r.size.width, r.size.height));
2757 paintEvent.region.And(paintEvent.region, boundingRect);
2759 paintEvent.region = boundingRect;
2762 #ifndef NP_NO_QUICKDRAW
2763 // Subtract quickdraw plugin rectangles from the region
2764 NSArray* subviews = [self subviews];
2765 for (int i = 0; i < int([subviews count]); ++i) {
2766 NSView* view = [subviews objectAtIndex:i];
2767 if (![view isKindOfClass:[ChildView class]] || [view isHidden])
2769 ChildView* cview = (ChildView*) view;
2770 if ([cview isPluginView] && [cview pluginDrawingModel] == NPDrawingModelQuickDraw) {
2771 NSRect frame = [view frame];
2772 paintEvent.region.Sub(paintEvent.region,
2773 nsIntRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height));
2778 if (mGeckoChild->GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_OPENGL) {
2779 LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(mGeckoChild->GetLayerManager(nsnull));
2780 manager->SetClippingRegion(paintEvent.region);
2782 mGLContext = (NSOpenGLContext *)manager->gl()->GetNativeData(mozilla::gl::GLContext::NativeGLContext);
2783 [mGLContext retain];
2785 mGeckoChild->DispatchWindowEvent(paintEvent);
2787 // Force OpenGL to refresh the very first time we draw. This works around a
2788 // Mac OS X bug that stops windows updating on OS X when we use OpenGL.
2789 if (!mDidForceRefreshOpenGL) {
2790 [self performSelector:@selector(forceRefreshOpenGL) withObject:nil afterDelay:0];
2791 mDidForceRefreshOpenGL = YES;
2797 // Create Cairo objects.
2798 NSSize bufferSize = [self bounds].size;
2799 nsRefPtr<gfxQuartzSurface> targetSurface =
2800 new gfxQuartzSurface(aContext, gfxSize(bufferSize.width, bufferSize.height));
2801 targetSurface->SetAllowUseAsSource(PR_FALSE);
2803 nsRefPtr<gfxContext> targetContext = new gfxContext(targetSurface);
2805 // Set up the clip region.
2806 nsIntRegionRectIterator iter(paintEvent.region);
2807 targetContext->NewPath();
2809 const nsIntRect* r = iter.Next();
2812 targetContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
2814 targetContext->Clip();
2816 nsAutoRetainCocoaObject kungFuDeathGrip(self);
2819 nsBaseWidget::AutoLayerManagerSetup
2820 setupLayerManager(mGeckoChild, targetContext, BasicLayerManager::BUFFER_NONE);
2821 painted = mGeckoChild->DispatchWindowEvent(paintEvent);
2824 if (!painted && [self isOpaque]) {
2825 // Gecko refused to draw, but we've claimed to be opaque, so we have to
2826 // draw something--fill with white.
2827 CGContextSetRGBFillColor(aContext, 1, 1, 1, 1);
2828 CGContextFillRect(aContext, CGRectMake(aRect.origin.x, aRect.origin.y,
2829 aRect.size.width, aRect.size.height));
2832 // note that the cairo surface *MUST* be destroyed at this point,
2833 // or bad things will happen (since we can't keep the cgContext around
2834 // beyond this drawRect message handler)
2837 fprintf (stderr, "---- update done ----\n");
2840 CGContextSetRGBStrokeColor (aContext,
2841 ((((unsigned long)self) & 0xff)) / 255.0,
2842 ((((unsigned long)self) & 0xff00) >> 8) / 255.0,
2843 ((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
2846 CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8);
2847 CGContextSetLineWidth(aContext, 4.0);
2848 CGContextStrokeRect(aContext,
2849 CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height));
2853 - (void)releaseWidgets:(NSArray*)aWidgetArray
2855 if (!aWidgetArray) {
2858 NSInteger count = [aWidgetArray count];
2859 for (NSInteger i = 0; i < count; ++i) {
2860 NSNumber* pointer = (NSNumber*) [aWidgetArray objectAtIndex:i];
2861 nsIWidget* widget = (nsIWidget*) [pointer unsignedIntegerValue];
2866 - (void)viewWillDraw
2869 // The OS normally *will* draw our NSWindow, no matter what we do here.
2870 // But Gecko can delete our parent widget(s) (along with mGeckoChild)
2871 // while processing an NS_WILL_PAINT event, which closes our NSWindow and
2872 // makes the OS throw an NSInternalInconsistencyException assertion when
2873 // it tries to draw it. Sometimes the OS also aborts the browser process.
2874 // So we need to retain our parent(s) here and not release it/them until
2875 // the next time through the main thread's run loop. When we do this we
2876 // also need to retain and release mGeckoChild, which holds a strong
2877 // reference to us (otherwise we might have been deleted by the time
2878 // releaseWidgets: is called on us). See bug 550392.
2879 nsIWidget* parent = mGeckoChild->GetParent();
2881 NSMutableArray* widgetArray = [NSMutableArray arrayWithCapacity:3];
2884 [widgetArray addObject:[NSNumber numberWithUnsignedInteger:(NSUInteger)parent]];
2885 parent = parent->GetParent();
2887 NS_ADDREF(mGeckoChild);
2888 [widgetArray addObject:[NSNumber numberWithUnsignedInteger:(NSUInteger)mGeckoChild]];
2889 [self performSelector:@selector(releaseWidgets:)
2890 withObject:widgetArray
2893 nsPaintEvent paintEvent(PR_TRUE, NS_WILL_PAINT, mGeckoChild);
2894 mGeckoChild->DispatchWindowEvent(paintEvent);
2896 [super viewWillDraw];
2899 // Allows us to turn off setting up the clip region
2900 // before each drawRect. We already clip within gecko.
2901 - (BOOL)wantsDefaultClipping
2906 #if USE_CLICK_HOLD_CONTEXTMENU
2908 // -clickHoldCallback:
2910 // called from a timer two seconds after a mouse down to see if we should display
2911 // a context menu (click-hold). |anEvent| is the original mouseDown event. If we're
2912 // still in that mouseDown by this time, put up the context menu, otherwise just
2913 // fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback
2914 // fires so we're ok there.
2916 // This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314)
2917 // so removing it until we get it straightened out.
2919 - (void)clickHoldCallback:(id)theEvent;
2921 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2923 if( theEvent == [NSApp currentEvent] ) {
2924 // we're still in the middle of the same mousedown event here, activate
2925 // click-hold context menu by triggering the right mouseDown action.
2926 NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown
2927 location:[theEvent locationInWindow]
2928 modifierFlags:[theEvent modifierFlags]
2929 timestamp:[theEvent timestamp]
2930 windowNumber:[theEvent windowNumber]
2931 context:[theEvent context]
2932 eventNumber:[theEvent eventNumber]
2933 clickCount:[theEvent clickCount]
2934 pressure:[theEvent pressure]];
2935 [self rightMouseDown:clickHoldEvent];
2938 NS_OBJC_END_TRY_ABORT_BLOCK;
2942 // If we've just created a non-native context menu, we need to mark it as
2943 // such and let the OS (and other programs) know when it opens and closes
2944 // (this is how the OS knows to close other programs' context menus when
2945 // ours open). We send the initial notification here, but others are sent
2946 // in nsCocoaWindow::Show().
2947 - (void)maybeInitContextMenuTracking
2949 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2954 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
2956 PRBool useNativeContextMenus;
2957 nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
2958 if (NS_SUCCEEDED(rv) && useNativeContextMenus)
2962 NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
2963 if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
2966 [[NSDistributedNotificationCenter defaultCenter]
2967 postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
2968 object:@"org.mozilla.gecko.PopupWindow"];
2969 [(PopupWindow*)popupWindow setIsContextMenu:YES];
2971 NS_OBJC_END_TRY_ABORT_BLOCK;
2974 // Returns true if the event should no longer be processed, false otherwise.
2975 // This does not return whether or not anything was rolled up.
2976 - (BOOL)maybeRollup:(NSEvent*)theEvent
2978 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2980 BOOL consumeEvent = NO;
2982 if (gRollupWidget && gRollupListener) {
2983 NSWindow* currentPopup = static_cast<NSWindow*>(gRollupWidget->GetNativeData(NS_NATIVE_WINDOW));
2984 if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
2985 // event is not over the rollup window, default is to roll up
2986 PRBool shouldRollup = PR_TRUE;
2988 // check to see if scroll events should roll up the popup
2989 if ([theEvent type] == NSScrollWheel) {
2990 gRollupListener->ShouldRollupOnMouseWheelEvent(&shouldRollup);
2991 // always consume scroll events that aren't over the popup
2995 // if we're dealing with menus, we probably have submenus and
2996 // we don't want to rollup if the click is in a parent menu of
2997 // the current submenu
2998 PRUint32 popupsToRollup = PR_UINT32_MAX;
3000 nsAutoTArray<nsIWidget*, 5> widgetChain;
3001 gMenuRollup->GetSubmenuWidgetChain(&widgetChain);
3002 PRUint32 sameTypeCount = gMenuRollup->GetSubmenuWidgetChain(&widgetChain);
3003 for (PRUint32 i = 0; i < widgetChain.Length(); i++) {
3004 nsIWidget* widget = widgetChain[i];
3005 NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
3006 if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
3007 // don't roll up if the mouse event occurred within a menu of the
3008 // same type. If the mouse event occurred in a menu higher than
3009 // that, roll up, but pass the number of popups to Rollup so
3010 // that only those of the same type close up.
3011 if (i < sameTypeCount) {
3012 shouldRollup = PR_FALSE;
3015 popupsToRollup = sameTypeCount;
3023 gRollupListener->Rollup(popupsToRollup, nsnull);
3024 consumeEvent = (BOOL)gConsumeRollupEvent;
3029 return consumeEvent;
3031 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
3035 * XXX - The swipeWithEvent, beginGestureWithEvent, magnifyWithEvent,
3036 * rotateWithEvent, and endGestureWithEvent methods are part of a
3037 * PRIVATE interface exported by nsResponder and reverse-engineering
3038 * was necessary to obtain the methods' prototypes. Thus, Apple may
3039 * change the interface in the future without notice.
3041 * The prototypes were obtained from the following link:
3042 * http://cocoadex.com/2008/02/nsevent-modifications-swipe-ro.html
3045 - (void)swipeWithEvent:(NSEvent *)anEvent
3047 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3049 if (!anEvent || !mGeckoChild)
3052 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3054 float deltaX = [anEvent deltaX]; // left=1.0, right=-1.0
3055 float deltaY = [anEvent deltaY]; // up=1.0, down=-1.0
3057 // Setup the "swipe" event.
3058 nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_SWIPE, mGeckoChild, 0, 0.0);
3059 [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
3061 // Record the left/right direction.
3063 geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
3064 else if (deltaX < 0.0)
3065 geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
3067 // Record the up/down direction.
3069 geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_UP;
3070 else if (deltaY < 0.0)
3071 geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_DOWN;
3074 mGeckoChild->DispatchWindowEvent(geckoEvent);
3076 NS_OBJC_END_TRY_ABORT_BLOCK;
3079 - (void)beginGestureWithEvent:(NSEvent *)anEvent
3081 NS_ASSERTION(mGestureState == eGestureState_None, "mGestureState should be eGestureState_None");
3086 mGestureState = eGestureState_StartGesture;
3087 mCumulativeMagnification = 0;
3088 mCumulativeRotation = 0.0;
3091 - (void)magnifyWithEvent:(NSEvent *)anEvent
3093 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3095 if (!anEvent || !mGeckoChild)
3098 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3100 float deltaZ = [anEvent deltaZ];
3103 switch (mGestureState) {
3104 case eGestureState_StartGesture:
3105 msg = NS_SIMPLE_GESTURE_MAGNIFY_START;
3106 mGestureState = eGestureState_MagnifyGesture;
3109 case eGestureState_MagnifyGesture:
3110 msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE;
3113 case eGestureState_None:
3114 case eGestureState_RotateGesture:
3120 nsSimpleGestureEvent geckoEvent(PR_TRUE, msg, mGeckoChild, 0, deltaZ);
3121 [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
3124 mGeckoChild->DispatchWindowEvent(geckoEvent);
3126 // Keep track of the cumulative magnification for the final "magnify" event.
3127 mCumulativeMagnification += deltaZ;
3129 NS_OBJC_END_TRY_ABORT_BLOCK;
3132 - (void)rotateWithEvent:(NSEvent *)anEvent
3134 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3136 if (!anEvent || !mGeckoChild)
3139 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3141 float rotation = [anEvent rotation];
3144 switch (mGestureState) {
3145 case eGestureState_StartGesture:
3146 msg = NS_SIMPLE_GESTURE_ROTATE_START;
3147 mGestureState = eGestureState_RotateGesture;
3150 case eGestureState_RotateGesture:
3151 msg = NS_SIMPLE_GESTURE_ROTATE_UPDATE;
3154 case eGestureState_None:
3155 case eGestureState_MagnifyGesture:
3161 nsSimpleGestureEvent geckoEvent(PR_TRUE, msg, mGeckoChild, 0, 0.0);
3162 [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
3163 geckoEvent.delta = -rotation;
3164 if (rotation > 0.0) {
3165 geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE;
3167 geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE;
3171 mGeckoChild->DispatchWindowEvent(geckoEvent);
3173 // Keep track of the cumulative rotation for the final "rotate" event.
3174 mCumulativeRotation += rotation;
3176 NS_OBJC_END_TRY_ABORT_BLOCK;
3179 - (void)endGestureWithEvent:(NSEvent *)anEvent
3181 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3183 if (!anEvent || !mGeckoChild) {
3184 // Clear the gestures state if we cannot send an event.
3185 mGestureState = eGestureState_None;
3186 mCumulativeMagnification = 0.0;
3187 mCumulativeRotation = 0.0;
3191 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3193 switch (mGestureState) {
3194 case eGestureState_MagnifyGesture:
3196 // Setup the "magnify" event.
3197 nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_MAGNIFY,
3198 mGeckoChild, 0, mCumulativeMagnification);
3199 [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
3202 mGeckoChild->DispatchWindowEvent(geckoEvent);
3206 case eGestureState_RotateGesture:
3208 // Setup the "rotate" event.
3209 nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_ROTATE, mGeckoChild, 0, 0.0);
3210 [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
3211 geckoEvent.delta = -mCumulativeRotation;
3212 if (mCumulativeRotation > 0.0) {
3213 geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE;
3215 geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE;
3219 mGeckoChild->DispatchWindowEvent(geckoEvent);
3223 case eGestureState_None:
3224 case eGestureState_StartGesture:
3229 // Clear the gestures state.
3230 mGestureState = eGestureState_None;
3231 mCumulativeMagnification = 0.0;
3232 mCumulativeRotation = 0.0;
3234 NS_OBJC_END_TRY_ABORT_BLOCK;
3237 // Returning NO from this method only disallows ordering on mousedown - in order
3238 // to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering]
3239 // when handling the mousedown event.
3240 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)aEvent
3242 // Always using system-provided window ordering for normal windows.
3243 if (![[self window] isKindOfClass:[PopupWindow class]])
3246 // Don't reorder when we don't have a parent window, like when we're a
3247 // context menu or a tooltip.
3248 return ![[self window] parentWindow];
3251 - (void)mouseDown:(NSEvent*)theEvent
3253 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3255 if ([self shouldDelayWindowOrderingForEvent:theEvent]) {
3256 [NSApp preventWindowOrdering];
3259 // If we've already seen this event due to direct dispatch from menuForEvent:
3260 // just bail; if not, remember it.
3261 if (mLastMouseDownEvent == theEvent) {
3262 [mLastMouseDownEvent release];
3263 mLastMouseDownEvent = nil;
3267 [mLastMouseDownEvent release];
3268 mLastMouseDownEvent = [theEvent retain];
3271 [gLastDragMouseDownEvent release];
3272 gLastDragMouseDownEvent = [theEvent retain];
3274 // We need isClickThrough because at this point the window we're in might
3275 // already have become main, so the check for isMainWindow in
3276 // WindowAcceptsEvent isn't enough. It also has to check isClickThrough.
3277 BOOL isClickThrough = (theEvent == mClickThroughMouseDownEvent);
3278 [mClickThroughMouseDownEvent release];
3279 mClickThroughMouseDownEvent = nil;
3281 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3283 if ([self maybeRollup:theEvent] ||
3284 !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self, isClickThrough)) {
3285 // Remember blocking because that means we want to block mouseup as well.
3286 mBlockedLastMouseDown = YES;
3290 #if USE_CLICK_HOLD_CONTEXTMENU
3291 // fire off timer to check for click-hold after two seconds. retains |theEvent|
3292 [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
3295 // in order to send gecko events we'll need a gecko widget
3299 NSUInteger modifierFlags = [theEvent modifierFlags];
3301 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3302 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3304 NSInteger clickCount = [theEvent clickCount];
3305 if (mBlockedLastMouseDown && clickCount > 1) {
3306 // Don't send a double click if the first click of the double click was
3310 geckoEvent.clickCount = clickCount;
3312 if (modifierFlags & NSControlKeyMask)
3313 geckoEvent.button = nsMouseEvent::eRightButton;
3315 geckoEvent.button = nsMouseEvent::eLeftButton;
3317 // Create event for use by plugins.
3318 // This is going to our child view so we don't need to look up the destination
3320 #ifndef NP_NO_CARBON
3321 EventRecord carbonEvent;
3322 if (mPluginEventModel == NPEventModelCarbon) {
3323 carbonEvent.what = mouseDown;
3324 carbonEvent.message = 0;
3325 carbonEvent.when = ::TickCount();
3326 ::GetGlobalMouse(&carbonEvent.where);
3327 carbonEvent.modifiers = ::GetCurrentKeyModifiers();
3328 geckoEvent.pluginEvent = &carbonEvent;
3331 NPCocoaEvent cocoaEvent;
3332 if (mPluginEventModel == NPEventModelCocoa) {
3333 InitNPCocoaEvent(&cocoaEvent);
3334 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3335 cocoaEvent.type = NPCocoaEventMouseDown;
3336 cocoaEvent.data.mouse.modifierFlags = modifierFlags;
3337 cocoaEvent.data.mouse.pluginX = point.x;
3338 cocoaEvent.data.mouse.pluginY = point.y;
3339 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3340 cocoaEvent.data.mouse.clickCount = clickCount;
3341 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3342 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3343 cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
3344 geckoEvent.pluginEvent = &cocoaEvent;
3347 mGeckoChild->DispatchWindowEvent(geckoEvent);
3348 mBlockedLastMouseDown = NO;
3350 // XXX maybe call markedTextSelectionChanged:client: here?
3352 NS_OBJC_END_TRY_ABORT_BLOCK;
3355 - (void)mouseUp:(NSEvent *)theEvent
3357 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3359 if (!mGeckoChild || mBlockedLastMouseDown)
3362 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3364 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3365 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3366 if ([theEvent modifierFlags] & NSControlKeyMask)
3367 geckoEvent.button = nsMouseEvent::eRightButton;
3369 geckoEvent.button = nsMouseEvent::eLeftButton;
3371 // Create event for use by plugins.
3372 // This is going to our child view so we don't need to look up the destination
3374 if (mIsPluginView) {
3375 #ifndef NP_NO_CARBON
3376 EventRecord carbonEvent;
3377 if (mPluginEventModel == NPEventModelCarbon) {
3378 carbonEvent.what = mouseUp;
3379 carbonEvent.message = 0;
3380 carbonEvent.when = ::TickCount();
3381 ::GetGlobalMouse(&carbonEvent.where);
3382 carbonEvent.modifiers = ::GetCurrentKeyModifiers();
3383 geckoEvent.pluginEvent = &carbonEvent;
3386 NPCocoaEvent cocoaEvent;
3387 if (mPluginEventModel == NPEventModelCocoa) {
3388 InitNPCocoaEvent(&cocoaEvent);
3389 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3390 cocoaEvent.type = NPCocoaEventMouseUp;
3391 cocoaEvent.data.mouse.modifierFlags = [theEvent modifierFlags];
3392 cocoaEvent.data.mouse.pluginX = point.x;
3393 cocoaEvent.data.mouse.pluginY = point.y;
3394 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3395 cocoaEvent.data.mouse.clickCount = [theEvent clickCount];
3396 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3397 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3398 cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
3399 geckoEvent.pluginEvent = &cocoaEvent;
3403 // This might destroy our widget (and null out mGeckoChild).
3404 mGeckoChild->DispatchWindowEvent(geckoEvent);
3406 // If our mouse-up event's location is over some other object (as might
3407 // happen if it came at the end of a dragging operation), also send our
3408 // Gecko frame a mouse-exit event.
3409 if (mGeckoChild && mIsPluginView) {
3410 #ifndef NP_NO_CARBON
3411 if (mPluginEventModel == NPEventModelCocoa)
3414 if (ChildViewMouseTracker::ViewForEvent(theEvent) != self) {
3415 nsMouseEvent geckoExitEvent(PR_TRUE, NS_MOUSE_EXIT, nsnull, nsMouseEvent::eReal);
3416 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoExitEvent];
3418 NPCocoaEvent cocoaEvent;
3419 InitNPCocoaEvent(&cocoaEvent);
3420 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3421 cocoaEvent.type = NPCocoaEventMouseExited;
3422 cocoaEvent.data.mouse.modifierFlags = [theEvent modifierFlags];
3423 cocoaEvent.data.mouse.pluginX = point.x;
3424 cocoaEvent.data.mouse.pluginY = point.y;
3425 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3426 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3427 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3428 cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
3429 geckoExitEvent.pluginEvent = &cocoaEvent;
3431 mGeckoChild->DispatchWindowEvent(geckoExitEvent);
3436 NS_OBJC_END_TRY_ABORT_BLOCK;
3439 - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
3441 type:(nsMouseEvent::exitType)aType
3446 NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, [self window]);
3447 NSPoint localEventLocation = [self convertPoint:windowEventLocation fromView:nil];
3449 PRUint32 msg = aEnter ? NS_MOUSE_ENTER : NS_MOUSE_EXIT;
3450 nsMouseEvent event(PR_TRUE, msg, mGeckoChild, nsMouseEvent::eReal);
3451 event.refPoint.x = nscoord((PRInt32)localEventLocation.x);
3452 event.refPoint.y = nscoord((PRInt32)localEventLocation.y);
3454 // Create event for use by plugins.
3455 // This is going to our child view so we don't need to look up the destination
3457 #ifndef NP_NO_CARBON
3458 EventRecord carbonEvent;
3460 NPCocoaEvent cocoaEvent;
3461 if (mIsPluginView) {
3462 #ifndef NP_NO_CARBON
3463 if (mPluginEventModel == NPEventModelCarbon) {
3464 carbonEvent.what = NPEventType_AdjustCursorEvent;
3465 carbonEvent.message = 0;
3466 carbonEvent.when = ::TickCount();
3467 ::GetGlobalMouse(&carbonEvent.where);
3468 carbonEvent.modifiers = ::GetCurrentKeyModifiers();
3469 event.pluginEvent = &carbonEvent;
3472 if (mPluginEventModel == NPEventModelCocoa) {
3473 InitNPCocoaEvent(&cocoaEvent);
3474 cocoaEvent.type = ((msg == NS_MOUSE_ENTER) ? NPCocoaEventMouseEntered : NPCocoaEventMouseExited);
3475 cocoaEvent.data.mouse.modifierFlags = [aEvent modifierFlags];
3476 cocoaEvent.data.mouse.pluginX = 5;
3477 cocoaEvent.data.mouse.pluginY = 5;
3478 cocoaEvent.data.mouse.buttonNumber = [aEvent buttonNumber];
3479 cocoaEvent.data.mouse.deltaX = [aEvent deltaX];
3480 cocoaEvent.data.mouse.deltaY = [aEvent deltaY];
3481 cocoaEvent.data.mouse.deltaZ = [aEvent deltaZ];
3482 event.pluginEvent = &cocoaEvent;
3488 nsEventStatus status; // ignored
3489 mGeckoChild->DispatchEvent(&event, status);
3492 - (void)mouseMoved:(NSEvent*)aEvent
3494 ChildViewMouseTracker::MouseMoved(aEvent);
3497 - (void)handleMouseMoved:(NSEvent*)theEvent
3499 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3504 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3505 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3507 // Create event for use by plugins.
3508 // This is going to our child view so we don't need to look up the destination
3510 #ifndef NP_NO_CARBON
3511 EventRecord carbonEvent;
3513 NPCocoaEvent cocoaEvent;
3514 if (mIsPluginView) {
3515 #ifndef NP_NO_CARBON
3516 if (mPluginEventModel == NPEventModelCarbon) {
3517 carbonEvent.what = NPEventType_AdjustCursorEvent;
3518 carbonEvent.message = 0;
3519 carbonEvent.when = ::TickCount();
3520 ::GetGlobalMouse(&carbonEvent.where);
3521 carbonEvent.modifiers = ::GetCurrentKeyModifiers();
3522 geckoEvent.pluginEvent = &carbonEvent;
3525 if (mPluginEventModel == NPEventModelCocoa) {
3526 InitNPCocoaEvent(&cocoaEvent);
3527 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3528 cocoaEvent.type = NPCocoaEventMouseMoved;
3529 cocoaEvent.data.mouse.modifierFlags = [theEvent modifierFlags];
3530 cocoaEvent.data.mouse.pluginX = point.x;
3531 cocoaEvent.data.mouse.pluginY = point.y;
3532 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3533 cocoaEvent.data.mouse.clickCount = [theEvent clickCount];
3534 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3535 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3536 cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
3537 geckoEvent.pluginEvent = &cocoaEvent;
3540 mGeckoChild->DispatchWindowEvent(geckoEvent);
3542 NS_OBJC_END_TRY_ABORT_BLOCK;
3545 - (void)mouseDragged:(NSEvent*)theEvent
3547 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3552 gLastDragView = self;
3554 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3555 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3557 // create event for use by plugins
3558 if (mIsPluginView) {
3559 #ifndef NP_NO_CARBON
3560 EventRecord carbonEvent;
3561 if (mPluginEventModel == NPEventModelCarbon) {
3562 carbonEvent.what = NPEventType_AdjustCursorEvent;
3563 carbonEvent.message = 0;
3564 carbonEvent.when = ::TickCount();
3565 ::GetGlobalMouse(&carbonEvent.where);
3566 carbonEvent.modifiers = btnState | ::GetCurrentKeyModifiers();
3567 geckoEvent.pluginEvent = &carbonEvent;
3570 NPCocoaEvent cocoaEvent;
3571 if (mPluginEventModel == NPEventModelCocoa) {
3572 InitNPCocoaEvent(&cocoaEvent);
3573 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3574 cocoaEvent.type = NPCocoaEventMouseDragged;
3575 cocoaEvent.data.mouse.modifierFlags = [theEvent modifierFlags];
3576 cocoaEvent.data.mouse.pluginX = point.x;
3577 cocoaEvent.data.mouse.pluginY = point.y;
3578 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3579 cocoaEvent.data.mouse.clickCount = [theEvent clickCount];
3580 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3581 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3582 cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
3583 geckoEvent.pluginEvent = &cocoaEvent;
3587 mGeckoChild->DispatchWindowEvent(geckoEvent);
3589 // Note, sending the above event might have destroyed our widget since we didn't retain.
3590 // Fine so long as we don't access any local variables from here on.
3591 gLastDragView = nil;
3593 // XXX maybe call markedTextSelectionChanged:client: here?
3595 NS_OBJC_END_TRY_ABORT_BLOCK;
3598 - (void)rightMouseDown:(NSEvent *)theEvent
3600 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3602 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3604 [self maybeRollup:theEvent];
3608 // The right mouse went down, fire off a right mouse down event to gecko
3609 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3610 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3611 geckoEvent.button = nsMouseEvent::eRightButton;
3612 geckoEvent.clickCount = [theEvent clickCount];
3614 // create event for use by plugins
3615 #ifndef NP_NO_CARBON
3616 EventRecord carbonEvent;
3617 if (mPluginEventModel == NPEventModelCarbon) {
3618 carbonEvent.what = mouseDown;
3619 carbonEvent.message = 0;
3620 carbonEvent.when = ::TickCount();
3621 ::GetGlobalMouse(&carbonEvent.where);
3622 carbonEvent.modifiers = controlKey; // fake a context menu click
3623 geckoEvent.pluginEvent = &carbonEvent;
3626 NPCocoaEvent cocoaEvent;
3627 if (mPluginEventModel == NPEventModelCocoa) {
3628 InitNPCocoaEvent(&cocoaEvent);
3629 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3630 cocoaEvent.type = NPCocoaEventMouseDown;
3631 cocoaEvent.data.mouse.modifierFlags = [theEvent modifierFlags];
3632 cocoaEvent.data.mouse.pluginX = point.x;
3633 cocoaEvent.data.mouse.pluginY = point.y;
3634 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3635 cocoaEvent.data.mouse.clickCount = [theEvent clickCount];
3636 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3637 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3638 cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
3639 geckoEvent.pluginEvent = &cocoaEvent;
3642 PRBool handled = mGeckoChild->DispatchWindowEvent(geckoEvent);
3647 [super rightMouseDown:theEvent]; // let the superview do context menu stuff
3649 NS_OBJC_END_TRY_ABORT_BLOCK;
3652 - (void)rightMouseUp:(NSEvent *)theEvent
3654 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3659 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3660 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3661 geckoEvent.button = nsMouseEvent::eRightButton;
3662 geckoEvent.clickCount = [theEvent clickCount];
3664 // create event for use by plugins
3665 if (mIsPluginView) {
3666 #ifndef NP_NO_CARBON
3667 EventRecord carbonEvent;
3668 if (mPluginEventModel == NPEventModelCarbon) {
3669 carbonEvent.what = mouseUp;
3670 carbonEvent.message = 0;
3671 carbonEvent.when = ::TickCount();
3672 ::GetGlobalMouse(&carbonEvent.where);
3673 carbonEvent.modifiers = controlKey; // fake a context menu click
3674 geckoEvent.pluginEvent = &carbonEvent;
3677 NPCocoaEvent cocoaEvent;
3678 if (mPluginEventModel == NPEventModelCocoa) {
3679 InitNPCocoaEvent(&cocoaEvent);
3680 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3681 cocoaEvent.type = NPCocoaEventMouseUp;
3682 cocoaEvent.data.mouse.modifierFlags = [theEvent modifierFlags];
3683 cocoaEvent.data.mouse.pluginX = point.x;
3684 cocoaEvent.data.mouse.pluginY = point.y;
3685 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3686 cocoaEvent.data.mouse.clickCount = [theEvent clickCount];
3687 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3688 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3689 cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
3690 geckoEvent.pluginEvent = &cocoaEvent;
3694 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3695 mGeckoChild->DispatchWindowEvent(geckoEvent);
3697 NS_OBJC_END_TRY_ABORT_BLOCK;
3700 - (void)rightMouseDragged:(NSEvent*)theEvent
3705 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3706 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3707 geckoEvent.button = nsMouseEvent::eRightButton;
3709 // send event into Gecko by going directly to the
3711 mGeckoChild->DispatchWindowEvent(geckoEvent);
3714 - (void)otherMouseDown:(NSEvent *)theEvent
3716 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3718 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3720 if ([self maybeRollup:theEvent] ||
3721 !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self))
3727 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3728 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3729 geckoEvent.button = nsMouseEvent::eMiddleButton;
3730 geckoEvent.clickCount = [theEvent clickCount];
3732 mGeckoChild->DispatchWindowEvent(geckoEvent);
3734 NS_OBJC_END_TRY_ABORT_BLOCK;
3737 - (void)otherMouseUp:(NSEvent *)theEvent
3742 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3743 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3744 geckoEvent.button = nsMouseEvent::eMiddleButton;
3746 mGeckoChild->DispatchWindowEvent(geckoEvent);
3749 - (void)otherMouseDragged:(NSEvent*)theEvent
3754 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3755 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3756 geckoEvent.button = nsMouseEvent::eMiddleButton;
3758 // send event into Gecko by going directly to the
3760 mGeckoChild->DispatchWindowEvent(geckoEvent);
3763 // Handle an NSScrollWheel event for a single axis only.
3764 -(void)scrollWheel:(NSEvent*)theEvent forAxis:(enum nsMouseScrollEvent::nsMouseScrollFlags)inAxis
3766 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3771 float scrollDelta = 0;
3772 float scrollDeltaPixels = 0;
3773 PRBool checkPixels = PR_TRUE;
3775 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
3777 prefs->GetBoolPref("mousewheel.enable_pixel_scrolling", &checkPixels);
3779 // Calling deviceDeltaX or deviceDeltaY on theEvent will trigger a Cocoa
3780 // assertion and an Objective-C NSInternalInconsistencyException if the
3781 // underlying "Carbon" event doesn't contain pixel scrolling information.
3782 // For these events, carbonEventKind is kEventMouseWheelMoved instead of
3783 // kEventMouseScroll.
3785 EventRef theCarbonEvent = [theEvent _eventRef];
3786 UInt32 carbonEventKind = theCarbonEvent ? ::GetEventKind(theCarbonEvent) : 0;
3787 if (carbonEventKind != kEventMouseScroll)
3788 checkPixels = PR_FALSE;
3791 // Some scrolling devices supports pixel scrolling, e.g. a Macbook
3792 // touchpad or a Mighty Mouse. On those devices, [event deviceDeltaX/Y]
3793 // contains the amount of pixels to scroll.
3794 if (inAxis & nsMouseScrollEvent::kIsVertical) {
3795 scrollDelta = -[theEvent deltaY];
3796 if (checkPixels && (scrollDelta == 0 || scrollDelta != floor(scrollDelta))) {
3797 scrollDeltaPixels = -[theEvent deviceDeltaY];
3799 } else if (inAxis & nsMouseScrollEvent::kIsHorizontal) {
3800 scrollDelta = -[theEvent deltaX];
3801 if (checkPixels && (scrollDelta == 0 || scrollDelta != floor(scrollDelta))) {
3802 scrollDeltaPixels = -[theEvent deviceDeltaX];
3805 return; // caller screwed up
3808 BOOL hasPixels = (scrollDeltaPixels != 0);
3810 if (!hasPixels && scrollDelta == 0)
3811 // No sense in firing off a Gecko event.
3814 BOOL isMomentumScroll = [theEvent respondsToSelector:@selector(_scrollPhase)] &&
3815 [theEvent _scrollPhase] != 0;
3817 if (scrollDelta != 0) {
3818 // Send the line scroll event.
3819 nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
3820 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3821 geckoEvent.scrollFlags |= inAxis;
3824 geckoEvent.scrollFlags |= nsMouseScrollEvent::kHasPixels;
3826 if (isMomentumScroll)
3827 geckoEvent.scrollFlags |= nsMouseScrollEvent::kIsMomentum;
3829 // Gecko only understands how to scroll by an integer value. Using floor
3830 // and ceil is better than truncating the fraction, especially when
3832 if (scrollDelta < 0)
3833 geckoEvent.delta = (PRInt32)floorf(scrollDelta);
3835 geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
3837 NPCocoaEvent cocoaEvent;
3838 if (mPluginEventModel == NPEventModelCocoa) {
3839 InitNPCocoaEvent(&cocoaEvent);
3840 NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
3841 cocoaEvent.type = NPCocoaEventScrollWheel;
3842 cocoaEvent.data.mouse.modifierFlags = [theEvent modifierFlags];
3843 cocoaEvent.data.mouse.pluginX = point.x;
3844 cocoaEvent.data.mouse.pluginY = point.y;
3845 cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
3846 cocoaEvent.data.mouse.clickCount = 0;
3847 if (inAxis & nsMouseScrollEvent::kIsHorizontal)
3848 cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
3850 cocoaEvent.data.mouse.deltaX = 0.0;
3851 if (inAxis & nsMouseScrollEvent::kIsVertical)
3852 cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3854 cocoaEvent.data.mouse.deltaY = 0.0;
3855 cocoaEvent.data.mouse.deltaZ = 0.0;
3856 geckoEvent.pluginEvent = &cocoaEvent;
3859 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3860 mGeckoChild->DispatchWindowEvent(geckoEvent);
3864 #ifndef NP_NO_CARBON
3865 // dispatch scroll wheel carbon event for plugins
3866 if (mPluginEventModel == NPEventModelCarbon) {
3868 OSStatus err = ::CreateEvent(NULL,
3870 kEventMouseWheelMoved,
3871 TicksToEventTime(TickCount()),
3872 kEventAttributeUserEvent,
3875 EventMouseWheelAxis axis;
3876 if (inAxis & nsMouseScrollEvent::kIsVertical)
3877 axis = kEventMouseWheelAxisY;
3878 else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
3879 axis = kEventMouseWheelAxisX;
3881 SetEventParameter(theEvent,
3882 kEventParamMouseWheelAxis,
3884 sizeof(EventMouseWheelAxis),
3887 SInt32 delta = (SInt32)-geckoEvent.delta;
3888 SetEventParameter(theEvent,
3889 kEventParamMouseWheelDelta,
3895 ::GetGlobalMouse(&mouseLoc);
3896 SetEventParameter(theEvent,
3897 kEventParamMouseLocation,
3902 ::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
3903 ReleaseEvent(theEvent);
3910 // Send the pixel scroll event.
3911 nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_PIXEL_SCROLL, nsnull);
3912 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3913 geckoEvent.scrollFlags |= inAxis;
3914 if (isMomentumScroll)
3915 geckoEvent.scrollFlags |= nsMouseScrollEvent::kIsMomentum;
3916 geckoEvent.delta = NSToIntRound(scrollDeltaPixels);
3917 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3918 mGeckoChild->DispatchWindowEvent(geckoEvent);
3921 NS_OBJC_END_TRY_ABORT_BLOCK;
3924 -(void)scrollWheel:(NSEvent*)theEvent
3926 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3928 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3930 if ([self maybeRollup:theEvent])
3936 // It's possible for a single NSScrollWheel event to carry both useful
3937 // deltaX and deltaY, for example, when the "wheel" is a trackpad.
3938 // NSMouseScrollEvent can only carry one axis at a time, so the system
3939 // event will be split into two Gecko events if necessary.
3940 [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsVertical];
3943 [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsHorizontal];
3945 NS_OBJC_END_TRY_ABORT_BLOCK;
3948 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
3950 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3952 if (!mGeckoChild || [self isPluginView])
3955 nsAutoRetainCocoaObject kungFuDeathGrip(self);
3957 [self maybeRollup:theEvent];
3961 // Cocoa doesn't always dispatch a mouseDown: for a control-click event,
3962 // depends on what we return from menuForEvent:. Gecko always expects one
3963 // and expects the mouse down event before the context menu event, so
3964 // get that event sent first if this is a left mouse click.
3965 if ([theEvent type] == NSLeftMouseDown) {
3966 [self mouseDown:theEvent];
3971 nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
3972 [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3973 geckoEvent.button = nsMouseEvent::eRightButton;
3974 mGeckoChild->DispatchWindowEvent(geckoEvent);
3978 [self maybeInitContextMenuTracking];
3980 // Go up our view chain to fetch the correct menu to return.
3981 return [self contextMenu];
3983 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3986 - (NSMenu*)contextMenu
3988 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3990 NSView* superView = [self superview];
3991 if ([superView respondsToSelector:@selector(contextMenu)])
3992 return [(NSView<mozView>*)superView contextMenu];
3996 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3999 #ifndef NP_NO_CARBON
4000 static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar)
4002 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
4004 UnicodeToTextInfo converterInfo;
4005 TextEncoding systemEncoding;
4006 Str255 convertedString;
4011 err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
4015 err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
4019 err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
4023 *outChar = convertedString[1];
4024 ::DisposeUnicodeToTextInfo(&converterInfo);
4027 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
4029 #endif // NP_NO_CARBON
4031 static void ConvertCocoaKeyEventToNPCocoaEvent(NSEvent* cocoaEvent, NPCocoaEvent& pluginEvent, PRUint32 keyType = 0)
4033 InitNPCocoaEvent(&pluginEvent);
4034 NSEventType nativeType = [cocoaEvent type];
4035 switch (nativeType) {
4037 pluginEvent.type = NPCocoaEventKeyDown;
4040 pluginEvent.type = NPCocoaEventKeyUp;
4042 case NSFlagsChanged:
4043 pluginEvent.type = NPCocoaEventFlagsChanged;
4046 printf("Asked to convert key event of unknown type to Cocoa plugin event!");
4048 pluginEvent.data.key.modifierFlags = [cocoaEvent modifierFlags];
4049 // don't try to access character data for flags changed events, it will raise an exception
4050 if (nativeType != NSFlagsChanged) {
4051 pluginEvent.data.key.characters = (NPNSString*)[cocoaEvent characters];
4052 pluginEvent.data.key.charactersIgnoringModifiers = (NPNSString*)[cocoaEvent charactersIgnoringModifiers];
4053 pluginEvent.data.key.isARepeat = [cocoaEvent isARepeat];
4054 pluginEvent.data.key.keyCode = [cocoaEvent keyCode];
4058 #ifndef NP_NO_CARBON
4059 static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord& pluginEvent, PRUint32 keyType = 0)
4061 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4063 UInt32 charCode = 0;
4064 if ([cocoaEvent type] == NSFlagsChanged) {
4065 pluginEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp;
4067 if ([[cocoaEvent characters] length] > 0)
4068 charCode = [[cocoaEvent characters] characterAtIndex:0];
4069 if ([cocoaEvent type] == NSKeyDown)
4070 pluginEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
4072 pluginEvent.what = keyUp;
4075 if (charCode >= 0x0080) {
4077 case NSUpArrowFunctionKey:
4078 charCode = kUpArrowCharCode;
4080 case NSDownArrowFunctionKey:
4081 charCode = kDownArrowCharCode;
4083 case NSLeftArrowFunctionKey:
4084 charCode = kLeftArrowCharCode;
4086 case NSRightArrowFunctionKey:
4087 charCode = kRightArrowCharCode;
4090 unsigned char convertedCharCode;
4091 if (ConvertUnicodeToCharCode(charCode, &convertedCharCode))
4092 charCode = convertedCharCode;
4093 //NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]);
4097 pluginEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8);
4098 pluginEvent.when = ::TickCount();
4099 ::GetGlobalMouse(&pluginEvent.where);
4100 pluginEvent.modifiers = ::GetCurrentKeyModifiers();
4102 NS_OBJC_END_TRY_ABORT_BLOCK;
4104 #endif // NP_NO_CARBON
4106 static PRBool IsPrintableChar(PRUnichar aChar)
4108 return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
4111 static PRUint32 GetGeckoKeyCodeFromChar(PRUnichar aChar)
4113 // We don't support the key code for non-ASCII characters
4117 if (aChar >= 'a' && aChar <= 'z') // lowercase
4118 return PRUint32(toupper(aChar));
4119 else if (aChar >= 'A' && aChar <= 'Z') // uppercase
4120 return PRUint32(aChar);
4121 else if (aChar >= '0' && aChar <= '9')
4122 return PRUint32(aChar - '0' + NS_VK_0);
4126 case kReturnCharCode:
4127 case kEnterCharCode:
4129 return NS_VK_RETURN;
4132 return NS_VK_OPEN_BRACKET;
4135 return NS_VK_CLOSE_BRACKET;
4140 case '\\': return NS_VK_BACK_SLASH;
4141 case ' ': return NS_VK_SPACE;
4142 case ';': return NS_VK_SEMICOLON;
4143 case '=': return NS_VK_EQUALS;
4144 case ',': return NS_VK_COMMA;
4145 case '.': return NS_VK_PERIOD;
4146 case '/': return NS_VK_SLASH;
4147 case '`': return NS_VK_BACK_QUOTE;
4148 case '\t': return NS_VK_TAB;
4149 case '-': return NS_VK_SUBTRACT;
4150 case '+': return NS_VK_ADD;
4153 if (!IsPrintableChar(aChar))
4154 NS_WARNING("GetGeckoKeyCodeFromChar has failed.");
4159 static PRUint32 ConvertMacToGeckoKeyCode(UInt32 keyCode, nsKeyEvent* aKeyEvent, NSString* characters)
4161 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
4163 PRUint32 geckoKeyCode = 0;
4167 // modifiers. We don't get separate events for these
4168 case kEscapeKeyCode: geckoKeyCode = NS_VK_ESCAPE; break;
4169 case kRCommandKeyCode:
4170 case kCommandKeyCode: geckoKeyCode = NS_VK_META; break;
4171 case kRShiftKeyCode:
4172 case kShiftKeyCode: geckoKeyCode = NS_VK_SHIFT; break;
4173 case kCapsLockKeyCode: geckoKeyCode = NS_VK_CAPS_LOCK; break;
4174 case kRControlKeyCode:
4175 case kControlKeyCode: geckoKeyCode = NS_VK_CONTROL; break;
4176 case kROptionKeyCode:
4177 case kOptionkeyCode: geckoKeyCode = NS_VK_ALT; break;
4178 case kClearKeyCode: geckoKeyCode = NS_VK_CLEAR; break;
4181 case kF1KeyCode: geckoKeyCode = NS_VK_F1; break;
4182 case kF2KeyCode: geckoKeyCode = NS_VK_F2; break;
4183 case kF3KeyCode: geckoKeyCode = NS_VK_F3; break;
4184 case kF4KeyCode: geckoKeyCode = NS_VK_F4; break;
4185 case kF5KeyCode: geckoKeyCode = NS_VK_F5; break;
4186 case kF6KeyCode: geckoKeyCode = NS_VK_F6; break;
4187 case kF7KeyCode: geckoKeyCode = NS_VK_F7; break;
4188 case kF8KeyCode: geckoKeyCode = NS_VK_F8; break;
4189 case kF9KeyCode: geckoKeyCode = NS_VK_F9; break;
4190 case kF10KeyCode: geckoKeyCode = NS_VK_F10; break;
4191 case kF11KeyCode: geckoKeyCode = NS_VK_F11; break;
4192 case kF12KeyCode: geckoKeyCode = NS_VK_F12; break;
4193 // case kF13KeyCode: geckoKeyCode = NS_VK_F13; break; // clash with the 3 below
4194 // case kF14KeyCode: geckoKeyCode = NS_VK_F14; break;
4195 // case kF15KeyCode: geckoKeyCode = NS_VK_F15; break;
4196 case kPauseKeyCode: geckoKeyCode = NS_VK_PAUSE; break;
4197 case kScrollLockKeyCode: geckoKeyCode = NS_VK_SCROLL_LOCK; break;
4198 case kPrintScreenKeyCode: geckoKeyCode = NS_VK_PRINTSCREEN; break;
4201 case kKeypad0KeyCode: geckoKeyCode = NS_VK_NUMPAD0; break;
4202 case kKeypad1KeyCode: geckoKeyCode = NS_VK_NUMPAD1; break;
4203 case kKeypad2KeyCode: geckoKeyCode = NS_VK_NUMPAD2; break;
4204 case kKeypad3KeyCode: geckoKeyCode = NS_VK_NUMPAD3; break;
4205 case kKeypad4KeyCode: geckoKeyCode = NS_VK_NUMPAD4; break;
4206 case kKeypad5KeyCode: geckoKeyCode = NS_VK_NUMPAD5; break;
4207 case kKeypad6KeyCode: geckoKeyCode = NS_VK_NUMPAD6; break;
4208 case kKeypad7KeyCode: geckoKeyCode = NS_VK_NUMPAD7; break;
4209 case kKeypad8KeyCode: geckoKeyCode = NS_VK_NUMPAD8; break;
4210 case kKeypad9KeyCode: geckoKeyCode = NS_VK_NUMPAD9; break;
4212 case kKeypadMultiplyKeyCode: geckoKeyCode = NS_VK_MULTIPLY; break;
4213 case kKeypadAddKeyCode: geckoKeyCode = NS_VK_ADD; break;
4214 case kKeypadSubtractKeyCode: geckoKeyCode = NS_VK_SUBTRACT; break;
4215 case kKeypadDecimalKeyCode: geckoKeyCode = NS_VK_DECIMAL; break;
4216 case kKeypadDivideKeyCode: geckoKeyCode = NS_VK_DIVIDE; break;
4218 // these may clash with forward delete and help
4219 case kInsertKeyCode: geckoKeyCode = NS_VK_INSERT; break;
4220 case kDeleteKeyCode: geckoKeyCode = NS_VK_DELETE; break;
4222 case kBackspaceKeyCode: geckoKeyCode = NS_VK_BACK; break;
4223 case kTabKeyCode: geckoKeyCode = NS_VK_TAB; break;
4224 case kHomeKeyCode: geckoKeyCode = NS_VK_HOME; break;
4225 case kEndKeyCode: geckoKeyCode = NS_VK_END; break;
4226 case kPageUpKeyCode: geckoKeyCode = NS_VK_PAGE_UP; break;
4227 case kPageDownKeyCode: geckoKeyCode = NS_VK_PAGE_DOWN; break;
4228 case kLeftArrowKeyCode: geckoKeyCode = NS_VK_LEFT; break;
4229 case kRightArrowKeyCode: geckoKeyCode = NS_VK_RIGHT; break;
4230 case kUpArrowKeyCode: geckoKeyCode = NS_VK_UP; break;
4231 case kDownArrowKeyCode: geckoKeyCode = NS_VK_DOWN; break;
4232 case kVK_ANSI_1: geckoKeyCode = NS_VK_1; break;
4233 case kVK_ANSI_2: geckoKeyCode = NS_VK_2; break;
4234 case kVK_ANSI_3: geckoKeyCode = NS_VK_3; break;
4235 case kVK_ANSI_4: geckoKeyCode = NS_VK_4; break;
4236 case kVK_ANSI_5: geckoKeyCode = NS_VK_5; break;
4237 case kVK_ANSI_6: geckoKeyCode = NS_VK_6; break;
4238 case kVK_ANSI_7: geckoKeyCode = NS_VK_7; break;
4239 case kVK_ANSI_8: geckoKeyCode = NS_VK_8; break;
4240 case kVK_ANSI_9: geckoKeyCode = NS_VK_9; break;
4241 case kVK_ANSI_0: geckoKeyCode = NS_VK_0; break;
4244 // if we haven't gotten the key code already, look at the char code
4245 if ([characters length])
4246 geckoKeyCode = GetGeckoKeyCodeFromChar([characters characterAtIndex:0]);
4249 return geckoKeyCode;
4251 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
4254 static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
4258 // this table is used to determine which keys are special and should not generate a charCode
4261 // modifiers - we don't get separate events for these yet
4262 case kEscapeKeyCode:
4264 case kRShiftKeyCode:
4265 case kCommandKeyCode:
4266 case kRCommandKeyCode:
4267 case kCapsLockKeyCode:
4268 case kControlKeyCode:
4269 case kRControlKeyCode:
4270 case kOptionkeyCode:
4271 case kROptionKeyCode:
4288 case kScrollLockKeyCode:
4289 case kPrintScreenKeyCode:
4291 case kInsertKeyCode:
4292 case kDeleteKeyCode:
4294 case kBackspaceKeyCode:
4298 case kPageUpKeyCode:
4299 case kPageDownKeyCode:
4300 case kLeftArrowKeyCode:
4301 case kRightArrowKeyCode:
4302 case kUpArrowKeyCode:
4303 case kDownArrowKeyCode:
4304 case kReturnKeyCode:
4306 case kPowerbookEnterKeyCode:
4307 isSpecial = PR_TRUE;
4311 isSpecial = PR_FALSE;
4318 static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
4320 // this is not character inputting event, simply.
4321 if (!aEvent.isChar || !aEvent.charCode || aEvent.isMeta)
4323 // if this is unicode char inputting event, we don't need to check
4324 // ctrl/alt/command keys
4325 if (aEvent.charCode > 0x7F)
4327 // ASCII chars should be inputted without ctrl/alt/command keys
4328 return !aEvent.isControl && !aEvent.isAlt;
4331 // Basic conversion for cocoa to gecko events, common to all conversions.
4332 // Note that it is OK for inEvent to be nil.
4333 - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
4335 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4337 NS_ASSERTION(outGeckoEvent, "convertGenericCocoaEvent:toGeckoEvent: requires non-null outGeckoEvent");
4341 outGeckoEvent->widget = [self widget];
4342 outGeckoEvent->time = PR_IntervalNow();
4345 unsigned int modifiers = [inEvent modifierFlags];
4346 outGeckoEvent->isShift = ((modifiers & NSShiftKeyMask) != 0);
4347 outGeckoEvent->isControl = ((modifiers & NSControlKeyMask) != 0);
4348 outGeckoEvent->isAlt = ((modifiers & NSAlternateKeyMask) != 0);
4349 outGeckoEvent->isMeta = ((modifiers & NSCommandKeyMask) != 0);
4352 NS_OBJC_END_TRY_ABORT_BLOCK;
4355 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
4357 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4359 NS_ASSERTION(outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null aoutGeckoEvent");
4363 [self convertGenericCocoaEvent:aMouseEvent toGeckoEvent:outGeckoEvent];
4365 // convert point to view coordinate system
4366 NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(aMouseEvent, [self window]);
4367 NSPoint localPoint = [self convertPoint:locationInWindow fromView:nil];
4368 outGeckoEvent->refPoint.x = static_cast<nscoord>(localPoint.x);
4369 outGeckoEvent->refPoint.y = static_cast<nscoord>(localPoint.y);
4371 NS_OBJC_END_TRY_ABORT_BLOCK;
4374 // #define DEBUG_KB 1
4377 UCKeyTranslateToUnicode(const UCKeyboardLayout* aHandle, UInt32 aKeyCode, UInt32 aModifiers,
4381 NSLog(@"**** UCKeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aKbType: %X",
4382 aHandle, aKeyCode, aModifiers, aKbType);
4383 PRBool isShift = aModifiers & shiftKey;
4384 PRBool isCtrl = aModifiers & controlKey;
4385 PRBool isOpt = aModifiers & optionKey;
4386 PRBool isCmd = aModifiers & cmdKey;
4387 PRBool isCL = aModifiers & alphaLock;
4388 PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
4389 NSLog(@" Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
4390 isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
4391 isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
4393 UInt32 deadKeyState = 0;
4396 OSStatus err = ::UCKeyTranslate(aHandle, aKeyCode,
4397 kUCKeyActionDown, aModifiers >> 8,
4398 aKbType, kUCKeyTranslateNoDeadKeysMask,
4399 &deadKeyState, 5, &len, chars);
4400 PRUint32 ch = (err == noErr && len == 1) ? PRUint32(chars[0]) : 0;
4402 NSLog(@" result: %X(%C)", ch, ch > ' ' ? ch : ' ');
4407 struct KeyTranslateData {
4408 KeyTranslateData() {
4409 mUchr.mLayout = nsnull;
4414 const UCKeyboardLayout* mLayout;
4420 GetUniCharFromKeyTranslate(KeyTranslateData& aData,
4421 UInt32 aKeyCode, UInt32 aModifiers)
4423 if (aData.mUchr.mLayout) {
4424 return UCKeyTranslateToUnicode(aData.mUchr.mLayout, aKeyCode, aModifiers,
4425 aData.mUchr.mKbType);
4432 GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
4434 static const UCKeyboardLayout* sUSLayout = nsnull;
4436 nsTISInputSource tis("com.apple.keylayout.US");
4437 sUSLayout = tis.GetUCKeyboardLayout();
4438 NS_ENSURE_TRUE(sUSLayout, 0);
4441 UInt32 kbType = 40; // ANSI, don't use actual layout
4442 return UCKeyTranslateToUnicode(sUSLayout, aKeyCode, aModifiers, kbType);
4445 - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent
4447 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4449 NS_ASSERTION(aKeyEvent && outGeckoEvent, "convertCocoaKeyEvent:toGeckoEvent: requires non-null arguments");
4450 if (!aKeyEvent || !outGeckoEvent)
4453 [self convertGenericCocoaEvent:aKeyEvent toGeckoEvent:outGeckoEvent];
4455 // coords for key events are always 0,0
4456 outGeckoEvent->refPoint.x = outGeckoEvent->refPoint.y = 0;
4458 // Initialize whether or not we are using charCodes to false.
4459 outGeckoEvent->isChar = PR_FALSE;
4461 // Check to see if the message is a key press that does not involve
4462 // one of our special key codes.
4463 if (outGeckoEvent->message == NS_KEY_PRESS &&
4464 !IsSpecialGeckoKey([aKeyEvent keyCode])) {
4465 outGeckoEvent->isChar = PR_TRUE; // this is not a special key
4467 outGeckoEvent->charCode = 0;
4468 outGeckoEvent->keyCode = 0; // not set for key press events
4470 NSString* chars = [aKeyEvent characters];
4471 if ([chars length] > 0)
4472 outGeckoEvent->charCode = [chars characterAtIndex:0];
4474 // convert control-modified charCode to raw charCode (with appropriate case)
4475 if (outGeckoEvent->isControl && outGeckoEvent->charCode <= 26)
4476 outGeckoEvent->charCode += (outGeckoEvent->isShift) ? ('A' - 1) : ('a' - 1);
4478 // Accel and access key handling needs to know the characters that this
4479 // key produces with Shift up or down. So, provide this information
4480 // when Ctrl or Command or Alt is pressed.
4481 if (outGeckoEvent->isControl || outGeckoEvent->isMeta ||
4482 outGeckoEvent->isAlt) {
4483 KeyTranslateData kt;
4485 PRBool isRomanKeyboardLayout;
4487 nsTISInputSource tis;
4488 if (gOverrideKeyboardLayout.mOverrideEnabled) {
4489 tis.InitByLayoutID(gOverrideKeyboardLayout.mKeyboardLayout);
4491 tis.InitByCurrentKeyboardLayout();
4493 kt.mUchr.mLayout = tis.GetUCKeyboardLayout();
4494 isRomanKeyboardLayout = tis.IsASCIICapable();
4496 // If a keyboard layout override is set, we also need to force the
4497 // keyboard type to something ANSI to avoid test failures on machines
4498 // with JIS keyboards (since the pair of keyboard layout and physical
4499 // keyboard type form the actual key layout). This assumes that the
4500 // test setting the override was written assuming an ANSI keyboard.
4501 if (kt.mUchr.mLayout)
4502 kt.mUchr.mKbType = gOverrideKeyboardLayout.mOverrideEnabled ? 40 : ::LMGetKbdType();
4504 UInt32 key = [aKeyEvent keyCode];
4506 // Caps lock and num lock modifier state:
4507 UInt32 lockState = 0;
4508 if ([aKeyEvent modifierFlags] & NSAlphaShiftKeyMask)
4509 lockState |= alphaLock;
4510 if ([aKeyEvent modifierFlags] & NSNumericPadKeyMask)
4511 lockState |= kEventKeyModifierNumLockMask;
4514 PRUint32 unshiftedChar = GetUniCharFromKeyTranslate(kt, key, lockState);
4515 UInt32 shiftLockMod = shiftKey | lockState;
4516 PRUint32 shiftedChar = GetUniCharFromKeyTranslate(kt, key, shiftLockMod);
4518 // characters generated with Cmd key
4519 // XXX we should remove CapsLock state, which changes characters from
4520 // Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
4522 UInt32 numState = (lockState & ~alphaLock); // only num lock state
4523 PRUint32 uncmdedChar = GetUniCharFromKeyTranslate(kt, key, numState);
4524 UInt32 shiftNumMod = numState | shiftKey;
4525 PRUint32 uncmdedShiftChar =
4526 GetUniCharFromKeyTranslate(kt, key, shiftNumMod);
4527 PRUint32 uncmdedUSChar = GetUSLayoutCharFromKeyTranslate(key, numState);
4528 UInt32 cmdNumMod = cmdKey | numState;
4529 PRUint32 cmdedChar = GetUniCharFromKeyTranslate(kt, key, cmdNumMod);
4530 UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
4531 PRUint32 cmdedShiftChar =
4532 GetUniCharFromKeyTranslate(kt, key, cmdShiftNumMod);
4534 // Is the keyboard layout changed by Cmd key?
4535 // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
4536 PRBool isCmdSwitchLayout = uncmdedChar != cmdedChar;
4537 // Is the keyboard layout for Latin, but Cmd key switches the layout?
4538 // I.e., Dvorak-QWERTY
4539 PRBool isDvorakQWERTY = isCmdSwitchLayout && isRomanKeyboardLayout;
4541 // If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed,
4542 // we should append unshiftedChar and shiftedChar for handling the
4543 // normal characters. These are the characters that the user is most
4544 // likely to associate with this key.
4545 if ((unshiftedChar || shiftedChar) &&
4546 (!outGeckoEvent->isMeta || !isDvorakQWERTY)) {
4547 nsAlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
4548 outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4551 // Most keyboard layouts provide the same characters in the NSEvents
4552 // with Command+Shift as with Command. However, with Command+Shift we
4553 // want the character on the second level. e.g. With a US QWERTY
4554 // layout, we want "?" when the "/","?" key is pressed with
4557 // On a German layout, the OS gives us '/' with Cmd+Shift+SS(eszett)
4558 // even though Cmd+SS is 'SS' and Shift+'SS' is '?'. This '/' seems
4559 // like a hack to make the Cmd+"?" event look the same as the Cmd+"?"
4560 // event on a US keyboard. The user thinks they are typing Cmd+"?", so
4561 // we'll prefer the "?" character, replacing charCode with shiftedChar
4562 // when Shift is pressed. However, in case there is a layout where the
4563 // character unique to Cmd+Shift is the character that the user expects,
4564 // we'll send it as an alternative char.
4565 PRBool hasCmdShiftOnlyChar =
4566 cmdedChar != cmdedShiftChar && uncmdedShiftChar != cmdedShiftChar;
4567 PRUint32 originalCmdedShiftChar = cmdedShiftChar;
4569 // If we can make a good guess at the characters that the user would
4570 // expect this key combination to produce (with and without Shift) then
4571 // use those characters. This also corrects for CapsLock, which was
4573 if (!isCmdSwitchLayout) {
4574 // The characters produced with Command seem similar to those without
4577 cmdedChar = unshiftedChar;
4579 cmdedShiftChar = shiftedChar;
4580 } else if (uncmdedUSChar == cmdedChar) {
4581 // It looks like characters from a US layout are provided when Command
4583 PRUint32 ch = GetUSLayoutCharFromKeyTranslate(key, lockState);
4586 ch = GetUSLayoutCharFromKeyTranslate(key, shiftLockMod);
4588 cmdedShiftChar = ch;
4591 // Only charCode (not alternativeCharCodes) is available to javascript,
4592 // so attempt to set this to the most likely intended (or most useful)
4593 // character. Note that cmdedChar and cmdedShiftChar are usually
4594 // Latin/ASCII characters and that is what is wanted here as accel
4595 // keys are expected to be Latin characters.
4597 // XXX We should do something similar when Control is down (bug 429510).
4598 if (outGeckoEvent->isMeta &&
4599 !(outGeckoEvent->isControl || outGeckoEvent->isAlt)) {
4601 // The character to use for charCode.
4602 PRUint32 preferredCharCode = 0;
4603 preferredCharCode = outGeckoEvent->isShift ? cmdedShiftChar : cmdedChar;
4605 if (preferredCharCode) {
4607 if (outGeckoEvent->charCode != preferredCharCode) {
4608 NSLog(@" charCode replaced: %X(%C) to %X(%C)",
4609 outGeckoEvent->charCode,
4610 outGeckoEvent->charCode > ' ' ? outGeckoEvent->charCode : ' ',
4612 preferredCharCode > ' ' ? preferredCharCode : ' ');
4615 outGeckoEvent->charCode = preferredCharCode;
4619 // If the current keyboard layout is switched by the Cmd key,
4620 // we should append cmdedChar and shiftedCmdChar that are
4621 // Latin char for the key. But don't append at Dvorak-QWERTY.
4622 if ((cmdedChar || cmdedShiftChar) &&
4623 isCmdSwitchLayout && !isDvorakQWERTY) {
4624 nsAlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
4625 outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4627 // Special case for 'SS' key of German layout. See the comment of
4628 // hasCmdShiftOnlyChar definition for the detail.
4629 if (hasCmdShiftOnlyChar && originalCmdedShiftChar) {
4630 nsAlternativeCharCode altCharCodes(0, originalCmdedShiftChar);
4631 outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4636 NSString* characters = nil;
4637 if ([aKeyEvent type] != NSFlagsChanged)
4638 characters = [aKeyEvent charactersIgnoringModifiers];
4640 outGeckoEvent->keyCode =
4641 ConvertMacToGeckoKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
4642 outGeckoEvent->charCode = 0;
4645 if (outGeckoEvent->message == NS_KEY_PRESS && !outGeckoEvent->isMeta)
4646 [NSCursor setHiddenUntilMouseMoves:YES];
4648 NS_OBJC_END_TRY_ABORT_BLOCK;
4651 #ifndef NP_NO_CARBON
4652 // Called from PluginKeyEventsHandler() (a handler for Carbon TSM events) to
4653 // process a Carbon key event for the currently focused plugin. Both Unicode
4654 // characters and "Mac encoding characters" (in the MBCS or "multibyte
4655 // character system") are (or should be) available from aKeyEvent, but here we
4656 // use the MCBS characters. This is how the WebKit does things, and seems to
4657 // be what plugins expect.
4658 - (void) processPluginKeyEvent:(EventRef)aKeyEvent
4660 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4665 if (mPluginEventModel == NPEventModelCocoa) {
4667 OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, 0, &size, NULL);
4668 if (status != noErr)
4671 UniChar* chars = (UniChar*)malloc(size);
4675 status = ::GetEventParameter(aKeyEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, size, NULL, chars);
4676 if (status != noErr) {
4681 CFStringRef text = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, chars, (size / sizeof(UniChar)), kCFAllocatorNull);
4687 NPCocoaEvent cocoaTextEvent;
4688 InitNPCocoaEvent(&cocoaTextEvent);
4689 cocoaTextEvent.type = NPCocoaEventTextInput;
4690 cocoaTextEvent.data.text.text = (NPNSString*)text;
4692 nsGUIEvent pluginTextEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
4693 pluginTextEvent.time = PR_IntervalNow();
4694 pluginTextEvent.pluginEvent = (void*)&cocoaTextEvent;
4695 mGeckoChild->DispatchWindowEvent(pluginTextEvent);
4703 nsAutoRetainCocoaObject kungFuDeathGrip(self);
4705 UInt32 numCharCodes;
4706 OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
4707 typeChar, NULL, 0, &numCharCodes, NULL);
4708 if (status != noErr)
4711 nsAutoTArray<unsigned char, 3> charCodes;
4712 charCodes.SetLength(numCharCodes);
4713 status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
4714 typeChar, NULL, numCharCodes, NULL, charCodes.Elements());
4715 if (status != noErr)
4719 status = ::GetEventParameter(aKeyEvent, kEventParamKeyModifiers,
4720 typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
4721 if (status != noErr)
4725 status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
4726 typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode);
4727 if (status != noErr)
4730 EventRef cloneEvent = ::CopyEvent(aKeyEvent);
4731 for (unsigned int i = 0; i < numCharCodes; ++i) {
4732 status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
4733 typeChar, 1, charCodes.Elements() + i);
4734 if (status != noErr)
4737 EventRecord eventRec;
4738 if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
4739 nsKeyEvent keyDownEvent(PR_TRUE, NS_KEY_DOWN, mGeckoChild);
4741 PRUint32 keyCode(ConvertMacToGeckoKeyCode(macKeyCode, &keyDownEvent, @""));
4742 PRUint32 charCode(charCodes.ElementAt(i));
4744 keyDownEvent.time = PR_IntervalNow();
4745 keyDownEvent.pluginEvent = &eventRec;
4746 if (IsSpecialGeckoKey(macKeyCode)) {
4747 keyDownEvent.keyCode = keyCode;
4749 keyDownEvent.charCode = charCode;
4750 keyDownEvent.isChar = PR_TRUE;
4752 keyDownEvent.isShift = ((modifiers & shiftKey) != 0);
4753 keyDownEvent.isControl = ((modifiers & controlKey) != 0);
4754 keyDownEvent.isAlt = ((modifiers & optionKey) != 0);
4755 keyDownEvent.isMeta = ((modifiers & cmdKey) != 0); // Should never happen
4756 mGeckoChild->DispatchWindowEvent(keyDownEvent);
4762 ::ReleaseEvent(cloneEvent);
4764 NS_OBJC_END_TRY_ABORT_BLOCK;
4766 #endif // NP_NO_CARBON
4768 - (void)pluginRequestsComplexTextInputForCurrentEvent
4770 mPluginComplexTextInputRequested = YES;
4773 - (void)sendCompositionEvent:(PRInt32) aEventType
4776 NSLog(@"****in sendCompositionEvent; type = %d", aEventType);
4782 // static void init_composition_event( *aEvent, int aType)
4783 nsCompositionEvent event(PR_TRUE, aEventType, mGeckoChild);
4784 event.time = PR_IntervalNow();
4785 mGeckoChild->DispatchWindowEvent(event);
4788 - (void)sendTextEvent:(PRUnichar*) aBuffer
4789 attributedString:(NSAttributedString*) aString
4790 selectedRange:(NSRange) selRange
4791 markedRange:(NSRange) markRange
4792 doCommit:(BOOL) doCommit
4795 NSLog(@"****in sendTextEvent; string = '%@'", aString);
4796 NSLog(@" markRange = %d, %d; selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length);
4802 nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild);
4803 textEvent.time = PR_IntervalNow();
4804 textEvent.theText = aBuffer;
4806 FillTextRangeInTextEvent(&textEvent, aString, markRange, selRange);
4808 mGeckoChild->DispatchWindowEvent(textEvent);
4809 if (textEvent.rangeArray)
4810 delete [] textEvent.rangeArray;
4814 // NSTextInput implementation
4816 #define MAX_BUFFER_SIZE 32
4818 - (void)insertText:(id)insertString
4820 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4823 NSLog(@"****in insertText: '%@'", insertString);
4824 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4829 if (mGeckoChild->TextInputHandler()->IgnoreIMEComposition())
4832 nsAutoRetainCocoaObject kungFuDeathGrip(self);
4834 if (![insertString isKindOfClass:[NSAttributedString class]])
4835 insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease];
4837 NSString *tmpStr = [insertString string];
4838 unsigned int len = [tmpStr length];
4839 if (!mGeckoChild->TextInputHandler()->IsIMEComposing() && len == 0)
4840 return; // nothing to do
4841 PRUnichar buffer[MAX_BUFFER_SIZE];
4842 PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
4843 [tmpStr getCharacters:bufPtr];
4844 bufPtr[len] = PRUnichar('\0');
4846 if (len == 1 && !mGeckoChild->TextInputHandler()->IsIMEComposing()) {
4847 // don't let the same event be fired twice when hitting
4848 // enter/return! (Bug 420502)
4852 // dispatch keypress event with char instead of textEvent
4853 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
4854 geckoEvent.time = PR_IntervalNow();
4855 geckoEvent.charCode = bufPtr[0]; // gecko expects OS-translated unicode
4856 geckoEvent.keyCode = 0;
4857 geckoEvent.isChar = PR_TRUE;
4858 if (mKeyDownHandled)
4859 geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
4860 // don't set other modifiers from the current event, because here in
4861 // -insertText: they've already been taken into account in creating
4862 // the input string.
4864 // create event for use by plugins
4865 #ifndef NP_NO_CARBON
4866 EventRecord carbonEvent;
4869 // XXX The ASCII characters inputting mode of egbridge (Japanese IME)
4870 // might send the keyDown event with wrong keyboard layout if other
4871 // keyboard layouts are already loaded. In that case, the native event
4872 // doesn't match to this gecko event...
4873 #ifndef NP_NO_CARBON
4874 if (mPluginEventModel == NPEventModelCarbon) {
4875 ConvertCocoaKeyEventToCarbonEvent(mCurKeyEvent, carbonEvent);
4876 geckoEvent.pluginEvent = &carbonEvent;
4880 geckoEvent.isShift = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
4881 if (!IsPrintableChar(geckoEvent.charCode)) {
4882 geckoEvent.keyCode =
4883 ConvertMacToGeckoKeyCode([mCurKeyEvent keyCode], &geckoEvent,
4884 [mCurKeyEvent charactersIgnoringModifiers]);
4885 geckoEvent.charCode = 0;
4888 // Note that insertText is not called only at key pressing.
4889 if (!IsPrintableChar(geckoEvent.charCode)) {
4890 geckoEvent.keyCode = GetGeckoKeyCodeFromChar(geckoEvent.charCode);
4891 geckoEvent.charCode = 0;
4895 PRBool keyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
4896 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4897 // Only record the results of dispatching geckoEvent if we're currently
4898 // processing a keyDown event.
4900 mKeyPressHandled = keyPressHandled;
4901 mKeyPressSent = YES;
4905 if (!mGeckoChild->TextInputHandler()->IsIMEComposing()) {
4906 [self sendCompositionEvent:NS_COMPOSITION_START];
4907 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4909 mGeckoChild->TextInputHandler()->OnStartIMEComposition(self);
4910 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4914 if (mGeckoChild && mGeckoChild->TextInputHandler()->IgnoreIMECommit()) {
4915 tmpStr = [tmpStr init];
4917 bufPtr[0] = PRUnichar('\0');
4919 [[[NSAttributedString alloc] initWithString:tmpStr] autorelease];
4921 [self sendTextEvent:bufPtr attributedString:insertString
4922 selectedRange:NSMakeRange(0, len)
4923 markedRange:mMarkedRange
4925 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4927 [self sendCompositionEvent:NS_COMPOSITION_END];
4928 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4930 mGeckoChild->TextInputHandler()->OnEndIMEComposition();
4931 // Note: mGeckoChild might have become null here. Don't count on it from here on.
4933 mMarkedRange = NSMakeRange(NSNotFound, 0);
4936 if (bufPtr != buffer)
4939 NS_OBJC_END_TRY_ABORT_BLOCK;
4942 - (void)insertNewline:(id)sender
4944 [self insertText:@"\n"];
4947 - (void) doCommandBySelector:(SEL)aSelector
4949 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4952 NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
4955 if (!mKeyPressHandled)
4956 [super doCommandBySelector:aSelector];
4958 NS_OBJC_END_TRY_ABORT_BLOCK;
4961 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
4963 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4966 NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length);
4967 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4968 NSLog(@" aString = '%@'", aString);
4974 if (mGeckoChild->TextInputHandler()->IgnoreIMEComposition())
4977 nsAutoRetainCocoaObject kungFuDeathGrip(self);
4979 if (![aString isKindOfClass:[NSAttributedString class]])
4980 aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
4982 NSMutableAttributedString *mutableAttribStr = aString;
4983 NSString *tmpStr = [mutableAttribStr string];
4984 unsigned int len = [tmpStr length];
4985 PRUnichar buffer[MAX_BUFFER_SIZE];
4986 PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
4987 [tmpStr getCharacters:bufPtr];
4988 bufPtr[len] = PRUnichar('\0');
4991 printf("****in setMarkedText, len = %d, text = ", len);
4993 PRUint32 maxlen = len > 12 ? 12 : len;
4994 for (PRUnichar *a = bufPtr; (*a != PRUnichar('\0')) && n<maxlen; a++, n++)
4995 printf((*a&0xff80) ? "\\u%4X" : "%c", *a);
4999 mMarkedRange.length = len;
5001 if (!mGeckoChild->TextInputHandler()->IsIMEComposing() && len > 0) {
5002 nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
5003 mGeckoChild->DispatchWindowEvent(selection);
5004 mMarkedRange.location = selection.mSucceeded ? selection.mReply.mOffset : 0;
5005 [self sendCompositionEvent:NS_COMPOSITION_START];
5006 // Note: mGeckoChild might have become null here. Don't count on it from here on.
5008 mGeckoChild->TextInputHandler()->OnStartIMEComposition(self);
5009 // Note: mGeckoChild might have become null here. Don't count on it from here on.
5013 if (mGeckoChild->TextInputHandler()->IsIMEComposing()) {
5014 mGeckoChild->TextInputHandler()->OnUpdateIMEComposition(tmpStr);
5016 BOOL commit = len == 0;
5017 [self sendTextEvent:bufPtr attributedString:aString
5018 selectedRange:selRange
5019 markedRange:mMarkedRange
5021 // Note: mGeckoChild might have become null here. Don't count on it from here on.
5024 [self sendCompositionEvent:NS_COMPOSITION_END];
5025 // Note: mGeckoChild might have become null here. Don't count on it from here on.
5027 mGeckoChild->TextInputHandler()->OnEndIMEComposition();
5028 // Note: mGeckoChild might have become null here. Don't count on it from here on.
5033 if (bufPtr != buffer)
5036 NS_OBJC_END_TRY_ABORT_BLOCK;
5042 NSLog(@"****in unmarkText");
5043 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5046 mGeckoChild->TextInputHandler()->CommitIMEComposition();
5049 - (BOOL) hasMarkedText
5052 NSLog(@"****in hasMarkText");
5053 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5055 return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
5058 - (NSInteger) conversationIdentifier
5061 NSLog(@"****in conversationIdentifier");
5065 nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
5066 textContent.InitForQueryTextContent(0, 0);
5067 mGeckoChild->DispatchWindowEvent(textContent);
5068 if (!textContent.mSucceeded)
5071 NSLog(@" the ID = %ld", (long)textContent.mReply.mContentsRoot);
5073 return (long)textContent.mReply.mContentsRoot;
5076 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
5078 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5081 NSLog(@"****in attributedSubstringFromRange");
5082 NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
5083 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5085 if (!mGeckoChild || theRange.length == 0)
5089 nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
5090 textContent.InitForQueryTextContent(theRange.location, theRange.length);
5091 mGeckoChild->DispatchWindowEvent(textContent);
5093 if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())
5096 NSString* nsstr = ToNSString(textContent.mReply.mString);
5097 NSAttributedString* result =
5098 [[[NSAttributedString alloc] initWithString:nsstr
5099 attributes:nil] autorelease];
5102 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5105 - (NSRange) markedRange
5107 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5110 NSLog(@"****in markedRange");
5111 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5114 if (![self hasMarkedText]) {
5115 return NSMakeRange(NSNotFound, 0);
5118 return mMarkedRange;
5120 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
5123 - (NSRange) selectedRange
5125 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5128 NSLog(@"****in selectedRange");
5129 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5132 return NSMakeRange(NSNotFound, 0);
5133 nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
5134 mGeckoChild->DispatchWindowEvent(selection);
5135 if (!selection.mSucceeded)
5136 return NSMakeRange(NSNotFound, 0);
5139 NSLog(@" result of selectedRange = %d, %d",
5140 selection.mReply.mOffset, selection.mReply.mString.Length());
5142 return NSMakeRange(selection.mReply.mOffset,
5143 selection.mReply.mString.Length());
5145 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
5148 - (NSRect) firstRectForCharacterRange:(NSRange)theRange
5150 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5153 NSLog(@"****in firstRectForCharacterRange");
5154 NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
5155 NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5157 // XXX this returns first character rect or caret rect, it is limitation of
5158 // now. We need more work for returns first line rect. But current
5159 // implementation is enough for IMEs.
5162 if (!mGeckoChild || theRange.location == NSNotFound)
5166 PRBool useCaretRect = theRange.length == 0;
5167 if (!useCaretRect) {
5168 nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_TEXT_RECT, mGeckoChild);
5169 charRect.InitForQueryTextRect(theRange.location, 1);
5170 mGeckoChild->DispatchWindowEvent(charRect);
5171 if (charRect.mSucceeded)
5172 r = charRect.mReply.mRect;
5174 useCaretRect = PR_TRUE;
5178 nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, mGeckoChild);
5179 caretRect.InitForQueryCaretRect(theRange.location);
5180 mGeckoChild->DispatchWindowEvent(caretRect);
5181 if (!caretRect.mSucceeded)
5183 r = caretRect.mReply.mRect;
5187 nsIWidget* rootWidget = mGeckoChild->GetTopLevelWidget();
5188 NSWindow* rootWindow =
5189 static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
5191 static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
5192 if (!rootWindow || !rootView)
5194 GeckoRectToNSRect(r, rect);
5195 rect = [rootView convertRect:rect toView:nil];
5196 rect.origin = [rootWindow convertBaseToScreen:rect.origin];
5198 NSLog(@" result rect (x,y,w,h) = %f, %f, %f, %f",
5199 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
5203 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
5206 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
5209 NSLog(@"****in characterIndexForPoint");
5210 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5213 // To implement this, we'd have to grovel in text frames looking at text offsets.
5217 - (NSArray*) validAttributesForMarkedText
5219 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5222 NSLog(@"****in validAttributesForMarkedText");
5223 NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5226 //return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
5227 return [NSArray array]; // empty array; we don't support any attributes right now
5229 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5234 + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent
5236 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5238 NSEvent* newEvent = [NSEvent keyEventWithType:type
5239 location:[theEvent locationInWindow]
5240 modifierFlags:[theEvent modifierFlags]
5241 timestamp:[theEvent timestamp]
5242 windowNumber:[theEvent windowNumber]
5243 context:[theEvent context]
5244 characters:[theEvent characters]
5245 charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
5246 isARepeat:[theEvent isARepeat]
5247 keyCode:[theEvent keyCode]];
5250 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5254 static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
5256 for (PRUint32 i = 0; i < [aString length]; ++i) {
5257 unichar ch = [aString characterAtIndex:i];
5258 if (ch >= 32 && ch < 128) {
5259 aBuf.Append(char(ch));
5261 aBuf += nsPrintfCString("\\u%04x", ch);
5268 // Returns PR_TRUE if Gecko claims to have handled the event, PR_FALSE otherwise.
5269 // We only send Carbon plugin events with NS_KEY_DOWN gecko events, and only send
5270 // Cocoa plugin events with NS_KEY_PRESS gecko events. This is because we want to
5271 // send repeat key down events to Cocoa plugins but not Carbon plugins.
5272 - (PRBool)processKeyDownEvent:(NSEvent*)theEvent
5274 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5283 PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5284 ("ChildView processKeyDownEvent: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5286 [theEvent modifierFlags],
5287 ToEscapedString([theEvent characters], str1),
5288 ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
5290 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5291 mCurKeyEvent = theEvent;
5293 BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
5294 if (nonDeadKeyPress && !mGeckoChild->TextInputHandler()->IsIMEComposing()) {
5295 NSResponder* firstResponder = [[self window] firstResponder];
5297 nsKeyEvent geckoKeydown(PR_TRUE, NS_KEY_DOWN, nsnull);
5298 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoKeydown];
5300 #ifndef NP_NO_CARBON
5301 EventRecord carbonEvent;
5302 if (mPluginEventModel == NPEventModelCarbon) {
5303 ConvertCocoaKeyEventToCarbonEvent(theEvent, carbonEvent);
5304 geckoKeydown.pluginEvent = &carbonEvent;
5308 mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoKeydown);
5310 return mKeyDownHandled;
5313 // The key down event may have shifted the focus, in which
5314 // case we should not fire the key press.
5315 if (firstResponder != [[self window] firstResponder]) {
5316 PRBool handled = mKeyDownHandled;
5318 mKeyDownHandled = PR_FALSE;
5322 // If this is the context menu key command, send a context menu key event.
5323 unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5324 if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
5325 nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey);
5326 contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
5327 PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent);
5328 [self maybeInitContextMenuTracking];
5329 // Bail, there is nothing else to do here.
5330 PRBool handled = (cmEventHandled || mKeyDownHandled);
5332 mKeyDownHandled = PR_FALSE;
5336 nsKeyEvent geckoKeypress(PR_TRUE, NS_KEY_PRESS, nsnull);
5337 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoKeypress];
5339 // if this is a non-letter keypress, or the control key is down,
5340 // dispatch the keydown to gecko, so that we trap delete,
5341 // control-letter combinations etc before Cocoa tries to use
5342 // them for keybindings.
5343 if ((!geckoKeypress.isChar || geckoKeypress.isControl) &&
5344 !mGeckoChild->TextInputHandler()->IsIMEComposing()) {
5345 if (mKeyDownHandled)
5346 geckoKeypress.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5347 mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoKeypress);
5348 mKeyPressSent = YES;
5350 return (mKeyDownHandled || mKeyPressHandled);
5354 // Let Cocoa interpret the key events, caching IsIMEComposing first.
5355 PRBool wasComposing = mGeckoChild->TextInputHandler()->IsIMEComposing();
5356 PRBool interpretKeyEventsCalled = PR_FALSE;
5357 if (mGeckoChild->TextInputHandler()->IsIMEEnabled() ||
5358 mGeckoChild->TextInputHandler()->IsASCIICapableOnly()) {
5359 [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
5360 interpretKeyEventsCalled = PR_TRUE;
5364 return (mKeyDownHandled || mKeyPressHandled);
5367 if (!mKeyPressSent && nonDeadKeyPress && !wasComposing &&
5368 !mGeckoChild->TextInputHandler()->IsIMEComposing()) {
5369 nsKeyEvent geckoKeypress(PR_TRUE, NS_KEY_PRESS, nsnull);
5370 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoKeypress];
5372 // If we called interpretKeyEvents and this isn't normal character input
5373 // then IME probably ate the event for some reason. We do not want to
5374 // send a key press event in that case.
5375 if (!(interpretKeyEventsCalled && IsNormalCharInputtingEvent(geckoKeypress))) {
5376 if (mKeyDownHandled) {
5377 geckoKeypress.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5379 mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoKeypress);
5383 // Note: mGeckoChild might have become null here. Don't count on it from here on.
5385 PRBool handled = (mKeyDownHandled || mKeyPressHandled);
5387 // See note about nested event loops where these variables are declared in header.
5388 mKeyPressHandled = NO;
5391 mKeyDownHandled = PR_FALSE;
5395 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5399 - (NSTextInputContext *)inputContext
5401 if (mIsPluginView && mPluginEventModel == NPEventModelCocoa)
5402 return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
5404 return [super inputContext];
5408 #ifndef NP_NO_CARBON
5409 // Create a TSM document for use with plugins, so that we can support IME in
5410 // them. Once it's created, if need be (re)activate it. Some plugins (e.g.
5411 // the Flash plugin running in Camino) don't create their own TSM document --
5412 // without which IME can't work. Others (e.g. the Flash plugin running in
5413 // Firefox) create a TSM document that (somehow) makes the input window behave
5414 // badly when it contains more than one kind of input (say Hiragana and
5415 // Romaji). (We can't just use the per-NSView TSM documents that Cocoa
5416 // provides (those created and managed by the NSTSMInputContext class) -- for
5417 // some reason TSMProcessRawKeyEvent() doesn't work with them.)
5418 - (void)activatePluginTSMDoc
5420 if (!mPluginTSMDoc) {
5421 // Create a TSM document that supports both non-Unicode and Unicode input.
5422 // Though [ChildView processPluginKeyEvent:] only sends Mac char codes to
5423 // the plugin, this makes the input window behave better when it contains
5424 // more than one kind of input (say Hiragana and Romaji). This is what
5425 // the OS does when it creates a TSM document for use by an
5426 // NSTSMInputContext class.
5427 InterfaceTypeList supportedServices;
5428 supportedServices[0] = kTextServiceDocumentInterfaceType;
5429 supportedServices[1] = kUnicodeDocumentInterfaceType;
5430 ::NewTSMDocument(2, supportedServices, &mPluginTSMDoc, 0);
5431 // We'll need to use the "input window".
5432 ::UseInputWindow(mPluginTSMDoc, YES);
5433 ::ActivateTSMDocument(mPluginTSMDoc);
5434 } else if (::TSMGetActiveDocument() != mPluginTSMDoc) {
5435 ::ActivateTSMDocument(mPluginTSMDoc);
5438 #endif // NP_NO_CARBON
5440 // This is a private API that Cocoa uses.
5441 // Cocoa will call this after the menu system returns "NO" for "performKeyEquivalent:".
5442 // We want all they key events we can get so just return YES. In particular, this fixes
5443 // ctrl-tab - we don't get a "keyDown:" call for that without this.
5444 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event
5449 - (BOOL)inCocoaPluginComposition
5452 return [[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition];
5454 return mPluginTSMInComposition;
5458 - (void)sendCocoaNPAPITextEvent:(NSString*)string
5460 NPCocoaEvent cocoaTextEvent;
5461 InitNPCocoaEvent(&cocoaTextEvent);
5462 cocoaTextEvent.type = NPCocoaEventTextInput;
5463 cocoaTextEvent.data.text.text = (NPNSString*)string;
5465 nsGUIEvent pluginTextEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
5466 pluginTextEvent.time = PR_IntervalNow();
5467 pluginTextEvent.pluginEvent = (void*)&cocoaTextEvent;
5468 mGeckoChild->DispatchWindowEvent(pluginTextEvent);
5471 - (void)keyDown:(NSEvent*)theEvent
5473 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5475 if (mGeckoChild && mIsPluginView) {
5477 if (mPluginEventModel == NPEventModelCocoa) {
5478 ComplexTextInputPanel* ctiPanel = [ComplexTextInputPanel sharedComplexTextInputPanel];
5480 // If a composition is in progress then simply let the input panel continue it.
5481 if ([self inCocoaPluginComposition]) {
5482 // Don't send key up events for key downs associated with compositions.
5483 mIgnoreNextKeyUpEvent = YES;
5485 NSString* textString = nil;
5486 [ctiPanel interpretKeyEvent:theEvent string:&textString];
5488 [self sendCocoaNPAPITextEvent:textString];
5494 // Reset complex text input request flag.
5495 mPluginComplexTextInputRequested = NO;
5497 // Send key down event to the plugin.
5498 nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
5499 NPCocoaEvent cocoaEvent;
5500 ConvertCocoaKeyEventToNPCocoaEvent(theEvent, cocoaEvent);
5501 pluginEvent.pluginEvent = &cocoaEvent;
5502 mGeckoChild->DispatchWindowEvent(pluginEvent);
5507 // Start complex text composition if requested.
5508 if (mPluginComplexTextInputRequested) {
5509 // Don't send key up events for key downs associated with compositions.
5510 mIgnoreNextKeyUpEvent = YES;
5512 NSString* textString = nil;
5513 [ctiPanel interpretKeyEvent:theEvent string:&textString];
5515 [self sendCocoaNPAPITextEvent:textString];
5521 // Nothing else to do for Cocoa NPAPI plugins.
5526 #ifndef NP_NO_CARBON
5527 BOOL wasInComposition = NO;
5528 if (mPluginEventModel == NPEventModelCocoa) {
5529 if ([self inCocoaPluginComposition]) {
5530 wasInComposition = YES;
5532 // Don't send key up events for key downs associated with compositions.
5533 mIgnoreNextKeyUpEvent = YES;
5536 // Reset complex text input request flag.
5537 mPluginComplexTextInputRequested = NO;
5539 // Send key down event to the plugin.
5540 nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
5541 NPCocoaEvent cocoaEvent;
5542 ConvertCocoaKeyEventToNPCocoaEvent(theEvent, cocoaEvent);
5543 pluginEvent.pluginEvent = &cocoaEvent;
5544 mGeckoChild->DispatchWindowEvent(pluginEvent);
5549 // Only continue if plugin wants complex text input.
5550 if (mPluginComplexTextInputRequested) {
5551 // Don't send key up events for key downs associated with compositions.
5552 mIgnoreNextKeyUpEvent = YES;
5560 // This will take care of all Carbon plugin events and also send Cocoa plugin
5561 // text events when NSInputContext is not available (ifndef NP_NO_CARBON).
5562 [self activatePluginTSMDoc];
5563 // We use the active TSM document to pass a pointer to ourselves (the
5564 // currently focused ChildView) to PluginKeyEventsHandler(). Because this
5565 // pointer is weak, we should retain and release ourselves around the call
5566 // to TSMProcessRawKeyEvent().
5567 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5568 ::TSMSetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
5569 sizeof(ChildView *), &self);
5570 ::TSMProcessRawKeyEvent([theEvent _eventRef]);
5571 ::TSMRemoveDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag);
5577 PRBool handled = [self processKeyDownEvent:theEvent];
5579 // We always allow keyboard events to propagate to keyDown: but if they are not
5580 // handled we give special Application menu items a chance to act.
5581 if (!handled && sApplicationMenu) {
5582 [sApplicationMenu performKeyEquivalent:theEvent];
5585 NS_OBJC_END_TRY_ABORT_BLOCK;
5588 - (void)keyUp:(NSEvent*)theEvent
5590 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5596 PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5597 ("ChildView keyUp: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5599 [theEvent modifierFlags],
5600 ToEscapedString([theEvent characters], str1),
5601 ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
5606 if (mIgnoreNextKeyUpEvent) {
5607 mIgnoreNextKeyUpEvent = NO;
5611 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5613 if (mIsPluginView) {
5614 if (mPluginEventModel == NPEventModelCocoa) {
5615 // Don't send key up events to Cocoa plugins during composition.
5616 if ([self inCocoaPluginComposition]) {
5620 nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, nsnull);
5621 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&keyUpEvent];
5622 NPCocoaEvent pluginEvent;
5623 ConvertCocoaKeyEventToNPCocoaEvent(theEvent, pluginEvent);
5624 keyUpEvent.pluginEvent = &pluginEvent;
5625 mGeckoChild->DispatchWindowEvent(keyUpEvent);
5627 #ifndef NP_NO_CARBON
5628 if (mPluginEventModel == NPEventModelCarbon) {
5629 // I'm not sure the call to TSMProcessRawKeyEvent() is needed here (though
5630 // WebKit makes one).
5631 ::TSMProcessRawKeyEvent([theEvent _eventRef]);
5633 // Don't send a keyUp event if the corresponding keyDown event(s) is/are
5634 // still being processed (idea borrowed from WebKit).
5635 ChildView *keyDownTarget = nil;
5636 OSStatus status = ::TSMGetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
5637 sizeof(ChildView *), nil, &keyDownTarget);
5638 if (status != noErr)
5639 keyDownTarget = nil;
5640 if (keyDownTarget == self)
5643 // PluginKeyEventsHandler() never sends keyUp events to [ChildView
5644 // processPluginKeyEvent:], so we need to send them to Gecko here. (This
5645 // means that when commiting text from IME, several keyDown events may be
5646 // sent to Gecko (in processPluginKeyEvent) for one keyUp event here.
5647 // But this is how the WebKit does it, and games expect a keyUp event to
5648 // be sent when it actually happens (they need to be able to detect how
5649 // long a key has been held down) -- which wouldn't be possible if we sent
5650 // them from processPluginKeyEvent.)
5651 nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, nsnull);
5652 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&keyUpEvent];
5653 EventRecord macKeyUpEvent;
5654 ConvertCocoaKeyEventToCarbonEvent(theEvent, macKeyUpEvent);
5655 keyUpEvent.pluginEvent = &macKeyUpEvent;
5656 mGeckoChild->DispatchWindowEvent(keyUpEvent);
5662 // if we don't have any characters we can't generate a keyUp event
5663 if ([[theEvent characters] length] == 0 ||
5664 mGeckoChild->TextInputHandler()->IsIMEComposing()) {
5668 nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_UP, nsnull);
5669 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5671 mGeckoChild->DispatchWindowEvent(geckoEvent);
5673 NS_OBJC_END_TRY_ABORT_BLOCK;
5676 - (void)flagsChanged:(NSEvent*)theEvent
5678 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5683 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5685 // CapsLock state and other modifier states are different:
5686 // CapsLock state does not revert when the CapsLock key goes up, as the
5687 // modifier state does for other modifier keys on key up.
5688 if ([theEvent keyCode] == kCapsLockKeyCode) {
5689 // Fire key down event for caps lock.
5690 [self fireKeyEventForFlagsChanged:theEvent keyDown:YES];
5693 // XXX should we fire keyup event too? The keyup event for CapsLock key
5694 // is never sent to gecko.
5695 } else if ([theEvent type] == NSFlagsChanged) {
5696 // Fire key up/down events for the modifier keys (shift, alt, ctrl, command).
5697 unsigned int modifiers = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5698 const PRUint32 kModifierMaskTable[] =
5699 { NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask, NSCommandKeyMask };
5700 const PRUint32 kModifierCount = sizeof(kModifierMaskTable) /
5701 sizeof(kModifierMaskTable[0]);
5703 for (PRUint32 i = 0; i < kModifierCount; i++) {
5704 PRUint32 modifierBit = kModifierMaskTable[i];
5705 if ((modifiers & modifierBit) != (gLastModifierState & modifierBit)) {
5706 BOOL isKeyDown = (modifiers & modifierBit) != 0 ? YES : NO;
5708 [self fireKeyEventForFlagsChanged:theEvent keyDown:isKeyDown];
5713 // Stop if focus has changed.
5714 // Check to see if we are still the first responder.
5715 if (![self isFirstResponder])
5720 gLastModifierState = modifiers;
5723 NS_OBJC_END_TRY_ABORT_BLOCK;
5726 - (BOOL) isFirstResponder
5728 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5730 NSResponder* resp = [[self window] firstResponder];
5731 return (resp == (NSResponder*)self);
5733 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5736 - (BOOL)isDragInProgress
5741 nsCOMPtr<nsIDragSession> dragSession;
5742 mDragService->GetCurrentSession(getter_AddRefs(dragSession));
5743 return dragSession != nsnull;
5746 - (void)fireKeyEventForFlagsChanged:(NSEvent*)theEvent keyDown:(BOOL)isKeyDown
5748 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5750 if (!mGeckoChild || [theEvent type] != NSFlagsChanged ||
5751 mGeckoChild->TextInputHandler()->IsIMEComposing()) {
5755 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5757 PRUint32 message = isKeyDown ? NS_KEY_DOWN : NS_KEY_UP;
5759 // Fire a key event.
5760 nsKeyEvent geckoEvent(PR_TRUE, message, nsnull);
5761 [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5763 // create event for use by plugins
5764 #ifndef NP_NO_CARBON
5765 EventRecord carbonEvent;
5766 if (mPluginEventModel == NPEventModelCarbon) {
5767 ConvertCocoaKeyEventToCarbonEvent(theEvent, carbonEvent, message);
5768 geckoEvent.pluginEvent = &carbonEvent;
5771 NPCocoaEvent cocoaEvent;
5772 if (mPluginEventModel == NPEventModelCocoa) {
5773 ConvertCocoaKeyEventToNPCocoaEvent(theEvent, cocoaEvent, message);
5774 geckoEvent.pluginEvent = &cocoaEvent;
5777 mGeckoChild->DispatchWindowEvent(geckoEvent);
5779 NS_OBJC_END_TRY_ABORT_BLOCK;
5782 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent
5784 // If we're being destroyed assume the default -- return YES.
5788 nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_ACTIVATE, nsnull, nsMouseEvent::eReal);
5789 [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
5790 return !mGeckoChild->DispatchWindowEvent(geckoEvent);
5793 // Don't focus a plugin if we're in a left click-through that will fail (see
5794 // [ChildView isInFailingLeftClickThrough] above).
5795 - (BOOL)shouldFocusPlugin
5800 nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
5801 if (windowWidget && !windowWidget->ShouldFocusPlugin())
5807 // Returns NO if the plugin shouldn't be focused/unfocused.
5808 - (BOOL)updateCocoaPluginFocusStatus:(BOOL)hasFocus
5813 if (![self shouldFocusPlugin])
5816 nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
5817 NPCocoaEvent cocoaEvent;
5818 InitNPCocoaEvent(&cocoaEvent);
5819 cocoaEvent.type = NPCocoaEventFocusChanged;
5820 cocoaEvent.data.focus.hasFocus = hasFocus;
5821 pluginEvent.pluginEvent = &cocoaEvent;
5822 mGeckoChild->DispatchWindowEvent(pluginEvent);
5825 [self sendFocusEvent:NS_PLUGIN_FOCUS];
5830 // We must always call through to our superclass, even when mGeckoChild is
5831 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
5832 - (BOOL)becomeFirstResponder
5834 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5836 if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) {
5837 if (![self updateCocoaPluginFocusStatus:YES])
5841 return [super becomeFirstResponder];
5843 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES);
5846 // We must always call through to our superclass, even when mGeckoChild is
5847 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
5848 - (BOOL)resignFirstResponder
5850 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5852 if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) {
5853 if (![self updateCocoaPluginFocusStatus:NO])
5857 return [super resignFirstResponder];
5859 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES);
5862 - (void)viewsWindowDidBecomeKey
5864 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5869 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5871 // check to see if the window implements the mozWindow protocol. This
5872 // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
5873 // which can happen because these activate calls propagate out
5874 // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
5875 BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
5877 [[self window] setSuppressMakeKeyFront:YES];
5879 [self sendFocusEvent:NS_ACTIVATE];
5882 [[self window] setSuppressMakeKeyFront:NO];
5884 NS_OBJC_END_TRY_ABORT_BLOCK;
5887 - (void)viewsWindowDidResignKey
5892 nsAutoRetainCocoaObject kungFuDeathGrip(self);
5894 [self sendFocusEvent:NS_DEACTIVATE];
5897 // If the call to removeFromSuperview isn't delayed from nsChildView::
5898 // TearDownView(), the NSView hierarchy might get changed during calls to
5899 // [ChildView drawRect:], which leads to "beyond bounds" exceptions in
5900 // NSCFArray. For more info see bmo bug 373122. Apple's docs claim that
5901 // removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
5902 // display" (whatever "display" means). But it's _not_ true that it can be
5903 // safely invoked during calls to [NSView drawRect:]. We use
5904 // removeFromSuperview here because there's no longer any danger of being
5905 // "invoked during display", and because doing do clears up bmo bug 384343.
5906 - (void)delayedTearDown
5908 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5910 [self removeFromSuperview];
5913 NS_OBJC_END_TRY_ABORT_BLOCK;
5918 // drag'n'drop stuff
5919 #define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
5921 - (NSDragOperation)dragOperationForSession:(nsIDragSession*)aDragSession
5923 PRUint32 dragAction;
5924 aDragSession->GetDragAction(&dragAction);
5925 if (nsIDragService::DRAGDROP_ACTION_LINK & dragAction)
5926 return NSDragOperationLink;
5927 if (nsIDragService::DRAGDROP_ACTION_COPY & dragAction)
5928 return NSDragOperationCopy;
5929 if (nsIDragService::DRAGDROP_ACTION_MOVE & dragAction)
5930 return NSDragOperationGeneric;
5931 return NSDragOperationNone;
5934 // This is a utility function used by NSView drag event methods
5935 // to send events. It contains all of the logic needed for Gecko
5936 // dragging to work. Returns the appropriate cocoa drag operation code.
5937 - (NSDragOperation)doDragAction:(PRUint32)aMessage sender:(id)aSender
5939 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5942 return NSDragOperationNone;
5944 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView doDragAction: entered\n"));
5946 if (!mDragService) {
5947 CallGetService(kDragServiceContractID, &mDragService);
5948 NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
5950 return NSDragOperationNone;
5953 if (aMessage == NS_DRAGDROP_ENTER)
5954 mDragService->StartDragSession();
5956 nsCOMPtr<nsIDragSession> dragSession;
5957 mDragService->GetCurrentSession(getter_AddRefs(dragSession));
5959 if (aMessage == NS_DRAGDROP_OVER) {
5960 // fire the drag event at the source. Just ignore whether it was
5961 // cancelled or not as there isn't actually a means to stop the drag
5962 mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
5963 dragSession->SetCanDrop(PR_FALSE);
5965 else if (aMessage == NS_DRAGDROP_DROP) {
5966 // We make the assumption that the dragOver handlers have correctly set
5967 // the |canDrop| property of the Drag Session.
5968 PRBool canDrop = PR_FALSE;
5969 if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
5970 [self doDragAction:NS_DRAGDROP_EXIT sender:aSender];
5972 nsCOMPtr<nsIDOMNode> sourceNode;
5973 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
5975 mDragService->EndDragSession(PR_FALSE);
5977 return NSDragOperationNone;
5981 unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
5982 PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
5983 // force copy = option, alias = cmd-option, default is move
5984 if (modifierFlags & NSAlternateKeyMask) {
5985 if (modifierFlags & NSCommandKeyMask)
5986 action = nsIDragService::DRAGDROP_ACTION_LINK;
5988 action = nsIDragService::DRAGDROP_ACTION_COPY;
5990 dragSession->SetDragAction(action);
5993 // set up gecko event
5994 nsDragEvent geckoEvent(PR_TRUE, aMessage, nsnull);
5995 [self convertGenericCocoaEvent:[NSApp currentEvent] toGeckoEvent:&geckoEvent];
5997 // Use our own coordinates in the gecko event.
5998 // Convert event from gecko global coords to gecko view coords.
5999 NSPoint localPoint = [self convertPoint:[aSender draggingLocation] fromView:nil];
6000 geckoEvent.refPoint.x = static_cast<nscoord>(localPoint.x);
6001 geckoEvent.refPoint.y = static_cast<nscoord>(localPoint.y);
6003 nsAutoRetainCocoaObject kungFuDeathGrip(self);
6004 mGeckoChild->DispatchWindowEvent(geckoEvent);
6006 return NSDragOperationNone;
6010 case NS_DRAGDROP_ENTER:
6011 case NS_DRAGDROP_OVER:
6012 return [self dragOperationForSession:dragSession];
6013 case NS_DRAGDROP_EXIT:
6014 case NS_DRAGDROP_DROP: {
6015 nsCOMPtr<nsIDOMNode> sourceNode;
6016 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
6018 // We're leaving a window while doing a drag that was
6019 // initiated in a different app. End the drag session,
6020 // since we're done with it for now (until the user
6021 // drags back into mozilla).
6022 mDragService->EndDragSession(PR_FALSE);
6028 return NSDragOperationGeneric;
6030 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
6033 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
6035 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
6037 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingEntered: entered\n"));
6039 // there should never be a globalDragPboard when "draggingEntered:" is
6040 // called, but just in case we'll take care of it here.
6041 [globalDragPboard release];
6043 // Set the global drag pasteboard that will be used for this drag session.
6044 // This will be set back to nil when the drag session ends (mouse exits
6045 // the view or a drop happens within the view).
6046 globalDragPboard = [[sender draggingPasteboard] retain];
6048 return [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
6050 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
6053 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
6055 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingUpdated: entered\n"));
6057 return [self doDragAction:NS_DRAGDROP_OVER sender:sender];
6060 - (void)draggingExited:(id <NSDraggingInfo>)sender
6062 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingExited: entered\n"));
6064 nsAutoRetainCocoaObject kungFuDeathGrip(self);
6065 [self doDragAction:NS_DRAGDROP_EXIT sender:sender];
6066 NS_IF_RELEASE(mDragService);
6069 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
6071 nsAutoRetainCocoaObject kungFuDeathGrip(self);
6072 BOOL handled = [self doDragAction:NS_DRAGDROP_DROP sender:sender] != NSDragOperationNone;
6073 NS_IF_RELEASE(mDragService);
6078 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
6080 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6082 gDraggedTransferables = nsnull;
6084 NSEvent *currentEvent = [NSApp currentEvent];
6085 gUserCancelledDrag = ([currentEvent type] == NSKeyDown &&
6086 [currentEvent keyCode] == kEscapeKeyCode);
6088 if (!mDragService) {
6089 CallGetService(kDragServiceContractID, &mDragService);
6090 NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
6094 // set the dragend point from the current mouse location
6095 nsDragService* dragService = static_cast<nsDragService *>(mDragService);
6096 NSPoint pnt = [NSEvent mouseLocation];
6097 FlipCocoaScreenCoordinate(pnt);
6098 dragService->SetDragEndPoint(nsIntPoint(NSToIntRound(pnt.x), NSToIntRound(pnt.y)));
6100 // XXX: dropEffect should be updated per |operation|.
6101 // As things stand though, |operation| isn't well handled within "our"
6102 // events, that is, when the drop happens within the window: it is set
6103 // either to NSDragOperationGeneric or to NSDragOperationNone.
6104 // For that reason, it's not yet possible to override dropEffect per the
6105 // given OS value, and it's also unclear what's the correct dropEffect
6106 // value for NSDragOperationGeneric that is passed by other applications.
6107 // All that said, NSDragOperationNone is still reliable.
6108 if (operation == NSDragOperationNone) {
6109 nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
6110 dragService->GetDataTransfer(getter_AddRefs(dataTransfer));
6111 nsCOMPtr<nsIDOMNSDataTransfer> dataTransferNS =
6112 do_QueryInterface(dataTransfer);
6115 dataTransferNS->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
6118 mDragService->EndDragSession(PR_TRUE);
6119 NS_RELEASE(mDragService);
6122 [globalDragPboard release];
6123 globalDragPboard = nil;
6124 [gLastDragMouseDownEvent release];
6125 gLastDragMouseDownEvent = nil;
6127 NS_OBJC_END_TRY_ABORT_BLOCK;
6131 // this is just implemented so we comply with the NSDraggingSource informal protocol
6132 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
6137 // This method is a callback typically invoked in response to a drag ending on the desktop
6138 // or a Findow folder window; the argument passed is a path to the drop location, to be used
6139 // in constructing a complete pathname for the file(s) we want to create as a result of
6141 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination
6143 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
6147 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n"));
6149 nsCOMPtr<nsILocalFile> targFile;
6150 NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(targFile));
6151 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
6152 if (!macLocalFile) {
6153 NS_ERROR("No Mac local file");
6157 if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
6158 NS_ERROR("failed InitWithCFURL");
6162 if (!gDraggedTransferables)
6165 PRUint32 transferableCount;
6166 rv = gDraggedTransferables->Count(&transferableCount);
6170 for (PRUint32 i = 0; i < transferableCount; i++) {
6171 nsCOMPtr<nsISupports> genericItem;
6172 gDraggedTransferables->GetElementAt(i, getter_AddRefs(genericItem));
6173 nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
6175 NS_ERROR("no transferable");
6179 item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsILocalFile*));
6181 // now request the kFilePromiseMime data, which will invoke the data provider
6182 // If successful, the returned data is a reference to the resulting file.
6183 nsCOMPtr<nsISupports> fileDataPrimitive;
6184 PRUint32 dataSize = 0;
6185 item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
6188 NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
6189 NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"];
6190 NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
6191 NSArray* rslt = [NSArray arrayWithObject:name];
6197 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6202 // Support for the "Services" menu. We currently only support sending strings
6203 // and HTML to system services.
6205 - (id)validRequestorForSendType:(NSString *)sendType
6206 returnType:(NSString *)returnType
6208 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
6210 // sendType contains the type of data that the service would like this
6211 // application to send to it. sendType is nil if the service is not
6212 // requesting any data.
6214 // returnType contains the type of data the the service would like to
6215 // return to this application (e.g., to overwrite the selection).
6216 // returnType is nil if the service will not return any data.
6218 // The following condition thus triggers when the service expects a string
6219 // or HTML from us or no data at all AND when the service will either not
6220 // send back any data to us or will send a string or HTML back to us.
6222 #define IsSupportedType(typeStr) ([typeStr isEqual:NSStringPboardType] || [typeStr isEqual:NSHTMLPboardType])
6226 if ((!sendType || IsSupportedType(sendType)) &&
6227 (!returnType || IsSupportedType(returnType))) {
6229 // Assume that this object will be able to handle this request.
6232 // Keep the ChildView alive during this operation.
6233 nsAutoRetainCocoaObject kungFuDeathGrip(self);
6235 // Determine if there is a selection (if sending to the service).
6237 nsQueryContentEvent event(PR_TRUE, NS_QUERY_CONTENT_STATE, mGeckoChild);
6238 // This might destroy our widget (and null out mGeckoChild).
6239 mGeckoChild->DispatchWindowEvent(event);
6240 if (!mGeckoChild || !event.mSucceeded || !event.mReply.mHasSelection)
6244 // Determine if we can paste (if receiving data from the service).
6245 if (mGeckoChild && returnType) {
6246 nsContentCommandEvent command(PR_TRUE, NS_CONTENT_COMMAND_PASTE_TRANSFERABLE, mGeckoChild, PR_TRUE);
6247 // This might possibly destroy our widget (and null out mGeckoChild).
6248 mGeckoChild->DispatchWindowEvent(command);
6249 if (!mGeckoChild || !command.mSucceeded || !command.mIsEnabled)
6255 #undef IsSupportedType
6257 // Give the superclass a chance if this object will not handle this request.
6259 result = [super validRequestorForSendType:sendType returnType:returnType];
6263 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6266 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
6267 types:(NSArray *)types
6269 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
6271 nsAutoRetainCocoaObject kungFuDeathGrip(self);
6273 // Make sure that the service will accept strings or HTML.
6274 if ([types containsObject:NSStringPboardType] == NO &&
6275 [types containsObject:NSHTMLPboardType] == NO)
6278 // Bail out if there is no Gecko object.
6282 // Obtain the current selection.
6283 nsQueryContentEvent event(PR_TRUE,
6284 NS_QUERY_SELECTION_AS_TRANSFERABLE,
6286 mGeckoChild->DispatchWindowEvent(event);
6287 if (!event.mSucceeded || !event.mReply.mTransferable)
6290 // Transform the transferable to an NSDictionary.
6291 NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(event.mReply.mTransferable);
6292 if (!pasteboardOutputDict)
6295 // Declare the pasteboard types.
6296 unsigned int typeCount = [pasteboardOutputDict count];
6297 NSMutableArray * types = [NSMutableArray arrayWithCapacity:typeCount];
6298 [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
6299 [pboard declareTypes:types owner:nil];
6301 // Write the data to the pasteboard.
6302 for (unsigned int i = 0; i < typeCount; i++) {
6303 NSString* currentKey = [types objectAtIndex:i];
6304 id currentValue = [pasteboardOutputDict valueForKey:currentKey];
6306 if (currentKey == NSStringPboardType ||
6307 currentKey == kCorePboardType_url ||
6308 currentKey == kCorePboardType_urld ||
6309 currentKey == kCorePboardType_urln) {
6310 [pboard setString:currentValue forType:currentKey];
6311 } else if (currentKey == NSHTMLPboardType) {
6312 [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) forType:currentKey];
6313 } else if (currentKey == NSTIFFPboardType) {
6314 [pboard setData:currentValue forType:currentKey];
6315 } else if (currentKey == NSFilesPromisePboardType) {
6316 [pboard setPropertyList:currentValue forType:currentKey];
6322 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
6325 // Called if the service wants us to replace the current selection.
6326 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard
6329 nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
6333 trans->AddDataFlavor(kUnicodeMime);
6334 trans->AddDataFlavor(kHTMLMime);
6336 rv = nsClipboard::TransferableFromPasteboard(trans, pboard);
6343 nsContentCommandEvent command(PR_TRUE,
6344 NS_CONTENT_COMMAND_PASTE_TRANSFERABLE,
6346 command.mTransferable = trans;
6347 mGeckoChild->DispatchWindowEvent(command);
6349 return command.mSucceeded && command.mIsEnabled;
6354 #ifdef ACCESSIBILITY
6356 /* Every ChildView has a corresponding mozDocAccessible object that is doing all
6357 the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
6360 All ChildView needs to do is to route all accessibility calls (from the NSAccessibility APIs)
6361 down to its object, pretending that they are the same.
6363 - (id<mozAccessible>)accessible
6368 id<mozAccessible> nativeAccessible = nil;
6370 nsAutoRetainCocoaObject kungFuDeathGrip(self);
6371 nsCOMPtr<nsIWidget> kungFuDeathGrip2(mGeckoChild);
6372 nsRefPtr<nsAccessible> accessible = mGeckoChild->GetDocumentAccessible();
6377 accessible->GetNativeInterface((void**)&nativeAccessible);
6380 NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
6383 return nativeAccessible;
6386 /* Implementation of formal mozAccessible formal protocol (enabling mozViews
6387 to talk to mozAccessible objects in the accessibility module). */
6389 - (BOOL)hasRepresentedView
6394 - (id)representedView
6401 return [[self accessible] isRoot];
6405 - (void)printHierarchy
6407 [[self accessible] printHierarchy];
6415 - (BOOL)accessibilityIsIgnored
6417 return [[self accessible] accessibilityIsIgnored];
6420 - (id)accessibilityHitTest:(NSPoint)point
6422 return [[self accessible] accessibilityHitTest:point];
6425 - (id)accessibilityFocusedUIElement
6427 return [[self accessible] accessibilityFocusedUIElement];
6432 - (NSArray*)accessibilityActionNames
6434 return [[self accessible] accessibilityActionNames];
6437 - (NSString*)accessibilityActionDescription:(NSString*)action
6439 return [[self accessible] accessibilityActionDescription:action];
6442 - (void)accessibilityPerformAction:(NSString*)action
6444 return [[self accessible] accessibilityPerformAction:action];
6449 - (NSArray*)accessibilityAttributeNames
6451 return [[self accessible] accessibilityAttributeNames];
6454 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
6456 return [[self accessible] accessibilityIsAttributeSettable:attribute];
6459 - (id)accessibilityAttributeValue:(NSString*)attribute
6461 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
6463 id<mozAccessible> accessible = [self accessible];
6465 // if we're the root (topmost) accessible, we need to return our native AXParent as we
6466 // traverse outside to the hierarchy of whoever embeds us. thus, fall back on NSView's
6467 // default implementation for this attribute.
6468 if ([attribute isEqualToString:NSAccessibilityParentAttribute] && [accessible isRoot]) {
6469 id parentAccessible = [super accessibilityAttributeValue:attribute];
6470 return parentAccessible;
6473 return [accessible accessibilityAttributeValue:attribute];
6475 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6478 #endif /* ACCESSIBILITY */
6485 ChildViewMouseTracker::OnDestroyView(ChildView* aView)
6487 if (sLastMouseEventView == aView)
6488 sLastMouseEventView = nil;
6492 ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent)
6494 ChildView* oldView = sLastMouseEventView;
6495 sLastMouseEventView = ViewForEvent(aEvent);
6496 if (sLastMouseEventView != oldView) {
6497 // Send enter and / or exit events.
6498 nsMouseEvent::exitType type = [sLastMouseEventView window] == [oldView window] ?
6499 nsMouseEvent::eChild : nsMouseEvent::eTopLevel;
6500 [oldView sendMouseEnterOrExitEvent:aEvent enter:NO type:type];
6501 // After the cursor exits the window set it to a visible regular arrow cursor.
6502 if (type == nsMouseEvent::eTopLevel) {
6503 [[nsCursorManager sharedInstance] setCursor:eCursor_standard];
6505 [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent enter:YES type:type];
6510 ChildViewMouseTracker::MouseMoved(NSEvent* aEvent)
6512 ReEvaluateMouseEnterState(aEvent);
6513 [sLastMouseEventView handleMouseMoved:aEvent];
6517 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
6519 NSWindow* window = WindowForEvent(aEvent);
6523 NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
6524 NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
6525 NS_ASSERTION(view, "How can the mouse be over a window but not over a view in that window?");
6526 if (![view isKindOfClass:[ChildView class]])
6529 ChildView* childView = (ChildView*)view;
6530 // If childView is being destroyed return nil.
6531 if (![childView widget])
6533 return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
6536 static CGWindowLevel kDockWindowLevel = 0;
6537 static CGWindowLevel kPopupWindowLevel = 0;
6538 static CGWindowLevel kFloatingWindowLevel = 0;
6540 static BOOL WindowNumberIsUnderPoint(NSInteger aWindowNumber, NSPoint aPoint) {
6541 NSWindow* window = [NSApp windowWithWindowNumber:aWindowNumber];
6543 // This is one of our own windows.
6544 return NSMouseInRect(aPoint, [window frame], NO);
6547 CGSConnection cid = _CGSDefaultConnection();
6549 if (!kDockWindowLevel) {
6550 // These constants are in fact function calls, so cache them.
6551 kDockWindowLevel = kCGDockWindowLevel;
6552 kPopupWindowLevel = kCGPopUpMenuWindowLevel;
6553 kFloatingWindowLevel = kCGFloatingWindowLevel;
6556 // Some things put transparent windows on top of everything. Ignore them.
6557 CGWindowLevel level;
6558 if ((kCGErrorSuccess == CGSGetWindowLevel(cid, aWindowNumber, &level)) &&
6559 (level == kDockWindowLevel || // Transparent layer, spanning the whole screen
6560 level == kFloatingWindowLevel || // invisible Jing window
6561 level > kPopupWindowLevel)) // Snapz Pro X while recording a screencast
6564 // Ignore transparent windows.
6566 if ((kCGErrorSuccess == CGSGetWindowAlpha(cid, aWindowNumber, &alpha)) &&
6571 if (kCGErrorSuccess != CGSGetScreenRectForWindow(cid, aWindowNumber, &rect))
6574 CGPoint point = { aPoint.x, nsCocoaUtils::FlippedScreenY(aPoint.y) };
6575 return CGRectContainsPoint(rect, point);
6578 // Find the window number of the window under the given point, regardless of
6579 // which app the window belongs to. Returns 0 if no window was found.
6580 static NSInteger WindowNumberAtPoint(NSPoint aPoint) {
6581 // We'd like to use the new windowNumberAtPoint API on 10.6 but we can't rely
6582 // on it being up-to-date. For example, if we've just opened a window,
6583 // windowNumberAtPoint might not know about it yet, so we'd send events to the
6584 // wrong window. See bug 557986.
6585 // So we'll have to find the right window manually by iterating over all
6586 // windows on the screen and testing whether the mouse is inside the window's
6587 // rect. We do this using private CGS functions.
6588 // Another way of doing it would be to use tracking rects, but those are
6589 // view-controlled, so they need to be reset whenever an NSView changes its
6590 // size or position, which is expensive. See bug 300904 comment 20.
6591 // A problem with using the CGS functions is that we only look at the windows'
6592 // rects, not at the transparency of the actual pixel the mouse is over. This
6593 // means that we won't treat transparent pixels as transparent to mouse
6594 // events, which is a disadvantage over using tracking rects and leads to the
6595 // crummy window level workarounds in WindowNumberIsUnderPoint.
6596 // But speed is more important.
6598 // Get the window list.
6599 NSInteger windowCount;
6600 NSCountWindows(&windowCount);
6601 NSInteger* windowList = (NSInteger*)malloc(sizeof(NSInteger) * windowCount);
6605 // The list we get back here is in order from front to back.
6606 NSWindowList(windowCount, windowList);
6607 for (NSInteger i = 0; i < windowCount; i++) {
6608 NSInteger windowNumber = windowList[i];
6609 if (WindowNumberIsUnderPoint(windowNumber, aPoint)) {
6611 return windowNumber;
6619 // Find Gecko window under the mouse. Returns nil if the mouse isn't over
6620 // any of our windows.
6622 ChildViewMouseTracker::WindowForEvent(NSEvent* anEvent)
6624 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
6626 NSPoint screenPoint = nsCocoaUtils::ScreenLocationForEvent(anEvent);
6627 NSInteger windowNumber = WindowNumberAtPoint(screenPoint);
6629 // This will return nil if windowNumber belongs to a window that we don't own.
6630 return [NSApp windowWithWindowNumber:windowNumber];
6632 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6636 ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
6637 ChildView* aView, BOOL aIsClickThrough)
6639 // Right mouse down events may get through to all windows, even to a top level
6640 // window with an open sheet.
6641 if (!aWindow || [aEvent type] == NSRightMouseDown)
6644 id delegate = [aWindow delegate];
6645 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
6648 nsIWidget *windowWidget = [(WindowDelegate *)delegate geckoWidget];
6652 nsWindowType windowType;
6653 windowWidget->GetWindowType(windowType);
6655 NSWindow* topLevelWindow = nil;
6657 switch (windowType) {
6658 case eWindowType_popup:
6659 // If this is a context menu, it won't have a parent. So we'll always
6660 // accept mouse move events on context menus even when none of our windows
6661 // is active, which is the right thing to do.
6662 // For panels, the parent window is the XUL window that owns the panel.
6663 return WindowAcceptsEvent([aWindow parentWindow], aEvent, aView, aIsClickThrough);
6665 case eWindowType_toplevel:
6666 case eWindowType_dialog:
6667 if ([aWindow attachedSheet])
6670 topLevelWindow = aWindow;
6672 case eWindowType_sheet: {
6673 nsIWidget* parentWidget = windowWidget->GetSheetWindowParent();
6677 topLevelWindow = (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW);
6685 if (!topLevelWindow ||
6686 ([topLevelWindow isMainWindow] && !aIsClickThrough) ||
6687 [aEvent type] == NSOtherMouseDown ||
6688 (([aEvent modifierFlags] & NSCommandKeyMask) != 0 &&
6689 [aEvent type] != NSMouseMoved))
6692 // If we're here then we're dealing with a left click or mouse move on an
6693 // inactive window or something similar. Ask Gecko what to do.
6694 return [aView inactiveWindowAcceptsMouseEvent:aEvent];
6699 #ifndef NP_NO_CARBON
6701 // Target for text services events sent as the result of calls made to
6702 // TSMProcessRawKeyEvent() in [ChildView keyDown:] (above) when a plugin has
6703 // the focus. The calls to TSMProcessRawKeyEvent() short-circuit Cocoa-based
6704 // IME (which would otherwise interfere with our efforts) and allow Carbon-
6705 // based IME to work in plugins (via the NPAPI). This strategy doesn't cause
6706 // trouble for plugins that (like the Java Embedding Plugin) bypass the NPAPI
6707 // to get their keyboard events and do their own Cocoa-based IME.
6708 OSStatus PluginKeyEventsHandler(EventHandlerCallRef inHandlerRef,
6709 EventRef inEvent, void *userData)
6711 nsAutoreleasePool localPool;
6713 TSMDocumentID activeDoc = ::TSMGetActiveDocument();
6715 return eventNotHandledErr;
6718 ChildView *target = nil;
6719 OSStatus status = ::TSMGetDocumentProperty(activeDoc, kFocusedChildViewTSMDocPropertyTag,
6720 sizeof(ChildView *), nil, &target);
6721 if (status != noErr)
6724 return eventNotHandledErr;
6727 EventRef keyEvent = NULL;
6728 status = ::GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent,
6729 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
6730 if ((status != noErr) || !keyEvent) {
6731 return eventNotHandledErr;
6734 [target processPluginKeyEvent:keyEvent];
6739 static EventHandlerRef gPluginKeyEventsHandler = NULL;
6741 // Called from nsAppShell::Init()
6742 void NS_InstallPluginKeyEventsHandler()
6744 if (gPluginKeyEventsHandler)
6746 static const EventTypeSpec sTSMEvents[] =
6747 { { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
6748 ::InstallEventHandler(::GetEventDispatcherTarget(),
6749 ::NewEventHandlerUPP(PluginKeyEventsHandler),
6750 GetEventTypeCount(sTSMEvents),
6753 &gPluginKeyEventsHandler);
6756 // Called from nsAppShell::Exit()
6757 void NS_RemovePluginKeyEventsHandler()
6759 if (!gPluginKeyEventsHandler)
6761 ::RemoveEventHandler(gPluginKeyEventsHandler);
6762 gPluginKeyEventsHandler = NULL;
6765 // IMKInputSession is an undocumented class in the HIToolbox framework. It's
6766 // present on both Leopard and SnowLeopard, and is used at a low level to
6767 // process IME input regardless of which high-level API is used (Text Services
6768 // Manager or Cocoa). It works the same way in both 32-bit and 64-bit code.
6769 @interface NSObject (IMKInputSessionMethodSwizzling)
6770 - (BOOL)nsChildView_IMKInputSession_handleEvent:(EventRef)theEvent;
6771 - (void)nsChildView_IMKInputSession_commitComposition;
6772 - (void)nsChildView_IMKInputSession_finishSession;
6775 @implementation NSObject (IMKInputSessionMethodSwizzling)
6777 - (BOOL)nsChildView_IMKInputSession_handleEvent:(EventRef)theEvent
6780 BOOL retval = [self nsChildView_IMKInputSession_handleEvent:theEvent];
6781 NSUInteger retainCount = [self retainCount];
6783 // Return without doing anything if we've been deleted.
6784 if (retainCount == 1) {
6788 NSWindow *mainWindow = [NSApp mainWindow];
6789 NSResponder *firstResponder = [mainWindow firstResponder];
6790 if (![firstResponder isKindOfClass:[ChildView class]]) {
6794 // 'charactersEntered' is the length (in bytes) of currently "marked text"
6795 // -- text that's been entered in IME but not yet committed. If it's
6796 // non-zero we're composing text in an IME session; if it's zero we're
6797 // not in an IME session.
6798 NSInteger charactersEntered = 0;
6799 object_getInstanceVariable(self, "charactersEntered", (void **) &charactersEntered);
6800 [(ChildView*)firstResponder setPluginTSMInComposition:(charactersEntered != 0)];
6805 // This method is called whenever IME input is committed as a result of an
6806 // "abnormal" termination -- for example when changing the keyboard focus from
6807 // one input field to another.
6808 - (void)nsChildView_IMKInputSession_commitComposition
6810 NSWindow *mainWindow = [NSApp mainWindow];
6811 NSResponder *firstResponder = [mainWindow firstResponder];
6812 if ([firstResponder isKindOfClass:[ChildView class]]) {
6813 [(ChildView*)firstResponder setPluginTSMInComposition:NO];
6815 [self nsChildView_IMKInputSession_commitComposition];
6818 // This method is called just before we're deallocated.
6819 - (void)nsChildView_IMKInputSession_finishSession
6821 NSWindow *mainWindow = [NSApp mainWindow];
6822 NSResponder *firstResponder = [mainWindow firstResponder];
6823 if ([firstResponder isKindOfClass:[ChildView class]]) {
6824 [(ChildView*)firstResponder setPluginTSMInComposition:NO];
6826 [self nsChildView_IMKInputSession_finishSession];
6831 #endif // NP_NO_CARBON
6833 @interface NSView (MethodSwizzling)
6834 - (BOOL)nsChildView_NSView_mouseDownCanMoveWindow;
6837 @implementation NSView (MethodSwizzling)
6839 // All top-level browser windows belong to the ToolbarWindow class and have
6840 // NSTexturedBackgroundWindowMask turned on in their "style" (see particularly
6841 // [ToolbarWindow initWithContentRect:...] in nsCocoaWindow.mm). This style
6842 // normally means the window "may be moved by clicking and dragging anywhere
6843 // in the window background", but we've suppressed this by giving the
6844 // ChildView class a mouseDownCanMoveWindow method that always returns NO.
6845 // Normally a ToolbarWindow's contentView (not a ChildView) returns YES when
6846 // NSTexturedBackgroundWindowMask is turned on. But normally this makes no
6847 // difference. However, under some (probably very unusual) circumstances
6848 // (and only on Leopard) it *does* make a difference -- for example it
6849 // triggers bmo bugs 431902 and 476393. So here we make sure that a
6850 // ToolbarWindow's contentView always returns NO from the
6851 // mouseDownCanMoveWindow method.
6852 - (BOOL)nsChildView_NSView_mouseDownCanMoveWindow
6854 NSWindow *ourWindow = [self window];
6855 NSView *contentView = [ourWindow contentView];
6856 if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView))
6858 return [self nsChildView_NSView_mouseDownCanMoveWindow];