PageRenderTime 76ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/widget/src/cocoa/nsChildView.mm

https://bitbucket.org/mkato/mozilla-1.9.0-win64
Objective C++ | 6236 lines | 4337 code | 1171 blank | 728 comment | 628 complexity | e15973147b4c5196df4c65f4aeea1434 MD5 | raw file
Possible License(s): LGPL-3.0, MIT, BSD-3-Clause, MPL-2.0-no-copyleft-exception, GPL-2.0, LGPL-2.1
  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 ***** */
  42. #include <unistd.h>
  43. #include "nsChildView.h"
  44. #include "nsCocoaWindow.h"
  45. #include "nsObjCExceptions.h"
  46. #include "nsCOMPtr.h"
  47. #include "nsToolkit.h"
  48. #include "nsCRT.h"
  49. #include "nsplugindefs.h"
  50. #include "nsIPrefService.h"
  51. #include "nsIPrefBranch.h"
  52. #include "nsIFontMetrics.h"
  53. #include "nsIDeviceContext.h"
  54. #include "nsIRegion.h"
  55. #include "nsIRollupListener.h"
  56. #include "nsIScrollableView.h"
  57. #include "nsIViewManager.h"
  58. #include "nsIInterfaceRequestor.h"
  59. #include "nsIServiceManager.h"
  60. #include "nsILocalFile.h"
  61. #include "nsILocalFileMac.h"
  62. #include "nsGfxCIID.h"
  63. #include "nsIMenuRollup.h"
  64. #include "nsDragService.h"
  65. #include "nsCursorManager.h"
  66. #include "nsWindowMap.h"
  67. #include "nsCocoaUtils.h"
  68. #include "nsMenuBarX.h"
  69. #include "gfxContext.h"
  70. #include "gfxQuartzSurface.h"
  71. #include <dlfcn.h>
  72. #undef DEBUG_IME
  73. #undef DEBUG_UPDATE
  74. #undef INVALIDATE_DEBUGGING // flash areas as they are invalidated
  75. #ifdef MOZ_LOGGING
  76. #define FORCE_PR_LOG
  77. #endif
  78. #include "prlog.h"
  79. #ifdef PR_LOGGING
  80. PRLogModuleInfo* sCocoaLog = nsnull;
  81. #endif
  82. // npapi.h defines NPEventType_AdjustCursorEvent but we don't want to include npapi.h here.
  83. // We need to send this in the "what" field for certain native plugin events. WebKit does
  84. // this as well.
  85. #define adjustCursorEvent 33
  86. extern "C" {
  87. CG_EXTERN void CGContextResetCTM(CGContextRef);
  88. CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
  89. CG_EXTERN void CGContextResetClip(CGContextRef);
  90. }
  91. #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
  92. struct __TISInputSource;
  93. typedef __TISInputSource* TISInputSourceRef;
  94. #endif
  95. TISInputSourceRef (*Leopard_TISCopyCurrentKeyboardLayoutInputSource)() = NULL;
  96. void* (*Leopard_TISGetInputSourceProperty)(TISInputSourceRef inputSource, CFStringRef propertyKey) = NULL;
  97. CFArrayRef (*Leopard_TISCreateInputSourceList)(CFDictionaryRef properties, Boolean includeAllInstalled) = NULL;
  98. CFStringRef kOurTISPropertyUnicodeKeyLayoutData = NULL;
  99. CFStringRef kOurTISPropertyInputSourceID = NULL;
  100. extern PRBool gCocoaWindowMethodsSwizzled; // Defined in nsCocoaWindow.mm
  101. extern nsISupportsArray *gDraggedTransferables;
  102. PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
  103. PRBool nsTSMManager::sIsRomanKeyboardsOnly = PR_FALSE;
  104. PRBool nsTSMManager::sIgnoreCommit = PR_FALSE;
  105. NSView<mozView>* nsTSMManager::sComposingView = nsnull;
  106. TSMDocumentID nsTSMManager::sDocumentID = nsnull;
  107. NSString* nsTSMManager::sComposingString = nsnull;
  108. static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
  109. static NSView* sLastViewEntered = nil;
  110. #ifdef INVALIDATE_DEBUGGING
  111. static void blinkRect(Rect* r);
  112. static void blinkRgn(RgnHandle rgn);
  113. #endif
  114. nsIRollupListener * gRollupListener = nsnull;
  115. nsIWidget * gRollupWidget = nsnull;
  116. @interface ChildView(Private)
  117. // sets up our view, attaching it to its owning gecko view
  118. - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
  119. // sends gecko an ime composition event
  120. - (nsRect) sendCompositionEvent:(PRInt32)aEventType;
  121. // sends gecko an ime text event
  122. - (void) sendTextEvent:(PRUnichar*) aBuffer
  123. attributedString:(NSAttributedString*) aString
  124. selectedRange:(NSRange)selRange
  125. markedRange:(NSRange)markRange
  126. doCommit:(BOOL)doCommit;
  127. // do generic gecko event setup with a generic cocoa event. accepts nil inEvent.
  128. - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
  129. // set up a gecko mouse event based on a cocoa mouse event
  130. - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent;
  131. // set up a gecko key event based on a cocoa key event
  132. - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent;
  133. - (NSMenu*)contextMenu;
  134. - (TopLevelWindowData*)ensureWindowData;
  135. - (void)setIsPluginView:(BOOL)aIsPlugin;
  136. - (BOOL)isPluginView;
  137. - (BOOL)childViewHasPlugin;
  138. - (BOOL)isRectObscuredBySubview:(NSRect)inRect;
  139. - (void)processPendingRedraws;
  140. - (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv;
  141. - (BOOL)ensureCorrectMouseEventTarget:(NSEvent *)anEvent;
  142. - (void)maybeInitContextMenuTracking;
  143. + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
  144. #if USE_CLICK_HOLD_CONTEXTMENU
  145. // called on a timer two seconds after a mouse down to see if we should display
  146. // a context menu (click-hold)
  147. - (void)clickHoldCallback:(id)inEvent;
  148. #endif
  149. #ifdef ACCESSIBILITY
  150. - (id<mozAccessible>)accessible;
  151. #endif
  152. @end
  153. #pragma mark -
  154. /* Convenience routines to go from a gecko rect to cocoa NSRects and back
  155. *
  156. * Gecko rects (nsRect) contain an origin (x,y) in a coordinate
  157. * system with (0,0) in the top-left of the screen. Cocoa rects
  158. * (NSRect) contain an origin (x,y) in a coordinate system with
  159. * (0,0) in the bottom-left of the screen. Both nsRect and NSRect
  160. * contain width/height info, with no difference in their use.
  161. * If a Cocoa rect is from a flipped view, there is no need to
  162. * convert coordinate systems.
  163. */
  164. static inline void
  165. GeckoRectToNSRect(const nsRect & inGeckoRect, NSRect & outCocoaRect)
  166. {
  167. outCocoaRect.origin.x = inGeckoRect.x;
  168. outCocoaRect.origin.y = inGeckoRect.y;
  169. outCocoaRect.size.width = inGeckoRect.width;
  170. outCocoaRect.size.height = inGeckoRect.height;
  171. }
  172. static inline void
  173. NSRectToGeckoRect(const NSRect & inCocoaRect, nsRect & outGeckoRect)
  174. {
  175. outGeckoRect.x = static_cast<nscoord>(inCocoaRect.origin.x);
  176. outGeckoRect.y = static_cast<nscoord>(inCocoaRect.origin.y);
  177. outGeckoRect.width = static_cast<nscoord>(inCocoaRect.size.width);
  178. outGeckoRect.height = static_cast<nscoord>(inCocoaRect.size.height);
  179. }
  180. static inline void
  181. ConvertGeckoRectToMacRect(const nsRect& aRect, Rect& outMacRect)
  182. {
  183. outMacRect.left = aRect.x;
  184. outMacRect.top = aRect.y;
  185. outMacRect.right = aRect.x + aRect.width;
  186. outMacRect.bottom = aRect.y + aRect.height;
  187. }
  188. // Flips a screen coordinate from a point in the cocoa coordinate system (bottom-left rect) to a point
  189. // that is a "flipped" cocoa coordinate system (starts in the top-left).
  190. static inline void
  191. FlipCocoaScreenCoordinate (NSPoint &inPoint)
  192. {
  193. inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y);
  194. }
  195. static PRUint32
  196. UnderlineAttributeToTextRangeType(PRUint32 aUnderlineStyle, NSRange selRange)
  197. {
  198. #ifdef DEBUG_IME
  199. NSLog(@"****in underlineAttributeToTextRangeType = %d", aUnderlineStyle);
  200. #endif
  201. // For more info on the underline attribute, please see:
  202. // http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/AttributedStrings/Tasks/AccessingAttrs.html
  203. // We are not clear where the define for value 2 is right now.
  204. // To see this value in japanese ime, type 'aaaaaaaaa' and hit space to make the
  205. // ime send you some part of text in 1 (NSSingleUnderlineStyle) and some part in 2.
  206. // ftang will ask apple for more details
  207. //
  208. // it probably means show 1-pixel thickness underline vs 2-pixel thickness
  209. PRUint32 attr;
  210. if (selRange.length == 0) {
  211. switch (aUnderlineStyle) {
  212. case 1:
  213. attr = NS_TEXTRANGE_RAWINPUT;
  214. break;
  215. case 2:
  216. default:
  217. attr = NS_TEXTRANGE_SELECTEDRAWTEXT;
  218. break;
  219. }
  220. }
  221. else {
  222. switch (aUnderlineStyle) {
  223. case 1:
  224. attr = NS_TEXTRANGE_CONVERTEDTEXT;
  225. break;
  226. case 2:
  227. default:
  228. attr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
  229. break;
  230. }
  231. }
  232. return attr;
  233. }
  234. static PRUint32
  235. CountRanges(NSAttributedString *aString)
  236. {
  237. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  238. // Iterate through aString for the NSUnderlineStyleAttributeName and count the
  239. // different segments adjusting limitRange as we go.
  240. PRUint32 count = 0;
  241. NSRange effectiveRange;
  242. NSRange limitRange = NSMakeRange(0, [aString length]);
  243. while (limitRange.length > 0) {
  244. [aString attribute:NSUnderlineStyleAttributeName
  245. atIndex:limitRange.location
  246. longestEffectiveRange:&effectiveRange
  247. inRange:limitRange];
  248. limitRange = NSMakeRange(NSMaxRange(effectiveRange),
  249. NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
  250. count++;
  251. }
  252. return count;
  253. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
  254. }
  255. static void
  256. ConvertAttributeToGeckoRange(NSAttributedString *aString, NSRange markRange, NSRange selRange, PRUint32 inCount, nsTextRange* aRanges)
  257. {
  258. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  259. // Convert the Cocoa range into the nsTextRange Array used in Gecko.
  260. // Iterate through the attributed string and map the underline attribute to Gecko IME textrange attributes.
  261. // We may need to change the code here if we change the implementation of validAttributesForMarkedText.
  262. PRUint32 i = 0;
  263. NSRange effectiveRange;
  264. NSRange limitRange = NSMakeRange(0, [aString length]);
  265. while ((limitRange.length > 0) && (i < inCount)) {
  266. id attributeValue = [aString attribute:NSUnderlineStyleAttributeName
  267. atIndex:limitRange.location
  268. longestEffectiveRange:&effectiveRange
  269. inRange:limitRange];
  270. aRanges[i].mStartOffset = effectiveRange.location;
  271. aRanges[i].mEndOffset = NSMaxRange(effectiveRange);
  272. aRanges[i].mRangeType = UnderlineAttributeToTextRangeType([attributeValue intValue], selRange);
  273. limitRange = NSMakeRange(NSMaxRange(effectiveRange),
  274. NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
  275. i++;
  276. }
  277. // Get current caret position.
  278. // Caret is indicator of insertion point, so mEndOffset = 0.
  279. aRanges[i].mStartOffset = selRange.location + selRange.length;
  280. aRanges[i].mEndOffset = 0;
  281. aRanges[i].mRangeType = NS_TEXTRANGE_CARETPOSITION;
  282. NS_OBJC_END_TRY_ABORT_BLOCK;
  283. }
  284. static void
  285. FillTextRangeInTextEvent(nsTextEvent *aTextEvent, NSAttributedString* aString, NSRange markRange, NSRange selRange)
  286. {
  287. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  288. // Count the number of segments in the attributed string and add one more count for sending current caret position to Gecko.
  289. // Allocate the right size of nsTextRange and draw caret at right position.
  290. // Convert the attributed string into an array of nsTextRange and get current caret position by calling above functions.
  291. PRUint32 count = CountRanges(aString) + 1;
  292. aTextEvent->rangeArray = new nsTextRange[count];
  293. if (aTextEvent->rangeArray) {
  294. aTextEvent->rangeCount = count;
  295. ConvertAttributeToGeckoRange(aString, markRange, selRange, aTextEvent->rangeCount, aTextEvent->rangeArray);
  296. }
  297. NS_OBJC_END_TRY_ABORT_BLOCK;
  298. }
  299. #pragma mark -
  300. nsChildView::nsChildView() : nsBaseWidget()
  301. , mView(nsnull)
  302. , mParentView(nsnull)
  303. , mParentWidget(nsnull)
  304. , mVisible(PR_FALSE)
  305. , mDrawing(PR_FALSE)
  306. , mLiveResizeInProgress(PR_FALSE)
  307. , mIsPluginView(PR_FALSE)
  308. , mPluginDrawing(PR_FALSE)
  309. , mPluginIsCG(PR_FALSE)
  310. , mInSetFocus(PR_FALSE)
  311. {
  312. #ifdef PR_LOGGING
  313. if (!sCocoaLog) {
  314. sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
  315. CFIndex idx;
  316. KLGetKeyboardLayoutCount(&idx);
  317. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("Keyboard layout configuration:"));
  318. for (CFIndex i = 0; i < idx; ++i) {
  319. KeyboardLayoutRef curKL;
  320. if (KLGetKeyboardLayoutAtIndex(i, &curKL) == noErr) {
  321. CFStringRef name;
  322. if (KLGetKeyboardLayoutProperty(curKL, kKLName, (const void**)&name) == noErr) {
  323. int idn;
  324. KLGetKeyboardLayoutProperty(curKL, kKLIdentifier, (const void**)&idn);
  325. int kind;
  326. KLGetKeyboardLayoutProperty(curKL, kKLKind, (const void**)&kind);
  327. char buf[256];
  328. CFStringGetCString(name, buf, 256, kCFStringEncodingASCII);
  329. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, (" %d,%s,%d\n", idn, buf, kind));
  330. }
  331. }
  332. }
  333. }
  334. #endif
  335. SetBackgroundColor(NS_RGB(255, 255, 255));
  336. SetForegroundColor(NS_RGB(0, 0, 0));
  337. if (nsToolkit::OnLeopardOrLater() && !Leopard_TISCopyCurrentKeyboardLayoutInputSource) {
  338. // This libary would already be open for LMGetKbdType (and probably other
  339. // symbols), so merely using RTLD_DEFAULT in dlsym would be sufficient,
  340. // but man dlsym says: "all mach-o images in the process (except ...) are
  341. // searched in the order they were loaded. This can be a costly search
  342. // and should be avoided."
  343. void* hitoolboxHandle = dlopen("/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Versions/A/HIToolbox", RTLD_LAZY);
  344. if (hitoolboxHandle) {
  345. *(void **)(&Leopard_TISCopyCurrentKeyboardLayoutInputSource) = dlsym(hitoolboxHandle, "TISCopyCurrentKeyboardLayoutInputSource");
  346. *(void **)(&Leopard_TISGetInputSourceProperty) = dlsym(hitoolboxHandle, "TISGetInputSourceProperty");
  347. *(void **)(&Leopard_TISCreateInputSourceList) = dlsym(hitoolboxHandle, "TISCreateInputSourceList");
  348. kOurTISPropertyUnicodeKeyLayoutData = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyUnicodeKeyLayoutData"));
  349. kOurTISPropertyInputSourceID = *static_cast<CFStringRef*>(dlsym(hitoolboxHandle, "kTISPropertyInputSourceID"));
  350. }
  351. }
  352. }
  353. nsChildView::~nsChildView()
  354. {
  355. // notify the children that we're gone
  356. for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
  357. nsChildView* childView = static_cast<nsChildView*>(kid);
  358. childView->mParentWidget = nsnull;
  359. }
  360. NS_WARN_IF_FALSE(mOnDestroyCalled, "nsChildView object destroyed without calling Destroy()");
  361. // An nsChildView object that was in use can be destroyed without Destroy()
  362. // ever being called on it. So we also need to do a quick, safe cleanup
  363. // here (it's too late to just call Destroy(), which can cause crashes).
  364. // It's particularly important to make sure widgetDestroyed is called on our
  365. // mView -- this method NULLs mView's mGeckoChild, and NULL checks on
  366. // mGeckoChild are used throughout the ChildView class to tell if it's safe
  367. // to use a ChildView object.
  368. [mView widgetDestroyed]; // Safe if mView is nil.
  369. mParentWidget = nil;
  370. TearDownView(); // Safe if called twice.
  371. }
  372. NS_IMPL_ISUPPORTS_INHERITED2(nsChildView, nsBaseWidget, nsIPluginWidget, nsIKBStateControl)
  373. // Utility method for implementing both Create(nsIWidget ...)
  374. // and Create(nsNativeWidget...)
  375. nsresult nsChildView::StandardCreate(nsIWidget *aParent,
  376. const nsRect &aRect,
  377. EVENT_CALLBACK aHandleEventFunction,
  378. nsIDeviceContext *aContext,
  379. nsIAppShell *aAppShell,
  380. nsIToolkit *aToolkit,
  381. nsWidgetInitData *aInitData,
  382. nsNativeWidget aNativeParent)
  383. {
  384. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  385. // See NSWindow (MethodSwizzling) in nsCocoaWindow.mm.
  386. if (!gCocoaWindowMethodsSwizzled) {
  387. nsToolkit::SwizzleMethods([NSWindow class], @selector(sendEvent:),
  388. @selector(nsCocoaWindow_NSWindow_sendEvent:));
  389. gCocoaWindowMethodsSwizzled = PR_TRUE;
  390. }
  391. mBounds = aRect;
  392. BaseCreate(aParent, aRect, aHandleEventFunction,
  393. aContext, aAppShell, aToolkit, aInitData);
  394. // inherit things from the parent view and create our parallel
  395. // NSView in the Cocoa display system
  396. mParentView = nil;
  397. if (aParent) {
  398. SetBackgroundColor(aParent->GetBackgroundColor());
  399. SetForegroundColor(aParent->GetForegroundColor());
  400. // inherit the top-level window. NS_NATIVE_WIDGET is always a NSView
  401. // regardless of if we're asking a window or a view (for compatibility
  402. // with windows).
  403. mParentView = (NSView*)aParent->GetNativeData(NS_NATIVE_WIDGET);
  404. mParentWidget = aParent;
  405. }
  406. else
  407. mParentView = reinterpret_cast<NSView*>(aNativeParent);
  408. // create our parallel NSView and hook it up to our parent. Recall
  409. // that NS_NATIVE_WIDGET is the NSView.
  410. NSRect r;
  411. GeckoRectToNSRect(mBounds, r);
  412. mView = [CreateCocoaView(r) retain];
  413. if (!mView) return NS_ERROR_FAILURE;
  414. #if DEBUG
  415. // if our parent is a popup window, we're most certainly coming from a <select> list dropdown which
  416. // we handle in a different way than other platforms. It's ok that we don't have a parent
  417. // view because we bailed before even creating the cocoa widgetry and as a result, we
  418. // don't need to assert. However, if that's not the case, we definitely want to assert
  419. // to show views aren't getting correctly parented.
  420. if (aParent) {
  421. nsWindowType windowType;
  422. aParent->GetWindowType(windowType);
  423. if (windowType != eWindowType_popup)
  424. NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
  425. }
  426. else {
  427. NS_ASSERTION(mParentView && mView, "couldn't hook up new NSView in hierarchy");
  428. }
  429. #endif
  430. // If this view was created in a Gecko view hierarchy, the initial state
  431. // is hidden. If the view is attached only to a native NSView but has
  432. // no Gecko parent (as in embedding), the initial state is visible.
  433. if (mParentWidget)
  434. [mView setHidden:YES];
  435. else
  436. mVisible = PR_TRUE;
  437. // Hook it up in the NSView hierarchy.
  438. if (mParentView) {
  439. NSWindow* window = [mParentView window];
  440. if (!window &&
  441. [mParentView respondsToSelector:@selector(nativeWindow)])
  442. window = [mParentView nativeWindow];
  443. [mView setNativeWindow:window];
  444. [mParentView addSubview:mView];
  445. }
  446. // if this is a ChildView, make sure that our per-window data
  447. // is set up
  448. if ([mView isKindOfClass:[ChildView class]])
  449. [(ChildView*)mView ensureWindowData];
  450. return NS_OK;
  451. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  452. }
  453. // Creates the appropriate child view. Override to create something other than
  454. // our |ChildView| object. Autoreleases, so caller must retain.
  455. NSView*
  456. nsChildView::CreateCocoaView(NSRect inFrame)
  457. {
  458. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  459. return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this] autorelease];
  460. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  461. }
  462. void nsChildView::TearDownView()
  463. {
  464. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  465. if (!mView)
  466. return;
  467. NSWindow* win = [mView window];
  468. NSResponder* responder = [win firstResponder];
  469. // We're being unhooked from the view hierarchy, don't leave our view
  470. // or a child view as the window first responder.
  471. if (responder && [responder isKindOfClass:[NSView class]] &&
  472. [(NSView*)responder isDescendantOf:mView]) {
  473. [win makeFirstResponder:[mView superview]];
  474. }
  475. // If mView is win's contentView, win (mView's NSWindow) "owns" mView --
  476. // win has retained mView, and will detach it from the view hierarchy and
  477. // release it when necessary (when win is itself destroyed (in a call to
  478. // [win dealloc])). So all we need to do here is call [mView release] (to
  479. // match the call to [mView retain] in nsChildView::StandardCreate()).
  480. // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes
  481. // mView to be released again and dealloced, while remaining win's
  482. // contentView. So if we do that here, win will (for a short while) have
  483. // an invalid contentView (for the consequences see bmo bugs 381087 and
  484. // 374260).
  485. if ([mView isEqual:[win contentView]]) {
  486. [mView release];
  487. } else {
  488. // Stop NSView hierarchy being changed during [ChildView drawRect:]
  489. [mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false];
  490. }
  491. mView = nil;
  492. NS_OBJC_END_TRY_ABORT_BLOCK;
  493. }
  494. // create a nsChildView
  495. NS_IMETHODIMP nsChildView::Create(nsIWidget *aParent,
  496. const nsRect &aRect,
  497. EVENT_CALLBACK aHandleEventFunction,
  498. nsIDeviceContext *aContext,
  499. nsIAppShell *aAppShell,
  500. nsIToolkit *aToolkit,
  501. nsWidgetInitData *aInitData)
  502. {
  503. return(StandardCreate(aParent, aRect, aHandleEventFunction, aContext,
  504. aAppShell, aToolkit, aInitData, nsnull));
  505. }
  506. // Creates a main nsChildView using a native widget (an NSView)
  507. NS_IMETHODIMP nsChildView::Create(nsNativeWidget aNativeParent,
  508. const nsRect &aRect,
  509. EVENT_CALLBACK aHandleEventFunction,
  510. nsIDeviceContext *aContext,
  511. nsIAppShell *aAppShell,
  512. nsIToolkit *aToolkit,
  513. nsWidgetInitData *aInitData)
  514. {
  515. // what we're passed in |aNativeParent| is an NSView.
  516. return(StandardCreate(nsnull, aRect, aHandleEventFunction, aContext,
  517. aAppShell, aToolkit, aInitData, aNativeParent));
  518. }
  519. NS_IMETHODIMP nsChildView::Destroy()
  520. {
  521. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  522. if (mOnDestroyCalled)
  523. return NS_OK;
  524. mOnDestroyCalled = PR_TRUE;
  525. [mView widgetDestroyed];
  526. nsBaseWidget::OnDestroy();
  527. nsBaseWidget::Destroy();
  528. ReportDestroyEvent();
  529. mParentWidget = nil;
  530. TearDownView();
  531. return NS_OK;
  532. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  533. }
  534. #pragma mark -
  535. #if 0
  536. static void PrintViewHierarchy(NSView *view)
  537. {
  538. while (view) {
  539. NSLog(@" view is %x, frame %@", view, NSStringFromRect([view frame]));
  540. view = [view superview];
  541. }
  542. }
  543. #endif
  544. // Return native data according to aDataType
  545. void* nsChildView::GetNativeData(PRUint32 aDataType)
  546. {
  547. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
  548. void* retVal = nsnull;
  549. switch (aDataType)
  550. {
  551. case NS_NATIVE_WIDGET:
  552. case NS_NATIVE_DISPLAY:
  553. retVal = (void*)mView;
  554. break;
  555. case NS_NATIVE_WINDOW:
  556. retVal = [mView nativeWindow];
  557. break;
  558. case NS_NATIVE_GRAPHIC:
  559. NS_ASSERTION(0, "Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!");
  560. retVal = nsnull;
  561. break;
  562. case NS_NATIVE_OFFSETX:
  563. retVal = 0;
  564. break;
  565. case NS_NATIVE_OFFSETY:
  566. retVal = 0;
  567. break;
  568. case NS_NATIVE_PLUGIN_PORT:
  569. #ifndef NP_NO_QUICKDRAW
  570. case NS_NATIVE_PLUGIN_PORT_QD:
  571. {
  572. mPluginIsCG = PR_FALSE;
  573. mIsPluginView = PR_TRUE;
  574. if ([mView isKindOfClass:[ChildView class]])
  575. [(ChildView*)mView setIsPluginView:YES];
  576. NSWindow* window = [mView nativeWindow];
  577. if (window) {
  578. WindowRef topLevelWindow = (WindowRef)[window windowRef];
  579. if (topLevelWindow) {
  580. mPluginPort.qdPort.port = ::GetWindowPort(topLevelWindow);
  581. NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
  582. NSRect frame = [[window contentView] frame];
  583. viewOrigin.y = frame.size.height - viewOrigin.y;
  584. // need to convert view's origin to window coordinates.
  585. // then, encode as "SetOrigin" ready values.
  586. mPluginPort.qdPort.portx = (PRInt32)-viewOrigin.x;
  587. mPluginPort.qdPort.porty = (PRInt32)-viewOrigin.y;
  588. }
  589. }
  590. retVal = (void*)&mPluginPort;
  591. break;
  592. }
  593. #endif
  594. case NS_NATIVE_PLUGIN_PORT_CG:
  595. {
  596. mPluginIsCG = PR_TRUE;
  597. mIsPluginView = PR_TRUE;
  598. if ([mView isKindOfClass:[ChildView class]])
  599. [(ChildView*)mView setIsPluginView:YES];
  600. mPluginPort.cgPort.context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
  601. NSWindow* window = [mView nativeWindow];
  602. if (window) {
  603. WindowRef topLevelWindow = (WindowRef)[window windowRef];
  604. mPluginPort.cgPort.window = topLevelWindow;
  605. }
  606. retVal = (void*)&mPluginPort;
  607. break;
  608. }
  609. }
  610. return retVal;
  611. NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
  612. }
  613. #pragma mark -
  614. NS_IMETHODIMP nsChildView::GetHasTransparentBackground(PRBool& aTransparent)
  615. {
  616. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  617. aTransparent = ![mView isOpaque];
  618. return NS_OK;
  619. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  620. }
  621. // This is called by nsContainerFrame on the root widget for all window types
  622. // except popup windows (when nsCocoaWindow::SetHasTransparentBackground is used instead).
  623. NS_IMETHODIMP nsChildView::SetHasTransparentBackground(PRBool aTransparent)
  624. {
  625. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  626. BOOL currentTransparency = ![[mView nativeWindow] isOpaque];
  627. if (aTransparent != currentTransparency) {
  628. // Find out if this is a window we created by seeing if the delegate is WindowDelegate. If it is,
  629. // tell the nsCocoaWindow to set its background to transparent.
  630. id windowDelegate = [[mView nativeWindow] delegate];
  631. if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
  632. nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
  633. if (widget) {
  634. widget->MakeBackgroundTransparent(aTransparent);
  635. [(ChildView*)mView setTransparent:aTransparent];
  636. }
  637. }
  638. }
  639. return NS_OK;
  640. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  641. }
  642. NS_IMETHODIMP nsChildView::IsVisible(PRBool& outState)
  643. {
  644. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  645. if (!mVisible) {
  646. outState = mVisible;
  647. }
  648. else {
  649. // mVisible does not accurately reflect the state of a hidden tabbed view
  650. // so verify that the view has a window as well
  651. outState = ([mView window] != nil);
  652. // now check native widget hierarchy visibility
  653. if (outState && NSIsEmptyRect([mView visibleRect])) {
  654. outState = PR_FALSE;
  655. }
  656. }
  657. return NS_OK;
  658. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  659. }
  660. void nsChildView::HidePlugin()
  661. {
  662. NS_ASSERTION(mIsPluginView, "HidePlugin called on non-plugin view");
  663. if (mPluginInstanceOwner && !mPluginIsCG) {
  664. nsPluginWindow* window;
  665. mPluginInstanceOwner->GetWindow(window);
  666. nsCOMPtr<nsIPluginInstance> instance;
  667. mPluginInstanceOwner->GetInstance(*getter_AddRefs(instance));
  668. if (window && instance) {
  669. window->clipRect.top = 0;
  670. window->clipRect.left = 0;
  671. window->clipRect.bottom = 0;
  672. window->clipRect.right = 0;
  673. instance->SetWindow(window);
  674. }
  675. }
  676. }
  677. static void HideChildPluginViews(NSView* aView)
  678. {
  679. NSArray* subviews = [aView subviews];
  680. for (unsigned int i = 0; i < [subviews count]; ++i) {
  681. NSView* view = [subviews objectAtIndex: i];
  682. if (![view isKindOfClass:[ChildView class]])
  683. continue;
  684. ChildView* childview = static_cast<ChildView*>(view);
  685. if ([childview isPluginView]) {
  686. nsChildView* widget = static_cast<nsChildView*>([childview widget]);
  687. if (widget) {
  688. widget->HidePlugin();
  689. }
  690. } else {
  691. HideChildPluginViews(view);
  692. }
  693. }
  694. }
  695. // Hide or show this component
  696. NS_IMETHODIMP nsChildView::Show(PRBool aState)
  697. {
  698. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  699. if (aState != mVisible) {
  700. [mView setHidden:!aState];
  701. mVisible = aState;
  702. if (!mVisible)
  703. HideChildPluginViews(mView);
  704. }
  705. return NS_OK;
  706. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  707. }
  708. nsIWidget*
  709. nsChildView::GetParent(void)
  710. {
  711. return mParentWidget;
  712. }
  713. nsIWidget*
  714. nsChildView::GetTopLevelWidget()
  715. {
  716. nsIWidget* current = this;
  717. for (nsIWidget* parent = GetParent(); parent ; parent = parent->GetParent())
  718. current = parent;
  719. return current;
  720. }
  721. NS_IMETHODIMP nsChildView::ModalEventFilter(PRBool aRealEvent, void *aEvent,
  722. PRBool *aForWindow)
  723. {
  724. if (aForWindow)
  725. *aForWindow = PR_FALSE;
  726. return NS_ERROR_NOT_IMPLEMENTED;
  727. }
  728. NS_IMETHODIMP nsChildView::Enable(PRBool aState)
  729. {
  730. return NS_OK;
  731. }
  732. NS_IMETHODIMP nsChildView::IsEnabled(PRBool *aState)
  733. {
  734. // unimplemented
  735. if (aState)
  736. *aState = PR_TRUE;
  737. return NS_OK;
  738. }
  739. NS_IMETHODIMP nsChildView::SetFocus(PRBool aRaise)
  740. {
  741. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  742. // Don't do anything if SetFocus() has been called reentrantly on the same
  743. // object. Sometimes calls to nsChildView::DispatchEvent() can get
  744. // temporarily stuck, causing calls to [ChildView sendFocusEvent:] and
  745. // SetFocus() to be reentered. These reentrant calls are probably the
  746. // result of one or more bugs, and doing things on a reentrant call can
  747. // cause problems: For example if mView is already the first responder and
  748. // we send it an NS_GOTFOCUS event (see below), this causes the Mochitests
  749. // to get stuck in the toolkit/content/tests/widgets/test_popup_button.xul
  750. // test.
  751. if (mInSetFocus)
  752. return NS_OK;
  753. mInSetFocus = PR_TRUE;
  754. NSWindow* window = [mView window];
  755. if (window) {
  756. nsAutoRetainCocoaObject kungFuDeathGrip(mView);
  757. // For reasons that aren't yet clear, focus changes within a window (as
  758. // opposed to those between windows or between apps) should only trigger
  759. // NS_LOSTFOCUS and NS_GOTFOCUS events (sent to Gecko) in the context of
  760. // a call to nsChildView::SetFocus() (or nsCocoaWindow::SetFocus(), which
  761. // in any case re-routes to nsChildView::SetFocus()). If we send these
  762. // events on every intra-window focus change (on every call to
  763. // [ChildView becomeFirstResponder:] or [ChildView resignFirstResponder:]),
  764. // the result will be strange focus bugs (like bmo bugs 399471, 403232,
  765. // 404433 and 408266).
  766. NSResponder* firstResponder = [window firstResponder];
  767. if ([mView isEqual:firstResponder]) {
  768. // Sometimes SetFocus() is called on an nsChildView object that's
  769. // already focused. In principle this shouldn't happen, and in any
  770. // case we shouldn't have to dispatch any events. But if we don't, we
  771. // sometimes get text-input cursors blinking in more than one text
  772. // field, or still blinking when the browser is no longer active. For
  773. // reasons that aren't at all clear, this problem can be avoided by
  774. // always sending an NS_GOTFOCUS message here.
  775. if ([mView isKindOfClass:[ChildView class]])
  776. [(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
  777. } else {
  778. // Retain and release firstResponder around the call to
  779. // makeFirstResponder.
  780. [firstResponder retain];
  781. if ([window makeFirstResponder:mView]) {
  782. if ([firstResponder isKindOfClass:[ChildView class]])
  783. [(ChildView *)firstResponder sendFocusEvent:NS_LOSTFOCUS];
  784. if ([mView isKindOfClass:[ChildView class]])
  785. [(ChildView *)mView sendFocusEvent:NS_GOTFOCUS];
  786. }
  787. [firstResponder release];
  788. }
  789. }
  790. mInSetFocus = PR_FALSE;
  791. return NS_OK;
  792. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  793. }
  794. // Set the colormap of the window
  795. NS_IMETHODIMP nsChildView::SetColorMap(nsColorMap *aColorMap)
  796. {
  797. return NS_OK;
  798. }
  799. NS_IMETHODIMP nsChildView::SetMenuBar(nsIMenuBar * aMenuBar)
  800. {
  801. return NS_ERROR_FAILURE; // subviews don't have menu bars
  802. }
  803. NS_IMETHODIMP nsChildView::ShowMenuBar(PRBool aShow)
  804. {
  805. return NS_ERROR_FAILURE; // subviews don't have menu bars
  806. }
  807. // Override to set the cursor on the mac
  808. NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
  809. {
  810. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  811. nsBaseWidget::SetCursor(aCursor);
  812. [[nsCursorManager sharedInstance] setCursor: aCursor];
  813. return NS_OK;
  814. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  815. }
  816. // implement to fix "hidden virtual function" warning
  817. NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
  818. PRUint32 aHotspotX, PRUint32 aHotspotY)
  819. {
  820. return nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
  821. }
  822. #pragma mark -
  823. // Get this component dimension
  824. NS_IMETHODIMP nsChildView::GetBounds(nsRect &aRect)
  825. {
  826. aRect = mBounds;
  827. return NS_OK;
  828. }
  829. NS_IMETHODIMP nsChildView::SetBounds(const nsRect &aRect)
  830. {
  831. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  832. nsresult rv = Inherited::SetBounds(aRect);
  833. if (NS_SUCCEEDED(rv)) {
  834. NSRect r;
  835. GeckoRectToNSRect(aRect, r);
  836. [mView setFrame:r];
  837. }
  838. return rv;
  839. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  840. }
  841. NS_IMETHODIMP nsChildView::ConstrainPosition(PRBool aAllowSlop,
  842. PRInt32 *aX, PRInt32 *aY)
  843. {
  844. return NS_OK;
  845. }
  846. // Move this component, aX and aY are in the parent widget coordinate system
  847. NS_IMETHODIMP nsChildView::Move(PRInt32 aX, PRInt32 aY)
  848. {
  849. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  850. if (!mView || (mBounds.x == aX && mBounds.y == aY))
  851. return NS_OK;
  852. mBounds.x = aX;
  853. mBounds.y = aY;
  854. NSRect r;
  855. GeckoRectToNSRect(mBounds, r);
  856. [mView setFrame:r];
  857. if (mVisible)
  858. [mView setNeedsDisplay:YES];
  859. ReportMoveEvent();
  860. return NS_OK;
  861. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  862. }
  863. NS_IMETHODIMP nsChildView::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
  864. {
  865. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  866. if (!mView || (mBounds.width == aWidth && mBounds.height == aHeight))
  867. return NS_OK;
  868. mBounds.width = aWidth;
  869. mBounds.height = aHeight;
  870. NSRect r;
  871. GeckoRectToNSRect(mBounds, r);
  872. [mView setFrame:r];
  873. if (mVisible && aRepaint)
  874. [mView setNeedsDisplay:YES];
  875. ReportSizeEvent();
  876. return NS_OK;
  877. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  878. }
  879. NS_IMETHODIMP nsChildView::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
  880. {
  881. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  882. BOOL isMoving = (mBounds.x != aX || mBounds.y != aY);
  883. BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight);
  884. if (!mView || (!isMoving && !isResizing))
  885. return NS_OK;
  886. if (isMoving) {
  887. mBounds.x = aX;
  888. mBounds.y = aY;
  889. }
  890. if (isResizing) {
  891. mBounds.width = aWidth;
  892. mBounds.height = aHeight;
  893. }
  894. NSRect r;
  895. GeckoRectToNSRect(mBounds, r);
  896. [mView setFrame:r];
  897. if (mVisible && aRepaint)
  898. [mView setNeedsDisplay:YES];
  899. if (isMoving) {
  900. ReportMoveEvent();
  901. if (mOnDestroyCalled)
  902. return NS_OK;
  903. }
  904. if (isResizing)
  905. ReportSizeEvent();
  906. return NS_OK;
  907. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  908. }
  909. NS_METHOD nsChildView::GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight)
  910. {
  911. return NS_ERROR_FAILURE; // nobody call this anywhere in the code
  912. }
  913. NS_METHOD nsChildView::SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight)
  914. {
  915. return NS_ERROR_FAILURE; // nobody call this anywhere in the code
  916. }
  917. NS_IMETHODIMP nsChildView::BeginResizingChildren(void)
  918. {
  919. return NS_OK;
  920. }
  921. NS_IMETHODIMP nsChildView::EndResizingChildren(void)
  922. {
  923. return NS_OK;
  924. }
  925. NS_IMETHODIMP nsChildView::GetPluginClipRect(nsRect& outClipRect, nsPoint& outOrigin, PRBool& outWidgetVisible)
  926. {
  927. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  928. NS_ASSERTION(mIsPluginView, "GetPluginClipRect must only be called on a plugin widget");
  929. if (!mIsPluginView) return NS_ERROR_FAILURE;
  930. NSWindow* window = [mView nativeWindow];
  931. if (!window) return NS_ERROR_FAILURE;
  932. NSPoint viewOrigin = [mView convertPoint:NSZeroPoint toView:nil];
  933. NSRect frame = [[window contentView] frame];
  934. viewOrigin.y = frame.size.height - viewOrigin.y;
  935. // set up the clipping region for plugins.
  936. NSRect visibleBounds = [mView visibleRect];
  937. NSPoint clipOrigin = [mView convertPoint:visibleBounds.origin toView:nil];
  938. // Convert from cocoa to QuickDraw coordinates
  939. clipOrigin.y = frame.size.height - clipOrigin.y;
  940. outClipRect.x = (nscoord)clipOrigin.x;
  941. outClipRect.y = (nscoord)clipOrigin.y;
  942. PRBool isVisible;
  943. IsVisible(isVisible);
  944. if (isVisible && [mView window] != nil) {
  945. outClipRect.width = (nscoord)visibleBounds.size.width;
  946. outClipRect.height = (nscoord)visibleBounds.size.height;
  947. outWidgetVisible = PR_TRUE;
  948. }
  949. else {
  950. outClipRect.width = 0;
  951. outClipRect.height = 0;
  952. outWidgetVisible = PR_FALSE;
  953. }
  954. // need to convert view's origin to window coordinates.
  955. // then, encode as "SetOrigin" ready values.
  956. outOrigin.x = (nscoord)-viewOrigin.x;
  957. outOrigin.y = (nscoord)-viewOrigin.y;
  958. return NS_OK;
  959. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  960. }
  961. NS_IMETHODIMP nsChildView::StartDrawPlugin()
  962. {
  963. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  964. NS_ASSERTION(mIsPluginView, "StartDrawPlugin must only be called on a plugin widget");
  965. if (!mIsPluginView) return NS_ERROR_FAILURE;
  966. // Prevent reentrant "drawing" (or in fact reentrant handling of any plugin
  967. // event). Doing this for both CoreGraphics and QuickDraw plugins restores
  968. // the 1.8-branch behavior wrt reentrancy, and fixes (or works around) bugs
  969. // caused by plugins depending on the old behavior -- e.g. bmo bug 409615.
  970. if (mPluginDrawing)
  971. return NS_ERROR_FAILURE;
  972. NSWindow* window = [mView nativeWindow];
  973. if (!window)
  974. return NS_ERROR_FAILURE;
  975. // It appears that the WindowRef from which we get the plugin port undergoes the
  976. // traditional BeginUpdate/EndUpdate cycle, which, if you recall, sets the visible
  977. // region to the intersection of the visible region and the update region. Since
  978. // we don't know here if we're being drawn inside a BeginUpdate/EndUpdate pair
  979. // (which seem to occur in [NSWindow display]), and we don't want to have the burden
  980. // of correctly doing Carbon invalidates of the plugin rect, we manually set the
  981. // visible region to be the entire port every time. It is necessary to set up our
  982. // window's port even for CoreGraphics plugins, because they may still use Carbon
  983. // internally (see bug #420527 for details).
  984. CGrafPtr port = ::GetWindowPort(WindowRef([window windowRef]));
  985. if (!mPluginIsCG)
  986. port = mPluginPort.qdPort.port;
  987. RgnHandle pluginRegion = ::NewRgn();
  988. if (pluginRegion) {
  989. PRBool portChanged = (port != CGrafPtr(GetQDGlobalsThePort()));
  990. CGrafPtr oldPort;
  991. GDHandle oldDevice;
  992. if (portChanged) {
  993. ::GetGWorld(&oldPort, &oldDevice);
  994. ::SetGWorld(port, ::IsPortOffscreen(port) ? nsnull : ::GetMainDevice());
  995. }
  996. ::SetOrigin(0, 0);
  997. nsRect clipRect; // this is in native window coordinates
  998. nsPoint origin;
  999. PRBool visible;
  1000. GetPluginClipRect(clipRect, origin, visible);
  1001. // XXX if we're not visible, set an empty clip region?
  1002. Rect pluginRect;
  1003. ConvertGeckoRectToMacRect(clipRect, pluginRect);
  1004. ::RectRgn(pluginRegion, &pluginRect);
  1005. ::SetPortVisibleRegion(port, pluginRegion);
  1006. ::SetPortClipRegion(port, pluginRegion);
  1007. // now set up the origin for the plugin
  1008. ::SetOrigin(origin.x, origin.y);
  1009. ::DisposeRgn(pluginRegion);
  1010. if (portChanged)
  1011. ::SetGWorld(oldPort, oldDevice);
  1012. }
  1013. mPluginDrawing = PR_TRUE;
  1014. return NS_OK;
  1015. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1016. }
  1017. NS_IMETHODIMP nsChildView::EndDrawPlugin()
  1018. {
  1019. NS_ASSERTION(mIsPluginView, "EndDrawPlugin must only be called on a plugin widget");
  1020. if (!mIsPluginView) return NS_ERROR_FAILURE;
  1021. mPluginDrawing = PR_FALSE;
  1022. return NS_OK;
  1023. }
  1024. NS_IMETHODIMP nsChildView::SetPluginInstanceOwner(nsIPluginInstanceOwner* aInstanceOwner)
  1025. {
  1026. mPluginInstanceOwner = aInstanceOwner;
  1027. return NS_OK;
  1028. }
  1029. void nsChildView::LiveResizeStarted()
  1030. {
  1031. // XXX todo. Use this to disable Java async redraw during resize
  1032. mLiveResizeInProgress = PR_TRUE;
  1033. }
  1034. void nsChildView::LiveResizeEnded()
  1035. {
  1036. mLiveResizeInProgress = PR_FALSE;
  1037. }
  1038. static NSString* ToNSString(const nsAString& aString)
  1039. {
  1040. return [NSString stringWithCharacters:aString.BeginReading()
  1041. length:aString.Length()];
  1042. }
  1043. struct KeyboardLayoutOverride {
  1044. PRInt32 mKeyboardLayout;
  1045. PRBool mOverrideEnabled;
  1046. };
  1047. static KeyboardLayoutOverride gOverrideKeyboardLayout;
  1048. static const PRUint32 sModifierFlagMap[][2] = {
  1049. { nsIWidget::CAPS_LOCK, NSAlphaShiftKeyMask },
  1050. { nsIWidget::SHIFT_L, NSShiftKeyMask },
  1051. { nsIWidget::CTRL_L, NSControlKeyMask },
  1052. { nsIWidget::ALT_L, NSAlternateKeyMask },
  1053. { nsIWidget::COMMAND, NSCommandKeyMask },
  1054. { nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask },
  1055. { nsIWidget::HELP, NSHelpKeyMask },
  1056. { nsIWidget::FUNCTION, NSFunctionKeyMask }
  1057. };
  1058. nsresult nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
  1059. PRInt32 aNativeKeyCode,
  1060. PRUint32 aModifierFlags,
  1061. const nsAString& aCharacters,
  1062. const nsAString& aUnmodifiedCharacters)
  1063. {
  1064. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1065. PRUint32 modifierFlags = 0;
  1066. for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sModifierFlagMap); ++i) {
  1067. if (aModifierFlags & sModifierFlagMap[i][0]) {
  1068. modifierFlags |= sModifierFlagMap[i][1];
  1069. }
  1070. }
  1071. int windowNumber = [[mView window] windowNumber];
  1072. NSEvent* downEvent = [NSEvent keyEventWithType:NSKeyDown
  1073. location:NSMakePoint(0,0)
  1074. modifierFlags:modifierFlags
  1075. timestamp:0
  1076. windowNumber:windowNumber
  1077. context:[NSGraphicsContext currentContext]
  1078. characters:ToNSString(aCharacters)
  1079. charactersIgnoringModifiers:ToNSString(aUnmodifiedCharacters)
  1080. isARepeat:NO
  1081. keyCode:aNativeKeyCode];
  1082. NSEvent* upEvent = [ChildView makeNewCocoaEventWithType:NSKeyUp
  1083. fromEvent:downEvent];
  1084. if (downEvent && upEvent) {
  1085. KeyboardLayoutOverride currentLayout = gOverrideKeyboardLayout;
  1086. gOverrideKeyboardLayout.mKeyboardLayout = aNativeKeyboardLayout;
  1087. gOverrideKeyboardLayout.mOverrideEnabled = PR_TRUE;
  1088. [NSApp sendEvent:downEvent];
  1089. [NSApp sendEvent:upEvent];
  1090. // processKeyDownEvent and keyUp block exceptions so we're sure to
  1091. // reach here to restore gOverrideKeyboardLayout
  1092. gOverrideKeyboardLayout = currentLayout;
  1093. }
  1094. return NS_OK;
  1095. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1096. }
  1097. #pragma mark -
  1098. #ifdef INVALIDATE_DEBUGGING
  1099. static Boolean KeyDown(const UInt8 theKey)
  1100. {
  1101. KeyMap map;
  1102. GetKeys(map);
  1103. return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0;
  1104. }
  1105. static Boolean caps_lock()
  1106. {
  1107. return KeyDown(0x39);
  1108. }
  1109. static void blinkRect(Rect* r)
  1110. {
  1111. StRegionFromPool oldClip;
  1112. if (oldClip != NULL)
  1113. ::GetClip(oldClip);
  1114. ::ClipRect(r);
  1115. ::InvertRect(r);
  1116. UInt32 end = ::TickCount() + 5;
  1117. while (::TickCount() < end) ;
  1118. ::InvertRect(r);
  1119. if (oldClip != NULL)
  1120. ::SetClip(oldClip);
  1121. }
  1122. static void blinkRgn(RgnHandle rgn)
  1123. {
  1124. StRegionFromPool oldClip;
  1125. if (oldClip != NULL)
  1126. ::GetClip(oldClip);
  1127. ::SetClip(rgn);
  1128. ::InvertRgn(rgn);
  1129. UInt32 end = ::TickCount() + 5;
  1130. while (::TickCount() < end) ;
  1131. ::InvertRgn(rgn);
  1132. if (oldClip != NULL)
  1133. ::SetClip(oldClip);
  1134. }
  1135. #endif
  1136. // Invalidate this component's visible area
  1137. NS_IMETHODIMP nsChildView::Invalidate(PRBool aIsSynchronous)
  1138. {
  1139. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1140. if (!mView || !mVisible)
  1141. return NS_OK;
  1142. if (aIsSynchronous) {
  1143. [mView display];
  1144. }
  1145. else if ([NSView focusView]) {
  1146. // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
  1147. // don't lose it.
  1148. [mView setNeedsPendingDisplay];
  1149. }
  1150. else {
  1151. [mView setNeedsDisplay:YES];
  1152. }
  1153. return NS_OK;
  1154. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1155. }
  1156. // Invalidate this component's visible area
  1157. NS_IMETHODIMP nsChildView::Invalidate(const nsRect &aRect, PRBool aIsSynchronous)
  1158. {
  1159. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1160. if (!mView || !mVisible)
  1161. return NS_OK;
  1162. NSRect r;
  1163. GeckoRectToNSRect(aRect, r);
  1164. if (aIsSynchronous) {
  1165. [mView displayRect:r];
  1166. }
  1167. else if ([NSView focusView]) {
  1168. // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
  1169. // don't lose it.
  1170. [mView setNeedsPendingDisplayInRect:r];
  1171. }
  1172. else {
  1173. [mView setNeedsDisplayInRect:r];
  1174. }
  1175. return NS_OK;
  1176. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1177. }
  1178. // Validate the widget
  1179. NS_IMETHODIMP nsChildView::Validate()
  1180. {
  1181. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1182. [mView setNeedsDisplay:NO];
  1183. return NS_OK;
  1184. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1185. }
  1186. // Invalidate this component's visible area
  1187. NS_IMETHODIMP nsChildView::InvalidateRegion(const nsIRegion *aRegion, PRBool aIsSynchronous)
  1188. {
  1189. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1190. if (!mView || !mVisible)
  1191. return NS_OK;
  1192. // FIXME rewrite to use a Cocoa region when nsIRegion isn't a QD Region
  1193. NSRect r;
  1194. nsRect bounds;
  1195. nsIRegion* region = const_cast<nsIRegion*>(aRegion); // ugh. this method should be const
  1196. region->GetBoundingBox(&bounds.x, &bounds.y, &bounds.width, &bounds.height);
  1197. GeckoRectToNSRect(bounds, r);
  1198. if (aIsSynchronous)
  1199. [mView displayRect:r];
  1200. else
  1201. [mView setNeedsDisplayInRect:r];
  1202. return NS_OK;
  1203. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1204. }
  1205. inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8)
  1206. {
  1207. // return (color8 == 0xFF ? 0xFFFF : (color8 << 8));
  1208. return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */
  1209. }
  1210. // Dummy impl, meant to be overridden
  1211. PRBool
  1212. nsChildView::OnPaint(nsPaintEvent &event)
  1213. {
  1214. return PR_TRUE;
  1215. }
  1216. // this is handled for us by UpdateWidget
  1217. NS_IMETHODIMP nsChildView::Update()
  1218. {
  1219. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1220. // Update means "Flush any pending changes right now." It does *not* mean
  1221. // repaint the world. :) -- dwh
  1222. [mView displayIfNeeded];
  1223. return NS_OK;
  1224. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1225. }
  1226. #pragma mark -
  1227. // Scroll the bits of a view and its children
  1228. // FIXME: I'm sure the invalidating can be optimized, just no time now.
  1229. NS_IMETHODIMP nsChildView::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
  1230. {
  1231. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1232. BOOL viewWasDirty = NO;
  1233. if (mVisible) {
  1234. viewWasDirty = [mView needsDisplay];
  1235. NSSize scrollVector = {aDx,aDy};
  1236. [mView scrollRect: [mView visibleRect] by:scrollVector];
  1237. }
  1238. // Scroll the children (even if the widget is not visible)
  1239. for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
  1240. // We use resize rather than move since it gives us control
  1241. // over repainting. We can scroll like a bat out of hell
  1242. // by not wasting time invalidating the widgets, since it's
  1243. // completely unnecessary to do so.
  1244. nsRect bounds;
  1245. kid->GetBounds(bounds);
  1246. kid->Resize(bounds.x + aDx, bounds.y + aDy, bounds.width, bounds.height, PR_FALSE);
  1247. }
  1248. if (mOnDestroyCalled)
  1249. return NS_OK;
  1250. if (mVisible) {
  1251. if (viewWasDirty) {
  1252. [mView setNeedsDisplay:YES];
  1253. }
  1254. else {
  1255. NSRect frame = [mView visibleRect];
  1256. NSRect horizInvalid = frame;
  1257. NSRect vertInvalid = frame;
  1258. if (aDx != 0) {
  1259. horizInvalid.size.width = abs(aDx);
  1260. if (aDx < 0)
  1261. horizInvalid.origin.x = frame.origin.x + frame.size.width + aDx;
  1262. [mView setNeedsDisplayInRect: horizInvalid];
  1263. }
  1264. if (aDy != 0) {
  1265. vertInvalid.size.height = abs(aDy);
  1266. if (aDy < 0)
  1267. vertInvalid.origin.y = frame.origin.y + frame.size.height + aDy;
  1268. [mView setNeedsDisplayInRect: vertInvalid];
  1269. }
  1270. // We also need to check for any ChildViews which overlap this widget
  1271. // but are not descendent widgets. If there are any, we need to
  1272. // invalidate the area of this view that these ChildViews will have been
  1273. // blitted into, since these widgets aren't supposed to scroll with
  1274. // this widget.
  1275. // To do this, start at the root Gecko NSView, and walk down along
  1276. // our ancestor view chain, looking at all the subviews in each level
  1277. // of the hierarchy. If we find a non-ancestor view that overlaps
  1278. // this view, invalidate the area around it.
  1279. // We need to convert all rects to a common ancestor view to intersect
  1280. // them, since a view's frame is in the coordinate space of its parent.
  1281. // Use mParentView as the frame of reference.
  1282. NSRect selfFrame = [mParentView convertRect:[mView frame] fromView:[mView superview]];
  1283. NSView* view = mParentView;
  1284. BOOL selfLevel = NO;
  1285. while (!selfLevel) {
  1286. NSView* nextAncestorView = nil;
  1287. NSArray* subviews = [view subviews];
  1288. for (unsigned int i = 0; i < [subviews count]; ++i) {
  1289. NSView* subView = [subviews objectAtIndex: i];
  1290. if (subView == mView)
  1291. selfLevel = YES;
  1292. else if ([mView isDescendantOf:subView])
  1293. nextAncestorView = subView;
  1294. else {
  1295. NSRect intersectArea = NSIntersectionRect([mParentView convertRect:[subView frame] fromView:[subView superview]], selfFrame);
  1296. if (!NSIsEmptyRect(intersectArea)) {
  1297. NSPoint origin = [mView convertPoint:intersectArea.origin fromView:mParentView];
  1298. if (aDy != 0) {
  1299. vertInvalid.origin.x = origin.x;
  1300. if (aDy < 0) // scrolled down, invalidate above
  1301. vertInvalid.origin.y = origin.y + aDy;
  1302. else // invalidate below
  1303. vertInvalid.origin.y = origin.y + intersectArea.size.height;
  1304. vertInvalid.size.width = intersectArea.size.width;
  1305. [mView setNeedsDisplayInRect: vertInvalid];
  1306. }
  1307. if (aDx != 0) {
  1308. horizInvalid.origin.y = origin.y;
  1309. if (aDx < 0) // scrolled right, invalidate to the left
  1310. horizInvalid.origin.x = origin.x + aDx;
  1311. else // invalidate to the right
  1312. horizInvalid.origin.x = origin.x + intersectArea.size.width;
  1313. horizInvalid.size.height = intersectArea.size.height;
  1314. [mView setNeedsDisplayInRect: horizInvalid];
  1315. }
  1316. }
  1317. }
  1318. }
  1319. view = nextAncestorView;
  1320. }
  1321. }
  1322. }
  1323. // This is an evil hack that doesn't always work.
  1324. //
  1325. // Drawing plugins in a Cocoa environment is tricky, because the
  1326. // plugins are living in a Carbon WindowRef/BeginUpdate/EndUpdate
  1327. // world, and Cocoa has its own notion of dirty rectangles. Throw
  1328. // Quartz Extreme and QuickTime into the mix, and things get bad.
  1329. //
  1330. // This code is working around a cosmetic issue seen when Quartz Extreme
  1331. // is active, and you're scrolling a page with a QuickTime plugin; areas
  1332. // outside the plugin fail to scroll properly. This [display] ensures that
  1333. // the view is properly drawn before the next Scroll call.
  1334. //
  1335. // The time this doesn't work is when you're scrolling a page containing
  1336. // an iframe which in turn contains a plugin.
  1337. //
  1338. // This is turned off because it makes scrolling pages with plugins slow.
  1339. //
  1340. //if ([mView childViewHasPlugin])
  1341. // [mView display];
  1342. return NS_OK;
  1343. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1344. }
  1345. // Invokes callback and ProcessEvent methods on Event Listener object
  1346. NS_IMETHODIMP nsChildView::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
  1347. {
  1348. #ifdef DEBUG
  1349. debug_DumpEvent(stdout, event->widget, event, nsCAutoString("something"), 0);
  1350. #endif
  1351. aStatus = nsEventStatus_eIgnore;
  1352. nsCOMPtr<nsIWidget> kungFuDeathGrip(mParentWidget ? mParentWidget : this);
  1353. if (mParentWidget) {
  1354. nsWindowType type;
  1355. mParentWidget->GetWindowType(type);
  1356. if (type == eWindowType_popup) {
  1357. // use the parent popup's widget if there is no view
  1358. void* clientData = nsnull;
  1359. if (event->widget)
  1360. event->widget->GetClientData(clientData);
  1361. if (!clientData)
  1362. event->widget = mParentWidget;
  1363. }
  1364. }
  1365. if (mEventCallback)
  1366. aStatus = (*mEventCallback)(event);
  1367. // dispatch to event listener if event was not consumed
  1368. if (mEventListener && aStatus != nsEventStatus_eConsumeNoDefault)
  1369. aStatus = mEventListener->ProcessEvent(*event);
  1370. return NS_OK;
  1371. }
  1372. PRBool nsChildView::DispatchWindowEvent(nsGUIEvent &event)
  1373. {
  1374. nsEventStatus status;
  1375. DispatchEvent(&event, status);
  1376. return ConvertStatus(status);
  1377. }
  1378. // Deal with all sort of mouse event
  1379. PRBool nsChildView::DispatchMouseEvent(nsMouseEvent &aEvent)
  1380. {
  1381. PRBool result = PR_FALSE;
  1382. // call the event callback
  1383. if (mEventCallback)
  1384. return DispatchWindowEvent(aEvent);
  1385. if (mMouseListener) {
  1386. nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
  1387. switch (aEvent.message) {
  1388. case NS_MOUSE_MOVE:
  1389. result = ConvertStatus(mMouseListener->MouseMoved(aEvent));
  1390. break;
  1391. case NS_MOUSE_BUTTON_DOWN:
  1392. result = ConvertStatus(mMouseListener->MousePressed(aEvent));
  1393. break;
  1394. case NS_MOUSE_BUTTON_UP: {
  1395. result = ConvertStatus(mMouseListener->MouseReleased(aEvent));
  1396. if (mMouseListener)
  1397. result = ConvertStatus(mMouseListener->MouseClicked(aEvent));
  1398. break;
  1399. }
  1400. } // switch
  1401. }
  1402. return result;
  1403. }
  1404. #pragma mark -
  1405. PRBool nsChildView::ReportDestroyEvent()
  1406. {
  1407. nsGUIEvent event(PR_TRUE, NS_DESTROY, this);
  1408. event.time = PR_IntervalNow();
  1409. return DispatchWindowEvent(event);
  1410. }
  1411. PRBool nsChildView::ReportMoveEvent()
  1412. {
  1413. nsGUIEvent moveEvent(PR_TRUE, NS_MOVE, this);
  1414. moveEvent.refPoint.x = mBounds.x;
  1415. moveEvent.refPoint.y = mBounds.y;
  1416. moveEvent.time = PR_IntervalNow();
  1417. return DispatchWindowEvent(moveEvent);
  1418. }
  1419. PRBool nsChildView::ReportSizeEvent()
  1420. {
  1421. nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
  1422. sizeEvent.time = PR_IntervalNow();
  1423. sizeEvent.windowSize = &mBounds;
  1424. sizeEvent.mWinWidth = mBounds.width;
  1425. sizeEvent.mWinHeight = mBounds.height;
  1426. return DispatchWindowEvent(sizeEvent);
  1427. }
  1428. #pragma mark -
  1429. /* Calculate the x and y offsets for this particular widget
  1430. * @update ps 09/22/98
  1431. * @param aX -- x offset amount
  1432. * @param aY -- y offset amount
  1433. * @return NOTHING
  1434. */
  1435. NS_IMETHODIMP nsChildView::CalcOffset(PRInt32 &aX,PRInt32 &aY)
  1436. {
  1437. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1438. aX = aY = 0;
  1439. NSRect bounds = {{0, 0}, {0, 0}};
  1440. bounds = [mView convertRect:bounds toView:nil];
  1441. aX += static_cast<PRInt32>(bounds.origin.x);
  1442. aY += static_cast<PRInt32>(bounds.origin.y);
  1443. return NS_OK;
  1444. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1445. }
  1446. // Find if a point in local coordinates is inside this object
  1447. PRBool nsChildView::PointInWidget(Point aThePoint)
  1448. {
  1449. // get the origin in local coordinates
  1450. nsPoint widgetOrigin(0, 0);
  1451. LocalToWindowCoordinate(widgetOrigin);
  1452. // get rectangle relatively to the parent
  1453. nsRect widgetRect;
  1454. GetBounds(widgetRect);
  1455. // convert the topLeft corner to local coordinates
  1456. widgetRect.MoveBy(widgetOrigin.x, widgetOrigin.y);
  1457. // finally tell whether it's a hit
  1458. return widgetRect.Contains(aThePoint.h, aThePoint.v);
  1459. }
  1460. #pragma mark -
  1461. // Convert the given rect to global coordinates.
  1462. // @param aLocalRect -- rect in local coordinates of this widget
  1463. // @param aGlobalRect -- |aLocalRect| in global coordinates
  1464. NS_IMETHODIMP nsChildView::WidgetToScreen(const nsRect& aLocalRect, nsRect& aGlobalRect)
  1465. {
  1466. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1467. NSRect temp;
  1468. GeckoRectToNSRect(aLocalRect, temp);
  1469. // 1. First translate this rect into window coords. The returned rect is always in
  1470. // bottom-left coordinates.
  1471. //
  1472. // NOTE: convertRect:toView:nil doesn't care if |mView| is a flipped view (with
  1473. // top-left coords) and so assumes that our passed-in rect's origin is in
  1474. // bottom-left coordinates. We adjust this further down, by subtracting
  1475. // the final screen rect's origin by the rect's height, to get the origo
  1476. // where we want it.
  1477. temp = [mView convertRect:temp toView:nil];
  1478. // 2. We turn the window-coord rect's origin into screen (still bottom-left) coords.
  1479. temp.origin = [[mView nativeWindow] convertBaseToScreen:temp.origin];
  1480. // 3. Since we're dealing in bottom-left coords, we need to make it top-left coords
  1481. // before we pass it back to Gecko.
  1482. FlipCocoaScreenCoordinate(temp.origin);
  1483. // 4. If this is rect has a size (and is not simply a point), it is important to account
  1484. // for the fact that convertRect:toView:nil thought our passed-in point was in bottom-left
  1485. // coords in step #1. Thus, we subtract the rect's height, to get the top-left rect's origin
  1486. // where we want it.
  1487. temp.origin.y -= temp.size.height;
  1488. NSRectToGeckoRect(temp, aGlobalRect);
  1489. return NS_OK;
  1490. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1491. }
  1492. // Convert the given rect to local coordinates.
  1493. // @param aGlobalRect -- rect in screen coordinates
  1494. // @param aLocalRect -- |aGlobalRect| in coordinates of this widget
  1495. NS_IMETHODIMP nsChildView::ScreenToWidget(const nsRect& aGlobalRect, nsRect& aLocalRect)
  1496. {
  1497. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1498. NSRect temp;
  1499. GeckoRectToNSRect(aGlobalRect, temp);
  1500. FlipCocoaScreenCoordinate(temp.origin);
  1501. temp.origin = [[mView nativeWindow] convertScreenToBase:temp.origin]; // convert to screen coords
  1502. temp = [mView convertRect:temp fromView:nil]; // convert to window coords
  1503. NSRectToGeckoRect(temp, aLocalRect);
  1504. return NS_OK;
  1505. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1506. }
  1507. // Convert the coordinates to some device coordinates so GFX can draw.
  1508. void nsChildView::ConvertToDeviceCoordinates(nscoord &aX, nscoord &aY)
  1509. {
  1510. PRInt32 offX = 0, offY = 0;
  1511. this->CalcOffset(offX,offY);
  1512. aX += offX;
  1513. aY += offY;
  1514. }
  1515. NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener,
  1516. PRBool aDoCapture,
  1517. PRBool aConsumeRollupEvent)
  1518. {
  1519. // this never gets called, only top-level windows can be rollup widgets
  1520. return NS_OK;
  1521. }
  1522. NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
  1523. {
  1524. // child views don't have titles
  1525. return NS_OK;
  1526. }
  1527. NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
  1528. {
  1529. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1530. [NSApp requestUserAttention:NSInformationalRequest];
  1531. return NS_OK;
  1532. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1533. }
  1534. #pragma mark -
  1535. // Force Input Method Editor to commit the uncommited input
  1536. // Note that this and other nsIKBStateControl methods don't necessarily
  1537. // get called on the same ChildView that input is going through.
  1538. NS_IMETHODIMP nsChildView::ResetInputState()
  1539. {
  1540. #ifdef DEBUG_IME
  1541. NSLog(@"**** ResetInputState");
  1542. #endif
  1543. nsTSMManager::CommitIME();
  1544. return NS_OK;
  1545. }
  1546. // 'open' means that it can take non-ASCII chars
  1547. NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
  1548. {
  1549. #ifdef DEBUG_IME
  1550. NSLog(@"**** SetIMEOpenState aState = %d", aState);
  1551. #endif
  1552. nsTSMManager::SetIMEOpenState(aState);
  1553. return NS_OK;
  1554. }
  1555. // 'open' means that it can take non-ASCII chars
  1556. NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
  1557. {
  1558. #ifdef DEBUG_IME
  1559. NSLog(@"**** GetIMEOpenState");
  1560. #endif
  1561. *aState = nsTSMManager::GetIMEOpenState();
  1562. return NS_OK;
  1563. }
  1564. NS_IMETHODIMP nsChildView::SetIMEEnabled(PRUint32 aState)
  1565. {
  1566. #ifdef DEBUG_IME
  1567. NSLog(@"**** SetIMEEnabled aState = %d", aState);
  1568. #endif
  1569. switch (aState) {
  1570. case nsIKBStateControl::IME_STATUS_ENABLED:
  1571. nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
  1572. nsTSMManager::EnableIME(PR_TRUE);
  1573. break;
  1574. case nsIKBStateControl::IME_STATUS_DISABLED:
  1575. nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
  1576. nsTSMManager::EnableIME(PR_FALSE);
  1577. break;
  1578. case nsIKBStateControl::IME_STATUS_PASSWORD:
  1579. nsTSMManager::SetRomanKeyboardsOnly(PR_TRUE);
  1580. nsTSMManager::EnableIME(PR_FALSE);
  1581. break;
  1582. default:
  1583. NS_ERROR("not implemented!");
  1584. }
  1585. return NS_OK;
  1586. }
  1587. NS_IMETHODIMP nsChildView::GetIMEEnabled(PRUint32* aState)
  1588. {
  1589. #ifdef DEBUG_IME
  1590. NSLog(@"**** GetIMEEnabled");
  1591. #endif
  1592. if (nsTSMManager::IsIMEEnabled())
  1593. *aState = nsIKBStateControl::IME_STATUS_ENABLED;
  1594. else if (nsTSMManager::IsRomanKeyboardsOnly())
  1595. *aState = nsIKBStateControl::IME_STATUS_PASSWORD;
  1596. else
  1597. *aState = nsIKBStateControl::IME_STATUS_DISABLED;
  1598. return NS_OK;
  1599. }
  1600. // Destruct and don't commit the IME composition string.
  1601. NS_IMETHODIMP nsChildView::CancelIMEComposition()
  1602. {
  1603. #ifdef DEBUG_IME
  1604. NSLog(@"**** CancelIMEComposition");
  1605. #endif
  1606. nsTSMManager::CancelIME();
  1607. return NS_OK;
  1608. }
  1609. NS_IMETHODIMP nsChildView::GetToggledKeyState(PRUint32 aKeyCode,
  1610. PRBool* aLEDState)
  1611. {
  1612. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1613. #ifdef DEBUG_IME
  1614. NSLog(@"**** GetToggledKeyState");
  1615. #endif
  1616. NS_ENSURE_ARG_POINTER(aLEDState);
  1617. PRUint32 key;
  1618. switch (aKeyCode) {
  1619. case NS_VK_CAPS_LOCK:
  1620. key = alphaLock;
  1621. break;
  1622. case NS_VK_NUM_LOCK:
  1623. key = kEventKeyModifierNumLockMask;
  1624. break;
  1625. // Mac doesn't support SCROLL_LOCK state.
  1626. default:
  1627. return NS_ERROR_NOT_IMPLEMENTED;
  1628. }
  1629. PRUint32 modifierFlags = ::GetCurrentEventKeyModifiers();
  1630. *aLEDState = (modifierFlags & key) != 0;
  1631. return NS_OK;
  1632. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1633. }
  1634. #pragma mark -
  1635. gfxASurface*
  1636. nsChildView::GetThebesSurface()
  1637. {
  1638. if (!mTempThebesSurface) {
  1639. mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxASurface::ImageFormatARGB32);
  1640. }
  1641. return mTempThebesSurface;
  1642. }
  1643. NS_IMETHODIMP
  1644. nsChildView::BeginSecureKeyboardInput()
  1645. {
  1646. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1647. nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
  1648. if (NS_SUCCEEDED(rv))
  1649. ::EnableSecureEventInput();
  1650. return rv;
  1651. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1652. }
  1653. NS_IMETHODIMP
  1654. nsChildView::EndSecureKeyboardInput()
  1655. {
  1656. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1657. nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
  1658. if (NS_SUCCEEDED(rv))
  1659. ::DisableSecureEventInput();
  1660. return rv;
  1661. NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1662. }
  1663. #ifdef ACCESSIBILITY
  1664. void
  1665. nsChildView::GetDocumentAccessible(nsIAccessible** aAccessible)
  1666. {
  1667. *aAccessible = nsnull;
  1668. nsCOMPtr<nsIAccessible> accessible = do_QueryReferent(mAccessible);
  1669. if (!mAccessible) {
  1670. // need to fetch the accessible anew, because it has gone away.
  1671. nsEventStatus status;
  1672. nsAccessibleEvent event(PR_TRUE, NS_GETACCESSIBLE, this);
  1673. DispatchEvent(&event, status);
  1674. // cache the accessible in our weak ptr
  1675. mAccessible = do_GetWeakReference(event.accessible);
  1676. // now try again
  1677. accessible = do_QueryReferent(mAccessible);
  1678. }
  1679. NS_IF_ADDREF(*aAccessible = accessible.get());
  1680. return;
  1681. }
  1682. #endif
  1683. #pragma mark -
  1684. @implementation ChildView
  1685. // globalDragPboard is non-null during native drag sessions that did not originate
  1686. // in our native NSView (it is set in |draggingEntered:|). It is unset when the
  1687. // drag session ends for this view, either with the mouse exiting or when a drop
  1688. // occurs in this view.
  1689. NSPasteboard* globalDragPboard = nil;
  1690. // gLastDragView and gLastDragEvent are only non-null during calls to |mouseDragged:|
  1691. // in our native NSView. They are used to communicate information to the drag service
  1692. // during drag invocation (starting a drag in from the view). All drag service drag
  1693. // invocations happen only while these two global variables are non-null, while |mouseDragged:|
  1694. // is on the stack.
  1695. NSView* gLastDragView = nil;
  1696. NSEvent* gLastDragEvent = nil;
  1697. // initWithFrame:geckoChild:
  1698. - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
  1699. {
  1700. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1701. if ((self = [super initWithFrame:inFrame])) {
  1702. mWindow = nil;
  1703. mGeckoChild = inChild;
  1704. mIsPluginView = NO;
  1705. mCurKeyEvent = nil;
  1706. mKeyDownHandled = PR_FALSE;
  1707. mKeyPressHandled = NO;
  1708. mKeyPressSent = NO;
  1709. // initialization for NSTextInput
  1710. mMarkedRange.location = NSNotFound;
  1711. mMarkedRange.length = 0;
  1712. mLastMouseDownEvent = nil;
  1713. mDragService = nsnull;
  1714. mPluginTSMDoc = nil;
  1715. }
  1716. // register for things we'll take from other applications
  1717. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
  1718. [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
  1719. NSStringPboardType,
  1720. NSURLPboardType,
  1721. NSFilesPromisePboardType,
  1722. kWildcardPboardType,
  1723. kCorePboardType_url,
  1724. kCorePboardType_urld,
  1725. kCorePboardType_urln,
  1726. nil]];
  1727. return self;
  1728. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1729. }
  1730. - (void)dealloc
  1731. {
  1732. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1733. [mPendingDirtyRects release];
  1734. [mLastMouseDownEvent release];
  1735. if (mPluginTSMDoc)
  1736. ::DeleteTSMDocument(mPluginTSMDoc);
  1737. if (sLastViewEntered == self)
  1738. sLastViewEntered = nil;
  1739. [super dealloc];
  1740. // This sets the current port to _savePort.
  1741. // todo: Only do if a Quickdraw plugin is present in the hierarchy!
  1742. ::SetPort(NULL);
  1743. NS_OBJC_END_TRY_ABORT_BLOCK;
  1744. }
  1745. - (void)widgetDestroyed
  1746. {
  1747. nsTSMManager::OnDestroyView(self);
  1748. mGeckoChild = nsnull;
  1749. mWindow = nil;
  1750. // Just in case we're destroyed abruptly and missed the draggingExited
  1751. // or performDragOperation message.
  1752. NS_IF_RELEASE(mDragService);
  1753. }
  1754. // mozView method, return our gecko child view widget. Note this does not AddRef.
  1755. - (nsIWidget*) widget
  1756. {
  1757. return static_cast<nsIWidget*>(mGeckoChild);
  1758. }
  1759. // mozView method, get the window that this view is associated with
  1760. - (NSWindow*)nativeWindow
  1761. {
  1762. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1763. NSWindow* currWin = [self window];
  1764. if (currWin)
  1765. return currWin;
  1766. else
  1767. return mWindow;
  1768. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1769. }
  1770. // mozView method, set the NSWindow that this view is associated with (even when
  1771. // not in the view hierarchy).
  1772. - (void)setNativeWindow:(NSWindow*)aWindow
  1773. {
  1774. mWindow = aWindow;
  1775. }
  1776. - (void)setNeedsPendingDisplay
  1777. {
  1778. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1779. mPendingFullDisplay = YES;
  1780. [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
  1781. NS_OBJC_END_TRY_ABORT_BLOCK;
  1782. }
  1783. - (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect
  1784. {
  1785. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1786. if (!mPendingDirtyRects)
  1787. mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1];
  1788. [mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]];
  1789. [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0];
  1790. NS_OBJC_END_TRY_ABORT_BLOCK;
  1791. }
  1792. // Clears the queue of any pending invalides
  1793. - (void)processPendingRedraws
  1794. {
  1795. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1796. if (mPendingFullDisplay) {
  1797. [self setNeedsDisplay:YES];
  1798. }
  1799. else {
  1800. unsigned int count = [mPendingDirtyRects count];
  1801. for (unsigned int i = 0; i < count; ++i) {
  1802. [self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]];
  1803. }
  1804. }
  1805. mPendingFullDisplay = NO;
  1806. [mPendingDirtyRects release];
  1807. mPendingDirtyRects = nil;
  1808. NS_OBJC_END_TRY_ABORT_BLOCK;
  1809. }
  1810. - (NSString*)description
  1811. {
  1812. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1813. return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])];
  1814. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1815. }
  1816. // Find the nearest scrollable view for this ChildView
  1817. // (recall that views are not refcounted)
  1818. - (nsIScrollableView*) getScrollableView
  1819. {
  1820. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
  1821. nsIScrollableView* scrollableView = nsnull;
  1822. ChildView* currView = self;
  1823. // we have to loop up through superviews in case the view that received the
  1824. // mouseDown is in fact a plugin view with no scrollbars
  1825. while (currView) {
  1826. nsIWidget* widget = [currView widget];
  1827. if (widget) {
  1828. void* clientData;
  1829. if (NS_SUCCEEDED(widget->GetClientData(clientData))) {
  1830. nsISupports* data = (nsISupports*)clientData;
  1831. nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(data));
  1832. if (req) {
  1833. req->GetInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollableView);
  1834. if (scrollableView)
  1835. break;
  1836. }
  1837. }
  1838. }
  1839. NSView* superview = [currView superview];
  1840. if (superview && [superview isMemberOfClass:[ChildView class]])
  1841. currView = (ChildView*)superview;
  1842. else
  1843. currView = nil;
  1844. }
  1845. return scrollableView;
  1846. NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
  1847. }
  1848. // set the closed hand cursor and record the starting scroll positions
  1849. - (void) startHandScroll:(NSEvent*)theEvent
  1850. {
  1851. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1852. if (!mGeckoChild)
  1853. return;
  1854. mHandScrollStartMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
  1855. nsIScrollableView* aScrollableView = [self getScrollableView];
  1856. // if we succeeded in getting aScrollableView
  1857. if (aScrollableView) {
  1858. aScrollableView->GetScrollPosition(mHandScrollStartScrollX, mHandScrollStartScrollY);
  1859. mGeckoChild->SetCursor(eCursor_grabbing);
  1860. mInHandScroll = TRUE;
  1861. }
  1862. NS_OBJC_END_TRY_ABORT_BLOCK;
  1863. }
  1864. // update the scroll position based on the new mouse coordinates
  1865. - (void) updateHandScroll:(NSEvent*)theEvent
  1866. {
  1867. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1868. if (!mGeckoChild)
  1869. return;
  1870. nsIScrollableView* aScrollableView = [self getScrollableView];
  1871. if (!aScrollableView)
  1872. return;
  1873. NSPoint newMouseLoc = [[self window] convertBaseToScreen:[theEvent locationInWindow]];
  1874. PRInt32 deltaX = (PRInt32)(mHandScrollStartMouseLoc.x - newMouseLoc.x);
  1875. PRInt32 deltaY = (PRInt32)(newMouseLoc.y - mHandScrollStartMouseLoc.y);
  1876. // convert to the nsIView coordinates
  1877. PRInt32 p2a = mGeckoChild->GetDeviceContext()->AppUnitsPerDevPixel();
  1878. nscoord newX = mHandScrollStartScrollX + NSIntPixelsToAppUnits(deltaX, p2a);
  1879. nscoord newY = mHandScrollStartScrollY + NSIntPixelsToAppUnits(deltaY, p2a);
  1880. aScrollableView->ScrollTo(newX, newY, NS_VMREFRESH_IMMEDIATE);
  1881. NS_OBJC_END_TRY_ABORT_BLOCK;
  1882. }
  1883. // Return true if the correct modifiers are pressed to perform hand scrolling.
  1884. + (BOOL) areHandScrollModifiers:(unsigned int)modifiers
  1885. {
  1886. // The command and option key should be held down. Ignore capsLock by
  1887. // setting it explicitly to match.
  1888. modifiers |= NSAlphaShiftKeyMask;
  1889. return (modifiers & NSDeviceIndependentModifierFlagsMask) ==
  1890. (NSAlphaShiftKeyMask | NSCommandKeyMask | NSAlternateKeyMask);
  1891. }
  1892. // If the user is pressing the hand scroll modifiers, then set
  1893. // the hand scroll cursor.
  1894. - (void) setHandScrollCursor:(NSEvent*)theEvent
  1895. {
  1896. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1897. if (!mGeckoChild)
  1898. return;
  1899. BOOL inMouseView = NO;
  1900. // check to see if the user has hand scroll modifiers held down; if so,
  1901. // find out if the cursor is in an ChildView
  1902. if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]]) {
  1903. NSPoint pointInWindow = [[self window] mouseLocationOutsideOfEventStream];
  1904. NSView* mouseView = [[[self window] contentView] hitTest:pointInWindow];
  1905. inMouseView = (mouseView != nil && [mouseView isMemberOfClass:[ChildView class]]);
  1906. }
  1907. if (inMouseView) {
  1908. mGeckoChild->SetCursor(eCursor_grab);
  1909. } else {
  1910. nsCursor cursor = mGeckoChild->GetCursor();
  1911. if (!mInHandScroll) {
  1912. if (cursor == eCursor_grab || cursor == eCursor_grabbing)
  1913. mGeckoChild->SetCursor(eCursor_standard);
  1914. }
  1915. }
  1916. NS_OBJC_END_TRY_ABORT_BLOCK;
  1917. }
  1918. // reset the scroll flag and cursor
  1919. - (void) stopHandScroll:(NSEvent*)theEvent
  1920. {
  1921. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1922. mInHandScroll = FALSE;
  1923. [self setHandScrollCursor:theEvent];
  1924. NS_OBJC_END_TRY_ABORT_BLOCK;
  1925. }
  1926. // When smooth scrolling is turned on on panther, the parent of a scrollbar (which
  1927. // I guess they assume is a NSScrollView) gets called with this method. I have no
  1928. // idea what the correct return value is, but we have to have this otherwise the scrollbar
  1929. // will not continuously respond when the mouse is held down in the pageup/down area.
  1930. -(float)_destinationFloatValueForScroller:(id)scroller
  1931. {
  1932. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1933. return [scroller floatValue];
  1934. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
  1935. }
  1936. // Override in order to keep our mouse enter/exit tracking rect in sync with
  1937. // the frame of the view
  1938. - (void)setFrame:(NSRect)frameRect
  1939. {
  1940. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1941. [super setFrame:frameRect];
  1942. if (mMouseEnterExitTag)
  1943. [self removeTrackingRect:mMouseEnterExitTag];
  1944. if ([self window])
  1945. mMouseEnterExitTag = [self addTrackingRect:[self bounds]
  1946. owner:self
  1947. userData:nil
  1948. assumeInside:[[self window] acceptsMouseMovedEvents]];
  1949. NS_OBJC_END_TRY_ABORT_BLOCK;
  1950. }
  1951. // Make the origin of this view the topLeft corner (gecko origin) rather
  1952. // than the bottomLeft corner (standard cocoa origin).
  1953. - (BOOL)isFlipped
  1954. {
  1955. return YES;
  1956. }
  1957. - (void)setTransparent:(BOOL)transparent
  1958. {
  1959. mIsTransparent = transparent;
  1960. }
  1961. - (BOOL)isOpaque
  1962. {
  1963. return !mIsTransparent;
  1964. }
  1965. -(void)setIsPluginView:(BOOL)aIsPlugin
  1966. {
  1967. mIsPluginView = aIsPlugin;
  1968. }
  1969. -(BOOL)isPluginView
  1970. {
  1971. return mIsPluginView;
  1972. }
  1973. - (BOOL)childViewHasPlugin
  1974. {
  1975. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1976. NSArray* subviews = [self subviews];
  1977. for (unsigned int i = 0; i < [subviews count]; i ++) {
  1978. id subview = [subviews objectAtIndex:i];
  1979. if ([subview respondsToSelector:@selector(isPluginView)] && [subview isPluginView])
  1980. return YES;
  1981. }
  1982. return NO;
  1983. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  1984. }
  1985. - (void)sendFocusEvent:(PRUint32)eventType
  1986. {
  1987. if (!mGeckoChild)
  1988. return;
  1989. nsEventStatus status = nsEventStatus_eIgnore;
  1990. nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoChild);
  1991. focusGuiEvent.time = PR_IntervalNow();
  1992. mGeckoChild->DispatchEvent(&focusGuiEvent, status);
  1993. }
  1994. // We accept key and mouse events, so don't keep passing them up the chain. Allow
  1995. // this to be a 'focussed' widget for event dispatch
  1996. - (BOOL)acceptsFirstResponder
  1997. {
  1998. return YES;
  1999. }
  2000. - (void)viewWillMoveToWindow:(NSWindow *)newWindow
  2001. {
  2002. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2003. if (!newWindow)
  2004. HideChildPluginViews(self);
  2005. if (mMouseEnterExitTag)
  2006. [self removeTrackingRect:mMouseEnterExitTag];
  2007. [super viewWillMoveToWindow:newWindow];
  2008. NS_OBJC_END_TRY_ABORT_BLOCK;
  2009. }
  2010. - (void)viewDidMoveToWindow
  2011. {
  2012. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2013. if ([self window])
  2014. mMouseEnterExitTag = [self addTrackingRect:[self bounds] owner:self
  2015. userData:nil assumeInside: [[self window]
  2016. acceptsMouseMovedEvents]];
  2017. [super viewDidMoveToWindow];
  2018. NS_OBJC_END_TRY_ABORT_BLOCK;
  2019. }
  2020. - (void)viewWillStartLiveResize
  2021. {
  2022. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2023. if (mGeckoChild && mIsPluginView)
  2024. mGeckoChild->LiveResizeStarted();
  2025. [super viewWillStartLiveResize];
  2026. NS_OBJC_END_TRY_ABORT_BLOCK;
  2027. }
  2028. - (void)viewDidEndLiveResize
  2029. {
  2030. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2031. if (mGeckoChild && mIsPluginView)
  2032. mGeckoChild->LiveResizeEnded();
  2033. [super viewDidEndLiveResize];
  2034. NS_OBJC_END_TRY_ABORT_BLOCK;
  2035. }
  2036. - (void)scrollRect:(NSRect)aRect by:(NSSize)offset
  2037. {
  2038. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2039. // Update any pending dirty rects to reflect the new scroll position
  2040. if (mPendingDirtyRects) {
  2041. unsigned int count = [mPendingDirtyRects count];
  2042. for (unsigned int i = 0; i < count; ++i) {
  2043. NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue];
  2044. NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height);
  2045. [mPendingDirtyRects replaceObjectAtIndex:i
  2046. withObject:[NSValue valueWithRect:newRect]];
  2047. }
  2048. }
  2049. [super scrollRect:aRect by:offset];
  2050. NS_OBJC_END_TRY_ABORT_BLOCK;
  2051. }
  2052. - (BOOL)mouseDownCanMoveWindow
  2053. {
  2054. return NO;
  2055. }
  2056. - (void)lockFocus
  2057. {
  2058. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2059. // Set the current GrafPort to a "safe" port before calling [NSQuickDrawView lockFocus],
  2060. // so that the NSQuickDrawView stashes a pointer to this known-good port internally.
  2061. // It will set the port back to this port on destruction.
  2062. ::SetPort(NULL); // todo: only do if a Quickdraw plugin is present in the hierarchy!
  2063. [super lockFocus];
  2064. NS_OBJC_END_TRY_ABORT_BLOCK;
  2065. }
  2066. // The display system has told us that a portion of our view is dirty. Tell
  2067. // gecko to paint it
  2068. - (void)drawRect:(NSRect)aRect
  2069. {
  2070. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2071. PRBool isVisible;
  2072. if (!mGeckoChild || NS_FAILED(mGeckoChild->IsVisible(isVisible)) || !isVisible)
  2073. return;
  2074. CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
  2075. nsRect geckoBounds;
  2076. mGeckoChild->GetBounds(geckoBounds);
  2077. NSRect bounds = [self bounds];
  2078. nsRefPtr<gfxQuartzSurface> targetSurface =
  2079. new gfxQuartzSurface(cgContext, gfxSize(bounds.size.width, bounds.size.height));
  2080. #ifdef DEBUG_UPDATE
  2081. fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d %d %d]\n",
  2082. self, mGeckoChild,
  2083. aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, cgContext,
  2084. geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height);
  2085. CGAffineTransform xform = CGContextGetCTM(cgContext);
  2086. fprintf (stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
  2087. #endif
  2088. nsRefPtr<gfxContext> targetContext = new gfxContext(targetSurface);
  2089. nsCOMPtr<nsIRenderingContext> rc;
  2090. mGeckoChild->GetDeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
  2091. rc->Init(mGeckoChild->GetDeviceContext(), targetContext);
  2092. /* clip and build a region */
  2093. nsCOMPtr<nsIRegion> rgn(do_CreateInstance(kRegionCID));
  2094. if (rgn)
  2095. rgn->Init();
  2096. const NSRect *rects;
  2097. int count, i;
  2098. [self getRectsBeingDrawn:&rects count:&count];
  2099. for (i = 0; i < count; ++i) {
  2100. const NSRect& r = rects[i];
  2101. // add to the region
  2102. if (rgn)
  2103. rgn->Union((PRInt32)r.origin.x, (PRInt32)r.origin.y, (PRInt32)r.size.width, (PRInt32)r.size.height);
  2104. // to the context for clipping
  2105. targetContext->Rectangle(gfxRect(r.origin.x, r.origin.y, r.size.width, r.size.height));
  2106. }
  2107. targetContext->Clip();
  2108. // bounding box of the dirty area
  2109. nsRect fullRect;
  2110. NSRectToGeckoRect(aRect, fullRect);
  2111. nsPaintEvent paintEvent(PR_TRUE, NS_PAINT, mGeckoChild);
  2112. paintEvent.renderingContext = rc;
  2113. paintEvent.rect = &fullRect;
  2114. paintEvent.region = rgn;
  2115. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2116. #ifdef MOZ_MACBROWSER
  2117. PRBool painted = mGeckoChild->DispatchWindowEvent(paintEvent);
  2118. if (!painted && [self isOpaque]) {
  2119. // Gecko refused to draw, but we've claimed to be opaque, so we have to
  2120. // draw something--fill with white.
  2121. CGContextSetRGBFillColor(cgContext, 1, 1, 1, 1);
  2122. CGContextFillRect(cgContext, CGRectMake(aRect.origin.x, aRect.origin.y,
  2123. aRect.size.width, aRect.size.height));
  2124. }
  2125. #else
  2126. mGeckoChild->DispatchWindowEvent(paintEvent);
  2127. #endif
  2128. if (!mGeckoChild)
  2129. return;
  2130. paintEvent.renderingContext = nsnull;
  2131. paintEvent.region = nsnull;
  2132. targetContext = nsnull;
  2133. targetSurface = nsnull;
  2134. // note that the cairo surface *MUST* be destroyed at this point,
  2135. // or bad things will happen (since we can't keep the cgContext around
  2136. // beyond this drawRect message handler)
  2137. #ifdef DEBUG_UPDATE
  2138. fprintf (stderr, " window coords: [%d %d %d %d]\n", fullRect.x, fullRect.y, fullRect.width, fullRect.height);
  2139. fprintf (stderr, "---- update done ----\n");
  2140. #if 0
  2141. CGContextSetRGBStrokeColor (cgContext,
  2142. ((((unsigned long)self) & 0xff)) / 255.0,
  2143. ((((unsigned long)self) & 0xff00) >> 8) / 255.0,
  2144. ((((unsigned long)self) & 0xff0000) >> 16) / 255.0,
  2145. 0.5);
  2146. #endif
  2147. CGContextSetRGBStrokeColor (cgContext, 1, 0, 0, 0.8);
  2148. CGContextSetLineWidth (cgContext, 4.0);
  2149. CGContextStrokeRect (cgContext,
  2150. CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height));
  2151. #endif
  2152. NS_OBJC_END_TRY_ABORT_BLOCK;
  2153. }
  2154. // Allows us to turn off setting up the clip region
  2155. // before each drawRect. We already clip within gecko.
  2156. - (BOOL)wantsDefaultClipping
  2157. {
  2158. return NO;
  2159. }
  2160. #if USE_CLICK_HOLD_CONTEXTMENU
  2161. //
  2162. // -clickHoldCallback:
  2163. //
  2164. // called from a timer two seconds after a mouse down to see if we should display
  2165. // a context menu (click-hold). |anEvent| is the original mouseDown event. If we're
  2166. // still in that mouseDown by this time, put up the context menu, otherwise just
  2167. // fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback
  2168. // fires so we're ok there.
  2169. //
  2170. // This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314)
  2171. // so removing it until we get it straightened out.
  2172. //
  2173. - (void)clickHoldCallback:(id)theEvent;
  2174. {
  2175. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2176. if( theEvent == [NSApp currentEvent] ) {
  2177. // we're still in the middle of the same mousedown event here, activate
  2178. // click-hold context menu by triggering the right mouseDown action.
  2179. NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown
  2180. location:[theEvent locationInWindow]
  2181. modifierFlags:[theEvent modifierFlags]
  2182. timestamp:[theEvent timestamp]
  2183. windowNumber:[theEvent windowNumber]
  2184. context:[theEvent context]
  2185. eventNumber:[theEvent eventNumber]
  2186. clickCount:[theEvent clickCount]
  2187. pressure:[theEvent pressure]];
  2188. [self rightMouseDown:clickHoldEvent];
  2189. }
  2190. NS_OBJC_END_TRY_ABORT_BLOCK;
  2191. }
  2192. #endif
  2193. // We sometimes need to reroute events when there is a rollup widget and the
  2194. // event isn't targeted at it.
  2195. //
  2196. // Rerouting may be needed when the user tries to navigate a context menu while
  2197. // keeping the mouse-button down (left or right mouse button) -- the OS thinks this
  2198. // is a dragging operation, so it sends events (mouseMoved and mouseUp) to the
  2199. // window where the dragging operation started (the parent of the context
  2200. // menu window). It also works around a bizarre Apple bug - if (while a context
  2201. // menu is open) you move the mouse over another app's window and then back over
  2202. // the context menu, mouseMoved events will be sent to the window underneath the
  2203. // context menu.
  2204. - (BOOL)ensureCorrectMouseEventTarget:(NSEvent*)anEvent
  2205. {
  2206. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  2207. // If there is no rollup widget we assume the OS routed the event correctly.
  2208. if (!gRollupWidget)
  2209. return YES;
  2210. // If this is the rollup widget and the event is not a mouse move then trust the OS routing.
  2211. // The reason for this trust is complicated.
  2212. //
  2213. // There are three types of mouse events that can legitimately need to be targeted at a window
  2214. // that they are not over. Mouse moves, mouse drags, and mouse ups. Anything else our app wouldn't
  2215. // handle (if the mouse was not over any window) or it would go to the appropriate window.
  2216. //
  2217. // We need to do manual event rerouting for mouse moves because we know that in some cases, like
  2218. // when there is a submenu opened from a popup window, the OS will route mouse move events to the
  2219. // submenu even if the mouse is over the parent. Mouse move events are never tied to a particular
  2220. // window because of some originating action like the starting point of a drag for drag events or
  2221. // a mouse down event for mouse up events, so it is always safe to do our own routing on them here.
  2222. //
  2223. // As for mouse drags and mouse ups, they have originating actions that tie them to windows they
  2224. // may no longer be over. If there is a rollup window present when one of these events is getting
  2225. // processed but we are not it, we are probably the window where the action originated, and that
  2226. // action must have caused the rollup window to come into existence. In that case, we might need
  2227. // to reroute the event if it is over the rollup window. That is why if we're not the rollup window
  2228. // we don't return YES here.
  2229. NSWindow* rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
  2230. if (mWindow == rollupWindow && [anEvent type] != NSMouseMoved)
  2231. return YES;
  2232. // Find the window that the event is over.
  2233. NSWindow* targetWindow = nsCocoaUtils::FindWindowUnderPoint(nsCocoaUtils::ScreenLocationForEvent(anEvent));
  2234. // If the event was not over any window, send it to the rollup window.
  2235. if (!targetWindow)
  2236. targetWindow = rollupWindow;
  2237. // At this point we've resolved a target window, if we are it then just return
  2238. // yes so we handle it. No need to redirect.
  2239. if (targetWindow == mWindow)
  2240. return YES;
  2241. // Send the event to its new destination.
  2242. NSPoint newWindowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, targetWindow);
  2243. NSEvent *newEvent = [NSEvent mouseEventWithType:[anEvent type]
  2244. location:newWindowLocation
  2245. modifierFlags:[anEvent modifierFlags]
  2246. timestamp:GetCurrentEventTime()
  2247. windowNumber:[targetWindow windowNumber]
  2248. context:nil
  2249. eventNumber:0
  2250. clickCount:1
  2251. pressure:0.0];
  2252. [targetWindow sendEvent:newEvent];
  2253. // Return NO because we just sent the event somewhere else.
  2254. return NO;
  2255. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  2256. }
  2257. // If we've just created a non-native context menu, we need to mark it as
  2258. // such and let the OS (and other programs) know when it opens and closes
  2259. // (this is how the OS knows to close other programs' context menus when
  2260. // ours open). We send the initial notification here, but others are sent
  2261. // in nsCocoaWindow::Show().
  2262. - (void)maybeInitContextMenuTracking
  2263. {
  2264. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2265. if (!gRollupWidget)
  2266. return;
  2267. nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
  2268. if (prefs) {
  2269. PRBool useNativeContextMenus;
  2270. nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
  2271. if (NS_SUCCEEDED(rv) && useNativeContextMenus)
  2272. return;
  2273. }
  2274. NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
  2275. if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
  2276. return;
  2277. [[NSDistributedNotificationCenter defaultCenter]
  2278. postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
  2279. object:@"org.mozilla.gecko.PopupWindow"];
  2280. [(PopupWindow*)popupWindow setIsContextMenu:YES];
  2281. NS_OBJC_END_TRY_ABORT_BLOCK;
  2282. }
  2283. - (BOOL)maybeRollup:(NSEvent*)theEvent
  2284. {
  2285. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  2286. PRBool retVal = PR_FALSE;
  2287. if (gRollupWidget && gRollupListener) {
  2288. NSWindow* currentPopup = static_cast<NSWindow*>(gRollupWidget->GetNativeData(NS_NATIVE_WINDOW));
  2289. if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
  2290. PRBool rollup = PR_TRUE;
  2291. if ([theEvent type] == NSScrollWheel) {
  2292. gRollupListener->ShouldRollupOnMouseWheelEvent(&rollup);
  2293. // We don't want the event passed on for scrollwheel events if we're
  2294. // not supposed to close the popup. Otherwise the background window
  2295. // will scroll when a custom context menu or the autoscroll popup is
  2296. // open (and the mouse isn't over the popup) -- which doesn't seem right.
  2297. // This change resolves bmo bug 344367.
  2298. retVal = PR_TRUE;
  2299. }
  2300. // if we're dealing with menus, we probably have submenus and
  2301. // we don't want to rollup if the click is in a parent menu of
  2302. // the current submenu
  2303. nsCOMPtr<nsIMenuRollup> menuRollup;
  2304. menuRollup = (do_QueryInterface(gRollupListener));
  2305. if (menuRollup) {
  2306. nsAutoTArray<nsIWidget*, 5> widgetChain;
  2307. menuRollup->GetSubmenuWidgetChain(&widgetChain);
  2308. for (PRUint32 i = 0; i < widgetChain.Length(); i++) {
  2309. nsIWidget* widget = widgetChain[i];
  2310. NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
  2311. if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
  2312. rollup = PR_FALSE;
  2313. break;
  2314. }
  2315. } // foreach parent menu widget
  2316. } // if rollup listener knows about menus
  2317. // if we've determined that we should still rollup, do it.
  2318. if (rollup) {
  2319. gRollupListener->Rollup(nsnull);
  2320. retVal = PR_TRUE;
  2321. }
  2322. }
  2323. }
  2324. return retVal;
  2325. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  2326. }
  2327. - (void)mouseDown:(NSEvent*)theEvent
  2328. {
  2329. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2330. // If we've already seen this event due to direct dispatch from menuForEvent:
  2331. // just bail; if not, remember it.
  2332. if (mLastMouseDownEvent == theEvent) {
  2333. [mLastMouseDownEvent release];
  2334. mLastMouseDownEvent = nil;
  2335. return;
  2336. }
  2337. else {
  2338. [mLastMouseDownEvent release];
  2339. mLastMouseDownEvent = [theEvent retain];
  2340. }
  2341. if (![self ensureCorrectMouseEventTarget:theEvent])
  2342. return;
  2343. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2344. if ([self maybeRollup:theEvent])
  2345. return;
  2346. unsigned int modifierFlags = [theEvent modifierFlags];
  2347. // if the command and alt keys are held down, initiate hand scrolling
  2348. if ([ChildView areHandScrollModifiers:modifierFlags]) {
  2349. [self startHandScroll:theEvent];
  2350. // needed to change the focus, among other things, since we don't
  2351. // get to do that below.
  2352. [super mouseDown:theEvent];
  2353. return; // do not pass this mousedown event to gecko
  2354. }
  2355. #if USE_CLICK_HOLD_CONTEXTMENU
  2356. // fire off timer to check for click-hold after two seconds. retains |theEvent|
  2357. [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
  2358. #endif
  2359. // in order to send gecko events we'll need a gecko widget
  2360. if (!mGeckoChild)
  2361. return;
  2362. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
  2363. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2364. geckoEvent.clickCount = [theEvent clickCount];
  2365. if (modifierFlags & NSControlKeyMask)
  2366. geckoEvent.button = nsMouseEvent::eRightButton;
  2367. else
  2368. geckoEvent.button = nsMouseEvent::eLeftButton;
  2369. // create native EventRecord for use by plugins
  2370. EventRecord macEvent;
  2371. macEvent.what = mouseDown;
  2372. macEvent.message = 0;
  2373. macEvent.when = ::TickCount();
  2374. ::GetGlobalMouse(&macEvent.where);
  2375. macEvent.modifiers = ::GetCurrentEventKeyModifiers();
  2376. geckoEvent.nativeMsg = &macEvent;
  2377. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2378. // XXX maybe call markedTextSelectionChanged:client: here?
  2379. NS_OBJC_END_TRY_ABORT_BLOCK;
  2380. }
  2381. - (void)mouseUp:(NSEvent *)theEvent
  2382. {
  2383. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2384. if (mInHandScroll) {
  2385. [self updateHandScroll:theEvent];
  2386. [self stopHandScroll:theEvent];
  2387. return;
  2388. }
  2389. if (![self ensureCorrectMouseEventTarget:theEvent])
  2390. return;
  2391. if (!mGeckoChild)
  2392. return;
  2393. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
  2394. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2395. if ([theEvent modifierFlags] & NSControlKeyMask)
  2396. geckoEvent.button = nsMouseEvent::eRightButton;
  2397. else
  2398. geckoEvent.button = nsMouseEvent::eLeftButton;
  2399. // create native EventRecord for use by plugins
  2400. EventRecord macEvent;
  2401. macEvent.what = mouseUp;
  2402. macEvent.message = 0;
  2403. macEvent.when = ::TickCount();
  2404. ::GetGlobalMouse(&macEvent.where);
  2405. macEvent.modifiers = ::GetCurrentEventKeyModifiers();
  2406. geckoEvent.nativeMsg = &macEvent;
  2407. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2408. NS_OBJC_END_TRY_ABORT_BLOCK;
  2409. }
  2410. // sends a mouse enter or exit event into gecko
  2411. static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted,
  2412. PRUint32 msg,
  2413. nsIWidget *widget,
  2414. nsMouseEvent::reasonType aReason,
  2415. NSPoint* localEventLocation,
  2416. nsMouseEvent::exitType type)
  2417. {
  2418. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  2419. if (!widget || !localEventLocation)
  2420. return nsEventStatus_eIgnore;
  2421. nsMouseEvent event(isTrusted, msg, widget, aReason);
  2422. event.refPoint.x = nscoord((PRInt32)localEventLocation->x);
  2423. event.refPoint.y = nscoord((PRInt32)localEventLocation->y);
  2424. EventRecord macEvent;
  2425. macEvent.what = adjustCursorEvent;
  2426. macEvent.message = 0;
  2427. macEvent.when = ::TickCount();
  2428. ::GetGlobalMouse(&macEvent.where);
  2429. macEvent.modifiers = ::GetCurrentEventKeyModifiers();
  2430. event.nativeMsg = &macEvent;
  2431. event.exit = type;
  2432. nsEventStatus status;
  2433. widget->DispatchEvent(&event, status);
  2434. // After the cursor exits a view set it to a visible regular arrow cursor.
  2435. // This lets us recover from plugins that mess with it.
  2436. if (msg == NS_MOUSE_EXIT) {
  2437. [NSCursor unhide];
  2438. [[NSCursor arrowCursor] set];
  2439. }
  2440. return status;
  2441. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore);
  2442. }
  2443. - (void)mouseMoved:(NSEvent*)theEvent
  2444. {
  2445. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2446. // Work around an Apple bug that causes the OS to continue sending
  2447. // mouseMoved events to a window for a while after it's been miniaturized.
  2448. // This may be related to a similar problem with popup windows (bmo bug
  2449. // 378645, popup windows continue to receive mouseMoved events after having
  2450. // been "ordered out"), which is worked around in nsCocoaWindow::Show()
  2451. // (search on 378645 in nsCocoaWindow.mm). This problem is bmo bug 410219,
  2452. // and exists in both OS X 10.4 and 10.5.
  2453. if ([[self window] isMiniaturized])
  2454. return;
  2455. NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(theEvent, mWindow);
  2456. NSPoint viewEventLocation = [self convertPoint:windowEventLocation fromView:nil];
  2457. // Installing a mouseMoved handler on the EventMonitor target (in
  2458. // nsToolkit::RegisterForAllProcessMouseEvents()) means that some of the
  2459. // events received here come from other processes. For this reason we need
  2460. // to avoid processing them unless they're over a context menu -- otherwise
  2461. // tooltips and other mouse-hover effects will "work" even when our app
  2462. // doesn't have the focus.
  2463. BOOL mouseEventIsOverRollupWidget = NO;
  2464. if (gRollupWidget) {
  2465. NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
  2466. mouseEventIsOverRollupWidget = nsCocoaUtils::IsEventOverWindow(theEvent, popupWindow);
  2467. }
  2468. if (![NSApp isActive] && !mouseEventIsOverRollupWidget) {
  2469. if (sLastViewEntered) {
  2470. nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
  2471. NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
  2472. SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
  2473. &exitEventLocation, nsMouseEvent::eTopLevel);
  2474. sLastViewEntered = nil;
  2475. }
  2476. return;
  2477. }
  2478. if (![self ensureCorrectMouseEventTarget:theEvent])
  2479. return;
  2480. NSView* view = [[mWindow contentView] hitTest:windowEventLocation];
  2481. if (view) {
  2482. // we shouldn't handle this if the hit view is not us
  2483. if (view != (NSView*)self) {
  2484. [view mouseMoved:theEvent];
  2485. return;
  2486. }
  2487. }
  2488. else {
  2489. // If the hit test returned nil then the mouse isn't over the window. If thse mouse
  2490. // exited the window then send mouse exit to the last view in the window it was over.
  2491. if (sLastViewEntered) {
  2492. NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
  2493. // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
  2494. nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
  2495. SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
  2496. &exitEventLocation, nsMouseEvent::eTopLevel);
  2497. sLastViewEntered = nil;
  2498. }
  2499. return;
  2500. }
  2501. // At this point we are supposed to handle this event. If we were not the last view entered, then
  2502. // we should send an exit event to the last view entered and an enter event to ourselves.
  2503. if (!mGeckoChild)
  2504. return;
  2505. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2506. if (sLastViewEntered != self) {
  2507. if (sLastViewEntered) {
  2508. NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
  2509. // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
  2510. nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
  2511. SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
  2512. &exitEventLocation, nsMouseEvent::eChild);
  2513. // The mouse exit event we just sent may have destroyed this widget, bail if that happened.
  2514. if (!mGeckoChild)
  2515. return;
  2516. }
  2517. // NSLog(@"sending NS_MOUSE_ENTER event with point %f,%f\n", viewEventLocation.x, viewEventLocation.y);
  2518. SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_ENTER, mGeckoChild, nsMouseEvent::eReal,
  2519. &viewEventLocation, nsMouseEvent::eChild);
  2520. // The mouse enter event we just sent may have destroyed this widget, bail if that happened.
  2521. if (!mGeckoChild)
  2522. return;
  2523. // mark this view as the last view entered
  2524. sLastViewEntered = (NSView*)self;
  2525. // checks to see if we should change to the hand cursor
  2526. [self setHandScrollCursor:theEvent];
  2527. }
  2528. // check if we are in a hand scroll or if the user
  2529. // has command and alt held down; if so, we do not want
  2530. // gecko messing with the cursor.
  2531. if ([ChildView areHandScrollModifiers:[theEvent modifierFlags]])
  2532. return;
  2533. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
  2534. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2535. // create native EventRecord for use by plugins
  2536. EventRecord macEvent;
  2537. macEvent.what = adjustCursorEvent;
  2538. macEvent.message = 0;
  2539. macEvent.when = ::TickCount();
  2540. ::GetGlobalMouse(&macEvent.where);
  2541. macEvent.modifiers = ::GetCurrentEventKeyModifiers();
  2542. geckoEvent.nativeMsg = &macEvent;
  2543. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2544. NS_OBJC_END_TRY_ABORT_BLOCK;
  2545. }
  2546. - (void)mouseDragged:(NSEvent*)theEvent
  2547. {
  2548. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2549. if (![self ensureCorrectMouseEventTarget:theEvent])
  2550. return;
  2551. if (!mGeckoChild)
  2552. return;
  2553. // if the handscroll flag is set, steal this event
  2554. if (mInHandScroll) {
  2555. [self updateHandScroll:theEvent];
  2556. return;
  2557. }
  2558. gLastDragView = self;
  2559. gLastDragEvent = theEvent;
  2560. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
  2561. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2562. // create native EventRecord for use by plugins
  2563. EventRecord macEvent;
  2564. macEvent.what = nullEvent;
  2565. macEvent.message = 0;
  2566. macEvent.when = ::TickCount();
  2567. ::GetGlobalMouse(&macEvent.where);
  2568. macEvent.modifiers = btnState | ::GetCurrentEventKeyModifiers();
  2569. geckoEvent.nativeMsg = &macEvent;
  2570. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2571. // Note, sending the above event might have destroyed our widget since we didn't retain.
  2572. // Fine so long as we don't access any local variables from here on.
  2573. gLastDragView = nil;
  2574. gLastDragEvent = nil;
  2575. // XXX maybe call markedTextSelectionChanged:client: here?
  2576. NS_OBJC_END_TRY_ABORT_BLOCK;
  2577. }
  2578. - (void)rightMouseDown:(NSEvent *)theEvent
  2579. {
  2580. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2581. if (![self ensureCorrectMouseEventTarget:theEvent])
  2582. return;
  2583. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2584. [self maybeRollup:theEvent];
  2585. if (!mGeckoChild)
  2586. return;
  2587. // The right mouse went down, fire off a right mouse down event to gecko
  2588. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
  2589. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2590. geckoEvent.button = nsMouseEvent::eRightButton;
  2591. geckoEvent.clickCount = [theEvent clickCount];
  2592. // create native EventRecord for use by plugins
  2593. EventRecord macEvent;
  2594. macEvent.what = mouseDown;
  2595. macEvent.message = 0;
  2596. macEvent.when = ::TickCount();
  2597. ::GetGlobalMouse(&macEvent.where);
  2598. macEvent.modifiers = controlKey; // fake a context menu click
  2599. geckoEvent.nativeMsg = &macEvent;
  2600. PRBool handled = mGeckoChild->DispatchMouseEvent(geckoEvent);
  2601. if (!mGeckoChild)
  2602. return;
  2603. if (!handled)
  2604. [super rightMouseDown:theEvent]; // let the superview do context menu stuff
  2605. NS_OBJC_END_TRY_ABORT_BLOCK;
  2606. }
  2607. - (void)rightMouseUp:(NSEvent *)theEvent
  2608. {
  2609. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2610. if (![self ensureCorrectMouseEventTarget:theEvent])
  2611. return;
  2612. if (!mGeckoChild)
  2613. return;
  2614. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
  2615. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2616. geckoEvent.button = nsMouseEvent::eRightButton;
  2617. geckoEvent.clickCount = [theEvent clickCount];
  2618. // create native EventRecord for use by plugins
  2619. EventRecord macEvent;
  2620. macEvent.what = mouseUp;
  2621. macEvent.message = 0;
  2622. macEvent.when = ::TickCount();
  2623. ::GetGlobalMouse(&macEvent.where);
  2624. macEvent.modifiers = controlKey; // fake a context menu click
  2625. geckoEvent.nativeMsg = &macEvent;
  2626. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2627. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2628. NS_OBJC_END_TRY_ABORT_BLOCK;
  2629. }
  2630. - (void)rightMouseDragged:(NSEvent*)theEvent
  2631. {
  2632. if (![self ensureCorrectMouseEventTarget:theEvent])
  2633. return;
  2634. if (!mGeckoChild)
  2635. return;
  2636. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
  2637. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2638. geckoEvent.button = nsMouseEvent::eRightButton;
  2639. // send event into Gecko by going directly to the
  2640. // the widget.
  2641. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2642. }
  2643. - (void)otherMouseDown:(NSEvent *)theEvent
  2644. {
  2645. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2646. if (![self ensureCorrectMouseEventTarget:theEvent])
  2647. return;
  2648. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2649. if ([self maybeRollup:theEvent])
  2650. return;
  2651. if (!mGeckoChild)
  2652. return;
  2653. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
  2654. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2655. geckoEvent.button = nsMouseEvent::eMiddleButton;
  2656. geckoEvent.clickCount = [theEvent clickCount];
  2657. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2658. NS_OBJC_END_TRY_ABORT_BLOCK;
  2659. }
  2660. - (void)otherMouseUp:(NSEvent *)theEvent
  2661. {
  2662. if (!mGeckoChild)
  2663. return;
  2664. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
  2665. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2666. geckoEvent.button = nsMouseEvent::eMiddleButton;
  2667. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2668. }
  2669. - (void)otherMouseDragged:(NSEvent*)theEvent
  2670. {
  2671. if (!mGeckoChild)
  2672. return;
  2673. nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
  2674. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2675. geckoEvent.button = nsMouseEvent::eMiddleButton;
  2676. // send event into Gecko by going directly to the
  2677. // the widget.
  2678. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2679. }
  2680. // Handle an NSScrollWheel event for a single axis only.
  2681. -(void)scrollWheel:(NSEvent*)theEvent forAxis:(enum nsMouseScrollEvent::nsMouseScrollFlags)inAxis
  2682. {
  2683. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2684. if (!mGeckoChild)
  2685. return;
  2686. float scrollDelta;
  2687. if (inAxis & nsMouseScrollEvent::kIsVertical)
  2688. scrollDelta = -[theEvent deltaY];
  2689. else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
  2690. scrollDelta = -[theEvent deltaX];
  2691. else
  2692. return; // caller screwed up
  2693. if (scrollDelta == 0)
  2694. // No sense in firing off a Gecko event. Note that as of 10.4 Tiger,
  2695. // a single NSScrollWheel event might result in deltaX = deltaY = 0.
  2696. return;
  2697. nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
  2698. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2699. geckoEvent.scrollFlags |= inAxis;
  2700. // Gecko only understands how to scroll by an integer value. Using floor
  2701. // and ceil is better than truncating the fraction, especially when
  2702. // |delta| < 1.
  2703. if (scrollDelta < 0)
  2704. geckoEvent.delta = (PRInt32)floorf(scrollDelta);
  2705. else
  2706. geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
  2707. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2708. mGeckoChild->DispatchWindowEvent(geckoEvent);
  2709. if (!mGeckoChild)
  2710. return;
  2711. // dispatch scroll wheel carbon event for plugins
  2712. {
  2713. EventRef theEvent;
  2714. OSStatus err = ::MacCreateEvent(NULL,
  2715. kEventClassMouse,
  2716. kEventMouseWheelMoved,
  2717. TicksToEventTime(TickCount()),
  2718. kEventAttributeUserEvent,
  2719. &theEvent);
  2720. if (err == noErr) {
  2721. EventMouseWheelAxis axis;
  2722. if (inAxis & nsMouseScrollEvent::kIsVertical)
  2723. axis = kEventMouseWheelAxisY;
  2724. else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
  2725. axis = kEventMouseWheelAxisX;
  2726. SetEventParameter(theEvent,
  2727. kEventParamMouseWheelAxis,
  2728. typeMouseWheelAxis,
  2729. sizeof(EventMouseWheelAxis),
  2730. &axis);
  2731. SInt32 delta = (SInt32)-geckoEvent.delta;
  2732. SetEventParameter(theEvent,
  2733. kEventParamMouseWheelDelta,
  2734. typeLongInteger,
  2735. sizeof(SInt32),
  2736. &delta);
  2737. Point mouseLoc;
  2738. ::GetGlobalMouse(&mouseLoc);
  2739. SetEventParameter(theEvent,
  2740. kEventParamMouseLocation,
  2741. typeQDPoint,
  2742. sizeof(Point),
  2743. &mouseLoc);
  2744. ::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
  2745. ReleaseEvent(theEvent);
  2746. }
  2747. }
  2748. NS_OBJC_END_TRY_ABORT_BLOCK;
  2749. }
  2750. -(void)scrollWheel:(NSEvent*)theEvent
  2751. {
  2752. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2753. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2754. if ([self maybeRollup:theEvent])
  2755. return;
  2756. if (!mGeckoChild)
  2757. return;
  2758. // It's possible for a single NSScrollWheel event to carry both useful
  2759. // deltaX and deltaY, for example, when the "wheel" is a trackpad.
  2760. // NSMouseScrollEvent can only carry one axis at a time, so the system
  2761. // event will be split into two Gecko events if necessary.
  2762. [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsVertical];
  2763. if (!mGeckoChild)
  2764. return;
  2765. [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsHorizontal];
  2766. NS_OBJC_END_TRY_ABORT_BLOCK;
  2767. }
  2768. -(NSMenu*)menuForEvent:(NSEvent*)theEvent
  2769. {
  2770. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  2771. if (!mGeckoChild || [self isPluginView])
  2772. return nil;
  2773. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  2774. [self maybeRollup:theEvent];
  2775. if (!mGeckoChild)
  2776. return nil;
  2777. // Cocoa doesn't always dispatch a mouseDown: for a control-click event,
  2778. // depends on what we return from menuForEvent:. Gecko always expects one
  2779. // and expects the mouse down event before the context menu event, so
  2780. // get that event sent first if this is a left mouse click.
  2781. if ([theEvent type] == NSLeftMouseDown) {
  2782. [self mouseDown:theEvent];
  2783. if (!mGeckoChild)
  2784. return nil;
  2785. }
  2786. nsMouseEvent geckoEvent(PR_TRUE, NS_CONTEXTMENU, nsnull, nsMouseEvent::eReal);
  2787. [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
  2788. geckoEvent.button = nsMouseEvent::eRightButton;
  2789. mGeckoChild->DispatchMouseEvent(geckoEvent);
  2790. if (!mGeckoChild)
  2791. return nil;
  2792. [self maybeInitContextMenuTracking];
  2793. // Go up our view chain to fetch the correct menu to return.
  2794. return [self contextMenu];
  2795. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  2796. }
  2797. - (NSMenu*)contextMenu
  2798. {
  2799. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  2800. NSView* superView = [self superview];
  2801. if ([superView respondsToSelector:@selector(contextMenu)])
  2802. return [(NSView<mozView>*)superView contextMenu];
  2803. return nil;
  2804. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  2805. }
  2806. - (TopLevelWindowData*)ensureWindowData
  2807. {
  2808. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  2809. WindowDataMap* windowMap = [WindowDataMap sharedWindowDataMap];
  2810. TopLevelWindowData* windowData = [windowMap dataForWindow:mWindow];
  2811. if (mWindow && !windowData)
  2812. {
  2813. windowData = [[TopLevelWindowData alloc] initWithWindow:mWindow];
  2814. [windowMap setData:windowData forWindow:mWindow]; // takes ownership
  2815. [windowData release];
  2816. }
  2817. return windowData;
  2818. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  2819. }
  2820. static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar)
  2821. {
  2822. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  2823. UnicodeToTextInfo converterInfo;
  2824. TextEncoding systemEncoding;
  2825. Str255 convertedString;
  2826. OSStatus err;
  2827. *outChar = 0;
  2828. err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
  2829. if (err != noErr)
  2830. return PR_FALSE;
  2831. err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
  2832. if (err != noErr)
  2833. return PR_FALSE;
  2834. err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
  2835. if (err != noErr)
  2836. return PR_FALSE;
  2837. *outChar = convertedString[1];
  2838. ::DisposeUnicodeToTextInfo(&converterInfo);
  2839. return PR_TRUE;
  2840. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
  2841. }
  2842. static void ConvertCocoaKeyEventToMacEvent(NSEvent* cocoaEvent, EventRecord& macEvent, PRUint32 keyType = 0)
  2843. {
  2844. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2845. UInt32 charCode = 0;
  2846. if ([cocoaEvent type] == NSFlagsChanged) {
  2847. macEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp;
  2848. } else {
  2849. if ([[cocoaEvent characters] length] > 0)
  2850. charCode = [[cocoaEvent characters] characterAtIndex:0];
  2851. if ([cocoaEvent type] == NSKeyDown)
  2852. macEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
  2853. else
  2854. macEvent.what = keyUp;
  2855. }
  2856. if (charCode >= 0x0080) {
  2857. switch (charCode) {
  2858. case NSUpArrowFunctionKey:
  2859. charCode = kUpArrowCharCode;
  2860. break;
  2861. case NSDownArrowFunctionKey:
  2862. charCode = kDownArrowCharCode;
  2863. break;
  2864. case NSLeftArrowFunctionKey:
  2865. charCode = kLeftArrowCharCode;
  2866. break;
  2867. case NSRightArrowFunctionKey:
  2868. charCode = kRightArrowCharCode;
  2869. break;
  2870. default:
  2871. unsigned char convertedCharCode;
  2872. if (ConvertUnicodeToCharCode(charCode, &convertedCharCode))
  2873. charCode = convertedCharCode;
  2874. //NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]);
  2875. break;
  2876. }
  2877. }
  2878. macEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8);
  2879. macEvent.when = ::TickCount();
  2880. ::GetGlobalMouse(&macEvent.where);
  2881. macEvent.modifiers = ::GetCurrentEventKeyModifiers();
  2882. NS_OBJC_END_TRY_ABORT_BLOCK;
  2883. }
  2884. // Key code constants
  2885. enum
  2886. {
  2887. kEscapeKeyCode = 0x35,
  2888. kRCommandKeyCode = 0x36, // right command key
  2889. kCommandKeyCode = 0x37,
  2890. kShiftKeyCode = 0x38,
  2891. kCapsLockKeyCode = 0x39,
  2892. kOptionkeyCode = 0x3A,
  2893. kControlKeyCode = 0x3B,
  2894. kRShiftKeyCode = 0x3C, // right shift key
  2895. kROptionKeyCode = 0x3D, // right option key
  2896. kRControlKeyCode = 0x3E, // right control key
  2897. kClearKeyCode = 0x47,
  2898. // function keys
  2899. kF1KeyCode = 0x7A,
  2900. kF2KeyCode = 0x78,
  2901. kF3KeyCode = 0x63,
  2902. kF4KeyCode = 0x76,
  2903. kF5KeyCode = 0x60,
  2904. kF6KeyCode = 0x61,
  2905. kF7KeyCode = 0x62,
  2906. kF8KeyCode = 0x64,
  2907. kF9KeyCode = 0x65,
  2908. kF10KeyCode = 0x6D,
  2909. kF11KeyCode = 0x67,
  2910. kF12KeyCode = 0x6F,
  2911. kF13KeyCode = 0x69,
  2912. kF14KeyCode = 0x6B,
  2913. kF15KeyCode = 0x71,
  2914. kPrintScreenKeyCode = kF13KeyCode,
  2915. kScrollLockKeyCode = kF14KeyCode,
  2916. kPauseKeyCode = kF15KeyCode,
  2917. // keypad
  2918. kKeypad0KeyCode = 0x52,
  2919. kKeypad1KeyCode = 0x53,
  2920. kKeypad2KeyCode = 0x54,
  2921. kKeypad3KeyCode = 0x55,
  2922. kKeypad4KeyCode = 0x56,
  2923. kKeypad5KeyCode = 0x57,
  2924. kKeypad6KeyCode = 0x58,
  2925. kKeypad7KeyCode = 0x59,
  2926. kKeypad8KeyCode = 0x5B,
  2927. kKeypad9KeyCode = 0x5C,
  2928. // The following key codes are not defined until Mac OS X 10.5
  2929. #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
  2930. kVK_ANSI_1 = 0x12,
  2931. kVK_ANSI_2 = 0x13,
  2932. kVK_ANSI_3 = 0x14,
  2933. kVK_ANSI_4 = 0x15,
  2934. kVK_ANSI_5 = 0x17,
  2935. kVK_ANSI_6 = 0x16,
  2936. kVK_ANSI_7 = 0x1A,
  2937. kVK_ANSI_8 = 0x1C,
  2938. kVK_ANSI_9 = 0x19,
  2939. kVK_ANSI_0 = 0x1D,
  2940. #endif
  2941. kKeypadMultiplyKeyCode = 0x43,
  2942. kKeypadAddKeyCode = 0x45,
  2943. kKeypadSubtractKeyCode = 0x4E,
  2944. kKeypadDecimalKeyCode = 0x41,
  2945. kKeypadDivideKeyCode = 0x4B,
  2946. kKeypadEqualsKeyCode = 0x51, // no correpsonding gecko key code
  2947. kEnterKeyCode = 0x4C,
  2948. kReturnKeyCode = 0x24,
  2949. kPowerbookEnterKeyCode = 0x34, // Enter on Powerbook's keyboard is different
  2950. kInsertKeyCode = 0x72, // also help key
  2951. kDeleteKeyCode = 0x75, // also forward delete key
  2952. kTabKeyCode = 0x30,
  2953. kTildeKeyCode = 0x32,
  2954. kBackspaceKeyCode = 0x33,
  2955. kHomeKeyCode = 0x73,
  2956. kEndKeyCode = 0x77,
  2957. kPageUpKeyCode = 0x74,
  2958. kPageDownKeyCode = 0x79,
  2959. kLeftArrowKeyCode = 0x7B,
  2960. kRightArrowKeyCode = 0x7C,
  2961. kUpArrowKeyCode = 0x7E,
  2962. kDownArrowKeyCode = 0x7D
  2963. };
  2964. static PRBool IsPrintableChar(PRUnichar aChar)
  2965. {
  2966. return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
  2967. }
  2968. static PRUint32 GetGeckoKeyCodeFromChar(PRUnichar aChar)
  2969. {
  2970. // We don't support the key code for non-ASCII characters
  2971. if (aChar > 0x7E)
  2972. return 0;
  2973. if (aChar >= 'a' && aChar <= 'z') // lowercase
  2974. return PRUint32(toupper(aChar));
  2975. else if (aChar >= 'A' && aChar <= 'Z') // uppercase
  2976. return PRUint32(aChar);
  2977. else if (aChar >= '0' && aChar <= '9')
  2978. return PRUint32(aChar - '0' + NS_VK_0);
  2979. switch (aChar)
  2980. {
  2981. case kReturnCharCode:
  2982. case kEnterCharCode:
  2983. case '\n':
  2984. return NS_VK_RETURN;
  2985. case '{':
  2986. case '[':
  2987. return NS_VK_OPEN_BRACKET;
  2988. case '}':
  2989. case ']':
  2990. return NS_VK_CLOSE_BRACKET;
  2991. case '\'':
  2992. case '"':
  2993. return NS_VK_QUOTE;
  2994. case '\\': return NS_VK_BACK_SLASH;
  2995. case ' ': return NS_VK_SPACE;
  2996. case ';': return NS_VK_SEMICOLON;
  2997. case '=': return NS_VK_EQUALS;
  2998. case ',': return NS_VK_COMMA;
  2999. case '.': return NS_VK_PERIOD;
  3000. case '/': return NS_VK_SLASH;
  3001. case '`': return NS_VK_BACK_QUOTE;
  3002. case '\t': return NS_VK_TAB;
  3003. case '-': return NS_VK_SUBTRACT;
  3004. case '+': return NS_VK_ADD;
  3005. default:
  3006. if (!IsPrintableChar(aChar))
  3007. NS_WARNING("GetGeckoKeyCodeFromChar has failed.");
  3008. return 0;
  3009. }
  3010. }
  3011. static PRUint32 ConvertMacToGeckoKeyCode(UInt32 keyCode, nsKeyEvent* aKeyEvent, NSString* characters)
  3012. {
  3013. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3014. PRUint32 geckoKeyCode = 0;
  3015. PRUnichar charCode = 0;
  3016. if ([characters length])
  3017. charCode = [characters characterAtIndex:0];
  3018. switch (keyCode)
  3019. {
  3020. // modifiers. We don't get separate events for these
  3021. case kEscapeKeyCode: geckoKeyCode = NS_VK_ESCAPE; break;
  3022. case kRCommandKeyCode:
  3023. case kCommandKeyCode: geckoKeyCode = NS_VK_META; break;
  3024. case kRShiftKeyCode:
  3025. case kShiftKeyCode: geckoKeyCode = NS_VK_SHIFT; break;
  3026. case kCapsLockKeyCode: geckoKeyCode = NS_VK_CAPS_LOCK; break;
  3027. case kRControlKeyCode:
  3028. case kControlKeyCode: geckoKeyCode = NS_VK_CONTROL; break;
  3029. case kROptionKeyCode:
  3030. case kOptionkeyCode: geckoKeyCode = NS_VK_ALT; break;
  3031. case kClearKeyCode: geckoKeyCode = NS_VK_CLEAR; break;
  3032. // function keys
  3033. case kF1KeyCode: geckoKeyCode = NS_VK_F1; break;
  3034. case kF2KeyCode: geckoKeyCode = NS_VK_F2; break;
  3035. case kF3KeyCode: geckoKeyCode = NS_VK_F3; break;
  3036. case kF4KeyCode: geckoKeyCode = NS_VK_F4; break;
  3037. case kF5KeyCode: geckoKeyCode = NS_VK_F5; break;
  3038. case kF6KeyCode: geckoKeyCode = NS_VK_F6; break;
  3039. case kF7KeyCode: geckoKeyCode = NS_VK_F7; break;
  3040. case kF8KeyCode: geckoKeyCode = NS_VK_F8; break;
  3041. case kF9KeyCode: geckoKeyCode = NS_VK_F9; break;
  3042. case kF10KeyCode: geckoKeyCode = NS_VK_F10; break;
  3043. case kF11KeyCode: geckoKeyCode = NS_VK_F11; break;
  3044. case kF12KeyCode: geckoKeyCode = NS_VK_F12; break;
  3045. // case kF13KeyCode: geckoKeyCode = NS_VK_F13; break; // clash with the 3 below
  3046. // case kF14KeyCode: geckoKeyCode = NS_VK_F14; break;
  3047. // case kF15KeyCode: geckoKeyCode = NS_VK_F15; break;
  3048. case kPauseKeyCode: geckoKeyCode = NS_VK_PAUSE; break;
  3049. case kScrollLockKeyCode: geckoKeyCode = NS_VK_SCROLL_LOCK; break;
  3050. case kPrintScreenKeyCode: geckoKeyCode = NS_VK_PRINTSCREEN; break;
  3051. // keypad
  3052. case kKeypad0KeyCode: geckoKeyCode = NS_VK_NUMPAD0; break;
  3053. case kKeypad1KeyCode: geckoKeyCode = NS_VK_NUMPAD1; break;
  3054. case kKeypad2KeyCode: geckoKeyCode = NS_VK_NUMPAD2; break;
  3055. case kKeypad3KeyCode: geckoKeyCode = NS_VK_NUMPAD3; break;
  3056. case kKeypad4KeyCode: geckoKeyCode = NS_VK_NUMPAD4; break;
  3057. case kKeypad5KeyCode: geckoKeyCode = NS_VK_NUMPAD5; break;
  3058. case kKeypad6KeyCode: geckoKeyCode = NS_VK_NUMPAD6; break;
  3059. case kKeypad7KeyCode: geckoKeyCode = NS_VK_NUMPAD7; break;
  3060. case kKeypad8KeyCode: geckoKeyCode = NS_VK_NUMPAD8; break;
  3061. case kKeypad9KeyCode: geckoKeyCode = NS_VK_NUMPAD9; break;
  3062. case kKeypadMultiplyKeyCode: geckoKeyCode = NS_VK_MULTIPLY; break;
  3063. case kKeypadAddKeyCode: geckoKeyCode = NS_VK_ADD; break;
  3064. case kKeypadSubtractKeyCode: geckoKeyCode = NS_VK_SUBTRACT; break;
  3065. case kKeypadDecimalKeyCode: geckoKeyCode = NS_VK_DECIMAL; break;
  3066. case kKeypadDivideKeyCode: geckoKeyCode = NS_VK_DIVIDE; break;
  3067. // these may clash with forward delete and help
  3068. case kInsertKeyCode: geckoKeyCode = NS_VK_INSERT; break;
  3069. case kDeleteKeyCode: geckoKeyCode = NS_VK_DELETE; break;
  3070. case kBackspaceKeyCode: geckoKeyCode = NS_VK_BACK; break;
  3071. case kTabKeyCode: geckoKeyCode = NS_VK_TAB; break;
  3072. case kHomeKeyCode: geckoKeyCode = NS_VK_HOME; break;
  3073. case kEndKeyCode: geckoKeyCode = NS_VK_END; break;
  3074. case kPageUpKeyCode: geckoKeyCode = NS_VK_PAGE_UP; break;
  3075. case kPageDownKeyCode: geckoKeyCode = NS_VK_PAGE_DOWN; break;
  3076. case kLeftArrowKeyCode: geckoKeyCode = NS_VK_LEFT; break;
  3077. case kRightArrowKeyCode: geckoKeyCode = NS_VK_RIGHT; break;
  3078. case kUpArrowKeyCode: geckoKeyCode = NS_VK_UP; break;
  3079. case kDownArrowKeyCode: geckoKeyCode = NS_VK_DOWN; break;
  3080. case kVK_ANSI_1: geckoKeyCode = NS_VK_1; break;
  3081. case kVK_ANSI_2: geckoKeyCode = NS_VK_2; break;
  3082. case kVK_ANSI_3: geckoKeyCode = NS_VK_3; break;
  3083. case kVK_ANSI_4: geckoKeyCode = NS_VK_4; break;
  3084. case kVK_ANSI_5: geckoKeyCode = NS_VK_5; break;
  3085. case kVK_ANSI_6: geckoKeyCode = NS_VK_6; break;
  3086. case kVK_ANSI_7: geckoKeyCode = NS_VK_7; break;
  3087. case kVK_ANSI_8: geckoKeyCode = NS_VK_8; break;
  3088. case kVK_ANSI_9: geckoKeyCode = NS_VK_9; break;
  3089. case kVK_ANSI_0: geckoKeyCode = NS_VK_0; break;
  3090. default:
  3091. // if we haven't gotten the key code already, look at the char code
  3092. geckoKeyCode = GetGeckoKeyCodeFromChar(charCode);
  3093. }
  3094. return geckoKeyCode;
  3095. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
  3096. }
  3097. static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
  3098. {
  3099. PRBool isSpecial;
  3100. // this table is used to determine which keys are special and should not generate a charCode
  3101. switch (macKeyCode)
  3102. {
  3103. // modifiers - we don't get separate events for these yet
  3104. case kEscapeKeyCode:
  3105. case kShiftKeyCode:
  3106. case kRShiftKeyCode:
  3107. case kCommandKeyCode:
  3108. case kRCommandKeyCode:
  3109. case kCapsLockKeyCode:
  3110. case kControlKeyCode:
  3111. case kRControlKeyCode:
  3112. case kOptionkeyCode:
  3113. case kROptionKeyCode:
  3114. case kClearKeyCode:
  3115. // function keys
  3116. case kF1KeyCode:
  3117. case kF2KeyCode:
  3118. case kF3KeyCode:
  3119. case kF4KeyCode:
  3120. case kF5KeyCode:
  3121. case kF6KeyCode:
  3122. case kF7KeyCode:
  3123. case kF8KeyCode:
  3124. case kF9KeyCode:
  3125. case kF10KeyCode:
  3126. case kF11KeyCode:
  3127. case kF12KeyCode:
  3128. case kPauseKeyCode:
  3129. case kScrollLockKeyCode:
  3130. case kPrintScreenKeyCode:
  3131. case kInsertKeyCode:
  3132. case kDeleteKeyCode:
  3133. case kTabKeyCode:
  3134. case kBackspaceKeyCode:
  3135. case kHomeKeyCode:
  3136. case kEndKeyCode:
  3137. case kPageUpKeyCode:
  3138. case kPageDownKeyCode:
  3139. case kLeftArrowKeyCode:
  3140. case kRightArrowKeyCode:
  3141. case kUpArrowKeyCode:
  3142. case kDownArrowKeyCode:
  3143. case kReturnKeyCode:
  3144. case kEnterKeyCode:
  3145. case kPowerbookEnterKeyCode:
  3146. isSpecial = PR_TRUE;
  3147. break;
  3148. default:
  3149. isSpecial = PR_FALSE;
  3150. break;
  3151. }
  3152. return isSpecial;
  3153. }
  3154. static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
  3155. {
  3156. // this is not character inputting event, simply.
  3157. if (!aEvent.isChar || !aEvent.charCode)
  3158. return PR_FALSE;
  3159. // if this is unicode char inputting event, we don't need to check
  3160. // ctrl/alt/command keys
  3161. if (aEvent.charCode > 0x7F)
  3162. return PR_TRUE;
  3163. // ASCII chars should be inputted without ctrl/alt/command keys
  3164. return !aEvent.isControl && !aEvent.isAlt && !aEvent.isMeta;
  3165. }
  3166. // Basic conversion for cocoa to gecko events, common to all conversions.
  3167. // Note that it is OK for inEvent to be nil.
  3168. - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
  3169. {
  3170. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3171. NS_ASSERTION(outGeckoEvent, "convertGenericCocoaEvent:toGeckoEvent: requires non-null outGeckoEvent");
  3172. if (!outGeckoEvent)
  3173. return;
  3174. outGeckoEvent->widget = [self widget];
  3175. outGeckoEvent->time = PR_IntervalNow();
  3176. outGeckoEvent->nativeMsg = inEvent;
  3177. if (inEvent) {
  3178. unsigned int modifiers = [inEvent modifierFlags];
  3179. outGeckoEvent->isShift = ((modifiers & NSShiftKeyMask) != 0);
  3180. outGeckoEvent->isControl = ((modifiers & NSControlKeyMask) != 0);
  3181. outGeckoEvent->isAlt = ((modifiers & NSAlternateKeyMask) != 0);
  3182. outGeckoEvent->isMeta = ((modifiers & NSCommandKeyMask) != 0);
  3183. }
  3184. NS_OBJC_END_TRY_ABORT_BLOCK;
  3185. }
  3186. - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
  3187. {
  3188. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3189. NS_ASSERTION(aMouseEvent && outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null arguments");
  3190. if (!aMouseEvent || !outGeckoEvent)
  3191. return;
  3192. [self convertGenericCocoaEvent:aMouseEvent toGeckoEvent:outGeckoEvent];
  3193. // convert point to view coordinate system
  3194. NSPoint localPoint = [self convertPoint:[aMouseEvent locationInWindow] fromView:nil];
  3195. outGeckoEvent->refPoint.x = static_cast<nscoord>(localPoint.x);
  3196. outGeckoEvent->refPoint.y = static_cast<nscoord>(localPoint.y);
  3197. NS_OBJC_END_TRY_ABORT_BLOCK;
  3198. }
  3199. #define CHARCODE_MASK_1 0x00FF0000
  3200. #define CHARCODE_MASK_2 0x000000FF
  3201. #define CHARCODE_MASK 0x00FF00FF
  3202. //#define DEBUG_KB 1
  3203. static PRUint32
  3204. KeyTranslateToUnicode(Handle aHandle, UInt32 aKeyCode, UInt32 aModifiers,
  3205. TextEncoding aEncoding)
  3206. {
  3207. #ifdef DEBUG_KB
  3208. NSLog(@"**** KeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aEncoding: %X",
  3209. aHandle, aKeyCode, aModifiers, aEncoding);
  3210. PRBool isShift = aModifiers & shiftKey;
  3211. PRBool isCtrl = aModifiers & controlKey;
  3212. PRBool isOpt = aModifiers & optionKey;
  3213. PRBool isCmd = aModifiers & cmdKey;
  3214. PRBool isCL = aModifiers & alphaLock;
  3215. PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
  3216. NSLog(@" Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
  3217. isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
  3218. isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
  3219. #endif
  3220. UInt32 state = 0;
  3221. UInt32 val =
  3222. ::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
  3223. // If state is not zero, it is in dead key state. Then, we need to recall
  3224. // KeyTranslate for getting the actual character.
  3225. if (state) {
  3226. val =
  3227. ::KeyTranslate(aHandle, aKeyCode | aModifiers, &state) & CHARCODE_MASK;
  3228. }
  3229. PRUint32 ch = 0;
  3230. UInt8 buf[2];
  3231. CFIndex len = 0;
  3232. if (val & CHARCODE_MASK_1)
  3233. buf[len++] = (val & CHARCODE_MASK_1) >> 16;
  3234. buf[len++] = val & CHARCODE_MASK_2;
  3235. CFStringRef str =
  3236. ::CFStringCreateWithBytes(kCFAllocatorDefault, buf, len,
  3237. (CFStringEncoding)aEncoding, false);
  3238. ch = ::CFStringGetLength(str) == 1 ?
  3239. ::CFStringGetCharacterAtIndex(str, 0) : 0;
  3240. ::CFRelease(str);
  3241. #ifdef DEBUG_KB
  3242. NSLog(@" result: %X(%C)", ch, ch > ' ' ? ch : ' ');
  3243. #endif
  3244. return ch;
  3245. }
  3246. static PRUint32
  3247. UCKeyTranslateToUnicode(const UCKeyboardLayout* aHandle, UInt32 aKeyCode, UInt32 aModifiers,
  3248. UInt32 aKbType)
  3249. {
  3250. #ifdef DEBUG_KB
  3251. NSLog(@"**** UCKeyTranslateToUnicode: aHandle: %p, aKeyCode: %X, aModifiers: %X, aKbType: %X",
  3252. aHandle, aKeyCode, aModifiers, aKbType);
  3253. PRBool isShift = aModifiers & shiftKey;
  3254. PRBool isCtrl = aModifiers & controlKey;
  3255. PRBool isOpt = aModifiers & optionKey;
  3256. PRBool isCmd = aModifiers & cmdKey;
  3257. PRBool isCL = aModifiers & alphaLock;
  3258. PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
  3259. NSLog(@" Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
  3260. isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
  3261. isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
  3262. #endif
  3263. UInt32 deadKeyState = 0;
  3264. UniCharCount len;
  3265. UniChar chars[5];
  3266. OSStatus err = ::UCKeyTranslate(aHandle, aKeyCode,
  3267. kUCKeyActionDown, aModifiers >> 8,
  3268. aKbType, kUCKeyTranslateNoDeadKeysMask,
  3269. &deadKeyState, 5, &len, chars);
  3270. PRUint32 ch = (err == noErr && len == 1) ? PRUint32(chars[0]) : 0;
  3271. #ifdef DEBUG_KB
  3272. NSLog(@" result: %X(%C)", ch, ch > ' ' ? ch : ' ');
  3273. #endif
  3274. return ch;
  3275. }
  3276. struct KeyTranslateData {
  3277. KeyTranslateData() {
  3278. mUchr.mLayout = nsnull;
  3279. mUchr.mKbType = 0;
  3280. mKchr.mHandle = nsnull;
  3281. mKchr.mEncoding = nsnull;
  3282. }
  3283. // The script of the layout determines the encoding of characters obtained
  3284. // from kchr resources.
  3285. SInt16 mScript;
  3286. // The keyboard layout identifier
  3287. SInt32 mLayoutID;
  3288. struct {
  3289. const UCKeyboardLayout* mLayout;
  3290. UInt32 mKbType;
  3291. } mUchr;
  3292. struct {
  3293. Handle mHandle;
  3294. TextEncoding mEncoding;
  3295. } mKchr;
  3296. };
  3297. static PRUint32
  3298. GetUniCharFromKeyTranslate(KeyTranslateData& aData,
  3299. UInt32 aKeyCode, UInt32 aModifiers)
  3300. {
  3301. if (aData.mUchr.mLayout) {
  3302. return UCKeyTranslateToUnicode(aData.mUchr.mLayout, aKeyCode, aModifiers,
  3303. aData.mUchr.mKbType);
  3304. }
  3305. if (aData.mKchr.mHandle) {
  3306. return KeyTranslateToUnicode(aData.mKchr.mHandle, aKeyCode, aModifiers,
  3307. aData.mKchr.mEncoding);
  3308. }
  3309. return 0;
  3310. }
  3311. static SInt32
  3312. GetScriptFromKeyboardLayout(SInt32 aLayoutID)
  3313. {
  3314. switch (aLayoutID) {
  3315. case 3: // German
  3316. case -2: return smRoman; // US-Extended
  3317. case -18944: return smGreek; // Greek
  3318. default: NS_NOTREACHED("unknown keyboard layout");
  3319. }
  3320. return smRoman;
  3321. }
  3322. static CFStringRef
  3323. GetInputSourceIDFromKeyboardLayout(SInt32 aLayoutID)
  3324. {
  3325. NS_ASSERTION(nsToolkit::OnLeopardOrLater() &&
  3326. Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
  3327. Leopard_TISGetInputSourceProperty &&
  3328. Leopard_TISCreateInputSourceList &&
  3329. kOurTISPropertyUnicodeKeyLayoutData &&
  3330. kOurTISPropertyInputSourceID,
  3331. "GetInputSourceIDFromKeyboardLayout should only be used on Leopard or later.");
  3332. KeyboardLayoutRef keylayout;
  3333. if (KLGetKeyboardLayoutWithIdentifier(aLayoutID, &keylayout) != noErr)
  3334. return nsnull;
  3335. const void* uchrFromID;
  3336. if (KLGetKeyboardLayoutProperty(keylayout, kKLuchrData, &uchrFromID) != noErr)
  3337. return nsnull;
  3338. CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, NULL, NULL);
  3339. CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
  3340. CFRelease(dict);
  3341. CFStringRef sourceID = nsnull;
  3342. for (CFIndex i = 0; i < CFArrayGetCount(inputSources); ++i) {
  3343. TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, i)));
  3344. CFDataRef data = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
  3345. if (!data)
  3346. continue;
  3347. const UCKeyboardLayout* uchr = reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(data));
  3348. if (uchr == uchrFromID) {
  3349. sourceID = static_cast<CFStringRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyInputSourceID));
  3350. break;
  3351. }
  3352. }
  3353. CFRelease(inputSources);
  3354. return sourceID;
  3355. }
  3356. static PRUint32
  3357. GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
  3358. {
  3359. KeyTranslateData kt;
  3360. Handle handle = ::GetResource('uchr', kKLUSKeyboard); // US keyboard layout
  3361. if (!handle || !(*handle)) {
  3362. NS_ERROR("US keyboard layout doesn't have uchr resource");
  3363. return 0;
  3364. }
  3365. UInt32 kbType = 40; // ANSI, don't use actual layout
  3366. return UCKeyTranslateToUnicode((UCKeyboardLayout*)(*handle), aKeyCode,
  3367. aModifiers, kbType);
  3368. }
  3369. - (void) convertCocoaKeyEvent:(NSEvent*)aKeyEvent toGeckoEvent:(nsKeyEvent*)outGeckoEvent
  3370. {
  3371. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3372. NS_ASSERTION(aKeyEvent && outGeckoEvent, "convertCocoaKeyEvent:toGeckoEvent: requires non-null arguments");
  3373. if (!aKeyEvent || !outGeckoEvent)
  3374. return;
  3375. [self convertGenericCocoaEvent:aKeyEvent toGeckoEvent:outGeckoEvent];
  3376. // coords for key events are always 0,0
  3377. outGeckoEvent->refPoint.x = outGeckoEvent->refPoint.y = 0;
  3378. // Initialize whether or not we are using charCodes to false.
  3379. outGeckoEvent->isChar = PR_FALSE;
  3380. // Check to see if the message is a key press that does not involve
  3381. // one of our special key codes.
  3382. if (outGeckoEvent->message == NS_KEY_PRESS && !IsSpecialGeckoKey([aKeyEvent keyCode])) {
  3383. outGeckoEvent->isChar = PR_TRUE; // this is not a special key
  3384. outGeckoEvent->charCode = 0;
  3385. outGeckoEvent->keyCode = 0; // not set for key press events
  3386. NSString* chars = [aKeyEvent characters];
  3387. if ([chars length] > 0)
  3388. outGeckoEvent->charCode = [chars characterAtIndex:0];
  3389. // convert control-modified charCode to raw charCode (with appropriate case)
  3390. if (outGeckoEvent->isControl && outGeckoEvent->charCode <= 26)
  3391. outGeckoEvent->charCode += (outGeckoEvent->isShift) ? ('A' - 1) : ('a' - 1);
  3392. // Accel and access key handling needs to know the characters that this
  3393. // key produces with Shift up or down. So, provide this information
  3394. // when Ctrl or Command or Alt is pressed.
  3395. if (outGeckoEvent->isControl || outGeckoEvent->isMeta ||
  3396. outGeckoEvent->isAlt) {
  3397. KeyTranslateData kt;
  3398. if (gOverrideKeyboardLayout.mOverrideEnabled) {
  3399. kt.mLayoutID = gOverrideKeyboardLayout.mKeyboardLayout;
  3400. kt.mScript = GetScriptFromKeyboardLayout(kt.mLayoutID);
  3401. } else {
  3402. // GetScriptManagerVariable and GetScriptVariable are both deprecated.
  3403. // KLGetCurrentKeyboardLayout is newer but also deprecated in OS X
  3404. // 10.5. It's not clear from the documentation but it seems that
  3405. // KLGetKeyboardLayoutProperty with kKLGroupIdentifier may provide the
  3406. // script identifier for a KeyboardLayoutRef (bug 432388 comment 6).
  3407. // The "Text Input Source Services" API is not available prior to OS X
  3408. // 10.5.
  3409. kt.mScript = ::GetScriptManagerVariable(smKeyScript);
  3410. kt.mLayoutID = ::GetScriptVariable(kt.mScript, smScriptKeys);
  3411. }
  3412. CFDataRef uchr = NULL;
  3413. // GetResource('uchr', kt.mLayoutID) fails on OS X 10.5
  3414. if (nsToolkit::OnLeopardOrLater() &&
  3415. Leopard_TISCopyCurrentKeyboardLayoutInputSource &&
  3416. Leopard_TISGetInputSourceProperty &&
  3417. Leopard_TISCreateInputSourceList &&
  3418. kOurTISPropertyUnicodeKeyLayoutData &&
  3419. kOurTISPropertyInputSourceID) {
  3420. if (gOverrideKeyboardLayout.mOverrideEnabled) {
  3421. CFStringRef sourceID = GetInputSourceIDFromKeyboardLayout(kt.mLayoutID);
  3422. NS_ASSERTION(sourceID, "unable to map keyboard layout ID to input source ID");
  3423. const void* keys[] = { kOurTISPropertyInputSourceID };
  3424. const void* vals[] = { sourceID };
  3425. CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, NULL, NULL);
  3426. CFArrayRef inputSources = Leopard_TISCreateInputSourceList(dict, true);
  3427. CFRelease(dict);
  3428. if (CFArrayGetCount(inputSources) == 1) {
  3429. TISInputSourceRef tis = static_cast<TISInputSourceRef>(const_cast<void *>(CFArrayGetValueAtIndex(inputSources, 0)));
  3430. uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
  3431. }
  3432. CFRelease(inputSources);
  3433. } else {
  3434. TISInputSourceRef tis = Leopard_TISCopyCurrentKeyboardLayoutInputSource();
  3435. uchr = static_cast<CFDataRef>(Leopard_TISGetInputSourceProperty(tis, kOurTISPropertyUnicodeKeyLayoutData));
  3436. }
  3437. }
  3438. // This fails for Azeri on 10.4 even though kKLKind (2) indicates that
  3439. // the layout has a uchr resource. Perhaps KLGetKeyboardLayoutProperty
  3440. // with kKLuchrData would be helpful here.
  3441. Handle handle = ::GetResource('uchr', kt.mLayoutID);
  3442. if (uchr) {
  3443. // We should be here on OS X 10.5 for any Apple provided layout, as
  3444. // they are all uchr. It may be possible to still use kchr resources
  3445. // from elsewhere, so they may be picked by
  3446. // GetScriptManagerVariable(smKCHRCache) below
  3447. kt.mUchr.mLayout = reinterpret_cast<const UCKeyboardLayout*>
  3448. (CFDataGetBytePtr(uchr));
  3449. } else if (handle) {
  3450. // uchr (Unicode) keyboard layout resource prior to 10.5.
  3451. kt.mUchr.mLayout = *((UCKeyboardLayout**)handle);
  3452. } else {
  3453. // kchr (non-Unicode) keyboard layout resource.
  3454. // There are no know cases where GetResource succeeds here, and so
  3455. // tests (gOverrideKeyboardLayout.mOverrideEnabled) currently end up
  3456. // with no keyboard layout. KLGetKeyboardLayoutWithIdentifier and
  3457. // KLGetKeyboardLayoutProperty with kKLKCHRData would be useful here.
  3458. kt.mKchr.mHandle = ::GetResource('kchr', kt.mLayoutID);
  3459. if (!kt.mKchr.mHandle && !gOverrideKeyboardLayout.mOverrideEnabled)
  3460. kt.mKchr.mHandle = (char**)::GetScriptManagerVariable(smKCHRCache);
  3461. if (kt.mKchr.mHandle) {
  3462. OSStatus err =
  3463. ::GetTextEncodingFromScriptInfo(kt.mScript, kTextLanguageDontCare,
  3464. kTextRegionDontCare,
  3465. &kt.mKchr.mEncoding);
  3466. if (err != noErr)
  3467. kt.mKchr.mHandle = nsnull;
  3468. }
  3469. }
  3470. // If a keyboard layout override is set, we also need to force the
  3471. // keyboard type to something ANSI to avoid test failures on machines
  3472. // with JIS keyboards (since the pair of keyboard layout and physical
  3473. // keyboard type form the actual key layout). This assumes that the
  3474. // test setting the override was written assuming an ANSI keyboard.
  3475. if (kt.mUchr.mLayout)
  3476. kt.mUchr.mKbType = gOverrideKeyboardLayout.mOverrideEnabled ? 40 : ::LMGetKbdType();
  3477. UInt32 key = [aKeyEvent keyCode];
  3478. // Caps lock and num lock modifier state:
  3479. UInt32 lockState = 0;
  3480. if ([aKeyEvent modifierFlags] & NSAlphaShiftKeyMask)
  3481. lockState |= alphaLock;
  3482. if ([aKeyEvent modifierFlags] & NSNumericPadKeyMask)
  3483. lockState |= kEventKeyModifierNumLockMask;
  3484. // normal chars
  3485. PRUint32 unshiftedChar = GetUniCharFromKeyTranslate(kt, key, lockState);
  3486. UInt32 shiftLockMod = shiftKey | lockState;
  3487. PRUint32 shiftedChar = GetUniCharFromKeyTranslate(kt, key, shiftLockMod);
  3488. // characters generated with Cmd key
  3489. // XXX we should remove CapsLock state, which changes characters from
  3490. // Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
  3491. // is pressed.
  3492. UInt32 numState = (lockState & ~alphaLock); // only num lock state
  3493. PRUint32 uncmdedChar = GetUniCharFromKeyTranslate(kt, key, numState);
  3494. UInt32 shiftNumMod = numState | shiftKey;
  3495. PRUint32 uncmdedShiftChar =
  3496. GetUniCharFromKeyTranslate(kt, key, shiftNumMod);
  3497. PRUint32 uncmdedUSChar = GetUSLayoutCharFromKeyTranslate(key, numState);
  3498. UInt32 cmdNumMod = cmdKey | numState;
  3499. PRUint32 cmdedChar = GetUniCharFromKeyTranslate(kt, key, cmdNumMod);
  3500. UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
  3501. PRUint32 cmdedShiftChar =
  3502. GetUniCharFromKeyTranslate(kt, key, cmdShiftNumMod);
  3503. // Is the keyboard layout changed by Cmd key?
  3504. // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
  3505. PRBool isCmdSwitchLayout = uncmdedChar != cmdedChar;
  3506. // Is the keyboard layout for Latin, but Cmd key switches the layout?
  3507. // I.e., Dvorak-QWERTY
  3508. PRBool isDvorakQWERTY = isCmdSwitchLayout && kt.mScript == smRoman;
  3509. // If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed,
  3510. // we should append unshiftedChar and shiftedChar for handling the
  3511. // normal characters. These are the characters that the user is most
  3512. // likely to associate with this key.
  3513. if ((unshiftedChar || shiftedChar) &&
  3514. (!outGeckoEvent->isMeta || !isDvorakQWERTY)) {
  3515. nsAlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
  3516. outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
  3517. }
  3518. // Most keyboard layouts provide the same characters in the NSEvents
  3519. // with Command+Shift as with Command. However, with Command+Shift we
  3520. // want the character on the second level. e.g. With a US QWERTY
  3521. // layout, we want "?" when the "/","?" key is pressed with
  3522. // Command+Shift.
  3523. // On a German layout, the OS gives us '/' with Cmd+Shift+SS(eszett)
  3524. // even though Cmd+SS is 'SS' and Shift+'SS' is '?'. This '/' seems
  3525. // like a hack to make the Cmd+"?" event look the same as the Cmd+"?"
  3526. // event on a US keyboard. The user thinks they are typing Cmd+"?", so
  3527. // we'll prefer the "?" character, replacing charCode with shiftedChar
  3528. // when Shift is pressed. However, in case there is a layout where the
  3529. // character unique to Cmd+Shift is the character that the user expects,
  3530. // we'll send it as an alternative char.
  3531. PRBool hasCmdShiftOnlyChar =
  3532. cmdedChar != cmdedShiftChar && uncmdedShiftChar != cmdedShiftChar;
  3533. PRUint32 originalCmdedShiftChar = cmdedShiftChar;
  3534. // If we can make a good guess at the characters that the user would
  3535. // expect this key combination to produce (with and without Shift) then
  3536. // use those characters. This also corrects for CapsLock, which was
  3537. // ignored above.
  3538. if (!isCmdSwitchLayout) {
  3539. // The characters produced with Command seem similar to those without
  3540. // Command.
  3541. if (unshiftedChar)
  3542. cmdedChar = unshiftedChar;
  3543. if (shiftedChar)
  3544. cmdedShiftChar = shiftedChar;
  3545. } else if (uncmdedUSChar == cmdedChar) {
  3546. // It looks like characters from a US layout are provided when Command
  3547. // is down.
  3548. PRUint32 ch = GetUSLayoutCharFromKeyTranslate(key, lockState);
  3549. if (ch)
  3550. cmdedChar = ch;
  3551. ch = GetUSLayoutCharFromKeyTranslate(key, shiftLockMod);
  3552. if (ch)
  3553. cmdedShiftChar = ch;
  3554. }
  3555. // Only charCode (not alternativeCharCodes) is available to javascript,
  3556. // so attempt to set this to the most likely intended (or most useful)
  3557. // character. Note that cmdedChar and cmdedShiftChar are usually
  3558. // Latin/ASCII characters and that is what is wanted here as accel
  3559. // keys are expected to be Latin characters.
  3560. //
  3561. // XXX We should do something similar when Control is down (bug 429510).
  3562. if (outGeckoEvent->isMeta &&
  3563. !(outGeckoEvent->isControl || outGeckoEvent->isAlt)) {
  3564. // The character to use for charCode.
  3565. PRUint32 preferredCharCode = 0;
  3566. preferredCharCode = outGeckoEvent->isShift ? cmdedShiftChar : cmdedChar;
  3567. if (preferredCharCode) {
  3568. #ifdef DEBUG_KB
  3569. if (outGeckoEvent->charCode != preferredCharCode) {
  3570. NSLog(@" charCode replaced: %X(%C) to %X(%C)",
  3571. outGeckoEvent->charCode,
  3572. outGeckoEvent->charCode > ' ' ? outGeckoEvent->charCode : ' ',
  3573. preferredCharCode,
  3574. preferredCharCode > ' ' ? preferredCharCode : ' ');
  3575. }
  3576. #endif
  3577. outGeckoEvent->charCode = preferredCharCode;
  3578. }
  3579. }
  3580. // If the current keyboard layout is switched by the Cmd key,
  3581. // we should append cmdedChar and shiftedCmdChar that are
  3582. // Latin char for the key. But don't append at Dvorak-QWERTY.
  3583. if ((cmdedChar || cmdedShiftChar) &&
  3584. isCmdSwitchLayout && !isDvorakQWERTY) {
  3585. nsAlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
  3586. outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
  3587. }
  3588. // Special case for 'SS' key of German layout. See the comment of
  3589. // hasCmdShiftOnlyChar definition for the detail.
  3590. if (hasCmdShiftOnlyChar && originalCmdedShiftChar) {
  3591. nsAlternativeCharCode altCharCodes(0, originalCmdedShiftChar);
  3592. outGeckoEvent->alternativeCharCodes.AppendElement(altCharCodes);
  3593. }
  3594. }
  3595. }
  3596. else {
  3597. NSString* characters = nil;
  3598. if ([aKeyEvent type] != NSFlagsChanged)
  3599. characters = [aKeyEvent charactersIgnoringModifiers];
  3600. outGeckoEvent->keyCode = ConvertMacToGeckoKeyCode([aKeyEvent keyCode], outGeckoEvent, characters);
  3601. outGeckoEvent->charCode = 0;
  3602. }
  3603. if (outGeckoEvent->message == NS_KEY_PRESS && !outGeckoEvent->isMeta)
  3604. [NSCursor setHiddenUntilMouseMoves:YES];
  3605. NS_OBJC_END_TRY_ABORT_BLOCK;
  3606. }
  3607. // Called from PluginKeyEventsHandler() (a handler for Carbon TSM events) to
  3608. // process a Carbon key event for the currently focused plugin. Both Unicode
  3609. // characters and "Mac encoding characters" (in the MBCS or "multibyte
  3610. // character system") are (or should be) available from aKeyEvent, but here we
  3611. // use the MCBS characters. This is how the WebKit does things, and seems to
  3612. // be what plugins expect.
  3613. - (void) processPluginKeyEvent:(EventRef)aKeyEvent
  3614. {
  3615. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3616. if (!mGeckoChild)
  3617. return;
  3618. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  3619. UInt32 numCharCodes;
  3620. OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
  3621. typeChar, NULL, 0, &numCharCodes, NULL);
  3622. if (status != noErr)
  3623. return;
  3624. nsAutoTArray<unsigned char, 3> charCodes;
  3625. charCodes.SetLength(numCharCodes);
  3626. status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
  3627. typeChar, NULL, numCharCodes, NULL, charCodes.Elements());
  3628. if (status != noErr)
  3629. return;
  3630. UInt32 modifiers;
  3631. status = ::GetEventParameter(aKeyEvent, kEventParamKeyModifiers,
  3632. typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
  3633. if (status != noErr)
  3634. return;
  3635. UInt32 macKeyCode;
  3636. status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
  3637. typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode);
  3638. if (status != noErr)
  3639. return;
  3640. EventRef cloneEvent = ::CopyEvent(aKeyEvent);
  3641. for (unsigned int i = 0; i < numCharCodes; ++i) {
  3642. status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
  3643. typeChar, 1, charCodes.Elements() + i);
  3644. if (status != noErr)
  3645. break;
  3646. EventRecord eventRec;
  3647. if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
  3648. nsKeyEvent keyDownEvent(PR_TRUE, NS_KEY_DOWN, mGeckoChild);
  3649. PRUint32 keyCode(ConvertMacToGeckoKeyCode(macKeyCode, &keyDownEvent, @""));
  3650. PRUint32 charCode(charCodes.ElementAt(i));
  3651. keyDownEvent.time = PR_IntervalNow();
  3652. keyDownEvent.nativeMsg = &eventRec;
  3653. if (IsSpecialGeckoKey(macKeyCode)) {
  3654. keyDownEvent.keyCode = keyCode;
  3655. } else {
  3656. keyDownEvent.charCode = charCode;
  3657. keyDownEvent.isChar = PR_TRUE;
  3658. }
  3659. keyDownEvent.isShift = ((modifiers & shiftKey) != 0);
  3660. keyDownEvent.isControl = ((modifiers & controlKey) != 0);
  3661. keyDownEvent.isAlt = ((modifiers & optionKey) != 0);
  3662. keyDownEvent.isMeta = ((modifiers & cmdKey) != 0); // Should never happen
  3663. mGeckoChild->DispatchWindowEvent(keyDownEvent);
  3664. if (!mGeckoChild)
  3665. break;
  3666. }
  3667. }
  3668. ::ReleaseEvent(cloneEvent);
  3669. NS_OBJC_END_TRY_ABORT_BLOCK;
  3670. }
  3671. - (nsRect)sendCompositionEvent:(PRInt32) aEventType
  3672. {
  3673. #ifdef DEBUG_IME
  3674. NSLog(@"****in sendCompositionEvent; type = %d", aEventType);
  3675. #endif
  3676. if (!mGeckoChild)
  3677. return nsRect(0, 0, 0, 0);
  3678. // static void init_composition_event( *aEvent, int aType)
  3679. nsCompositionEvent event(PR_TRUE, aEventType, mGeckoChild);
  3680. event.time = PR_IntervalNow();
  3681. mGeckoChild->DispatchWindowEvent(event);
  3682. return event.theReply.mCursorPosition;
  3683. }
  3684. - (void)sendTextEvent:(PRUnichar*) aBuffer
  3685. attributedString:(NSAttributedString*) aString
  3686. selectedRange:(NSRange) selRange
  3687. markedRange:(NSRange) markRange
  3688. doCommit:(BOOL) doCommit
  3689. {
  3690. #ifdef DEBUG_IME
  3691. NSLog(@"****in sendTextEvent; string = '%@'", aString);
  3692. NSLog(@" markRange = %d, %d; selRange = %d, %d", markRange.location, markRange.length, selRange.location, selRange.length);
  3693. #endif
  3694. if (!mGeckoChild)
  3695. return;
  3696. nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mGeckoChild);
  3697. textEvent.time = PR_IntervalNow();
  3698. textEvent.theText = aBuffer;
  3699. if (!doCommit)
  3700. FillTextRangeInTextEvent(&textEvent, aString, markRange, selRange);
  3701. mGeckoChild->DispatchWindowEvent(textEvent);
  3702. if (textEvent.rangeArray)
  3703. delete [] textEvent.rangeArray;
  3704. }
  3705. #pragma mark -
  3706. // NSTextInput implementation
  3707. #define MAX_BUFFER_SIZE 32
  3708. - (void)insertText:(id)insertString
  3709. {
  3710. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3711. #if DEBUG_IME
  3712. NSLog(@"****in insertText: '%@'", insertString);
  3713. NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3714. #endif
  3715. if (!mGeckoChild)
  3716. return;
  3717. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  3718. if (![insertString isKindOfClass:[NSAttributedString class]])
  3719. insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease];
  3720. NSString *tmpStr = [insertString string];
  3721. unsigned int len = [tmpStr length];
  3722. if (!nsTSMManager::IsComposing() && len == 0)
  3723. return; // nothing to do
  3724. PRUnichar buffer[MAX_BUFFER_SIZE];
  3725. PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
  3726. [tmpStr getCharacters:bufPtr];
  3727. bufPtr[len] = PRUnichar('\0');
  3728. if (len == 1 && !nsTSMManager::IsComposing()) {
  3729. // don't let the same event be fired twice when hitting
  3730. // enter/return! (Bug 420502)
  3731. if (mKeyPressSent)
  3732. return;
  3733. // dispatch keypress event with char instead of textEvent
  3734. nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
  3735. geckoEvent.time = PR_IntervalNow();
  3736. geckoEvent.charCode = bufPtr[0]; // gecko expects OS-translated unicode
  3737. geckoEvent.keyCode = 0;
  3738. geckoEvent.isChar = PR_TRUE;
  3739. if (mKeyDownHandled)
  3740. geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
  3741. // don't set other modifiers from the current event, because here in
  3742. // -insertText: they've already been taken into account in creating
  3743. // the input string.
  3744. // create native EventRecord for use by plugins
  3745. EventRecord macEvent;
  3746. if (mCurKeyEvent) {
  3747. // XXX The ASCII characters inputting mode of egbridge (Japanese IME)
  3748. // might send the keyDown event with wrong keyboard layout if other
  3749. // keyboard layouts are already loaded. In that case, the native event
  3750. // doesn't match to this gecko event...
  3751. ConvertCocoaKeyEventToMacEvent(mCurKeyEvent, macEvent);
  3752. geckoEvent.nativeMsg = &macEvent;
  3753. geckoEvent.isShift = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
  3754. if (!IsPrintableChar(geckoEvent.charCode)) {
  3755. geckoEvent.keyCode =
  3756. ConvertMacToGeckoKeyCode([mCurKeyEvent keyCode], &geckoEvent,
  3757. [mCurKeyEvent charactersIgnoringModifiers]);
  3758. geckoEvent.charCode = 0;
  3759. }
  3760. } else {
  3761. // Note that insertText is not called only at key pressing.
  3762. if (!IsPrintableChar(geckoEvent.charCode)) {
  3763. geckoEvent.keyCode = GetGeckoKeyCodeFromChar(geckoEvent.charCode);
  3764. geckoEvent.charCode = 0;
  3765. }
  3766. }
  3767. mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
  3768. mKeyPressSent = YES;
  3769. }
  3770. else {
  3771. if (!nsTSMManager::IsComposing()) {
  3772. [self sendCompositionEvent:NS_COMPOSITION_START];
  3773. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  3774. nsTSMManager::StartComposing(self);
  3775. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  3776. }
  3777. if (nsTSMManager::IgnoreCommit()) {
  3778. tmpStr = [tmpStr init];
  3779. len = 0;
  3780. bufPtr[0] = PRUnichar('\0');
  3781. insertString =
  3782. [[[NSAttributedString alloc] initWithString:tmpStr] autorelease];
  3783. }
  3784. [self sendTextEvent:bufPtr attributedString:insertString
  3785. selectedRange:NSMakeRange(0, len)
  3786. markedRange:mMarkedRange
  3787. doCommit:YES];
  3788. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  3789. [self sendCompositionEvent:NS_COMPOSITION_END];
  3790. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  3791. nsTSMManager::EndComposing();
  3792. mMarkedRange = NSMakeRange(NSNotFound, 0);
  3793. }
  3794. if (bufPtr != buffer)
  3795. delete[] bufPtr;
  3796. NS_OBJC_END_TRY_ABORT_BLOCK;
  3797. }
  3798. - (void)insertNewline:(id)sender
  3799. {
  3800. [self insertText:@"\n"];
  3801. }
  3802. - (void) doCommandBySelector:(SEL)aSelector
  3803. {
  3804. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3805. #if DEBUG_IME
  3806. NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
  3807. #endif
  3808. if (!mKeyPressHandled)
  3809. [super doCommandBySelector:aSelector];
  3810. NS_OBJC_END_TRY_ABORT_BLOCK;
  3811. }
  3812. - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
  3813. {
  3814. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3815. #if DEBUG_IME
  3816. NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length);
  3817. NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3818. NSLog(@" aString = '%@'", aString);
  3819. #endif
  3820. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  3821. if (![aString isKindOfClass:[NSAttributedString class]])
  3822. aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
  3823. NSMutableAttributedString *mutableAttribStr = aString;
  3824. NSString *tmpStr = [mutableAttribStr string];
  3825. unsigned int len = [tmpStr length];
  3826. PRUnichar buffer[MAX_BUFFER_SIZE];
  3827. PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
  3828. [tmpStr getCharacters:bufPtr];
  3829. bufPtr[len] = PRUnichar('\0');
  3830. #if DEBUG_IME
  3831. printf("****in setMarkedText, len = %d, text = ", len);
  3832. PRUint32 n = 0;
  3833. PRUint32 maxlen = len > 12 ? 12 : len;
  3834. for (PRUnichar *a = bufPtr; (*a != PRUnichar('\0')) && n<maxlen; a++, n++)
  3835. printf((*a&0xff80) ? "\\u%4X" : "%c", *a);
  3836. printf("\n");
  3837. #endif
  3838. mMarkedRange.length = len;
  3839. if (!nsTSMManager::IsComposing() && len > 0) {
  3840. nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
  3841. mGeckoChild->DispatchWindowEvent(selection);
  3842. mMarkedRange.location = selection.mSucceeded ? selection.mReply.mOffset : 0;
  3843. [self sendCompositionEvent:NS_COMPOSITION_START];
  3844. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  3845. nsTSMManager::StartComposing(self);
  3846. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  3847. }
  3848. if (nsTSMManager::IsComposing()) {
  3849. nsTSMManager::UpdateComposing(tmpStr);
  3850. BOOL commit = len == 0;
  3851. [self sendTextEvent:bufPtr attributedString:aString
  3852. selectedRange:selRange
  3853. markedRange:mMarkedRange
  3854. doCommit:commit];
  3855. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  3856. if (commit) {
  3857. [self sendCompositionEvent:NS_COMPOSITION_END];
  3858. nsTSMManager::EndComposing();
  3859. }
  3860. }
  3861. if (bufPtr != buffer)
  3862. delete[] bufPtr;
  3863. NS_OBJC_END_TRY_ABORT_BLOCK;
  3864. }
  3865. - (void) unmarkText
  3866. {
  3867. #if DEBUG_IME
  3868. NSLog(@"****in unmarkText");
  3869. NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3870. #endif
  3871. nsTSMManager::CommitIME();
  3872. }
  3873. - (BOOL) hasMarkedText
  3874. {
  3875. #if DEBUG_IME
  3876. NSLog(@"****in hasMarkText");
  3877. NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3878. #endif
  3879. return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
  3880. }
  3881. - (long) conversationIdentifier
  3882. {
  3883. #if DEBUG_IME
  3884. NSLog(@"****in conversationIdentifier");
  3885. #endif
  3886. if (!mGeckoChild)
  3887. return (long)self;
  3888. nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
  3889. textContent.InitForQueryTextContent(0, 0);
  3890. mGeckoChild->DispatchWindowEvent(textContent);
  3891. if (!textContent.mSucceeded)
  3892. return (long)self;
  3893. #if DEBUG_IME
  3894. NSLog(@" the ID = %ld", (long)textContent.mReply.mContentsRoot);
  3895. #endif
  3896. return (long)textContent.mReply.mContentsRoot;
  3897. }
  3898. - (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
  3899. {
  3900. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  3901. #if DEBUG_IME
  3902. NSLog(@"****in attributedSubstringFromRange");
  3903. NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
  3904. NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3905. #endif
  3906. if (!mGeckoChild || theRange.length == 0)
  3907. return nil;
  3908. nsAutoString str;
  3909. nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
  3910. textContent.InitForQueryTextContent(theRange.location, theRange.length);
  3911. mGeckoChild->DispatchWindowEvent(textContent);
  3912. if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())
  3913. return nil;
  3914. NSString* nsstr = ToNSString(textContent.mReply.mString);
  3915. NSAttributedString* result =
  3916. [[[NSAttributedString alloc] initWithString:nsstr
  3917. attributes:nil] autorelease];
  3918. return result;
  3919. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  3920. }
  3921. - (NSRange) markedRange
  3922. {
  3923. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3924. #if DEBUG_IME
  3925. NSLog(@"****in markedRange");
  3926. NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3927. #endif
  3928. if (![self hasMarkedText]) {
  3929. return NSMakeRange(NSNotFound, 0);
  3930. }
  3931. return mMarkedRange;
  3932. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
  3933. }
  3934. - (NSRange) selectedRange
  3935. {
  3936. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3937. #if DEBUG_IME
  3938. NSLog(@"****in selectedRange");
  3939. NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3940. #endif
  3941. if (!mGeckoChild)
  3942. return NSMakeRange(NSNotFound, 0);
  3943. nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
  3944. mGeckoChild->DispatchWindowEvent(selection);
  3945. if (!selection.mSucceeded)
  3946. return NSMakeRange(NSNotFound, 0);
  3947. #if DEBUG_IME
  3948. NSLog(@" result of selectedRange = %d, %d",
  3949. selection.mReply.mOffset, selection.mReply.mString.Length());
  3950. #endif
  3951. return NSMakeRange(selection.mReply.mOffset,
  3952. selection.mReply.mString.Length());
  3953. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
  3954. }
  3955. - (NSRect) firstRectForCharacterRange:(NSRange)theRange
  3956. {
  3957. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3958. #if DEBUG_IME
  3959. NSLog(@"****in firstRectForCharacterRange");
  3960. NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
  3961. NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  3962. #endif
  3963. // XXX this returns first character rect or caret rect, it is limitation of
  3964. // now. We need more work for returns first line rect. But current
  3965. // implementation is enough for IMEs.
  3966. NSRect rect;
  3967. if (!mGeckoChild || theRange.location == NSNotFound)
  3968. return rect;
  3969. nsRect r;
  3970. PRBool useCaretRect = theRange.length == 0;
  3971. if (!useCaretRect) {
  3972. nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild);
  3973. charRect.InitForQueryCharacterRect(theRange.location);
  3974. mGeckoChild->DispatchWindowEvent(charRect);
  3975. if (charRect.mSucceeded)
  3976. r = charRect.mReply.mRect;
  3977. else
  3978. useCaretRect = PR_TRUE;
  3979. }
  3980. if (useCaretRect) {
  3981. nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, mGeckoChild);
  3982. caretRect.InitForQueryCaretRect(theRange.location);
  3983. mGeckoChild->DispatchWindowEvent(caretRect);
  3984. if (!caretRect.mSucceeded)
  3985. return rect;
  3986. r = caretRect.mReply.mRect;
  3987. r.width = 0;
  3988. }
  3989. nsIWidget* rootWidget = mGeckoChild->GetTopLevelWidget();
  3990. NSWindow* rootWindow =
  3991. static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
  3992. NSView* rootView =
  3993. static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
  3994. if (!rootWindow || !rootView)
  3995. return rect;
  3996. GeckoRectToNSRect(r, rect);
  3997. rect = [rootView convertRect:rect toView:nil];
  3998. rect.origin = [rootWindow convertBaseToScreen:rect.origin];
  3999. #if DEBUG_IME
  4000. NSLog(@" result rect (x,y,w,h) = %f, %f, %f, %f",
  4001. rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
  4002. #endif
  4003. return rect;
  4004. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
  4005. }
  4006. - (unsigned int)characterIndexForPoint:(NSPoint)thePoint
  4007. {
  4008. #if DEBUG_IME
  4009. NSLog(@"****in characterIndexForPoint");
  4010. NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  4011. #endif
  4012. // To implement this, we'd have to grovel in text frames looking at text offsets.
  4013. return 0;
  4014. }
  4015. - (NSArray*) validAttributesForMarkedText
  4016. {
  4017. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  4018. #if DEBUG_IME
  4019. NSLog(@"****in validAttributesForMarkedText");
  4020. NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
  4021. #endif
  4022. //return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
  4023. return [NSArray array]; // empty array; we don't support any attributes right now
  4024. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  4025. }
  4026. #pragma mark -
  4027. + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent
  4028. {
  4029. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  4030. NSEvent* newEvent = [NSEvent keyEventWithType:type
  4031. location:[theEvent locationInWindow]
  4032. modifierFlags:[theEvent modifierFlags]
  4033. timestamp:[theEvent timestamp]
  4034. windowNumber:[theEvent windowNumber]
  4035. context:[theEvent context]
  4036. characters:[theEvent characters]
  4037. charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers]
  4038. isARepeat:[theEvent isARepeat]
  4039. keyCode:[theEvent keyCode]];
  4040. return newEvent;
  4041. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  4042. }
  4043. #ifdef PR_LOGGING
  4044. static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
  4045. {
  4046. for (PRUint32 i = 0; i < [aString length]; ++i) {
  4047. unichar ch = [aString characterAtIndex:i];
  4048. if (ch >= 32 && ch < 128) {
  4049. aBuf.Append(char(ch));
  4050. } else {
  4051. aBuf += nsPrintfCString("\\u%04x", ch);
  4052. }
  4053. }
  4054. return aBuf.get();
  4055. }
  4056. #endif
  4057. // Returns PR_TRUE if Gecko claims to have handled the event, PR_FALSE otherwise.
  4058. - (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv
  4059. {
  4060. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  4061. if (!mGeckoChild)
  4062. return NO;
  4063. #ifdef PR_LOGGING
  4064. nsCAutoString str1;
  4065. nsCAutoString str2;
  4066. #endif
  4067. PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
  4068. ("ChildView processKeyDownEvent: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
  4069. [theEvent keyCode], [theEvent modifierFlags],
  4070. ToEscapedString([theEvent characters], str1),
  4071. ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
  4072. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4073. mCurKeyEvent = theEvent;
  4074. BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
  4075. if (nonDeadKeyPress) {
  4076. if (![theEvent isARepeat]) {
  4077. NSResponder* firstResponder = [[self window] firstResponder];
  4078. nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
  4079. [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
  4080. // create native EventRecord for use by plugins
  4081. EventRecord macEvent;
  4082. ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
  4083. geckoEvent.nativeMsg = &macEvent;
  4084. mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
  4085. if (!mGeckoChild)
  4086. return mKeyDownHandled;
  4087. // The key down event may have shifted the focus, in which
  4088. // case we should not fire the key press.
  4089. if (firstResponder != [[self window] firstResponder]) {
  4090. PRBool handled = mKeyDownHandled;
  4091. mCurKeyEvent = nil;
  4092. mKeyDownHandled = PR_FALSE;
  4093. return handled;
  4094. }
  4095. }
  4096. // If this is the context menu key command, send a context menu key event.
  4097. unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
  4098. if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
  4099. nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey);
  4100. contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
  4101. PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent);
  4102. [self maybeInitContextMenuTracking];
  4103. // Bail, there is nothing else to do here.
  4104. PRBool handled = (cmEventHandled || mKeyDownHandled);
  4105. mCurKeyEvent = nil;
  4106. mKeyDownHandled = PR_FALSE;
  4107. return handled;
  4108. }
  4109. nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
  4110. [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
  4111. // if this is a non-letter keypress, or the control key is down,
  4112. // dispatch the keydown to gecko, so that we trap delete,
  4113. // control-letter combinations etc before Cocoa tries to use
  4114. // them for keybindings.
  4115. if ((!geckoEvent.isChar || geckoEvent.isControl) &&
  4116. !nsTSMManager::IsComposing()) {
  4117. if (mKeyDownHandled)
  4118. geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
  4119. // create native EventRecord for use by plugins
  4120. EventRecord macEvent;
  4121. ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
  4122. geckoEvent.nativeMsg = &macEvent;
  4123. mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
  4124. mKeyPressSent = YES;
  4125. if (!mGeckoChild)
  4126. return (mKeyDownHandled || mKeyPressHandled);
  4127. }
  4128. }
  4129. // Let Cocoa interpret the key events, caching IsComposing first.
  4130. // We don't do it if this came from performKeyEquivalent because
  4131. // interpretKeyEvents isn't set up to handle those key combinations.
  4132. PRBool wasComposing = nsTSMManager::IsComposing();
  4133. PRBool interpretKeyEventsCalled = PR_FALSE;
  4134. if (!isKeyEquiv &&
  4135. (nsTSMManager::IsIMEEnabled() || nsTSMManager::IsRomanKeyboardsOnly())) {
  4136. [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
  4137. interpretKeyEventsCalled = PR_TRUE;
  4138. }
  4139. if (!mGeckoChild)
  4140. return (mKeyDownHandled || mKeyPressHandled);;
  4141. if (!mKeyPressSent && nonDeadKeyPress && !wasComposing && !nsTSMManager::IsComposing()) {
  4142. nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
  4143. [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
  4144. // If we called interpretKeyEvents and this isn't normal character input
  4145. // then IME probably ate the event for some reason. We do not want to
  4146. // send a key press event in that case.
  4147. if (!(interpretKeyEventsCalled && IsNormalCharInputtingEvent(geckoEvent))) {
  4148. if (mKeyDownHandled)
  4149. geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
  4150. // create native EventRecord for use by plugins
  4151. EventRecord macEvent;
  4152. ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
  4153. geckoEvent.nativeMsg = &macEvent;
  4154. mKeyPressHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
  4155. }
  4156. }
  4157. // Note: mGeckoChild might have become null here. Don't count on it from here on.
  4158. PRBool handled = (mKeyDownHandled || mKeyPressHandled);
  4159. // See note about nested event loops where these variables are declared in header.
  4160. mKeyPressHandled = NO;
  4161. mKeyPressSent = NO;
  4162. mCurKeyEvent = nil;
  4163. mKeyDownHandled = PR_FALSE;
  4164. return handled;
  4165. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  4166. }
  4167. // Create a TSM document for use with plugins, so that we can support IME in
  4168. // them. Once it's created, if need be (re)activate it. Some plugins (e.g.
  4169. // the Flash plugin running in Camino) don't create their own TSM document --
  4170. // without which IME can't work. Others (e.g. the Flash plugin running in
  4171. // Firefox) create a TSM document that (somehow) makes the input window behave
  4172. // badly when it contains more than one kind of input (say Hiragana and
  4173. // Romaji). (We can't just use the per-NSView TSM documents that Cocoa
  4174. // provices (those created and managed by the NSTSMInputContext class) -- for
  4175. // some reason TSMProcessRawKeyEvent() doesn't work with them.)
  4176. - (void)activatePluginTSMDoc
  4177. {
  4178. if (!mPluginTSMDoc) {
  4179. // Create a TSM document that supports both non-Unicode and Unicode input.
  4180. // Though [ChildView processPluginKeyEvent:] only sends Mac char codes to
  4181. // the plugin, this makes the input window behave better when it contains
  4182. // more than one kind of input (say Hiragana and Romaji). This is what
  4183. // the OS does when it creates a TSM document for use by an
  4184. // NSTSMInputContext class.
  4185. InterfaceTypeList supportedServices;
  4186. supportedServices[0] = kTextServiceDocumentInterfaceType;
  4187. supportedServices[1] = kUnicodeDocumentInterfaceType;
  4188. ::NewTSMDocument(2, supportedServices, &mPluginTSMDoc, 0);
  4189. // We'll need to use the "input window".
  4190. ::UseInputWindow(mPluginTSMDoc, YES);
  4191. ::ActivateTSMDocument(mPluginTSMDoc);
  4192. } else if (::TSMGetActiveDocument() != mPluginTSMDoc) {
  4193. ::ActivateTSMDocument(mPluginTSMDoc);
  4194. }
  4195. }
  4196. - (void)keyDown:(NSEvent*)theEvent
  4197. {
  4198. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4199. // If a plugin has the focus, we need to use an alternate method for
  4200. // handling NSKeyDown and NSKeyUp events (otherwise Carbon-based IME won't
  4201. // work in plugins like the Flash plugin). The same strategy is used by the
  4202. // WebKit. See PluginKeyEventsHandler() and [ChildView processPluginKeyEvent:]
  4203. // for more info.
  4204. if (mGeckoChild && mIsPluginView) {
  4205. [self activatePluginTSMDoc];
  4206. // We use the active TSM document to pass a pointer to ourselves (the
  4207. // currently focused ChildView) to PluginKeyEventsHandler(). Because this
  4208. // pointer is weak, we should retain and release ourselves around the call
  4209. // to TSMProcessRawKeyEvent().
  4210. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4211. ::TSMSetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
  4212. sizeof(ChildView *), &self);
  4213. ::TSMProcessRawKeyEvent([theEvent _eventRef]);
  4214. ::TSMRemoveDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag);
  4215. return;
  4216. }
  4217. #ifdef MOZ_MACBROWSER
  4218. PRBool handled = [self processKeyDownEvent:theEvent keyEquiv:NO];
  4219. if (!handled) {
  4220. NSResponder* targetResponder = self;
  4221. do {
  4222. targetResponder = [targetResponder nextResponder];
  4223. if (!targetResponder || (targetResponder == self))
  4224. return;
  4225. } while ([targetResponder class] == [ChildView class]);
  4226. [targetResponder keyDown:theEvent];
  4227. }
  4228. #else
  4229. [self processKeyDownEvent:theEvent keyEquiv:NO];
  4230. #endif
  4231. NS_OBJC_END_TRY_ABORT_BLOCK;
  4232. }
  4233. static BOOL keyUpAlreadySentKeyDown = NO;
  4234. - (void)keyUp:(NSEvent*)theEvent
  4235. {
  4236. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4237. #ifdef PR_LOGGING
  4238. nsCAutoString str1;
  4239. nsCAutoString str2;
  4240. #endif
  4241. PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
  4242. ("ChildView keyUp: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
  4243. [theEvent keyCode], [theEvent modifierFlags],
  4244. ToEscapedString([theEvent characters], str1),
  4245. ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
  4246. if (!mGeckoChild)
  4247. return;
  4248. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4249. if (mIsPluginView) {
  4250. // I'm not sure the call to TSMProcessRawKeyEvent() is needed here (though
  4251. // WebKit makes one).
  4252. ::TSMProcessRawKeyEvent([theEvent _eventRef]);
  4253. // Don't send a keyUp event if the corresponding keyDown event(s) is/are
  4254. // still being processed (idea borrowed from WebKit).
  4255. ChildView *keyDownTarget = nil;
  4256. OSStatus status = ::TSMGetDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag,
  4257. sizeof(ChildView *), nil, &keyDownTarget);
  4258. if (status != noErr)
  4259. keyDownTarget = nil;
  4260. if (keyDownTarget == self)
  4261. return;
  4262. // PluginKeyEventsHandler() never sends keyUp events to [ChildView
  4263. // processPluginKeyEvent:], so we need to send them to Gecko here. (This
  4264. // means that when commiting text from IME, several keyDown events may be
  4265. // sent to Gecko (in processPluginKeyEvent) for one keyUp event here.
  4266. // But this is how the WebKit does it, and games expect a keyUp event to
  4267. // be sent when it actually happens (they need to be able to detect how
  4268. // long a key has been held down) -- which wouldn't be possible if we sent
  4269. // them from processPluginKeyEvent.)
  4270. nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, nsnull);
  4271. [self convertCocoaKeyEvent:theEvent toGeckoEvent:&keyUpEvent];
  4272. EventRecord macKeyUpEvent;
  4273. ConvertCocoaKeyEventToMacEvent(theEvent, macKeyUpEvent);
  4274. keyUpEvent.nativeMsg = &macKeyUpEvent;
  4275. mGeckoChild->DispatchWindowEvent(keyUpEvent);
  4276. return;
  4277. }
  4278. // if we don't have any characters we can't generate a keyUp event
  4279. if ([[theEvent characters] length] == 0)
  4280. return;
  4281. // Cocoa doesn't send an NSKeyDown event for control-tab on 10.4, so if this
  4282. // is an NSKeyUp event for control-tab, send a down event to gecko first.
  4283. if (!nsToolkit::OnLeopardOrLater() && !keyUpAlreadySentKeyDown &&
  4284. [theEvent modifierFlags] & NSControlKeyMask && [theEvent keyCode] == kTabKeyCode) {
  4285. // We'll need an NSKeyDown copy of our native event so we convert to a gecko event correctly.
  4286. NSEvent* nativeKeyDownEvent = [ChildView makeNewCocoaEventWithType:NSKeyDown fromEvent:theEvent];
  4287. // send a key down event if we should
  4288. PRBool keyDownHandled = PR_FALSE;
  4289. if (![nativeKeyDownEvent isARepeat]) {
  4290. nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_DOWN, nsnull);
  4291. [self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
  4292. // create native EventRecord for use by plugins
  4293. EventRecord macEvent;
  4294. ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
  4295. geckoEvent.nativeMsg = &macEvent;
  4296. keyDownHandled = mGeckoChild->DispatchWindowEvent(geckoEvent);
  4297. if (!mGeckoChild)
  4298. return;
  4299. }
  4300. // Check to see if we are still the first responder.
  4301. // The key down event may have shifted the focus, in which
  4302. // case we should not fire the key press.
  4303. NSResponder* resp = [[self window] firstResponder];
  4304. if (resp != (NSResponder*)self) {
  4305. keyUpAlreadySentKeyDown = YES;
  4306. [resp keyUp:theEvent];
  4307. keyUpAlreadySentKeyDown = NO;
  4308. return;
  4309. }
  4310. // now send a key press event if we should
  4311. if (!nsTSMManager::IsComposing()) {
  4312. nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
  4313. [self convertCocoaKeyEvent:nativeKeyDownEvent toGeckoEvent:&geckoEvent];
  4314. if (keyDownHandled)
  4315. geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
  4316. // create native EventRecord for use by plugins
  4317. EventRecord macEvent;
  4318. ConvertCocoaKeyEventToMacEvent(nativeKeyDownEvent, macEvent);
  4319. geckoEvent.nativeMsg = &macEvent;
  4320. mGeckoChild->DispatchWindowEvent(geckoEvent);
  4321. if (!mGeckoChild)
  4322. return;
  4323. }
  4324. }
  4325. nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_UP, nsnull);
  4326. [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
  4327. // create native EventRecord for use by plugins
  4328. EventRecord macEvent;
  4329. ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
  4330. geckoEvent.nativeMsg = &macEvent;
  4331. mGeckoChild->DispatchWindowEvent(geckoEvent);
  4332. NS_OBJC_END_TRY_ABORT_BLOCK;
  4333. }
  4334. - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
  4335. {
  4336. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  4337. // don't do anything if we don't have a gecko widget
  4338. if (!mGeckoChild)
  4339. return NO;
  4340. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4341. // If we're not the first responder and the first responder is an NSView
  4342. // object, pass the event on. Otherwise (if, for example, the first
  4343. // responder is an NSWindow object) we should trust the OS to have called
  4344. // us correctly.
  4345. id firstResponder = [[self window] firstResponder];
  4346. if (firstResponder != self) {
  4347. // Special handling if the other first responder is a ChildView.
  4348. if ([firstResponder isKindOfClass:[ChildView class]])
  4349. return [(ChildView *)firstResponder performKeyEquivalent:theEvent];
  4350. if ([firstResponder isKindOfClass:[NSView class]])
  4351. return [super performKeyEquivalent:theEvent];
  4352. }
  4353. // don't process if we're composing, but don't consume the event
  4354. if (nsTSMManager::IsComposing())
  4355. return NO;
  4356. // Sometimes we need to return YES unconditionally. An example is when an embedder's
  4357. // native menus handle an event but don't change focus.
  4358. BOOL handledByEmbedding = NO;
  4359. NSMenu* mainMenu = [NSApp mainMenu];
  4360. if (![mainMenu isKindOfClass:[GeckoNSMenu class]]) { // Embedding
  4361. // For embedding, *always* let the menus win over content that conflicts
  4362. // with menu shortcuts, to prevent a page from completely breaking keyboard
  4363. // accessibility. This is a short-term work-around to prevent regressing
  4364. // Camino's keyboard accessibility from 1.8; while it does break sites that
  4365. // want to legitimately override a few keyboard commands to enhance web-app
  4366. // usability, it prevents badly-coded or malicious sites from creating
  4367. // black holes that keyboard users are unable to escape from.
  4368. // If the native menu handles the event then we eventually return YES no
  4369. // matter what Gecko (to prevent double-processing of menus).
  4370. handledByEmbedding = [mainMenu performKeyEquivalent:theEvent];
  4371. // If a menu handled the event, then normally we wouldn't let Gecko handle
  4372. // it too. However, if the target is a plugin, and the menu didn't change
  4373. // window focus, we give the plugin a chance to handle it.
  4374. if (handledByEmbedding && !(mIsPluginView &&
  4375. [[self window] firstResponder] == self)) {
  4376. return YES;
  4377. }
  4378. }
  4379. else { // Non-embedding
  4380. // Perform native menu UI feedback even if we stop the event from propagating to it normally.
  4381. // Recall that the menu system won't actually execute any commands for keyboard command invocations.
  4382. //
  4383. // If this is a plugin, we do actually perform the action on keyboard commands. See bug 428047.
  4384. // If the action on plugins here changes the first responder, don't continue.
  4385. if (mIsPluginView) {
  4386. [(GeckoNSMenu*)mainMenu actOnKeyEquivalent:theEvent];
  4387. if ([[self window] firstResponder] != self)
  4388. return YES;
  4389. }
  4390. else {
  4391. [(GeckoNSMenu*)mainMenu performMenuUserInterfaceEffectsForEvent:theEvent];
  4392. }
  4393. }
  4394. // With Cmd key or Ctrl+Tab or Ctrl+Esc, keyDown will be never called.
  4395. // Therefore, we need to call processKeyDownEvent from performKeyEquivalent.
  4396. UInt32 modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
  4397. UInt32 keyCode = [theEvent keyCode];
  4398. PRBool keyDownNeverFiredEvent = (modifierFlags & NSCommandKeyMask) ||
  4399. ((modifierFlags & NSControlKeyMask) &&
  4400. (keyCode == kEscapeKeyCode || keyCode == kTabKeyCode));
  4401. // don't handle this if certain modifiers are down - those should
  4402. // be sent as normal key up/down events and cocoa will do so automatically
  4403. // if we reject here
  4404. if (!keyDownNeverFiredEvent &&
  4405. (modifierFlags & (NSFunctionKeyMask| NSNumericPadKeyMask)))
  4406. return handledByEmbedding;
  4407. // Control and option modifiers are used when changing input sources in the
  4408. // input menu. We need to send such key events via "keyDown:", which will
  4409. // happen if we return NO here. This only applies to Mac OS X 10.5 and higher,
  4410. // previous OS versions just call "keyDown:" and not "performKeyEquivalent:"
  4411. // for such events.
  4412. if (!keyDownNeverFiredEvent &&
  4413. (modifierFlags & (NSControlKeyMask | NSAlternateKeyMask)))
  4414. return handledByEmbedding;
  4415. if ([theEvent type] == NSKeyDown) {
  4416. // We trust the Gecko handled status for cmd key events. See bug 417466 for more info.
  4417. if (modifierFlags & NSCommandKeyMask)
  4418. return ([self processKeyDownEvent:theEvent keyEquiv:YES] || handledByEmbedding);
  4419. else
  4420. [self processKeyDownEvent:theEvent keyEquiv:YES];
  4421. }
  4422. return YES;
  4423. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  4424. }
  4425. - (void)flagsChanged:(NSEvent*)theEvent
  4426. {
  4427. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4428. if (!mGeckoChild)
  4429. return;
  4430. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4431. // Fire key up/down events for the modifier keys (shift, alt, ctrl, command).
  4432. if ([theEvent type] == NSFlagsChanged) {
  4433. unsigned int modifiers =
  4434. [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
  4435. const PRUint32 kModifierMaskTable[] =
  4436. { NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask, NSCommandKeyMask };
  4437. const PRUint32 kModifierCount = sizeof(kModifierMaskTable) /
  4438. sizeof(kModifierMaskTable[0]);
  4439. for (PRUint32 i = 0; i < kModifierCount; i++) {
  4440. PRUint32 modifierBit = kModifierMaskTable[i];
  4441. if ((modifiers & modifierBit) != (mLastModifierState & modifierBit)) {
  4442. PRUint32 message = ((modifiers & modifierBit) != 0 ? NS_KEY_DOWN :
  4443. NS_KEY_UP);
  4444. // Fire a key event.
  4445. nsKeyEvent geckoEvent(PR_TRUE, message, nsnull);
  4446. [self convertCocoaKeyEvent:theEvent toGeckoEvent:&geckoEvent];
  4447. // create native EventRecord for use by plugins
  4448. EventRecord macEvent;
  4449. ConvertCocoaKeyEventToMacEvent(theEvent, macEvent, message);
  4450. geckoEvent.nativeMsg = &macEvent;
  4451. mGeckoChild->DispatchWindowEvent(geckoEvent);
  4452. if (!mGeckoChild)
  4453. return;
  4454. // Stop if focus has changed.
  4455. // Check to see if we are still the first responder.
  4456. NSResponder* resp = [[self window] firstResponder];
  4457. if (resp != (NSResponder*)self)
  4458. break;
  4459. }
  4460. }
  4461. mLastModifierState = modifiers;
  4462. }
  4463. // check if the hand scroll cursor needs to be set/unset
  4464. [self setHandScrollCursor:theEvent];
  4465. NS_OBJC_END_TRY_ABORT_BLOCK;
  4466. }
  4467. // This method is called when are are about to lose focus.
  4468. // We must always call through to our superclass, even when mGeckoChild is
  4469. // nil -- otherwise the keyboard focus can end up in the wrong NSView.
  4470. - (BOOL)resignFirstResponder
  4471. {
  4472. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  4473. return [super resignFirstResponder];
  4474. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  4475. }
  4476. - (void)viewsWindowDidBecomeKey
  4477. {
  4478. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4479. if (!mGeckoChild)
  4480. return;
  4481. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4482. // check to see if the window implements the mozWindow protocol. This
  4483. // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront,
  4484. // which can happen because these activate/focus calls propagate out
  4485. // to the embedder via nsIEmbeddingSiteWindow::SetFocus().
  4486. BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)];
  4487. if (isMozWindow)
  4488. [[self window] setSuppressMakeKeyFront:YES];
  4489. [self sendFocusEvent:NS_GOTFOCUS];
  4490. [self sendFocusEvent:NS_ACTIVATE];
  4491. if (isMozWindow)
  4492. [[self window] setSuppressMakeKeyFront:NO];
  4493. NS_OBJC_END_TRY_ABORT_BLOCK;
  4494. }
  4495. - (void)viewsWindowDidResignKey
  4496. {
  4497. if (!mGeckoChild)
  4498. return;
  4499. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4500. [self sendFocusEvent:NS_DEACTIVATE];
  4501. [self sendFocusEvent:NS_LOSTFOCUS];
  4502. }
  4503. // If the call to removeFromSuperview isn't delayed from nsChildView::
  4504. // TearDownView(), the NSView hierarchy might get changed during calls to
  4505. // [ChildView drawRect:], which leads to "beyond bounds" exceptions in
  4506. // NSCFArray. For more info see bmo bug 373122. Apple's docs claim that
  4507. // removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during
  4508. // display" (whatever "display" means). But it's _not_ true that it can be
  4509. // safely invoked during calls to [NSView drawRect:]. We use
  4510. // removeFromSuperview here because there's no longer any danger of being
  4511. // "invoked during display", and because doing do clears up bmo bug 384343.
  4512. - (void)delayedTearDown
  4513. {
  4514. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4515. [self removeFromSuperview];
  4516. [self release];
  4517. NS_OBJC_END_TRY_ABORT_BLOCK;
  4518. }
  4519. #pragma mark -
  4520. // drag'n'drop stuff
  4521. #define kDragServiceContractID "@mozilla.org/widget/dragservice;1"
  4522. // This is a utility function used by NSView drag event methods
  4523. // to send events. It contains all of the logic needed for Gecko
  4524. // dragging to work. Returns YES if the event was handled, NO
  4525. // if it wasn't.
  4526. - (BOOL)doDragAction:(PRUint32)aMessage sender:(id)aSender
  4527. {
  4528. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  4529. if (!mGeckoChild)
  4530. return NO;
  4531. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView doDragAction: entered\n"));
  4532. if (!mDragService) {
  4533. CallGetService(kDragServiceContractID, &mDragService);
  4534. NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
  4535. if (!mDragService)
  4536. return NO;
  4537. }
  4538. if (aMessage == NS_DRAGDROP_ENTER)
  4539. mDragService->StartDragSession();
  4540. nsCOMPtr<nsIDragSession> dragSession;
  4541. mDragService->GetCurrentSession(getter_AddRefs(dragSession));
  4542. if (dragSession) {
  4543. if (aMessage == NS_DRAGDROP_OVER) {
  4544. // fire the drag event at the source. Just ignore whether it was
  4545. // cancelled or not as there isn't actually a means to stop the drag
  4546. mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
  4547. dragSession->SetCanDrop(PR_FALSE);
  4548. }
  4549. else if (aMessage == NS_DRAGDROP_DROP) {
  4550. // We make the assuption that the dragOver handlers have correctly set
  4551. // the |canDrop| property of the Drag Session.
  4552. PRBool canDrop = PR_FALSE;
  4553. if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop)
  4554. return NO;
  4555. }
  4556. unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
  4557. PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
  4558. // force copy = option, alias = cmd-option, default is move
  4559. if (modifierFlags & NSAlternateKeyMask) {
  4560. if (modifierFlags & NSCommandKeyMask)
  4561. action = nsIDragService::DRAGDROP_ACTION_LINK;
  4562. else
  4563. action = nsIDragService::DRAGDROP_ACTION_COPY;
  4564. }
  4565. dragSession->SetDragAction(action);
  4566. }
  4567. // set up gecko event
  4568. nsMouseEvent geckoEvent(PR_TRUE, aMessage, nsnull, nsMouseEvent::eReal);
  4569. [self convertGenericCocoaEvent:nil toGeckoEvent:&geckoEvent];
  4570. // Use our own coordinates in the gecko event.
  4571. // Convert event from gecko global coords to gecko view coords.
  4572. NSPoint localPoint = [self convertPoint:[aSender draggingLocation] fromView:nil];
  4573. geckoEvent.refPoint.x = static_cast<nscoord>(localPoint.x);
  4574. geckoEvent.refPoint.y = static_cast<nscoord>(localPoint.y);
  4575. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4576. mGeckoChild->DispatchWindowEvent(geckoEvent);
  4577. if (!mGeckoChild)
  4578. return YES;
  4579. if (aMessage == NS_DRAGDROP_EXIT && dragSession) {
  4580. nsCOMPtr<nsIDOMNode> sourceNode;
  4581. dragSession->GetSourceNode(getter_AddRefs(sourceNode));
  4582. if (!sourceNode) {
  4583. // We're leaving a window while doing a drag that was
  4584. // initiated in a different app. End the drag session,
  4585. // since we're done with it for now (until the user
  4586. // drags back into mozilla).
  4587. mDragService->EndDragSession(PR_FALSE);
  4588. }
  4589. }
  4590. return YES;
  4591. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  4592. }
  4593. - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
  4594. {
  4595. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  4596. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingEntered: entered\n"));
  4597. // there should never be a globalDragPboard when "draggingEntered:" is
  4598. // called, but just in case we'll take care of it here.
  4599. [globalDragPboard release];
  4600. // Set the global drag pasteboard that will be used for this drag session.
  4601. // This will be set back to nil when the drag session ends (mouse exits
  4602. // the view or a drop happens within the view).
  4603. globalDragPboard = [[sender draggingPasteboard] retain];
  4604. BOOL handled = [self doDragAction:NS_DRAGDROP_ENTER sender:sender];
  4605. return handled ? NSDragOperationGeneric : NSDragOperationNone;
  4606. NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
  4607. }
  4608. - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
  4609. {
  4610. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingUpdated: entered\n"));
  4611. BOOL handled = [self doDragAction:NS_DRAGDROP_OVER sender:sender];
  4612. return handled ? NSDragOperationGeneric : NSDragOperationNone;
  4613. }
  4614. - (void)draggingExited:(id <NSDraggingInfo>)sender
  4615. {
  4616. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView draggingExited: entered\n"));
  4617. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4618. [self doDragAction:NS_DRAGDROP_EXIT sender:sender];
  4619. NS_IF_RELEASE(mDragService);
  4620. }
  4621. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
  4622. {
  4623. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4624. BOOL handled = [self doDragAction:NS_DRAGDROP_DROP sender:sender];
  4625. NS_IF_RELEASE(mDragService);
  4626. return handled;
  4627. }
  4628. // NSDraggingSource
  4629. - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
  4630. {
  4631. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4632. gDraggedTransferables = nsnull;
  4633. if (!mDragService) {
  4634. CallGetService(kDragServiceContractID, &mDragService);
  4635. NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
  4636. }
  4637. if (mDragService) {
  4638. mDragService->EndDragSession(PR_TRUE);
  4639. NS_RELEASE(mDragService);
  4640. }
  4641. [globalDragPboard release];
  4642. globalDragPboard = nil;
  4643. NS_OBJC_END_TRY_ABORT_BLOCK;
  4644. }
  4645. // NSDraggingSource
  4646. // this is just implemented so we comply with the NSDraggingSource informal protocol
  4647. - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal
  4648. {
  4649. return UINT_MAX;
  4650. }
  4651. // This method is a callback typically invoked in response to a drag ending on the desktop
  4652. // or a Findow folder window; the argument passed is a path to the drop location, to be used
  4653. // in constructing a complete pathname for the file(s) we want to create as a result of
  4654. // the drag.
  4655. - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(id <NSDraggingInfo>)dropDestination
  4656. {
  4657. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  4658. nsresult rv;
  4659. PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n"));
  4660. nsCOMPtr<nsILocalFile> targFile;
  4661. NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(targFile));
  4662. nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile);
  4663. if (!macLocalFile) {
  4664. NS_ERROR("No Mac local file");
  4665. return nil;
  4666. }
  4667. if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) {
  4668. NS_ERROR("failed InitWithCFURL");
  4669. return nil;
  4670. }
  4671. if (!gDraggedTransferables)
  4672. return nil;
  4673. PRUint32 transferableCount;
  4674. rv = gDraggedTransferables->Count(&transferableCount);
  4675. if (NS_FAILED(rv))
  4676. return nil;
  4677. for (PRUint32 i = 0; i < transferableCount; i++) {
  4678. nsCOMPtr<nsISupports> genericItem;
  4679. gDraggedTransferables->GetElementAt(i, getter_AddRefs(genericItem));
  4680. nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
  4681. if (!item) {
  4682. NS_ERROR("no transferable");
  4683. return nil;
  4684. }
  4685. item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsILocalFile*));
  4686. // now request the kFilePromiseMime data, which will invoke the data provider
  4687. // If successful, the returned data is a reference to the resulting file.
  4688. nsCOMPtr<nsISupports> fileDataPrimitive;
  4689. PRUint32 dataSize = 0;
  4690. item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
  4691. }
  4692. NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
  4693. NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"];
  4694. NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  4695. NSArray* rslt = [NSArray arrayWithObject:name];
  4696. [name release];
  4697. return rslt;
  4698. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  4699. }
  4700. #pragma mark -
  4701. #ifdef ACCESSIBILITY
  4702. /* Every ChildView has a corresponding mozDocAccessible object that is doing all
  4703. the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible
  4704. object.
  4705. All ChildView needs to do is to route all accessibility calls (from the NSAccessibility APIs)
  4706. down to its object, pretending that they are the same.
  4707. */
  4708. - (id<mozAccessible>)accessible
  4709. {
  4710. if (!mGeckoChild)
  4711. return nil;
  4712. id<mozAccessible> nativeAccessible = nil;
  4713. nsAutoRetainCocoaObject kungFuDeathGrip(self);
  4714. nsCOMPtr<nsIWidget> kungFuDeathGrip2(mGeckoChild);
  4715. nsCOMPtr<nsIAccessible> accessible;
  4716. mGeckoChild->GetDocumentAccessible(getter_AddRefs(accessible));
  4717. if (!mGeckoChild)
  4718. return nil;
  4719. if (accessible)
  4720. accessible->GetNativeInterface((void**)&nativeAccessible);
  4721. #ifdef DEBUG_hakan
  4722. NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!");
  4723. #endif
  4724. return nativeAccessible;
  4725. }
  4726. /* Implementation of formal mozAccessible formal protocol (enabling mozViews
  4727. to talk to mozAccessible objects in the accessibility module). */
  4728. - (BOOL)hasRepresentedView
  4729. {
  4730. return YES;
  4731. }
  4732. - (id)representedView
  4733. {
  4734. return self;
  4735. }
  4736. - (BOOL)isRoot
  4737. {
  4738. return [[self accessible] isRoot];
  4739. }
  4740. #ifdef DEBUG
  4741. - (void)printHierarchy
  4742. {
  4743. [[self accessible] printHierarchy];
  4744. }
  4745. #endif
  4746. #pragma mark -
  4747. // general
  4748. - (BOOL)accessibilityIsIgnored
  4749. {
  4750. return [[self accessible] accessibilityIsIgnored];
  4751. }
  4752. - (id)accessibilityHitTest:(NSPoint)point
  4753. {
  4754. return [[self accessible] accessibilityHitTest:point];
  4755. }
  4756. - (id)accessibilityFocusedUIElement
  4757. {
  4758. return [[self accessible] accessibilityFocusedUIElement];
  4759. }
  4760. // actions
  4761. - (NSArray*)accessibilityActionNames
  4762. {
  4763. return [[self accessible] accessibilityActionNames];
  4764. }
  4765. - (NSString*)accessibilityActionDescription:(NSString*)action
  4766. {
  4767. return [[self accessible] accessibilityActionDescription:action];
  4768. }
  4769. - (void)accessibilityPerformAction:(NSString*)action
  4770. {
  4771. return [[self accessible] accessibilityPerformAction:action];
  4772. }
  4773. // attributes
  4774. - (NSArray*)accessibilityAttributeNames
  4775. {
  4776. return [[self accessible] accessibilityAttributeNames];
  4777. }
  4778. - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
  4779. {
  4780. return [[self accessible] accessibilityIsAttributeSettable:attribute];
  4781. }
  4782. - (id)accessibilityAttributeValue:(NSString*)attribute
  4783. {
  4784. NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  4785. id<mozAccessible> accessible = [self accessible];
  4786. // if we're the root (topmost) accessible, we need to return our native AXParent as we
  4787. // traverse outside to the hierarchy of whoever embeds us. thus, fall back on NSView's
  4788. // default implementation for this attribute.
  4789. if ([attribute isEqualToString:NSAccessibilityParentAttribute] && [accessible isRoot]) {
  4790. id parentAccessible = [super accessibilityAttributeValue:attribute];
  4791. return parentAccessible;
  4792. }
  4793. return [accessible accessibilityAttributeValue:attribute];
  4794. NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  4795. }
  4796. #endif /* ACCESSIBILITY */
  4797. @end
  4798. #pragma mark -
  4799. void
  4800. nsTSMManager::OnDestroyView(NSView<mozView>* aDestroyingView)
  4801. {
  4802. if (aDestroyingView != sComposingView)
  4803. return;
  4804. if (IsComposing()) {
  4805. CancelIME(); // XXX Might CancelIME() fail because sComposingView is being destroyed?
  4806. EndComposing();
  4807. }
  4808. }
  4809. PRBool
  4810. nsTSMManager::GetIMEOpenState()
  4811. {
  4812. return GetScriptManagerVariable(smKeyScript) != smRoman ? PR_TRUE : PR_FALSE;
  4813. }
  4814. void
  4815. nsTSMManager::StartComposing(NSView<mozView>* aComposingView)
  4816. {
  4817. if (sComposingView && sComposingView != sComposingView)
  4818. CommitIME();
  4819. sComposingView = aComposingView;
  4820. sDocumentID = ::TSMGetActiveDocument();
  4821. }
  4822. void
  4823. nsTSMManager::UpdateComposing(NSString* aComposingString)
  4824. {
  4825. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4826. if (sComposingString)
  4827. [sComposingString release];
  4828. sComposingString = [aComposingString retain];
  4829. NS_OBJC_END_TRY_ABORT_BLOCK;
  4830. }
  4831. void
  4832. nsTSMManager::EndComposing()
  4833. {
  4834. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4835. sComposingView = nsnull;
  4836. if (sComposingString) {
  4837. [sComposingString release];
  4838. sComposingString = nsnull;
  4839. }
  4840. sDocumentID = nsnull;
  4841. NS_OBJC_END_TRY_ABORT_BLOCK;
  4842. }
  4843. void
  4844. nsTSMManager::EnableIME(PRBool aEnable)
  4845. {
  4846. if (aEnable == sIsIMEEnabled)
  4847. return;
  4848. CommitIME();
  4849. sIsIMEEnabled = aEnable;
  4850. }
  4851. void
  4852. nsTSMManager::SetIMEOpenState(PRBool aOpen)
  4853. {
  4854. if (aOpen == GetIMEOpenState())
  4855. return;
  4856. CommitIME();
  4857. KeyScript(aOpen ? smKeySwapScript : smKeyRoman);
  4858. }
  4859. #define ENABLE_ROMAN_KYBDS_ONLY -23
  4860. void
  4861. nsTSMManager::SetRomanKeyboardsOnly(PRBool aRomanOnly)
  4862. {
  4863. if (aRomanOnly == sIsRomanKeyboardsOnly)
  4864. return;
  4865. CommitIME();
  4866. KeyScript(aRomanOnly ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds);
  4867. sIsRomanKeyboardsOnly = aRomanOnly;
  4868. }
  4869. void
  4870. nsTSMManager::KillComposing()
  4871. {
  4872. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4873. // Force commit the current composition
  4874. // XXX Don't use NSInputManager. Because it cannot control the non-forcused
  4875. // input manager, therefore, on deactivating a window, it does not work fine.
  4876. NS_ASSERTION(sDocumentID, "The TSMDocumentID is null");
  4877. ::FixTSMDocument(sDocumentID);
  4878. NS_OBJC_END_TRY_ABORT_BLOCK;
  4879. }
  4880. void
  4881. nsTSMManager::CommitIME()
  4882. {
  4883. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4884. if (!IsComposing())
  4885. return;
  4886. KillComposing();
  4887. if (!IsComposing())
  4888. return;
  4889. // If the composing transaction is still there, KillComposing only kills the
  4890. // composing in TSM. We also need to kill the our composing transaction too.
  4891. NSAttributedString* str =
  4892. [[NSAttributedString alloc] initWithString:sComposingString];
  4893. [sComposingView insertText:str];
  4894. [str release];
  4895. NS_OBJC_END_TRY_ABORT_BLOCK;
  4896. }
  4897. void
  4898. nsTSMManager::CancelIME()
  4899. {
  4900. NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  4901. if (!IsComposing())
  4902. return;
  4903. // For canceling the current composing, we need to ignore the param of
  4904. // insertText. But this code is ugly...
  4905. sIgnoreCommit = PR_TRUE;
  4906. KillComposing();
  4907. sIgnoreCommit = PR_FALSE;
  4908. if (!IsComposing())
  4909. return;
  4910. // If the composing transaction is still there, KillComposing only kills the
  4911. // composing in TSM. We also need to kill the our composing transaction too.
  4912. NSAttributedString* str = [[NSAttributedString alloc] initWithString:@""];
  4913. [sComposingView insertText:str];
  4914. [str release];
  4915. NS_OBJC_END_TRY_ABORT_BLOCK;
  4916. }
  4917. // Target for text services events sent as the result of calls made to
  4918. // TSMProcessRawKeyEvent() in [ChildView keyDown:] (above) when a plugin has
  4919. // the focus. The calls to TSMProcessRawKeyEvent() short-circuit Cocoa-based
  4920. // IME (which would otherwise interfere with our efforts) and allow Carbon-
  4921. // based IME to work in plugins (via the NPAPI). This strategy doesn't cause
  4922. // trouble for plugins that (like the Java Embedding Plugin) bypass the NPAPI
  4923. // to get their keyboard events and do their own Cocoa-based IME.
  4924. OSStatus PluginKeyEventsHandler(EventHandlerCallRef inHandlerRef,
  4925. EventRef inEvent, void *userData)
  4926. {
  4927. id arp = [[NSAutoreleasePool alloc] init];
  4928. TSMDocumentID activeDoc = ::TSMGetActiveDocument();
  4929. if (!activeDoc) {
  4930. [arp release];
  4931. return eventNotHandledErr;
  4932. }
  4933. ChildView *target = nil;
  4934. OSStatus status = ::TSMGetDocumentProperty(activeDoc, kFocusedChildViewTSMDocPropertyTag,
  4935. sizeof(ChildView *), nil, &target);
  4936. if (status != noErr)
  4937. target = nil;
  4938. if (!target) {
  4939. [arp release];
  4940. return eventNotHandledErr;
  4941. }
  4942. EventRef keyEvent = NULL;
  4943. status = ::GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent,
  4944. typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
  4945. if ((status != noErr) || !keyEvent) {
  4946. [arp release];
  4947. return eventNotHandledErr;
  4948. }
  4949. [target processPluginKeyEvent:keyEvent];
  4950. [arp release];
  4951. return noErr;
  4952. }
  4953. static EventHandlerRef gPluginKeyEventsHandler = NULL;
  4954. // Called from nsAppShell::Init()
  4955. void NS_InstallPluginKeyEventsHandler()
  4956. {
  4957. if (gPluginKeyEventsHandler)
  4958. return;
  4959. static const EventTypeSpec sTSMEvents[] =
  4960. { { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
  4961. ::InstallEventHandler(::GetEventDispatcherTarget(),
  4962. ::NewEventHandlerUPP(PluginKeyEventsHandler),
  4963. GetEventTypeCount(sTSMEvents),
  4964. sTSMEvents,
  4965. NULL,
  4966. &gPluginKeyEventsHandler);
  4967. }
  4968. // Called from nsAppShell::Exit()
  4969. void NS_RemovePluginKeyEventsHandler()
  4970. {
  4971. if (!gPluginKeyEventsHandler)
  4972. return;
  4973. ::RemoveEventHandler(gPluginKeyEventsHandler);
  4974. gPluginKeyEventsHandler = NULL;
  4975. }