CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / cocoa / nsChildView.mm
blob738e4375075e4614e4dc373b4b274c18a8e77704
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
4  *
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/
9  *
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
13  * License.
14  *
15  * The Original Code is mozilla.org code.
16  *
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.
21  *
22  * Contributor(s):
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>
29  *
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.
41  *
42  * ***** END LICENSE BLOCK ***** */
44 #ifdef MOZ_LOGGING
45 #define FORCE_PR_LOG
46 #endif
47 #include "prlog.h"
49 #include <unistd.h>
51 #include "nsChildView.h"
52 #include "nsCocoaWindow.h"
54 #include "nsObjCExceptions.h"
55 #include "nsCOMPtr.h"
56 #include "nsToolkit.h"
57 #include "nsCRT.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"
83 #ifdef __LP64__
84 #include "ComplexTextInputPanel.h"
85 #endif
87 #include "gfxContext.h"
88 #include "gfxQuartzSurface.h"
89 #include "nsRegion.h"
90 #include "Layers.h"
91 #include "LayerManagerOGL.h"
93 #include <dlfcn.h>
95 #include <ApplicationServices/ApplicationServices.h>
97 using namespace mozilla::layers;
98 #undef DEBUG_IME
99 #undef DEBUG_UPDATE
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
106 #ifdef PR_LOGGING
107 PRLogModuleInfo* sCocoaLog = nsnull;
108 #endif
110 extern "C" {
111   CG_EXTERN void CGContextResetCTM(CGContextRef);
112   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
113   CG_EXTERN void CGContextResetClip(CGContextRef);
115   // CGSPrivate.h
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);
140 #endif
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;
186 #ifndef NP_NO_CARBON
187 - (void)setPluginTSMInComposition:(BOOL)inComposition;
188 #endif
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;
208 #endif
210 #ifdef ACCESSIBILITY
211 - (id<mozAccessible>)accessible;
212 #endif
214 - (BOOL)isFirstResponder;
216 - (BOOL)isDragInProgress;
218 - (void)fireKeyEventForFlagsChanged:(NSEvent*)theEvent keyDown:(BOOL)isKeyDown;
220 - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
222 @end
224 #pragma mark -
226 // Key code constants
227 enum
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,
241   // function keys
242   kF1KeyCode          = 0x7A,
243   kF2KeyCode          = 0x78,
244   kF3KeyCode          = 0x63,
245   kF4KeyCode          = 0x76,
246   kF5KeyCode          = 0x60,
247   kF6KeyCode          = 0x61,
248   kF7KeyCode          = 0x62,
249   kF8KeyCode          = 0x64,
250   kF9KeyCode          = 0x65,
251   kF10KeyCode         = 0x6D,
252   kF11KeyCode         = 0x67,
253   kF12KeyCode         = 0x6F,
254   kF13KeyCode         = 0x69,
255   kF14KeyCode         = 0x6B,
256   kF15KeyCode         = 0x71,
257   
258   kPrintScreenKeyCode = kF13KeyCode,
259   kScrollLockKeyCode  = kF14KeyCode,
260   kPauseKeyCode       = kF15KeyCode,
261   
262   // keypad
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
283   
284   kInsertKeyCode          = 0x72, // also help key
285   kDeleteKeyCode          = 0x75, // also forward delete key
286   kTabKeyCode             = 0x30,
287   kTildeKeyCode           = 0x32,
288   kBackspaceKeyCode       = 0x33,
289   kHomeKeyCode            = 0x73, 
290   kEndKeyCode             = 0x77,
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.
308  */
310 static inline void
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;
319 static inline void
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;
328 static inline void 
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).
339 static inline void
340 FlipCocoaScreenCoordinate(NSPoint &inPoint)
341 {  
342   inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
345 static void
346 InitNPCocoaEvent(NPCocoaEvent* event)
348   memset(event, 0, sizeof(NPCocoaEvent));
351 static PRUint32
352 UnderlineAttributeToTextRangeType(PRUint32 aUnderlineStyle, NSRange selRange)
354 #ifdef DEBUG_IME
355   NSLog(@"****in underlineAttributeToTextRangeType = %d", aUnderlineStyle);
356 #endif
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
364   //
365   // It probably means show 1-pixel thickness underline vs 2-pixel thickness.
366   
367   PRUint32 attr;
368   if (selRange.length == 0) {
369     switch (aUnderlineStyle) {
370       case 1:
371         attr = NS_TEXTRANGE_RAWINPUT;
372         break;
373       case 2:
374       default:
375         attr = NS_TEXTRANGE_SELECTEDRAWTEXT;
376         break;
377     }
378   }
379   else {
380     switch (aUnderlineStyle) {
381       case 1:
382         attr = NS_TEXTRANGE_CONVERTEDTEXT;
383         break;
384       case 2:
385       default:
386         attr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
387         break;
388     }
389   }
390   return attr;
393 static PRUint32
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.
400   PRUint32 count = 0;
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
407                inRange:limitRange];
408     limitRange = NSMakeRange(NSMaxRange(effectiveRange), 
409                              NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
410     count++;
411   }
412   return count;
414   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
417 static void
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.
425   PRUint32 i = 0;
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
432                               inRange:limitRange];
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));
438     i++;
439   }
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;
448 static void
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);
461   }
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)
476 #pragma mark -
478 nsChildView::nsChildView() : nsBaseWidget()
479 , mView(nsnull)
480 , mParentView(nsnull)
481 , mParentWidget(nsnull)
482 , mVisible(PR_FALSE)
483 , mDrawing(PR_FALSE)
484 , mPluginDrawing(PR_FALSE)
485 , mIsDispatchPaint(PR_FALSE)
486 , mPluginInstanceOwner(nsnull)
488 #ifdef PR_LOGGING
489   if (!sCocoaLog) {
490     sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
491 #ifdef DEBUG
492     DebugPrintAllKeyboardLayouts();
493 #endif // DEBUG
494   }
495 #endif // PR_LOGGING
497   memset(&mPluginCGContext, 0, sizeof(mPluginCGContext));
498 #ifndef NP_NO_QUICKDRAW
499   memset(&mPluginQDPort, 0, sizeof(mPluginQDPort));
500 #endif
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();
515   }
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.
527   mParentWidget = 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
546   // (see bug 559075).
547   nsAutoreleasePool localPool;
549   // See NSView (MethodSwizzling) below.
550   if (!gChildViewMethodsSwizzled) {
551     nsToolkit::SwizzleMethods([NSView class], @selector(mouseDownCanMoveWindow),
552                               @selector(nsChildView_NSView_mouseDownCanMoveWindow));
553 #ifndef NP_NO_CARBON
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));
561 #endif
562     gChildViewMethodsSwizzled = PR_TRUE;
563   }
565   mBounds = aRect;
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
572   mParentView = nil;
573   if (aParent) {
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
580     // with windows).
581     mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET); 
582     mParentWidget = aParent;   
583   } else {
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);
588   }
589   
590   // create our parallel NSView and hook it up to our parent. Recall
591   // that NS_NATIVE_WIDGET is the NSView.
592   NSRect r;
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.
602   if (mParentWidget)
603     [mView setHidden:YES];
604   else
605     mVisible = PR_TRUE;
607   // Hook it up in the NSView hierarchy.
608   if (mParentView) {
609     [mParentView addSubview:mView];
610   }
612   // if this is a ChildView, make sure that our per-window data
613   // is set up
614   if ([mView isKindOfClass:[ChildView class]])
615     [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]];
617   mTextInputHandler.Init(this);
619   return NS_OK;
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.
626 NSView*
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;
640   if (!mView)
641     return;
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]];
651   }
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
662   // 374260).
663   if ([mView isEqual:[win contentView]]) {
664     [mView release];
665   } else {
666     // Stop NSView hierarchy being changed during [ChildView drawRect:]
667     [mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
668   }
669   mView = nil;
671   NS_OBJC_END_TRY_ABORT_BLOCK;
674 nsCocoaWindow*
675 nsChildView::GetXULWindowWidget()
677   id windowDelegate = [[mView window] delegate];
678   if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
679     return [(WindowDelegate *)windowDelegate geckoWidget];
680   }
681   return nsnull;
684 NS_IMETHODIMP nsChildView::Destroy()
686   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
688   if (mOnDestroyCalled)
689     return NS_OK;
690   mOnDestroyCalled = PR_TRUE;
692   [mView widgetDestroyed];
694   nsBaseWidget::Destroy();
696   ReportDestroyEvent(); 
697   mParentWidget = nil;
699   TearDownView();
701   nsBaseWidget::OnDestroy();
703   return NS_OK;
705   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
708 #pragma mark -
710 #if 0
711 static void PrintViewHierarchy(NSView *view)
713   while (view) {
714     NSLog(@"  view is %x, frame %@", view, NSStringFromRect([view frame]));
715     view = [view superview];
716   }
718 #endif
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;
727   switch (aDataType) 
728   {
729     case NS_NATIVE_WIDGET:
730     case NS_NATIVE_DISPLAY:
731       retVal = (void*)mView;
732       break;
734     case NS_NATIVE_WINDOW:
735       retVal = [mView window];
736       break;
738     case NS_NATIVE_GRAPHIC:
739       NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
740       retVal = nsnull;
741       break;
743     case NS_NATIVE_OFFSETX:
744       retVal = 0;
745       break;
747     case NS_NATIVE_OFFSETY:
748       retVal = 0;
749       break;
751     case NS_NATIVE_PLUGIN_PORT:
752     case NS_NATIVE_PLUGIN_PORT_QD:
753     case NS_NATIVE_PLUGIN_PORT_CG:
754     {
755       // The NP_CGContext pointer should always be NULL in the Cocoa event model.
756       if ([(ChildView*)mView pluginEventModel] == NPEventModelCocoa)
757         return nsnull;
759       UpdatePluginPort();
760 #ifndef NP_NO_QUICKDRAW
761       if (aDataType != NS_NATIVE_PLUGIN_PORT_CG) {
762         retVal = (void*)&mPluginQDPort;
763         break;
764       }
765 #endif
766       retVal = (void*)&mPluginCGContext;
767       break;
768     }
769   }
771   return retVal;
773   NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
776 #pragma mark -
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();
795   if (windowWidget) {
796     windowWidget->SetTransparencyMode(aMode);
797   }
799   NS_OBJC_END_TRY_ABORT_BLOCK;
802 NS_IMETHODIMP nsChildView::IsVisible(PRBool& outState)
804   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
806   if (!mVisible) {
807     outState = mVisible;
808   }
809   else {
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])) {
815       outState = PR_FALSE;
816     }
817   }
819   return NS_OK;
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) {
832     NPWindow* window;
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);
842     }
843   }
844 #endif
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;
855 #endif
857   if (!mView
858 #ifndef NP_NO_QUICKDRAW
859     || [(ChildView*)mView pluginDrawingModel] != NPDrawingModelQuickDraw
860 #endif
861     ) {
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;
871 #ifndef NP_NO_CARBON
872     if (carbonWindow) {
873       mPluginCGContext.context = (CGContextRef)[[cocoaWindow graphicsContext] graphicsPort];
874       mPluginCGContext.window = carbonWindow;
875     }
876 #endif
877   }
878 #ifndef NP_NO_QUICKDRAW
879   else {
880     if (carbonWindow) {
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;
891     } else {
892       mPluginQDPort.port = NULL;
893     }
894   }
895 #endif
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]])
906       continue;
908     ChildView* childview = static_cast<ChildView*>(view);
909     if ([childview isPluginView]) {
910       nsChildView* widget = static_cast<nsChildView*>([childview widget]);
911       if (widget) {
912         widget->HidePlugin();
913       }
914     } else {
915       HideChildPluginViews(view);
916     }
917   }
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
928     // no pool in place.
929     nsAutoreleasePool localPool;
931     [mView setHidden:!aState];
932     mVisible = aState;
933     if (!mVisible && IsPluginView())
934       HidePlugin();
935   }
936   return NS_OK;
938   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
941 // Change the parent of this widget
942 NS_IMETHODIMP
943 nsChildView::SetParent(nsIWidget* aNewParent)
945   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
947   NS_ENSURE_ARG(aNewParent);
949   if (mOnDestroyCalled)
950     return NS_OK;
952   // make sure we stay alive
953   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
954   
955   // remove us from our existing parent
956   if (mParentWidget)
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);
965   return NS_OK;
966   
967   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
970 NS_IMETHODIMP
971 nsChildView::ReparentNativeWidget(nsIWidget* aNewParent)
973   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
975   NS_PRECONDITION(aNewParent, "");
977   if (mOnDestroyCalled)
978     return NS_OK;
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];
988   return NS_OK;
990   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
993 void nsChildView::ResetParent()
995   if (!mOnDestroyCalled) {
996     if (mParentWidget)
997       mParentWidget->RemoveChild(this);
998     if (mView)
999       [mView removeFromSuperview];
1000   }
1001   mParentWidget = nsnull;
1004 nsIWidget*
1005 nsChildView::GetParent()
1007   return mParentWidget;
1010 float
1011 nsChildView::GetDPI()
1013   NSWindow* window = [mView window];
1014   if (window && [window isKindOfClass:[BaseWindow class]]) {
1015     return [(BaseWindow*)window getDPI];
1016   }
1018   return 96.0;
1021 NS_IMETHODIMP nsChildView::Enable(PRBool aState)
1023   return NS_OK;
1026 NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
1028   // unimplemented
1029   if (aState)
1030    *aState = PR_TRUE;
1031   return NS_OK;
1034 NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
1036   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1038   NSWindow* window = [mView window];
1039   if (window)
1040     [window makeFirstResponder:mView];
1041   return NS_OK;
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;
1072 #pragma mark -
1074 // Get this component dimension
1075 NS_IMETHODIMP nsChildView::GetBounds(nsIntRect &aRect)
1077   if (!mView) {
1078     aRect = mBounds;
1079   } else {
1080     NSRect frame = [mView frame];
1081     NSRectToGeckoRect(frame, aRect);
1082   }
1083   return NS_OK;
1086 NS_IMETHODIMP nsChildView::ConstrainPosition(PRBool aAllowSlop,
1087                                              PRInt32 *aX, PRInt32 *aY)
1089   return NS_OK;
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))
1098     return NS_OK;
1100   mBounds.x = aX;
1101   mBounds.y = aY;
1103   NSRect r;
1104   GeckoRectToNSRect(mBounds, r);
1105   [mView setFrame:r];
1107   if (mVisible)
1108     [mView setNeedsDisplay:YES];
1110   ReportMoveEvent();
1112   return NS_OK;
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))
1122     return NS_OK;
1124   mBounds.width  = aWidth;
1125   mBounds.height = aHeight;
1127   NSRect r;
1128   GeckoRectToNSRect(mBounds, r);
1129   [mView setFrame:r];
1131   if (mVisible && aRepaint)
1132     [mView setNeedsDisplay:YES];
1134   ReportSizeEvent();
1136   return NS_OK;
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))
1148     return NS_OK;
1150   if (isMoving) {
1151     mBounds.x = aX;
1152     mBounds.y = aY;
1153   }
1154   if (isResizing) {
1155     mBounds.width  = aWidth;
1156     mBounds.height = aHeight;
1157   }
1159   NSRect r;
1160   GeckoRectToNSRect(mBounds, r);
1161   [mView setFrame:r];
1163   if (mVisible && aRepaint)
1164     [mView setNeedsDisplay:YES];
1166   if (isMoving) {
1167     ReportMoveEvent();
1168     if (mOnDestroyCalled)
1169       return NS_OK;
1170   }
1171   if (isResizing)
1172     ReportSizeEvent();
1174   return NS_OK;
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))
1189     return PR_FALSE;
1191   if (aResizerRect) {
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);
1198   }
1199   return PR_TRUE;
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;
1222   
1223   NSWindow* window = [mView window];
1224   if (!window) return NS_ERROR_FAILURE;
1225   
1226   NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
1227   NSRect frame = [[window contentView] frame];
1228   viewOrigin.y = frame.size.height - viewOrigin.y;
1229   
1230   // set up the clipping region for plugins.
1231   NSRect visibleBounds = [mView visibleRect];
1232   NSPoint clipOrigin   = [mView convertPoint:visibleBounds.origin toView:nil];
1233   
1234   // Convert from cocoa to QuickDraw coordinates
1235   clipOrigin.y = frame.size.height - clipOrigin.y;
1236   
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);
1245   PRBool isVisible;
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);
1251     if (mClipRects) {
1252       nsIntRect clipBounds;
1253       for (PRInt32 i = 0; i < mClipRectCount; ++i) {
1254         clipBounds.UnionRect(clipBounds, mClipRects[i]);
1255       }
1256       outClipRect.IntersectRect(outClipRect, clipBounds - outOrigin);
1257     }
1259     // XXXroc should this be !outClipRect.IsEmpty()?
1260     outWidgetVisible = PR_TRUE;
1261   }
1262   else {
1263     outClipRect.width = 0;
1264     outClipRect.height = 0;
1265     outWidgetVisible = PR_FALSE;
1266   }
1268   return NS_OK;
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;
1279   } else {
1280     ::GetGlobalMouse(&event->where);
1281   }
1282   event->when = ::TickCount();
1283   event->modifiers = ::GetCurrentKeyModifiers();
1285 #endif
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);
1303   EndDrawPlugin();
1304 #endif
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.
1318 #ifndef __LP64__
1319   NSWindow* window = [mView window];
1320   if (!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) {
1333     if (mPluginDrawing)
1334       return NS_ERROR_FAILURE;
1335   }
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]));
1347   if (isQDPlugin) {
1348     port = mPluginQDPort.port;
1349   }
1351   RgnHandle pluginRegion = ::NewRgn();
1352   if (pluginRegion) {
1353     PRBool portChanged = (port != CGrafPtr(GetQDGlobalsThePort()));
1354     CGrafPtr oldPort;
1355     GDHandle oldDevice;
1357     if (portChanged) {
1358       ::GetGWorld(&oldPort, &oldDevice);
1359       ::SetGWorld(port, ::IsPortOffscreen(port) ? nsnull : ::GetMainDevice());
1360     }
1362     ::SetOrigin(0, 0);
1363     
1364     nsIntRect clipRect; // this is in native window coordinates
1365     nsIntPoint origin;
1366     PRBool visible;
1367     GetPluginClipRect(clipRect, origin, visible);
1368     
1369     // XXX if we're not visible, set an empty clip region?
1370     Rect pluginRect;
1371     ConvertGeckoRectToMacRect(clipRect, pluginRect);
1372     
1373     ::RectRgn(pluginRegion, &pluginRect);
1374     ::SetPortVisibleRegion(port, pluginRegion);
1375     ::SetPortClipRegion(port, pluginRegion);
1376     
1377     // now set up the origin for the plugin
1378     ::SetOrigin(origin.x, origin.y);
1379     
1380     ::DisposeRgn(pluginRegion);
1382     if (portChanged)
1383       ::SetGWorld(oldPort, oldDevice);
1384   }
1385 #endif
1387   mPluginDrawing = PR_TRUE;
1388   return NS_OK;
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;
1400   return NS_OK;
1403 NS_IMETHODIMP nsChildView::SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner)
1405   mPluginInstanceOwner = aInstanceOwner;
1407   return NS_OK;
1410 NS_IMETHODIMP nsChildView::SetPluginEventModel(int inEventModel)
1412   [(ChildView*)mView setPluginEventModel:(NPEventModel)inEventModel];
1413   return NS_OK;
1416 NS_IMETHODIMP nsChildView::GetPluginEventModel(int* outEventModel)
1418   *outEventModel = [(ChildView*)mView pluginEventModel];
1419   return NS_OK;
1422 NS_IMETHODIMP nsChildView::SetPluginDrawingModel(int inDrawingModel)
1424   [(ChildView*)mView setPluginDrawingModel:(NPDrawingModel)inDrawingModel];
1425   return NS_OK;
1428 NS_IMETHODIMP nsChildView::StartComplexTextInputForCurrentEvent()
1430   [(ChildView*)mView pluginRequestsComplexTextInputForCurrentEvent];
1431   return NS_OK;
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;
1468   
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];
1473     }
1474   }
1475   int windowNumber = [[mView window] windowNumber];
1476   BOOL sendFlagsChangedEvent = NO;
1477   switch (aNativeKeyCode) {
1478     case kCapsLockKeyCode:
1479     case kRCommandKeyCode:
1480     case kCommandKeyCode:
1481     case kShiftKeyCode:
1482     case kOptionkeyCode:
1483     case kControlKeyCode:
1484     case kRShiftKeyCode:
1485     case kROptionKeyCode:
1486     case kRControlKeyCode:
1487       sendFlagsChangedEvent = YES;
1488   }
1489   NSEventType eventType = sendFlagsChangedEvent ? NSFlagsChanged : NSKeyDown;
1490   NSEvent* downEvent = [NSEvent keyEventWithType:eventType
1491                                         location:NSMakePoint(0,0)
1492                                    modifierFlags:modifierFlags
1493                                        timestamp:0
1494                                     windowNumber:windowNumber
1495                                          context:[NSGraphicsContext currentContext]
1496                                       characters:ToNSString(aCharacters)
1497                      charactersIgnoringModifiers:ToNSString(aUnmodifiedCharacters)
1498                                        isARepeat:NO
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];
1510     if (upEvent)
1511       [NSApp sendEvent:upEvent];
1512     // processKeyDownEvent and keyUp block exceptions so we're sure to
1513     // reach here to restore gOverrideKeyboardLayout
1514     gOverrideKeyboardLayout = currentLayout;
1515   }
1517   return NS_OK;
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]
1542                                        context:nil
1543                                    eventNumber:0
1544                                     clickCount:1
1545                                       pressure:0.0];
1547   if (!event)
1548     return NS_ERROR_FAILURE;
1550   [NSApp sendEvent:event];
1551   return NS_OK;
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)
1563     return nil;
1565   NSMenu* currentSubmenu = [NSApp mainMenu];
1566   for (unsigned int i = 0; i < indexCount; i++) {
1567     int targetIndex;
1568     // We remove the application menu from consideration for the top-level menu
1569     if (i == 0)
1570       targetIndex = [[indexes objectAtIndex:i] intValue] + 1;
1571     else
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))
1578         return menuItem;
1579       // if this is not the last index find the submenu and keep going
1580       if ([menuItem hasSubmenu])
1581         currentSubmenu = [menuItem submenu];
1582       else
1583         return nil;
1584     }
1585   }
1587   return nil;
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];
1601     if (parent) {
1602       // NSLog(@"Performing action for native menu item titled: %@\n",
1603       //       [[currentSubmenu itemAtIndex:targetIndex] title]);
1604       [parent performActionForItemAtIndex:[parent indexOfItem:item]];
1605       return NS_OK;
1606     }
1607   }
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();
1619   if (widget) {
1620     nsMenuBarX* mb = widget->GetMenuBar();
1621     if (mb) {
1622       if (indexString.IsEmpty())
1623         mb->ForceNativeMenuReload();
1624       else
1625         mb->ForceUpdateNativeMenuAt(indexString);
1626     }
1627   }
1628   return NS_OK;
1630   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1633 #pragma mark -
1635 #ifdef INVALIDATE_DEBUGGING
1637 static Boolean KeyDown(const UInt8 theKey)
1639   KeyMap map;
1640   GetKeys(map);
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)
1653     ::GetClip(oldClip);
1655   ::ClipRect(r);
1656   ::InvertRect(r);
1657   UInt32 end = ::TickCount() + 5;
1658   while (::TickCount() < end) ;
1659   ::InvertRect(r);
1661   if (oldClip != NULL)
1662     ::SetClip(oldClip);
1665 static void blinkRgn(RgnHandle rgn)
1667   StRegionFromPool oldClip;
1668   if (oldClip != NULL)
1669     ::GetClip(oldClip);
1671   ::SetClip(rgn);
1672   ::InvertRgn(rgn);
1673   UInt32 end = ::TickCount() + 5;
1674   while (::TickCount() < end) ;
1675   ::InvertRgn(rgn);
1677   if (oldClip != NULL)
1678     ::SetClip(oldClip);
1681 #endif
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)
1689     return NS_OK;
1691   NSRect r;
1692   GeckoRectToNSRect(aRect, r);
1693   
1694   if (aIsSynchronous) {
1695     [mView displayRect:r];
1696   }
1697   else if ([NSView focusView]) {
1698     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
1699     // don't lose it.
1700     [mView setNeedsPendingDisplayInRect:r];
1701   }
1702   else {
1703     [mView setNeedsDisplayInRect:r];
1704   }
1706   return NS_OK;
1708   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1711 PRBool
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]])
1717     return PR_FALSE;
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()
1734   return NS_OK;
1737 #pragma mark -
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);
1744 #ifdef DEBUG
1745     nsWindowType kidType;
1746     child->GetWindowType(kidType);
1747 #endif
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;
1761 #endif
1762     child->Resize(
1763         config.mBounds.x, config.mBounds.y,
1764         config.mBounds.width, config.mBounds.height,
1765         repaint);
1767     // Store the clip region here in case GetPluginClipRect needs it.
1768     child->StoreWindowClipRegion(config.mClipRegion);
1769   }
1770   return NS_OK;
1773 // Invokes callback and ProcessEvent methods on Event Listener object
1774 NS_IMETHODIMP nsChildView::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
1776 #ifdef DEBUG
1777   debug_DumpEvent(stdout, event->widget, event, nsCAutoString("something"), 0);
1778 #endif
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) {
1787     nsWindowType type;
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;
1792       if (event->widget)
1793         event->widget->GetClientData(clientData);
1794       if (!clientData)
1795         event->widget = mParentWidget;
1796     }
1797   }
1799   PRBool restoreIsDispatchPaint = mIsDispatchPaint;
1800   mIsDispatchPaint = mIsDispatchPaint || event->eventStructType == NS_PAINT_EVENT;
1802   if (mEventCallback)
1803     aStatus = (*mEventCallback)(event);
1805   mIsDispatchPaint = restoreIsDispatchPaint;
1807   return NS_OK;
1810 PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event)
1812   nsEventStatus status;
1813   DispatchEvent(&event, status);
1814   return ConvertStatus(status);
1817 #pragma mark -
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);
1845 #pragma mark -
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;
1853   NSPoint temp;
1854   temp.x = 0;
1855   temp.y = 0;
1856   
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];  
1860   
1861   // 2. We turn the window-coord rect's origin into screen (still bottom-left) coords.
1862   temp = [[mView window] convertBaseToScreen:temp];
1863   
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);
1867   
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,
1875                                                PRBool aDoCapture, 
1876                                                PRBool aConsumeRollupEvent)
1878   // this never gets called, only top-level windows can be rollup widgets
1879   return NS_OK;
1882 NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
1884   // child views don't have titles
1885   return NS_OK;
1888 NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
1890   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1892   [NSApp requestUserAttention:NSInformationalRequest];
1893   return NS_OK;
1895   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1898 /* static */
1899 PRBool nsChildView::DoHasPendingInputEvent()
1901   return sLastInputEventCount != GetCurrentInputEventCount(); 
1904 /* static */
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,
1915     kCGEventMouseMoved,
1916     kCGEventLeftMouseDragged,
1917     kCGEventRightMouseDragged,
1918     kCGEventKeyDown,
1919     kCGEventKeyUp,
1920     kCGEventScrollWheel,
1921     kCGEventTabletPointer,
1922     kCGEventOtherMouseDown,
1923     kCGEventOtherMouseUp,
1924     kCGEventOtherMouseDragged
1925   };
1927   PRUint32 eventCount = 0;
1928   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(eventTypes); ++i) {
1929     eventCount +=
1930       CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState,
1931                                        eventTypes[i]);
1932   }
1933   return eventCount;
1936 /* static */
1937 void nsChildView::UpdateCurrentInputEventCount()
1939   sLastInputEventCount = GetCurrentInputEventCount();
1942 PRBool nsChildView::HasPendingInputEvent()
1944   return DoHasPendingInputEvent();
1947 #pragma mark -
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()
1954 #ifdef DEBUG_IME
1955   NSLog(@"**** ResetInputState");
1956 #endif
1958   mTextInputHandler.CommitIMEComposition();
1959   return NS_OK;
1962 // 'open' means that it can take non-ASCII chars
1963 NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
1965 #ifdef DEBUG_IME
1966   NSLog(@"**** SetIMEOpenState aState = %d", aState);
1967 #endif
1969   mTextInputHandler.SetIMEOpenState(aState);
1970   return NS_OK;
1973 // 'open' means that it can take non-ASCII chars
1974 NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
1976 #ifdef DEBUG_IME
1977   NSLog(@"**** GetIMEOpenState");
1978 #endif
1980   *aState = mTextInputHandler.IsIMEOpened();
1981   return NS_OK;
1984 NS_IMETHODIMP nsChildView::SetInputMode(const IMEContext& aContext)
1986 #ifdef DEBUG_IME
1987   NSLog(@"**** SetInputMode mStatus = %d", aContext.mStatus);
1988 #endif
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);
1996       break;
1997     case nsIWidget::IME_STATUS_DISABLED:
1998       mTextInputHandler.SetASCIICapableOnly(PR_FALSE);
1999       mTextInputHandler.EnableIME(PR_FALSE);
2000       break;
2001     case nsIWidget::IME_STATUS_PASSWORD:
2002       mTextInputHandler.SetASCIICapableOnly(PR_TRUE);
2003       mTextInputHandler.EnableIME(PR_FALSE);
2004       break;
2005     default:
2006       NS_ERROR("not implemented!");
2007   }
2008   return NS_OK;
2011 NS_IMETHODIMP nsChildView::GetInputMode(IMEContext& aContext)
2013 #ifdef DEBUG_IME
2014   NSLog(@"**** GetInputMode");
2015 #endif
2017   aContext = mIMEContext;
2018   return NS_OK;
2021 // Destruct and don't commit the IME composition string.
2022 NS_IMETHODIMP nsChildView::CancelIMEComposition()
2024 #ifdef DEBUG_IME
2025   NSLog(@"**** CancelIMEComposition");
2026 #endif
2028   mTextInputHandler.CancelIMEComposition();
2029   return NS_OK;
2032 NS_IMETHODIMP nsChildView::GetToggledKeyState(PRUint32 aKeyCode,
2033                                               PRBool* aLEDState)
2035   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2037 #ifdef DEBUG_IME
2038   NSLog(@"**** GetToggledKeyState");
2039 #endif
2040   NS_ENSURE_ARG_POINTER(aLEDState);
2041   PRUint32 key;
2042   switch (aKeyCode) {
2043     case NS_VK_CAPS_LOCK:
2044       key = alphaLock;
2045       break;
2046     case NS_VK_NUM_LOCK:
2047       key = kEventKeyModifierNumLockMask;
2048       break;
2049     // Mac doesn't support SCROLL_LOCK state.
2050     default:
2051       return NS_ERROR_NOT_IMPLEMENTED;
2052   }
2053   PRUint32 modifierFlags = ::GetCurrentKeyModifiers();
2054   *aLEDState = (modifierFlags & key) != 0;
2055   return NS_OK;
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));
2079     if (view)
2080       editorView = view;
2081   }
2082   return editorView;
2085 #pragma mark -
2087 gfxASurface*
2088 nsChildView::GetThebesSurface()
2090   if (!mTempThebesSurface) {
2091     mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxASurface::ImageFormatARGB32);
2092   }
2094   return mTempThebesSurface;
2097 void
2098 nsChildView::DrawOver(LayerManager* aManager, nsIntRect aRect)
2100   nsCocoaWindow *cocoaWindow = GetXULWindowWidget();
2101   if (cocoaWindow) {
2102     cocoaWindow->DrawOver(aManager, aRect);
2103   }
2106 void
2107 nsChildView::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries)
2109   NSWindow* win = [mView window];
2110   if (!win || ![win isKindOfClass:[ToolbarWindow class]])
2111     return;
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();
2122     }
2123   }
2124   [(ToolbarWindow*)win setUnifiedToolbarHeight:unifiedToolbarHeight];
2127 NS_IMETHODIMP
2128 nsChildView::BeginSecureKeyboardInput()
2130   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2132   nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
2133   if (NS_SUCCEEDED(rv)) {
2134     ::EnableSecureEventInput();
2135   }
2136   return rv;
2138   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2141 NS_IMETHODIMP
2142 nsChildView::EndSecureKeyboardInput()
2144   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2146   nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
2147   if (NS_SUCCEEDED(rv)) {
2148     ::DisableSecureEventInput();
2149   }
2150   return rv;
2152   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2155 #ifdef ACCESSIBILITY
2156 already_AddRefed<nsAccessible>
2157 nsChildView::GetDocumentAccessible()
2159   nsAccessible *docAccessible = nsnull;
2160   if (mAccessible) {
2161     CallQueryReferent(mAccessible.get(), &docAccessible);
2162     return docAccessible;
2163   }
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
2171   mAccessible =
2172     do_GetWeakReference(static_cast<nsIAccessible*>(event.mAccessible));
2174   NS_IF_ADDREF(event.mAccessible);
2175   return event.mAccessible;
2177 #endif
2179 #pragma mark -
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;
2195 + (void)initialize
2197   static BOOL initialized = NO;
2199   if (!initialized) {
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];
2205     
2206     [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes];
2208     [sendTypes release];
2209     [returnTypes release];
2211     initialized = YES;
2212   }
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;
2222     mIsPluginView = NO;
2223 #ifndef NP_NO_CARBON
2224     mPluginEventModel = NPEventModelCarbon;
2225 #else
2226     mPluginEventModel = NPEventModelCocoa;
2227 #endif
2228 #ifndef NP_NO_QUICKDRAW
2229     mPluginDrawingModel = NPDrawingModelQuickDraw;
2230 #else
2231     mPluginDrawingModel = NPDrawingModelCoreGraphics;
2232 #endif
2233     mCurKeyEvent = nil;
2234     mKeyDownHandled = PR_FALSE;
2235     mKeyPressHandled = NO;
2236     mKeyPressSent = 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;
2251 #endif
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
2263     // progress.
2264     mDidForceRefreshOpenGL = NO;
2266     [self setFocusRingType:NSFocusRingTypeNone];
2267   }
2268   
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,
2272                                                           NSStringPboardType,
2273                                                           NSHTMLPboardType,
2274                                                           NSURLPboardType,
2275                                                           NSFilesPromisePboardType,
2276                                                           kWildcardPboardType,
2277                                                           kCorePboardType_url,
2278                                                           kCorePboardType_urld,
2279                                                           kCorePboardType_urln,
2280                                                           nil]];
2281   [[NSNotificationCenter defaultCenter] addObserver:self
2282                                            selector:@selector(windowBecameMain:)
2283                                                name:NSWindowDidBecomeMainNotification
2284                                              object:nil];
2285   [[NSNotificationCenter defaultCenter] addObserver:self
2286                                            selector:@selector(windowResignedMain:)
2287                                                name:NSWindowDidResignMainNotification
2288                                              object:nil];
2289   [[NSNotificationCenter defaultCenter] addObserver:self
2290                                            selector:@selector(systemMetricsChanged)
2291                                                name:NSControlTintDidChangeNotification
2292                                              object:nil];
2293   [[NSNotificationCenter defaultCenter] addObserver:self
2294                                            selector:@selector(systemMetricsChanged)
2295                                                name:NSSystemColorsDidChangeNotification
2296                                              object:nil];
2297   [[NSDistributedNotificationCenter defaultCenter] addObserver:self
2298                                                       selector:@selector(systemMetricsChanged)
2299                                                           name:@"AppleAquaScrollBarVariantChanged"
2300                                                         object:nil
2301                                             suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; 
2302   [[NSNotificationCenter defaultCenter] addObserver:self
2303                                            selector:@selector(_surfaceNeedsUpdate:)
2304                                                name:NSViewGlobalFrameDidChangeNotification
2305                                              object:self];
2307   return self;
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
2316 // again.
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;
2329 - (void)dealloc
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
2339   if (mPluginTSMDoc)
2340     ::DeleteTSMDocument(mPluginTSMDoc);
2341 #endif
2343   [[NSNotificationCenter defaultCenter] removeObserver:self];
2344   [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
2346   [super dealloc];    
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!
2351   ::SetPort(NULL);
2352 #endif
2354   NS_OBJC_END_TRY_ABORT_BLOCK;
2357 - (void)updatePluginTopLevelWindowStatus:(BOOL)hasMain
2359   if (!mGeckoChild)
2360     return;
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];
2378     }
2379   }
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];
2391     }
2392   }
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
2415   if (!mGeckoChild)
2416     return;
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;
2430   }
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;
2445   }
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];
2457   }
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]];
2462     }
2463   }
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]];
2482     }
2483   }
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).
2497 - (BOOL)isFlipped
2499   return YES;
2502 - (BOOL)isOpaque
2504   return [[self window] isOpaque] && !mIsPluginView;
2507 -(void)setIsPluginView:(BOOL)aIsPlugin
2509   mIsPluginView = aIsPlugin;
2512 -(BOOL)isPluginView
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
2521   if (!mGeckoChild)
2522     return NO;
2524   if (!mClickThroughMouseDownEvent ||
2525       [mClickThroughMouseDownEvent type] != NSLeftMouseDown)
2526     return NO;
2528   BOOL retval =
2529     !ChildViewMouseTracker::WindowAcceptsEvent([self window],
2530                                                mClickThroughMouseDownEvent,
2531                                                self, PR_TRUE);
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.
2537   if (retval) {
2538     [mClickThroughMouseDownEvent release];
2539     mClickThroughMouseDownEvent = nil;
2540   }
2542   return retval;
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;
2570 #endif
2572 - (void)sendFocusEvent:(PRUint32)eventType
2574   if (!mGeckoChild)
2575     return;
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
2587   return YES;
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];
2600   }
2601   return YES;
2604 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
2606   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2608   if (!newWindow)
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();
2621   }
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]];
2638     }
2639   }
2640   [super scrollRect:aRect by:offset];
2642   NS_OBJC_END_TRY_ABORT_BLOCK;
2645 - (BOOL)mouseDownCanMoveWindow
2647   return NO;
2650 - (void)lockFocus
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!
2659 #endif
2661   [super lockFocus];
2663   if (mGLContext) {
2664     if ([mGLContext view] != self) {
2665       [mGLContext setView:self];
2666     }
2668     [mGLContext makeCurrentContext];
2669   }
2671   NS_OBJC_END_TRY_ABORT_BLOCK;
2674 -(void)update
2676   if (mGLContext) {
2677     [mGLContext update];
2678   }
2681 - (void) _surfaceNeedsUpdate:(NSNotification*)notification
2683    [self update];
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];
2697   }
2700 - (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext
2702   if (!mGeckoChild)
2703     return;
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
2713   PRBool isVisible;
2714   if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) ||
2715       !isVisible)
2716     return;
2718 #ifndef NP_NO_QUICKDRAW
2719   if (mIsPluginView && mPluginDrawingModel == NPDrawingModelQuickDraw) {
2720     mGeckoChild->PaintQD();
2721     return;
2722   }
2723 #endif
2725   // Don't ever draw non-QuickDraw plugin views explicitly; they'll be drawn as
2726   // part of their parent widget.
2727   if (mIsPluginView)
2728     return;
2730 #ifdef DEBUG_UPDATE
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",
2735            self, mGeckoChild,
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);
2741 #endif
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;
2748   NSInteger count, i;
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));
2756     }
2757     paintEvent.region.And(paintEvent.region, boundingRect);
2758   } else {
2759     paintEvent.region = boundingRect;
2760   }
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])
2768       continue;
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));
2774     }
2775   }
2776 #endif
2778   if (mGeckoChild->GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_OPENGL) {
2779     LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(mGeckoChild->GetLayerManager(nsnull));
2780     manager->SetClippingRegion(paintEvent.region); 
2781     if (!mGLContext) {
2782       mGLContext = (NSOpenGLContext *)manager->gl()->GetNativeData(mozilla::gl::GLContext::NativeGLContext);
2783       [mGLContext retain];
2784     }
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;
2792     }
2794     return;
2795   }
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();
2808   for (;;) {
2809     const nsIntRect* r = iter.Next();
2810     if (!r)
2811       break;
2812     targetContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
2813   }
2814   targetContext->Clip();
2816   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2817   PRBool painted;
2818   {
2819     nsBaseWidget::AutoLayerManagerSetup
2820       setupLayerManager(mGeckoChild, targetContext, BasicLayerManager::BUFFER_NONE);
2821     painted = mGeckoChild->DispatchWindowEvent(paintEvent);
2822   }
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));
2830   }
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)
2836 #ifdef DEBUG_UPDATE
2837   fprintf (stderr, "---- update done ----\n");
2839 #if 0
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,
2844                             0.5);
2845 #endif 
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));
2850 #endif
2853 - (void)releaseWidgets:(NSArray*)aWidgetArray
2855   if (!aWidgetArray) {
2856     return;
2857   }
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];
2862     NS_RELEASE(widget);
2863   }
2866 - (void)viewWillDraw
2868   if (mGeckoChild) {
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();
2880     if (parent) {
2881       NSMutableArray* widgetArray = [NSMutableArray arrayWithCapacity:3];
2882       while (parent) {
2883         NS_ADDREF(parent);
2884         [widgetArray addObject:[NSNumber numberWithUnsignedInteger:(NSUInteger)parent]];
2885         parent = parent->GetParent();
2886       }
2887       NS_ADDREF(mGeckoChild);
2888       [widgetArray addObject:[NSNumber numberWithUnsignedInteger:(NSUInteger)mGeckoChild]];
2889       [self performSelector:@selector(releaseWidgets:)
2890                  withObject:widgetArray
2891                  afterDelay:0];
2892     }
2893     nsPaintEvent paintEvent(PR_TRUE, NS_WILL_PAINT, mGeckoChild);
2894     mGeckoChild->DispatchWindowEvent(paintEvent);
2895   }
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
2903   return NO;
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];
2936   }
2938   NS_OBJC_END_TRY_ABORT_BLOCK;
2940 #endif
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;
2951   if (!gRollupWidget)
2952     return;
2954   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
2955   if (prefs) {
2956     PRBool useNativeContextMenus;
2957     nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
2958     if (NS_SUCCEEDED(rv) && useNativeContextMenus)
2959       return;
2960   }
2962   NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
2963   if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
2964     return;
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
2992         consumeEvent = YES;
2993       }
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;
2999       if (gMenuRollup) {
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;
3013             }
3014             else {
3015               popupsToRollup = sameTypeCount;
3016             }
3017             break;
3018           }
3019         }
3020       }
3022       if (shouldRollup) {
3023         gRollupListener->Rollup(popupsToRollup, nsnull);
3024         consumeEvent = (BOOL)gConsumeRollupEvent;
3025       }
3026     }
3027   }
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
3043  */
3045 - (void)swipeWithEvent:(NSEvent *)anEvent
3047   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3049   if (!anEvent || !mGeckoChild)
3050     return;
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.
3062   if (deltaX > 0.0)
3063     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
3064   else if (deltaX < 0.0)
3065     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
3067   // Record the up/down direction.
3068   if (deltaY > 0.0)
3069     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_UP;
3070   else if (deltaY < 0.0)
3071     geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_DOWN;
3073   // Send the event.
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");
3083   if (!anEvent)
3084     return;
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)
3096     return;
3098   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3100   float deltaZ = [anEvent deltaZ];
3102   PRUint32 msg;
3103   switch (mGestureState) {
3104   case eGestureState_StartGesture:
3105     msg = NS_SIMPLE_GESTURE_MAGNIFY_START;
3106     mGestureState = eGestureState_MagnifyGesture;
3107     break;
3109   case eGestureState_MagnifyGesture:
3110     msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE;
3111     break;
3113   case eGestureState_None:
3114   case eGestureState_RotateGesture:
3115   default:
3116     return;
3117   }
3119   // Setup the event.
3120   nsSimpleGestureEvent geckoEvent(PR_TRUE, msg, mGeckoChild, 0, deltaZ);
3121   [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
3123   // Send the event.
3124   mGeckoChild->DispatchWindowEvent(geckoEvent);
3126   // Keep track of the cumulative magnification for the final "magnify" event.
3127   mCumulativeMagnification += deltaZ;
3128   
3129   NS_OBJC_END_TRY_ABORT_BLOCK;
3132 - (void)rotateWithEvent:(NSEvent *)anEvent
3134   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3136   if (!anEvent || !mGeckoChild)
3137     return;
3139   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3141   float rotation = [anEvent rotation];
3143   PRUint32 msg;
3144   switch (mGestureState) {
3145   case eGestureState_StartGesture:
3146     msg = NS_SIMPLE_GESTURE_ROTATE_START;
3147     mGestureState = eGestureState_RotateGesture;
3148     break;
3150   case eGestureState_RotateGesture:
3151     msg = NS_SIMPLE_GESTURE_ROTATE_UPDATE;
3152     break;
3154   case eGestureState_None:
3155   case eGestureState_MagnifyGesture:
3156   default:
3157     return;
3158   }
3160   // Setup the event.
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;
3166   } else {
3167     geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE;
3168   }
3170   // Send the event.
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;
3188     return;
3189   }
3191   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3193   switch (mGestureState) {
3194   case eGestureState_MagnifyGesture:
3195     {
3196       // Setup the "magnify" event.
3197       nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_MAGNIFY,
3198                                       mGeckoChild, 0, mCumulativeMagnification);
3199       [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
3201       // Send the event.
3202       mGeckoChild->DispatchWindowEvent(geckoEvent);
3203     }
3204     break;
3206   case eGestureState_RotateGesture:
3207     {
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;
3214       } else {
3215         geckoEvent.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE;
3216       }
3218       // Send the event.
3219       mGeckoChild->DispatchWindowEvent(geckoEvent);
3220     }
3221     break;
3223   case eGestureState_None:
3224   case eGestureState_StartGesture:
3225   default:
3226     break;
3227   }
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]])
3244     return NO;
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];
3257   }
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;
3264     return;
3265   }
3266   else {
3267     [mLastMouseDownEvent release];
3268     mLastMouseDownEvent = [theEvent retain];
3269   }
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;
3287     return;
3288   }
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];
3293 #endif
3295   // in order to send gecko events we'll need a gecko widget
3296   if (!mGeckoChild)
3297     return;
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
3307     // blocked.
3308     clickCount--;
3309   }
3310   geckoEvent.clickCount = clickCount;
3312   if (modifierFlags & NSControlKeyMask)
3313     geckoEvent.button = nsMouseEvent::eRightButton;
3314   else
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
3319   // event type.
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;
3329   }
3330 #endif
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;
3345   }
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)
3360     return;
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;
3368   else
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
3373   // event type.
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;
3384     }
3385 #endif
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;
3400     }
3401   }
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)
3412 #endif
3413     {
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);
3432       }
3433     }
3434   }
3436   NS_OBJC_END_TRY_ABORT_BLOCK;
3439 - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
3440                             enter:(BOOL)aEnter
3441                              type:(nsMouseEvent::exitType)aType
3443   if (!mGeckoChild)
3444     return;
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
3456   // event type.
3457 #ifndef NP_NO_CARBON  
3458   EventRecord carbonEvent;
3459 #endif
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;
3470     }
3471 #endif
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;
3483     }
3484   }
3486   event.exit = aType;
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;
3501   if (!mGeckoChild)
3502     return;
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
3509   // event type.
3510 #ifndef NP_NO_CARBON
3511   EventRecord carbonEvent;
3512 #endif
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;
3523     }
3524 #endif
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;
3538     }
3539   }
3540   mGeckoChild->DispatchWindowEvent(geckoEvent);
3542   NS_OBJC_END_TRY_ABORT_BLOCK;
3545 - (void)mouseDragged:(NSEvent*)theEvent
3547   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3549   if (!mGeckoChild)
3550     return;
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;
3568     }
3569 #endif
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;
3584     }
3585   }
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];
3605   if (!mGeckoChild)
3606     return;
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;    
3624   }
3625 #endif
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;
3640   }
3642   PRBool handled = mGeckoChild->DispatchWindowEvent(geckoEvent);
3643   if (!mGeckoChild)
3644     return;
3646   if (!handled)
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;
3656   if (!mGeckoChild)
3657     return;
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;
3675     }
3676 #endif
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;
3691     }
3692   }
3694   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3695   mGeckoChild->DispatchWindowEvent(geckoEvent);
3697   NS_OBJC_END_TRY_ABORT_BLOCK;
3700 - (void)rightMouseDragged:(NSEvent*)theEvent
3702   if (!mGeckoChild)
3703     return;
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
3710   // the widget.
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))
3722     return;
3724   if (!mGeckoChild)
3725     return;
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
3739   if (!mGeckoChild)
3740     return;
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
3751   if (!mGeckoChild)
3752     return;
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
3759   // the widget.
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;
3768   if (!mGeckoChild)
3769     return;
3771   float scrollDelta = 0;
3772   float scrollDeltaPixels = 0;
3773   PRBool checkPixels = PR_TRUE;
3775   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
3776   if (prefs)
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.
3784   if (checkPixels) {
3785     EventRef theCarbonEvent = [theEvent _eventRef];
3786     UInt32 carbonEventKind = theCarbonEvent ? ::GetEventKind(theCarbonEvent) : 0;
3787     if (carbonEventKind != kEventMouseScroll)
3788       checkPixels = PR_FALSE;
3789   }
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];
3798     }
3799   } else if (inAxis & nsMouseScrollEvent::kIsHorizontal) {
3800     scrollDelta       = -[theEvent deltaX];
3801     if (checkPixels && (scrollDelta == 0 || scrollDelta != floor(scrollDelta))) {
3802       scrollDeltaPixels = -[theEvent deviceDeltaX];
3803     }
3804   } else {
3805     return; // caller screwed up
3806   }
3808   BOOL hasPixels = (scrollDeltaPixels != 0);
3810   if (!hasPixels && scrollDelta == 0)
3811     // No sense in firing off a Gecko event.
3812      return;
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;
3823     if (hasPixels)
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
3831     // |delta| < 1.
3832     if (scrollDelta < 0)
3833       geckoEvent.delta = (PRInt32)floorf(scrollDelta);
3834     else
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];
3849       else
3850         cocoaEvent.data.mouse.deltaX = 0.0;
3851       if (inAxis & nsMouseScrollEvent::kIsVertical)
3852         cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
3853       else
3854         cocoaEvent.data.mouse.deltaY = 0.0;
3855       cocoaEvent.data.mouse.deltaZ = 0.0;
3856       geckoEvent.pluginEvent = &cocoaEvent;
3857     }
3859     nsAutoRetainCocoaObject kungFuDeathGrip(self);
3860     mGeckoChild->DispatchWindowEvent(geckoEvent);
3861     if (!mGeckoChild)
3862       return;
3864 #ifndef NP_NO_CARBON
3865     // dispatch scroll wheel carbon event for plugins
3866     if (mPluginEventModel == NPEventModelCarbon) {
3867       EventRef theEvent;
3868       OSStatus err = ::CreateEvent(NULL,
3869                                    kEventClassMouse,
3870                                    kEventMouseWheelMoved,
3871                                    TicksToEventTime(TickCount()),
3872                                    kEventAttributeUserEvent,
3873                                    &theEvent);
3874       if (err == noErr) {
3875         EventMouseWheelAxis axis;
3876         if (inAxis & nsMouseScrollEvent::kIsVertical)
3877           axis = kEventMouseWheelAxisY;
3878         else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
3879           axis = kEventMouseWheelAxisX;
3880         
3881         SetEventParameter(theEvent,
3882                           kEventParamMouseWheelAxis,
3883                           typeMouseWheelAxis,
3884                           sizeof(EventMouseWheelAxis),
3885                           &axis);
3886         
3887         SInt32 delta = (SInt32)-geckoEvent.delta;
3888         SetEventParameter(theEvent,
3889                           kEventParamMouseWheelDelta,
3890                           typeLongInteger,
3891                           sizeof(SInt32),
3892                           &delta);
3893         
3894         Point mouseLoc;
3895         ::GetGlobalMouse(&mouseLoc);
3896         SetEventParameter(theEvent,
3897                           kEventParamMouseLocation,
3898                           typeQDPoint,
3899                           sizeof(Point),
3900                           &mouseLoc);
3901         
3902         ::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
3903         ReleaseEvent(theEvent);
3904       }
3905     }
3906 #endif
3907   }
3909   if (hasPixels) {
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);
3919   }
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])
3931     return;
3933   if (!mGeckoChild)
3934     return;
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];
3941   if (!mGeckoChild)
3942     return;
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])
3953     return nil;
3955   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3957   [self maybeRollup:theEvent];
3958   if (!mGeckoChild)
3959     return nil;
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];
3967     if (!mGeckoChild)
3968       return nil;
3969   }
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);
3975   if (!mGeckoChild)
3976     return nil;
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];
3994   return nil;
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;
4007   OSStatus          err;
4008   
4009   *outChar = 0;
4010   
4011   err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
4012   if (err != noErr)
4013     return PR_FALSE;
4014   
4015   err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
4016   if (err != noErr)
4017     return PR_FALSE;
4018   
4019   err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
4020   if (err != noErr)
4021     return PR_FALSE;
4023   *outChar = convertedString[1];
4024   ::DisposeUnicodeToTextInfo(&converterInfo);
4025   return PR_TRUE;
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) {
4036     case NSKeyDown:
4037       pluginEvent.type = NPCocoaEventKeyDown;
4038       break;
4039     case NSKeyUp:
4040       pluginEvent.type = NPCocoaEventKeyUp;
4041       break;
4042     case NSFlagsChanged:
4043       pluginEvent.type = NPCocoaEventFlagsChanged;
4044       break;
4045     default:
4046       printf("Asked to convert key event of unknown type to Cocoa plugin event!");
4047   }
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];
4055   }
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;
4066     } else {
4067       if ([[cocoaEvent characters] length] > 0)
4068         charCode = [[cocoaEvent characters] characterAtIndex:0];
4069       if ([cocoaEvent type] == NSKeyDown)
4070         pluginEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
4071       else
4072         pluginEvent.what = keyUp;
4073     }
4075     if (charCode >= 0x0080) {
4076         switch (charCode) {
4077         case NSUpArrowFunctionKey:
4078             charCode = kUpArrowCharCode;
4079             break;
4080         case NSDownArrowFunctionKey:
4081             charCode = kDownArrowCharCode;
4082             break;
4083         case NSLeftArrowFunctionKey:
4084             charCode = kLeftArrowCharCode;
4085             break;
4086         case NSRightArrowFunctionKey:
4087             charCode = kRightArrowCharCode;
4088             break;
4089         default:
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]);
4094             break;
4095         }
4096     }
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
4114   if (aChar > 0x7E)
4115     return 0;
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);
4124   switch (aChar)
4125   {
4126     case kReturnCharCode:
4127     case kEnterCharCode:
4128     case '\n':
4129       return NS_VK_RETURN;
4130     case '{':
4131     case '[':
4132       return NS_VK_OPEN_BRACKET;
4133     case '}':
4134     case ']':
4135       return NS_VK_CLOSE_BRACKET;
4136     case '\'':
4137     case '"':
4138       return NS_VK_QUOTE;
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;
4152     default:
4153       if (!IsPrintableChar(aChar))
4154         NS_WARNING("GetGeckoKeyCodeFromChar has failed.");
4155       return 0;
4156     }
4159 static PRUint32 ConvertMacToGeckoKeyCode(UInt32 keyCode, nsKeyEvent* aKeyEvent, NSString* characters)
4161   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
4163   PRUint32 geckoKeyCode = 0;
4165   switch (keyCode)
4166   {
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;
4180     // function keys
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;
4200     // keypad
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;
4243     default:
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]);
4247   }
4249   return geckoKeyCode;
4251   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
4254 static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
4256   PRBool  isSpecial;
4257   
4258   // this table is used to determine which keys are special and should not generate a charCode
4259   switch (macKeyCode)
4260   {
4261     // modifiers - we don't get separate events for these yet
4262     case kEscapeKeyCode:
4263     case kShiftKeyCode:
4264     case kRShiftKeyCode:
4265     case kCommandKeyCode:
4266     case kRCommandKeyCode:
4267     case kCapsLockKeyCode:
4268     case kControlKeyCode:
4269     case kRControlKeyCode:
4270     case kOptionkeyCode:
4271     case kROptionKeyCode:
4272     case kClearKeyCode:
4273       
4274       // function keys
4275     case kF1KeyCode:
4276     case kF2KeyCode:
4277     case kF3KeyCode:
4278     case kF4KeyCode:
4279     case kF5KeyCode:
4280     case kF6KeyCode:
4281     case kF7KeyCode:
4282     case kF8KeyCode:
4283     case kF9KeyCode:
4284     case kF10KeyCode:
4285     case kF11KeyCode:
4286     case kF12KeyCode:
4287     case kPauseKeyCode:
4288     case kScrollLockKeyCode:
4289     case kPrintScreenKeyCode:
4290       
4291     case kInsertKeyCode:
4292     case kDeleteKeyCode:
4293     case kTabKeyCode:
4294     case kBackspaceKeyCode:
4295       
4296     case kHomeKeyCode:
4297     case kEndKeyCode:
4298     case kPageUpKeyCode:
4299     case kPageDownKeyCode:
4300     case kLeftArrowKeyCode:
4301     case kRightArrowKeyCode:
4302     case kUpArrowKeyCode:
4303     case kDownArrowKeyCode:
4304     case kReturnKeyCode:
4305     case kEnterKeyCode:
4306     case kPowerbookEnterKeyCode:
4307       isSpecial = PR_TRUE;
4308       break;
4309       
4310     default:
4311       isSpecial = PR_FALSE;
4312       break;
4313   }
4314   
4315   return isSpecial;
4318 static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
4320   // this is not character inputting event, simply.
4321   if (!aEvent.isChar || !aEvent.charCode || aEvent.isMeta)
4322     return PR_FALSE;
4323   // if this is unicode char inputting event, we don't need to check
4324   // ctrl/alt/command keys
4325   if (aEvent.charCode > 0x7F)
4326     return PR_TRUE;
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");
4338   if (!outGeckoEvent)
4339     return;
4341   outGeckoEvent->widget = [self widget];
4342   outGeckoEvent->time = PR_IntervalNow();
4344   if (inEvent) {
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);
4350   }
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");
4360   if (!outGeckoEvent)
4361     return;
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
4376 static PRUint32
4377 UCKeyTranslateToUnicode(const UCKeyboardLayout* aHandle, UInt32 aKeyCode, UInt32 aModifiers,
4378                         UInt32 aKbType)
4380 #ifdef DEBUG_KB
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");
4392 #endif
4393   UInt32 deadKeyState = 0;
4394   UniCharCount len;
4395   UniChar chars[5];
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;
4401 #ifdef DEBUG_KB
4402   NSLog(@"       result: %X(%C)", ch, ch > ' ' ? ch : ' ');
4403 #endif
4404   return ch;
4407 struct KeyTranslateData {
4408   KeyTranslateData() {
4409     mUchr.mLayout = nsnull;
4410     mUchr.mKbType = 0;
4411   }
4413   struct {
4414     const UCKeyboardLayout* mLayout;
4415     UInt32 mKbType;
4416   } mUchr;
4419 static PRUint32
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);
4426   }
4428   return 0;
4431 static PRUint32
4432 GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
4434   static const UCKeyboardLayout* sUSLayout = nsnull;
4435   if (!sUSLayout) {
4436     nsTISInputSource tis("com.apple.keylayout.US");
4437     sUSLayout = tis.GetUCKeyboardLayout();
4438     NS_ENSURE_TRUE(sUSLayout, 0);
4439   }
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)
4451     return;
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
4466     
4467     outGeckoEvent->charCode = 0;
4468     outGeckoEvent->keyCode  = 0; // not set for key press events
4469     
4470     NSString* chars = [aKeyEvent characters];
4471     if ([chars length] > 0)
4472       outGeckoEvent->charCode = [chars characterAtIndex:0];
4473     
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);
4490       } else {
4491         tis.InitByCurrentKeyboardLayout();
4492       }
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;
4513       // normal chars
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
4521       //     is pressed.
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);
4549       }
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
4555       // Command+Shift.
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
4572       // ignored above.
4573       if (!isCmdSwitchLayout) {
4574         // The characters produced with Command seem similar to those without
4575         // Command.
4576         if (unshiftedChar)
4577           cmdedChar = unshiftedChar;
4578         if (shiftedChar)
4579           cmdedShiftChar = shiftedChar;
4580       } else if (uncmdedUSChar == cmdedChar) {
4581         // It looks like characters from a US layout are provided when Command
4582         // is down.
4583         PRUint32 ch = GetUSLayoutCharFromKeyTranslate(key, lockState);
4584         if (ch)
4585           cmdedChar = ch;
4586         ch = GetUSLayoutCharFromKeyTranslate(key, shiftLockMod);
4587         if (ch)
4588           cmdedShiftChar = ch;
4589       }
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.
4596       //
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) {
4606 #ifdef DEBUG_KB
4607           if (outGeckoEvent->charCode != preferredCharCode) {
4608             NSLog(@"      charCode replaced: %X(%C) to %X(%C)",
4609                   outGeckoEvent->charCode,
4610                   outGeckoEvent->charCode > ' ' ? outGeckoEvent->charCode : ' ',
4611                   preferredCharCode,
4612                   preferredCharCode > ' ' ? preferredCharCode : ' ');
4613           }
4614 #endif
4615           outGeckoEvent->charCode = preferredCharCode;
4616         }
4617       }
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);
4626       }
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);
4632       }
4633     }
4634   }
4635   else {
4636     NSString* characters = nil;
4637     if ([aKeyEvent type] != NSFlagsChanged)
4638       characters = [aKeyEvent charactersIgnoringModifiers];
4639     
4640     outGeckoEvent->keyCode =
4641       ConvertMacToGeckoKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
4642     outGeckoEvent->charCode = 0;
4643   } 
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;
4662   if (!mGeckoChild)
4663     return;
4665   if (mPluginEventModel == NPEventModelCocoa) {
4666     UInt32 size;
4667     OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, 0, &size, NULL);
4668     if (status != noErr)
4669       return;
4671     UniChar* chars = (UniChar*)malloc(size);
4672     if (!chars)
4673       return;
4675     status = ::GetEventParameter(aKeyEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, size, NULL, chars);
4676     if (status != noErr) {
4677       free(chars);
4678       return;
4679     }
4681     CFStringRef text = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, chars, (size / sizeof(UniChar)), kCFAllocatorNull);
4682     if (!text) {
4683       free(chars);
4684       return;
4685     }
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);
4697     ::CFRelease(text);
4698     free(chars);
4700     return;
4701   }
4703   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4705   UInt32 numCharCodes;
4706   OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
4707                                         typeChar, NULL, 0, &numCharCodes, NULL);
4708   if (status != noErr)
4709     return;
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)
4716     return;
4718   UInt32 modifiers;
4719   status = ::GetEventParameter(aKeyEvent, kEventParamKeyModifiers,
4720                                typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
4721   if (status != noErr)
4722     return;
4724   UInt32 macKeyCode;
4725   status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
4726                                typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode);
4727   if (status != noErr)
4728     return;
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)
4735       break;
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;
4748       } else {
4749         keyDownEvent.charCode = charCode;
4750         keyDownEvent.isChar   = PR_TRUE;
4751       }
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);
4757       if (!mGeckoChild)
4758         break;
4759     }
4760   }
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
4775 #ifdef DEBUG_IME
4776   NSLog(@"****in sendCompositionEvent; type = %d", aEventType);
4777 #endif
4779   if (!mGeckoChild)
4780     return;
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
4794 #ifdef DEBUG_IME
4795   NSLog(@"****in sendTextEvent; string = '%@'", aString);
4796   NSLog(@" markRange = %d, %d;  selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length);
4797 #endif
4799   if (!mGeckoChild)
4800     return;
4802   nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild);
4803   textEvent.time = PR_IntervalNow();
4804   textEvent.theText = aBuffer;
4805   if (!doCommit)
4806     FillTextRangeInTextEvent(&textEvent, aString, markRange, selRange);
4808   mGeckoChild->DispatchWindowEvent(textEvent);
4809   if (textEvent.rangeArray)
4810     delete [] textEvent.rangeArray;
4813 #pragma mark -
4814 // NSTextInput implementation
4816 #define MAX_BUFFER_SIZE 32
4818 - (void)insertText:(id)insertString
4820   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4822 #if DEBUG_IME
4823   NSLog(@"****in insertText: '%@'", insertString);
4824   NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4825 #endif
4826   if (!mGeckoChild)
4827     return;
4829   if (mGeckoChild->TextInputHandler()->IgnoreIMEComposition())
4830     return;
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)
4849     if (mKeyPressSent)
4850       return;
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.
4863         
4864     // create event for use by plugins
4865 #ifndef NP_NO_CARBON
4866     EventRecord carbonEvent;
4867 #endif
4868     if (mCurKeyEvent) {
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;
4877       }
4878 #endif
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;
4886       }
4887     } else {
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;
4892       }
4893     }
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.
4899     if (mCurKeyEvent) {
4900       mKeyPressHandled = keyPressHandled;
4901       mKeyPressSent = YES;
4902     }
4903   }
4904   else {
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.
4908       if (mGeckoChild) {
4909         mGeckoChild->TextInputHandler()->OnStartIMEComposition(self);
4910         // Note: mGeckoChild might have become null here. Don't count on it from here on.
4911       }
4912     }
4914     if (mGeckoChild && mGeckoChild->TextInputHandler()->IgnoreIMECommit()) {
4915       tmpStr = [tmpStr init];
4916       len = 0;
4917       bufPtr[0] = PRUnichar('\0');
4918       insertString =
4919         [[[NSAttributedString alloc] initWithString:tmpStr] autorelease];
4920     }
4921     [self sendTextEvent:bufPtr attributedString:insertString
4922                                selectedRange:NSMakeRange(0, len)
4923                                markedRange:mMarkedRange
4924                                doCommit:YES];
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.
4929     if (mGeckoChild) {
4930       mGeckoChild->TextInputHandler()->OnEndIMEComposition();
4931       // Note: mGeckoChild might have become null here. Don't count on it from here on.
4932     }
4933     mMarkedRange = NSMakeRange(NSNotFound, 0);
4934   }
4936   if (bufPtr != buffer)
4937     delete[] bufPtr;
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;
4951 #if DEBUG_IME 
4952   NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
4953 #endif
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;
4965 #if DEBUG_IME 
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);
4969 #endif
4971   if (!mGeckoChild)
4972     return;
4974   if (mGeckoChild->TextInputHandler()->IgnoreIMEComposition())
4975     return;
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');
4990 #if DEBUG_IME 
4991   printf("****in setMarkedText, len = %d, text = ", len);
4992   PRUint32 n = 0;
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); 
4996   printf("\n");
4997 #endif
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.
5007     if (mGeckoChild) {
5008       mGeckoChild->TextInputHandler()->OnStartIMEComposition(self);
5009       // Note: mGeckoChild might have become null here. Don't count on it from here on.
5010     }
5011   }
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
5020                                        doCommit:commit];
5021     // Note: mGeckoChild might have become null here. Don't count on it from here on.
5023     if (commit) {
5024       [self sendCompositionEvent:NS_COMPOSITION_END];
5025       // Note: mGeckoChild might have become null here. Don't count on it from here on.
5026       if (mGeckoChild) {
5027         mGeckoChild->TextInputHandler()->OnEndIMEComposition();
5028         // Note: mGeckoChild might have become null here. Don't count on it from here on.
5029       }
5030     }
5031   }
5033   if (bufPtr != buffer)
5034     delete[] bufPtr;
5036   NS_OBJC_END_TRY_ABORT_BLOCK;
5039 - (void) unmarkText
5041 #if DEBUG_IME
5042   NSLog(@"****in unmarkText");
5043   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
5044 #endif
5045   if (mGeckoChild)
5046     mGeckoChild->TextInputHandler()->CommitIMEComposition();
5049 - (BOOL) hasMarkedText
5051 #if DEBUG_IME
5052   NSLog(@"****in hasMarkText");
5053   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
5054 #endif
5055   return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
5058 - (NSInteger) conversationIdentifier
5060 #if DEBUG_IME
5061   NSLog(@"****in conversationIdentifier");
5062 #endif
5063   if (!mGeckoChild)
5064     return (long)self;
5065   nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
5066   textContent.InitForQueryTextContent(0, 0);
5067   mGeckoChild->DispatchWindowEvent(textContent);
5068   if (!textContent.mSucceeded)
5069     return (long)self;
5070 #if DEBUG_IME
5071   NSLog(@" the ID = %ld", (long)textContent.mReply.mContentsRoot);
5072 #endif
5073   return (long)textContent.mReply.mContentsRoot;
5076 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
5078   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5080 #if DEBUG_IME
5081   NSLog(@"****in attributedSubstringFromRange");
5082   NSLog(@" theRange      = %d, %d", theRange.location, theRange.length);
5083   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
5084 #endif
5085   if (!mGeckoChild || theRange.length == 0)
5086     return nil;
5088   nsAutoString str;
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())
5094     return nil;
5096   NSString* nsstr = ToNSString(textContent.mReply.mString);
5097   NSAttributedString* result =
5098     [[[NSAttributedString alloc] initWithString:nsstr
5099                                      attributes:nil] autorelease];
5100   return result;
5102   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5105 - (NSRange) markedRange
5107   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5109 #if DEBUG_IME
5110   NSLog(@"****in markedRange");
5111   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
5112 #endif
5114   if (![self hasMarkedText]) {
5115     return NSMakeRange(NSNotFound, 0);
5116   }
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;
5127 #if DEBUG_IME
5128   NSLog(@"****in selectedRange");
5129   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
5130 #endif
5131   if (!mGeckoChild)
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);
5138 #if DEBUG_IME
5139   NSLog(@" result of selectedRange = %d, %d",
5140         selection.mReply.mOffset, selection.mReply.mString.Length());
5141 #endif
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;
5152 #if DEBUG_IME
5153   NSLog(@"****in firstRectForCharacterRange");
5154   NSLog(@" theRange      = %d, %d", theRange.location, theRange.length);
5155   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
5156 #endif
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.
5161   NSRect rect;
5162   if (!mGeckoChild || theRange.location == NSNotFound)
5163     return rect;
5165   nsIntRect r;
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;
5173     else
5174       useCaretRect = PR_TRUE;
5175   }
5177   if (useCaretRect) {
5178     nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, mGeckoChild);
5179     caretRect.InitForQueryCaretRect(theRange.location);
5180     mGeckoChild->DispatchWindowEvent(caretRect);
5181     if (!caretRect.mSucceeded)
5182       return rect;
5183     r = caretRect.mReply.mRect;
5184     r.width = 0;
5185   }
5187   nsIWidget* rootWidget = mGeckoChild->GetTopLevelWidget();
5188   NSWindow* rootWindow =
5189     static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
5190   NSView* rootView =
5191     static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
5192   if (!rootWindow || !rootView)
5193     return rect;
5194   GeckoRectToNSRect(r, rect);
5195   rect = [rootView convertRect:rect toView:nil];
5196   rect.origin = [rootWindow convertBaseToScreen:rect.origin];
5197 #if DEBUG_IME
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);
5200 #endif
5201   return rect;
5203   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
5206 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
5208 #if DEBUG_IME
5209   NSLog(@"****in characterIndexForPoint");
5210   NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5211 #endif
5213   // To implement this, we'd have to grovel in text frames looking at text offsets.
5214   return 0;
5217 - (NSArray*) validAttributesForMarkedText
5219   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5221 #if DEBUG_IME
5222   NSLog(@"****in validAttributesForMarkedText");
5223   NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5224 #endif
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;
5232 #pragma mark -
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]];
5248   return newEvent;
5250   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5253 #ifdef PR_LOGGING
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));
5260     } else {
5261       aBuf += nsPrintfCString("\\u%04x", ch);
5262     }
5263   }
5264   return aBuf.get();
5266 #endif
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;
5276   if (!mGeckoChild)
5277     return NO;
5279 #ifdef PR_LOGGING
5280   nsCAutoString str1;
5281   nsCAutoString str2;
5282 #endif
5283   PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5284          ("ChildView processKeyDownEvent: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5285           [theEvent keyCode],
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;
5305     }
5306 #endif
5308     mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoKeydown);
5309     if (!mGeckoChild) {
5310       return mKeyDownHandled;
5311     }
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;
5317       mCurKeyEvent = nil;
5318       mKeyDownHandled = PR_FALSE;
5319       return handled;
5320     }
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);
5331       mCurKeyEvent = nil;
5332       mKeyDownHandled = PR_FALSE;
5333       return handled;
5334     }
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;
5349       if (!mGeckoChild)
5350         return (mKeyDownHandled || mKeyPressHandled);
5351     }
5352   }
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;
5361   }
5363   if (!mGeckoChild) {
5364     return (mKeyDownHandled || mKeyPressHandled);
5365   }
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;
5378       }
5379       mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoKeypress);
5380     }
5381   }
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;
5389   mKeyPressSent = NO;
5390   mCurKeyEvent = nil;
5391   mKeyDownHandled = PR_FALSE;
5393   return handled;
5395   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5398 #ifdef NP_NO_CARBON
5399 - (NSTextInputContext *)inputContext
5401   if (mIsPluginView && mPluginEventModel == NPEventModelCocoa)
5402     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
5403   else
5404     return [super inputContext];
5406 #endif
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);
5436   }
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
5446   return YES;
5449 - (BOOL)inCocoaPluginComposition
5451 #ifdef NP_NO_CARBON
5452   return [[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition];
5453 #else
5454   return mPluginTSMInComposition;
5455 #endif
5458 - (void)sendCocoaNPAPITextEvent:(NSString*)string
5460   NPCocoaEvent cocoaTextEvent;
5461   InitNPCocoaEvent(&cocoaTextEvent);
5462   cocoaTextEvent.type = NPCocoaEventTextInput;
5463   cocoaTextEvent.data.text.text = (NPNSString*)string;
5464   
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) {
5476 #ifdef NP_NO_CARBON
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];
5487         if (textString) {
5488           [self sendCocoaNPAPITextEvent:textString];
5489         }
5491         return;
5492       }
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);
5503       if (!mGeckoChild) {
5504         return;
5505       }
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];
5514         if (textString) {
5515           [self sendCocoaNPAPITextEvent:textString];
5516         }
5518         return;
5519       }
5521       // Nothing else to do for Cocoa NPAPI plugins.
5522       return;
5523     }
5524 #endif
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;
5534       }
5535       else {
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);
5545         if (!mGeckoChild) {
5546           return;
5547         }
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;
5553         }
5554         else {
5555           return;
5556         }
5557       }
5558     }
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);
5573     return;
5574 #endif
5575   }
5577   PRBool handled = [self processKeyDownEvent:theEvent];
5578   
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];
5583   }
5585   NS_OBJC_END_TRY_ABORT_BLOCK;
5588 - (void)keyUp:(NSEvent*)theEvent
5590   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5592 #ifdef PR_LOGGING
5593   nsCAutoString str1;
5594   nsCAutoString str2;
5595 #endif
5596   PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5597          ("ChildView keyUp: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5598           [theEvent keyCode],
5599           [theEvent modifierFlags],
5600           ToEscapedString([theEvent characters], str1),
5601           ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
5603   if (!mGeckoChild)
5604     return;
5606   if (mIgnoreNextKeyUpEvent) {
5607     mIgnoreNextKeyUpEvent = NO;
5608     return;
5609   }
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]) {
5617         return;
5618       }
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);
5626     }
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]);
5632       
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)
5641         return;
5642       
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);      
5657     }
5658 #endif
5659     return;
5660   }
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()) {
5665     return;
5666   }
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;
5680   if (!mGeckoChild)
5681     return;
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];
5691     if (!mGeckoChild)
5692       return;
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];
5710         if (!mGeckoChild)
5711           return;
5713         // Stop if focus has changed.
5714         // Check to see if we are still the first responder.
5715         if (![self isFirstResponder])
5716           break;
5717       }
5718     }
5720     gLastModifierState = modifiers;
5721   }
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
5738   if (!mDragService)
5739     return NO;
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()) {
5752     return;
5753   }
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;
5769   }
5770 #endif
5771   NPCocoaEvent cocoaEvent;
5772   if (mPluginEventModel == NPEventModelCocoa) {
5773     ConvertCocoaKeyEventToNPCocoaEvent(theEvent, cocoaEvent, message);
5774     geckoEvent.pluginEvent = &cocoaEvent;
5775   }
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.
5785   if (!mGeckoChild)
5786     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
5797   if (!mGeckoChild)
5798     return NO;
5800   nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
5801   if (windowWidget && !windowWidget->ShouldFocusPlugin())
5802     return NO;
5804   return YES;
5807 // Returns NO if the plugin shouldn't be focused/unfocused.
5808 - (BOOL)updateCocoaPluginFocusStatus:(BOOL)hasFocus
5810   if (!mGeckoChild)
5811     return NO;
5813   if (![self shouldFocusPlugin])
5814     return NO;
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);
5824   if (hasFocus)
5825     [self sendFocusEvent:NS_PLUGIN_FOCUS];
5827   return YES;
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])
5838       return NO;
5839   }
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])
5854       return NO;
5855   }
5857   return [super resignFirstResponder];
5859   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES);
5862 - (void)viewsWindowDidBecomeKey
5864   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5866   if (!mGeckoChild)
5867     return;
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:)];
5876   if (isMozWindow)
5877     [[self window] setSuppressMakeKeyFront:YES];
5879   [self sendFocusEvent:NS_ACTIVATE];
5881   if (isMozWindow)
5882     [[self window] setSuppressMakeKeyFront:NO];
5884   NS_OBJC_END_TRY_ABORT_BLOCK;
5887 - (void)viewsWindowDidResignKey
5889   if (!mGeckoChild)
5890     return;
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];
5911   [self release];
5913   NS_OBJC_END_TRY_ABORT_BLOCK;
5916 #pragma mark -
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;
5941   if (!mGeckoChild)
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!");
5949     if (!mDragService)
5950       return NSDragOperationNone;
5951   }
5953   if (aMessage == NS_DRAGDROP_ENTER)
5954     mDragService->StartDragSession();
5956   nsCOMPtr<nsIDragSession> dragSession;
5957   mDragService->GetCurrentSession(getter_AddRefs(dragSession));
5958   if (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);
5964     }
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));
5974         if (!sourceNode) {
5975           mDragService->EndDragSession(PR_FALSE);
5976         }
5977         return NSDragOperationNone;
5978       }
5979     }
5980     
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;
5987       else
5988         action = nsIDragService::DRAGDROP_ACTION_COPY;
5989     }
5990     dragSession->SetDragAction(action);
5991   }
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);
6005   if (!mGeckoChild)
6006     return NSDragOperationNone;
6008   if (dragSession) {
6009     switch (aMessage) {
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));
6017         if (!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);
6023         }
6024       }
6025     }
6026   }
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"));
6038   
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);
6074   return handled;
6077 // NSDraggingSource
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!");
6091   }
6093   if (mDragService) {
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);
6114       if (dataTransferNS)
6115         dataTransferNS->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
6116     }
6118     mDragService->EndDragSession(PR_TRUE);
6119     NS_RELEASE(mDragService);
6120   }
6122   [globalDragPboard release];
6123   globalDragPboard = nil;
6124   [gLastDragMouseDownEvent release];
6125   gLastDragMouseDownEvent = nil;
6127   NS_OBJC_END_TRY_ABORT_BLOCK;
6130 // NSDraggingSource
6131 // this is just implemented so we comply with the NSDraggingSource informal protocol
6132 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
6134   return UINT_MAX;
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
6140 // the drag.
6141 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination
6143   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
6145   nsresult rv;
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");
6154     return nil;
6155   }
6157   if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
6158     NS_ERROR("failed InitWithCFURL");
6159     return nil;
6160   }
6162   if (!gDraggedTransferables)
6163     return nil;
6165   PRUint32 transferableCount;
6166   rv = gDraggedTransferables->Count(&transferableCount);
6167   if (NS_FAILED(rv))
6168     return nil;
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));
6174     if (!item) {
6175       NS_ERROR("no transferable");
6176       return nil;
6177     }
6179     item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsILocalFile*));
6180     
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);
6186   }
6187   
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];
6193   [name release];
6195   return rslt;
6197   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6200 #pragma mark -
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.
6213   //
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.
6217   //
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])
6224   id result = nil;
6226   if ((!sendType || IsSupportedType(sendType)) &&
6227       (!returnType || IsSupportedType(returnType))) {
6228     if (mGeckoChild) {
6229       // Assume that this object will be able to handle this request.
6230       result = self;
6232       // Keep the ChildView alive during this operation.
6233       nsAutoRetainCocoaObject kungFuDeathGrip(self);
6234       
6235       // Determine if there is a selection (if sending to the service).
6236       if (sendType) {
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)
6241           result = nil;
6242       }
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)
6250           result = nil;
6251       }
6252     }
6253   }
6255 #undef IsSupportedType
6257   // Give the superclass a chance if this object will not handle this request.
6258   if (!result)
6259     result = [super validRequestorForSendType:sendType returnType:returnType];
6261   return result;
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)
6276     return NO;
6278   // Bail out if there is no Gecko object.
6279   if (!mGeckoChild)
6280     return NO;
6282   // Obtain the current selection.
6283   nsQueryContentEvent event(PR_TRUE,
6284                             NS_QUERY_SELECTION_AS_TRANSFERABLE,
6285                             mGeckoChild);
6286   mGeckoChild->DispatchWindowEvent(event);
6287   if (!event.mSucceeded || !event.mReply.mTransferable)
6288     return NO;
6290   // Transform the transferable to an NSDictionary.
6291   NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(event.mReply.mTransferable);
6292   if (!pasteboardOutputDict)
6293     return NO;
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];        
6317     }
6318   }
6320   return YES;
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
6328   nsresult rv;
6329   nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
6330   if (NS_FAILED(rv))
6331     return NO;
6333   trans->AddDataFlavor(kUnicodeMime);
6334   trans->AddDataFlavor(kHTMLMime);
6336   rv = nsClipboard::TransferableFromPasteboard(trans, pboard);
6337   if (NS_FAILED(rv))
6338     return NO;
6340   if (!mGeckoChild)
6341     return NO;
6343   nsContentCommandEvent command(PR_TRUE,
6344                                 NS_CONTENT_COMMAND_PASTE_TRANSFERABLE,
6345                                 mGeckoChild);
6346   command.mTransferable = trans;
6347   mGeckoChild->DispatchWindowEvent(command);
6348   
6349   return command.mSucceeded && command.mIsEnabled;
6352 #pragma mark -
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
6358    object.
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
6365   if (!mGeckoChild)
6366     return nil;
6368   id<mozAccessible> nativeAccessible = nil;
6370   nsAutoRetainCocoaObject kungFuDeathGrip(self);
6371   nsCOMPtr<nsIWidget> kungFuDeathGrip2(mGeckoChild);
6372   nsRefPtr<nsAccessible> accessible = mGeckoChild->GetDocumentAccessible();
6373   if (!mGeckoChild)
6374     return nil;
6376   if (accessible)
6377     accessible->GetNativeInterface((void**)&nativeAccessible);
6379 #ifdef DEBUG_hakan
6380   NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
6381 #endif
6382   
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
6391   return YES;
6394 - (id)representedView
6396   return self;
6399 - (BOOL)isRoot
6401   return [[self accessible] isRoot];
6404 #ifdef DEBUG
6405 - (void)printHierarchy
6407   [[self accessible] printHierarchy];
6409 #endif
6411 #pragma mark -
6413 // general
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];
6430 // actions
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];
6447 // attributes
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];
6464   
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;
6471   }
6473   return [accessible accessibilityAttributeValue:attribute];
6475   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6478 #endif /* ACCESSIBILITY */
6480 @end
6482 #pragma mark -
6484 void
6485 ChildViewMouseTracker::OnDestroyView(ChildView* aView)
6487   if (sLastMouseEventView == aView)
6488     sLastMouseEventView = nil;
6491 void
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];
6504     }
6505     [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent enter:YES type:type];
6506   }
6509 void
6510 ChildViewMouseTracker::MouseMoved(NSEvent* aEvent)
6512   ReEvaluateMouseEnterState(aEvent);
6513   [sLastMouseEventView handleMouseMoved:aEvent];
6516 ChildView*
6517 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
6519   NSWindow* window = WindowForEvent(aEvent);
6520   if (!window)
6521     return nil;
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]])
6527     return nil;
6529   ChildView* childView = (ChildView*)view;
6530   // If childView is being destroyed return nil.
6531   if (![childView widget])
6532     return nil;
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];
6542   if (window) {
6543     // This is one of our own windows.
6544     return NSMouseInRect(aPoint, [window frame], NO);
6545   }
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;
6554   }
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
6562     return false;
6564   // Ignore transparent windows.
6565   float alpha;
6566   if ((kCGErrorSuccess == CGSGetWindowAlpha(cid, aWindowNumber, &alpha)) &&
6567       alpha < 0.1f)
6568     return false;
6570   CGRect rect;
6571   if (kCGErrorSuccess != CGSGetScreenRectForWindow(cid, aWindowNumber, &rect))
6572     return false;
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);
6602   if (!windowList)
6603     return nil;
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)) {
6610       free(windowList);
6611       return windowNumber;
6612     }
6613   }
6615   free(windowList);
6616   return 0;
6619 // Find Gecko window under the mouse. Returns nil if the mouse isn't over
6620 // any of our windows.
6621 NSWindow*
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;
6635 BOOL
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)
6642     return YES;
6644   id delegate = [aWindow delegate];
6645   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
6646     return YES;
6648   nsIWidget *windowWidget = [(WindowDelegate *)delegate geckoWidget];
6649   if (!windowWidget)
6650     return YES;
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])
6668         return NO;
6670       topLevelWindow = aWindow;
6671       break;
6672     case eWindowType_sheet: {
6673       nsIWidget* parentWidget = windowWidget->GetSheetWindowParent();
6674       if (!parentWidget)
6675         return YES;
6677       topLevelWindow = (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW);
6678       break;
6679     }
6681     default:
6682       return YES;
6683   }
6685   if (!topLevelWindow ||
6686       ([topLevelWindow isMainWindow] && !aIsClickThrough) ||
6687       [aEvent type] == NSOtherMouseDown ||
6688       (([aEvent modifierFlags] & NSCommandKeyMask) != 0 &&
6689        [aEvent type] != NSMouseMoved))
6690     return YES;
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];
6697 #pragma mark -
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();
6714   if (!activeDoc) {
6715     return eventNotHandledErr;
6716   }
6718   ChildView *target = nil;
6719   OSStatus status = ::TSMGetDocumentProperty(activeDoc, kFocusedChildViewTSMDocPropertyTag,
6720                                              sizeof(ChildView *), nil, &target);
6721   if (status != noErr)
6722     target = nil;
6723   if (!target) {
6724     return eventNotHandledErr;
6725   }
6727   EventRef keyEvent = NULL;
6728   status = ::GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent,
6729                                typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
6730   if ((status != noErr) || !keyEvent) {
6731     return eventNotHandledErr;
6732   }
6734   [target processPluginKeyEvent:keyEvent];
6736   return noErr;
6739 static EventHandlerRef gPluginKeyEventsHandler = NULL;
6741 // Called from nsAppShell::Init()
6742 void NS_InstallPluginKeyEventsHandler()
6744   if (gPluginKeyEventsHandler)
6745     return;
6746   static const EventTypeSpec sTSMEvents[] =
6747     { { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
6748   ::InstallEventHandler(::GetEventDispatcherTarget(),
6749                         ::NewEventHandlerUPP(PluginKeyEventsHandler),
6750                         GetEventTypeCount(sTSMEvents),
6751                         sTSMEvents,
6752                         NULL,
6753                         &gPluginKeyEventsHandler);
6756 // Called from nsAppShell::Exit()
6757 void NS_RemovePluginKeyEventsHandler()
6759   if (!gPluginKeyEventsHandler)
6760     return;
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;
6773 @end
6775 @implementation NSObject (IMKInputSessionMethodSwizzling)
6777 - (BOOL)nsChildView_IMKInputSession_handleEvent:(EventRef)theEvent
6779   [self retain];
6780   BOOL retval = [self nsChildView_IMKInputSession_handleEvent:theEvent];
6781   NSUInteger retainCount = [self retainCount];
6782   [self release];
6783   // Return without doing anything if we've been deleted.
6784   if (retainCount == 1) {
6785     return retval;
6786   }
6788   NSWindow *mainWindow = [NSApp mainWindow];
6789   NSResponder *firstResponder = [mainWindow firstResponder];
6790   if (![firstResponder isKindOfClass:[ChildView class]]) {
6791     return retval;
6792   }
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)];
6802   return retval;
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];
6814   }
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];
6825   }
6826   [self nsChildView_IMKInputSession_finishSession];
6829 @end
6831 #endif // NP_NO_CARBON
6833 @interface NSView (MethodSwizzling)
6834 - (BOOL)nsChildView_NSView_mouseDownCanMoveWindow;
6835 @end
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))
6857     return NO;
6858   return [self nsChildView_NSView_mouseDownCanMoveWindow];
6861 @end