Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / widget / src / cocoa / nsCocoaWindow.mm
blob8d0a64d24a8c9b83ca4e3094c937c6ee5c3fe66c
1 /* -*- Mode: Objective-C++; 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 Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * 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  *   Colin Barrett <cbarrett@mozilla.com>
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either the GNU General Public License Version 2 or later (the "GPL"), or
28  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 #include "nsCocoaWindow.h"
42 #include "nsObjCExceptions.h"
43 #include "nsCOMPtr.h"
44 #include "nsWidgetsCID.h"
45 #include "nsGUIEvent.h"
46 #include "nsIRollupListener.h"
47 #include "nsCocoaUtils.h"
48 #include "nsChildView.h"
49 #include "nsWindowMap.h"
50 #include "nsIAppShell.h"
51 #include "nsIAppShellService.h"
52 #include "nsIBaseWindow.h"
53 #include "nsIInterfaceRequestorUtils.h"
54 #include "nsIXULWindow.h"
55 #include "nsIPrefService.h"
56 #include "nsIPrefBranch.h"
57 #include "nsToolkit.h"
58 #include "nsPrintfCString.h"
59 #include "nsThreadUtils.h"
60 #include "nsMenuBarX.h"
61 #include "nsMenuUtilsX.h"
62 #include "nsStyleConsts.h"
63 #include "nsNativeThemeColors.h"
65 #include "gfxPlatform.h"
66 #include "lcms.h"
68 PRInt32 gXULModalLevel = 0;
69 // In principle there should be only one app-modal window at any given time.
70 // But sometimes, despite our best efforts, another window appears above the
71 // current app-modal window.  So we need to keep a linked list of app-modal
72 // windows.  (A non-sheet window that appears above an app-modal window is
73 // also made app-modal.)  See nsCocoaWindow::SetModal().
74 nsCocoaWindowList *gAppModalWindowList = NULL;
76 PRBool gCocoaWindowMethodsSwizzled = PR_FALSE;
78 // defined in nsMenuBarX.mm
79 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
81 // defined in nsChildView.mm
82 extern nsIRollupListener * gRollupListener;
83 extern nsIWidget         * gRollupWidget;
84 extern BOOL                gSomeMenuBarPainted;
86 #define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
88 NS_IMPL_ISUPPORTS_INHERITED1(nsCocoaWindow, Inherited, nsPIWidgetCocoa)
91 // A note on testing to see if your object is a sheet...
92 // |mWindowType == eWindowType_sheet| is true if your gecko nsIWidget is a sheet
93 // widget - whether or not the sheet is showing. |[mWindow isSheet]| will return
94 // true *only when the sheet is actually showing*. Choose your test wisely.
97 // roll up any popup windows
98 static void RollUpPopups()
100   if (gRollupListener && gRollupWidget)
101     gRollupListener->Rollup(nsnull);
105 nsCocoaWindow::nsCocoaWindow()
106 : mParent(nsnull)
107 , mWindow(nil)
108 , mDelegate(nil)
109 , mSheetWindowParent(nil)
110 , mPopupContentView(nil)
111 , mIsResizing(PR_FALSE)
112 , mWindowMadeHere(PR_FALSE)
113 , mSheetNeedsShow(PR_FALSE)
114 , mModal(PR_FALSE)
115 , mNumModalDescendents(0)
121 nsCocoaWindow::~nsCocoaWindow()
123   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
125   // notify the children that we're gone
126   for (nsIWidget* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
127     nsCocoaWindow* childWindow = static_cast<nsCocoaWindow*>(kid);
128     childWindow->mParent = nsnull;
129   }
131   if (mWindow && mWindowMadeHere) {
132     // we want to unhook the delegate here because we don't want events
133     // sent to it after this object has been destroyed
134     [mWindow setDelegate:nil];
135     [mWindow autorelease];
136     [mDelegate autorelease];
137   }
139   NS_IF_RELEASE(mPopupContentView);
141   // Deal with the possiblity that we're being destroyed while running modal.
142   NS_ASSERTION(!mModal, "Widget destroyed while running modal!");
143   if (mModal) {
144     --gXULModalLevel;
145     NS_ASSERTION(gXULModalLevel >= 0, "Wierdness setting modality!");
146   }
148   NS_OBJC_END_TRY_ABORT_BLOCK;
152 // Very large windows work in Cocoa, but can take a long time to
153 // process (multiple minutes), during which time the system is
154 // unresponsive and seems hung. Although it's likely that windows
155 // much larger than screen size are bugs, be conservative and only
156 // intervene if the values are so large as to hog the cpu.
157 #define SIZE_LIMIT 100000
158 static bool WindowSizeAllowed(PRInt32 aWidth, PRInt32 aHeight)
160   if (aWidth > SIZE_LIMIT) {
161     NS_ERROR(nsPrintfCString(256, "Requested Cocoa window width of %d is too much, max allowed is %d\n",
162                              aWidth, SIZE_LIMIT).get());
163     return false;
164   }
165   if (aHeight > SIZE_LIMIT) {
166     NS_ERROR(nsPrintfCString(256, "Requested Cocoa window height of %d is too much, max allowed is %d\n",
167                              aHeight, SIZE_LIMIT).get());
168     return false;
169   }
170   return true;
174 // Utility method for implementing both Create(nsIWidget ...) and
175 // Create(nsNativeWidget...)
176 nsresult nsCocoaWindow::StandardCreate(nsIWidget *aParent,
177                         const nsRect &aRect,
178                         EVENT_CALLBACK aHandleEventFunction,
179                         nsIDeviceContext *aContext,
180                         nsIAppShell *aAppShell,
181                         nsIToolkit *aToolkit,
182                         nsWidgetInitData *aInitData,
183                         nsNativeWidget aNativeWindow)
185   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
187   if (!WindowSizeAllowed(aRect.width, aRect.height))
188     return NS_ERROR_FAILURE;
190   Inherited::BaseCreate(aParent, aRect, aHandleEventFunction, aContext, aAppShell,
191                         aToolkit, aInitData);
192   
193   mParent = aParent;
194   
195   // create a window if we aren't given one, always create if this should be a popup
196   if (!aNativeWindow || (aInitData && aInitData->mWindowType == eWindowType_popup)) {
197     // decide on a window type
198     PRBool allOrDefault = PR_FALSE;
199     if (aInitData) {
200       allOrDefault = aInitData->mBorderStyle == eBorderStyle_all ||
201                      aInitData->mBorderStyle == eBorderStyle_default;
202       mWindowType = aInitData->mWindowType;
203       // if a toplevel window was requested without a titlebar, use a dialog
204       if (mWindowType == eWindowType_toplevel &&
205           (aInitData->mBorderStyle == eBorderStyle_none ||
206            !allOrDefault &&
207            !(aInitData->mBorderStyle & eBorderStyle_title)))
208         mWindowType = eWindowType_dialog;
209     }
210     else {
211       allOrDefault = PR_TRUE;
212       mWindowType = eWindowType_toplevel;
213     }
215     // Some applications like Camino use native popup windows
216     // (native context menus, native tooltips)
217     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
218     if (prefs) {
219       PRBool useNativeContextMenus;
220       nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativeContextMenus);
221       if (NS_SUCCEEDED(rv) && useNativeContextMenus && mWindowType == eWindowType_popup)
222         return NS_OK;
223     }
225     // we default to NSBorderlessWindowMask, add features if needed
226     unsigned int features = NSBorderlessWindowMask;
227     
228     // Configure the window we will create based on the window type
229     switch (mWindowType)
230     {
231       case eWindowType_invisible:
232       case eWindowType_child:
233         break;
234       case eWindowType_dialog:
235         if (aInitData) {
236           switch (aInitData->mBorderStyle)
237           {
238             case eBorderStyle_none:
239               break;
240             case eBorderStyle_default:
241               features |= NSTitledWindowMask;
242               break;
243             case eBorderStyle_all:
244               features |= NSClosableWindowMask;
245               features |= NSTitledWindowMask;
246               features |= NSResizableWindowMask;
247               features |= NSMiniaturizableWindowMask;
248               break;
249             default:
250               if (aInitData->mBorderStyle & eBorderStyle_title) {
251                 features |= NSTitledWindowMask;
252                 features |= NSMiniaturizableWindowMask;
253               }
254               if (aInitData->mBorderStyle & eBorderStyle_resizeh)
255                 features |= NSResizableWindowMask;
256               if (aInitData->mBorderStyle & eBorderStyle_close)
257                 features |= NSClosableWindowMask;
258               break;
259           }
260         }
261         else {
262           features |= NSTitledWindowMask;
263           features |= NSMiniaturizableWindowMask;
264         }
265         break;
266       case eWindowType_sheet:
267         if (aInitData) {
268           nsWindowType parentType;
269           aParent->GetWindowType(parentType);
270           if (parentType != eWindowType_invisible &&
271               aInitData->mBorderStyle & eBorderStyle_resizeh) {
272             features = NSResizableWindowMask;
273           }
274           else {
275             features = NSMiniaturizableWindowMask;
276           }
277         }
278         else {
279           features = NSMiniaturizableWindowMask;
280         }
281         features |= NSTitledWindowMask;
282         break;
283       case eWindowType_popup:
284         features |= NSBorderlessWindowMask;
285         break;
286       case eWindowType_toplevel:
287         features |= NSTitledWindowMask;
288         features |= NSMiniaturizableWindowMask;
289         if (allOrDefault || aInitData->mBorderStyle & eBorderStyle_close)
290           features |= NSClosableWindowMask;
291         if (allOrDefault || aInitData->mBorderStyle & eBorderStyle_resizeh)
292           features |= NSResizableWindowMask;
293         break;
294       default:
295         NS_ERROR("Unhandled window type!");
296         return NS_ERROR_FAILURE;
297     }
299     /* Apple's docs on NSWindow styles say that "a window's style mask should
300      * include NSTitledWindowMask if it includes any of the others [besides
301      * NSBorderlessWindowMask]".  This implies that a borderless window
302      * shouldn't have any other styles than NSBorderlessWindowMask.
303      */
304     if (!(features & NSTitledWindowMask))
305       features = NSBorderlessWindowMask;
306     
307     /* 
308      * We pass a content area rect to initialize the native Cocoa window. The
309      * content rect we give is the same size as the size we're given by gecko.
310      * The origin we're given for non-popup windows is moved down by the height
311      * of the menu bar so that an origin of (0,100) from gecko puts the window
312      * 100 pixels below the top of the available desktop area. We also move the
313      * origin down by the height of a title bar if it exists. This is so the
314      * origin that gecko gives us for the top-left of  the window turns out to
315      * be the top-left of the window we create. This is how it was done in
316      * Carbon. If it ought to be different we'll probably need to look at all
317      * the callers.
318      *
319      * Note: This means that if you put a secondary screen on top of your main
320      * screen and open a window in the top screen, it'll be incorrectly shifted
321      * down by the height of the menu bar. Same thing would happen in Carbon.
322      *
323      * Note: If you pass a rect with 0,0 for an origin, the window ends up in a
324      * weird place for some reason. This stops that without breaking popups.
325      */
326     NSRect rect = nsCocoaUtils::GeckoRectToCocoaRect(aRect);
327     
328     // compensate for difference between frame and content area height (e.g. title bar)
329     NSRect newWindowFrame = [NSWindow frameRectForContentRect:rect styleMask:features];
331     rect.origin.y -= (newWindowFrame.size.height - rect.size.height);
332     
333     if (mWindowType != eWindowType_popup)
334       rect.origin.y -= ::GetMBarHeight();
336     // NSLog(@"Top-level window being created at Cocoa rect: %f, %f, %f, %f\n",
337     //       rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
339     Class windowClass = [NSWindow class];
340     // If we have a titlebar on a top-level window, we want to be able to control the 
341     // titlebar color (for unified windows), so use the special ToolbarWindow class. 
342     // Note that we need to check the window type because we mark sheets sheets as 
343     // having titlebars.
344     if (mWindowType == eWindowType_toplevel &&
345         (features & NSTitledWindowMask))
346       windowClass = [ToolbarWindow class];
347     // If we're a popup window we need to use the PopupWindow class.
348     else if (mWindowType == eWindowType_popup)
349       windowClass = [PopupWindow class];
350     // If we're a non-popup borderless window we need to use the
351     // BorderlessWindow class.
352     else if (features == NSBorderlessWindowMask)
353       windowClass = [BorderlessWindow class];
355     // Create the window
356     mWindow = [[windowClass alloc] initWithContentRect:rect styleMask:features 
357                                    backing:NSBackingStoreBuffered defer:YES];
358     
359     if (mWindowType == eWindowType_popup) {
360       [mWindow setLevel:NSPopUpMenuWindowLevel];
361       [mWindow setHasShadow:YES];
363       // we need to make our content view a ChildView
364       mPopupContentView = new nsChildView();
365       if (mPopupContentView) {
366         NS_ADDREF(mPopupContentView);
368         nsIWidget* thisAsWidget = static_cast<nsIWidget*>(this);
369         mPopupContentView->StandardCreate(thisAsWidget, aRect, aHandleEventFunction,
370                                           aContext, aAppShell, aToolkit, nsnull, nsnull);
372         ChildView* newContentView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET);
373         [mWindow setContentView:newContentView];
374       }
375     }
376     else if (mWindowType == eWindowType_invisible) {
377       [mWindow setLevel:kCGDesktopWindowLevelKey];
378     }
380     [mWindow setBackgroundColor:[NSColor whiteColor]];
381     [mWindow setContentMinSize:NSMakeSize(60, 60)];
382     [mWindow setReleasedWhenClosed:NO];
384     // setup our notification delegate. Note that setDelegate: does NOT retain.
385     mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
386     [mWindow setDelegate:mDelegate];
387     
388     mWindowMadeHere = PR_TRUE;
389   }
390   else {
391     mWindow = (NSWindow*)aNativeWindow;
392   }
393   
394   return NS_OK;
396   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
400 // Create a nsCocoaWindow using a native window provided by the application
401 NS_IMETHODIMP nsCocoaWindow::Create(nsNativeWidget aNativeWindow,
402                       const nsRect &aRect,
403                       EVENT_CALLBACK aHandleEventFunction,
404                       nsIDeviceContext *aContext,
405                       nsIAppShell *aAppShell,
406                       nsIToolkit *aToolkit,
407                       nsWidgetInitData *aInitData)
409   return(StandardCreate(nsnull, aRect, aHandleEventFunction, aContext,
410                         aAppShell, aToolkit, aInitData, aNativeWindow));
414 NS_IMETHODIMP nsCocoaWindow::Create(nsIWidget* aParent,
415                       const nsRect &aRect,
416                       EVENT_CALLBACK aHandleEventFunction,
417                       nsIDeviceContext *aContext,
418                       nsIAppShell *aAppShell,
419                       nsIToolkit *aToolkit,
420                       nsWidgetInitData *aInitData)
422   return(StandardCreate(aParent, aRect, aHandleEventFunction, aContext,
423                         aAppShell, aToolkit, aInitData, nsnull));
427 NS_IMETHODIMP nsCocoaWindow::Destroy()
429   if (mPopupContentView)
430     mPopupContentView->Destroy();
432   nsBaseWidget::OnDestroy();
433   nsBaseWidget::Destroy();
435   return NS_OK;
439 nsIWidget* nsCocoaWindow::GetSheetWindowParent(void)
441   if (mWindowType != eWindowType_sheet)
442     return nsnull;
443   nsCocoaWindow *parent = static_cast<nsCocoaWindow*>(mParent);
444   while (parent && (parent->mWindowType == eWindowType_sheet))
445     parent = static_cast<nsCocoaWindow*>(parent->mParent);
446   return parent;
450 void* nsCocoaWindow::GetNativeData(PRUint32 aDataType)
452   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
454   void* retVal = nsnull;
455   
456   switch (aDataType) {
457     // to emulate how windows works, we always have to return a NSView
458     // for NS_NATIVE_WIDGET
459     case NS_NATIVE_WIDGET:
460     case NS_NATIVE_DISPLAY:
461       retVal = [mWindow contentView];
462       break;
463       
464     case NS_NATIVE_WINDOW:
465       retVal = mWindow;
466       break;
467       
468     case NS_NATIVE_GRAPHIC:
469       // There isn't anything that makes sense to return here,
470       // and it doesn't matter so just return nsnull.
471       NS_ASSERTION(0, "Requesting NS_NATIVE_GRAPHIC on a top-level window!");
472       break;
473   }
475   return retVal;
477   NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
481 NS_IMETHODIMP nsCocoaWindow::IsVisible(PRBool & aState)
483   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
485   aState = ([mWindow isVisible] || mSheetNeedsShow);
486   return NS_OK;
488   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
492 NS_IMETHODIMP nsCocoaWindow::SetModal(PRBool aState)
494   mModal = aState;
495   nsCocoaWindow *aParent = static_cast<nsCocoaWindow*>(mParent);
496   if (aState) {
497     ++gXULModalLevel;
498     // When a non-sheet window gets "set modal", make the window(s) that it
499     // appears over behave as they should.  We can't rely on native methods to
500     // do this, for the following reason:  The OS runs modal non-sheet windows
501     // in an event loop (using [NSApplication runModalForWindow:] or similar
502     // methods) that's incompatible with the modal event loop in nsXULWindow::
503     // ShowModal() (each of these event loops is "exclusive", and can't run at
504     // the same time as other (similar) event loops).
505     if (mWindowType != eWindowType_sheet) {
506       while (aParent) {
507         if (aParent->mNumModalDescendents++ == 0) {
508           NSWindow *aWindow = aParent->GetCocoaWindow();
509           if (aParent->mWindowType != eWindowType_invisible) {
510             [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:NO];
511             [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:NO];
512             [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO];
513           }
514         }
515         aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
516       }
517       [mWindow setLevel:NSModalPanelWindowLevel];
518       nsCocoaWindowList *windowList = new nsCocoaWindowList;
519       if (windowList) {
520         windowList->window = this; // Don't ADDREF
521         windowList->prev = gAppModalWindowList;
522         gAppModalWindowList = windowList;
523       }
524     }
525   }
526   else {
527     --gXULModalLevel;
528     NS_ASSERTION(gXULModalLevel >= 0, "Mismatched call to nsCocoaWindow::SetModal(PR_FALSE)!");
529     if (mWindowType != eWindowType_sheet) {
530       while (aParent) {
531         if (--aParent->mNumModalDescendents == 0) {
532           NSWindow *aWindow = aParent->GetCocoaWindow();
533           if (aParent->mWindowType != eWindowType_invisible) {
534             [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:YES];
535             [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:YES];
536             [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:YES];
537           }
538         }
539         NS_ASSERTION(aParent->mNumModalDescendents >= 0, "Widget hierarchy changed while modal!");
540         aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
541       }
542       if (gAppModalWindowList) {
543         NS_ASSERTION(gAppModalWindowList->window == this, "Widget hierarchy changed while modal!");
544         nsCocoaWindowList *saved = gAppModalWindowList;
545         gAppModalWindowList = gAppModalWindowList->prev;
546         delete saved; // "window" not ADDREFed
547       }
548       if (mWindowType == eWindowType_popup)
549         [mWindow setLevel:NSPopUpMenuWindowLevel];
550       else
551         [mWindow setLevel:NSNormalWindowLevel];
552     }
553   }
554   return NS_OK;
558 // Hide or show this window
559 NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
561   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
563   // We need to re-execute sometimes in order to bring already-visible
564   // windows forward.
565   if (!mSheetNeedsShow && !bState && ![mWindow isVisible])
566     return NS_OK;
568   nsIWidget* parentWidget = mParent;
569   nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget));
570   NSWindow* nativeParentWindow = (parentWidget) ?
571     (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) : nil;
573   if (bState && !mBounds.IsEmpty()) {
574     if (mWindowType == eWindowType_sheet) {
575       // bail if no parent window (its basically what we do in Carbon)
576       if (!nativeParentWindow || !piParentWidget)
577         return NS_ERROR_FAILURE;
579       NSWindow* topNonSheetWindow = nativeParentWindow;
580       
581       // If this sheet is the child of another sheet, hide the parent so that
582       // this sheet can be displayed. Leave the parent mSheetNeedsShow alone,
583       // that is only used to handle sibling sheet contention. The parent will
584       // return once there are no more child sheets.
585       PRBool parentIsSheet = PR_FALSE;
586       if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
587           parentIsSheet) {
588         piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
589         [NSApp endSheet:nativeParentWindow];
590         [nativeParentWindow setAcceptsMouseMovedEvents:NO];
591       }
593       nsCocoaWindow* sheetShown = nsnull;
594       if (NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_TRUE, &sheetShown)) &&
595           (!sheetShown || sheetShown == this)) {
596         // If this sheet is already the sheet actually being shown, don't
597         // tell it to show again. Otherwise the number of calls to
598         // [NSApp beginSheet...] won't match up with [NSApp endSheet...].
599         if (![mWindow isVisible]) {
600           mSheetNeedsShow = PR_FALSE;
601           mSheetWindowParent = topNonSheetWindow;
602           // Only set contextInfo if our parent isn't a sheet.
603           NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
604           [TopLevelWindowData deactivateInWindow:mSheetWindowParent];
605           [mWindow setAcceptsMouseMovedEvents:YES];
606           [NSApp beginSheet:mWindow
607              modalForWindow:mSheetWindowParent
608               modalDelegate:mDelegate
609              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
610                 contextInfo:contextInfo];
611           [TopLevelWindowData activateInWindow:mWindow];
612           SendSetZLevelEvent();
613         }
614       }
615       else {
616         // A sibling of this sheet is active, don't show this sheet yet.
617         // When the active sheet hides, its brothers and sisters that have
618         // mSheetNeedsShow set will have their opportunities to display.
619         mSheetNeedsShow = PR_TRUE;
620       }
621     }
622     else if (mWindowType == eWindowType_popup) {
623       // If a popup window is shown after being hidden, it needs to be "reset"
624       // for it to receive any mouse events aside from mouse-moved events
625       // (because it was removed from the "window cache" when it was hidden
626       // -- see below).  Setting the window number to -1 and then back to its
627       // original value seems to accomplish this.  The idea was "borrowed"
628       // from the Java Embedding Plugin.
629       int windowNumber = [mWindow windowNumber];
630       [mWindow _setWindowNumber:-1];
631       [mWindow _setWindowNumber:windowNumber];
632       [mWindow setAcceptsMouseMovedEvents:YES];
633       [mWindow orderFront:nil];
634       SendSetZLevelEvent();
635       // If our popup window is a non-native context menu, tell the OS (and
636       // other programs) that a menu has opened.  This is how the OS knows to
637       // close other programs' context menus when ours open.
638       if ([mWindow isKindOfClass:[PopupWindow class]] &&
639           [(PopupWindow*) mWindow isContextMenu]) {
640         [[NSDistributedNotificationCenter defaultCenter]
641           postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
642                         object:@"org.mozilla.gecko.PopupWindow"];
643       }
645       // if a parent was supplied, set its child window. This will cause the
646       // child window to appear above the parent and move when the parent
647       // does. Setting this needs to happen after the _setWindowNumber calls
648       // above, otherwise the window doesn't focus properly.
649       if (nativeParentWindow)
650         [nativeParentWindow addChildWindow:mWindow
651                             ordered:NSWindowAbove];
652     }
653     else {
654       [mWindow setAcceptsMouseMovedEvents:YES];
655       [mWindow makeKeyAndOrderFront:nil];
656       SendSetZLevelEvent();
657     }
658   }
659   else {
660     // roll up any popups if a top-level window is going away
661     if (mWindowType == eWindowType_toplevel)
662       RollUpPopups();
664     // now get rid of the window/sheet
665     if (mWindowType == eWindowType_sheet) {
666       if (mSheetNeedsShow) {
667         // This is an attempt to hide a sheet that never had a chance to
668         // be shown. There's nothing to do other than make sure that it
669         // won't show.
670         mSheetNeedsShow = PR_FALSE;
671       }
672       else {
673         // get sheet's parent *before* hiding the sheet (which breaks the linkage)
674         NSWindow* sheetParent = mSheetWindowParent;
675         
676         // hide the sheet
677         [NSApp endSheet:mWindow];
678         
679         [mWindow setAcceptsMouseMovedEvents:NO];
681         [TopLevelWindowData deactivateInWindow:mWindow];
683         nsCocoaWindow* siblingSheetToShow = nsnull;
684         PRBool parentIsSheet = PR_FALSE;
686         if (nativeParentWindow && piParentWidget &&
687             NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_FALSE, &siblingSheetToShow)) &&
688             siblingSheetToShow) {
689           // First, give sibling sheets an opportunity to show.
690           siblingSheetToShow->Show(PR_TRUE);
691         }
692         else if (nativeParentWindow && piParentWidget &&
693                  NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
694                  parentIsSheet) {
695           // Only set contextInfo if the parent of the parent sheet we're about
696           // to restore isn't itself a sheet.
697           NSWindow* contextInfo = sheetParent;
698           nsIWidget* grandparentWidget = nil;
699           if (NS_SUCCEEDED(piParentWidget->GetRealParent(&grandparentWidget)) && grandparentWidget) {
700             nsCOMPtr<nsPIWidgetCocoa> piGrandparentWidget(do_QueryInterface(grandparentWidget));
701             PRBool grandparentIsSheet = PR_FALSE;
702             if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) &&
703                 grandparentIsSheet) {
704                 contextInfo = nil;
705             }
706           }
707           // If there are no sibling sheets, but the parent is a sheet, restore
708           // it.  It wasn't sent any deactivate events when it was hidden, so
709           // don't call through Show, just let the OS put it back up.
710           [nativeParentWindow setAcceptsMouseMovedEvents:YES];
711           [NSApp beginSheet:nativeParentWindow
712              modalForWindow:sheetParent
713               modalDelegate:[nativeParentWindow delegate]
714              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
715                 contextInfo:contextInfo];
716         }
717         else {
718           // Sheet, that was hard.  No more siblings or parents, going back
719           // to a real window.
720           [sheetParent makeKeyAndOrderFront:nil];
721           [sheetParent setAcceptsMouseMovedEvents:YES];
722         }
723         SendSetZLevelEvent();
724       }
725     }
726     else {
727       // If the window is a popup window with a parent window we need to
728       // unhook it here before ordering it out. When you order out the child
729       // of a window it hides the parent window.
730       if (mWindowType == eWindowType_popup && nativeParentWindow)
731         [nativeParentWindow removeChildWindow:mWindow];
733       [mWindow orderOut:nil];
734       // Unless it's explicitly removed from NSApp's "window cache", a popup
735       // window will keep receiving mouse-moved events even after it's been
736       // "ordered out" (instead of the browser window that was underneath it,
737       // until you click on that window).  This is bmo bug 378645, but it's
738       // surely an Apple bug.  The "window cache" is an undocumented subsystem,
739       // all of whose methods are included in the NSWindowCache category of
740       // the NSApplication class (in header files generated using class-dump).
741       // This workaround was "borrowed" from the Java Embedding Plugin (which
742       // uses it for a different purpose).
743       if (mWindowType == eWindowType_popup) {
744         [NSApp _removeWindowFromCache:mWindow];
745         // Apple's focus ring APIs sometimes clip themselves when they draw under
746         // other windows. Redraw the window that was likely under the popup to
747         // get focus rings to draw correctly. Sometimes the window is not properly
748         // the parent of the popup, so we can't just tell the parent to redraw.
749         // We only have this problem on 10.4. See bug 417124.
750         if (!nsToolkit::OnLeopardOrLater()) {
751           NSWindow* keyWindow = [NSApp keyWindow];
752           if (keyWindow)
753             [keyWindow display];
754           NSWindow* mainWindow = [NSApp mainWindow];
755           if (mainWindow && mainWindow != keyWindow)
756             [mainWindow display];          
757         }
758       }
760       // it's very important to turn off mouse moved events when hiding a window, otherwise
761       // the windows' tracking rects will interfere with each other. (bug 356528)
762       [mWindow setAcceptsMouseMovedEvents:NO];
764       // If our popup window is a non-native context menu, tell the OS (and
765       // other programs) that a menu has closed.
766       if ([mWindow isKindOfClass:[PopupWindow class]] &&
767           [(PopupWindow*) mWindow isContextMenu]) {
768         [[NSDistributedNotificationCenter defaultCenter]
769           postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification"
770                         object:@"org.mozilla.gecko.PopupWindow"];
771       }
772     }
773   }
774   
775   if (mPopupContentView)
776       mPopupContentView->Show(bState);
778   return NS_OK;
780   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
784 void nsCocoaWindow::MakeBackgroundTransparent(PRBool aTransparent)
786   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
788   BOOL currentTransparency = ![mWindow isOpaque];
789   if (aTransparent != currentTransparency) {
790     [mWindow setOpaque:!aTransparent];
791     [mWindow setBackgroundColor:(aTransparent ? [NSColor clearColor] : [NSColor whiteColor])];
792   }
794   NS_OBJC_END_TRY_ABORT_BLOCK;
798 nsTransparencyMode nsCocoaWindow::GetTransparencyMode()
800   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
802   return [mWindow isOpaque] ? eTransparencyOpaque : eTransparencyTransparent;
804   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque);
808 // This is called from nsMenuPopupFrame when making a popup transparent.
809 // For other window types, nsChildView::SetTransparencyMode is used.
810 void nsCocoaWindow::SetTransparencyMode(nsTransparencyMode aMode)
812   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
814   BOOL isTransparent = aMode == eTransparencyTransparent;
816   BOOL currentTransparency = ![mWindow isOpaque];
817   if (isTransparent != currentTransparency) {
818     // Take care of window transparency
819     MakeBackgroundTransparent(isTransparent);
820     // Make sure our content view is also transparent
821     if (mPopupContentView) {
822       ChildView *childView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET);
823       if (childView) {
824         [childView setTransparent:isTransparent];
825       }
826     }
827   }
829   NS_OBJC_END_TRY_ABORT_BLOCK;
833 NS_METHOD nsCocoaWindow::AddEventListener(nsIEventListener * aListener)
835   nsBaseWidget::AddEventListener(aListener);
837   if (mPopupContentView)
838     mPopupContentView->AddEventListener(aListener);
840   return NS_OK;
844 NS_IMETHODIMP nsCocoaWindow::Enable(PRBool aState)
846   return NS_OK;
850 NS_IMETHODIMP nsCocoaWindow::IsEnabled(PRBool *aState)
852   if (aState)
853     *aState = PR_TRUE;
854   return NS_OK;
858 NS_IMETHODIMP nsCocoaWindow::ConstrainPosition(PRBool aAllowSlop,
859                                                PRInt32 *aX, PRInt32 *aY)
861   return NS_OK;
865 NS_IMETHODIMP nsCocoaWindow::Move(PRInt32 aX, PRInt32 aY)
867   if (!mWindow || (mBounds.x == aX && mBounds.y == aY))
868     return NS_OK;
870   // The point we have is in Gecko coordinates (origin top-left). Convert
871   // it to Cocoa ones (origin bottom-left).
872   NSPoint coord = {aX, nsCocoaUtils::FlippedScreenY(aY)};
873   [mWindow setFrameTopLeftPoint:coord];
875   return NS_OK;
879 // Position the window behind the given window
880 NS_METHOD nsCocoaWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
881                                      nsIWidget *aWidget, PRBool aActivate)
883   return NS_OK;
887 // Note bug 278777, we need to update state when the window is unminimized
888 // from the dock by users.
889 NS_METHOD nsCocoaWindow::SetSizeMode(PRInt32 aMode)
891   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
893   PRInt32 previousMode;
894   nsBaseWidget::GetSizeMode(&previousMode);
896   nsresult rv = nsBaseWidget::SetSizeMode(aMode);
897   NS_ENSURE_SUCCESS(rv, rv);
899   if (aMode == nsSizeMode_Normal) {
900     if ([mWindow isMiniaturized])
901       [mWindow deminiaturize:nil];
902     else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
903       [mWindow zoom:nil];
904   }
905   else if (aMode == nsSizeMode_Minimized) {
906     if (![mWindow isMiniaturized])
907       [mWindow miniaturize:nil];
908   }
909   else if (aMode == nsSizeMode_Maximized) {
910     if ([mWindow isMiniaturized])
911       [mWindow deminiaturize:nil];
912     if (![mWindow isZoomed])
913       [mWindow zoom:nil];
914   }
916   return NS_OK;
918   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
922 NS_IMETHODIMP nsCocoaWindow::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
924   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
926   if (!WindowSizeAllowed(aWidth, aHeight))
927     return NS_ERROR_FAILURE;
929   nsRect windowBounds(nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]));
930   BOOL isMoving = (windowBounds.x != aX || windowBounds.y != aY);
931   BOOL isResizing = (windowBounds.width != aWidth || windowBounds.height != aHeight);
933   if (IsResizing() || !mWindow || (!isMoving && !isResizing))
934     return NS_OK;
936   nsRect geckoRect(aX, aY, aWidth, aHeight);
937   NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(geckoRect);
939   // We have to report the size event -first-, to make sure that content
940   // repositions itself.  Cocoa views are anchored at the bottom left,
941   // so if we don't do this our child view will end up being stuck in the
942   // wrong place during a resize.
943   if (isResizing)
944     ReportSizeEvent(&newFrame);
946   StartResizing();
947   // We ignore aRepaint -- we have to call display:YES, otherwise the
948   // title bar doesn't immediately get repainted and is displayed in
949   // the wrong place, leading to a visual jump.
950   [mWindow setFrame:newFrame display:YES];
951   StopResizing();
953   // now, check whether we got the frame that we wanted
954   NSRect actualFrame = [mWindow frame];
955   if (newFrame.size.width != actualFrame.size.width || newFrame.size.height != actualFrame.size.height) {
956     // We didn't; the window must have been too big or otherwise invalid.
957     // Report -another- resize in this case, to make sure things are in
958     // the right place.  This will cause some visual jitter, but
959     // shouldn't happen often.
960     ReportSizeEvent();
961   }
963   return NS_OK;
965   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
969 NS_IMETHODIMP nsCocoaWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
971   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
973   if (!WindowSizeAllowed(aWidth, aHeight))
974     return NS_ERROR_FAILURE;
976   nsRect windowBounds(nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]));
977   return Resize(windowBounds.x, windowBounds.y, aWidth, aHeight, aRepaint);
979   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
983 NS_IMETHODIMP nsCocoaWindow::GetScreenBounds(nsRect &aRect)
985   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
987   nsRect windowFrame = nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]);
988   aRect.x = windowFrame.x;
989   aRect.y = windowFrame.y;
990   aRect.width = windowFrame.width;
991   aRect.height = windowFrame.height;
992   // printf("GetScreenBounds: output: %d,%d,%d,%d\n", aRect.x, aRect.y, aRect.width, aRect.height);
993   return NS_OK;
995   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
999 PRBool nsCocoaWindow::OnPaint(nsPaintEvent &event)
1001   return PR_TRUE; // don't dispatch the update event
1005 NS_IMETHODIMP nsCocoaWindow::SetTitle(const nsAString& aTitle)
1007   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1009   const nsString& strTitle = PromiseFlatString(aTitle);
1010   NSString* title = [NSString stringWithCharacters:strTitle.get() length:strTitle.Length()];
1011   [mWindow setTitle:title];
1013   return NS_OK;
1015   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1019 NS_IMETHODIMP nsCocoaWindow::Invalidate(const nsRect & aRect, PRBool aIsSynchronous)
1021   if (mPopupContentView)
1022     return mPopupContentView->Invalidate(aRect, aIsSynchronous);
1024   return NS_OK;
1028 NS_IMETHODIMP nsCocoaWindow::Invalidate(PRBool aIsSynchronous)
1030   if (mPopupContentView)
1031     return mPopupContentView->Invalidate(aIsSynchronous);
1033   return NS_OK;
1037 NS_IMETHODIMP nsCocoaWindow::Update()
1039   if (mPopupContentView)
1040     return mPopupContentView->Update();
1042   return NS_OK;
1046 // Pass notification of some drag event to Gecko
1048 // The drag manager has let us know that something related to a drag has
1049 // occurred in this window. It could be any number of things, ranging from 
1050 // a drop, to a drag enter/leave, or a drag over event. The actual event
1051 // is passed in |aMessage| and is passed along to our event hanlder so Gecko
1052 // knows about it.
1053 PRBool nsCocoaWindow::DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers)
1055   return PR_FALSE;
1059 NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent()
1061   nsZLevelEvent event(PR_TRUE, NS_SETZLEVEL, this);
1063   event.refPoint.x = mBounds.x;
1064   event.refPoint.y = mBounds.y;
1065   event.time = PR_IntervalNow();
1067   event.mImmediate = PR_TRUE;
1069   nsEventStatus status = nsEventStatus_eIgnore;
1070   DispatchEvent(&event, status);
1072   return NS_OK;
1076 NS_IMETHODIMP nsCocoaWindow::GetChildSheet(PRBool aShown, nsCocoaWindow** _retval)
1078   nsIWidget* child = GetFirstChild();
1080   while (child) {
1081     nsWindowType type;
1082     if (NS_SUCCEEDED(child->GetWindowType(type)) && type == eWindowType_sheet) {
1083       // if it's a sheet, it must be an nsCocoaWindow
1084       nsCocoaWindow* cocoaWindow = static_cast<nsCocoaWindow*>(child);
1085       if ((aShown && [cocoaWindow->mWindow isVisible]) ||
1086           (!aShown && cocoaWindow->mSheetNeedsShow)) {
1087         *_retval = cocoaWindow;
1088         return NS_OK;
1089       }
1090     }
1091     child = child->GetNextSibling();
1092   }
1094   *_retval = nsnull;
1096   return NS_OK;
1100 NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent)
1102   *parent = mParent;
1103   return NS_OK;
1107 NS_IMETHODIMP nsCocoaWindow::GetIsSheet(PRBool* isSheet)
1109   mWindowType == eWindowType_sheet ? *isSheet = PR_TRUE : *isSheet = PR_FALSE;
1110   return NS_OK;
1114 NS_IMETHODIMP nsCocoaWindow::GetSheetWindowParent(NSWindow** sheetWindowParent)
1116   *sheetWindowParent = mSheetWindowParent;
1117   return NS_OK;
1121 NS_IMETHODIMP nsCocoaWindow::ResetInputState()
1123   return NS_OK;
1127 // Invokes callback and ProcessEvent methods on Event Listener object
1128 NS_IMETHODIMP 
1129 nsCocoaWindow::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
1131   aStatus = nsEventStatus_eIgnore;
1133   nsIWidget* aWidget = event->widget;
1134   NS_IF_ADDREF(aWidget);
1136   if (mEventCallback)
1137     aStatus = (*mEventCallback)(event);
1139   // Dispatch to event listener if event was not consumed
1140   if (mEventListener && aStatus != nsEventStatus_eConsumeNoDefault)
1141     aStatus = mEventListener->ProcessEvent(*event);
1143   NS_IF_RELEASE(aWidget);
1145   return NS_OK;
1149 void
1150 nsCocoaWindow::DispatchSizeModeEvent(nsSizeMode aSizeMode)
1152   nsSizeModeEvent event(PR_TRUE, NS_SIZEMODE, this);
1153   event.mSizeMode = aSizeMode;
1154   event.time = PR_IntervalNow();
1156   nsEventStatus status = nsEventStatus_eIgnore;
1157   DispatchEvent(&event, status);
1160 void
1161 nsCocoaWindow::ReportSizeEvent(NSRect *r)
1163   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1165   NSRect windowFrame;
1166   if (r)
1167     windowFrame = [mWindow contentRectForFrameRect:(*r)];
1168   else
1169     windowFrame = [mWindow contentRectForFrameRect:[mWindow frame]];
1170   mBounds.width  = nscoord(windowFrame.size.width);
1171   mBounds.height = nscoord(windowFrame.size.height);
1173   nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
1174   sizeEvent.time = PR_IntervalNow();
1176   sizeEvent.windowSize = &mBounds;
1177   sizeEvent.mWinWidth  = mBounds.width;
1178   sizeEvent.mWinHeight = mBounds.height;
1180   nsEventStatus status = nsEventStatus_eIgnore;
1181   DispatchEvent(&sizeEvent, status);
1183   NS_OBJC_END_TRY_ABORT_BLOCK;
1187 NS_IMETHODIMP nsCocoaWindow::SetMenuBar(void *aMenuBar)
1189   if (mMenuBar)
1190     mMenuBar->SetParent(nsnull);
1191   mMenuBar = static_cast<nsMenuBarX*>(aMenuBar);
1192   
1193   // We paint the hidden window menu bar if no other menu bar has been painted
1194   // yet so that some reasonable menu bar is displayed when the app starts up.
1195   if (!gSomeMenuBarPainted && mMenuBar && (nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar))
1196     mMenuBar->Paint();
1197   
1198   return NS_OK;
1202 NS_IMETHODIMP nsCocoaWindow::SetFocus(PRBool aState)
1204   if (mPopupContentView)
1205     mPopupContentView->SetFocus(aState);
1207   return NS_OK;
1211 NS_IMETHODIMP nsCocoaWindow::ShowMenuBar(PRBool aShow)
1213   return NS_ERROR_FAILURE;
1217 NS_IMETHODIMP nsCocoaWindow::WidgetToScreen(const nsRect& aOldRect, nsRect& aNewRect)
1219   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1221   nsRect r = nsCocoaUtils::CocoaRectToGeckoRect([mWindow contentRectForFrameRect:[mWindow frame]]);
1223   aNewRect.x = r.x + aOldRect.x;
1224   aNewRect.y = r.y + aOldRect.y;
1225   aNewRect.width = aOldRect.width;
1226   aNewRect.height = aOldRect.height;
1228   return NS_OK;
1230   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1234 NS_IMETHODIMP nsCocoaWindow::ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect)
1236   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1238   nsRect r = nsCocoaUtils::CocoaRectToGeckoRect([mWindow contentRectForFrameRect:[mWindow frame]]);
1240   aNewRect.x = aOldRect.x - r.x;
1241   aNewRect.y = aOldRect.y - r.y;
1242   aNewRect.width = aOldRect.width;
1243   aNewRect.height = aOldRect.height;
1245   return NS_OK;
1247   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1251 nsMenuBarX* nsCocoaWindow::GetMenuBar()
1253   return mMenuBar;
1257 NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener * aListener, 
1258                                                  PRBool aDoCapture, 
1259                                                  PRBool aConsumeRollupEvent)
1261   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1263   NS_IF_RELEASE(gRollupListener);
1264   NS_IF_RELEASE(gRollupWidget);
1265   
1266   if (aDoCapture) {
1267     gRollupListener = aListener;
1268     NS_ADDREF(aListener);
1269     gRollupWidget = this;
1270     NS_ADDREF(this);
1271     // Sometimes more than one popup window can be visible at the same time
1272     // (e.g. nested non-native context menus, or the test case (attachment
1273     // 276885) for bmo bug 392389, which displays a non-native combo-box in
1274     // a non-native popup window).  In these cases the "active" popup window
1275     // (the one that corresponds to the current gRollupWidget) should be the
1276     // topmost -- the (nested) context menu the mouse is currently over, or
1277     // the combo-box's drop-down list (when it's displayed).  But (among
1278     // windows that have the same "level") OS X makes topmost the window that
1279     // last received a mouse-down event, which may be incorrect (in the combo-
1280     // box case, it makes topmost the window containing the combo-box).  So
1281     // here we fiddle with a non-native popup window's level to make sure the
1282     // "active" one is always above any other non-native popup windows that
1283     // may be visible.
1284     if (mWindow && (mWindowType == eWindowType_popup))
1285       [mWindow setLevel:NSPopUpMenuWindowLevel];
1286   } else {
1287     if (mWindow && (mWindowType == eWindowType_popup))
1288       [mWindow setLevel:NSModalPanelWindowLevel];
1289   }
1290   
1291   return NS_OK;
1293   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1297 NS_IMETHODIMP nsCocoaWindow::GetAttention(PRInt32 aCycleCount)
1299   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1301   [NSApp requestUserAttention:NSInformationalRequest];
1302   return NS_OK;
1304   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1308 NS_IMETHODIMP nsCocoaWindow::SetWindowShadowStyle(PRInt32 aStyle)
1310   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1312   if ([mWindow hasShadow] != (aStyle != NS_STYLE_WINDOW_SHADOW_NONE))
1313     [mWindow setHasShadow:(aStyle != NS_STYLE_WINDOW_SHADOW_NONE)];
1314   return NS_OK;
1316   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1320 NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, PRBool aActive)
1322   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1324   // If our cocoa window isn't a ToolbarWindow, something is wrong.
1325   if (![mWindow isKindOfClass:[ToolbarWindow class]]) {
1326     // Don't output a warning for the hidden window.
1327     NS_WARN_IF_FALSE(SameCOMIdentity(nsCocoaUtils::GetHiddenWindowWidget(), (nsIWidget*)this),
1328                      "Calling SetWindowTitlebarColor on window that isn't of the ToolbarWindow class.");
1329     return NS_ERROR_FAILURE;
1330   }
1332   // If they pass a color with a complete transparent alpha component, use the
1333   // native titlebar appearance.
1334   if (NS_GET_A(aColor) == 0) {
1335     [(ToolbarWindow*)mWindow setTitlebarColor:nil forActiveWindow:(BOOL)aActive]; 
1336   } else {
1337     // Transform from sRGBA to monitor RGBA. This seems like it would make trying
1338     // to match the system appearance lame, so probably we just shouldn't color 
1339     // correct chrome.
1340     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
1341       cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBATransform();
1342       if (transform) {
1343         PRUint8 color[3];
1344         color[0] = NS_GET_R(aColor);
1345         color[1] = NS_GET_G(aColor);
1346         color[2] = NS_GET_B(aColor);
1347         cmsDoTransform(transform, color, color, 1);
1348         aColor = NS_RGB(color[0], color[1], color[2]);
1349       }
1350     }
1352     [(ToolbarWindow*)mWindow setTitlebarColor:[NSColor colorWithDeviceRed:NS_GET_R(aColor)/255.0
1353                                                                     green:NS_GET_G(aColor)/255.0
1354                                                                      blue:NS_GET_B(aColor)/255.0
1355                                                                     alpha:NS_GET_A(aColor)/255.0]
1356                               forActiveWindow:(BOOL)aActive];
1357   }
1358   return NS_OK;
1360   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1364 gfxASurface* nsCocoaWindow::GetThebesSurface()
1366   if (mPopupContentView)
1367     return mPopupContentView->GetThebesSurface();
1368   return nsnull;
1372 NS_IMETHODIMP nsCocoaWindow::BeginSecureKeyboardInput()
1374   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1376   nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
1377   if (NS_SUCCEEDED(rv))
1378     ::EnableSecureEventInput();
1379   return rv;
1381   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1385 NS_IMETHODIMP nsCocoaWindow::EndSecureKeyboardInput()
1387   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1389   nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
1390   if (NS_SUCCEEDED(rv))
1391     ::DisableSecureEventInput();
1392   return rv;
1394   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1398 // Callback used by the default titlebar and toolbar shading.
1399 // *aIn == 0 at the top of the titlebar/toolbar, *aIn == 1 at the bottom
1400 /* static */ void
1401 nsCocoaWindow::UnifiedShading(void* aInfo, const float* aIn, float* aOut)
1403   UnifiedGradientInfo* info = (UnifiedGradientInfo*)aInfo;
1404   // The gradient percentage at the bottom of the titlebar / top of the toolbar
1405   float start = info->titlebarHeight / (info->titlebarHeight + info->toolbarHeight - 1);
1406   const float startGrey = NativeGreyColorAsFloat(headerStartGrey, info->windowIsMain);
1407   const float endGrey = NativeGreyColorAsFloat(headerEndGrey, info->windowIsMain);
1408   // *aIn is the gradient percentage of the titlebar or toolbar gradient,
1409   // a is the gradient percentage of the whole unified gradient.
1410   float a = info->drawTitlebar ? *aIn * start : start + *aIn * (1 - start);
1411   float result = (1.0f - a) * startGrey + a * endGrey;
1412   aOut[0] = result;
1413   aOut[1] = result;
1414   aOut[2] = result;
1415   aOut[3] = 1.0f;
1419 @implementation WindowDelegate
1422 // We try to find a gecko menu bar to paint. If one does not exist, just paint
1423 // the application menu by itself so that a window doesn't have some other
1424 // window's menu bar.
1425 + (void)paintMenubarForWindow:(NSWindow*)aWindow
1427   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1429   // make sure we only act on windows that have this kind of
1430   // object as a delegate
1431   id windowDelegate = [aWindow delegate];
1432   if ([windowDelegate class] != [self class])
1433     return;
1435   nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget];
1436   NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!");
1437   
1438   nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar();
1439   if (geckoMenuBar) {
1440     geckoMenuBar->Paint();
1441   }
1442   else {
1443     // sometimes we don't have a native application menu early in launching
1444     if (!sApplicationMenu)
1445       return;
1447     NSMenu* mainMenu = [NSApp mainMenu];
1448     NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
1450     // Create a new menu bar.
1451     // We create a GeckoNSMenu because all menu bar NSMenu objects should use that subclass for
1452     // key handling reasons.
1453     GeckoNSMenu* newMenuBar = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
1455     // move the application menu from the existing menu bar to the new one
1456     NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
1457     [mainMenu removeItemAtIndex:0];
1458     [newMenuBar insertItem:firstMenuItem atIndex:0];
1459     [firstMenuItem release];
1461     // set our new menu bar as the main menu
1462     [NSApp setMainMenu:newMenuBar];
1463     [newMenuBar release];
1464   }
1466   NS_OBJC_END_TRY_ABORT_BLOCK;
1470 - (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind
1472   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1474   [super init];
1475   mGeckoWindow = geckoWind;
1476   mToplevelActiveState = PR_FALSE;
1477   return self;
1479   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1483 - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
1485   RollUpPopups();
1486   
1487   return proposedFrameSize;
1491 - (void)windowDidResize:(NSNotification *)aNotification
1493   if (!mGeckoWindow || mGeckoWindow->IsResizing())
1494     return;
1496   mGeckoWindow->ReportSizeEvent();
1500 - (void)windowDidBecomeMain:(NSNotification *)aNotification
1502   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1504   RollUpPopups();
1506   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
1507   // app modally. If one of those is up then we want it to retain its menu bar.
1508   if ([NSApp _isRunningAppModal])
1509     return;
1510   NSWindow* window = [aNotification object];
1511   if (window)
1512     [WindowDelegate paintMenubarForWindow:window];
1514   NS_OBJC_END_TRY_ABORT_BLOCK;
1518 - (void)windowDidResignMain:(NSNotification *)aNotification
1520   RollUpPopups();
1522   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
1523   // app modally. If one of those is up then we want it to retain its menu bar.
1524   if ([NSApp _isRunningAppModal])
1525     return;
1526   nsRefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
1527   if (hiddenWindowMenuBar) {
1528     // printf("painting hidden window menu bar due to window losing main status\n");
1529     hiddenWindowMenuBar->Paint();
1530   }
1534 - (void)windowDidBecomeKey:(NSNotification *)aNotification
1536   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1538   NSWindow* window = [aNotification object];
1539   if ([window isSheet])
1540     [WindowDelegate paintMenubarForWindow:window];
1542   NS_OBJC_END_TRY_ABORT_BLOCK;
1546 - (void)windowDidResignKey:(NSNotification *)aNotification
1548   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1550   // If a sheet just resigned key then we should paint the menu bar
1551   // for whatever window is now main.
1552   NSWindow* window = [aNotification object];
1553   if ([window isSheet])
1554     [WindowDelegate paintMenubarForWindow:[NSApp mainWindow]];
1556   NS_OBJC_END_TRY_ABORT_BLOCK;
1560 - (void)windowWillMove:(NSNotification *)aNotification
1562   RollUpPopups();
1566 - (void)windowDidMove:(NSNotification *)aNotification
1568   // Dispatch the move event to Gecko
1569   nsGUIEvent guiEvent(PR_TRUE, NS_MOVE, mGeckoWindow);
1570   nsRect rect;
1571   mGeckoWindow->GetScreenBounds(rect);
1572   guiEvent.refPoint.x = rect.x;
1573   guiEvent.refPoint.y = rect.y;
1574   guiEvent.time = PR_IntervalNow();
1575   nsEventStatus status = nsEventStatus_eIgnore;
1576   mGeckoWindow->DispatchEvent(&guiEvent, status);
1580 - (BOOL)windowShouldClose:(id)sender
1582   // We only want to send NS_XUL_CLOSE and let gecko close the window
1583   nsGUIEvent guiEvent(PR_TRUE, NS_XUL_CLOSE, mGeckoWindow);
1584   guiEvent.time = PR_IntervalNow();
1585   nsEventStatus status = nsEventStatus_eIgnore;
1586   mGeckoWindow->DispatchEvent(&guiEvent, status);
1587   return NO; // gecko will do it
1591 - (void)windowWillClose:(NSNotification *)aNotification
1593   RollUpPopups();
1597 - (void)windowWillMiniaturize:(NSNotification *)aNotification
1599   RollUpPopups();
1603 - (void)windowDidMiniaturize:(NSNotification *)aNotification
1605   if (mGeckoWindow)
1606     mGeckoWindow->DispatchSizeModeEvent(nsSizeMode_Minimized);
1610 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
1612   if (mGeckoWindow)
1613     mGeckoWindow->DispatchSizeModeEvent(nsSizeMode_Normal);
1617 - (void)sendFocusEvent:(PRUint32)eventType
1619   if (!mGeckoWindow)
1620     return;
1622   nsEventStatus status = nsEventStatus_eIgnore;
1623   nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoWindow);
1624   focusGuiEvent.time = PR_IntervalNow();
1625   mGeckoWindow->DispatchEvent(&focusGuiEvent, status);
1629 - (void)didEndSheet:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
1631   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1633   // Note: 'contextInfo' (if it is set) is the window that is the parent of
1634   // the sheet.  The value of contextInfo is determined in
1635   // nsCocoaWindow::Show().  If it's set, 'contextInfo' is always the top-
1636   // level window, not another sheet itself.  But 'contextInfo' is nil if
1637   // our parent window is also a sheet -- in that case we shouldn't send
1638   // the top-level window any activate events (because it's our parent
1639   // window that needs to get these events, not the top-level window).
1640   [TopLevelWindowData deactivateInWindow:sheet];
1641   [sheet orderOut:self];
1642   if (contextInfo)
1643     [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo];
1645   NS_OBJC_END_TRY_ABORT_BLOCK;
1649 - (nsCocoaWindow*)geckoWidget
1651   return mGeckoWindow;
1655 - (PRBool)toplevelActiveState
1657   return mToplevelActiveState;
1661 - (void)sendToplevelActivateEvents
1663   if (!mToplevelActiveState) {
1664     [self sendFocusEvent:NS_GOTFOCUS];
1665     [self sendFocusEvent:NS_ACTIVATE];
1666     mToplevelActiveState = PR_TRUE;
1667   }
1671 - (void)sendToplevelDeactivateEvents
1673   if (mToplevelActiveState) {
1674     [self sendFocusEvent:NS_DEACTIVATE];
1675     [self sendFocusEvent:NS_LOSTFOCUS];
1676     mToplevelActiveState = PR_FALSE;
1677   }
1680 @end
1683 // Category on NSWindow so callers can use the same method on both ToolbarWindows
1684 // and NSWindows for accessing the background color.
1685 @implementation NSWindow(ToolbarWindowCompat)
1687 - (NSColor*)windowBackgroundColor
1689   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1691   return [self backgroundColor];
1693   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1696 @end
1699 @interface ToolbarWindow(Private)
1701 - (void)redrawTitlebar;
1703 @end
1706 // This class allows us to have a "unified toolbar" style window. It works like this:
1707 // 1) We set the window's style to textured.
1708 // 2) Because of this, the background color applies to the entire window, including
1709 //     the titlebar area. For normal textured windows, the default pattern is a 
1710 //    "brushed metal" image on Tiger and a unified gradient on Leopard.
1711 // 3) We set the background color to a custom NSColor subclass that knows how tall the window is.
1712 //    When -set is called on it, it sets a pattern (with a draw callback) as the fill. In that callback,
1713 //    it paints the the titlebar and background colors in the correct areas of the context it's given,
1714 //    which will fill the entire window (CG will tile it horizontally for us).
1715 // 4) Whenever the window's main state changes and when [window display] is called,
1716 //    Cocoa redraws the titlebar using the patternDraw callback function.
1718 // This class also provides us with a pill button to show/hide the toolbar.
1720 // Drawing the unified gradient in the titlebar and the toolbar works like this:
1721 // 1) In the style sheet we set the toolbar's -moz-appearance to -moz-mac-unified-toolbar.
1722 // 2) When the toolbar is drawn, Gecko calls nsNativeThemeCocoa::DrawWidgetBackground
1723 //    for the widget type NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
1724 // 3) This calls DrawUnifiedToolbar which finds the toolbar frame's ToolbarWindow
1725 //    and passes the toolbar frame's height to setUnifiedToolbarHeight.
1726 // 4) If the toolbar height has changed, a titlebar redraw is triggered by
1727 //    [self display] and the upper part of the unified gradient is drawn in the
1728 //    titlebar.
1729 // 5) DrawUnifiedToolbar draws the lower part of the unified gradient in the toolbar.
1731 // Whenever the unified gradient is drawn in the titlebar or the toolbar, both
1732 // titlebar height and toolbar height must be known in order to construct the
1733 // correct gradient (which is a linear gradient with the length
1734 // titlebarHeight + toolbarHeight - 1). But you can only get from the toolbar frame
1735 // to the containing window - the other direction doesn't work. That's why the
1736 // toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
1737 // query the window for its titlebar height when drawing the toolbar.
1738 @implementation ToolbarWindow
1740 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
1742   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1744   aStyle = aStyle | NSTexturedBackgroundWindowMask;
1745   if ((self = [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag])) {
1746     mColor = [[TitlebarAndBackgroundColor alloc] initWithActiveTitlebarColor:nil
1747                                                        inactiveTitlebarColor:nil
1748                                                              backgroundColor:[NSColor whiteColor]
1749                                                                    forWindow:self];
1750     // Call the superclass's implementation, to avoid our guard method below.
1751     [super setBackgroundColor:mColor];
1753     mUnifiedToolbarHeight = 0.0f;
1754     mSuppressPainting = NO;
1756     // setBottomCornerRounded: is a private API call, so we check to make sure
1757     // we respond to it just in case.
1758     if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
1759       [self setBottomCornerRounded:NO];
1760   }
1761   return self;
1763   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1767 - (void)dealloc
1769   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1771   [mColor release];
1772   [super dealloc];
1774   NS_OBJC_END_TRY_ABORT_BLOCK;
1778 // We don't provide our own implementation of -backgroundColor because NSWindow
1779 // looks at it, apparently. This is here to keep someone from messing with our
1780 // custom NSColor subclass.
1781 - (void)setBackgroundColor:(NSColor*)aColor
1783   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1785   [mColor setBackgroundColor:aColor];
1787   NS_OBJC_END_TRY_ABORT_BLOCK;
1791 // If you need to get at the background color of the window (in the traditional
1792 // sense) use this method instead.
1793 - (NSColor*)windowBackgroundColor
1795   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1797   return [mColor backgroundColor];
1799   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1803 // Pass nil here to get the default appearance.
1804 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
1806   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1808   [mColor setTitlebarColor:aColor forActiveWindow:aActive];
1810   NS_OBJC_END_TRY_ABORT_BLOCK;
1813 // This is called by nsNativeThemeCocoa.mm's DrawUnifiedToolbar.
1814 // We need to know the toolbar's height in order to draw the correct
1815 // unified gradient in the titlebar.
1816 - (void)setUnifiedToolbarHeight:(float)aToolbarHeight
1818   if (mUnifiedToolbarHeight == aToolbarHeight)
1819     return;
1820   mUnifiedToolbarHeight = aToolbarHeight;
1821   [self redrawTitlebar];
1825 - (float)unifiedToolbarHeight
1827   return mUnifiedToolbarHeight;
1830 - (float)titlebarHeight
1832   NSRect frameRect = [self frame];
1833   return frameRect.size.height - [self contentRectForFrameRect:frameRect].size.height;
1836 - (BOOL)isPaintingSuppressed
1838   return mSuppressPainting;
1841 // Always show the toolbar pill button.
1842 - (BOOL)_hasToolbar
1844   return YES;
1848 // Dispatch a toolbar pill button clicked message to Gecko.
1849 - (void)_toolbarPillButtonClicked:(id)sender
1851   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1853   nsCocoaWindow *geckoWindow = [[self delegate] geckoWidget];
1854   if (!geckoWindow)
1855     return;
1856   nsEventStatus status = nsEventStatus_eIgnore;
1857   nsGUIEvent guiEvent(PR_TRUE, NS_OS_TOOLBAR, geckoWindow);
1858   guiEvent.time = PR_IntervalNow();
1859   geckoWindow->DispatchEvent(&guiEvent, status);
1861   NS_OBJC_END_TRY_ABORT_BLOCK;
1864 // Retain and release "self" to avoid crashes when our widget (and its native
1865 // window) is closed as a result of processing a key equivalent (e.g.
1866 // Command+w or Command+q).  This workaround is only needed for a window
1867 // that can become key.
1868 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
1870   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
1872   NSWindow *nativeWindow = [self retain];
1873   BOOL retval = [super performKeyEquivalent:theEvent];
1874   [nativeWindow release];
1875   return retval;
1877   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
1880 - (void)sendEvent:(NSEvent *)anEvent
1882   NSEventType type = [anEvent type];
1883   
1884   switch (type) {
1885     case NSScrollWheel:
1886     case NSLeftMouseDown:
1887     case NSLeftMouseUp:
1888     case NSRightMouseDown:
1889     case NSRightMouseUp:
1890     case NSOtherMouseDown:
1891     case NSOtherMouseUp:
1892     case NSMouseMoved:
1893     case NSLeftMouseDragged:
1894     case NSRightMouseDragged:
1895     case NSOtherMouseDragged:
1896     {
1897       // Drop all mouse events if a modal window has appeared above us.
1898       // This helps make us behave as if the OS were running a "real" modal
1899       // event loop.
1900       id delegate = [self delegate];
1901       if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
1902         nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
1903         if (widget) {
1904           if (gAppModalWindowList && (widget != gAppModalWindowList->window))
1905             return;
1906           if (widget->HasModalDescendents())
1907             return;
1908         }
1909       }
1910       break;
1911     }
1912     default:
1913       break;
1914   }
1916   [super sendEvent:anEvent];
1919 @end
1921 @implementation ToolbarWindow(Private)
1923 // [self display] seems to be the only way to repaint a window's titlebar.
1924 // The bad thing about it is that it repaints all the window's subviews as well.
1925 // So we use a guard to prevent unnecessary redrawing.
1926 - (void)redrawTitlebar
1928   mSuppressPainting = YES;
1929   [self display];
1930   mSuppressPainting = NO;
1933 @end
1935 // Custom NSColor subclass where most of the work takes place for drawing in
1936 // the titlebar area.
1937 @implementation TitlebarAndBackgroundColor
1939 - (id)initWithActiveTitlebarColor:(NSColor*)aActiveTitlebarColor
1940             inactiveTitlebarColor:(NSColor*)aInactiveTitlebarColor
1941                   backgroundColor:(NSColor*)aBackgroundColor
1942                         forWindow:(NSWindow*)aWindow
1944   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1946   if ((self = [super init])) {
1947     mActiveTitlebarColor = [aActiveTitlebarColor retain];
1948     mInactiveTitlebarColor = [aInactiveTitlebarColor retain];
1949     mBackgroundColor = [aBackgroundColor retain];
1950     mWindow = aWindow; // weak ref to avoid a cycle
1951   }
1952   return self;
1954   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1958 - (void)dealloc
1960   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1962   [mActiveTitlebarColor release];
1963   [mInactiveTitlebarColor release];
1964   [mBackgroundColor release];
1965   [super dealloc];
1967   NS_OBJC_END_TRY_ABORT_BLOCK;
1970 // Our pattern width is 1 pixel. CoreGraphics can cache and tile for us.
1971 static const float sPatternWidth = 1.0f;
1973 // Callback where all of the drawing for this color takes place.
1974 void patternDraw(void* aInfo, CGContextRef aContext)
1976   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1978   TitlebarAndBackgroundColor *color = (TitlebarAndBackgroundColor*)aInfo;
1979   NSColor *backgroundColor = [color backgroundColor];
1980   ToolbarWindow *window = (ToolbarWindow*)[color window];
1981   BOOL isMain = [window isMainWindow];
1982   NSColor *titlebarColor = isMain ? [color activeTitlebarColor] : [color inactiveTitlebarColor];
1984   // Remember: this context is NOT flipped, so the origin is in the bottom left.
1985   float titlebarHeight = [window titlebarHeight];
1986   float titlebarOrigin = [window frame].size.height - titlebarHeight;
1988   UnifiedGradientInfo info = { titlebarHeight, [window unifiedToolbarHeight], isMain, YES };
1990   [NSGraphicsContext saveGraphicsState];
1991   [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:aContext flipped:NO]];
1993   // If the titlebar color is nil, draw the default titlebar shading.
1994   if (!titlebarColor) {
1995     // Create and draw a CGShading that uses nsCocoaWindow::UnifiedShading() as its callback.
1996     CGFunctionCallbacks callbacks = {0, nsCocoaWindow::UnifiedShading, NULL};
1997     CGFunctionRef function = CGFunctionCreate(&info, 1, NULL, 4, NULL, &callbacks);
1998     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
1999     CGShadingRef shading = CGShadingCreateAxial(colorSpace,
2000                                                 CGPointMake(0.0f, titlebarOrigin + titlebarHeight),
2001                                                 CGPointMake(0.0f, titlebarOrigin),
2002                                                 function, NO, NO);
2003     CGColorSpaceRelease(colorSpace);
2004     CGFunctionRelease(function);
2005     CGContextDrawShading(aContext, shading);
2006     CGShadingRelease(shading);
2008     // Draw the one pixel border at the bottom of the titlebar.
2009     if ([window unifiedToolbarHeight] == 0) {
2010       [NativeGreyColorAsNSColor(headerBorderGrey, isMain) set];
2011       NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, 1.0f));
2012     }
2013   } else {
2014     // if the titlebar color is not nil, just set and draw it normally.
2015     [titlebarColor set];
2016     NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, titlebarHeight));
2017   }
2019   // Draw the background color of the window everywhere but where the titlebar is.
2020   [backgroundColor set];
2021   NSRectFill(NSMakeRect(0.0f, 0.0f, 1.0f, titlebarOrigin));
2023   [NSGraphicsContext restoreGraphicsState];
2025   NS_OBJC_END_TRY_ABORT_BLOCK;
2029 - (void)setFill
2031   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2033   CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2035   // Set up the pattern to be as tall as our window, and one pixel wide.
2036   // CoreGraphics can cache and tile us quickly.
2037   CGPatternCallbacks callbacks = {0, &patternDraw, NULL};
2038   CGPatternRef pattern = CGPatternCreate(self, CGRectMake(0.0f, 0.0f, sPatternWidth, [mWindow frame].size.height), 
2039                                          CGAffineTransformIdentity, 1, [mWindow frame].size.height,
2040                                          kCGPatternTilingConstantSpacing, true, &callbacks);
2042   // Set the pattern as the fill, which is what we were asked to do. All our
2043   // drawing will take place in the patternDraw callback.
2044   CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
2045   CGContextSetFillColorSpace(context, patternSpace);
2046   CGColorSpaceRelease(patternSpace);
2047   float component = 1.0f;
2048   CGContextSetFillPattern(context, pattern, &component);
2049   CGPatternRelease(pattern);
2051   NS_OBJC_END_TRY_ABORT_BLOCK;
2055 // Pass nil here to get the default appearance.
2056 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
2058   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2060   if (aActive) {
2061     [mActiveTitlebarColor autorelease];
2062     mActiveTitlebarColor = [aColor retain];
2063   } else {
2064     [mInactiveTitlebarColor autorelease];
2065     mInactiveTitlebarColor = [aColor retain];
2066   }
2068   NS_OBJC_END_TRY_ABORT_BLOCK;
2072 - (NSColor*)activeTitlebarColor
2074   return mActiveTitlebarColor;
2078 - (NSColor*)inactiveTitlebarColor
2080   return mInactiveTitlebarColor;
2084 - (void)setBackgroundColor:(NSColor*)aColor
2086   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2088   [mBackgroundColor autorelease];
2089   mBackgroundColor = [aColor retain];
2091   NS_OBJC_END_TRY_ABORT_BLOCK;
2095 - (NSColor*)backgroundColor
2097   return mBackgroundColor;
2101 - (NSWindow*)window
2103   return mWindow;
2107 - (NSString*)colorSpaceName
2109   return NSDeviceRGBColorSpace;
2113 - (void)set
2115   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2117   [self setFill];
2119   NS_OBJC_END_TRY_ABORT_BLOCK;
2122 @end
2125 @interface NSWindow (MethodSwizzling)
2126 - (void)nsCocoaWindow_NSWindow_sendEvent:(NSEvent *)anEvent;
2127 @end
2129 @implementation NSWindow (MethodSwizzling)
2131 // An NSLeftMouseDown event can change the focus, but it doesn't always do
2132 // this correctly/appropriately.  As a result, Gecko keyboard events may be
2133 // sent to the wrong nsChildView object, and thereby "lost".  So if we know
2134 // which ChildView object should have the focus in our NSWindow, and if
2135 // processing an NSLeftMouseDown event has caused the focus to be set
2136 // incorrectly, we change the focus back to where it belongs.  This resolves
2137 // bmo bugs 314160, 403232, 404433 and 357535/418031.
2139 // Only check for incorrect focus if our NSWindow's "new" first responder is
2140 // also a ChildView object:  Embedders (like Camino) sometimes legitimately
2141 // use NSView objects which are not ChildView objects (which don't have
2142 // corresponding child widgets), and expect them to be focusable via an
2143 // NSLeftMouseDown event.
2145 // For non-embedders (e.g. Firefox, Thunderbird and Seamonkey), it would
2146 // probably only be necessary to add a sendEvent: method to the ToolbarWindow
2147 // class.  But embedders (like Camino) generally create their own NSWindows.
2148 // So in order to fix this problem everywhere, it's necessary to "hook"
2149 // NSWindow's own sendEvent: method.
2150 - (void)nsCocoaWindow_NSWindow_sendEvent:(NSEvent *)anEvent
2152   // Since we've hooked a "system call" ([NSWindow sendEvent:]), we're always
2153   // called from system code (not browser code).  So avoid crashing on any
2154   // Objective-C exceptions that occur here.
2155   NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
2157   [self nsCocoaWindow_NSWindow_sendEvent:anEvent];
2159   if ([anEvent type] == NSLeftMouseDown) {
2160     NSResponder *newFirstResponder = [self firstResponder];
2161     if ([newFirstResponder isKindOfClass:[ChildView class]]) {
2162       WindowDataMap *windowMap = [WindowDataMap sharedWindowDataMap];
2163       TopLevelWindowData *windowData = [windowMap dataForWindow:self];
2164       if (windowData) {
2165         ChildView *shouldFocusView = [windowData getShouldFocusView];
2166         if (shouldFocusView && (shouldFocusView != newFirstResponder))
2167           [self makeFirstResponder:shouldFocusView];
2168       }
2169     }
2170   }
2172   NS_OBJC_END_TRY_LOGONLY_BLOCK;
2175 @end
2177 @implementation PopupWindow
2179 // The OS treats our custom popup windows very strangely -- many mouse events
2180 // sent to them never reach their target NSView objects.  (That these windows
2181 // are borderless and of level NSPopUpMenuWindowLevel may have something to do
2182 // with it.)  The best solution is to pre-empt the OS, as follows.  (All
2183 // events for a given NSWindow object go through its sendEvent: method.)
2184 - (void)sendEvent:(NSEvent *)anEvent
2186   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2188   NSView *target = nil;
2189   NSView *contentView = nil;
2190   NSEventType type = [anEvent type];
2191   NSPoint windowLocation = NSZeroPoint;
2192   switch (type) {
2193     case NSScrollWheel:
2194     case NSLeftMouseDown:
2195     case NSLeftMouseUp:
2196     case NSRightMouseDown:
2197     case NSRightMouseUp:
2198     case NSOtherMouseDown:
2199     case NSOtherMouseUp:
2200     case NSMouseMoved:
2201     case NSLeftMouseDragged:
2202     case NSRightMouseDragged:
2203     case NSOtherMouseDragged:
2204       if ((contentView = [self contentView])) {
2205         // Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
2206         windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
2207         target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
2208         // If the hit test failed, the event is targeted here but is not over the window.
2209         // Target it at the first responder.
2210         if (!target)
2211           target = (NSView*)[self firstResponder];
2212       }
2213       break;
2214     default:
2215       break;
2216   }
2217   if (target) {
2218     switch (type) {
2219       case NSScrollWheel:
2220         [target scrollWheel:anEvent];
2221         break;
2222       case NSLeftMouseDown:
2223         if ([NSApp isActive]) {
2224           [target mouseDown:anEvent];
2225         } else if (mIsContextMenu) {
2226           [target mouseDown:anEvent];
2227           // If we're in a context menu and our NSApp isn't active (i.e. if
2228           // we're in a context menu raised by a right mouse-down event), we
2229           // don't want the OS to send the coming NSLeftMouseUp event to NSApp
2230           // via the window server, but we do want our ChildView to receive an
2231           // NSLeftMouseUp event (and to send a Gecko NS_MOUSE_BUTTON_UP event
2232           // to the corresponding nsChildView object).  If our NSApp isn't
2233           // active when it receives the coming NSLeftMouseUp via the window
2234           // server, our app will (in effect) become partially activated,
2235           // which has strange side effects:  For example, if another app's
2236           // window had the focus, that window will lose the focus and the
2237           // other app's main menu will be completely disabled (though it will
2238           // continue to be displayed).
2239           // A side effect of not allowing the coming NSLeftMouseUp event to be
2240           // sent to NSApp via the window server is that our custom context
2241           // menus will roll up whenever the user left-clicks on them, whether
2242           // or not the left-click hit an active menu item.  This is how native
2243           // context menus behave, but wasn't how our custom context menus
2244           // behaved previously (on the trunk or e.g. in Firefox 2.0.0.4).
2245           // If our ChildView's corresponding nsChildView object doesn't
2246           // dispatch an NS_MOUSE_BUTTON_UP event, none of our active menu items
2247           // will "work" on an NSLeftMouseUp.
2248           NSEvent *newEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2249                                                  location:windowLocation
2250                                             modifierFlags:nsCocoaUtils::GetCocoaEventModifierFlags(anEvent)
2251                                                 timestamp:GetCurrentEventTime()
2252                                              windowNumber:[self windowNumber]
2253                                                   context:nil
2254                                               eventNumber:0
2255                                                clickCount:1
2256                                                  pressure:0.0];
2257           [target mouseUp:newEvent];
2258           RollUpPopups();
2259         } else {
2260           // If our NSApp isn't active and we're not a context menu (i.e. if
2261           // we're an ordinary popup window), activate us before sending the
2262           // event to its target.  This prevents us from being used in the
2263           // background, and resolves bmo bug 434097 (another app focus
2264           // wierdness bug).
2265           [NSApp activateIgnoringOtherApps:YES];
2266           [target mouseDown:anEvent];
2267         }
2268         break;
2269       case NSLeftMouseUp:
2270         [target mouseUp:anEvent];
2271         break;
2272       case NSRightMouseDown:
2273         [target rightMouseDown:anEvent];
2274         break;
2275       case NSRightMouseUp:
2276         [target rightMouseUp:anEvent];
2277         break;
2278       case NSOtherMouseDown:
2279         [target otherMouseDown:anEvent];
2280         break;
2281       case NSOtherMouseUp:
2282         [target otherMouseUp:anEvent];
2283         break;
2284       case NSMouseMoved:
2285         [target mouseMoved:anEvent];
2286         break;
2287       case NSLeftMouseDragged:
2288         [target mouseDragged:anEvent];
2289         break;
2290       case NSRightMouseDragged:
2291         [target rightMouseDragged:anEvent];
2292         break;
2293       case NSOtherMouseDragged:
2294         [target otherMouseDragged:anEvent];
2295         break;
2296       default:
2297         [super sendEvent:anEvent];
2298         break;
2299     }
2300   } else {
2301     [super sendEvent:anEvent];
2302   }
2304   NS_OBJC_END_TRY_ABORT_BLOCK;
2308 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
2309       backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
2311   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2313   mIsContextMenu = false;
2314   return [super initWithContentRect:contentRect styleMask:styleMask
2315           backing:bufferingType defer:deferCreation];
2317   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2321 - (BOOL)isContextMenu
2323   return mIsContextMenu;
2327 - (void)setIsContextMenu:(BOOL)flag
2329   mIsContextMenu = flag;
2333 @end
2335 // According to Apple's docs on [NSWindow canBecomeKeyWindow] and [NSWindow
2336 // canBecomeMainWindow], windows without a title bar or resize bar can't (by
2337 // default) become key or main.  But if a window can't become key, it can't
2338 // accept keyboard input (bmo bug 393250).  And it should also be possible for
2339 // an otherwise "ordinary" window to become main.  We need to override these
2340 // two methods to make this happen.
2341 @implementation BorderlessWindow
2343 - (BOOL)canBecomeKeyWindow
2345   return YES;
2349 - (void)sendEvent:(NSEvent *)anEvent
2351   NSEventType type = [anEvent type];
2352   
2353   switch (type) {
2354     case NSScrollWheel:
2355     case NSLeftMouseDown:
2356     case NSLeftMouseUp:
2357     case NSRightMouseDown:
2358     case NSRightMouseUp:
2359     case NSOtherMouseDown:
2360     case NSOtherMouseUp:
2361     case NSMouseMoved:
2362     case NSLeftMouseDragged:
2363     case NSRightMouseDragged:
2364     case NSOtherMouseDragged:
2365     {
2366       // Drop all mouse events if a modal window has appeared above us.
2367       // This helps make us behave as if the OS were running a "real" modal
2368       // event loop.
2369       id delegate = [self delegate];
2370       if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
2371         nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
2372         if (widget) {
2373           if (gAppModalWindowList && (widget != gAppModalWindowList->window))
2374             return;
2375           if (widget->HasModalDescendents())
2376             return;
2377         }
2378       }
2379       break;
2380     }
2381     default:
2382       break;
2383   }
2385   [super sendEvent:anEvent];
2389 // Apple's doc on this method says that the NSWindow class's default is not to
2390 // become main if the window isn't "visible" -- so we should replicate that
2391 // behavior here.  As best I can tell, the [NSWindow isVisible] method is an
2392 // accurate test of what Apple means by "visibility".
2393 - (BOOL)canBecomeMainWindow
2395   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2397   if (![self isVisible])
2398     return NO;
2399   return YES;
2401   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2404 // Retain and release "self" to avoid crashes when our widget (and its native
2405 // window) is closed as a result of processing a key equivalent (e.g.
2406 // Command+w or Command+q).  This workaround is only needed for a window
2407 // that can become key.
2408 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
2410   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2412   NSWindow *nativeWindow = [self retain];
2413   BOOL retval = [super performKeyEquivalent:theEvent];
2414   [nativeWindow release];
2415   return retval;
2417   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2420 @end