Bug 439354 - OS X toolbar background doesn't have a good gradient. Part 1 (widget...
[mozilla-central.git] / widget / src / cocoa / nsChildView.mm
blobfc60c90e29c1aa28a7aac2391b3a0fdbd834499f
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 <mats.palmgren@bredband.net>
28  *
29  * Alternatively, the contents of this file may be used under the terms of
30  * either the GNU General Public License Version 2 or later (the "GPL"), or 
31  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32  * in which case the provisions of the GPL or the LGPL are applicable instead
33  * of those above. If you wish to allow use of your version of this file only
34  * under the terms of either the GPL or the LGPL, and not to allow others to
35  * use your version of this file under the terms of the MPL, indicate your
36  * decision by deleting the provisions above and replace them with the notice
37  * and other provisions required by the GPL or the LGPL. If you do not delete
38  * the provisions above, a recipient may use your version of this file under
39  * the terms of any one of the MPL, the GPL or the LGPL.
40  *
41  * ***** END LICENSE BLOCK ***** */
43 #include <unistd.h>
45 #include "nsChildView.h"
46 #include "nsCocoaWindow.h"
48 #include "nsObjCExceptions.h"
49 #include "nsCOMPtr.h"
50 #include "nsToolkit.h"
51 #include "nsCRT.h"
52 #include "nsplugindefs.h"
53 #include "nsIPrefService.h"
54 #include "nsIPrefBranch.h"
56 #include "nsIFontMetrics.h"
57 #include "nsIDeviceContext.h"
58 #include "nsIRegion.h"
59 #include "nsIRollupListener.h"
60 #include "nsIScrollableView.h"
61 #include "nsIViewManager.h"
62 #include "nsIInterfaceRequestor.h"
63 #include "nsIServiceManager.h"
64 #include "nsILocalFile.h"
65 #include "nsILocalFileMac.h"
66 #include "nsGfxCIID.h"
67 #include "nsIMenuRollup.h"
69 #include "nsDragService.h"
70 #include "nsCursorManager.h"
71 #include "nsWindowMap.h"
72 #include "nsCocoaUtils.h"
73 #include "nsMenuBarX.h"
75 #include "gfxContext.h"
76 #include "gfxQuartzSurface.h"
78 #include <dlfcn.h>
80 #undef DEBUG_IME
81 #undef DEBUG_UPDATE
82 #undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated
84 #ifdef MOZ_LOGGING
85 #define FORCE_PR_LOG
86 #endif
87 #include "prlog.h"
89 #ifdef PR_LOGGING
90 PRLogModuleInfo* sCocoaLog = nsnull;
91 #endif
93 // npapi.h defines NPEventType_AdjustCursorEvent but we don't want to include npapi.h here.
94 // We need to send this in the "what" field for certain native plugin events. WebKit does
95 // this as well.
96 #define adjustCursorEvent 33
98 extern "C" {
99   CG_EXTERN void CGContextResetCTM(CGContextRef);
100   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
101   CG_EXTERN void CGContextResetClip(CGContextRef);
104 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
105 struct __TISInputSource;
106 typedef __TISInputSource* TISInputSourceRef;
107 #endif
108 TISInputSourceRef (*Leopard_TISCopyCurrentKeyboardLayoutInputSource)() = NULL;
109 void* (*Leopard_TISGetInputSourceProperty)(TISInputSourceRef inputSource, CFStringRef propertyKey) = NULL;
110 CFArrayRef (*Leopard_TISCreateInputSourceList)(CFDictionaryRef properties, Boolean includeAllInstalled) = NULL;
111 CFStringRef kOurTISPropertyUnicodeKeyLayoutData = NULL;
112 CFStringRef kOurTISPropertyInputSourceID = NULL;
114 extern PRBool gCocoaWindowMethodsSwizzled; // Defined in nsCocoaWindow.mm
116 extern nsISupportsArray *gDraggedTransferables;
118 PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
119 PRBool nsTSMManager::sIsRomanKeyboardsOnly = PR_FALSE;
120 PRBool nsTSMManager::sIgnoreCommit = PR_FALSE;
121 NSView<mozView>* nsTSMManager::sComposingView = nsnull;
122 TSMDocumentID nsTSMManager::sDocumentID = nsnull;
123 NSString* nsTSMManager::sComposingString = nsnull;
125 static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
126 static NSView* sLastViewEntered = nil;
127 #ifdef INVALIDATE_DEBUGGING
128 static void blinkRect(Rect* r);
129 static void blinkRgn(RgnHandle rgn);
130 #endif
132 nsIRollupListener * gRollupListener = nsnull;
133 nsIWidget         * gRollupWidget   = nsnull;
136 @interface ChildView(Private)
138 // sets up our view, attaching it to its owning gecko view
139 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
141 // sends gecko an ime composition event
142 - (nsRect) sendCompositionEvent:(PRInt32)aEventType;
144 // sends gecko an ime text event
145 - (void) sendTextEvent:(PRUnichar*) aBuffer 
146                        attributedString:(NSAttributedString*) aString
147                        selectedRange:(NSRange)selRange
148                        markedRange:(NSRange)markRange
149                        doCommit:(BOOL)doCommit;
151 // do generic gecko event setup with a generic cocoa event. accepts nil inEvent.
152 - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
154 // set up a gecko mouse event based on a cocoa mouse event
155 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
157 // set up a gecko key event based on a cocoa key event
158 - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent;
160 - (NSMenu*)contextMenu;
161 - (TopLevelWindowData*)ensureWindowData;
163 - (void)setIsPluginView:(BOOL)aIsPlugin;
164 - (BOOL)isPluginView;
166 - (BOOL)childViewHasPlugin;
168 - (BOOL)isRectObscuredBySubview:(NSRect)inRect;
170 - (void)processPendingRedraws;
172 - (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv;
174 - (BOOL)ensureCorrectMouseEventTarget:(NSEvent *)anEvent;
176 - (void)maybeInitContextMenuTracking;
178 + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
180 #if USE_CLICK_HOLD_CONTEXTMENU
181  // called on a timer two seconds after a mouse down to see if we should display
182  // a context menu (click-hold)
183 - (void)clickHoldCallback:(id)inEvent;
184 #endif
186 #ifdef ACCESSIBILITY
187 - (id<mozAccessible>)accessible;
188 #endif
190 @end
193 #pragma mark -
196 /* Convenience routines to go from a gecko rect to cocoa NSRects and back
198  * Gecko rects (nsRect) contain an origin (x,y) in a coordinate
199  * system with (0,0) in the top-left of the screen. Cocoa rects
200  * (NSRect) contain an origin (x,y) in a coordinate system with
201  * (0,0) in the bottom-left of the screen. Both nsRect and NSRect
202  * contain width/height info, with no difference in their use.
203  * If a Cocoa rect is from a flipped view, there is no need to
204  * convert coordinate systems.
205  */
208 static inline void
209 GeckoRectToNSRect(const nsRect & inGeckoRect, NSRect & outCocoaRect)
211   outCocoaRect.origin.x = inGeckoRect.x;
212   outCocoaRect.origin.y = inGeckoRect.y;
213   outCocoaRect.size.width = inGeckoRect.width;
214   outCocoaRect.size.height = inGeckoRect.height;
217 static inline void
218 NSRectToGeckoRect(const NSRect & inCocoaRect, nsRect & outGeckoRect)
220   outGeckoRect.x = static_cast<nscoord>(inCocoaRect.origin.x);
221   outGeckoRect.y = static_cast<nscoord>(inCocoaRect.origin.y);
222   outGeckoRect.width = static_cast<nscoord>(inCocoaRect.size.width);
223   outGeckoRect.height = static_cast<nscoord>(inCocoaRect.size.height);
227 static inline void 
228 ConvertGeckoRectToMacRect(const nsRect& aRect, Rect& outMacRect)
230   outMacRect.left = aRect.x;
231   outMacRect.top = aRect.y;
232   outMacRect.right = aRect.x + aRect.width;
233   outMacRect.bottom = aRect.y + aRect.height;
236 // Flips a screen coordinate from a point in the cocoa coordinate system (bottom-left rect) to a point
237 // that is a "flipped" cocoa coordinate system (starts in the top-left).
238 static inline void
239 FlipCocoaScreenCoordinate (NSPoint &inPoint)
240 {  
241   inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
243   
245 static PRUint32
246 UnderlineAttributeToTextRangeType(PRUint32 aUnderlineStyle, NSRange selRange)
248 #ifdef DEBUG_IME
249   NSLog(@"****in underlineAttributeToTextRangeType = %d", aUnderlineStyle);
250 #endif
252   // For more info on the underline attribute, please see: 
253   // http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/AttributedStrings/Tasks/AccessingAttrs.html
254   // We are not clear where the define for value 2 is right now. 
255   // To see this value in japanese ime, type 'aaaaaaaaa' and hit space to make the
256   // ime send you some part of text in 1 (NSSingleUnderlineStyle) and some part in 2. 
257   // ftang will ask apple for more details
258   //
259   // it probably means show 1-pixel thickness underline vs 2-pixel thickness
260   
261   PRUint32 attr;
262   if (selRange.length == 0) {
263     switch (aUnderlineStyle) {
264       case 1:
265         attr = NS_TEXTRANGE_RAWINPUT;
266         break;
267       case 2:
268       default:
269         attr = NS_TEXTRANGE_SELECTEDRAWTEXT;
270         break;
271     }
272   }
273   else {
274     switch (aUnderlineStyle) {
275       case 1:
276         attr = NS_TEXTRANGE_CONVERTEDTEXT;
277         break;
278       case 2:
279       default:
280         attr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
281         break;
282     }
283   }
284   return attr;
288 static PRUint32
289 CountRanges(NSAttributedString *aString)
291   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
293   // Iterate through aString for the NSUnderlineStyleAttributeName and count the 
294   // different segments adjusting limitRange as we go.
295   PRUint32 count = 0;
296   NSRange effectiveRange;
297   NSRange limitRange = NSMakeRange(0, [aString length]);
298   while (limitRange.length > 0) {
299     [aString attribute:NSUnderlineStyleAttributeName 
300                atIndex:limitRange.location 
301  longestEffectiveRange:&effectiveRange
302                inRange:limitRange];
303     limitRange = NSMakeRange(NSMaxRange(effectiveRange), 
304                              NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
305     count++;
306   }
307   return count;
309   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
313 static void
314 ConvertAttributeToGeckoRange(NSAttributedString *aString, NSRange markRange, NSRange selRange, PRUint32 inCount, nsTextRange* aRanges)
316   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
318   // Convert the Cocoa range into the nsTextRange Array used in Gecko.
319   // Iterate through the attributed string and map the underline attribute to Gecko IME textrange attributes.
320   // We may need to change the code here if we change the implementation of validAttributesForMarkedText.
321   PRUint32 i = 0;
322   NSRange effectiveRange;
323   NSRange limitRange = NSMakeRange(0, [aString length]);
324   while ((limitRange.length > 0) && (i < inCount)) {
325     id attributeValue = [aString attribute:NSUnderlineStyleAttributeName 
326                               atIndex:limitRange.location 
327                               longestEffectiveRange:&effectiveRange
328                               inRange:limitRange];
329     aRanges[i].mStartOffset = effectiveRange.location;                         
330     aRanges[i].mEndOffset = NSMaxRange(effectiveRange);                         
331     aRanges[i].mRangeType = UnderlineAttributeToTextRangeType([attributeValue intValue], selRange); 
332     limitRange = NSMakeRange(NSMaxRange(effectiveRange), 
333                              NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
334     i++;
335   }
336   // Get current caret position.
337   // Caret is indicator of insertion point, so mEndOffset = 0.
338   aRanges[i].mStartOffset = selRange.location + selRange.length;                         
339   aRanges[i].mEndOffset = 0;                         
340   aRanges[i].mRangeType = NS_TEXTRANGE_CARETPOSITION;
342   NS_OBJC_END_TRY_ABORT_BLOCK;
346 static void
347 FillTextRangeInTextEvent(nsTextEvent *aTextEvent, NSAttributedString* aString, NSRange markRange, NSRange selRange)
349   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
351   // Count the number of segments in the attributed string and add one more count for sending current caret position to Gecko.
352   // Allocate the right size of nsTextRange and draw caret at right position.
353   // Convert the attributed string into an array of nsTextRange and get current caret position by calling above functions.
354   PRUint32 count = CountRanges(aString) + 1;
355   aTextEvent->rangeArray = new nsTextRange[count];
356   if (aTextEvent->rangeArray) {
357     aTextEvent->rangeCount = count;
358     ConvertAttributeToGeckoRange(aString, markRange, selRange, aTextEvent->rangeCount,  aTextEvent->rangeArray);
359   }
361   NS_OBJC_END_TRY_ABORT_BLOCK;
364 #pragma mark -
367 nsChildView::nsChildView() : nsBaseWidget()
368 , mView(nsnull)
369 , mParentView(nsnull)
370 , mParentWidget(nsnull)
371 , mVisible(PR_FALSE)
372 , mDrawing(PR_FALSE)
373 , mLiveResizeInProgress(PR_FALSE)
374 , mIsPluginView(PR_FALSE)
375 , mPluginDrawing(PR_FALSE)
376 , mPluginIsCG(PR_FALSE)
377 , mInSetFocus(PR_FALSE)
379 #ifdef PR_LOGGING
380   if (!sCocoaLog) {
381     sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
382     CFIndex idx;
383     KLGetKeyboardLayoutCount(&idx);
384     PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("Keyboard layout configuration:"));
385     for (CFIndex i = 0; i < idx; ++i) {
386       KeyboardLayoutRef curKL;
387       if (KLGetKeyboardLayoutAtIndex(i, &curKL) == noErr) {
388         CFStringRef name;
389         if (KLGetKeyboardLayoutProperty(curKL, kKLName, (const void**)&name) == noErr) {
390           int idn;
391           KLGetKeyboardLayoutProperty(curKL, kKLIdentifier, (const void**)&idn);
392           int kind;
393           KLGetKeyboardLayoutProperty(curKL, kKLKind, (const void**)&kind);
394           char buf[256];
395           CFStringGetCString(name, buf, 256, kCFStringEncodingASCII);
396           PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("  %d,%s,%d\n", idn, buf, kind));
397         }
398       }
399     }
400   }
401 #endif
403   SetBackgroundColor(NS_RGB(255, 255, 255));
404   SetForegroundColor(NS_RGB(0, 0, 0));
406   if (nsToolkit::OnLeopardOrLater() && !Leopard_TISCopyCurrentKeyboardLayoutInputSource) {
407     // This libary would already be open for LMGetKbdType (and probably other
408     // symbols), so merely using RTLD_DEFAULT in dlsym would be sufficient,
409     // but man dlsym says: "all mach-o images in the process (except ...) are
410     // searched in the order they were loaded.  This can be a costly search
411     // and should be avoided."
412     void* hitoolboxHandle = dlopen("/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Versions/A/HIToolbox", RTLD_LAZY);
413     if (hitoolboxHandle) {
414       *(void **)(&Leopard_TISCopyCurrentKeyboardLayoutInputSource) = dlsym(hitoolboxHandle, "TISCopyCurrentKeyboardLayoutInputSource");
415       *(void **)(&Leopard_TISGetInputSourceProperty) = dlsym(hitoolboxHandle, "TISGetInputSourceProperty");
416       *(void **)(&Leopard_TISCreateInputSourceList) = dlsym(hitoolboxHandle, "TISCreateInputSourceList");
417       kOurTISPropertyUnicodeKeyLayoutData = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyUnicodeKeyLayoutData"));
418       kOurTISPropertyInputSourceID = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyInputSourceID"));
419     }
420   }
424 nsChildView::~nsChildView()
426   // notify the children that we're gone
427   for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
428     nsChildView* childView = static_cast<nsChildView*>(kid);
429     childView->mParentWidget = nsnull;
430   }
432   NS_WARN_IF_FALSE(mOnDestroyCalled, "nsChildView object destroyed without calling Destroy()");
434   // An nsChildView object that was in use can be destroyed without Destroy()
435   // ever being called on it.  So we also need to do a quick, safe cleanup
436   // here (it's too late to just call Destroy(), which can cause crashes).
437   // It's particularly important to make sure widgetDestroyed is called on our
438   // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
439   // mGeckoChild are used throughout the ChildView class to tell if it's safe
440   // to use a ChildView object.
441   [mView widgetDestroyed]; // Safe if mView is nil.
442   mParentWidget = nil;
443   TearDownView(); // Safe if called twice.
447 NS_IMPL_ISUPPORTS_INHERITED1(nsChildView, nsBaseWidget, nsIPluginWidget)
450 // Utility method for implementing both Create(nsIWidget ...)
451 // and Create(nsNativeWidget...)
452 nsresult nsChildView::StandardCreate(nsIWidget *aParent,
453                       const nsRect &aRect,
454                       EVENT_CALLBACK aHandleEventFunction,
455                       nsIDeviceContext *aContext,
456                       nsIAppShell *aAppShell,
457                       nsIToolkit *aToolkit,
458                       nsWidgetInitData *aInitData,
459                       nsNativeWidget aNativeParent)
461   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
463   // See NSWindow (MethodSwizzling) in nsCocoaWindow.mm.
464   if (!gCocoaWindowMethodsSwizzled) {
465     nsToolkit::SwizzleMethods([NSWindow class], @selector(sendEvent:),
466                               @selector(nsCocoaWindow_NSWindow_sendEvent:));
467     gCocoaWindowMethodsSwizzled = PR_TRUE;
468   }
470   mBounds = aRect;
472   BaseCreate(aParent, aRect, aHandleEventFunction, 
473               aContext, aAppShell, aToolkit, aInitData);
475   // inherit things from the parent view and create our parallel 
476   // NSView in the Cocoa display system
477   mParentView = nil;
478   if (aParent) {
479     SetBackgroundColor(aParent->GetBackgroundColor());
480     SetForegroundColor(aParent->GetForegroundColor());
482     // inherit the top-level window. NS_NATIVE_WIDGET is always a NSView
483     // regardless of if we're asking a window or a view (for compatibility
484     // with windows).
485     mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET); 
486     mParentWidget = aParent;   
487   }
488   else
489     mParentView = reinterpret_cast<NSView*>(aNativeParent);
490   
491   // create our parallel NSView and hook it up to our parent. Recall
492   // that NS_NATIVE_WIDGET is the NSView.
493   NSRect r;
494   GeckoRectToNSRect(mBounds, r);
495   mView = [CreateCocoaView(r) retain];
496   if (!mView) return NS_ERROR_FAILURE;
497   
498 #if DEBUG
499   // if our parent is a popup window, we're most certainly coming from a <select> list dropdown which
500   // we handle in a different way than other platforms. It's ok that we don't have a parent
501   // view because we bailed before even creating the cocoa widgetry and as a result, we
502   // don't need to assert. However, if that's not the case, we definitely want to assert
503   // to show views aren't getting correctly parented.
504   if (aParent) {
505     nsWindowType windowType;
506     aParent->GetWindowType(windowType);
507     if (windowType != eWindowType_popup)
508       NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
509   }
510   else {
511     NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
512   }
513 #endif
515   // If this view was created in a Gecko view hierarchy, the initial state
516   // is hidden.  If the view is attached only to a native NSView but has
517   // no Gecko parent (as in embedding), the initial state is visible.
518   if (mParentWidget)
519     [mView setHidden:YES];
520   else
521     mVisible = PR_TRUE;
523   // Hook it up in the NSView hierarchy.
524   if (mParentView) {
525     NSWindow* window = [mParentView window];
526     if (!window &&
527         [mParentView respondsToSelector:@selector(nativeWindow)])
528       window = [mParentView nativeWindow];
530     [mView setNativeWindow:window];
532     [mParentView addSubview:mView];
533   }
535   // if this is a ChildView, make sure that our per-window data
536   // is set up
537   if ([mView isKindOfClass:[ChildView class]])
538     [(ChildView*)mView ensureWindowData];
540   return NS_OK;
542   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
546 // Creates the appropriate child view. Override to create something other than
547 // our |ChildView| object. Autoreleases, so caller must retain.
548 NSView*
549 nsChildView::CreateCocoaView(NSRect inFrame)
551   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
553   return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this] autorelease];
555   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
559 void nsChildView::TearDownView()
561   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
563   if (!mView)
564     return;
566   NSWindow* win = [mView window];
567   NSResponder* responder = [win firstResponder];
568   
569   // We're being unhooked from the view hierarchy, don't leave our view
570   // or a child view as the window first responder.
571   if (responder && [responder isKindOfClass:[NSView class]] &&
572       [(NSView*)responder isDescendantOf:mView]) {
573     [win makeFirstResponder:[mView superview]];
574   }
576   // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
577   // win has retained mView, and will detach it from the view hierarchy and
578   // release it when necessary (when win is itself destroyed (in a call to
579   // [win dealloc])).  So all we need to do here is call [mView release] (to
580   // match the call to [mView retain] in nsChildView::StandardCreate()).
581   // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
582   // mView to be released again and dealloced, while remaining win's
583   // contentView.  So if we do that here, win will (for a short while) have
584   // an invalid contentView (for the consequences see bmo bugs 381087 and
585   // 374260).
586   if ([mView isEqual:[win contentView]]) {
587     [mView release];
588   } else {
589     // Stop NSView hierarchy being changed during [ChildView drawRect:]
590     [mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
591   }
592   mView = nil;
594   NS_OBJC_END_TRY_ABORT_BLOCK;
598 // create a nsChildView
599 NS_IMETHODIMP nsChildView::Create(nsIWidget *aParent,
600                       const nsRect &aRect,
601                       EVENT_CALLBACK aHandleEventFunction,
602                       nsIDeviceContext *aContext,
603                       nsIAppShell *aAppShell,
604                       nsIToolkit *aToolkit,
605                       nsWidgetInitData *aInitData)
606 {  
607   return(StandardCreate(aParent, aRect, aHandleEventFunction, aContext,
608                         aAppShell, aToolkit, aInitData, nsnull));
612 // Creates a main nsChildView using a native widget (an NSView)
613 NS_IMETHODIMP nsChildView::Create(nsNativeWidget aNativeParent,
614                       const nsRect &aRect,
615                       EVENT_CALLBACK aHandleEventFunction,
616                       nsIDeviceContext *aContext,
617                       nsIAppShell *aAppShell,
618                       nsIToolkit *aToolkit,
619                       nsWidgetInitData *aInitData)
621   // what we're passed in |aNativeParent| is an NSView. 
622   return(StandardCreate(nsnull, aRect, aHandleEventFunction, aContext,
623                         aAppShell, aToolkit, aInitData, aNativeParent));
627 NS_IMETHODIMP nsChildView::Destroy()
629   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
631   if (mOnDestroyCalled)
632     return NS_OK;
633   mOnDestroyCalled = PR_TRUE;
635   [mView widgetDestroyed];
637   nsBaseWidget::OnDestroy();
638   nsBaseWidget::Destroy();
640   ReportDestroyEvent(); 
641   mParentWidget = nil;
643   TearDownView();
645   return NS_OK;
647   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
651 #pragma mark -
654 #if 0
655 static void PrintViewHierarchy(NSView *view)
657   while (view) {
658     NSLog(@"  view is %x, frame %@", view, NSStringFromRect([view frame]));
659     view = [view superview];
660   }
662 #endif
666 // Return native data according to aDataType
667 void* nsChildView::GetNativeData(PRUint32 aDataType)
669   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
671   void* retVal = nsnull;
673   switch (aDataType) 
674   {
675     case NS_NATIVE_WIDGET:
676     case NS_NATIVE_DISPLAY:
677       retVal = (void*)mView;
678       break;
680     case NS_NATIVE_WINDOW:
681       retVal = [mView nativeWindow];
682       break;
684     case NS_NATIVE_GRAPHIC:
685       NS_ASSERTION(0, "Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
686       retVal = nsnull;
687       break;
689     case NS_NATIVE_OFFSETX:
690       retVal = 0;
691       break;
693     case NS_NATIVE_OFFSETY:
694       retVal = 0;
695       break;
697     case NS_NATIVE_PLUGIN_PORT:
698 #ifndef NP_NO_QUICKDRAW
699     case NS_NATIVE_PLUGIN_PORT_QD:
700     {
701       mPluginIsCG = PR_FALSE;
702       mIsPluginView = PR_TRUE;
703       if ([mView isKindOfClass:[ChildView class]])
704         [(ChildView*)mView setIsPluginView:YES];
706       NSWindow* window = [mView nativeWindow];
707       if (window) {
708         WindowRef topLevelWindow = (WindowRef)[window windowRef];
709         if (topLevelWindow) {
710           mPluginPort.qdPort.port = ::GetWindowPort(topLevelWindow);
712           NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
713           NSRect frame = [[window contentView] frame];
714           viewOrigin.y = frame.size.height - viewOrigin.y;
715           
716           // need to convert view's origin to window coordinates.
717           // then, encode as "SetOrigin" ready values.
718           mPluginPort.qdPort.portx = (PRInt32)-viewOrigin.x;
719           mPluginPort.qdPort.porty = (PRInt32)-viewOrigin.y;
720         }
721       }
723       retVal = (void*)&mPluginPort;
724       break;
725     }
726 #endif
728     case NS_NATIVE_PLUGIN_PORT_CG:
729     {
730       mPluginIsCG = PR_TRUE;
731       mIsPluginView = PR_TRUE;
732       if ([mView isKindOfClass:[ChildView class]])
733         [(ChildView*)mView setIsPluginView:YES];
735       mPluginPort.cgPort.context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
737       NSWindow* window = [mView nativeWindow];
738       if (window) {
739         WindowRef topLevelWindow = (WindowRef)[window windowRef];
740         mPluginPort.cgPort.window = topLevelWindow;
741       }
743       retVal = (void*)&mPluginPort;
744       break;
745     }
746   }
748   return retVal;
750   NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
753 #pragma mark -
755 nsTransparencyMode nsChildView::GetTransparencyMode()
757   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
759   return [mView isOpaque] ? eTransparencyOpaque : eTransparencyTransparent;
761   NS_OBJC_END_TRY_ABORT_BLOCK;
762   return eTransparencyOpaque;
766 // This is called by nsContainerFrame on the root widget for all window types
767 // except popup windows (when nsCocoaWindow::SetTransparencyMode is used instead).
768 void nsChildView::SetTransparencyMode(nsTransparencyMode aMode)
770   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
772   BOOL isTransparent = aMode == eTransparencyTransparent;
773   BOOL currentTransparency = ![[mView nativeWindow] isOpaque];
774   if (isTransparent != currentTransparency) {
775     // Find out if this is a window we created by seeing if the delegate is WindowDelegate. If it is,
776     // tell the nsCocoaWindow to set its background to transparent.
777     id windowDelegate = [[mView nativeWindow] delegate];
778     if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
779       nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
780       if (widget) {
781         widget->MakeBackgroundTransparent(aMode);
782         [(ChildView*)mView setTransparent:isTransparent];
783       }
784     }
785   }
787   NS_OBJC_END_TRY_ABORT_BLOCK;
791 NS_IMETHODIMP nsChildView::IsVisible(PRBool& outState)
793   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
795   if (!mVisible) {
796     outState = mVisible;
797   }
798   else {
799     // mVisible does not accurately reflect the state of a hidden tabbed view
800     // so verify that the view has a window as well
801     outState = ([mView window] != nil);
802     // now check native widget hierarchy visibility
803     if (outState && NSIsEmptyRect([mView visibleRect])) {
804       outState = PR_FALSE;
805     }
806   }
808   return NS_OK;
810   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
814 void nsChildView::HidePlugin()
816   NS_ASSERTION(mIsPluginView, "HidePlugin called on non-plugin view");
818   if (mPluginInstanceOwner && !mPluginIsCG) {
819     nsPluginWindow* window;
820     mPluginInstanceOwner->GetWindow(window);
821     nsCOMPtr<nsIPluginInstance> instance;
822     mPluginInstanceOwner->GetInstance(*getter_AddRefs(instance));
823     if (window && instance) {
824        window->clipRect.top = 0;
825        window->clipRect.left = 0;
826        window->clipRect.bottom = 0;
827        window->clipRect.right = 0;
828        instance->SetWindow(window);
829     }
830   }
834 static void HideChildPluginViews(NSView* aView)
836   NSArray* subviews = [aView subviews];
838   for (unsigned int i = 0; i < [subviews count]; ++i) {
839     NSView* view = [subviews objectAtIndex: i];
841     if (![view isKindOfClass:[ChildView class]])
842       continue;
844     ChildView* childview = static_cast<ChildView*>(view);
845     if ([childview isPluginView]) {
846       nsChildView* widget = static_cast<nsChildView*>([childview widget]);
847       if (widget) {
848         widget->HidePlugin();
849       }
850     } else {
851       HideChildPluginViews(view);
852     }
853   }
857 // Hide or show this component
858 NS_IMETHODIMP nsChildView::Show(PRBool aState)
860   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
862   if (aState != mVisible) {
863     [mView setHidden:!aState];
864     mVisible = aState;
865     if (!mVisible)
866       HideChildPluginViews(mView);
867   }
868   return NS_OK;
870   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
874 nsIWidget*
875 nsChildView::GetParent(void)
877   return mParentWidget;
881 NS_IMETHODIMP nsChildView::ModalEventFilter(PRBool aRealEvent, void *aEvent,
882                                             PRBool *aForWindow)
884   if (aForWindow)
885     *aForWindow = PR_FALSE;
886   return NS_ERROR_NOT_IMPLEMENTED;
890 NS_IMETHODIMP nsChildView::Enable(PRBool aState)
892   return NS_OK;
896 NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
898   // unimplemented
899   if (aState)
900    *aState = PR_TRUE;
901   return NS_OK;
905 NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
907   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
909   // Don't do anything if SetFocus() has been called reentrantly on the same
910   // object.  Sometimes calls to nsChildView::DispatchEvent() can get
911   // temporarily stuck, causing calls to [ChildView sendFocusEvent:] and
912   // SetFocus() to be reentered.  These reentrant calls are probably the
913   // result of one or more bugs, and doing things on a reentrant call can
914   // cause problems:  For example if mView is already the first responder and
915   // we send it an NS_GOTFOCUS event (see below), this causes the Mochitests
916   // to get stuck in the toolkit/content/tests/widgets/test_popup_button.xul
917   // test.
918   if (mInSetFocus)
919     return NS_OK;
920   mInSetFocus = PR_TRUE;
921   NSWindow* window = [mView window];
922   if (window) {
923     nsAutoRetainCocoaObject kungFuDeathGrip(mView);
924     // For reasons that aren't yet clear, focus changes within a window (as
925     // opposed to those between windows or between apps) should only trigger
926     // NS_LOSTFOCUS and NS_GOTFOCUS events (sent to Gecko) in the context of
927     // a call to nsChildView::SetFocus() (or nsCocoaWindow::SetFocus(), which
928     // in any case re-routes to nsChildView::SetFocus()).  If we send these
929     // events on every intra-window focus change (on every call to
930     // [ChildView becomeFirstResponder:] or [ChildView resignFirstResponder:]),
931     // the result will be strange focus bugs (like bmo bugs 399471, 403232,
932     // 404433 and 408266).
933     NSResponder* firstResponder = [window firstResponder];
934     if ([mView isEqual:firstResponder]) {
935       // Sometimes SetFocus() is called on an nsChildView object that's
936       // already focused.  In principle this shouldn't happen, and in any
937       // case we shouldn't have to dispatch any events.  But if we don't, we
938       // sometimes get text-input cursors blinking in more than one text
939       // field, or still blinking when the browser is no longer active.  For
940       // reasons that aren't at all clear, this problem can be avoided by
941       // always sending an NS_GOTFOCUS message here.
942       if ([mView isKindOfClass:[ChildView class]])
943         [(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
944     } else {
945       // Retain and release firstResponder around the call to
946       // makeFirstResponder.
947       [firstResponder retain];
948       if ([window makeFirstResponder:mView]) {
949         if ([firstResponder isKindOfClass:[ChildView class]])
950           [(ChildView *)firstResponder sendFocusEvent:NS_LOSTFOCUS];
951         if ([mView isKindOfClass:[ChildView class]])
952           [(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
953       }
954       [firstResponder release];
955     }
956   }
957   mInSetFocus = PR_FALSE;
958   return NS_OK;
960   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
964 // Set the colormap of the window
965 NS_IMETHODIMP nsChildView::SetColorMap(nsColorMap *aColorMap)
967   return NS_OK;
971 NS_IMETHODIMP nsChildView::SetMenuBar(void* aMenuBar)
973   return NS_ERROR_FAILURE; // subviews don't have menu bars
977 NS_IMETHODIMP nsChildView::ShowMenuBar(PRBool aShow)
979   return NS_ERROR_FAILURE; // subviews don't have menu bars
983 // Override to set the cursor on the mac
984 NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
986   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
988   nsBaseWidget::SetCursor(aCursor);
989   [[nsCursorManager sharedInstance] setCursor: aCursor];
990   return NS_OK;
992   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
996 // implement to fix "hidden virtual function" warning
997 NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
998                                       PRUint32 aHotspotX, PRUint32 aHotspotY)
1000   return nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
1004 #pragma mark -
1007 // Get this component dimension
1008 NS_IMETHODIMP nsChildView::GetBounds(nsRect &aRect)
1010   aRect = mBounds;
1011   return NS_OK;
1015 NS_IMETHODIMP nsChildView::SetBounds(const nsRect &aRect)
1017   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1019   nsresult rv = Inherited::SetBounds(aRect);
1020   if (NS_SUCCEEDED(rv)) {
1021     NSRect r;
1022     GeckoRectToNSRect(aRect, r);
1023     [mView setFrame:r];
1024   }
1026   return rv;
1028   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1032 NS_IMETHODIMP nsChildView::ConstrainPosition(PRBool aAllowSlop,
1033                                              PRInt32 *aX, PRInt32 *aY)
1035   return NS_OK;
1039 // Move this component, aX and aY are in the parent widget coordinate system
1040 NS_IMETHODIMP nsChildView::Move(PRInt32 aX, PRInt32 aY)
1042   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1044   if (!mView || (mBounds.x == aX && mBounds.y == aY))
1045     return NS_OK;
1047   mBounds.x = aX;
1048   mBounds.y = aY;
1050   NSRect r;
1051   GeckoRectToNSRect(mBounds, r);
1052   [mView setFrame:r];
1054   if (mVisible)
1055     [mView setNeedsDisplay:YES];
1057   ReportMoveEvent();
1059   return NS_OK;
1061   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1065 NS_IMETHODIMP nsChildView::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1067   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1069   if (!mView || (mBounds.width == aWidth && mBounds.height == aHeight))
1070     return NS_OK;
1072   mBounds.width  = aWidth;
1073   mBounds.height = aHeight;
1075   NSRect r;
1076   GeckoRectToNSRect(mBounds, r);
1077   [mView setFrame:r];
1079   if (mVisible && aRepaint)
1080     [mView setNeedsDisplay:YES];
1082   ReportSizeEvent();
1084   return NS_OK;
1086   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1090 NS_IMETHODIMP nsChildView::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1092   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1094   BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
1095   BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
1096   if (!mView || (!isMoving && !isResizing))
1097     return NS_OK;
1099   if (isMoving) {
1100     mBounds.x = aX;
1101     mBounds.y = aY;
1102   }
1103   if (isResizing) {
1104     mBounds.width  = aWidth;
1105     mBounds.height = aHeight;
1106   }
1108   NSRect r;
1109   GeckoRectToNSRect(mBounds, r);
1110   [mView setFrame:r];
1112   if (mVisible && aRepaint)
1113     [mView setNeedsDisplay:YES];
1115   if (isMoving) {
1116     ReportMoveEvent();
1117     if (mOnDestroyCalled)
1118       return NS_OK;
1119   }
1120   if (isResizing)
1121     ReportSizeEvent();
1123   return NS_OK;
1125   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1129 NS_METHOD nsChildView::GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight)
1131   return NS_ERROR_FAILURE; // nobody call this anywhere in the code
1135 NS_METHOD nsChildView::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
1137   return NS_ERROR_FAILURE; // nobody call this anywhere in the code
1141 NS_IMETHODIMP nsChildView::BeginResizingChildren(void)
1143   return NS_OK;
1147 NS_IMETHODIMP nsChildView::EndResizingChildren(void)
1149   return NS_OK;
1153 static const PRInt32 resizeIndicatorWidth = 15;
1154 static const PRInt32 resizeIndicatorHeight = 15;
1155 PRBool nsChildView::ShowsResizeIndicator(nsIntRect* aResizerRect)
1157   NSView *topLevelView = mView, *superView = nil;
1158   while ((superView = [topLevelView superview]))
1159     topLevelView = superView;
1161   if (![[topLevelView window] showsResizeIndicator])
1162     return PR_FALSE;
1164   if (aResizerRect) {
1165     NSSize bounds = [topLevelView bounds].size;
1166     NSPoint corner = NSMakePoint(bounds.width, [topLevelView isFlipped] ? bounds.height : 0);
1167     corner = [topLevelView convertPoint:corner toView:mView];
1168     aResizerRect->SetRect(NSToIntRound(corner.x) - resizeIndicatorWidth,
1169                           NSToIntRound(corner.y) - resizeIndicatorHeight,
1170                           resizeIndicatorWidth, resizeIndicatorHeight);
1171   }
1172   return PR_TRUE;
1176 NS_IMETHODIMP nsChildView::GetPluginClipRect(nsRect& outClipRect, nsPoint& outOrigin, PRBool& outWidgetVisible)
1178   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1180   NS_ASSERTION(mIsPluginView, "GetPluginClipRect must only be called on a plugin widget");
1181   if (!mIsPluginView) return NS_ERROR_FAILURE;
1182   
1183   NSWindow* window = [mView nativeWindow];
1184   if (!window) return NS_ERROR_FAILURE;
1185   
1186   NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
1187   NSRect frame = [[window contentView] frame];
1188   viewOrigin.y = frame.size.height - viewOrigin.y;
1189   
1190   // set up the clipping region for plugins.
1191   NSRect visibleBounds = [mView visibleRect];
1192   NSPoint clipOrigin   = [mView convertPoint:visibleBounds.origin toView:nil];
1193   
1194   // Convert from cocoa to QuickDraw coordinates
1195   clipOrigin.y = frame.size.height - clipOrigin.y;
1196   
1197   outClipRect.x = (nscoord)clipOrigin.x;
1198   outClipRect.y = (nscoord)clipOrigin.y;
1199   
1200   
1201   PRBool isVisible;
1202   IsVisible(isVisible);
1203   if (isVisible && [mView window] != nil) {
1204     outClipRect.width  = (nscoord)visibleBounds.size.width;
1205     outClipRect.height = (nscoord)visibleBounds.size.height;
1206     outWidgetVisible = PR_TRUE;
1207   }
1208   else {
1209     outClipRect.width = 0;
1210     outClipRect.height = 0;
1211     outWidgetVisible = PR_FALSE;
1212   }
1214   // need to convert view's origin to window coordinates.
1215   // then, encode as "SetOrigin" ready values.
1216   outOrigin.x = (nscoord)-viewOrigin.x;
1217   outOrigin.y = (nscoord)-viewOrigin.y;
1218   
1219   return NS_OK;
1221   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1225 NS_IMETHODIMP nsChildView::StartDrawPlugin()
1227   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1229   NS_ASSERTION(mIsPluginView, "StartDrawPlugin must only be called on a plugin widget");
1230   if (!mIsPluginView) return NS_ERROR_FAILURE;
1232   // Prevent reentrant "drawing" (or in fact reentrant handling of any plugin
1233   // event).  Doing this for both CoreGraphics and QuickDraw plugins restores
1234   // the 1.8-branch behavior wrt reentrancy, and fixes (or works around) bugs
1235   // caused by plugins depending on the old behavior -- e.g. bmo bug 409615.
1236   if (mPluginDrawing)
1237     return NS_ERROR_FAILURE;
1239   NSWindow* window = [mView nativeWindow];
1240   if (!window)
1241     return NS_ERROR_FAILURE;
1242   
1243   // It appears that the WindowRef from which we get the plugin port undergoes the
1244   // traditional BeginUpdate/EndUpdate cycle, which, if you recall, sets the visible
1245   // region to the intersection of the visible region and the update region. Since
1246   // we don't know here if we're being drawn inside a BeginUpdate/EndUpdate pair
1247   // (which seem to occur in [NSWindow display]), and we don't want to have the burden
1248   // of correctly doing Carbon invalidates of the plugin rect, we manually set the
1249   // visible region to be the entire port every time. It is necessary to set up our
1250   // window's port even for CoreGraphics plugins, because they may still use Carbon
1251   // internally (see bug #420527 for details).
1252   CGrafPtr port = ::GetWindowPort(WindowRef([window windowRef]));
1253   if (!mPluginIsCG)
1254     port = mPluginPort.qdPort.port;
1256   RgnHandle pluginRegion = ::NewRgn();
1257   if (pluginRegion) {
1258     PRBool portChanged = (port != CGrafPtr(GetQDGlobalsThePort()));
1259     CGrafPtr oldPort;
1260     GDHandle oldDevice;
1262     if (portChanged) {
1263       ::GetGWorld(&oldPort, &oldDevice);
1264       ::SetGWorld(port, ::IsPortOffscreen(port) ? nsnull : ::GetMainDevice());
1265     }
1267     ::SetOrigin(0, 0);
1268     
1269     nsRect clipRect; // this is in native window coordinates
1270     nsPoint origin;
1271     PRBool visible;
1272     GetPluginClipRect(clipRect, origin, visible);
1273     
1274     // XXX if we're not visible, set an empty clip region?
1275     Rect pluginRect;
1276     ConvertGeckoRectToMacRect(clipRect, pluginRect);
1277     
1278     ::RectRgn(pluginRegion, &pluginRect);
1279     ::SetPortVisibleRegion(port, pluginRegion);
1280     ::SetPortClipRegion(port, pluginRegion);
1281     
1282     // now set up the origin for the plugin
1283     ::SetOrigin(origin.x, origin.y);
1284     
1285     ::DisposeRgn(pluginRegion);
1287     if (portChanged)
1288       ::SetGWorld(oldPort, oldDevice);
1289   }
1291   mPluginDrawing = PR_TRUE;
1292   return NS_OK;
1294   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1298 NS_IMETHODIMP nsChildView::EndDrawPlugin()
1300   NS_ASSERTION(mIsPluginView, "EndDrawPlugin must only be called on a plugin widget");
1301   if (!mIsPluginView) return NS_ERROR_FAILURE;
1303   mPluginDrawing = PR_FALSE;
1304   return NS_OK;
1308 NS_IMETHODIMP nsChildView::SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner)
1310   mPluginInstanceOwner = aInstanceOwner;
1312   return NS_OK;
1316 void nsChildView::LiveResizeStarted()
1318   // XXX todo. Use this to disable Java async redraw during resize
1319   mLiveResizeInProgress = PR_TRUE;
1323 void nsChildView::LiveResizeEnded()
1325   mLiveResizeInProgress = PR_FALSE;
1328 static NSString* ToNSString(const nsAString& aString)
1330   return [NSString stringWithCharacters:aString.BeginReading()
1331                                  length:aString.Length()];
1334 struct KeyboardLayoutOverride {
1335   PRInt32 mKeyboardLayout;
1336   PRBool mOverrideEnabled;
1339 static KeyboardLayoutOverride gOverrideKeyboardLayout;
1341 static const PRUint32 sModifierFlagMap[][2] = {
1342   { nsIWidget::CAPS_LOCK, NSAlphaShiftKeyMask },
1343   { nsIWidget::SHIFT_L, NSShiftKeyMask },
1344   { nsIWidget::CTRL_L, NSControlKeyMask },
1345   { nsIWidget::ALT_L, NSAlternateKeyMask },
1346   { nsIWidget::COMMAND, NSCommandKeyMask },
1347   { nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask },
1348   { nsIWidget::HELP, NSHelpKeyMask },
1349   { nsIWidget::FUNCTION, NSFunctionKeyMask }
1351 nsresult nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
1352                                                PRInt32 aNativeKeyCode,
1353                                                PRUint32 aModifierFlags,
1354                                                const nsAString& aCharacters,
1355                                                const nsAString& aUnmodifiedCharacters)
1357   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1358   
1359   PRUint32 modifierFlags = 0;
1360   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sModifierFlagMap); ++i) {
1361     if (aModifierFlags & sModifierFlagMap[i][0]) {
1362       modifierFlags |= sModifierFlagMap[i][1];
1363     }
1364   }
1365   int windowNumber = [[mView window] windowNumber];
1366   NSEvent* downEvent = [NSEvent keyEventWithType:NSKeyDown
1367                                         location:NSMakePoint(0,0)
1368                                    modifierFlags:modifierFlags
1369                                        timestamp:0
1370                                     windowNumber:windowNumber
1371                                          context:[NSGraphicsContext currentContext]
1372                                       characters:ToNSString(aCharacters)
1373                      charactersIgnoringModifiers:ToNSString(aUnmodifiedCharacters)
1374                                        isARepeat:NO
1375                                          keyCode:aNativeKeyCode];
1377   NSEvent* upEvent = [ChildView makeNewCocoaEventWithType:NSKeyUp
1378                                                 fromEvent:downEvent];
1380   if (downEvent && upEvent) {
1381     KeyboardLayoutOverride currentLayout = gOverrideKeyboardLayout;
1382     gOverrideKeyboardLayout.mKeyboardLayout = aNativeKeyboardLayout;
1383     gOverrideKeyboardLayout.mOverrideEnabled = PR_TRUE;
1384     [NSApp sendEvent:downEvent];
1385     [NSApp sendEvent:upEvent];
1386     // processKeyDownEvent and keyUp block exceptions so we're sure to
1387     // reach here to restore gOverrideKeyboardLayout
1388     gOverrideKeyboardLayout = currentLayout;
1389   }
1391   return NS_OK;
1393   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1396 // Used for testing native menu system structure and event handling.
1397 NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString)
1399   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1401   NSString* title = [NSString stringWithCharacters:indexString.BeginReading() length:indexString.Length()];
1402   NSArray* indexes = [title componentsSeparatedByString:@"|"];
1403   unsigned int indexCount = [indexes count];
1404   if (indexCount == 0)
1405     return NS_OK;
1406   
1407   NSMenu* currentSubmenu = [NSApp mainMenu];
1408   for (unsigned int i = 0; i < (indexCount - 1); i++) {
1409     NSMenu* newSubmenu = nil;
1410     int targetIndex;
1411     // We remove the application menu from consideration for the top-level menu
1412     if (i == 0)
1413       targetIndex = [[indexes objectAtIndex:i] intValue] + 1;
1414     else
1415       targetIndex = [[indexes objectAtIndex:i] intValue];
1416     int itemCount = [currentSubmenu numberOfItems];
1417     if (targetIndex < itemCount) {
1418       NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
1419       if ([menuItem hasSubmenu])
1420         newSubmenu = [menuItem submenu];
1421     }
1422     
1423     if (newSubmenu)
1424       currentSubmenu = newSubmenu;
1425     else
1426       return NS_ERROR_FAILURE;
1427   }
1429   int itemCount = [currentSubmenu numberOfItems];
1430   int targetIndex = [[indexes objectAtIndex:(indexCount - 1)] intValue];
1431   // We can't perform an action on an item with a submenu, that will raise
1432   // an obj-c exception.
1433   if (targetIndex < itemCount && ![[currentSubmenu itemAtIndex:targetIndex] hasSubmenu]) {
1434       // NSLog(@"Performing action for native menu item titled: %@\n",
1435       //       [[currentSubmenu itemAtIndex:targetIndex] title]);
1436       [currentSubmenu performActionForItemAtIndex:targetIndex];      
1437   }
1438   else {
1439     return NS_ERROR_FAILURE;
1440   }
1442   return NS_OK;
1444   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1448 NS_IMETHODIMP nsChildView::ForceNativeMenuReload()
1450   id windowDelegate = [[mView nativeWindow] delegate];
1451   if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
1452     nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
1453     if (widget) {
1454       nsMenuBarX* mb = widget->GetMenuBar();
1455       if (mb) {
1456         mb->ForceNativeMenuReload();
1457       }
1458     }
1459   }
1461   return NS_OK;
1465 #pragma mark -
1468 #ifdef INVALIDATE_DEBUGGING
1470 static Boolean KeyDown(const UInt8 theKey)
1472   KeyMap map;
1473   GetKeys(map);
1474   return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
1477 static Boolean caps_lock()
1479   return KeyDown(0x39);
1482 static void blinkRect(Rect* r)
1484   StRegionFromPool oldClip;
1485   if (oldClip != NULL)
1486     ::GetClip(oldClip);
1488   ::ClipRect(r);
1489   ::InvertRect(r);
1490   UInt32 end = ::TickCount() + 5;
1491   while (::TickCount() < end) ;
1492   ::InvertRect(r);
1494   if (oldClip != NULL)
1495     ::SetClip(oldClip);
1498 static void blinkRgn(RgnHandle rgn)
1500   StRegionFromPool oldClip;
1501   if (oldClip != NULL)
1502     ::GetClip(oldClip);
1504   ::SetClip(rgn);
1505   ::InvertRgn(rgn);
1506   UInt32 end = ::TickCount() + 5;
1507   while (::TickCount() < end) ;
1508   ::InvertRgn(rgn);
1510   if (oldClip != NULL)
1511     ::SetClip(oldClip);
1514 #endif
1517 // Invalidate this component's visible area
1518 NS_IMETHODIMP nsChildView::Invalidate(PRBool aIsSynchronous)
1520   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1522   if (!mView || !mVisible)
1523     return NS_OK;
1525   if (aIsSynchronous) {
1526     [mView display];
1527   }
1528   else if ([NSView focusView]) {
1529     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
1530     // don't lose it.
1531     [mView setNeedsPendingDisplay];
1532   }
1533   else {
1534     [mView setNeedsDisplay:YES];
1535   }
1537   return NS_OK;
1539   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1543 // Invalidate this component's visible area
1544 NS_IMETHODIMP nsChildView::Invalidate(const nsRect &aRect, PRBool aIsSynchronous)
1546   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1548   if (!mView || !mVisible)
1549     return NS_OK;
1551   NSRect r;
1552   GeckoRectToNSRect(aRect, r);
1553   
1554   if (aIsSynchronous) {
1555     [mView displayRect:r];
1556   }
1557   else if ([NSView focusView]) {
1558     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
1559     // don't lose it.
1560     [mView setNeedsPendingDisplayInRect:r];
1561   }
1562   else {
1563     [mView setNeedsDisplayInRect:r];
1564   }
1566   return NS_OK;
1568   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1572 // Validate the widget
1573 NS_IMETHODIMP nsChildView::Validate()
1575   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1577   [mView setNeedsDisplay:NO];
1578   return NS_OK;
1580   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1584 // Invalidate this component's visible area
1585 NS_IMETHODIMP nsChildView::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous)
1587   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1589   if (!mView || !mVisible)
1590     return NS_OK;
1592   // FIXME rewrite to use a Cocoa region when nsIRegion isn't a QD Region
1593   NSRect r;
1594   nsRect bounds;
1595   nsIRegion* region = const_cast<nsIRegion*>(aRegion);     // ugh. this method should be const
1596   region->GetBoundingBox(&bounds.x, &bounds.y, &bounds.width, &bounds.height);
1597   GeckoRectToNSRect(bounds, r);
1598   
1599   if (aIsSynchronous)
1600     [mView displayRect:r];
1601   else
1602     [mView setNeedsDisplayInRect:r];
1604   return NS_OK;
1606   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1610 inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8)
1612   // return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
1613   return (color8 << 8) | color8;  /* (color8 * 257) == (color8 * 0x0101) */
1617 // Dummy impl, meant to be overridden
1618 PRBool
1619 nsChildView::OnPaint(nsPaintEvent &event)
1621   return PR_TRUE;
1625 // this is handled for us by UpdateWidget
1626 NS_IMETHODIMP nsChildView::Update()
1628   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1630   // Update means "Flush any pending changes right now."  It does *not* mean
1631   // repaint the world. :) -- dwh
1632   [mView displayIfNeeded];
1633   return NS_OK;
1635   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1639 #pragma mark -
1642 // Scroll the bits of a view and its children
1643 // FIXME: I'm sure the invalidating can be optimized, just no time now.
1644 NS_IMETHODIMP nsChildView::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
1646   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1648   BOOL viewWasDirty = NO;
1649   if (mVisible) {
1650     viewWasDirty = [mView needsDisplay];
1652     NSSize scrollVector = {aDx,aDy};
1653     [mView scrollRect: [mView visibleRect] by:scrollVector];
1654   }
1655   
1656   // Scroll the children (even if the widget is not visible)
1657   for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
1658     // We use resize rather than move since it gives us control
1659     // over repainting.  We can scroll like a bat out of hell
1660     // by not wasting time invalidating the widgets, since it's
1661     // completely unnecessary to do so.
1662     nsRect bounds;
1663     kid->GetBounds(bounds);
1664     kid->Resize(bounds.x + aDx, bounds.y + aDy, bounds.width, bounds.height, PR_FALSE);
1665   }
1667   if (mOnDestroyCalled)
1668     return NS_OK;
1670   if (mVisible) {
1671     if (viewWasDirty) {
1672       [mView setNeedsDisplay:YES];
1673     }
1674     else {
1675       NSRect frame = [mView visibleRect];
1676       NSRect horizInvalid = frame;
1677       NSRect vertInvalid = frame;
1678   
1679       if (aDx != 0) {
1680         horizInvalid.size.width = abs(aDx);
1681         if (aDx < 0)
1682           horizInvalid.origin.x = frame.origin.x + frame.size.width + aDx;
1683         [mView setNeedsDisplayInRect: horizInvalid];
1684       }
1686       if (aDy != 0) {
1687         vertInvalid.size.height = abs(aDy);
1688         if (aDy < 0)
1689           vertInvalid.origin.y = frame.origin.y + frame.size.height + aDy;
1690         [mView setNeedsDisplayInRect: vertInvalid];
1691       }
1693       // We also need to check for any ChildViews which overlap this widget
1694       // but are not descendent widgets.  If there are any, we need to
1695       // invalidate the area of this view that these ChildViews will have been
1696       // blitted into, since these widgets aren't supposed to scroll with
1697       // this widget.
1699       // To do this, start at the root Gecko NSView, and walk down along
1700       // our ancestor view chain, looking at all the subviews in each level
1701       // of the hierarchy.  If we find a non-ancestor view that overlaps
1702       // this view, invalidate the area around it.
1704       // We need to convert all rects to a common ancestor view to intersect
1705       // them, since a view's frame is in the coordinate space of its parent.
1706       // Use mParentView as the frame of reference.
1707       NSRect selfFrame = [mParentView convertRect:[mView frame] fromView:[mView superview]];
1708       NSView* view = mParentView;
1709       BOOL selfLevel = NO;
1711       while (!selfLevel) {
1712         NSView* nextAncestorView = nil;
1713         NSArray* subviews = [view subviews];
1714         for (unsigned int i = 0; i < [subviews count]; ++i) {
1715           NSView* subView = [subviews objectAtIndex: i];
1716           if (subView == mView)
1717             selfLevel = YES;
1718           else if ([mView isDescendantOf:subView])
1719             nextAncestorView = subView;
1720           else {
1721             NSRect intersectArea = NSIntersectionRect([mParentView convertRect:[subView frame] fromView:[subView superview]], selfFrame);
1722             if (!NSIsEmptyRect(intersectArea)) {
1723               NSPoint origin = [mView convertPoint:intersectArea.origin fromView:mParentView];
1725               if (aDy != 0) {
1726                 vertInvalid.origin.x = origin.x;
1727                 if (aDy < 0)  // scrolled down, invalidate above
1728                   vertInvalid.origin.y = origin.y + aDy;
1729                 else          // invalidate below
1730                   vertInvalid.origin.y = origin.y + intersectArea.size.height;
1731                 vertInvalid.size.width = intersectArea.size.width;
1732                 [mView setNeedsDisplayInRect: vertInvalid];
1733               }
1735               if (aDx != 0) {
1736                 horizInvalid.origin.y = origin.y;
1737                 if (aDx < 0)  // scrolled right, invalidate to the left
1738                   horizInvalid.origin.x = origin.x + aDx;
1739                 else          // invalidate to the right
1740                   horizInvalid.origin.x = origin.x + intersectArea.size.width;
1741                 horizInvalid.size.height = intersectArea.size.height;
1742                 [mView setNeedsDisplayInRect: horizInvalid];
1743               }
1744             }
1745           }
1746         }
1747         view = nextAncestorView;
1748       }
1749     }
1750   }
1751   
1752   // This is an evil hack that doesn't always work.
1753   // 
1754   // Drawing plugins in a Cocoa environment is tricky, because the
1755   // plugins are living in a Carbon WindowRef/BeginUpdate/EndUpdate
1756   // world, and Cocoa has its own notion of dirty rectangles. Throw
1757   // Quartz Extreme and QuickTime into the mix, and things get bad.
1758   // 
1759   // This code is working around a cosmetic issue seen when Quartz Extreme
1760   // is active, and you're scrolling a page with a QuickTime plugin; areas
1761   // outside the plugin fail to scroll properly. This [display] ensures that
1762   // the view is properly drawn before the next Scroll call.
1763   //
1764   // The time this doesn't work is when you're scrolling a page containing
1765   // an iframe which in turn contains a plugin.
1766   //
1767   // This is turned off because it makes scrolling pages with plugins slow.
1768   // 
1769   //if ([mView childViewHasPlugin])
1770   //  [mView display];
1772   return NS_OK;
1774   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1778 // Invokes callback and ProcessEvent methods on Event Listener object
1779 NS_IMETHODIMP nsChildView::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
1781 #ifdef DEBUG
1782   debug_DumpEvent(stdout, event->widget, event, nsCAutoString("something"), 0);
1783 #endif
1785   aStatus = nsEventStatus_eIgnore;
1787   nsCOMPtr<nsIWidget> kungFuDeathGrip(mParentWidget ? mParentWidget : this);
1788   if (mParentWidget) {
1789     nsWindowType type;
1790     mParentWidget->GetWindowType(type);
1791     if (type == eWindowType_popup) {
1792       // use the parent popup's widget if there is no view
1793       void* clientData = nsnull;
1794       if (event->widget)
1795         event->widget->GetClientData(clientData);
1796       if (!clientData)
1797         event->widget = mParentWidget;
1798     }
1799   }
1801   if (mEventCallback)
1802     aStatus = (*mEventCallback)(event);
1804   // dispatch to event listener if event was not consumed
1805   if (mEventListener && aStatus != nsEventStatus_eConsumeNoDefault)
1806     aStatus = mEventListener->ProcessEvent(*event);
1808   return NS_OK;
1812 PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event)
1814   nsEventStatus status;
1815   DispatchEvent(&event, status);
1816   return ConvertStatus(status);
1820 #pragma mark -
1823 PRBool nsChildView::ReportDestroyEvent()
1825   nsGUIEvent event(PR_TRUE, NS_DESTROY, this);
1826   event.time = PR_IntervalNow();
1827   return DispatchWindowEvent(event);
1831 PRBool nsChildView::ReportMoveEvent()
1833   nsGUIEvent moveEvent(PR_TRUE, NS_MOVE, this);
1834   moveEvent.refPoint.x = mBounds.x;
1835   moveEvent.refPoint.y = mBounds.y;
1836   moveEvent.time       = PR_IntervalNow();
1837   return DispatchWindowEvent(moveEvent);
1841 PRBool nsChildView::ReportSizeEvent()
1843   nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
1844   sizeEvent.time        = PR_IntervalNow();
1845   sizeEvent.windowSize  = &mBounds;
1846   sizeEvent.mWinWidth   = mBounds.width;
1847   sizeEvent.mWinHeight  = mBounds.height;
1848   return DispatchWindowEvent(sizeEvent);
1852 #pragma mark -
1855 /*  Calculate the x and y offsets for this particular widget
1856  *  @update  ps 09/22/98
1857  *  @param   aX -- x offset amount
1858  *  @param   aY -- y offset amount 
1859  *  @return  NOTHING
1860  */
1861 NS_IMETHODIMP nsChildView::CalcOffset(PRInt32 &aX,PRInt32 &aY)
1863   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1865   aX = aY = 0;
1866   NSRect bounds = {{0, 0}, {0, 0}};
1867   bounds = [mView convertRect:bounds toView:nil];
1868   aX += static_cast<PRInt32>(bounds.origin.x);
1869   aY += static_cast<PRInt32>(bounds.origin.y);
1871   return NS_OK;
1873   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1877 // Find if a point in local coordinates is inside this object
1878 PRBool nsChildView::PointInWidget(Point aThePoint)
1880   // get the origin in local coordinates
1881   nsPoint widgetOrigin(0, 0);
1882   LocalToWindowCoordinate(widgetOrigin);
1884   // get rectangle relatively to the parent
1885   nsRect widgetRect;
1886   GetBounds(widgetRect);
1888   // convert the topLeft corner to local coordinates
1889   widgetRect.MoveBy(widgetOrigin.x, widgetOrigin.y);
1891   // finally tell whether it's a hit
1892   return widgetRect.Contains(aThePoint.h, aThePoint.v);
1895 #pragma mark -
1898 //    Convert the given rect to global coordinates.
1899 //    @param aLocalRect  -- rect in local coordinates of this widget
1900 //    @param aGlobalRect -- |aLocalRect| in global coordinates
1901 NS_IMETHODIMP nsChildView::WidgetToScreen(const nsRect& aLocalRect, nsRect& aGlobalRect)
1903   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1905   NSRect temp;
1906   GeckoRectToNSRect(aLocalRect, temp);
1907   
1908   // 1. First translate this rect into window coords. The returned rect is always in
1909   //    bottom-left coordinates.
1910   //
1911   //    NOTE: convertRect:toView:nil doesn't care if |mView| is a flipped view (with
1912   //          top-left coords) and so assumes that our passed-in rect's origin is in
1913   //          bottom-left coordinates. We adjust this further down, by subtracting
1914   //          the final screen rect's origin by the rect's height, to get the origo
1915   //          where we want it.
1916   temp = [mView convertRect:temp toView:nil];  
1917   
1918   // 2. We turn the window-coord rect's origin into screen (still bottom-left) coords.
1919   temp.origin = [[mView nativeWindow] convertBaseToScreen:temp.origin];
1920   
1921   // 3. Since we're dealing in bottom-left coords, we need to make it top-left coords
1922   //    before we pass it back to Gecko.
1923   FlipCocoaScreenCoordinate(temp.origin);
1924   
1925   // 4. If this is rect has a size (and is not simply a point), it is important to account 
1926   //    for the fact that convertRect:toView:nil thought our passed-in point was in bottom-left 
1927   //    coords in step #1. Thus, we subtract the rect's height, to get the top-left rect's origin 
1928   //     where we want it.
1929   temp.origin.y -= temp.size.height;
1930   
1931   NSRectToGeckoRect(temp, aGlobalRect);
1932   return NS_OK;
1934   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1938 //    Convert the given rect to local coordinates.
1939 //    @param aGlobalRect  -- rect in screen coordinates 
1940 //    @param aLocalRect -- |aGlobalRect| in coordinates of this widget
1941 NS_IMETHODIMP nsChildView::ScreenToWidget(const nsRect& aGlobalRect, nsRect& aLocalRect)
1943   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1945   NSRect temp;
1946   GeckoRectToNSRect(aGlobalRect, temp);
1947   FlipCocoaScreenCoordinate(temp.origin);
1949   temp.origin = [[mView nativeWindow] convertScreenToBase:temp.origin];   // convert to screen coords
1950   temp = [mView convertRect:temp fromView:nil];                     // convert to window coords
1952   NSRectToGeckoRect(temp, aLocalRect);
1953   
1954   return NS_OK;
1956   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1960 // Convert the coordinates to some device coordinates so GFX can draw.
1961 void nsChildView::ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY)
1963   PRInt32 offX = 0, offY = 0;
1964   this->CalcOffset(offX,offY);
1966   aX += offX;
1967   aY += offY;
1971 NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener, 
1972                                                PRBool aDoCapture, 
1973                                                PRBool aConsumeRollupEvent)
1975   // this never gets called, only top-level windows can be rollup widgets
1976   return NS_OK;
1980 NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
1982   // child views don't have titles
1983   return NS_OK;
1987 NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
1989   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1991   [NSApp requestUserAttention:NSInformationalRequest];
1992   return NS_OK;
1994   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1998 #pragma mark -
2001 // Force Input Method Editor to commit the uncommited input
2002 // Note that this and other IME methods don't necessarily
2003 // get called on the same ChildView that input is going through.
2004 NS_IMETHODIMP nsChildView::ResetInputState()
2006 #ifdef DEBUG_IME
2007   NSLog(@"**** ResetInputState");
2008 #endif
2010   nsTSMManager::CommitIME();
2011   return NS_OK;
2015 // 'open' means that it can take non-ASCII chars
2016 NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
2018 #ifdef DEBUG_IME
2019   NSLog(@"**** SetIMEOpenState aState = %d", aState);
2020 #endif
2022   nsTSMManager::SetIMEOpenState(aState);
2023   return NS_OK;
2027 // 'open' means that it can take non-ASCII chars
2028 NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
2030 #ifdef DEBUG_IME
2031   NSLog(@"**** GetIMEOpenState");
2032 #endif
2034   *aState = nsTSMManager::GetIMEOpenState();
2035   return NS_OK;
2039 NS_IMETHODIMP nsChildView::SetIMEEnabled(PRUint32 aState)
2041 #ifdef DEBUG_IME
2042   NSLog(@"**** SetIMEEnabled aState = %d", aState);
2043 #endif
2045   switch (aState) {
2046     case nsIWidget::IME_STATUS_ENABLED:
2047       nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
2048       nsTSMManager::EnableIME(PR_TRUE);
2049       break;
2050     case nsIWidget::IME_STATUS_DISABLED:
2051       nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
2052       nsTSMManager::EnableIME(PR_FALSE);
2053       break;
2054     case nsIWidget::IME_STATUS_PASSWORD:
2055       nsTSMManager::SetRomanKeyboardsOnly(PR_TRUE);
2056       nsTSMManager::EnableIME(PR_FALSE);
2057       break;
2058     default:
2059       NS_ERROR("not implemented!");
2060   }
2061   return NS_OK;
2065 NS_IMETHODIMP nsChildView::GetIMEEnabled(PRUint32* aState)
2067 #ifdef DEBUG_IME
2068   NSLog(@"**** GetIMEEnabled");
2069 #endif
2071   if (nsTSMManager::IsIMEEnabled())
2072     *aState = nsIWidget::IME_STATUS_ENABLED;
2073   else if (nsTSMManager::IsRomanKeyboardsOnly())
2074     *aState = nsIWidget::IME_STATUS_PASSWORD;
2075   else
2076     *aState = nsIWidget::IME_STATUS_DISABLED;
2077   return NS_OK;
2081 // Destruct and don't commit the IME composition string.
2082 NS_IMETHODIMP nsChildView::CancelIMEComposition()
2084 #ifdef DEBUG_IME
2085   NSLog(@"**** CancelIMEComposition");
2086 #endif
2088   nsTSMManager::CancelIME();
2089   return NS_OK;
2093 NS_IMETHODIMP nsChildView::GetToggledKeyState(PRUint32 aKeyCode,
2094                                               PRBool* aLEDState)
2096   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2098 #ifdef DEBUG_IME
2099   NSLog(@"**** GetToggledKeyState");
2100 #endif
2101   NS_ENSURE_ARG_POINTER(aLEDState);
2102   PRUint32 key;
2103   switch (aKeyCode) {
2104     case NS_VK_CAPS_LOCK:
2105       key = alphaLock;
2106       break;
2107     case NS_VK_NUM_LOCK:
2108       key = kEventKeyModifierNumLockMask;
2109       break;
2110     // Mac doesn't support SCROLL_LOCK state.
2111     default:
2112       return NS_ERROR_NOT_IMPLEMENTED;
2113   }
2114   PRUint32 modifierFlags = ::GetCurrentEventKeyModifiers();
2115   *aLEDState = (modifierFlags & key) != 0;
2116   return NS_OK;
2118   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2122 #pragma mark -
2125 gfxASurface*
2126 nsChildView::GetThebesSurface()
2128   if (!mTempThebesSurface) {
2129     mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxASurface::ImageFormatARGB32);
2130   }
2132   return mTempThebesSurface;
2136 NS_IMETHODIMP
2137 nsChildView::BeginSecureKeyboardInput()
2139   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2141   nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
2142   if (NS_SUCCEEDED(rv))
2143     ::EnableSecureEventInput();
2144   return rv;
2146   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2150 NS_IMETHODIMP
2151 nsChildView::EndSecureKeyboardInput()
2153   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
2155   nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
2156   if (NS_SUCCEEDED(rv))
2157     ::DisableSecureEventInput();
2158   return rv;
2160   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2164 #ifdef ACCESSIBILITY
2165 void
2166 nsChildView::GetDocumentAccessible(nsIAccessible** aAccessible)
2168   *aAccessible = nsnull;
2169   
2170   nsCOMPtr<nsIAccessible> accessible = do_QueryReferent(mAccessible);
2171   if (!mAccessible) {
2172     // need to fetch the accessible anew, because it has gone away.
2173     nsEventStatus status;
2174     nsAccessibleEvent event(PR_TRUE, NS_GETACCESSIBLE, this);
2175     DispatchEvent(&event, status);
2176   
2177     // cache the accessible in our weak ptr
2178     mAccessible = do_GetWeakReference(event.accessible);
2179     
2180     // now try again
2181     accessible = do_QueryReferent(mAccessible);
2182   }
2183   
2184   NS_IF_ADDREF(*aAccessible = accessible.get());
2186   return;
2188 #endif
2191 #pragma mark -
2194 @implementation ChildView
2197 // globalDragPboard is non-null during native drag sessions that did not originate
2198 // in our native NSView (it is set in |draggingEntered:|). It is unset when the
2199 // drag session ends for this view, either with the mouse exiting or when a drop
2200 // occurs in this view.
2201 NSPasteboard* globalDragPboard = nil;
2204 // gLastDragView and gLastDragEvent are only non-null during calls to |mouseDragged:|
2205 // in our native NSView. They are used to communicate information to the drag service
2206 // during drag invocation (starting a drag in from the view). All drag service drag
2207 // invocations happen only while these two global variables are non-null, while |mouseDragged:|
2208 // is on the stack.
2209 NSView* gLastDragView = nil;
2210 NSEvent* gLastDragEvent = nil;
2213 // initWithFrame:geckoChild:
2214 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
2216   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2218   if ((self = [super initWithFrame:inFrame])) {
2219     mWindow = nil;
2220     mGeckoChild = inChild;
2221     mIsPluginView = NO;
2223     mCurKeyEvent = nil;
2224     mKeyDownHandled = PR_FALSE;
2225     mKeyPressHandled = NO;
2226     mKeyPressSent = NO;
2228     // initialization for NSTextInput
2229     mMarkedRange.location = NSNotFound;
2230     mMarkedRange.length = 0;
2232     mLastMouseDownEvent = nil;
2233     mDragService = nsnull;
2235     mPluginTSMDoc = nil;
2236   }
2237   
2238   // register for things we'll take from other applications
2239   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
2240   [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
2241                                                           NSStringPboardType,
2242                                                           NSURLPboardType,
2243                                                           NSFilesPromisePboardType,
2244                                                           kWildcardPboardType,
2245                                                           kCorePboardType_url,
2246                                                           kCorePboardType_urld,
2247                                                           kCorePboardType_urln,
2248                                                           nil]];
2250   return self;
2252   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2256 - (void)dealloc
2258   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2260   [mPendingDirtyRects release];
2261   [mLastMouseDownEvent release];
2262   if (mPluginTSMDoc)
2263     ::DeleteTSMDocument(mPluginTSMDoc);
2264   
2265   if (sLastViewEntered == self)
2266     sLastViewEntered = nil;
2268   [super dealloc];    
2270   // This sets the current port to _savePort.
2271   // todo: Only do if a Quickdraw plugin is present in the hierarchy!
2272   ::SetPort(NULL);
2274   NS_OBJC_END_TRY_ABORT_BLOCK;
2278 - (void)widgetDestroyed
2280   nsTSMManager::OnDestroyView(self);
2281   mGeckoChild = nsnull;
2282   // Just in case we're destroyed abruptly and missed the draggingExited
2283   // or performDragOperation message.
2284   NS_IF_RELEASE(mDragService);
2288 // mozView method, return our gecko child view widget. Note this does not AddRef.
2289 - (nsIWidget*) widget
2291   return static_cast<nsIWidget*>(mGeckoChild);
2295 // mozView method, get the window that this view is associated with
2296 - (NSWindow*)nativeWindow
2298   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2300   NSWindow* currWin = [self window];
2301   if (currWin)
2302      return currWin;
2303   else
2304      return mWindow;
2306   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2310 // mozView method, set the NSWindow that this view is associated with (even when
2311 // not in the view hierarchy).
2312 - (void)setNativeWindow:(NSWindow*)aWindow
2314   mWindow = aWindow;
2318 - (void)setNeedsPendingDisplay
2320   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2322   mPendingFullDisplay = YES;
2323   [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
2325   NS_OBJC_END_TRY_ABORT_BLOCK;
2329 - (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect
2331   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2333   if (!mPendingDirtyRects)
2334     mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1];
2335   [mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]];
2336   [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
2338   NS_OBJC_END_TRY_ABORT_BLOCK;
2342 // Clears the queue of any pending invalides
2343 - (void)processPendingRedraws
2345   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2347   if (mPendingFullDisplay) {
2348     [self setNeedsDisplay:YES];
2349   }
2350   else {
2351     unsigned int count = [mPendingDirtyRects count];
2352     for (unsigned int i = 0; i < count; ++i) {
2353       [self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]];
2354     }
2355   }
2356   mPendingFullDisplay = NO;
2357   [mPendingDirtyRects release];
2358   mPendingDirtyRects = nil;
2360   NS_OBJC_END_TRY_ABORT_BLOCK;
2364 - (NSString*)description
2366   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2368   return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])];
2370   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2374 // Find the nearest scrollable view for this ChildView
2375 // (recall that views are not refcounted)
2376 - (nsIScrollableView*) getScrollableView
2378   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
2380   nsIScrollableView* scrollableView = nsnull;
2382   ChildView* currView = self;
2383   // we have to loop up through superviews in case the view that received the
2384   // mouseDown is in fact a plugin view with no scrollbars
2385   while (currView) {
2386     // This is a hack I learned in nsView::GetViewFor(nsIWidget* aWidget)
2387     // that I'm not sure is kosher. If anyone knows a better way to get
2388     // the view for a widget, I'd love to hear it. --Nathan
2390     void* clientData;
2391     [currView widget]->GetClientData(clientData);
2393     nsISupports* data = (nsISupports*)clientData;
2394     nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(data));
2395     if (req) {
2396       req->GetInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollableView);
2397       if (scrollableView)
2398         break;
2399     }
2401     if ([[currView superview] isMemberOfClass:[ChildView class]])
2402         currView = (ChildView*)[currView superview];
2403     else
2404         currView = nil;
2405   }
2407   return scrollableView;
2409   NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
2413 // set the closed hand cursor and record the starting scroll positions
2414 - (void) startHandScroll:(NSEvent*)theEvent
2416   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2418   if (!mGeckoChild)
2419     return;
2421   mHandScrollStartMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
2423   nsIScrollableView* aScrollableView = [self getScrollableView]; 
2425   // if we succeeded in getting aScrollableView
2426   if (aScrollableView) {
2427     aScrollableView->GetScrollPosition(mHandScrollStartScrollX, mHandScrollStartScrollY);
2428     mGeckoChild->SetCursor(eCursor_grabbing);
2429     mInHandScroll = TRUE;
2430   }
2432   NS_OBJC_END_TRY_ABORT_BLOCK;
2436 // update the scroll position based on the new mouse coordinates
2437 - (void) updateHandScroll:(NSEvent*)theEvent
2439   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2441   if (!mGeckoChild)
2442     return;
2444   nsIScrollableView* aScrollableView = [self getScrollableView];
2445   if (!aScrollableView)
2446     return;
2448   NSPoint newMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
2450   PRInt32 deltaX = (PRInt32)(mHandScrollStartMouseLoc.x - newMouseLoc.x);
2451   PRInt32 deltaY = (PRInt32)(newMouseLoc.y - mHandScrollStartMouseLoc.y);
2453   // convert to the nsIView coordinates
2454   PRInt32 p2a = mGeckoChild->GetDeviceContext()->AppUnitsPerDevPixel();
2455   nscoord newX = mHandScrollStartScrollX + NSIntPixelsToAppUnits(deltaX, p2a);
2456   nscoord newY = mHandScrollStartScrollY + NSIntPixelsToAppUnits(deltaY, p2a);
2457   aScrollableView->ScrollTo(newX, newY, NS_VMREFRESH_IMMEDIATE);
2459   NS_OBJC_END_TRY_ABORT_BLOCK;
2463 // Return true if the correct modifiers are pressed to perform hand scrolling.
2464 + (BOOL) areHandScrollModifiers:(unsigned int)modifiers
2466   // The command and option key should be held down.  Ignore capsLock by
2467   // setting it explicitly to match.
2468   modifiers |= NSAlphaShiftKeyMask;
2469   return (modifiers & NSDeviceIndependentModifierFlagsMask) ==
2470       (NSAlphaShiftKeyMask | NSCommandKeyMask | NSAlternateKeyMask);
2474 // If the user is pressing the hand scroll modifiers, then set
2475 // the hand scroll cursor.
2476 - (void) setHandScrollCursor:(NSEvent*)theEvent
2478   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2480   if (!mGeckoChild)
2481     return;
2483   BOOL inMouseView = NO;
2485   // check to see if the user has hand scroll modifiers held down; if so, 
2486   // find out if the cursor is in an ChildView
2487   if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]]) {
2488     NSPoint pointInWindow = [[self window] mouseLocationOutsideOfEventStream];
2490     NSView* mouseView = [[[self window] contentView] hitTest:pointInWindow];
2491     inMouseView = (mouseView != nil && [mouseView isMemberOfClass:[ChildView class]]);   
2492   }
2493   if (inMouseView) {
2494       mGeckoChild->SetCursor(eCursor_grab);
2495   } else {
2496     nsCursor cursor = mGeckoChild->GetCursor();
2497     if (!mInHandScroll) {
2498       if (cursor == eCursor_grab || cursor == eCursor_grabbing)
2499         mGeckoChild->SetCursor(eCursor_standard);
2500     }
2501   }
2503   NS_OBJC_END_TRY_ABORT_BLOCK;
2507 // reset the scroll flag and cursor
2508 - (void) stopHandScroll:(NSEvent*)theEvent
2510   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2512   mInHandScroll = FALSE;
2513   [self setHandScrollCursor:theEvent];
2515   NS_OBJC_END_TRY_ABORT_BLOCK;
2519 // When smooth scrolling is turned on on panther, the parent of a scrollbar (which
2520 // I guess they assume is a NSScrollView) gets called with this method. I have no
2521 // idea what the correct return value is, but we have to have this otherwise the scrollbar
2522 // will not continuously respond when the mouse is held down in the pageup/down area.
2523 -(float)_destinationFloatValueForScroller:(id)scroller
2525   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2527   return [scroller floatValue];
2529   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
2533 // Override in order to keep our mouse enter/exit tracking rect in sync with
2534 // the frame of the view
2535 - (void)setFrame:(NSRect)frameRect
2537   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2539   [super setFrame:frameRect];
2540   if (mMouseEnterExitTag)
2541     [self removeTrackingRect:mMouseEnterExitTag];
2543   if ([self window])
2544     mMouseEnterExitTag = [self addTrackingRect:[self bounds]
2545                                          owner:self
2546                                       userData:nil
2547                                   assumeInside:[[self window] acceptsMouseMovedEvents]];
2549   NS_OBJC_END_TRY_ABORT_BLOCK;
2553 // Make the origin of this view the topLeft corner (gecko origin) rather
2554 // than the bottomLeft corner (standard cocoa origin).
2555 - (BOOL)isFlipped
2557   return YES;
2561 - (void)setTransparent:(BOOL)transparent
2563   mIsTransparent = transparent;
2567 - (BOOL)isOpaque
2569   return !mIsTransparent;
2573 -(void)setIsPluginView:(BOOL)aIsPlugin
2575   mIsPluginView = aIsPlugin;
2579 -(BOOL)isPluginView
2581   return mIsPluginView;
2585 - (BOOL)childViewHasPlugin
2587   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2589   NSArray* subviews = [self subviews];
2590   for (unsigned int i = 0; i < [subviews count]; i ++) {
2591     id subview = [subviews objectAtIndex:i];
2592     if ([subview respondsToSelector:@selector(isPluginView)] && [subview isPluginView])
2593       return YES;
2594   }
2595   
2596   return NO;
2598   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2602 - (void)sendFocusEvent:(PRUint32)eventType
2604   if (!mGeckoChild)
2605     return;
2607   nsEventStatus status = nsEventStatus_eIgnore;
2608   nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoChild);
2609   focusGuiEvent.time = PR_IntervalNow();
2610   mGeckoChild->DispatchEvent(&focusGuiEvent, status);
2614 // We accept key and mouse events, so don't keep passing them up the chain. Allow
2615 // this to be a 'focussed' widget for event dispatch
2616 - (BOOL)acceptsFirstResponder
2618   return YES;
2622 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
2624   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2626   if (mMouseEnterExitTag)
2627     [self removeTrackingRect:mMouseEnterExitTag];
2629   [super viewWillMoveToWindow:newWindow];
2631   NS_OBJC_END_TRY_ABORT_BLOCK;
2635 - (void)viewDidMoveToWindow
2637   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2639   if ([self window])
2640     mMouseEnterExitTag = [self addTrackingRect:[self bounds] owner:self
2641                                       userData:nil assumeInside: [[self window]
2642                                       acceptsMouseMovedEvents]];
2644   [super viewDidMoveToWindow];
2646   NS_OBJC_END_TRY_ABORT_BLOCK;
2650 - (void)viewWillStartLiveResize
2652   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2654   if (mGeckoChild && mIsPluginView)
2655     mGeckoChild->LiveResizeStarted();
2656   
2657   [super viewWillStartLiveResize];
2659   NS_OBJC_END_TRY_ABORT_BLOCK;
2663 - (void)viewDidEndLiveResize
2665   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2667   if (mGeckoChild && mIsPluginView)
2668     mGeckoChild->LiveResizeEnded();
2670   [super viewDidEndLiveResize];
2672   NS_OBJC_END_TRY_ABORT_BLOCK;
2676 - (void)scrollRect:(NSRect)aRect by:(NSSize)offset
2678   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2680   // Update any pending dirty rects to reflect the new scroll position
2681   if (mPendingDirtyRects) {
2682     unsigned int count = [mPendingDirtyRects count];
2683     for (unsigned int i = 0; i < count; ++i) {
2684       NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue];
2685       NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height);
2686       [mPendingDirtyRects replaceObjectAtIndex:i
2687                                     withObject:[NSValue valueWithRect:newRect]];
2688     }
2689   }
2690   [super scrollRect:aRect by:offset];
2692   NS_OBJC_END_TRY_ABORT_BLOCK;
2696 - (BOOL)mouseDownCanMoveWindow
2698   return NO;
2702 - (void)lockFocus
2704   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2706   // Set the current GrafPort to a "safe" port before calling [NSQuickDrawView lockFocus],
2707   // so that the NSQuickDrawView stashes a pointer to this known-good port internally.
2708   // It will set the port back to this port on destruction.
2709   ::SetPort(NULL);  // todo: only do if a Quickdraw plugin is present in the hierarchy!
2710   [super lockFocus];
2712   NS_OBJC_END_TRY_ABORT_BLOCK;
2716 static BOOL IsPaintingSuppressed(nsIWidget* aWidget)
2718   nsIWidget* topLevelWidget = aWidget->GetTopLevelWidget();
2719   NSWindow* win = (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
2720   return ([win isKindOfClass:[ToolbarWindow class]] &&
2721           [(ToolbarWindow*)win isPaintingSuppressed]);
2725 // The display system has told us that a portion of our view is dirty. Tell
2726 // gecko to paint it
2727 - (void)drawRect:(NSRect)aRect
2729   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2731   PRBool isVisible;
2732   if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) ||
2733       !isVisible || IsPaintingSuppressed(mGeckoChild))
2734     return;
2736   CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2738   nsRect geckoBounds;
2739   mGeckoChild->GetBounds(geckoBounds);
2741   NSRect bounds = [self bounds];
2742   nsRefPtr<gfxQuartzSurface> targetSurface =
2743     new gfxQuartzSurface(cgContext, gfxSize(bounds.size.width, bounds.size.height));
2745 #ifdef DEBUG_UPDATE
2746   fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n  gecko bounds: [%d %d %d %d]\n",
2747            self, mGeckoChild,
2748            aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, cgContext,
2749            geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
2751   CGAffineTransform xform = CGContextGetCTM(cgContext);
2752   fprintf (stderr, "  xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
2753 #endif
2755   nsRefPtr<gfxContext> targetContext = new gfxContext(targetSurface);
2757   nsCOMPtr<nsIRenderingContext> rc;
2758   mGeckoChild->GetDeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
2759   rc->Init(mGeckoChild->GetDeviceContext(), targetContext);
2761   /* clip and build a region */
2762   nsCOMPtr<nsIRegion> rgn(do_CreateInstance(kRegionCID));
2763   if (rgn)
2764     rgn->Init();
2766   const NSRect *rects;
2767   int count, i;
2768   [self getRectsBeingDrawn:&rects count:&count];
2769   for (i = 0; i < count; ++i) {
2770     const NSRect& r = rects[i];
2772     // add to the region
2773     if (rgn)
2774       rgn->Union((PRInt32)r.origin.x, (PRInt32)r.origin.y, (PRInt32)r.size.width, (PRInt32)r.size.height);
2776     // to the context for clipping
2777     targetContext->Rectangle(gfxRect(r.origin.x, r.origin.y, r.size.width, r.size.height));
2778   }
2779   targetContext->Clip();
2780   
2781   // bounding box of the dirty area
2782   nsRect fullRect;
2783   NSRectToGeckoRect(aRect, fullRect);
2785   nsPaintEvent paintEvent(PR_TRUE, NS_PAINT, mGeckoChild);
2786   paintEvent.renderingContext = rc;
2787   paintEvent.rect = &fullRect;
2788   paintEvent.region = rgn;
2790   nsAutoRetainCocoaObject kungFuDeathGrip(self);
2791   mGeckoChild->DispatchWindowEvent(paintEvent);
2792   if (!mGeckoChild)
2793     return;
2795   paintEvent.renderingContext = nsnull;
2796   paintEvent.region = nsnull;
2798   targetContext = nsnull;
2799   targetSurface = nsnull;
2801   // note that the cairo surface *MUST* be destroyed at this point,
2802   // or bad things will happen (since we can't keep the cgContext around
2803   // beyond this drawRect message handler)
2805 #ifdef DEBUG_UPDATE
2806   fprintf (stderr, "  window coords: [%d %d %d %d]\n", fullRect.x, fullRect.y, fullRect.width, fullRect.height);
2807   fprintf (stderr, "---- update done ----\n");
2809 #if 0
2810   CGContextSetRGBStrokeColor (cgContext,
2811                             ((((unsigned long)self) & 0xff)) / 255.0,
2812                             ((((unsigned long)self) & 0xff00) >> 8) / 255.0,
2813                             ((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
2814                             0.5);
2815 #endif 
2816   CGContextSetRGBStrokeColor (cgContext, 1, 0, 0, 0.8);
2817   CGContextSetLineWidth (cgContext, 4.0);
2818   CGContextStrokeRect (cgContext,
2819                        CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height));
2820 #endif
2822   NS_OBJC_END_TRY_ABORT_BLOCK;
2826 // Allows us to turn off setting up the clip region
2827 // before each drawRect. We already clip within gecko.
2828 - (BOOL)wantsDefaultClipping
2830   return NO;
2834 #if USE_CLICK_HOLD_CONTEXTMENU
2836 // -clickHoldCallback:
2838 // called from a timer two seconds after a mouse down to see if we should display
2839 // a context menu (click-hold). |anEvent| is the original mouseDown event. If we're
2840 // still in that mouseDown by this time, put up the context menu, otherwise just
2841 // fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback
2842 // fires so we're ok there.
2844 // This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314)
2845 // so removing it until we get it straightened out.
2847 - (void)clickHoldCallback:(id)theEvent;
2849   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2851   if( theEvent == [NSApp currentEvent] ) {
2852     // we're still in the middle of the same mousedown event here, activate
2853     // click-hold context menu by triggering the right mouseDown action.
2854     NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown
2855                                                   location:[theEvent locationInWindow]
2856                                              modifierFlags:[theEvent modifierFlags]
2857                                                  timestamp:[theEvent timestamp]
2858                                               windowNumber:[theEvent windowNumber]
2859                                                    context:[theEvent context]
2860                                                eventNumber:[theEvent eventNumber]
2861                                                 clickCount:[theEvent clickCount]
2862                                                   pressure:[theEvent pressure]];
2863     [self rightMouseDown:clickHoldEvent];
2864   }
2866   NS_OBJC_END_TRY_ABORT_BLOCK;
2868 #endif
2871 // We sometimes need to reroute events when there is a rollup widget and the
2872 // event isn't targeted at it.
2874 // Rerouting may be needed when the user tries to navigate a context menu while
2875 // keeping the mouse-button down (left or right mouse button) -- the OS thinks this
2876 // is a dragging operation, so it sends events (mouseMoved and mouseUp) to the
2877 // window where the dragging operation started (the parent of the context
2878 // menu window).  It also works around a bizarre Apple bug - if (while a context
2879 // menu is open) you move the mouse over another app's window and then back over
2880 // the context menu, mouseMoved events will be sent to the window underneath the
2881 // context menu.
2882 - (BOOL)ensureCorrectMouseEventTarget:(NSEvent*)anEvent
2884   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2886   // If there is no rollup widget we assume the OS routed the event correctly.
2887   if (!gRollupWidget)
2888     return YES;
2890   // If this is the rollup widget and the event is not a mouse move then trust the OS routing.  
2891   // The reason for this trust is complicated.
2892   //
2893   // There are three types of mouse events that can legitimately need to be targeted at a window
2894   // that they are not over. Mouse moves, mouse drags, and mouse ups. Anything else our app wouldn't
2895   // handle (if the mouse was not over any window) or it would go to the appropriate window.
2896   //
2897   // We need to do manual event rerouting for mouse moves because we know that in some cases, like
2898   // when there is a submenu opened from a popup window, the OS will route mouse move events to the
2899   // submenu even if the mouse is over the parent. Mouse move events are never tied to a particular
2900   // window because of some originating action like the starting point of a drag for drag events or
2901   // a mouse down event for mouse up events, so it is always safe to do our own routing on them here.
2902   //
2903   // As for mouse drags and mouse ups, they have originating actions that tie them to windows they
2904   // may no longer be over. If there is a rollup window present when one of these events is getting
2905   // processed but we are not it, we are probably the window where the action originated, and that
2906   // action must have caused the rollup window to come into existence. In that case, we might need
2907   // to reroute the event if it is over the rollup window. That is why if we're not the rollup window
2908   // we don't return YES here.
2909   NSWindow* rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
2910   if (mWindow == rollupWindow && [anEvent type] != NSMouseMoved)
2911     return YES;
2913   // Find the window that the event is over.
2914   NSWindow* targetWindow = nsCocoaUtils::FindWindowUnderPoint(nsCocoaUtils::ScreenLocationForEvent(anEvent));
2916   // If the event was not over any window, send it to the rollup window.
2917   if (!targetWindow)
2918     targetWindow = rollupWindow;
2920   // At this point we've resolved a target window, if we are it then just return
2921   // yes so we handle it. No need to redirect.
2922   if (targetWindow == mWindow)
2923     return YES;
2925   // Send the event to its new destination.
2926   NSPoint newWindowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, targetWindow);
2927   NSEvent *newEvent = [NSEvent mouseEventWithType:[anEvent type]
2928                                          location:newWindowLocation
2929                                     modifierFlags:[anEvent modifierFlags]
2930                                         timestamp:GetCurrentEventTime()
2931                                      windowNumber:[targetWindow windowNumber]
2932                                           context:nil
2933                                       eventNumber:0
2934                                        clickCount:1
2935                                          pressure:0.0];
2936   [targetWindow sendEvent:newEvent];
2938   // Return NO because we just sent the event somewhere else.
2939   return NO;
2941   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2945 // If we've just created a non-native context menu, we need to mark it as
2946 // such and let the OS (and other programs) know when it opens and closes
2947 // (this is how the OS knows to close other programs' context menus when
2948 // ours open).  We send the initial notification here, but others are sent
2949 // in nsCocoaWindow::Show().
2950 - (void)maybeInitContextMenuTracking
2952   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2954   if (!gRollupWidget)
2955     return;
2957   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
2958   if (prefs) {
2959     PRBool useNativeContextMenus;
2960     nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
2961     if (NS_SUCCEEDED(rv) && useNativeContextMenus)
2962       return;
2963   }
2965   NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
2966   if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
2967     return;
2969   [[NSDistributedNotificationCenter defaultCenter]
2970     postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
2971                   object:@"org.mozilla.gecko.PopupWindow"];
2972   [(PopupWindow*)popupWindow setIsContextMenu:YES];
2974   NS_OBJC_END_TRY_ABORT_BLOCK;
2978 - (BOOL)maybeRollup:(NSEvent*)theEvent
2980   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2982   PRBool retVal = PR_FALSE;
2983   if (gRollupWidget && gRollupListener) {
2984     NSWindow* currentPopup = static_cast<NSWindow*>(gRollupWidget->GetNativeData(NS_NATIVE_WINDOW));
2985     if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
2986       PRBool rollup = PR_TRUE;
2987       if ([theEvent type] == NSScrollWheel) {
2988         gRollupListener->ShouldRollupOnMouseWheelEvent(&rollup);
2989         // We don't want the event passed on for scrollwheel events if we're
2990         // not supposed to close the popup.  Otherwise the background window
2991         // will scroll when a custom context menu or the autoscroll popup is
2992         // open (and the mouse isn't over the popup) -- which doesn't seem right.
2993         // This change resolves bmo bug 344367.
2994         retVal = PR_TRUE;
2995       }
2996       // if we're dealing with menus, we probably have submenus and
2997       // we don't want to rollup if the click is in a parent menu of
2998       // the current submenu
2999       nsCOMPtr<nsIMenuRollup> menuRollup;
3000       menuRollup = (do_QueryInterface(gRollupListener));
3001       if (menuRollup) {
3002         nsAutoTArray<nsIWidget*, 5> widgetChain;
3003         menuRollup->GetSubmenuWidgetChain(&widgetChain);
3004         for (PRUint32 i = 0; i < widgetChain.Length(); i++) {
3005           nsIWidget* widget = widgetChain[i];
3006           NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
3007           if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
3008             rollup = PR_FALSE;
3009             break;
3010           }
3011         } // foreach parent menu widget
3012       } // if rollup listener knows about menus
3014       // if we've determined that we should still rollup, do it.
3015       if (rollup) {
3016         gRollupListener->Rollup(nsnull);
3017         retVal = PR_TRUE;
3018       }
3019     }
3020   }
3022   return retVal;
3024   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
3028 - (void)mouseDown:(NSEvent*)theEvent
3030   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3032   // If we've already seen this event due to direct dispatch from menuForEvent:
3033   // just bail; if not, remember it.
3034   if (mLastMouseDownEvent == theEvent) {
3035     [mLastMouseDownEvent release];
3036     mLastMouseDownEvent = nil;
3037     return;
3038   }
3039   else {
3040     [mLastMouseDownEvent release];
3041     mLastMouseDownEvent = [theEvent retain];
3042   }
3044   if (![self ensureCorrectMouseEventTarget:theEvent])
3045     return;
3047   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3049   if ([self maybeRollup:theEvent])
3050     return;
3052   unsigned int modifierFlags = [theEvent modifierFlags];
3054   // if the command and alt keys are held down, initiate hand scrolling
3055   if ([ChildView areHandScrollModifiers:modifierFlags]) {
3056     [self startHandScroll:theEvent];
3057     // needed to change the focus, among other things, since we don't
3058     // get to do that below.
3059     [super mouseDown:theEvent];
3060     return; // do not pass this mousedown event to gecko
3061   }
3063 #if USE_CLICK_HOLD_CONTEXTMENU
3064   // fire off timer to check for click-hold after two seconds. retains |theEvent|
3065   [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
3066 #endif
3068   // in order to send gecko events we'll need a gecko widget
3069   if (!mGeckoChild)
3070     return;
3072   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3073   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3074   geckoEvent.clickCount = [theEvent clickCount];
3075   if (modifierFlags & NSControlKeyMask)
3076     geckoEvent.button = nsMouseEvent::eRightButton;
3077   else
3078     geckoEvent.button = nsMouseEvent::eLeftButton;
3080   // create native EventRecord for use by plugins
3081   EventRecord macEvent;
3082   macEvent.what = mouseDown;
3083   macEvent.message = 0;
3084   macEvent.when = ::TickCount();
3085   ::GetGlobalMouse(&macEvent.where);
3086   macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3087   geckoEvent.nativeMsg = &macEvent;
3089   mGeckoChild->DispatchWindowEvent(geckoEvent);
3091   // XXX maybe call markedTextSelectionChanged:client: here?
3093   NS_OBJC_END_TRY_ABORT_BLOCK;
3097 - (void)mouseUp:(NSEvent *)theEvent
3099   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3101   if (mInHandScroll) {
3102     [self updateHandScroll:theEvent];
3103     [self stopHandScroll:theEvent];
3104     return;
3105   }
3107   if (![self ensureCorrectMouseEventTarget:theEvent])
3108     return;
3110   if (!mGeckoChild)
3111     return;
3113   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3114   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3116   // create native EventRecord for use by plugins
3117   EventRecord macEvent;
3118   macEvent.what = mouseUp;
3119   macEvent.message = 0;
3120   macEvent.when = ::TickCount();
3121   ::GetGlobalMouse(&macEvent.where);
3122   macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3123   geckoEvent.nativeMsg = &macEvent;
3125   mGeckoChild->DispatchWindowEvent(geckoEvent);
3127   NS_OBJC_END_TRY_ABORT_BLOCK;
3131 // sends a mouse enter or exit event into gecko
3132 static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted,
3133                                                     PRUint32 msg,
3134                                                     nsIWidget *widget,
3135                                                     nsMouseEvent::reasonType aReason,
3136                                                     NSPoint* localEventLocation,
3137                                                     nsMouseEvent::exitType type)
3139   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
3141   if (!widget || !localEventLocation)
3142     return nsEventStatus_eIgnore;
3144   nsMouseEvent event(isTrusted, msg, widget, aReason);
3145   event.refPoint.x = nscoord((PRInt32)localEventLocation->x);
3146   event.refPoint.y = nscoord((PRInt32)localEventLocation->y);
3148   EventRecord macEvent;
3149   macEvent.what = adjustCursorEvent;
3150   macEvent.message = 0;
3151   macEvent.when = ::TickCount();
3152   ::GetGlobalMouse(&macEvent.where);
3153   macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3154   event.nativeMsg = &macEvent;
3156   event.exit = type;
3158   nsEventStatus status;
3159   widget->DispatchEvent(&event, status);
3161   // After the cursor exits a view set it to a visible regular arrow cursor.
3162   // This lets us recover from plugins that mess with it.
3163   if (msg == NS_MOUSE_EXIT) {
3164     [NSCursor unhide];
3165     [[NSCursor arrowCursor] set];
3166   }
3168   return status;
3170   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore);
3174 - (void)mouseMoved:(NSEvent*)theEvent
3176   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3178   // Work around an Apple bug that causes the OS to continue sending
3179   // mouseMoved events to a window for a while after it's been miniaturized.
3180   // This may be related to a similar problem with popup windows (bmo bug
3181   // 378645, popup windows continue to receive mouseMoved events after having
3182   // been "ordered out"), which is worked around in nsCocoaWindow::Show()
3183   // (search on 378645 in nsCocoaWindow.mm).  This problem is bmo bug 410219,
3184   // and exists in both OS X 10.4 and 10.5.
3185   if ([[self window] isMiniaturized])
3186     return;
3188   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(theEvent, mWindow);
3189   NSPoint viewEventLocation = [self convertPoint:windowEventLocation fromView:nil];
3191   // Installing a mouseMoved handler on the EventMonitor target (in
3192   // nsToolkit::RegisterForAllProcessMouseEvents()) means that some of the
3193   // events received here come from other processes.  For this reason we need
3194   // to avoid processing them unless they're over a context menu -- otherwise
3195   // tooltips and other mouse-hover effects will "work" even when our app
3196   // doesn't have the focus.
3197   BOOL mouseEventIsOverRollupWidget = NO;
3198   if (gRollupWidget) {
3199     NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
3200     mouseEventIsOverRollupWidget = nsCocoaUtils::IsEventOverWindow(theEvent, popupWindow);
3201   }
3203   if (![NSApp isActive] && !mouseEventIsOverRollupWidget) {
3204     if (sLastViewEntered) {
3205       nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
3206       NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
3207       SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
3208                                      &exitEventLocation, nsMouseEvent::eTopLevel);
3209       sLastViewEntered = nil;
3210     }
3211     return;
3212   }
3214   if (![self ensureCorrectMouseEventTarget:theEvent])
3215     return;
3217   NSView* view = [[mWindow contentView] hitTest:windowEventLocation];
3218   if (view) {
3219     // we shouldn't handle this if the hit view is not us
3220     if (view != (NSView*)self) {
3221       [view mouseMoved:theEvent];
3222       return;
3223     }
3224   }
3225   else {
3226     // If the hit test returned nil then the mouse isn't over the window. If thse mouse
3227     // exited the window then send mouse exit to the last view in the window it was over.
3228     if (sLastViewEntered) {
3229       NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
3230       // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
3231       nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
3232       SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
3233                                      &exitEventLocation, nsMouseEvent::eTopLevel);
3234       sLastViewEntered = nil;
3235     }
3236     return;
3237   }
3239   // At this point we are supposed to handle this event. If we were not the last view entered, then
3240   // we should send an exit event to the last view entered and an enter event to ourselves.  
3241   if (!mGeckoChild)
3242     return;
3244   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3245   if (sLastViewEntered != self) {
3246     if (sLastViewEntered) {
3247       NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
3248       // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
3249       nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
3250       SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
3251                                      &exitEventLocation, nsMouseEvent::eChild);
3253       // The mouse exit event we just sent may have destroyed this widget, bail if that happened.
3254       if (!mGeckoChild)
3255         return;
3256     }
3258     // NSLog(@"sending NS_MOUSE_ENTER event with point %f,%f\n", viewEventLocation.x, viewEventLocation.y);
3259     SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_ENTER, mGeckoChild, nsMouseEvent::eReal,
3260                                    &viewEventLocation, nsMouseEvent::eChild);
3262     // The mouse enter event we just sent may have destroyed this widget, bail if that happened.
3263     if (!mGeckoChild)
3264       return;
3266     // mark this view as the last view entered
3267     sLastViewEntered = (NSView*)self;
3269     // checks to see if we should change to the hand cursor
3270     [self setHandScrollCursor:theEvent];
3271   }
3273   // check if we are in a hand scroll or if the user
3274   // has command and alt held down; if so,  we do not want
3275   // gecko messing with the cursor.
3276   if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]])
3277     return;
3279   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3280   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3282   // create native EventRecord for use by plugins
3283   EventRecord macEvent;
3284   macEvent.what = adjustCursorEvent;
3285   macEvent.message = 0;
3286   macEvent.when = ::TickCount();
3287   ::GetGlobalMouse(&macEvent.where);
3288   macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3289   geckoEvent.nativeMsg = &macEvent;
3291   mGeckoChild->DispatchWindowEvent(geckoEvent);
3293   NS_OBJC_END_TRY_ABORT_BLOCK;
3297 - (void)mouseDragged:(NSEvent*)theEvent
3299   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3301   if (![self ensureCorrectMouseEventTarget:theEvent])
3302     return;
3304   if (!mGeckoChild)
3305     return;
3307   // if the handscroll flag is set, steal this event
3308   if (mInHandScroll) {
3309     [self updateHandScroll:theEvent];
3310     return;
3311   }
3313   gLastDragView = self;
3314   gLastDragEvent = theEvent;
3316   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3317   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3319   // create native EventRecord for use by plugins
3320   EventRecord macEvent;
3321   macEvent.what = nullEvent;
3322   macEvent.message = 0;
3323   macEvent.when = ::TickCount();
3324   ::GetGlobalMouse(&macEvent.where);
3325   macEvent.modifiers = btnState | ::GetCurrentEventKeyModifiers();
3326   geckoEvent.nativeMsg = &macEvent;
3328   mGeckoChild->DispatchWindowEvent(geckoEvent);
3330   // Note, sending the above event might have destroyed our widget since we didn't retain.
3331   // Fine so long as we don't access any local variables from here on.
3333   gLastDragView = nil;
3334   gLastDragEvent = nil;
3335   // XXX maybe call markedTextSelectionChanged:client: here?
3337   NS_OBJC_END_TRY_ABORT_BLOCK;
3341 - (void)rightMouseDown:(NSEvent *)theEvent
3343   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3345   if (![self ensureCorrectMouseEventTarget:theEvent])
3346     return;
3348   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3350   [self maybeRollup:theEvent];
3351   if (!mGeckoChild)
3352     return;
3354   // The right mouse went down, fire off a right mouse down event to gecko
3355   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3356   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3357   geckoEvent.button = nsMouseEvent::eRightButton;
3358   geckoEvent.clickCount = [theEvent clickCount];
3360   // create native EventRecord for use by plugins
3361   EventRecord macEvent;
3362   macEvent.what = mouseDown;
3363   macEvent.message = 0;
3364   macEvent.when = ::TickCount();
3365   ::GetGlobalMouse(&macEvent.where);
3366   macEvent.modifiers = controlKey;  // fake a context menu click
3367   geckoEvent.nativeMsg = &macEvent;
3369   PRBool handled = mGeckoChild->DispatchWindowEvent(geckoEvent);
3370   if (!mGeckoChild)
3371     return;
3373   if (!handled)
3374     [super rightMouseDown:theEvent]; // let the superview do context menu stuff
3376   NS_OBJC_END_TRY_ABORT_BLOCK;
3380 - (void)rightMouseUp:(NSEvent *)theEvent
3382   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3384   if (![self ensureCorrectMouseEventTarget:theEvent])
3385     return;
3387   if (!mGeckoChild)
3388     return;
3390   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3391   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3392   geckoEvent.button = nsMouseEvent::eRightButton;
3393   geckoEvent.clickCount = [theEvent clickCount];
3395   // create native EventRecord for use by plugins
3396   EventRecord macEvent;
3397   macEvent.what = mouseUp;
3398   macEvent.message = 0;
3399   macEvent.when = ::TickCount();
3400   ::GetGlobalMouse(&macEvent.where);
3401   macEvent.modifiers = controlKey;  // fake a context menu click
3402   geckoEvent.nativeMsg = &macEvent;
3404   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3405   mGeckoChild->DispatchWindowEvent(geckoEvent);
3407   NS_OBJC_END_TRY_ABORT_BLOCK;
3411 - (void)rightMouseDragged:(NSEvent*)theEvent
3413   if (![self ensureCorrectMouseEventTarget:theEvent])
3414     return;
3416   if (!mGeckoChild)
3417     return;
3419   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3420   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3421   geckoEvent.button = nsMouseEvent::eRightButton;
3423   // send event into Gecko by going directly to the
3424   // the widget.
3425   mGeckoChild->DispatchWindowEvent(geckoEvent);
3429 - (void)otherMouseDown:(NSEvent *)theEvent
3431   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3433   if (![self ensureCorrectMouseEventTarget:theEvent])
3434     return;
3436   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3438   if ([self maybeRollup:theEvent])
3439     return;
3441   if (!mGeckoChild)
3442     return;
3444   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
3445   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3446   geckoEvent.button = nsMouseEvent::eMiddleButton;
3447   geckoEvent.clickCount = [theEvent clickCount];
3449   mGeckoChild->DispatchWindowEvent(geckoEvent);
3451   NS_OBJC_END_TRY_ABORT_BLOCK;
3455 - (void)otherMouseUp:(NSEvent *)theEvent
3457   if (!mGeckoChild)
3458     return;
3460   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
3461   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3462   geckoEvent.button = nsMouseEvent::eMiddleButton;
3464   mGeckoChild->DispatchWindowEvent(geckoEvent);
3468 - (void)otherMouseDragged:(NSEvent*)theEvent
3470   if (!mGeckoChild)
3471     return;
3473   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
3474   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3475   geckoEvent.button = nsMouseEvent::eMiddleButton;
3477   // send event into Gecko by going directly to the
3478   // the widget.
3479   mGeckoChild->DispatchWindowEvent(geckoEvent);
3483 // Handle an NSScrollWheel event for a single axis only.
3484 -(void)scrollWheel:(NSEvent*)theEvent forAxis:(enum nsMouseScrollEvent::nsMouseScrollFlags)inAxis
3486   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3488   if (!mGeckoChild)
3489     return;
3491   float scrollDelta;
3493   if (inAxis & nsMouseScrollEvent::kIsVertical)
3494     scrollDelta = -[theEvent deltaY];
3495   else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
3496     scrollDelta = -[theEvent deltaX];
3497   else
3498     return; // caller screwed up
3500   if (scrollDelta == 0)
3501     // No sense in firing off a Gecko event.  Note that as of 10.4 Tiger,
3502     // a single NSScrollWheel event might result in deltaX = deltaY = 0.
3503     return;
3504   
3505   nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
3506   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3507   geckoEvent.scrollFlags |= inAxis;
3509   // Gecko only understands how to scroll by an integer value.  Using floor
3510   // and ceil is better than truncating the fraction, especially when
3511   // |delta| < 1.
3512   if (scrollDelta < 0)
3513     geckoEvent.delta = (PRInt32)floorf(scrollDelta);
3514   else
3515     geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
3517   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3518   mGeckoChild->DispatchWindowEvent(geckoEvent);
3519   if (!mGeckoChild)
3520     return;
3522   // dispatch scroll wheel carbon event for plugins
3523   {
3524     EventRef theEvent;
3525     OSStatus err = ::MacCreateEvent(NULL,
3526                           kEventClassMouse,
3527                           kEventMouseWheelMoved,
3528                           TicksToEventTime(TickCount()),
3529                           kEventAttributeUserEvent,
3530                           &theEvent);
3531     if (err == noErr) {
3532       EventMouseWheelAxis axis;
3533       if (inAxis & nsMouseScrollEvent::kIsVertical)
3534         axis = kEventMouseWheelAxisY;
3535       else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
3536         axis = kEventMouseWheelAxisX;
3537       
3538       SetEventParameter(theEvent,
3539                             kEventParamMouseWheelAxis,
3540                             typeMouseWheelAxis,
3541                             sizeof(EventMouseWheelAxis),
3542                             &axis);
3544       SInt32 delta = (SInt32)-geckoEvent.delta;
3545       SetEventParameter(theEvent,
3546                             kEventParamMouseWheelDelta,
3547                             typeLongInteger,
3548                             sizeof(SInt32),
3549                             &delta);
3551       Point mouseLoc;
3552       ::GetGlobalMouse(&mouseLoc);
3553       SetEventParameter(theEvent,
3554                             kEventParamMouseLocation,
3555                             typeQDPoint,
3556                             sizeof(Point),
3557                             &mouseLoc);
3558       
3559       ::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
3560       ReleaseEvent(theEvent);
3561     }
3562   }
3564   NS_OBJC_END_TRY_ABORT_BLOCK;
3568 -(void)scrollWheel:(NSEvent*)theEvent
3570   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3572   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3574   if ([self maybeRollup:theEvent])
3575     return;
3577   if (!mGeckoChild)
3578     return;
3580   // It's possible for a single NSScrollWheel event to carry both useful
3581   // deltaX and deltaY, for example, when the "wheel" is a trackpad.
3582   // NSMouseScrollEvent can only carry one axis at a time, so the system
3583   // event will be split into two Gecko events if necessary.
3584   [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsVertical];
3585   if (!mGeckoChild)
3586     return;
3587   [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsHorizontal];
3589   NS_OBJC_END_TRY_ABORT_BLOCK;
3593 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
3595   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3597   if (!mGeckoChild || [self isPluginView])
3598     return nil;
3600   nsAutoRetainCocoaObject kungFuDeathGrip(self);
3602   [self maybeRollup:theEvent];
3603   if (!mGeckoChild)
3604     return nil;
3606   // Cocoa doesn't always dispatch a mouseDown: for a control-click event,
3607   // depends on what we return from menuForEvent:. Gecko always expects one
3608   // and expects the mouse down event before the context menu event, so
3609   // get that event sent first if this is a left mouse click.
3610   if ([theEvent type] == NSLeftMouseDown) {
3611     [self mouseDown:theEvent];
3612     if (!mGeckoChild)
3613       return nil;
3614   }
3616   nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
3617   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
3618   geckoEvent.button = nsMouseEvent::eRightButton;
3619   mGeckoChild->DispatchWindowEvent(geckoEvent);
3620   if (!mGeckoChild)
3621     return nil;
3623   [self maybeInitContextMenuTracking];
3625   // Go up our view chain to fetch the correct menu to return.
3626   return [self contextMenu];
3628   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3632 - (NSMenu*)contextMenu
3634   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3636   NSView* superView = [self superview];
3637   if ([superView respondsToSelector:@selector(contextMenu)])
3638     return [(NSView<mozView>*)superView contextMenu];
3640   return nil;
3642   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3646 - (TopLevelWindowData*)ensureWindowData
3648   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
3650   WindowDataMap* windowMap = [WindowDataMap sharedWindowDataMap];
3652   TopLevelWindowData* windowData = [windowMap dataForWindow:mWindow];
3653   if (mWindow && !windowData)
3654   {
3655     windowData = [[TopLevelWindowData alloc] initWithWindow:mWindow];
3656     [windowMap setData:windowData forWindow:mWindow]; // takes ownership
3657     [windowData release];
3658   }
3659   return windowData;
3661   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
3665 static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar)
3667   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
3669   UnicodeToTextInfo converterInfo;
3670   TextEncoding      systemEncoding;
3671   Str255            convertedString;
3672   OSStatus          err;
3673   
3674   *outChar = 0;
3675   
3676   err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
3677   if (err != noErr)
3678     return PR_FALSE;
3679   
3680   err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
3681   if (err != noErr)
3682     return PR_FALSE;
3683   
3684   err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
3685   if (err != noErr)
3686     return PR_FALSE;
3688   *outChar = convertedString[1];
3689   ::DisposeUnicodeToTextInfo(&converterInfo);
3690   return PR_TRUE;
3692   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
3696 static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& macEvent, PRUint32 keyType = 0)
3698   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
3700     UInt32 charCode = 0;
3701     if ([cocoaEvent type] == NSFlagsChanged) {
3702       macEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp;
3703     } else {
3704       if ([[cocoaEvent characters] length] > 0)
3705         charCode = [[cocoaEvent characters] characterAtIndex:0];
3706       if ([cocoaEvent type] == NSKeyDown)
3707         macEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
3708       else
3709         macEvent.what = keyUp;
3710     }
3712     if (charCode >= 0x0080) {
3713         switch (charCode) {
3714         case NSUpArrowFunctionKey:
3715             charCode = kUpArrowCharCode;
3716             break;
3717         case NSDownArrowFunctionKey:
3718             charCode = kDownArrowCharCode;
3719             break;
3720         case NSLeftArrowFunctionKey:
3721             charCode = kLeftArrowCharCode;
3722             break;
3723         case NSRightArrowFunctionKey:
3724             charCode = kRightArrowCharCode;
3725             break;
3726         default:
3727             unsigned char convertedCharCode;
3728             if (ConvertUnicodeToCharCode(charCode, &convertedCharCode))
3729               charCode = convertedCharCode;
3730             //NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]);
3731             break;
3732         }
3733     }
3734     macEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8);
3735     macEvent.when = ::TickCount();
3736     ::GetGlobalMouse(&macEvent.where);
3737     macEvent.modifiers = ::GetCurrentEventKeyModifiers();
3739   NS_OBJC_END_TRY_ABORT_BLOCK;
3742 // Key code constants
3743 enum
3745   kEscapeKeyCode      = 0x35,
3746   kRCommandKeyCode    = 0x36, // right command key
3747   kCommandKeyCode     = 0x37,
3748   kShiftKeyCode       = 0x38,
3749   kCapsLockKeyCode    = 0x39,
3750   kOptionkeyCode      = 0x3A,
3751   kControlKeyCode     = 0x3B,
3752   kRShiftKeyCode      = 0x3C, // right shift key
3753   kROptionKeyCode     = 0x3D, // right option key
3754   kRControlKeyCode    = 0x3E, // right control key
3755   kClearKeyCode       = 0x47,
3757   // function keys
3758   kF1KeyCode          = 0x7A,
3759   kF2KeyCode          = 0x78,
3760   kF3KeyCode          = 0x63,
3761   kF4KeyCode          = 0x76,
3762   kF5KeyCode          = 0x60,
3763   kF6KeyCode          = 0x61,
3764   kF7KeyCode          = 0x62,
3765   kF8KeyCode          = 0x64,
3766   kF9KeyCode          = 0x65,
3767   kF10KeyCode         = 0x6D,
3768   kF11KeyCode         = 0x67,
3769   kF12KeyCode         = 0x6F,
3770   kF13KeyCode         = 0x69,
3771   kF14KeyCode         = 0x6B,
3772   kF15KeyCode         = 0x71,
3773   
3774   kPrintScreenKeyCode = kF13KeyCode,
3775   kScrollLockKeyCode  = kF14KeyCode,
3776   kPauseKeyCode       = kF15KeyCode,
3777   
3778   // keypad
3779   kKeypad0KeyCode     = 0x52,
3780   kKeypad1KeyCode     = 0x53,
3781   kKeypad2KeyCode     = 0x54,
3782   kKeypad3KeyCode     = 0x55,
3783   kKeypad4KeyCode     = 0x56,
3784   kKeypad5KeyCode     = 0x57,
3785   kKeypad6KeyCode     = 0x58,
3786   kKeypad7KeyCode     = 0x59,
3787   kKeypad8KeyCode     = 0x5B,
3788   kKeypad9KeyCode     = 0x5C,
3790 // The following key codes are not defined until Mac OS X 10.5
3791 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
3792   kVK_ANSI_1          = 0x12,
3793   kVK_ANSI_2          = 0x13,
3794   kVK_ANSI_3          = 0x14,
3795   kVK_ANSI_4          = 0x15,
3796   kVK_ANSI_5          = 0x17,
3797   kVK_ANSI_6          = 0x16,
3798   kVK_ANSI_7          = 0x1A,
3799   kVK_ANSI_8          = 0x1C,
3800   kVK_ANSI_9          = 0x19,
3801   kVK_ANSI_0          = 0x1D,
3802 #endif
3804   kKeypadMultiplyKeyCode  = 0x43,
3805   kKeypadAddKeyCode       = 0x45,
3806   kKeypadSubtractKeyCode  = 0x4E,
3807   kKeypadDecimalKeyCode   = 0x41,
3808   kKeypadDivideKeyCode    = 0x4B,
3809   kKeypadEqualsKeyCode    = 0x51, // no correpsonding gecko key code
3810   kEnterKeyCode           = 0x4C,
3811   kReturnKeyCode          = 0x24,
3812   kPowerbookEnterKeyCode  = 0x34, // Enter on Powerbook's keyboard is different
3813   
3814   kInsertKeyCode          = 0x72, // also help key
3815   kDeleteKeyCode          = 0x75, // also forward delete key
3816   kTabKeyCode             = 0x30,
3817   kTildeKeyCode           = 0x32,
3818   kBackspaceKeyCode       = 0x33,
3819   kHomeKeyCode            = 0x73, 
3820   kEndKeyCode             = 0x77,
3821   kPageUpKeyCode          = 0x74,
3822   kPageDownKeyCode        = 0x79,
3823   kLeftArrowKeyCode       = 0x7B,
3824   kRightArrowKeyCode      = 0x7C,
3825   kUpArrowKeyCode         = 0x7E,
3826   kDownArrowKeyCode       = 0x7D
3830 static PRBool IsPrintableChar(PRUnichar aChar)
3832   return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
3835 static PRUint32 GetGeckoKeyCodeFromChar(PRUnichar aChar)
3837   // We don't support the key code for non-ASCII characters
3838   if (aChar > 0x7E)
3839     return 0;
3841   if (aChar >= 'a' && aChar <= 'z') // lowercase
3842     return PRUint32(toupper(aChar));
3843   else if (aChar >= 'A' && aChar <= 'Z') // uppercase
3844     return PRUint32(aChar);
3845   else if (aChar >= '0' && aChar <= '9')
3846     return PRUint32(aChar - '0' + NS_VK_0);
3848   switch (aChar)
3849   {
3850     case kReturnCharCode:
3851     case kEnterCharCode:
3852     case '\n':
3853       return NS_VK_RETURN;
3854     case '{':
3855     case '[':
3856       return NS_VK_OPEN_BRACKET;
3857     case '}':
3858     case ']':
3859       return NS_VK_CLOSE_BRACKET;
3860     case '\'':
3861     case '"':
3862       return NS_VK_QUOTE;
3864     case '\\':                  return NS_VK_BACK_SLASH;
3865     case ' ':                   return NS_VK_SPACE;
3866     case ';':                   return NS_VK_SEMICOLON;
3867     case '=':                   return NS_VK_EQUALS;
3868     case ',':                   return NS_VK_COMMA;
3869     case '.':                   return NS_VK_PERIOD;
3870     case '/':                   return NS_VK_SLASH;
3871     case '`':                   return NS_VK_BACK_QUOTE;
3872     case '\t':                  return NS_VK_TAB;
3873     case '-':                   return NS_VK_SUBTRACT;
3874     case '+':                   return NS_VK_ADD;
3876     default:
3877       if (!IsPrintableChar(aChar))
3878         NS_WARNING("GetGeckoKeyCodeFromChar has failed.");
3879       return 0;
3880     }
3884 static PRUint32 ConvertMacToGeckoKeyCode(UInt32 keyCode, nsKeyEvent* aKeyEvent, NSString* characters)
3886   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
3888   PRUint32 geckoKeyCode = 0;
3889   PRUnichar charCode = 0;
3890   if ([characters length])
3891     charCode = [characters characterAtIndex:0];
3893   switch (keyCode)
3894   {
3895     // modifiers. We don't get separate events for these
3896     case kEscapeKeyCode:        geckoKeyCode = NS_VK_ESCAPE;         break;
3897     case kRCommandKeyCode:
3898     case kCommandKeyCode:       geckoKeyCode = NS_VK_META;           break;
3899     case kRShiftKeyCode:
3900     case kShiftKeyCode:         geckoKeyCode = NS_VK_SHIFT;          break;
3901     case kCapsLockKeyCode:      geckoKeyCode = NS_VK_CAPS_LOCK;      break;
3902     case kRControlKeyCode:
3903     case kControlKeyCode:       geckoKeyCode = NS_VK_CONTROL;        break;
3904     case kROptionKeyCode:
3905     case kOptionkeyCode:        geckoKeyCode = NS_VK_ALT;            break;
3906     case kClearKeyCode:         geckoKeyCode = NS_VK_CLEAR;          break;
3908     // function keys
3909     case kF1KeyCode:            geckoKeyCode = NS_VK_F1;             break;
3910     case kF2KeyCode:            geckoKeyCode = NS_VK_F2;             break;
3911     case kF3KeyCode:            geckoKeyCode = NS_VK_F3;             break;
3912     case kF4KeyCode:            geckoKeyCode = NS_VK_F4;             break;
3913     case kF5KeyCode:            geckoKeyCode = NS_VK_F5;             break;
3914     case kF6KeyCode:            geckoKeyCode = NS_VK_F6;             break;
3915     case kF7KeyCode:            geckoKeyCode = NS_VK_F7;             break;
3916     case kF8KeyCode:            geckoKeyCode = NS_VK_F8;             break;
3917     case kF9KeyCode:            geckoKeyCode = NS_VK_F9;             break;
3918     case kF10KeyCode:           geckoKeyCode = NS_VK_F10;            break;
3919     case kF11KeyCode:           geckoKeyCode = NS_VK_F11;            break;
3920     case kF12KeyCode:           geckoKeyCode = NS_VK_F12;            break;
3921     // case kF13KeyCode:           geckoKeyCode = NS_VK_F13;            break;    // clash with the 3 below
3922     // case kF14KeyCode:           geckoKeyCode = NS_VK_F14;            break;
3923     // case kF15KeyCode:           geckoKeyCode = NS_VK_F15;            break;
3924     case kPauseKeyCode:         geckoKeyCode = NS_VK_PAUSE;          break;
3925     case kScrollLockKeyCode:    geckoKeyCode = NS_VK_SCROLL_LOCK;    break;
3926     case kPrintScreenKeyCode:   geckoKeyCode = NS_VK_PRINTSCREEN;    break;
3928     // keypad
3929     case kKeypad0KeyCode:       geckoKeyCode = NS_VK_NUMPAD0;        break;
3930     case kKeypad1KeyCode:       geckoKeyCode = NS_VK_NUMPAD1;        break;
3931     case kKeypad2KeyCode:       geckoKeyCode = NS_VK_NUMPAD2;        break;
3932     case kKeypad3KeyCode:       geckoKeyCode = NS_VK_NUMPAD3;        break;
3933     case kKeypad4KeyCode:       geckoKeyCode = NS_VK_NUMPAD4;        break;
3934     case kKeypad5KeyCode:       geckoKeyCode = NS_VK_NUMPAD5;        break;
3935     case kKeypad6KeyCode:       geckoKeyCode = NS_VK_NUMPAD6;        break;
3936     case kKeypad7KeyCode:       geckoKeyCode = NS_VK_NUMPAD7;        break;
3937     case kKeypad8KeyCode:       geckoKeyCode = NS_VK_NUMPAD8;        break;
3938     case kKeypad9KeyCode:       geckoKeyCode = NS_VK_NUMPAD9;        break;
3940     case kKeypadMultiplyKeyCode:  geckoKeyCode = NS_VK_MULTIPLY;     break;
3941     case kKeypadAddKeyCode:       geckoKeyCode = NS_VK_ADD;          break;
3942     case kKeypadSubtractKeyCode:  geckoKeyCode = NS_VK_SUBTRACT;     break;
3943     case kKeypadDecimalKeyCode:   geckoKeyCode = NS_VK_DECIMAL;      break;
3944     case kKeypadDivideKeyCode:    geckoKeyCode = NS_VK_DIVIDE;       break;
3946     // these may clash with forward delete and help
3947     case kInsertKeyCode:        geckoKeyCode = NS_VK_INSERT;         break;
3948     case kDeleteKeyCode:        geckoKeyCode = NS_VK_DELETE;         break;
3950     case kBackspaceKeyCode:     geckoKeyCode = NS_VK_BACK;           break;
3951     case kTabKeyCode:           geckoKeyCode = NS_VK_TAB;            break;
3952     case kHomeKeyCode:          geckoKeyCode = NS_VK_HOME;           break;
3953     case kEndKeyCode:           geckoKeyCode = NS_VK_END;            break;
3954     case kPageUpKeyCode:        geckoKeyCode = NS_VK_PAGE_UP;        break;
3955     case kPageDownKeyCode:      geckoKeyCode = NS_VK_PAGE_DOWN;      break;
3956     case kLeftArrowKeyCode:     geckoKeyCode = NS_VK_LEFT;           break;
3957     case kRightArrowKeyCode:    geckoKeyCode = NS_VK_RIGHT;          break;
3958     case kUpArrowKeyCode:       geckoKeyCode = NS_VK_UP;             break;
3959     case kDownArrowKeyCode:     geckoKeyCode = NS_VK_DOWN;           break;
3960     case kVK_ANSI_1:            geckoKeyCode = NS_VK_1;              break;
3961     case kVK_ANSI_2:            geckoKeyCode = NS_VK_2;              break;
3962     case kVK_ANSI_3:            geckoKeyCode = NS_VK_3;              break;
3963     case kVK_ANSI_4:            geckoKeyCode = NS_VK_4;              break;
3964     case kVK_ANSI_5:            geckoKeyCode = NS_VK_5;              break;
3965     case kVK_ANSI_6:            geckoKeyCode = NS_VK_6;              break;
3966     case kVK_ANSI_7:            geckoKeyCode = NS_VK_7;              break;
3967     case kVK_ANSI_8:            geckoKeyCode = NS_VK_8;              break;
3968     case kVK_ANSI_9:            geckoKeyCode = NS_VK_9;              break;
3969     case kVK_ANSI_0:            geckoKeyCode = NS_VK_0;              break;
3971     default:
3972       // if we haven't gotten the key code already, look at the char code
3973       geckoKeyCode = GetGeckoKeyCodeFromChar(charCode);
3974   }
3976   return geckoKeyCode;
3978   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
3982 static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
3984   PRBool  isSpecial;
3985   
3986   // this table is used to determine which keys are special and should not generate a charCode
3987   switch (macKeyCode)
3988   {
3989     // modifiers - we don't get separate events for these yet
3990     case kEscapeKeyCode:
3991     case kShiftKeyCode:
3992     case kRShiftKeyCode:
3993     case kCommandKeyCode:
3994     case kRCommandKeyCode:
3995     case kCapsLockKeyCode:
3996     case kControlKeyCode:
3997     case kRControlKeyCode:
3998     case kOptionkeyCode:
3999     case kROptionKeyCode:
4000     case kClearKeyCode:
4001       
4002       // function keys
4003     case kF1KeyCode:
4004     case kF2KeyCode:
4005     case kF3KeyCode:
4006     case kF4KeyCode:
4007     case kF5KeyCode:
4008     case kF6KeyCode:
4009     case kF7KeyCode:
4010     case kF8KeyCode:
4011     case kF9KeyCode:
4012     case kF10KeyCode:
4013     case kF11KeyCode:
4014     case kF12KeyCode:
4015     case kPauseKeyCode:
4016     case kScrollLockKeyCode:
4017     case kPrintScreenKeyCode:
4018       
4019     case kInsertKeyCode:
4020     case kDeleteKeyCode:
4021     case kTabKeyCode:
4022     case kBackspaceKeyCode:
4023       
4024     case kHomeKeyCode:
4025     case kEndKeyCode:
4026     case kPageUpKeyCode:
4027     case kPageDownKeyCode:
4028     case kLeftArrowKeyCode:
4029     case kRightArrowKeyCode:
4030     case kUpArrowKeyCode:
4031     case kDownArrowKeyCode:
4032     case kReturnKeyCode:
4033     case kEnterKeyCode:
4034     case kPowerbookEnterKeyCode:
4035       isSpecial = PR_TRUE;
4036       break;
4037       
4038     default:
4039       isSpecial = PR_FALSE;
4040       break;
4041   }
4042   
4043   return isSpecial;
4047 static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
4049   // this is not character inputting event, simply.
4050   if (!aEvent.isChar || !aEvent.charCode)
4051     return PR_FALSE;
4052   // if this is unicode char inputting event, we don't need to check
4053   // ctrl/alt/command keys
4054   if (aEvent.charCode > 0x7F)
4055     return PR_TRUE;
4056   // ASCII chars should be inputted without ctrl/alt/command keys
4057   return !aEvent.isControl && !aEvent.isAlt && !aEvent.isMeta;
4061 // Basic conversion for cocoa to gecko events, common to all conversions.
4062 // Note that it is OK for inEvent to be nil.
4063 - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
4065   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4067   NS_ASSERTION(outGeckoEvent, "convertGenericCocoaEvent:toGeckoEvent: requires non-null outGeckoEvent");
4068   if (!outGeckoEvent)
4069     return;
4071   outGeckoEvent->widget = [self widget];
4072   outGeckoEvent->time = PR_IntervalNow();
4073   outGeckoEvent->nativeMsg = inEvent;
4075   if (inEvent) {
4076     unsigned int modifiers = [inEvent modifierFlags];
4077     outGeckoEvent->isShift   = ((modifiers & NSShiftKeyMask) != 0);
4078     outGeckoEvent->isControl = ((modifiers & NSControlKeyMask) != 0);
4079     outGeckoEvent->isAlt     = ((modifiers & NSAlternateKeyMask) != 0);
4080     outGeckoEvent->isMeta    = ((modifiers & NSCommandKeyMask) != 0);
4081   }
4083   NS_OBJC_END_TRY_ABORT_BLOCK;
4087 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
4089   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4091   NS_ASSERTION(aMouseEvent && outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null arguments");
4092   if (!aMouseEvent || !outGeckoEvent)
4093     return;
4095   [self convertGenericCocoaEvent:aMouseEvent toGeckoEvent:outGeckoEvent];
4097   // convert point to view coordinate system
4098   NSPoint localPoint = [self convertPoint:[aMouseEvent locationInWindow] fromView:nil];
4099   outGeckoEvent->refPoint.x = static_cast<nscoord>(localPoint.x);
4100   outGeckoEvent->refPoint.y = static_cast<nscoord>(localPoint.y);
4102   NS_OBJC_END_TRY_ABORT_BLOCK;
4106 #define CHARCODE_MASK_1 0x00FF0000
4107 #define CHARCODE_MASK_2 0x000000FF
4108 #define CHARCODE_MASK   0x00FF00FF
4109 //#define DEBUG_KB 1
4111 static PRUint32
4112 KeyTranslateToUnicode(Handle aHandle, UInt32 aKeyCode, UInt32 aModifiers,
4113                       TextEncoding aEncoding)
4115 #ifdef DEBUG_KB
4116   NSLog(@"****  KeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aEncoding: %X",
4117         aHandle, aKeyCode, aModifiers, aEncoding);
4118   PRBool isShift = aModifiers & shiftKey;
4119   PRBool isCtrl = aModifiers & controlKey;
4120   PRBool isOpt = aModifiers & optionKey;
4121   PRBool isCmd = aModifiers & cmdKey;
4122   PRBool isCL = aModifiers & alphaLock;
4123   PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
4124   NSLog(@"        Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
4125         isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
4126         isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
4127 #endif
4128   UInt32 state = 0;
4129   UInt32 val =
4130     ::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
4131   // If state is not zero, it is in dead key state. Then, we need to recall
4132   // KeyTranslate for getting the actual character.
4133   if (state) {
4134     val =
4135       ::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
4136   }
4137   PRUint32 ch = 0;
4138   UInt8 buf[2];
4139   CFIndex len = 0;
4140   if (val & CHARCODE_MASK_1)
4141     buf[len++] = (val & CHARCODE_MASK_1) >> 16;
4142   buf[len++] = val & CHARCODE_MASK_2;
4144   CFStringRef str =
4145     ::CFStringCreateWithBytes(kCFAllocatorDefault, buf, len,
4146                               (CFStringEncoding)aEncoding, false);
4147   ch = ::CFStringGetLength(str) == 1 ?
4148          ::CFStringGetCharacterAtIndex(str, 0) : 0;
4149   ::CFRelease(str);
4150 #ifdef DEBUG_KB
4151   NSLog(@"       result: %X(%C)", ch, ch > ' ' ? ch : ' ');
4152 #endif
4153   return ch;
4156 static PRUint32
4157 UCKeyTranslateToUnicode(const UCKeyboardLayout* aHandle, UInt32 aKeyCode, UInt32 aModifiers,
4158                         UInt32 aKbType)
4160 #ifdef DEBUG_KB
4161   NSLog(@"**** UCKeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aKbType: %X",
4162         aHandle, aKeyCode, aModifiers, aKbType);
4163   PRBool isShift = aModifiers & shiftKey;
4164   PRBool isCtrl = aModifiers & controlKey;
4165   PRBool isOpt = aModifiers & optionKey;
4166   PRBool isCmd = aModifiers & cmdKey;
4167   PRBool isCL = aModifiers & alphaLock;
4168   PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
4169   NSLog(@"        Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
4170         isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
4171         isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
4172 #endif
4173   UInt32 deadKeyState = 0;
4174   UniCharCount len;
4175   UniChar chars[5];
4176   OSStatus err = ::UCKeyTranslate(aHandle, aKeyCode,
4177                                   kUCKeyActionDown, aModifiers >> 8,
4178                                   aKbType, kUCKeyTranslateNoDeadKeysMask,
4179                                   &deadKeyState, 5, &len, chars);
4180   PRUint32 ch = (err == noErr && len == 1) ? PRUint32(chars[0]) : 0;
4181 #ifdef DEBUG_KB
4182   NSLog(@"       result: %X(%C)", ch, ch > ' ' ? ch : ' ');
4183 #endif
4184   return ch;
4187 struct KeyTranslateData {
4188   KeyTranslateData() {
4189     mUchr.mLayout = nsnull;
4190     mUchr.mKbType = 0;
4191     mKchr.mHandle = nsnull;
4192     mKchr.mEncoding = nsnull;
4193   }
4195   // The script of the layout determines the encoding of characters obtained
4196   // from kchr resources.
4197   SInt16 mScript;
4198   // The keyboard layout identifier
4199   SInt32 mLayoutID;
4201   struct {
4202     const UCKeyboardLayout* mLayout;
4203     UInt32 mKbType;
4204   } mUchr;
4205   struct {
4206     Handle mHandle;
4207     TextEncoding mEncoding;
4208   } mKchr;
4211 static PRUint32
4212 GetUniCharFromKeyTranslate(KeyTranslateData& aData,
4213                            UInt32 aKeyCode, UInt32 aModifiers)
4215   if (aData.mUchr.mLayout) {
4216     return UCKeyTranslateToUnicode(aData.mUchr.mLayout, aKeyCode, aModifiers,
4217                                    aData.mUchr.mKbType);
4218   }
4219   if (aData.mKchr.mHandle) {
4220     return KeyTranslateToUnicode(aData.mKchr.mHandle, aKeyCode, aModifiers,
4221                                  aData.mKchr.mEncoding);
4222   }
4223   return 0;
4226 static SInt32
4227 GetScriptFromKeyboardLayout(SInt32 aLayoutID)
4229   switch (aLayoutID) {
4230     case 3:                      // German
4231     case -2:     return smRoman; // US-Extended
4232     case -18944: return smGreek; // Greek
4233     default: NS_NOTREACHED("unknown keyboard layout");
4234   }
4235   return smRoman;
4238 static CFStringRef
4239 GetInputSourceIDFromKeyboardLayout(SInt32 aLayoutID)
4241   NS_ASSERTION(nsToolkit::OnLeopardOrLater() &&
4242                Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
4243                Leopard_TISGetInputSourceProperty &&
4244                Leopard_TISCreateInputSourceList &&
4245                kOurTISPropertyUnicodeKeyLayoutData &&
4246                kOurTISPropertyInputSourceID,
4247                "GetInputSourceIDFromKeyboardLayout should only be used on Leopard or later.");
4249   KeyboardLayoutRef keylayout;
4250   if (KLGetKeyboardLayoutWithIdentifier(aLayoutID, &keylayout) != noErr)
4251     return nsnull;
4253   const void* uchrFromID;
4254   if (KLGetKeyboardLayoutProperty(keylayout, kKLuchrData, &uchrFromID) != noErr)
4255     return nsnull;
4257   CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, NULL, NULL);
4258   CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
4259   CFRelease(dict);
4261   CFStringRef sourceID = nsnull;
4262   for (CFIndex i = 0; i < CFArrayGetCount(inputSources); ++i) {
4263     TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, i)));
4264     CFDataRef data = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
4265     if (!data)
4266       continue;
4268     const UCKeyboardLayout* uchr = reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(data));
4269     if (uchr == uchrFromID) {
4270       sourceID = static_cast<CFStringRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyInputSourceID));
4271       break;
4272     }
4273   }
4275   CFRelease(inputSources);
4277   return sourceID;
4280 static PRUint32
4281 GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
4283   KeyTranslateData kt;
4284   Handle handle = ::GetResource('uchr', kKLUSKeyboard); // US keyboard layout
4285   if (!handle || !(*handle)) {
4286     NS_ERROR("US keyboard layout doesn't have uchr resource");
4287     return 0;
4288   }
4289   UInt32 kbType = 40; // ANSI, don't use actual layout
4290   return UCKeyTranslateToUnicode((UCKeyboardLayout*)(*handle), aKeyCode,
4291                                  aModifiers, kbType);
4294 - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent
4296   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4298   NS_ASSERTION(aKeyEvent && outGeckoEvent, "convertCocoaKeyEvent:toGeckoEvent: requires non-null arguments");
4299   if (!aKeyEvent || !outGeckoEvent)
4300     return;
4302   [self convertGenericCocoaEvent:aKeyEvent toGeckoEvent:outGeckoEvent];
4304   // coords for key events are always 0,0
4305   outGeckoEvent->refPoint.x = outGeckoEvent->refPoint.y = 0;
4307   // Initialize whether or not we are using charCodes to false.
4308   outGeckoEvent->isChar = PR_FALSE;
4310   // Check to see if the message is a key press that does not involve
4311   // one of our special key codes.
4312   if (outGeckoEvent->message == NS_KEY_PRESS && !IsSpecialGeckoKey([aKeyEvent keyCode])) {
4313     outGeckoEvent->isChar = PR_TRUE; // this is not a special key
4314     
4315     outGeckoEvent->charCode = 0;
4316     outGeckoEvent->keyCode  = 0; // not set for key press events
4317     
4318     NSString* chars = [aKeyEvent characters];
4319     if ([chars length] > 0)
4320       outGeckoEvent->charCode = [chars characterAtIndex:0];
4321     
4322     // convert control-modified charCode to raw charCode (with appropriate case)
4323     if (outGeckoEvent->isControl && outGeckoEvent->charCode <= 26)
4324       outGeckoEvent->charCode += (outGeckoEvent->isShift) ? ('A' - 1) : ('a' - 1);
4326     // Accel and access key handling needs to know the characters that this
4327     // key produces with Shift up or down.  So, provide this information
4328     // when Ctrl or Command or Alt is pressed.
4329     if (outGeckoEvent->isControl || outGeckoEvent->isMeta ||
4330         outGeckoEvent->isAlt) {
4331       KeyTranslateData kt;
4333       if (gOverrideKeyboardLayout.mOverrideEnabled) {
4334         kt.mLayoutID = gOverrideKeyboardLayout.mKeyboardLayout;
4335         kt.mScript = GetScriptFromKeyboardLayout(kt.mLayoutID);
4336       } else {
4337         // GetScriptManagerVariable and GetScriptVariable are both deprecated.
4338         // KLGetCurrentKeyboardLayout is newer but also deprecated in OS X
4339         // 10.5.  It's not clear from the documentation but it seems that
4340         // KLGetKeyboardLayoutProperty with kKLGroupIdentifier may provide the
4341         // script identifier for a KeyboardLayoutRef (bug 432388 comment 6).
4342         // The "Text Input Source Services" API is not available prior to OS X
4343         // 10.5.
4344         kt.mScript = ::GetScriptManagerVariable(smKeyScript);
4345         kt.mLayoutID = ::GetScriptVariable(kt.mScript, smScriptKeys);
4346       }
4348       CFDataRef uchr = NULL;
4349       // GetResource('uchr', kt.mLayoutID) fails on OS X 10.5
4350       if (nsToolkit::OnLeopardOrLater() &&
4351           Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
4352           Leopard_TISGetInputSourceProperty &&
4353           Leopard_TISCreateInputSourceList &&
4354           kOurTISPropertyUnicodeKeyLayoutData &&
4355           kOurTISPropertyInputSourceID) {
4356         if (gOverrideKeyboardLayout.mOverrideEnabled) {
4357           CFStringRef sourceID = GetInputSourceIDFromKeyboardLayout(kt.mLayoutID);
4358           NS_ASSERTION(sourceID, "unable to map keyboard layout ID to input source ID");
4359           const void* keys[] = { kOurTISPropertyInputSourceID };
4360           const void* vals[] = { sourceID };
4361           CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, NULL, NULL);
4362           CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
4363           CFRelease(dict);
4364           if (CFArrayGetCount(inputSources) == 1) {
4365             TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, 0)));
4366             uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
4367           }
4368           CFRelease(inputSources);
4369         } else {
4370           TISInputSourceRef tis = Leopard_TISCopyCurrentKeyboardLayoutInputSource();
4371           uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
4372         }
4373       }
4375       // This fails for Azeri on 10.4 even though kKLKind (2) indicates that
4376       // the layout has a uchr resource.  Perhaps KLGetKeyboardLayoutProperty
4377       // with kKLuchrData would be helpful here.
4378       Handle handle = ::GetResource('uchr', kt.mLayoutID);
4379       if (uchr) {
4380         // We should be here on OS X 10.5 for any Apple provided layout, as
4381         // they are all uchr.  It may be possible to still use kchr resources
4382         // from elsewhere, so they may be picked by
4383         // GetScriptManagerVariable(smKCHRCache) below
4384         kt.mUchr.mLayout = reinterpret_cast<const UCKeyboardLayout*>
4385           (CFDataGetBytePtr(uchr));
4386       } else if (handle) {
4387         // uchr (Unicode) keyboard layout resource prior to 10.5.
4388         kt.mUchr.mLayout = *((UCKeyboardLayout**)handle);
4389       } else {
4390         // kchr (non-Unicode) keyboard layout resource.
4392         // There are no know cases where GetResource succeeds here, and so
4393         // tests (gOverrideKeyboardLayout.mOverrideEnabled) currently end up
4394         // with no keyboard layout.  KLGetKeyboardLayoutWithIdentifier and
4395         // KLGetKeyboardLayoutProperty with kKLKCHRData would be useful here.
4396         kt.mKchr.mHandle = ::GetResource('kchr', kt.mLayoutID);
4398         if (!kt.mKchr.mHandle && !gOverrideKeyboardLayout.mOverrideEnabled)
4399           kt.mKchr.mHandle = (char**)::GetScriptManagerVariable(smKCHRCache);
4400         if (kt.mKchr.mHandle) {
4401           OSStatus err =
4402             ::GetTextEncodingFromScriptInfo(kt.mScript, kTextLanguageDontCare,
4403                                             kTextRegionDontCare,
4404                                             &kt.mKchr.mEncoding);
4405           if (err != noErr)
4406             kt.mKchr.mHandle = nsnull;
4407         }
4408       }
4410       // If a keyboard layout override is set, we also need to force the
4411       // keyboard type to something ANSI to avoid test failures on machines
4412       // with JIS keyboards (since the pair of keyboard layout and physical
4413       // keyboard type form the actual key layout).  This assumes that the
4414       // test setting the override was written assuming an ANSI keyboard.
4415       if (kt.mUchr.mLayout)
4416         kt.mUchr.mKbType = gOverrideKeyboardLayout.mOverrideEnabled ? 40 : ::LMGetKbdType();
4418       UInt32 key = [aKeyEvent keyCode];
4420       // Caps lock and num lock modifier state:
4421       UInt32 lockState = 0;
4422       if ([aKeyEvent modifierFlags] & NSAlphaShiftKeyMask)
4423         lockState |= alphaLock;
4424       if ([aKeyEvent modifierFlags] & NSNumericPadKeyMask)
4425         lockState |= kEventKeyModifierNumLockMask;
4427       // normal chars
4428       PRUint32 unshiftedChar = GetUniCharFromKeyTranslate(kt, key, lockState);
4429       UInt32 shiftLockMod = shiftKey | lockState;
4430       PRUint32 shiftedChar = GetUniCharFromKeyTranslate(kt, key, shiftLockMod);
4432       // characters generated with Cmd key
4433       // XXX we should remove CapsLock state, which changes characters from
4434       //     Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
4435       //     is pressed.
4436       UInt32 numState = (lockState & ~alphaLock); // only num lock state
4437       PRUint32 uncmdedChar = GetUniCharFromKeyTranslate(kt, key, numState);
4438       UInt32 shiftNumMod = numState | shiftKey;
4439       PRUint32 uncmdedShiftChar =
4440                  GetUniCharFromKeyTranslate(kt, key, shiftNumMod);
4441       PRUint32 uncmdedUSChar = GetUSLayoutCharFromKeyTranslate(key, numState);
4442       UInt32 cmdNumMod = cmdKey | numState;
4443       PRUint32 cmdedChar = GetUniCharFromKeyTranslate(kt, key, cmdNumMod);
4444       UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
4445       PRUint32 cmdedShiftChar =
4446         GetUniCharFromKeyTranslate(kt, key, cmdShiftNumMod);
4448       // Is the keyboard layout changed by Cmd key?
4449       // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
4450       PRBool isCmdSwitchLayout = uncmdedChar != cmdedChar;
4451       // Is the keyboard layout for Latin, but Cmd key switches the layout?
4452       // I.e., Dvorak-QWERTY
4453       PRBool isDvorakQWERTY = isCmdSwitchLayout && kt.mScript == smRoman;
4455       // If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed,
4456       // we should append unshiftedChar and shiftedChar for handling the
4457       // normal characters.  These are the characters that the user is most
4458       // likely to associate with this key.
4459       if ((unshiftedChar || shiftedChar) &&
4460           (!outGeckoEvent->isMeta || !isDvorakQWERTY)) {
4461         nsAlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
4462         outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4463       }
4465       // Most keyboard layouts provide the same characters in the NSEvents
4466       // with Command+Shift as with Command.  However, with Command+Shift we
4467       // want the character on the second level.  e.g. With a US QWERTY
4468       // layout, we want "?" when the "/","?" key is pressed with
4469       // Command+Shift.
4471       // On a German layout, the OS gives us '/' with Cmd+Shift+SS(eszett)
4472       // even though Cmd+SS is 'SS' and Shift+'SS' is '?'.  This '/' seems
4473       // like a hack to make the Cmd+"?" event look the same as the Cmd+"?"
4474       // event on a US keyboard.  The user thinks they are typing Cmd+"?", so
4475       // we'll prefer the "?" character, replacing charCode with shiftedChar
4476       // when Shift is pressed.  However, in case there is a layout where the
4477       // character unique to Cmd+Shift is the character that the user expects,
4478       // we'll send it as an alternative char.
4479       PRBool hasCmdShiftOnlyChar =
4480         cmdedChar != cmdedShiftChar && uncmdedShiftChar != cmdedShiftChar;
4481       PRUint32 originalCmdedShiftChar = cmdedShiftChar;
4483       // If we can make a good guess at the characters that the user would
4484       // expect this key combination to produce (with and without Shift) then
4485       // use those characters.  This also corrects for CapsLock, which was
4486       // ignored above.
4487       if (!isCmdSwitchLayout) {
4488         // The characters produced with Command seem similar to those without
4489         // Command.
4490         if (unshiftedChar)
4491           cmdedChar = unshiftedChar;
4492         if (shiftedChar)
4493           cmdedShiftChar = shiftedChar;
4494       } else if (uncmdedUSChar == cmdedChar) {
4495         // It looks like characters from a US layout are provided when Command
4496         // is down.
4497         PRUint32 ch = GetUSLayoutCharFromKeyTranslate(key, lockState);
4498         if (ch)
4499           cmdedChar = ch;
4500         ch = GetUSLayoutCharFromKeyTranslate(key, shiftLockMod);
4501         if (ch)
4502           cmdedShiftChar = ch;
4503       }
4505       // Only charCode (not alternativeCharCodes) is available to javascript,
4506       // so attempt to set this to the most likely intended (or most useful)
4507       // character.  Note that cmdedChar and cmdedShiftChar are usually
4508       // Latin/ASCII characters and that is what is wanted here as accel
4509       // keys are expected to be Latin characters.
4510       //
4511       // XXX We should do something similar when Control is down (bug 429510).
4512       if (outGeckoEvent->isMeta &&
4513            !(outGeckoEvent->isControl || outGeckoEvent->isAlt)) {
4515         // The character to use for charCode.
4516         PRUint32 preferredCharCode = 0;
4517         preferredCharCode = outGeckoEvent->isShift ? cmdedShiftChar : cmdedChar;
4519         if (preferredCharCode) {
4520 #ifdef DEBUG_KB
4521           if (outGeckoEvent->charCode != preferredCharCode) {
4522             NSLog(@"      charCode replaced: %X(%C) to %X(%C)",
4523                   outGeckoEvent->charCode,
4524                   outGeckoEvent->charCode > ' ' ? outGeckoEvent->charCode : ' ',
4525                   preferredCharCode,
4526                   preferredCharCode > ' ' ? preferredCharCode : ' ');
4527           }
4528 #endif
4529           outGeckoEvent->charCode = preferredCharCode;
4530         }
4531       }
4533       // If the current keyboard layout is switched by the Cmd key,
4534       // we should append cmdedChar and shiftedCmdChar that are
4535       // Latin char for the key. But don't append at Dvorak-QWERTY.
4536       if ((cmdedChar || cmdedShiftChar) &&
4537           isCmdSwitchLayout && !isDvorakQWERTY) {
4538         nsAlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
4539         outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4540       }
4541       // Special case for 'SS' key of German layout. See the comment of
4542       // hasCmdShiftOnlyChar definition for the detail.
4543       if (hasCmdShiftOnlyChar && originalCmdedShiftChar) {
4544         nsAlternativeCharCode altCharCodes(0, originalCmdedShiftChar);
4545         outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
4546       }
4547     }
4548   }
4549   else {
4550     NSString* characters = nil;
4551     if ([aKeyEvent type] != NSFlagsChanged)
4552       characters = [aKeyEvent charactersIgnoringModifiers];
4553     
4554     outGeckoEvent->keyCode = ConvertMacToGeckoKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
4555     outGeckoEvent->charCode = 0;
4556   } 
4558   if (outGeckoEvent->message == NS_KEY_PRESS && !outGeckoEvent->isMeta)
4559     [NSCursor setHiddenUntilMouseMoves:YES];
4561   NS_OBJC_END_TRY_ABORT_BLOCK;
4565 // Called from PluginKeyEventsHandler() (a handler for Carbon TSM events) to
4566 // process a Carbon key event for the currently focused plugin.  Both Unicode
4567 // characters and "Mac encoding characters" (in the MBCS or "multibyte
4568 // character system") are (or should be) available from aKeyEvent, but here we
4569 // use the MCBS characters.  This is how the WebKit does things, and seems to
4570 // be what plugins expect.
4571 - (void) processPluginKeyEvent:(EventRef)aKeyEvent
4573   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4575   if (!mGeckoChild)
4576     return;
4578   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4580   UInt32 numCharCodes;
4581   OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
4582                                         typeChar, NULL, 0, &numCharCodes, NULL);
4583   if (status != noErr)
4584     return;
4586   nsAutoTArray<unsigned char, 3> charCodes;
4587   charCodes.SetLength(numCharCodes);
4588   status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
4589                               typeChar, NULL, numCharCodes, NULL, charCodes.Elements());
4590   if (status != noErr)
4591     return;
4593   UInt32 modifiers;
4594   status = ::GetEventParameter(aKeyEvent, kEventParamKeyModifiers,
4595                                typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
4596   if (status != noErr)
4597     return;
4599   UInt32 macKeyCode;
4600   status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
4601                                typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode);
4602   if (status != noErr)
4603     return;
4605   EventRef cloneEvent = ::CopyEvent(aKeyEvent);
4606   for (unsigned int i = 0; i < numCharCodes; ++i) {
4607     status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
4608                                  typeChar, 1, charCodes.Elements() + i);
4609     if (status != noErr)
4610       break;
4612     EventRecord eventRec;
4613     if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
4614       nsKeyEvent keyDownEvent(PR_TRUE, NS_KEY_DOWN, mGeckoChild);
4616       PRUint32 keyCode(ConvertMacToGeckoKeyCode(macKeyCode, &keyDownEvent, @""));
4617       PRUint32 charCode(charCodes.ElementAt(i));
4619       keyDownEvent.time       = PR_IntervalNow();
4620       keyDownEvent.nativeMsg  = &eventRec;
4621       if (IsSpecialGeckoKey(macKeyCode)) {
4622         keyDownEvent.keyCode  = keyCode;
4623       } else {
4624         keyDownEvent.charCode = charCode;
4625         keyDownEvent.isChar   = PR_TRUE;
4626       }
4627       keyDownEvent.isShift   = ((modifiers & shiftKey) != 0);
4628       keyDownEvent.isControl = ((modifiers & controlKey) != 0);
4629       keyDownEvent.isAlt     = ((modifiers & optionKey) != 0);
4630       keyDownEvent.isMeta    = ((modifiers & cmdKey) != 0); // Should never happen
4631       mGeckoChild->DispatchWindowEvent(keyDownEvent);
4632       if (!mGeckoChild)
4633         break;
4634     }
4635   }
4637   ::ReleaseEvent(cloneEvent);
4639   NS_OBJC_END_TRY_ABORT_BLOCK;
4643 - (nsRect)sendCompositionEvent:(PRInt32) aEventType
4645 #ifdef DEBUG_IME
4646   NSLog(@"****in sendCompositionEvent; type = %d", aEventType);
4647 #endif
4649   if (!mGeckoChild)
4650     return nsRect(0, 0, 0, 0);
4652   // static void init_composition_event( *aEvent, int aType)
4653   nsCompositionEvent event(PR_TRUE, aEventType, mGeckoChild);
4654   event.time = PR_IntervalNow();
4655   mGeckoChild->DispatchWindowEvent(event);
4656   return event.theReply.mCursorPosition;
4660 - (void)sendTextEvent:(PRUnichar*) aBuffer 
4661                       attributedString:(NSAttributedString*) aString  
4662                       selectedRange:(NSRange) selRange 
4663                       markedRange:(NSRange) markRange
4664                       doCommit:(BOOL) doCommit
4666 #ifdef DEBUG_IME
4667   NSLog(@"****in sendTextEvent; string = '%@'", aString);
4668   NSLog(@" markRange = %d, %d;  selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length);
4669 #endif
4671   if (!mGeckoChild)
4672     return;
4674   nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild);
4675   textEvent.time = PR_IntervalNow();
4676   textEvent.theText = aBuffer;
4677   if (!doCommit)
4678     FillTextRangeInTextEvent(&textEvent, aString, markRange, selRange);
4680   mGeckoChild->DispatchWindowEvent(textEvent);
4681   if (textEvent.rangeArray)
4682     delete [] textEvent.rangeArray;
4686 #pragma mark -
4687 // NSTextInput implementation
4689 #define MAX_BUFFER_SIZE 32
4692 - (void)insertText:(id)insertString
4694   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4696 #if DEBUG_IME
4697   NSLog(@"****in insertText: '%@'", insertString);
4698   NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4699 #endif
4700   if (!mGeckoChild)
4701     return;
4703   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4705   if (![insertString isKindOfClass:[NSAttributedString class]])
4706     insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease];
4708   NSString *tmpStr = [insertString string];
4709   unsigned int len = [tmpStr length];
4710   if (!nsTSMManager::IsComposing() && len == 0)
4711     return; // nothing to do
4712   PRUnichar buffer[MAX_BUFFER_SIZE];
4713   PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
4714   [tmpStr getCharacters:bufPtr];
4715   bufPtr[len] = PRUnichar('\0');
4717   if (len == 1 && !nsTSMManager::IsComposing()) {
4718     // don't let the same event be fired twice when hitting
4719     // enter/return! (Bug 420502)
4720     if (mKeyPressSent)
4721       return;
4723     // dispatch keypress event with char instead of textEvent
4724     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
4725     geckoEvent.time      = PR_IntervalNow();
4726     geckoEvent.charCode  = bufPtr[0]; // gecko expects OS-translated unicode
4727     geckoEvent.keyCode   = 0;
4728     geckoEvent.isChar    = PR_TRUE;
4729     if (mKeyDownHandled)
4730       geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
4731     // don't set other modifiers from the current event, because here in
4732     // -insertText: they've already been taken into account in creating
4733     // the input string.
4734         
4735     // create native EventRecord for use by plugins
4736     EventRecord macEvent;
4737     if (mCurKeyEvent) {
4738       // XXX The ASCII characters inputting mode of egbridge (Japanese IME)
4739       // might send the keyDown event with wrong keyboard layout if other
4740       // keyboard layouts are already loaded. In that case, the native event
4741       // doesn't match to this gecko event...
4742       ConvertCocoaKeyEventToMacEvent(mCurKeyEvent, macEvent);
4743       geckoEvent.nativeMsg = &macEvent;
4744       geckoEvent.isShift   = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
4745       if (!IsPrintableChar(geckoEvent.charCode)) {
4746         geckoEvent.keyCode = 
4747           ConvertMacToGeckoKeyCode([mCurKeyEvent keyCode], &geckoEvent,
4748                                    [mCurKeyEvent charactersIgnoringModifiers]);
4749         geckoEvent.charCode = 0;
4750       }
4751     } else {
4752       // Note that insertText is not called only at key pressing.
4753       if (!IsPrintableChar(geckoEvent.charCode)) {
4754         geckoEvent.keyCode = GetGeckoKeyCodeFromChar(geckoEvent.charCode);
4755         geckoEvent.charCode = 0;
4756       }
4757     }
4759     mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
4760     mKeyPressSent = YES;
4761   }
4762   else {
4763     if (!nsTSMManager::IsComposing()) {
4764       [self sendCompositionEvent:NS_COMPOSITION_START];
4765       // Note: mGeckoChild might have become null here. Don't count on it from here on.
4766       nsTSMManager::StartComposing(self);
4767       // Note: mGeckoChild might have become null here. Don't count on it from here on.
4768     }
4770     if (nsTSMManager::IgnoreCommit()) {
4771       tmpStr = [tmpStr init];
4772       len = 0;
4773       bufPtr[0] = PRUnichar('\0');
4774       insertString =
4775         [[[NSAttributedString alloc] initWithString:tmpStr] autorelease];
4776     }
4777     [self sendTextEvent:bufPtr attributedString:insertString
4778                                selectedRange:NSMakeRange(0, len)
4779                                markedRange:mMarkedRange
4780                                doCommit:YES];
4781     // Note: mGeckoChild might have become null here. Don't count on it from here on.
4783     [self sendCompositionEvent:NS_COMPOSITION_END];
4784     // Note: mGeckoChild might have become null here. Don't count on it from here on.
4785     nsTSMManager::EndComposing();
4786     mMarkedRange = NSMakeRange(NSNotFound, 0);
4787   }
4789   if (bufPtr != buffer)
4790     delete[] bufPtr;
4792   NS_OBJC_END_TRY_ABORT_BLOCK;
4796 - (void)insertNewline:(id)sender
4798   [self insertText:@"\n"];
4802 - (void) doCommandBySelector:(SEL)aSelector
4804   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4806 #if DEBUG_IME 
4807   NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
4808 #endif
4810   if (!mKeyPressHandled)
4811     [super doCommandBySelector:aSelector];
4813   NS_OBJC_END_TRY_ABORT_BLOCK;
4817 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
4819   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
4821 #if DEBUG_IME 
4822   NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length);
4823   NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
4824   NSLog(@" aString = '%@'", aString);
4825 #endif
4827   nsAutoRetainCocoaObject kungFuDeathGrip(self);
4829   if (![aString isKindOfClass:[NSAttributedString class]])
4830     aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
4832   NSMutableAttributedString *mutableAttribStr = aString;
4833   NSString *tmpStr = [mutableAttribStr string];
4834   unsigned int len = [tmpStr length];
4835   PRUnichar buffer[MAX_BUFFER_SIZE];
4836   PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
4837   [tmpStr getCharacters:bufPtr];
4838   bufPtr[len] = PRUnichar('\0');
4840 #if DEBUG_IME 
4841   printf("****in setMarkedText, len = %d, text = ", len);
4842   PRUint32 n = 0;
4843   PRUint32 maxlen = len > 12 ? 12 : len;
4844   for (PRUnichar *a = bufPtr; (*a != PRUnichar('\0')) && n<maxlen; a++, n++)
4845     printf((*a&0xff80) ? "\\u%4X" : "%c", *a); 
4846   printf("\n");
4847 #endif
4849   mMarkedRange.length = len;
4851   if (!nsTSMManager::IsComposing() && len > 0) {
4852     nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
4853     mGeckoChild->DispatchWindowEvent(selection);
4854     mMarkedRange.location = selection.mSucceeded ? selection.mReply.mOffset : 0;
4855     [self sendCompositionEvent:NS_COMPOSITION_START];
4856     // Note: mGeckoChild might have become null here. Don't count on it from here on.
4857     nsTSMManager::StartComposing(self);
4858     // Note: mGeckoChild might have become null here. Don't count on it from here on.
4859   }
4861   if (nsTSMManager::IsComposing()) {
4862     nsTSMManager::UpdateComposing(tmpStr);
4864     BOOL commit = len == 0;
4865     [self sendTextEvent:bufPtr attributedString:aString
4866                                   selectedRange:selRange
4867                                     markedRange:mMarkedRange
4868                                        doCommit:commit];
4869     // Note: mGeckoChild might have become null here. Don't count on it from here on.
4871     if (commit) {
4872       [self sendCompositionEvent:NS_COMPOSITION_END];
4873       nsTSMManager::EndComposing();
4874     }
4875   }
4877   if (bufPtr != buffer)
4878     delete[] bufPtr;
4880   NS_OBJC_END_TRY_ABORT_BLOCK;
4884 - (void) unmarkText
4886 #if DEBUG_IME
4887   NSLog(@"****in unmarkText");
4888   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
4889 #endif
4890   nsTSMManager::CommitIME();
4894 - (BOOL) hasMarkedText
4896 #if DEBUG_IME
4897   NSLog(@"****in hasMarkText");
4898   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
4899 #endif
4900   return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
4904 - (long) conversationIdentifier
4906 #if DEBUG_IME
4907   NSLog(@"****in conversationIdentifier");
4908 #endif
4909   if (!mGeckoChild)
4910     return (long)self;
4911   nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
4912   textContent.InitForQueryTextContent(0, 0);
4913   mGeckoChild->DispatchWindowEvent(textContent);
4914   if (!textContent.mSucceeded)
4915     return (long)self;
4916 #if DEBUG_IME
4917   NSLog(@" the ID = %ld", (long)textContent.mReply.mContentsRoot);
4918 #endif
4919   return (long)textContent.mReply.mContentsRoot;
4923 - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
4925   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
4927 #if DEBUG_IME
4928   NSLog(@"****in attributedSubstringFromRange");
4929   NSLog(@" theRange      = %d, %d", theRange.location, theRange.length);
4930   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
4931 #endif
4932   if (!mGeckoChild || theRange.length == 0)
4933     return nil;
4935   nsAutoString str;
4936   nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
4937   textContent.InitForQueryTextContent(theRange.location, theRange.length);
4938   mGeckoChild->DispatchWindowEvent(textContent);
4940   if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())
4941     return nil;
4943   NSString* nsstr = ToNSString(textContent.mReply.mString);
4944   NSAttributedString* result =
4945     [[[NSAttributedString alloc] initWithString:nsstr
4946                                      attributes:nil] autorelease];
4947   return result;
4949   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
4953 - (NSRange) markedRange
4955   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
4957 #if DEBUG_IME
4958   NSLog(@"****in markedRange");
4959   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
4960 #endif
4962   if (![self hasMarkedText]) {
4963     return NSMakeRange(NSNotFound, 0);
4964   }
4966   return mMarkedRange;
4968   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
4972 - (NSRange) selectedRange
4974   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
4976 #if DEBUG_IME
4977   NSLog(@"****in selectedRange");
4978   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
4979 #endif
4980   if (!mGeckoChild)
4981     return NSMakeRange(NSNotFound, 0);
4982   nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
4983   mGeckoChild->DispatchWindowEvent(selection);
4984   if (!selection.mSucceeded)
4985     return NSMakeRange(NSNotFound, 0);
4987 #if DEBUG_IME
4988   NSLog(@" result of selectedRange = %d, %d",
4989         selection.mReply.mOffset, selection.mReply.mString.Length());
4990 #endif
4991   return NSMakeRange(selection.mReply.mOffset,
4992                      selection.mReply.mString.Length());
4994   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
4998 - (NSRect) firstRectForCharacterRange:(NSRange)theRange
5000   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5002 #if DEBUG_IME
5003   NSLog(@"****in firstRectForCharacterRange");
5004   NSLog(@" theRange      = %d, %d", theRange.location, theRange.length);
5005   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
5006 #endif
5007   // XXX this returns first character rect or caret rect, it is limitation of
5008   // now. We need more work for returns first line rect. But current
5009   // implementation is enough for IMEs.
5011   NSRect rect;
5012   if (!mGeckoChild || theRange.location == NSNotFound)
5013     return rect;
5015   nsRect r;
5016   PRBool useCaretRect = theRange.length == 0;
5017   if (!useCaretRect) {
5018     nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild);
5019     charRect.InitForQueryCharacterRect(theRange.location);
5020     mGeckoChild->DispatchWindowEvent(charRect);
5021     if (charRect.mSucceeded)
5022       r = charRect.mReply.mRect;
5023     else
5024       useCaretRect = PR_TRUE;
5025   }
5027   if (useCaretRect) {
5028     nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, mGeckoChild);
5029     caretRect.InitForQueryCaretRect(theRange.location);
5030     mGeckoChild->DispatchWindowEvent(caretRect);
5031     if (!caretRect.mSucceeded)
5032       return rect;
5033     r = caretRect.mReply.mRect;
5034     r.width = 0;
5035   }
5037   nsIWidget* rootWidget = mGeckoChild->GetTopLevelWidget();
5038   NSWindow* rootWindow =
5039     static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
5040   NSView* rootView =
5041     static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
5042   if (!rootWindow || !rootView)
5043     return rect;
5044   GeckoRectToNSRect(r, rect);
5045   rect = [rootView convertRect:rect toView:nil];
5046   rect.origin = [rootWindow convertBaseToScreen:rect.origin];
5047 #if DEBUG_IME
5048   NSLog(@" result rect (x,y,w,h) = %f, %f, %f, %f",
5049         rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
5050 #endif
5051   return rect;
5053   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
5057 - (unsigned int)characterIndexForPoint:(NSPoint)thePoint
5059 #if DEBUG_IME
5060   NSLog(@"****in characterIndexForPoint");
5061   NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5062 #endif
5064   // To implement this, we'd have to grovel in text frames looking at text offsets.
5065   return 0;
5069 - (NSArray*) validAttributesForMarkedText
5071   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5073 #if DEBUG_IME
5074   NSLog(@"****in validAttributesForMarkedText");
5075   NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
5076 #endif
5078   //return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
5079   return [NSArray array]; // empty array; we don't support any attributes right now
5081   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5085 #pragma mark -
5088 + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent
5090   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5092   NSEvent* newEvent = [NSEvent keyEventWithType:type
5093                                        location:[theEvent locationInWindow] 
5094                                   modifierFlags:[theEvent modifierFlags]
5095                                       timestamp:[theEvent timestamp]
5096                                    windowNumber:[theEvent windowNumber]
5097                                         context:[theEvent context]
5098                                      characters:[theEvent characters]
5099                     charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
5100                                       isARepeat:[theEvent isARepeat]
5101                                         keyCode:[theEvent keyCode]];
5102   return newEvent;
5104   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5107 #ifdef PR_LOGGING
5108 static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
5110   for (PRUint32 i = 0; i < [aString length]; ++i) {
5111     unichar ch = [aString characterAtIndex:i];
5112     if (ch >= 32 && ch < 128) {
5113       aBuf.Append(char(ch));
5114     } else {
5115       aBuf += nsPrintfCString("\\u%04x", ch);
5116     }
5117   }
5118   return aBuf.get();
5120 #endif
5122 // Returns PR_TRUE if Gecko claims to have handled the event, PR_FALSE otherwise.
5123 - (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv
5125   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5127   if (!mGeckoChild)
5128     return NO;
5130 #ifdef PR_LOGGING
5131   nsCAutoString str1;
5132   nsCAutoString str2;
5133 #endif
5134   PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5135          ("ChildView processKeyDownEvent: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5136           [theEvent keyCode], [theEvent modifierFlags],
5137           ToEscapedString([theEvent characters], str1),
5138           ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
5140   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5141   mCurKeyEvent = theEvent;
5143   BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
5144   if (nonDeadKeyPress) {
5145     if (![theEvent isARepeat]) {
5146       NSResponder* firstResponder = [[self window] firstResponder];
5148       nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
5149       [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5151       // create native EventRecord for use by plugins
5152       EventRecord macEvent;
5153       ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5154       geckoEvent.nativeMsg = &macEvent;
5156       mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5157       if (!mGeckoChild)
5158         return mKeyDownHandled;
5160       // The key down event may have shifted the focus, in which
5161       // case we should not fire the key press.
5162       if (firstResponder != [[self window] firstResponder]) {
5163         PRBool handled = mKeyDownHandled;
5164         mCurKeyEvent = nil;
5165         mKeyDownHandled = PR_FALSE;
5166         return handled;
5167       }
5168     }
5170     // If this is the context menu key command, send a context menu key event.
5171     unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5172     if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
5173       nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey);
5174       contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
5175       PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent);
5176       [self maybeInitContextMenuTracking];
5177       // Bail, there is nothing else to do here.
5178       PRBool handled = (cmEventHandled || mKeyDownHandled);
5179       mCurKeyEvent = nil;
5180       mKeyDownHandled = PR_FALSE;
5181       return handled;
5182     }
5184     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
5185     [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5187     // if this is a non-letter keypress, or the control key is down,
5188     // dispatch the keydown to gecko, so that we trap delete,
5189     // control-letter combinations etc before Cocoa tries to use
5190     // them for keybindings.
5191     if ((!geckoEvent.isChar || geckoEvent.isControl) &&
5192         !nsTSMManager::IsComposing()) {
5193       if (mKeyDownHandled)
5194         geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5196       // create native EventRecord for use by plugins
5197       EventRecord macEvent;
5198       ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5199       geckoEvent.nativeMsg = &macEvent;
5201       mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5202       mKeyPressSent = YES;
5203       if (!mGeckoChild)
5204         return (mKeyDownHandled || mKeyPressHandled);
5205     }
5206   }
5208   // We need to initialize the TSMDocument *before* interpretKeyEvents when
5209   // IME is enabled.
5210   if (!isKeyEquiv && nsTSMManager::IsIMEEnabled()) {
5211     // We need to get actual focused view. E.g., the view is in bookmark dialog
5212     // that is <panel> element. Then, the key events are processed the parent
5213     // window's view that has native focus.
5214     nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT,
5215                                     mGeckoChild);
5216     textContent.InitForQueryTextContent(0, 0);
5217     mGeckoChild->DispatchWindowEvent(textContent);
5218     NSView<mozView>* focusedView = self;
5219     if (textContent.mSucceeded && textContent.mReply.mFocusedWidget) {
5220       NSView<mozView>* view =
5221         static_cast<NSView<mozView>*>(textContent.mReply.mFocusedWidget->
5222                                       GetNativeData(NS_NATIVE_WIDGET));
5223       if (view)
5224         focusedView = view;
5225     }
5226     nsTSMManager::InitTSMDocument(focusedView);
5227   }
5229   // Let Cocoa interpret the key events, caching IsComposing first.
5230   // We don't do it if this came from performKeyEquivalent because
5231   // interpretKeyEvents isn't set up to handle those key combinations.
5232   PRBool wasComposing = nsTSMManager::IsComposing();
5233   PRBool interpretKeyEventsCalled = PR_FALSE;
5234   if (!isKeyEquiv &&
5235       (nsTSMManager::IsIMEEnabled() || nsTSMManager::IsRomanKeyboardsOnly())) {
5236     [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
5237     interpretKeyEventsCalled = PR_TRUE;
5238   }
5240   if (!mGeckoChild)
5241     return (mKeyDownHandled || mKeyPressHandled);;
5243   if (!mKeyPressSent && nonDeadKeyPress && !wasComposing && !nsTSMManager::IsComposing()) {
5244     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
5245     [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5247     // If we called interpretKeyEvents and this isn't normal character input
5248     // then IME probably ate the event for some reason. We do not want to
5249     // send a key press event in that case.
5250     if (!(interpretKeyEventsCalled && IsNormalCharInputtingEvent(geckoEvent))) {
5251       if (mKeyDownHandled)
5252         geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5254       // create native EventRecord for use by plugins
5255       EventRecord macEvent;
5256       ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5257       geckoEvent.nativeMsg = &macEvent;
5259       mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5260     }
5261   }
5263   // Note: mGeckoChild might have become null here. Don't count on it from here on.
5265   PRBool handled = (mKeyDownHandled || mKeyPressHandled);
5267   // See note about nested event loops where these variables are declared in header.
5268   mKeyPressHandled = NO;
5269   mKeyPressSent = NO;
5270   mCurKeyEvent = nil;
5271   mKeyDownHandled = PR_FALSE;
5273   return handled;
5275   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5279 // Create a TSM document for use with plugins, so that we can support IME in
5280 // them.  Once it's created, if need be (re)activate it.  Some plugins (e.g.
5281 // the Flash plugin running in Camino) don't create their own TSM document --
5282 // without which IME can't work.  Others (e.g. the Flash plugin running in
5283 // Firefox) create a TSM document that (somehow) makes the input window behave
5284 // badly when it contains more than one kind of input (say Hiragana and
5285 // Romaji).  (We can't just use the per-NSView TSM documents that Cocoa
5286 // provices (those created and managed by the NSTSMInputContext class) -- for
5287 // some reason TSMProcessRawKeyEvent() doesn't work with them.)
5288 - (void)activatePluginTSMDoc
5290   if (!mPluginTSMDoc) {
5291     // Create a TSM document that supports both non-Unicode and Unicode input.
5292     // Though [ChildView processPluginKeyEvent:] only sends Mac char codes to
5293     // the plugin, this makes the input window behave better when it contains
5294     // more than one kind of input (say Hiragana and Romaji).  This is what
5295     // the OS does when it creates a TSM document for use by an
5296     // NSTSMInputContext class.
5297     InterfaceTypeList supportedServices;
5298     supportedServices[0] = kTextServiceDocumentInterfaceType;
5299     supportedServices[1] = kUnicodeDocumentInterfaceType;
5300     ::NewTSMDocument(2, supportedServices, &mPluginTSMDoc, 0);
5301     // We'll need to use the "input window".
5302     ::UseInputWindow(mPluginTSMDoc, YES);
5303     ::ActivateTSMDocument(mPluginTSMDoc);
5304   } else if (::TSMGetActiveDocument() != mPluginTSMDoc) {
5305     ::ActivateTSMDocument(mPluginTSMDoc);
5306   }
5310 - (void)keyDown:(NSEvent*)theEvent
5312   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5314   // If a plugin has the focus, we need to use an alternate method for
5315   // handling NSKeyDown and NSKeyUp events (otherwise Carbon-based IME won't
5316   // work in plugins like the Flash plugin).  The same strategy is used by the
5317   // WebKit.  See PluginKeyEventsHandler() and [ChildView processPluginKeyEvent:]
5318   // for more info.
5319   if (mGeckoChild && mIsPluginView) {
5320     [self activatePluginTSMDoc];
5321     // We use the active TSM document to pass a pointer to ourselves (the
5322     // currently focused ChildView) to PluginKeyEventsHandler().  Because this
5323     // pointer is weak, we should retain and release ourselves around the call
5324     // to TSMProcessRawKeyEvent().
5325     nsAutoRetainCocoaObject kungFuDeathGrip(self);
5326     ::TSMSetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
5327                              sizeof(ChildView *), &self);
5328     ::TSMProcessRawKeyEvent([theEvent _eventRef]);
5329     ::TSMRemoveDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag);
5330     return;
5331   }
5333   [self processKeyDownEvent:theEvent keyEquiv:NO];
5335   NS_OBJC_END_TRY_ABORT_BLOCK;
5339 static BOOL keyUpAlreadySentKeyDown = NO;
5341 - (void)keyUp:(NSEvent*)theEvent
5343   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5345 #ifdef PR_LOGGING
5346   nsCAutoString str1;
5347   nsCAutoString str2;
5348 #endif
5349   PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
5350          ("ChildView keyUp: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
5351           [theEvent keyCode], [theEvent modifierFlags],
5352           ToEscapedString([theEvent characters], str1),
5353           ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
5355   if (!mGeckoChild)
5356     return;
5358   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5360   if (mIsPluginView) {
5361     // I'm not sure the call to TSMProcessRawKeyEvent() is needed here (though
5362     // WebKit makes one).
5363     ::TSMProcessRawKeyEvent([theEvent _eventRef]);
5365     // Don't send a keyUp event if the corresponding keyDown event(s) is/are
5366     // still being processed (idea borrowed from WebKit).
5367     ChildView *keyDownTarget = nil;
5368     OSStatus status = ::TSMGetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
5369                                                sizeof(ChildView *), nil, &keyDownTarget);
5370     if (status != noErr)
5371       keyDownTarget = nil;
5372     if (keyDownTarget == self)
5373       return;
5375     // PluginKeyEventsHandler() never sends keyUp events to [ChildView
5376     // processPluginKeyEvent:], so we need to send them to Gecko here.  (This
5377     // means that when commiting text from IME, several keyDown events may be
5378     // sent to Gecko (in processPluginKeyEvent) for one keyUp event here.
5379     // But this is how the WebKit does it, and games expect a keyUp event to
5380     // be sent when it actually happens (they need to be able to detect how
5381     // long a key has been held down) -- which wouldn't be possible if we sent
5382     // them from processPluginKeyEvent.)
5383     nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, nsnull);
5384     [self convertCocoaKeyEvent:theEvent toGeckoEvent:&keyUpEvent];
5385     EventRecord macKeyUpEvent;
5386     ConvertCocoaKeyEventToMacEvent(theEvent, macKeyUpEvent);
5387     keyUpEvent.nativeMsg = &macKeyUpEvent;
5388     mGeckoChild->DispatchWindowEvent(keyUpEvent);
5389     return;
5390   }
5392   // if we don't have any characters we can't generate a keyUp event
5393   if ([[theEvent characters] length] == 0)
5394     return;
5396   // Cocoa doesn't send an NSKeyDown event for control-tab on 10.4, so if this
5397   // is an NSKeyUp event for control-tab, send a down event to gecko first.
5398   if (!nsToolkit::OnLeopardOrLater() && !keyUpAlreadySentKeyDown &&
5399       [theEvent modifierFlags] & NSControlKeyMask && [theEvent keyCode] == kTabKeyCode) {
5400     // We'll need an NSKeyDown copy of our native event so we convert to a gecko event correctly.
5401     NSEvent* nativeKeyDownEvent = [ChildView makeNewCocoaEventWithType:NSKeyDown fromEvent:theEvent];
5403     // send a key down event if we should
5404     PRBool keyDownHandled = PR_FALSE;
5405     if (![nativeKeyDownEvent isARepeat]) {
5406       nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
5407       [self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
5409       // create native EventRecord for use by plugins
5410       EventRecord macEvent;
5411       ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
5412       geckoEvent.nativeMsg = &macEvent;
5414       keyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
5415       if (!mGeckoChild)
5416         return;
5417     }
5419     // Check to see if we are still the first responder.
5420     // The key down event may have shifted the focus, in which
5421     // case we should not fire the key press.
5422     NSResponder* resp = [[self window] firstResponder];
5423     if (resp != (NSResponder*)self) {
5424       keyUpAlreadySentKeyDown = YES;
5425       [resp keyUp:theEvent];      
5426       keyUpAlreadySentKeyDown = NO;
5427       return;
5428     }
5430     // now send a key press event if we should
5431     if (!nsTSMManager::IsComposing()) {
5432       nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
5433       [self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
5435       if (keyDownHandled)
5436         geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
5438       // create native EventRecord for use by plugins
5439       EventRecord macEvent;
5440       ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
5441       geckoEvent.nativeMsg = &macEvent;
5443       mGeckoChild->DispatchWindowEvent(geckoEvent);
5444       if (!mGeckoChild)
5445         return;
5446     }
5447   }
5449   nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_UP, nsnull);
5450   [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5452   // create native EventRecord for use by plugins
5453   EventRecord macEvent;
5454   ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
5455   geckoEvent.nativeMsg = &macEvent;
5457   mGeckoChild->DispatchWindowEvent(geckoEvent);
5459   NS_OBJC_END_TRY_ABORT_BLOCK;
5463 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
5465   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5467   // don't do anything if we don't have a gecko widget
5468   if (!mGeckoChild)
5469     return NO;
5471   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5473   // If we're not the first responder and the first responder is an NSView
5474   // object, pass the event on.  Otherwise (if, for example, the first
5475   // responder is an NSWindow object) we should trust the OS to have called
5476   // us correctly.
5477   id firstResponder = [[self window] firstResponder];
5478   if (firstResponder != self) {
5479     // Special handling if the other first responder is a ChildView.
5480     if ([firstResponder isKindOfClass:[ChildView class]])
5481       return [(ChildView *)firstResponder performKeyEquivalent:theEvent];
5482     if ([firstResponder isKindOfClass:[NSView class]])
5483       return [super performKeyEquivalent:theEvent];
5484   }
5486   // don't process if we're composing, but don't consume the event
5487   if (nsTSMManager::IsComposing())
5488     return NO;
5490   // Set to true if embedding menus handled the event when a plugin has focus.
5491   // We give menus a crack at handling commands before Gecko in the plugin case.
5492   BOOL handledByEmbedding = NO;
5494   // Perform native menu UI feedback even if we stop the event from propagating to it normally.
5495   // Recall that the menu system won't actually execute any commands for keyboard command invocations.
5496   //
5497   // If this is a plugin, we do actually perform the action on keyboard commands. See bug 428047.
5498   // If the action on plugins here changes the first responder, don't continue.
5499   NSMenu* mainMenu = [NSApp mainMenu];
5500   if (mIsPluginView) {
5501     if ([mainMenu isKindOfClass:[GeckoNSMenu class]]) {
5502       [(GeckoNSMenu*)mainMenu actOnKeyEquivalent:theEvent];
5503     }
5504     else {
5505       // This is probably an embedding situation. If the native menu handle the event
5506       // then return YES from pKE no matter what Gecko or the plugin does.
5507       handledByEmbedding = [mainMenu performKeyEquivalent:theEvent];
5508     }
5509     if ([[self window] firstResponder] != self)
5510       return YES;
5511   }
5512   else {
5513     if ([mainMenu isKindOfClass:[GeckoNSMenu class]])
5514       [(GeckoNSMenu*)mainMenu performMenuUserInterfaceEffectsForEvent:theEvent];
5515   }
5517   // With Cmd key or Ctrl+Tab or Ctrl+Esc, keyDown will be never called.
5518   // Therefore, we need to call processKeyDownEvent from performKeyEquivalent.
5519   UInt32 modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5520   UInt32 keyCode = [theEvent keyCode];
5521   PRBool keyDownNeverFiredEvent = (modifierFlags & NSCommandKeyMask) ||
5522            ((modifierFlags & NSControlKeyMask) &&
5523             (keyCode == kEscapeKeyCode || keyCode == kTabKeyCode));
5525   // don't handle this if certain modifiers are down - those should
5526   // be sent as normal key up/down events and cocoa will do so automatically
5527   // if we reject here
5528   if (!keyDownNeverFiredEvent &&
5529       (modifierFlags & (NSFunctionKeyMask| NSNumericPadKeyMask)))
5530     return handledByEmbedding;
5532   // Control and option modifiers are used when changing input sources in the
5533   // input menu. We need to send such key events via "keyDown:", which will
5534   // happen if we return NO here. This only applies to Mac OS X 10.5 and higher,
5535   // previous OS versions just call "keyDown:" and not "performKeyEquivalent:"
5536   // for such events.
5537   if (!keyDownNeverFiredEvent &&
5538       (modifierFlags & (NSControlKeyMask | NSAlternateKeyMask)))
5539     return handledByEmbedding;
5541   if ([theEvent type] == NSKeyDown) {
5542     // We trust the Gecko handled status for cmd key events. See bug 417466 for more info.
5543     if (modifierFlags & NSCommandKeyMask)
5544       return ([self processKeyDownEvent:theEvent keyEquiv:YES] || handledByEmbedding);
5545     else
5546       [self processKeyDownEvent:theEvent keyEquiv:YES];
5547   }
5549   return YES;
5551   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5555 - (void)flagsChanged:(NSEvent*)theEvent
5557   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5559   if (!mGeckoChild)
5560     return;
5562   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5564   // Fire key up/down events for the modifier keys (shift, alt, ctrl, command).
5565   if ([theEvent type] == NSFlagsChanged) {
5566     unsigned int modifiers =
5567       [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
5568     const PRUint32 kModifierMaskTable[] =
5569       { NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask, NSCommandKeyMask };
5570     const PRUint32 kModifierCount = sizeof(kModifierMaskTable) /
5571                                     sizeof(kModifierMaskTable[0]);
5573     for (PRUint32 i = 0; i < kModifierCount; i++) {
5574       PRUint32 modifierBit = kModifierMaskTable[i];
5575       if ((modifiers & modifierBit) != (mLastModifierState & modifierBit)) {
5576         PRUint32 message = ((modifiers & modifierBit) != 0 ? NS_KEY_DOWN :
5577                                                              NS_KEY_UP);
5579         // Fire a key event.
5580         nsKeyEvent geckoEvent(PR_TRUE, message, nsnull);
5581         [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
5583         // create native EventRecord for use by plugins
5584         EventRecord macEvent;
5585         ConvertCocoaKeyEventToMacEvent(theEvent, macEvent, message);
5586         geckoEvent.nativeMsg = &macEvent;
5588         mGeckoChild->DispatchWindowEvent(geckoEvent);
5589         if (!mGeckoChild)
5590           return;
5592         // Stop if focus has changed.
5593         // Check to see if we are still the first responder.
5594         NSResponder* resp = [[self window] firstResponder];
5595         if (resp != (NSResponder*)self)
5596           break;
5597       }
5598     }
5600     mLastModifierState = modifiers;
5601   }
5603   // check if the hand scroll cursor needs to be set/unset
5604   [self setHandScrollCursor:theEvent];
5606   NS_OBJC_END_TRY_ABORT_BLOCK;
5610 // This method is called when are are about to lose focus.
5611 // We must always call through to our superclass, even when mGeckoChild is
5612 // nil -- otherwise the keyboard focus can end up in the wrong NSView.
5613 - (BOOL)resignFirstResponder
5615   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5617   return [super resignFirstResponder];
5619   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5623 - (void)viewsWindowDidBecomeKey
5625   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5627   if (!mGeckoChild)
5628     return;
5630   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5632   // check to see if the window implements the mozWindow protocol. This
5633   // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
5634   // which can happen because these activate/focus calls propagate out
5635   // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
5636   BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
5637   if (isMozWindow)
5638     [[self window] setSuppressMakeKeyFront:YES];
5640   [self sendFocusEvent:NS_GOTFOCUS];
5641   [self sendFocusEvent:NS_ACTIVATE];
5643   if (isMozWindow)
5644     [[self window] setSuppressMakeKeyFront:NO];
5646   NS_OBJC_END_TRY_ABORT_BLOCK;
5650 - (void)viewsWindowDidResignKey
5652   if (!mGeckoChild)
5653     return;
5655   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5657   [self sendFocusEvent:NS_DEACTIVATE];
5658   [self sendFocusEvent:NS_LOSTFOCUS];
5662 // If the call to removeFromSuperview isn't delayed from nsChildView::
5663 // TearDownView(), the NSView hierarchy might get changed during calls to
5664 // [ChildView drawRect:], which leads to "beyond bounds" exceptions in
5665 // NSCFArray.  For more info see bmo bug 373122.  Apple's docs claim that
5666 // removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
5667 // display" (whatever "display" means).  But it's _not_ true that it can be
5668 // safely invoked during calls to [NSView drawRect:].  We use
5669 // removeFromSuperview here because there's no longer any danger of being
5670 // "invoked during display", and because doing do clears up bmo bug 384343.
5671 - (void)delayedTearDown
5673   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5675   [self removeFromSuperview];
5676   [self release];
5678   NS_OBJC_END_TRY_ABORT_BLOCK;
5682 #pragma mark -
5685 // drag'n'drop stuff
5686 #define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
5689 // This is a utility function used by NSView drag event methods
5690 // to send events. It contains all of the logic needed for Gecko
5691 // dragging to work. Returns YES if the event was handled, NO
5692 // if it wasn't.
5693 - (BOOL)doDragAction:(PRUint32)aMessage sender:(id)aSender
5695   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5697   if (!mGeckoChild)
5698     return NO;
5700   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView doDragAction: entered\n"));
5702   if (!mDragService) {
5703     CallGetService(kDragServiceContractID, &mDragService);
5704     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
5705     if (!mDragService)
5706       return NO;
5707   }
5709   if (aMessage == NS_DRAGDROP_ENTER)
5710     mDragService->StartDragSession();
5712   nsCOMPtr<nsIDragSession> dragSession;
5713   mDragService->GetCurrentSession(getter_AddRefs(dragSession));
5714   if (dragSession) {
5715     if (aMessage == NS_DRAGDROP_OVER) {
5716       // fire the drag event at the source. Just ignore whether it was
5717       // cancelled or not as there isn't actually a means to stop the drag
5718       mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
5719       dragSession->SetCanDrop(PR_FALSE);
5720     }
5721     else if (aMessage == NS_DRAGDROP_DROP) {
5722       // We make the assumption that the dragOver handlers have correctly set
5723       // the |canDrop| property of the Drag Session.
5724       PRBool canDrop = PR_FALSE;
5725       if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
5726         nsCOMPtr<nsIDOMNode> sourceNode;
5727         dragSession->GetSourceNode(getter_AddRefs(sourceNode));
5728         if (!sourceNode) {
5729           mDragService->EndDragSession(PR_FALSE);
5730         }
5731         return NO;
5732       }
5733     }
5734     
5735     unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
5736     PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
5737     // force copy = option, alias = cmd-option, default is move
5738     if (modifierFlags & NSAlternateKeyMask) {
5739       if (modifierFlags & NSCommandKeyMask)
5740         action = nsIDragService::DRAGDROP_ACTION_LINK;
5741       else
5742         action = nsIDragService::DRAGDROP_ACTION_COPY;
5743     }
5744     dragSession->SetDragAction(action);
5745   }
5747   // set up gecko event
5748   nsDragEvent geckoEvent(PR_TRUE, aMessage, nsnull);
5749   [self convertGenericCocoaEvent:nil toGeckoEvent:&geckoEvent];
5751   // Use our own coordinates in the gecko event.
5752   // Convert event from gecko global coords to gecko view coords.
5753   NSPoint localPoint = [self convertPoint:[aSender draggingLocation] fromView:nil];
5754   geckoEvent.refPoint.x = static_cast<nscoord>(localPoint.x);
5755   geckoEvent.refPoint.y = static_cast<nscoord>(localPoint.y);
5757   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5758   mGeckoChild->DispatchWindowEvent(geckoEvent);
5759   if (!mGeckoChild)
5760     return YES;
5762   if ((aMessage == NS_DRAGDROP_EXIT || aMessage == NS_DRAGDROP_DROP) &&
5763       dragSession) {
5764     nsCOMPtr<nsIDOMNode> sourceNode;
5765     dragSession->GetSourceNode(getter_AddRefs(sourceNode));
5766     if (!sourceNode) {
5767       // We're leaving a window while doing a drag that was
5768       // initiated in a different app. End the drag session,
5769       // since we're done with it for now (until the user
5770       // drags back into mozilla).
5771       mDragService->EndDragSession(PR_FALSE);
5772     }
5773   }
5775   return YES;
5777   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
5781 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
5783   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
5785   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingEntered: entered\n"));
5786   
5787   // there should never be a globalDragPboard when "draggingEntered:" is
5788   // called, but just in case we'll take care of it here.
5789   [globalDragPboard release];
5791   // Set the global drag pasteboard that will be used for this drag session.
5792   // This will be set back to nil when the drag session ends (mouse exits
5793   // the view or a drop happens within the view).
5794   globalDragPboard = [[sender draggingPasteboard] retain];
5796   BOOL handled = [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
5798   return handled ? NSDragOperationGeneric : NSDragOperationNone;
5800   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
5804 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
5806   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingUpdated: entered\n"));
5808   BOOL handled = [self doDragAction:NS_DRAGDROP_OVER sender:sender];
5809   return handled ? NSDragOperationGeneric : NSDragOperationNone;
5813 - (void)draggingExited:(id <NSDraggingInfo>)sender
5815   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingExited: entered\n"));
5817   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5818   [self doDragAction:NS_DRAGDROP_EXIT sender:sender];
5819   NS_IF_RELEASE(mDragService);
5823 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
5825   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5826   BOOL handled = [self doDragAction:NS_DRAGDROP_DROP sender:sender];
5827   NS_IF_RELEASE(mDragService);
5828   return handled;
5832 // NSDraggingSource
5833 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
5835   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
5837   gDraggedTransferables = nsnull;
5839   if (!mDragService) {
5840     CallGetService(kDragServiceContractID, &mDragService);
5841     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
5842   }
5844   if (mDragService) {
5845     mDragService->EndDragSession(PR_TRUE);
5846     NS_RELEASE(mDragService);
5847   }
5849   [globalDragPboard release];
5850   globalDragPboard = nil;
5852   NS_OBJC_END_TRY_ABORT_BLOCK;
5856 // NSDraggingSource
5857 // this is just implemented so we comply with the NSDraggingSource informal protocol
5858 - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
5860   return UINT_MAX;
5863 // This method is a callback typically invoked in response to a drag ending on the desktop
5864 // or a Findow folder window; the argument passed is a path to the drop location, to be used
5865 // in constructing a complete pathname for the file(s) we want to create as a result of
5866 // the drag.
5867 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(id <NSDraggingInfo>)dropDestination
5869   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
5871   nsresult rv;
5873   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n"));
5875   nsCOMPtr<nsILocalFile> targFile;
5876   NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(targFile));
5877   nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
5878   if (!macLocalFile) {
5879     NS_ERROR("No Mac local file");
5880     return nil;
5881   }
5883   if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
5884     NS_ERROR("failed InitWithCFURL");
5885     return nil;
5886   }
5888   if (!gDraggedTransferables)
5889     return nil;
5891   PRUint32 transferableCount;
5892   rv = gDraggedTransferables->Count(&transferableCount);
5893   if (NS_FAILED(rv))
5894     return nil;
5896   for (PRUint32 i = 0; i < transferableCount; i++) {
5897     nsCOMPtr<nsISupports> genericItem;
5898     gDraggedTransferables->GetElementAt(i, getter_AddRefs(genericItem));
5899     nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
5900     if (!item) {
5901       NS_ERROR("no transferable");
5902       return nil;
5903     }
5905     item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsILocalFile*));
5906     
5907     // now request the kFilePromiseMime data, which will invoke the data provider
5908     // If successful, the returned data is a reference to the resulting file.
5909     nsCOMPtr<nsISupports> fileDataPrimitive;
5910     PRUint32 dataSize = 0;
5911     item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
5912   }
5913   
5914   NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
5915   NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"];
5916   NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
5917   NSArray* rslt = [NSArray arrayWithObject:name];
5919   [name release];
5921   return rslt;
5923   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
5926 #pragma mark -
5929 #ifdef ACCESSIBILITY
5931 /* Every ChildView has a corresponding mozDocAccessible object that is doing all
5932    the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
5933    object.
5935    All ChildView needs to do is to route all accessibility calls (from the NSAccessibility APIs)
5936    down to its object, pretending that they are the same.
5938 - (id<mozAccessible>)accessible
5940   if (!mGeckoChild)
5941     return nil;
5943   id<mozAccessible> nativeAccessible = nil;
5945   nsAutoRetainCocoaObject kungFuDeathGrip(self);
5946   nsCOMPtr<nsIWidget> kungFuDeathGrip2(mGeckoChild);
5947   nsCOMPtr<nsIAccessible> accessible;
5948   mGeckoChild->GetDocumentAccessible(getter_AddRefs(accessible));
5949   if (!mGeckoChild)
5950     return nil;
5952   if (accessible)
5953     accessible->GetNativeInterface((void**)&nativeAccessible);
5955 #ifdef DEBUG_hakan
5956   NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
5957 #endif
5958   
5959   return nativeAccessible;
5962 /* Implementation of formal mozAccessible formal protocol (enabling mozViews
5963    to talk to mozAccessible objects in the accessibility module). */
5965 - (BOOL)hasRepresentedView
5967   return YES;
5970 - (id)representedView
5972   return self;
5975 - (BOOL)isRoot
5977   return [[self accessible] isRoot];
5980 #ifdef DEBUG
5981 - (void)printHierarchy
5983   [[self accessible] printHierarchy];
5985 #endif
5987 #pragma mark -
5989 // general
5991 - (BOOL)accessibilityIsIgnored
5993   return [[self accessible] accessibilityIsIgnored];
5996 - (id)accessibilityHitTest:(NSPoint)point
5998   return [[self accessible] accessibilityHitTest:point];
6001 - (id)accessibilityFocusedUIElement
6003   return [[self accessible] accessibilityFocusedUIElement];
6006 // actions
6008 - (NSArray*)accessibilityActionNames
6010   return [[self accessible] accessibilityActionNames];
6013 - (NSString*)accessibilityActionDescription:(NSString*)action
6015   return [[self accessible] accessibilityActionDescription:action];
6018 - (void)accessibilityPerformAction:(NSString*)action
6020   return [[self accessible] accessibilityPerformAction:action];
6023 // attributes
6025 - (NSArray*)accessibilityAttributeNames
6027   return [[self accessible] accessibilityAttributeNames];
6030 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
6032   return [[self accessible] accessibilityIsAttributeSettable:attribute];
6035 - (id)accessibilityAttributeValue:(NSString*)attribute
6037   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
6039   id<mozAccessible> accessible = [self accessible];
6040   
6041   // if we're the root (topmost) accessible, we need to return our native AXParent as we
6042   // traverse outside to the hierarchy of whoever embeds us. thus, fall back on NSView's
6043   // default implementation for this attribute.
6044   if ([attribute isEqualToString:NSAccessibilityParentAttribute] && [accessible isRoot]) {
6045     id parentAccessible = [super accessibilityAttributeValue:attribute];
6046     return parentAccessible;
6047   }
6049   return [accessible accessibilityAttributeValue:attribute];
6051   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
6054 #endif /* ACCESSIBILITY */
6056 @end
6059 #pragma mark -
6062 void
6063 nsTSMManager::OnDestroyView(NSView<mozView>* aDestroyingView)
6065   if (aDestroyingView != sComposingView)
6066     return;
6067   if (IsComposing()) {
6068     CancelIME(); // XXX Might CancelIME() fail because sComposingView is being destroyed?
6069     EndComposing();
6070   }
6074 PRBool
6075 nsTSMManager::GetIMEOpenState()
6077   return GetScriptManagerVariable(smKeyScript) != smRoman ? PR_TRUE : PR_FALSE;
6081 void
6082 nsTSMManager::InitTSMDocument(NSView<mozView>* aViewForCaret)
6084   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6086   sDocumentID = ::TSMGetActiveDocument();
6087   if (!sDocumentID)
6088     return;
6090   // We need to set the focused window level to TSMDocument. Then, the popup
6091   // windows of IME (E.g., a candidate list window) will be over the focused
6092   // view. See http://developer.apple.com/technotes/tn2005/tn2128.html#TNTAG1
6093   NSInteger TSMLevel, windowLevel;
6094   UInt32 size = sizeof(TSMLevel);
6096   OSStatus err =
6097     ::TSMGetDocumentProperty(sDocumentID, kTSMDocumentWindowLevelPropertyTag,
6098                              size, &size, &TSMLevel);
6099   windowLevel = [[aViewForCaret window] level];
6101   // Chinese IMEs on 10.5 don't work fine if the level is NSNormalWindowLevel,
6102   // then, we need to increment the value.
6103   if (windowLevel == NSNormalWindowLevel)
6104     windowLevel++;
6106   if (err == noErr && TSMLevel >= windowLevel)
6107     return;
6108   ::TSMSetDocumentProperty(sDocumentID, kTSMDocumentWindowLevelPropertyTag,
6109                            sizeof(windowLevel), &windowLevel);
6111   // ATOK (Japanese IME) updates the window level at activating,
6112   // we need to notify the change with this hack.
6113   ::DeactivateTSMDocument(sDocumentID);
6114   ::ActivateTSMDocument(sDocumentID);
6116   NS_OBJC_END_TRY_ABORT_BLOCK;
6120 void
6121 nsTSMManager::StartComposing(NSView<mozView>* aComposingView)
6123   if (sComposingView && sComposingView != sComposingView)
6124     CommitIME();
6125   sComposingView = aComposingView;
6126   NS_ASSERTION(::TSMGetActiveDocument() == sDocumentID,
6127                "We didn't initialize the TSMDocument");
6131 void
6132 nsTSMManager::UpdateComposing(NSString* aComposingString)
6134   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6136   if (sComposingString)
6137     [sComposingString release];
6138   sComposingString = [aComposingString retain];
6140   NS_OBJC_END_TRY_ABORT_BLOCK;
6144 void
6145 nsTSMManager::EndComposing()
6147   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6149   sComposingView = nsnull;
6150   if (sComposingString) {
6151     [sComposingString release];
6152     sComposingString = nsnull;
6153   }
6155   NS_OBJC_END_TRY_ABORT_BLOCK;
6159 void
6160 nsTSMManager::EnableIME(PRBool aEnable)
6162   if (aEnable == sIsIMEEnabled)
6163     return;
6164   CommitIME();
6165   sIsIMEEnabled = aEnable;
6169 void
6170 nsTSMManager::SetIMEOpenState(PRBool aOpen)
6172   if (aOpen == GetIMEOpenState())
6173     return;
6174   CommitIME();
6175   KeyScript(aOpen ? smKeySwapScript : smKeyRoman);
6179 #define ENABLE_ROMAN_KYBDS_ONLY -23
6180 void
6181 nsTSMManager::SetRomanKeyboardsOnly(PRBool aRomanOnly)
6183   if (aRomanOnly == sIsRomanKeyboardsOnly)
6184     return;
6185   CommitIME();
6186   KeyScript(aRomanOnly ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds);
6187   sIsRomanKeyboardsOnly = aRomanOnly;
6191 void
6192 nsTSMManager::KillComposing()
6194   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6196   // Force commit the current composition
6197   // XXX Don't use NSInputManager. Because it cannot control the non-forcused
6198   // input manager, therefore, on deactivating a window, it does not work fine.
6199   NS_ASSERTION(sDocumentID, "The TSMDocumentID is null");
6200   ::FixTSMDocument(sDocumentID);
6202   NS_OBJC_END_TRY_ABORT_BLOCK;
6206 void
6207 nsTSMManager::CommitIME()
6209   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6211   if (!IsComposing())
6212     return;
6213   KillComposing();
6214   if (!IsComposing())
6215     return;
6216   // If the composing transaction is still there, KillComposing only kills the
6217   // composing in TSM. We also need to kill the our composing transaction too.
6218   NSAttributedString* str =
6219     [[NSAttributedString alloc] initWithString:sComposingString];
6220   [sComposingView insertText:str];
6221   [str release];
6223   NS_OBJC_END_TRY_ABORT_BLOCK;
6227 void
6228 nsTSMManager::CancelIME()
6230   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
6232   if (!IsComposing())
6233     return;
6234   // For canceling the current composing, we need to ignore the param of
6235   // insertText. But this code is ugly...
6236   sIgnoreCommit = PR_TRUE;
6237   KillComposing();
6238   sIgnoreCommit = PR_FALSE;
6239   if (!IsComposing())
6240     return;
6241   // If the composing transaction is still there, KillComposing only kills the
6242   // composing in TSM. We also need to kill the our composing transaction too.
6243   NSAttributedString* str = [[NSAttributedString alloc] initWithString:@""];
6244   [sComposingView insertText:str];
6245   [str release];
6247   NS_OBJC_END_TRY_ABORT_BLOCK;
6251 // Target for text services events sent as the result of calls made to
6252 // TSMProcessRawKeyEvent() in [ChildView keyDown:] (above) when a plugin has
6253 // the focus.  The calls to TSMProcessRawKeyEvent() short-circuit Cocoa-based
6254 // IME (which would otherwise interfere with our efforts) and allow Carbon-
6255 // based IME to work in plugins (via the NPAPI).  This strategy doesn't cause
6256 // trouble for plugins that (like the Java Embedding Plugin) bypass the NPAPI
6257 // to get their keyboard events and do their own Cocoa-based IME.
6258 OSStatus PluginKeyEventsHandler(EventHandlerCallRef inHandlerRef,
6259                                 EventRef inEvent, void *userData)
6261   id arp = [[NSAutoreleasePool alloc] init];
6263   TSMDocumentID activeDoc = ::TSMGetActiveDocument();
6264   if (!activeDoc) {
6265     [arp release];
6266     return eventNotHandledErr;
6267   }
6269   ChildView *target = nil;
6270   OSStatus status = ::TSMGetDocumentProperty(activeDoc, kFocusedChildViewTSMDocPropertyTag,
6271                                              sizeof(ChildView *), nil, &target);
6272   if (status != noErr)
6273     target = nil;
6274   if (!target) {
6275     [arp release];
6276     return eventNotHandledErr;
6277   }
6279   EventRef keyEvent = NULL;
6280   status = ::GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent,
6281                                typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
6282   if ((status != noErr) || !keyEvent) {
6283     [arp release];
6284     return eventNotHandledErr;
6285   }
6287   [target processPluginKeyEvent:keyEvent];
6289   [arp release];
6290   return noErr;
6293 static EventHandlerRef gPluginKeyEventsHandler = NULL;
6295 // Called from nsAppShell::Init()
6296 void NS_InstallPluginKeyEventsHandler()
6298   if (gPluginKeyEventsHandler)
6299     return;
6300   static const EventTypeSpec sTSMEvents[] =
6301     { { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
6302   ::InstallEventHandler(::GetEventDispatcherTarget(),
6303                         ::NewEventHandlerUPP(PluginKeyEventsHandler),
6304                         GetEventTypeCount(sTSMEvents),
6305                         sTSMEvents,
6306                         NULL,
6307                         &gPluginKeyEventsHandler);
6310 // Called from nsAppShell::Exit()
6311 void NS_RemovePluginKeyEventsHandler()
6313   if (!gPluginKeyEventsHandler)
6314     return;
6315   ::RemoveEventHandler(gPluginKeyEventsHandler);
6316   gPluginKeyEventsHandler = NULL;