CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / cocoa / nsCocoaWindow.mm
blobd596e5c1123622a66ac92802ebd591acb9671720
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 "nsChildView.h"
48 #include "nsWindowMap.h"
49 #include "nsAppShell.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 "nsIServiceManager.h"
60 #include "nsIDOMWindow.h"
61 #include "nsPIDOMWindow.h"
62 #include "nsIDOMElement.h"
63 #include "nsThreadUtils.h"
64 #include "nsMenuBarX.h"
65 #include "nsMenuUtilsX.h"
66 #include "nsStyleConsts.h"
67 #include "nsNativeThemeColors.h"
68 #include "nsChildView.h"
69 #include "nsIMenuRollup.h"
71 #include "gfxPlatform.h"
72 #include "qcms.h"
74 #include "GLContext.h"
75 #include "LayerManagerOGL.h"
76 #include "gfxQuartzSurface.h"
78 namespace mozilla {
79 namespace layers {
80 class LayerManager;
83 using namespace mozilla::layers;
84 using namespace mozilla::gl;
86 // defined in nsAppShell.mm
87 extern nsCocoaAppModalWindowList *gCocoaAppModalWindowList;
89 PRInt32 gXULModalLevel = 0;
91 // In principle there should be only one app-modal window at any given time.
92 // But sometimes, despite our best efforts, another window appears above the
93 // current app-modal window.  So we need to keep a linked list of app-modal
94 // windows.  (A non-sheet window that appears above an app-modal window is
95 // also made app-modal.)  See nsCocoaWindow::SetModal().
96 nsCocoaWindowList *gGeckoAppModalWindowList = NULL;
98 PRBool gConsumeRollupEvent;
100 // defined in nsMenuBarX.mm
101 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
103 // defined in nsChildView.mm
104 extern nsIRollupListener * gRollupListener;
105 extern nsIMenuRollup     * gMenuRollup;
106 extern nsIWidget         * gRollupWidget;
107 extern BOOL                gSomeMenuBarPainted;
109 extern "C" {
110   // CGSPrivate.h
111   typedef NSInteger CGSConnection;
112   typedef NSInteger CGSWindow;
113   typedef NSUInteger CGSWindowFilterRef;
114   extern CGSConnection _CGSDefaultConnection(void);
115   extern CGError CGSSetWindowShadowAndRimParameters(const CGSConnection cid, CGSWindow wid, float standardDeviation, float density, int offsetX, int offsetY, unsigned int flags);
116   extern CGError CGSNewCIFilterByName(CGSConnection cid, CFStringRef filterName, CGSWindowFilterRef *outFilter);
117   extern CGError CGSSetCIFilterValuesFromDictionary(CGSConnection cid, CGSWindowFilterRef filter, CFDictionaryRef filterValues);
118   extern CGError CGSAddWindowFilter(CGSConnection cid, CGSWindow wid, CGSWindowFilterRef filter, NSInteger flags);
119   extern CGError CGSRemoveWindowFilter(CGSConnection cid, CGSWindow wid, CGSWindowFilterRef filter);
120   extern CGError CGSReleaseCIFilter(CGSConnection cid, CGSWindowFilterRef filter);
123 #define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
125 NS_IMPL_ISUPPORTS_INHERITED1(nsCocoaWindow, Inherited, nsPIWidgetCocoa)
127 // A note on testing to see if your object is a sheet...
128 // |mWindowType == eWindowType_sheet| is true if your gecko nsIWidget is a sheet
129 // widget - whether or not the sheet is showing. |[mWindow isSheet]| will return
130 // true *only when the sheet is actually showing*. Choose your test wisely.
132 // roll up any popup windows
133 static void RollUpPopups()
135   if (gRollupListener && gRollupWidget)
136     gRollupListener->Rollup(nsnull, nsnull);
139 nsCocoaWindow::nsCocoaWindow()
140 : mParent(nsnull)
141 , mWindow(nil)
142 , mDelegate(nil)
143 , mSheetWindowParent(nil)
144 , mPopupContentView(nil)
145 , mShadowStyle(NS_STYLE_WINDOW_SHADOW_DEFAULT)
146 , mWindowFilter(0)
147 , mWindowMadeHere(PR_FALSE)
148 , mSheetNeedsShow(PR_FALSE)
149 , mFullScreen(PR_FALSE)
150 , mModal(PR_FALSE)
151 , mNumModalDescendents(0)
156 void nsCocoaWindow::DestroyNativeWindow()
158   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
160   CleanUpWindowFilter();
161   // We want to unhook the delegate here because we don't want events
162   // sent to it after this object has been destroyed.
163   [mWindow setDelegate:nil];
164   [mWindow close];
165   [mDelegate autorelease];
167   NS_OBJC_END_TRY_ABORT_BLOCK;
170 nsCocoaWindow::~nsCocoaWindow()
172   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
174   // Notify the children that we're gone.  Popup windows (e.g. tooltips) can
175   // have nsChildView children.  'kid' is an nsChildView object if and only if
176   // its 'type' is 'eWindowType_child' or 'eWindowType_plugin'.
177   // childView->ResetParent() can change our list of children while it's
178   // being iterated, so the way we iterate the list must allow for this.
179   for (nsIWidget* kid = mLastChild; kid;) {
180     nsWindowType kidType;
181     kid->GetWindowType(kidType);
182     if (kidType == eWindowType_child || kidType == eWindowType_plugin) {
183       nsChildView* childView = static_cast<nsChildView*>(kid);
184       kid = kid->GetPrevSibling();
185       childView->ResetParent();
186     } else {
187       nsCocoaWindow* childWindow = static_cast<nsCocoaWindow*>(kid);
188       childWindow->mParent = nsnull;
189       kid = kid->GetPrevSibling();
190     }
191   }
193   if (mWindow && mWindowMadeHere) {
194     DestroyNativeWindow();
195   }
197   NS_IF_RELEASE(mPopupContentView);
199   // Deal with the possiblity that we're being destroyed while running modal.
200   NS_ASSERTION(!mModal, "Widget destroyed while running modal!");
201   if (mModal) {
202     --gXULModalLevel;
203     NS_ASSERTION(gXULModalLevel >= 0, "Wierdness setting modality!");
204   }
206   NS_OBJC_END_TRY_ABORT_BLOCK;
209 static void FitRectToVisibleAreaForScreen(nsIntRect &aRect, NSScreen *screen)
211   if (!screen)
212     return;
213   
214   nsIntRect screenBounds(nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame]));
215   
216   if (aRect.width > screenBounds.width) {
217     aRect.width = screenBounds.width;
218   }
219   if (aRect.height > screenBounds.height) {
220     aRect.height = screenBounds.height;
221   }
222   
223   if (aRect.x - screenBounds.x + aRect.width > screenBounds.width) {
224     aRect.x += screenBounds.width - (aRect.x - screenBounds.x + aRect.width);
225   }
226   if (aRect.y - screenBounds.y + aRect.height > screenBounds.height) {
227     aRect.y += screenBounds.height - (aRect.y - screenBounds.y + aRect.height);
228   }
231 // Some applications like Camino use native popup windows
232 // (native context menus, native tooltips)
233 static PRBool UseNativePopupWindows()
235   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
236   if (!prefs)
237     return PR_FALSE;
239   PRBool useNativePopupWindows;
240   nsresult rv = prefs->GetBoolPref("ui.use_native_popup_windows", &useNativePopupWindows);
241   return (NS_SUCCEEDED(rv) && useNativePopupWindows);
244 nsresult nsCocoaWindow::Create(nsIWidget *aParent,
245                                nsNativeWidget aNativeParent,
246                                const nsIntRect &aRect,
247                                EVENT_CALLBACK aHandleEventFunction,
248                                nsIDeviceContext *aContext,
249                                nsIAppShell *aAppShell,
250                                nsIToolkit *aToolkit,
251                                nsWidgetInitData *aInitData)
253   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
255   // Because the hidden window is created outside of an event loop,
256   // we have to provide an autorelease pool (see bug 559075).
257   nsAutoreleasePool localPool;
259   nsIntRect newBounds = aRect;
260   FitRectToVisibleAreaForScreen(newBounds, [NSScreen mainScreen]);
262   // Set defaults which can be overriden from aInitData in BaseCreate
263   mWindowType = eWindowType_toplevel;
264   mBorderStyle = eBorderStyle_default;
266   Inherited::BaseCreate(aParent, newBounds, aHandleEventFunction, aContext, aAppShell,
267                         aToolkit, aInitData);
269   mParent = aParent;
271   // Applications that use native popups don't want us to create popup windows.
272   if ((mWindowType == eWindowType_popup) && UseNativePopupWindows())
273     return NS_OK;
275   nsresult rv = CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds),
276                                    mBorderStyle, PR_FALSE);
277   NS_ENSURE_SUCCESS(rv, rv);
279   if (mWindowType == eWindowType_popup)
280     return CreatePopupContentView(newBounds, aHandleEventFunction, aContext, aAppShell, aToolkit);
282   return NS_OK;
284   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
287 static unsigned int WindowMaskForBorderStyle(nsBorderStyle aBorderStyle)
289   PRBool allOrDefault = (aBorderStyle == eBorderStyle_all ||
290                          aBorderStyle == eBorderStyle_default);
292   /* Apple's docs on NSWindow styles say that "a window's style mask should
293    * include NSTitledWindowMask if it includes any of the others [besides
294    * NSBorderlessWindowMask]".  This implies that a borderless window
295    * shouldn't have any other styles than NSBorderlessWindowMask.
296    */
297   if (!allOrDefault && !(aBorderStyle & eBorderStyle_title))
298     return NSBorderlessWindowMask;
300   unsigned int mask = NSTitledWindowMask;
301   if (allOrDefault || aBorderStyle & eBorderStyle_close)
302     mask |= NSClosableWindowMask;
303   if (allOrDefault || aBorderStyle & eBorderStyle_minimize)
304     mask |= NSMiniaturizableWindowMask;
305   if (allOrDefault || aBorderStyle & eBorderStyle_resizeh)
306     mask |= NSResizableWindowMask;
308   return mask;
311 NS_IMETHODIMP nsCocoaWindow::ReparentNativeWidget(nsIWidget* aNewParent)
313   return NS_ERROR_NOT_IMPLEMENTED;
316 // If aRectIsFrameRect, aRect specifies the frame rect of the new window.
317 // Otherwise, aRect.x/y specify the position of the window's frame relative to
318 // the bottom of the menubar and aRect.width/height specify the size of the
319 // content rect.
320 nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect,
321                                            nsBorderStyle aBorderStyle,
322                                            PRBool aRectIsFrameRect)
324   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
326   // We default to NSBorderlessWindowMask, add features if needed.
327   unsigned int features = NSBorderlessWindowMask;
329   // Configure the window we will create based on the window type.
330   switch (mWindowType)
331   {
332     case eWindowType_invisible:
333     case eWindowType_child:
334     case eWindowType_plugin:
335       break;
336     case eWindowType_popup:
337       if (aBorderStyle != eBorderStyle_default && mBorderStyle & eBorderStyle_title) {
338         features |= NSTitledWindowMask;
339         if (aBorderStyle & eBorderStyle_close) {
340           features |= NSClosableWindowMask;
341         }
342       }
343       break;
344     case eWindowType_toplevel:
345     case eWindowType_dialog:
346       features = WindowMaskForBorderStyle(aBorderStyle);
347       break;
348     case eWindowType_sheet:
349       nsWindowType parentType;
350       mParent->GetWindowType(parentType);
351       if (parentType != eWindowType_invisible &&
352           aBorderStyle & eBorderStyle_resizeh) {
353         features = NSResizableWindowMask;
354       }
355       else {
356         features = NSMiniaturizableWindowMask;
357       }
358       features |= NSTitledWindowMask;
359       break;
360     default:
361       NS_ERROR("Unhandled window type!");
362       return NS_ERROR_FAILURE;
363   }
365   NSRect contentRect;
367   if (aRectIsFrameRect) {
368     contentRect = [NSWindow contentRectForFrameRect:aRect styleMask:features];
369   } else {
370     /* 
371      * We pass a content area rect to initialize the native Cocoa window. The
372      * content rect we give is the same size as the size we're given by gecko.
373      * The origin we're given for non-popup windows is moved down by the height
374      * of the menu bar so that an origin of (0,100) from gecko puts the window
375      * 100 pixels below the top of the available desktop area. We also move the
376      * origin down by the height of a title bar if it exists. This is so the
377      * origin that gecko gives us for the top-left of  the window turns out to
378      * be the top-left of the window we create. This is how it was done in
379      * Carbon. If it ought to be different we'll probably need to look at all
380      * the callers.
381      *
382      * Note: This means that if you put a secondary screen on top of your main
383      * screen and open a window in the top screen, it'll be incorrectly shifted
384      * down by the height of the menu bar. Same thing would happen in Carbon.
385      *
386      * Note: If you pass a rect with 0,0 for an origin, the window ends up in a
387      * weird place for some reason. This stops that without breaking popups.
388      */
389     // Compensate for difference between frame and content area height (e.g. title bar).
390     NSRect newWindowFrame = [NSWindow frameRectForContentRect:aRect styleMask:features];
392     contentRect = aRect;
393     contentRect.origin.y -= (newWindowFrame.size.height - aRect.size.height);
395     if (mWindowType != eWindowType_popup)
396       contentRect.origin.y -= [[NSApp mainMenu] menuBarHeight];
397   }
399   // NSLog(@"Top-level window being created at Cocoa rect: %f, %f, %f, %f\n",
400   //       rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
402   Class windowClass = [BaseWindow class];
403   // If we have a titlebar on a top-level window, we want to be able to control the 
404   // titlebar color (for unified windows), so use the special ToolbarWindow class. 
405   // Note that we need to check the window type because we mark sheets as 
406   // having titlebars.
407   if ((mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) &&
408       (features & NSTitledWindowMask))
409     windowClass = [ToolbarWindow class];
410   // If we're a popup window we need to use the PopupWindow class.
411   else if (mWindowType == eWindowType_popup)
412     windowClass = [PopupWindow class];
413   // If we're a non-popup borderless window we need to use the
414   // BorderlessWindow class.
415   else if (features == NSBorderlessWindowMask)
416     windowClass = [BorderlessWindow class];
418   // Create the window
419   mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features 
420                                  backing:NSBackingStoreBuffered defer:YES];
422   // Make sure that the content rect we gave has been honored.
423   NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
424   if (!NSEqualRects([mWindow frame], wantedFrame)) {
425     // This can happen when the window is not on the primary screen.
426     [mWindow setFrame:wantedFrame display:NO];
427   }
428   UpdateBounds();
430   if (mWindowType == eWindowType_invisible) {
431     [mWindow setLevel:kCGDesktopWindowLevelKey];
432   } else if (mWindowType == eWindowType_popup) {
433     SetPopupWindowLevel();
434     [mWindow setHasShadow:YES];
435   }
437   [mWindow setBackgroundColor:[NSColor whiteColor]];
438   [mWindow setContentMinSize:NSMakeSize(60, 60)];
439   [mWindow disableCursorRects];
441   // setup our notification delegate. Note that setDelegate: does NOT retain.
442   mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
443   [mWindow setDelegate:mDelegate];
445   [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
446   mWindowMadeHere = PR_TRUE;
448   return NS_OK;
450   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
453 NS_IMETHODIMP nsCocoaWindow::CreatePopupContentView(const nsIntRect &aRect,
454                              EVENT_CALLBACK aHandleEventFunction,
455                              nsIDeviceContext *aContext,
456                              nsIAppShell *aAppShell,
457                              nsIToolkit *aToolkit)
459   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
461   // We need to make our content view a ChildView.
462   mPopupContentView = new nsChildView();
463   if (!mPopupContentView)
464     return NS_ERROR_FAILURE;
466   NS_ADDREF(mPopupContentView);
468   nsIWidget* thisAsWidget = static_cast<nsIWidget*>(this);
469   mPopupContentView->Create(thisAsWidget, nsnull, aRect, aHandleEventFunction,
470                             aContext, aAppShell, aToolkit, nsnull);
472   ChildView* newContentView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET);
473   [mWindow setContentView:newContentView];
475   return NS_OK;
477   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
480 NS_IMETHODIMP nsCocoaWindow::Destroy()
482   if (mPopupContentView)
483     mPopupContentView->Destroy();
485   nsBaseWidget::Destroy();
486   nsBaseWidget::OnDestroy();
488   if (mFullScreen) {
489     nsCocoaUtils::HideOSChromeOnScreen(PR_FALSE, [mWindow screen]);
490   }
492   return NS_OK;
495 nsIWidget* nsCocoaWindow::GetSheetWindowParent(void)
497   if (mWindowType != eWindowType_sheet)
498     return nsnull;
499   nsCocoaWindow *parent = static_cast<nsCocoaWindow*>(mParent);
500   while (parent && (parent->mWindowType == eWindowType_sheet))
501     parent = static_cast<nsCocoaWindow*>(parent->mParent);
502   return parent;
505 void* nsCocoaWindow::GetNativeData(PRUint32 aDataType)
507   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
509   void* retVal = nsnull;
510   
511   switch (aDataType) {
512     // to emulate how windows works, we always have to return a NSView
513     // for NS_NATIVE_WIDGET
514     case NS_NATIVE_WIDGET:
515     case NS_NATIVE_DISPLAY:
516       retVal = [mWindow contentView];
517       break;
518       
519     case NS_NATIVE_WINDOW:
520       retVal = mWindow;
521       break;
522       
523     case NS_NATIVE_GRAPHIC:
524       // There isn't anything that makes sense to return here,
525       // and it doesn't matter so just return nsnull.
526       NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a top-level window!");
527       break;
528   }
530   return retVal;
532   NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
535 NS_IMETHODIMP nsCocoaWindow::IsVisible(PRBool & aState)
537   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
539   aState = ([mWindow isVisible] || mSheetNeedsShow);
540   return NS_OK;
542   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
545 NS_IMETHODIMP nsCocoaWindow::SetModal(PRBool aState)
547   // This is used during startup (outside the event loop) when creating
548   // the add-ons compatibility checking dialog and the profile manager UI;
549   // therefore, it needs to provide an autorelease pool to avoid cocoa
550   // objects leaking.
551   nsAutoreleasePool localPool;
553   mModal = aState;
554   nsCocoaWindow *aParent = static_cast<nsCocoaWindow*>(mParent);
555   if (aState) {
556     ++gXULModalLevel;
557     if (gCocoaAppModalWindowList)
558       gCocoaAppModalWindowList->PushGecko(mWindow, this);
559     // When a non-sheet window gets "set modal", make the window(s) that it
560     // appears over behave as they should.  We can't rely on native methods to
561     // do this, for the following reason:  The OS runs modal non-sheet windows
562     // in an event loop (using [NSApplication runModalForWindow:] or similar
563     // methods) that's incompatible with the modal event loop in nsXULWindow::
564     // ShowModal() (each of these event loops is "exclusive", and can't run at
565     // the same time as other (similar) event loops).
566     if (mWindowType != eWindowType_sheet) {
567       while (aParent) {
568         if (aParent->mNumModalDescendents++ == 0) {
569           NSWindow *aWindow = aParent->GetCocoaWindow();
570           if (aParent->mWindowType != eWindowType_invisible) {
571             [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:NO];
572             [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:NO];
573             [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO];
574           }
575         }
576         aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
577       }
578       [mWindow setLevel:NSModalPanelWindowLevel];
579       nsCocoaWindowList *windowList = new nsCocoaWindowList;
580       if (windowList) {
581         windowList->window = this; // Don't ADDREF
582         windowList->prev = gGeckoAppModalWindowList;
583         gGeckoAppModalWindowList = windowList;
584       }
585     }
586   }
587   else {
588     --gXULModalLevel;
589     NS_ASSERTION(gXULModalLevel >= 0, "Mismatched call to nsCocoaWindow::SetModal(PR_FALSE)!");
590     if (gCocoaAppModalWindowList)
591       gCocoaAppModalWindowList->PopGecko(mWindow, this);
592     if (mWindowType != eWindowType_sheet) {
593       while (aParent) {
594         if (--aParent->mNumModalDescendents == 0) {
595           NSWindow *aWindow = aParent->GetCocoaWindow();
596           if (aParent->mWindowType != eWindowType_invisible) {
597             [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:YES];
598             [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:YES];
599             [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:YES];
600           }
601         }
602         NS_ASSERTION(aParent->mNumModalDescendents >= 0, "Widget hierarchy changed while modal!");
603         aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
604       }
605       if (gGeckoAppModalWindowList) {
606         NS_ASSERTION(gGeckoAppModalWindowList->window == this, "Widget hierarchy changed while modal!");
607         nsCocoaWindowList *saved = gGeckoAppModalWindowList;
608         gGeckoAppModalWindowList = gGeckoAppModalWindowList->prev;
609         delete saved; // "window" not ADDREFed
610       }
611       if (mWindowType == eWindowType_popup)
612         SetPopupWindowLevel();
613       else
614         [mWindow setLevel:NSNormalWindowLevel];
615     }
616   }
617   return NS_OK;
620 // Hide or show this window
621 NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
623   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
625   // We need to re-execute sometimes in order to bring already-visible
626   // windows forward.
627   if (!mSheetNeedsShow && !bState && ![mWindow isVisible])
628     return NS_OK;
630   nsIWidget* parentWidget = mParent;
631   nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget));
632   NSWindow* nativeParentWindow = (parentWidget) ?
633     (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) : nil;
635   if (bState && !mBounds.IsEmpty()) {
636     if (mWindowType == eWindowType_sheet) {
637       // bail if no parent window (its basically what we do in Carbon)
638       if (!nativeParentWindow || !piParentWidget)
639         return NS_ERROR_FAILURE;
641       NSWindow* topNonSheetWindow = nativeParentWindow;
642       
643       // If this sheet is the child of another sheet, hide the parent so that
644       // this sheet can be displayed. Leave the parent mSheetNeedsShow alone,
645       // that is only used to handle sibling sheet contention. The parent will
646       // return once there are no more child sheets.
647       PRBool parentIsSheet = PR_FALSE;
648       if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
649           parentIsSheet) {
650         piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
651         [NSApp endSheet:nativeParentWindow];
652         [nativeParentWindow setAcceptsMouseMovedEvents:NO];
653       }
655       nsCocoaWindow* sheetShown = nsnull;
656       if (NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_TRUE, &sheetShown)) &&
657           (!sheetShown || sheetShown == this)) {
658         // If this sheet is already the sheet actually being shown, don't
659         // tell it to show again. Otherwise the number of calls to
660         // [NSApp beginSheet...] won't match up with [NSApp endSheet...].
661         if (![mWindow isVisible]) {
662           mSheetNeedsShow = PR_FALSE;
663           mSheetWindowParent = topNonSheetWindow;
664           // Only set contextInfo if our parent isn't a sheet.
665           NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
666           [TopLevelWindowData deactivateInWindow:mSheetWindowParent];
667           [mWindow setAcceptsMouseMovedEvents:YES];
668           [NSApp beginSheet:mWindow
669              modalForWindow:mSheetWindowParent
670               modalDelegate:mDelegate
671              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
672                 contextInfo:contextInfo];
673           [TopLevelWindowData activateInWindow:mWindow];
674           SendSetZLevelEvent();
675         }
676       }
677       else {
678         // A sibling of this sheet is active, don't show this sheet yet.
679         // When the active sheet hides, its brothers and sisters that have
680         // mSheetNeedsShow set will have their opportunities to display.
681         mSheetNeedsShow = PR_TRUE;
682       }
683     }
684     else if (mWindowType == eWindowType_popup) {
685       // If a popup window is shown after being hidden, it needs to be "reset"
686       // for it to receive any mouse events aside from mouse-moved events
687       // (because it was removed from the "window cache" when it was hidden
688       // -- see below).  Setting the window number to -1 and then back to its
689       // original value seems to accomplish this.  The idea was "borrowed"
690       // from the Java Embedding Plugin.
691       NSInteger windowNumber = [mWindow windowNumber];
692       [mWindow _setWindowNumber:-1];
693       [mWindow _setWindowNumber:windowNumber];
694       [mWindow setAcceptsMouseMovedEvents:YES];
695       // For reasons that aren't yet clear, calls to [NSWindow orderFront:] or
696       // [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000)
697       // creating CGSWindow", which in turn triggers an internal inconsistency
698       // NSException.  These errors shouldn't be fatal.  So we need to wrap
699       // calls to ...orderFront: in LOGONLY blocks.  See bmo bug 470864.
700       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
701       [mWindow orderFront:nil];
702       NS_OBJC_END_TRY_LOGONLY_BLOCK;
703       SendSetZLevelEvent();
704       AdjustWindowShadow();
705       SetUpWindowFilter();
706       // If our popup window is a non-native context menu, tell the OS (and
707       // other programs) that a menu has opened.  This is how the OS knows to
708       // close other programs' context menus when ours open.
709       if ([mWindow isKindOfClass:[PopupWindow class]] &&
710           [(PopupWindow*) mWindow isContextMenu]) {
711         [[NSDistributedNotificationCenter defaultCenter]
712           postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
713                         object:@"org.mozilla.gecko.PopupWindow"];
714       }
716       // If a parent window was supplied and this is a popup at the parent
717       // level, set its child window. This will cause the child window to
718       // appear above the parent and move when the parent does. Setting this
719       // needs to happen after the _setWindowNumber calls above, otherwise the
720       // window doesn't focus properly.
721       if (nativeParentWindow && mPopupLevel == ePopupLevelParent)
722         [nativeParentWindow addChildWindow:mWindow
723                             ordered:NSWindowAbove];
724     }
725     else {
726       [mWindow setAcceptsMouseMovedEvents:YES];
727       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
728       [mWindow makeKeyAndOrderFront:nil];
729       NS_OBJC_END_TRY_LOGONLY_BLOCK;
730       SendSetZLevelEvent();
731     }
732   }
733   else {
734     // roll up any popups if a top-level window is going away
735     if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog)
736       RollUpPopups();
738     // now get rid of the window/sheet
739     if (mWindowType == eWindowType_sheet) {
740       if (mSheetNeedsShow) {
741         // This is an attempt to hide a sheet that never had a chance to
742         // be shown. There's nothing to do other than make sure that it
743         // won't show.
744         mSheetNeedsShow = PR_FALSE;
745       }
746       else {
747         // get sheet's parent *before* hiding the sheet (which breaks the linkage)
748         NSWindow* sheetParent = mSheetWindowParent;
749         
750         // hide the sheet
751         [NSApp endSheet:mWindow];
752         
753         [mWindow setAcceptsMouseMovedEvents:NO];
755         [TopLevelWindowData deactivateInWindow:mWindow];
757         nsCocoaWindow* siblingSheetToShow = nsnull;
758         PRBool parentIsSheet = PR_FALSE;
760         if (nativeParentWindow && piParentWidget &&
761             NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_FALSE, &siblingSheetToShow)) &&
762             siblingSheetToShow) {
763           // First, give sibling sheets an opportunity to show.
764           siblingSheetToShow->Show(PR_TRUE);
765         }
766         else if (nativeParentWindow && piParentWidget &&
767                  NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
768                  parentIsSheet) {
769           // Only set contextInfo if the parent of the parent sheet we're about
770           // to restore isn't itself a sheet.
771           NSWindow* contextInfo = sheetParent;
772           nsIWidget* grandparentWidget = nil;
773           if (NS_SUCCEEDED(piParentWidget->GetRealParent(&grandparentWidget)) && grandparentWidget) {
774             nsCOMPtr<nsPIWidgetCocoa> piGrandparentWidget(do_QueryInterface(grandparentWidget));
775             PRBool grandparentIsSheet = PR_FALSE;
776             if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) &&
777                 grandparentIsSheet) {
778                 contextInfo = nil;
779             }
780           }
781           // If there are no sibling sheets, but the parent is a sheet, restore
782           // it.  It wasn't sent any deactivate events when it was hidden, so
783           // don't call through Show, just let the OS put it back up.
784           [nativeParentWindow setAcceptsMouseMovedEvents:YES];
785           [NSApp beginSheet:nativeParentWindow
786              modalForWindow:sheetParent
787               modalDelegate:[nativeParentWindow delegate]
788              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
789                 contextInfo:contextInfo];
790         }
791         else {
792           // Sheet, that was hard.  No more siblings or parents, going back
793           // to a real window.
794           NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
795           [sheetParent makeKeyAndOrderFront:nil];
796           NS_OBJC_END_TRY_LOGONLY_BLOCK;
797           [sheetParent setAcceptsMouseMovedEvents:YES];
798         }
799         SendSetZLevelEvent();
800       }
801     }
802     else {
803       // If the window is a popup window with a parent window we need to
804       // unhook it here before ordering it out. When you order out the child
805       // of a window it hides the parent window.
806       if (mWindowType == eWindowType_popup && nativeParentWindow)
807         [nativeParentWindow removeChildWindow:mWindow];
809       CleanUpWindowFilter();
810       [mWindow orderOut:nil];
811       // Unless it's explicitly removed from NSApp's "window cache", a popup
812       // window will keep receiving mouse-moved events even after it's been
813       // "ordered out" (instead of the browser window that was underneath it,
814       // until you click on that window).  This is bmo bug 378645, but it's
815       // surely an Apple bug.  The "window cache" is an undocumented subsystem,
816       // all of whose methods are included in the NSWindowCache category of
817       // the NSApplication class (in header files generated using class-dump).
818       // This workaround was "borrowed" from the Java Embedding Plugin (which
819       // uses it for a different purpose).
820       if (mWindowType == eWindowType_popup)
821         [NSApp _removeWindowFromCache:mWindow];
823       // it's very important to turn off mouse moved events when hiding a window, otherwise
824       // the windows' tracking rects will interfere with each other. (bug 356528)
825       [mWindow setAcceptsMouseMovedEvents:NO];
827       // If our popup window is a non-native context menu, tell the OS (and
828       // other programs) that a menu has closed.
829       if ([mWindow isKindOfClass:[PopupWindow class]] &&
830           [(PopupWindow*) mWindow isContextMenu]) {
831         [[NSDistributedNotificationCenter defaultCenter]
832           postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification"
833                         object:@"org.mozilla.gecko.PopupWindow"];
834       }
835     }
836   }
837   
838   if (mPopupContentView)
839       mPopupContentView->Show(bState);
841   return NS_OK;
843   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
846 struct ShadowParams {
847   float standardDeviation;
848   float density;
849   int offsetX;
850   int offsetY;
851   unsigned int flags;
854 // These numbers have been determined by looking at the results of
855 // CGSGetWindowShadowAndRimParameters for native window types.
856 static const ShadowParams kWindowShadowParameters[] = {
857   { 0.0f, 0.0f, 0, 0, 0 },        // none
858   { 8.0f, 0.5f, 0, 6, 1 },        // default
859   { 10.0f, 0.44f, 0, 10, 512 },   // menu
860   { 8.0f, 0.5f, 0, 6, 1 },        // tooltip
861   { 4.0f, 0.6f, 0, 4, 512 }       // sheet
864 // This method will adjust the window shadow style for popup windows after
865 // they have been made visible. Before they're visible, their window number
866 // might be -1, which is not useful.
867 // We won't attempt to change the shadow for windows that can acquire key state
868 // since OS X will reset the shadow whenever that happens.
869 void
870 nsCocoaWindow::AdjustWindowShadow()
872   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
874   if (![mWindow isVisible] || ![mWindow hasShadow] ||
875       [mWindow canBecomeKeyWindow] || [mWindow windowNumber] == -1)
876     return;
878   const ShadowParams& params = kWindowShadowParameters[mShadowStyle];
879   CGSConnection cid = _CGSDefaultConnection();
880   CGSSetWindowShadowAndRimParameters(cid, [mWindow windowNumber],
881                                      params.standardDeviation, params.density,
882                                      params.offsetX, params.offsetY,
883                                      params.flags);
885   NS_OBJC_END_TRY_ABORT_BLOCK;
888 void
889 nsCocoaWindow::SetUpWindowFilter()
891   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
893   if (![mWindow isVisible] || [mWindow windowNumber] == -1)
894     return;
896   CleanUpWindowFilter();
898   // Only blur the background of menus and fake sheets, but not on PPC
899   // because it results in blank windows (bug 547723).
900 #ifndef __ppc__
901   if (mShadowStyle != NS_STYLE_WINDOW_SHADOW_MENU &&
902       mShadowStyle != NS_STYLE_WINDOW_SHADOW_SHEET)
903 #endif
904     return;
906   // Create a CoreImage filter and set it up
907   CGSConnection cid = _CGSDefaultConnection();
908   CGSNewCIFilterByName(cid, (CFStringRef)@"CIGaussianBlur", &mWindowFilter);
909   NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:2.0] forKey:@"inputRadius"];
910   CGSSetCIFilterValuesFromDictionary(cid, mWindowFilter, (CFDictionaryRef)options);
912   // Now apply the filter to the window
913   NSInteger compositingType = 1 << 0; // Under the window
914   CGSAddWindowFilter(cid, [mWindow windowNumber], mWindowFilter, compositingType);
916   NS_OBJC_END_TRY_ABORT_BLOCK;
919 void
920 nsCocoaWindow::CleanUpWindowFilter()
922   if (!mWindowFilter || [mWindow windowNumber] == -1)
923     return;
925   CGSConnection cid = _CGSDefaultConnection();
926   CGSRemoveWindowFilter(cid, [mWindow windowNumber], mWindowFilter);
927   CGSReleaseCIFilter(cid, mWindowFilter);
928   mWindowFilter = 0;
931 nsresult
932 nsCocoaWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
934   if (mPopupContentView) {
935     mPopupContentView->ConfigureChildren(aConfigurations);
936   }
937   return NS_OK;
940 LayerManager*
941 nsCocoaWindow::GetLayerManager(LayerManagerPersistence, bool* aAllowRetaining)
943   if (mPopupContentView) {
944     return mPopupContentView->GetLayerManager(aAllowRetaining);
945   }
946   return nsnull;
949 void
950 nsCocoaWindow::DrawOver(LayerManager* aManager, nsIntRect aRect)
952   if (!([mWindow styleMask] & NSResizableWindowMask)) {
953     return;
954   }
956   nsRefPtr<LayerManagerOGL> manager(static_cast<LayerManagerOGL*>(aManager));
957   if (!manager) {
958     return;
959   }
960   
961   float bottomX = aRect.x + aRect.width;
962   float bottomY = aRect.y + aRect.height;
964   nsRefPtr<gfxQuartzSurface> image =
965     new gfxQuartzSurface(gfxIntSize(15, 15), gfxASurface::ImageFormatARGB32);
966   CGContextRef ctx = image->GetCGContext();
968   CGContextSetShouldAntialias(ctx, false);
969   CGPoint points[6];
970   points[0] = CGPointMake(13.0f, 4.0f);
971   points[1] = CGPointMake(3.0f, 14.0f);
972   points[2] = CGPointMake(13.0f, 8.0f);
973   points[3] = CGPointMake(7.0f, 14.0f);
974   points[4] = CGPointMake(13.0f, 12.0f);
975   points[5] = CGPointMake(11.0f, 14.0f);
976   CGContextSetRGBStrokeColor(ctx, 0.00f, 0.00f, 0.00f, 0.15f);
977   CGContextStrokeLineSegments(ctx, points, 6);
979   points[0] = CGPointMake(13.0f, 5.0f);
980   points[1] = CGPointMake(4.0f, 14.0f);
981   points[2] = CGPointMake(13.0f, 9.0f);
982   points[3] = CGPointMake(8.0f, 14.0f);
983   points[4] = CGPointMake(13.0f, 13.0f);
984   points[5] = CGPointMake(12.0f, 14.0f);
985   CGContextSetRGBStrokeColor(ctx, 0.13f, 0.13f, 0.13f, 0.54f);
986   CGContextStrokeLineSegments(ctx, points, 6);
988   points[0] = CGPointMake(13.0f, 6.0f);
989   points[1] = CGPointMake(5.0f, 14.0f);
990   points[2] = CGPointMake(13.0f, 10.0f);
991   points[3] = CGPointMake(9.0f, 14.0f);
992   points[5] = CGPointMake(13.0f, 13.9f);
993   points[4] = CGPointMake(13.0f, 14.0f);
994   CGContextSetRGBStrokeColor(ctx, 0.84f, 0.84f, 0.84f, 0.55f);
995   CGContextStrokeLineSegments(ctx, points, 6);
997   GLuint tex = 0;
999   ShaderProgramType shader = 
1000 #ifdef MOZ_ENABLE_LIBXUL
1001     manager->gl()->UploadSurfaceToTexture(image, nsIntRect(0, 0, 15, 15), tex);
1002 #else
1003     manager->gl()->UploadSurfaceToTextureExternal(image, nsIntRect(0, 0, 15, 15), tex);
1004 #endif
1006   ColorTextureLayerProgram *program =
1007     manager->GetColorTextureLayerProgram(shader);
1008   program->Activate();
1009   program->SetLayerQuadRect(nsIntRect(bottomX - 15,
1010                                       bottomY - 15,
1011                                       15,
1012                                       15));
1013   program->SetLayerTransform(gfx3DMatrix());
1014   program->SetLayerOpacity(1.0);
1015   program->SetRenderOffset(nsIntPoint(0,0));
1016   program->SetTextureUnit(0);
1018   manager->BindAndDrawQuad(program);
1019   manager->gl()->fDeleteTextures(1, &tex);
1022 nsTransparencyMode nsCocoaWindow::GetTransparencyMode()
1024   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
1026   return [mWindow isOpaque] ? eTransparencyOpaque : eTransparencyTransparent;
1028   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque);
1031 // This is called from nsMenuPopupFrame when making a popup transparent.
1032 // For other window types, nsChildView::SetTransparencyMode is used.
1033 void nsCocoaWindow::SetTransparencyMode(nsTransparencyMode aMode)
1035   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1037   BOOL isTransparent = aMode == eTransparencyTransparent;
1038   BOOL currentTransparency = ![mWindow isOpaque];
1039   if (isTransparent != currentTransparency) {
1040     [mWindow setOpaque:!isTransparent];
1041     [mWindow setBackgroundColor:(isTransparent ? [NSColor clearColor] : [NSColor whiteColor])];
1042   }
1044   NS_OBJC_END_TRY_ABORT_BLOCK;
1047 NS_IMETHODIMP nsCocoaWindow::Enable(PRBool aState)
1049   return NS_OK;
1052 NS_IMETHODIMP nsCocoaWindow::IsEnabled(PRBool *aState)
1054   if (aState)
1055     *aState = PR_TRUE;
1056   return NS_OK;
1059 NS_IMETHODIMP nsCocoaWindow::ConstrainPosition(PRBool aAllowSlop,
1060                                                PRInt32 *aX, PRInt32 *aY)
1062   return NS_OK;
1065 NS_IMETHODIMP nsCocoaWindow::Move(PRInt32 aX, PRInt32 aY)
1067   if (!mWindow || (mBounds.x == aX && mBounds.y == aY))
1068     return NS_OK;
1070   // The point we have is in Gecko coordinates (origin top-left). Convert
1071   // it to Cocoa ones (origin bottom-left).
1072   NSPoint coord = {aX, nsCocoaUtils::FlippedScreenY(aY)};
1073   [mWindow setFrameTopLeftPoint:coord];
1075   return NS_OK;
1078 // Position the window behind the given window
1079 NS_METHOD nsCocoaWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
1080                                      nsIWidget *aWidget, PRBool aActivate)
1082   return NS_OK;
1085 // Note bug 278777, we need to update state when the window is unminimized
1086 // from the dock by users.
1087 NS_METHOD nsCocoaWindow::SetSizeMode(PRInt32 aMode)
1089   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1091   PRInt32 previousMode;
1092   nsBaseWidget::GetSizeMode(&previousMode);
1094   nsresult rv = nsBaseWidget::SetSizeMode(aMode);
1095   NS_ENSURE_SUCCESS(rv, rv);
1097   if (aMode == nsSizeMode_Normal) {
1098     if ([mWindow isMiniaturized])
1099       [mWindow deminiaturize:nil];
1100     else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
1101       [mWindow zoom:nil];
1102   }
1103   else if (aMode == nsSizeMode_Minimized) {
1104     if (![mWindow isMiniaturized])
1105       [mWindow miniaturize:nil];
1106   }
1107   else if (aMode == nsSizeMode_Maximized) {
1108     if ([mWindow isMiniaturized])
1109       [mWindow deminiaturize:nil];
1110     if (![mWindow isZoomed])
1111       [mWindow zoom:nil];
1112   }
1114   return NS_OK;
1116   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1119 // This has to preserve the window's frame bounds.
1120 // This method requires (as does the Windows impl.) that you call Resize shortly
1121 // after calling HideWindowChrome. See bug 498835 for fixing this.
1122 NS_IMETHODIMP nsCocoaWindow::HideWindowChrome(PRBool aShouldHide)
1124   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1126   if (!mWindowMadeHere ||
1127       (mWindowType != eWindowType_toplevel && mWindowType != eWindowType_dialog))
1128     return NS_ERROR_FAILURE;
1130   BOOL isVisible = [mWindow isVisible];
1132   // Remove child windows.
1133   NSArray* childWindows = [mWindow childWindows];
1134   NSEnumerator* enumerator = [childWindows objectEnumerator];
1135   NSWindow* child = nil;
1136   while ((child = [enumerator nextObject])) {
1137     [mWindow removeChildWindow:child];
1138   }
1140   // Remove the content view.
1141   NSView* contentView = [mWindow contentView];
1142   [contentView retain];
1143   [contentView removeFromSuperviewWithoutNeedingDisplay];
1145   // Save state (like window title).
1146   NSMutableDictionary* state = [mWindow exportState];
1148   // Recreate the window with the right border style.
1149   NSRect frameRect = [mWindow frame];
1150   DestroyNativeWindow();
1151   nsresult rv = CreateNativeWindow(frameRect, aShouldHide ? eBorderStyle_none : mBorderStyle, PR_TRUE);
1152   NS_ENSURE_SUCCESS(rv, rv);
1154   // Re-import state.
1155   [mWindow importState:state];
1157   // Reparent the content view.
1158   [mWindow setContentView:contentView];
1159   [contentView release];
1161   // Reparent child windows.
1162   enumerator = [childWindows objectEnumerator];
1163   while ((child = [enumerator nextObject])) {
1164     [mWindow addChildWindow:child ordered:NSWindowAbove];
1165   }
1167   // Show the new window.
1168   if (isVisible) {
1169     rv = Show(PR_TRUE);
1170     NS_ENSURE_SUCCESS(rv, rv);
1171   }
1173   return NS_OK;
1175   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1179 NS_METHOD nsCocoaWindow::MakeFullScreen(PRBool aFullScreen)
1181   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1183   NS_ASSERTION(mFullScreen != aFullScreen, "Unnecessary MakeFullScreen call");
1185   NSDisableScreenUpdates();
1186   // The order here matters. When we exit full screen mode, we need to show the
1187   // Dock first, otherwise the newly-created window won't have its minimize
1188   // button enabled. See bug 526282.
1189   nsCocoaUtils::HideOSChromeOnScreen(aFullScreen, [mWindow screen]);
1190   nsresult rv = nsBaseWidget::MakeFullScreen(aFullScreen);
1191   NSEnableScreenUpdates();
1192   NS_ENSURE_SUCCESS(rv, rv);
1194   mFullScreen = aFullScreen;
1196   return NS_OK;
1198   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1202 NS_IMETHODIMP nsCocoaWindow::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1204   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1206   nsIntRect newBounds = nsIntRect(aX, aY, aWidth, aHeight);
1207   FitRectToVisibleAreaForScreen(newBounds, [mWindow screen]);
1209   BOOL isMoving = (mBounds.x != newBounds.x || mBounds.y != newBounds.y);
1210   BOOL isResizing = (mBounds.width != newBounds.width || mBounds.height != newBounds.height);
1212   if (!mWindow || (!isMoving && !isResizing))
1213     return NS_OK;
1215   NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(newBounds);
1217   // We ignore aRepaint -- we have to call display:YES, otherwise the
1218   // title bar doesn't immediately get repainted and is displayed in
1219   // the wrong place, leading to a visual jump.
1220   [mWindow setFrame:newFrame display:YES];
1222   return NS_OK;
1224   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1227 NS_IMETHODIMP nsCocoaWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
1229   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1230   
1231   return Resize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint);
1233   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1236 NS_IMETHODIMP nsCocoaWindow::GetClientBounds(nsIntRect &aRect)
1238   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1240   if ([mWindow isKindOfClass:[ToolbarWindow class]] &&
1241       [(ToolbarWindow*)mWindow drawsContentsIntoWindowFrame]) {
1242     aRect = nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]);
1243   } else {
1244     NSRect contentRect = [mWindow contentRectForFrameRect:[mWindow frame]];
1245     aRect = nsCocoaUtils::CocoaRectToGeckoRect(contentRect);
1246   }
1248   return NS_OK;
1250   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1253 void
1254 nsCocoaWindow::UpdateBounds()
1256   mBounds = nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]);
1259 NS_IMETHODIMP nsCocoaWindow::GetScreenBounds(nsIntRect &aRect)
1261   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1263   NS_ASSERTION(mBounds == nsCocoaUtils::CocoaRectToGeckoRect([mWindow frame]),
1264                "mBounds out of sync!");
1266   aRect = mBounds;
1267   return NS_OK;
1269   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1272 NS_IMETHODIMP nsCocoaWindow::SetCursor(nsCursor aCursor)
1274   if (mPopupContentView)
1275     return mPopupContentView->SetCursor(aCursor);
1277   return NS_OK;
1280 NS_IMETHODIMP nsCocoaWindow::SetCursor(imgIContainer* aCursor,
1281                                        PRUint32 aHotspotX, PRUint32 aHotspotY)
1283   if (mPopupContentView)
1284     return mPopupContentView->SetCursor(aCursor, aHotspotX, aHotspotY);
1286   return NS_OK;
1289 NS_IMETHODIMP nsCocoaWindow::SetTitle(const nsAString& aTitle)
1291   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1293   const nsString& strTitle = PromiseFlatString(aTitle);
1294   NSString* title = [NSString stringWithCharacters:strTitle.get() length:strTitle.Length()];
1295   [mWindow setTitle:title];
1297   return NS_OK;
1299   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1302 NS_IMETHODIMP nsCocoaWindow::Invalidate(const nsIntRect & aRect, PRBool aIsSynchronous)
1304   if (mPopupContentView)
1305     return mPopupContentView->Invalidate(aRect, aIsSynchronous);
1307   return NS_OK;
1310 NS_IMETHODIMP nsCocoaWindow::Update()
1312   if (mPopupContentView)
1313     return mPopupContentView->Update();
1315   return NS_OK;
1318 // Pass notification of some drag event to Gecko
1320 // The drag manager has let us know that something related to a drag has
1321 // occurred in this window. It could be any number of things, ranging from 
1322 // a drop, to a drag enter/leave, or a drag over event. The actual event
1323 // is passed in |aMessage| and is passed along to our event hanlder so Gecko
1324 // knows about it.
1325 PRBool nsCocoaWindow::DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers)
1327   return PR_FALSE;
1330 NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent()
1332   nsZLevelEvent event(PR_TRUE, NS_SETZLEVEL, this);
1334   event.refPoint.x = mBounds.x;
1335   event.refPoint.y = mBounds.y;
1336   event.time = PR_IntervalNow();
1338   event.mImmediate = PR_TRUE;
1340   nsEventStatus status = nsEventStatus_eIgnore;
1341   DispatchEvent(&event, status);
1343   return NS_OK;
1346 NS_IMETHODIMP nsCocoaWindow::GetChildSheet(PRBool aShown, nsCocoaWindow** _retval)
1348   nsIWidget* child = GetFirstChild();
1350   while (child) {
1351     nsWindowType type;
1352     if (NS_SUCCEEDED(child->GetWindowType(type)) && type == eWindowType_sheet) {
1353       // if it's a sheet, it must be an nsCocoaWindow
1354       nsCocoaWindow* cocoaWindow = static_cast<nsCocoaWindow*>(child);
1355       if ((aShown && [cocoaWindow->mWindow isVisible]) ||
1356           (!aShown && cocoaWindow->mSheetNeedsShow)) {
1357         *_retval = cocoaWindow;
1358         return NS_OK;
1359       }
1360     }
1361     child = child->GetNextSibling();
1362   }
1364   *_retval = nsnull;
1366   return NS_OK;
1369 NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent)
1371   *parent = mParent;
1372   return NS_OK;
1375 NS_IMETHODIMP nsCocoaWindow::GetIsSheet(PRBool* isSheet)
1377   mWindowType == eWindowType_sheet ? *isSheet = PR_TRUE : *isSheet = PR_FALSE;
1378   return NS_OK;
1381 NS_IMETHODIMP nsCocoaWindow::GetSheetWindowParent(NSWindow** sheetWindowParent)
1383   *sheetWindowParent = mSheetWindowParent;
1384   return NS_OK;
1387 NS_IMETHODIMP nsCocoaWindow::ResetInputState()
1389   return NS_OK;
1392 // Invokes callback and ProcessEvent methods on Event Listener object
1393 NS_IMETHODIMP 
1394 nsCocoaWindow::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
1396   aStatus = nsEventStatus_eIgnore;
1398   nsIWidget* aWidget = event->widget;
1399   NS_IF_ADDREF(aWidget);
1401   if (mEventCallback)
1402     aStatus = (*mEventCallback)(event);
1404   NS_IF_RELEASE(aWidget);
1406   return NS_OK;
1409 static nsSizeMode
1410 GetWindowSizeMode(NSWindow* aWindow) {
1411   if ([aWindow isMiniaturized])
1412     return nsSizeMode_Minimized;
1413   if (([aWindow styleMask] & NSResizableWindowMask) && [aWindow isZoomed])
1414     return nsSizeMode_Maximized;
1415   return nsSizeMode_Normal;
1418 void
1419 nsCocoaWindow::ReportMoveEvent()
1421   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1423   UpdateBounds();
1425   // Dispatch the move event to Gecko
1426   nsGUIEvent guiEvent(PR_TRUE, NS_MOVE, this);
1427   guiEvent.refPoint.x = mBounds.x;
1428   guiEvent.refPoint.y = mBounds.y;
1429   guiEvent.time = PR_IntervalNow();
1430   nsEventStatus status = nsEventStatus_eIgnore;
1431   DispatchEvent(&guiEvent, status);
1433   NS_OBJC_END_TRY_ABORT_BLOCK;
1436 void
1437 nsCocoaWindow::DispatchSizeModeEvent()
1439   nsSizeModeEvent event(PR_TRUE, NS_SIZEMODE, this);
1440   event.mSizeMode = GetWindowSizeMode(mWindow);
1441   event.time = PR_IntervalNow();
1443   nsEventStatus status = nsEventStatus_eIgnore;
1444   DispatchEvent(&event, status);
1447 void
1448 nsCocoaWindow::ReportSizeEvent()
1450   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1452   UpdateBounds();
1454   nsSizeEvent sizeEvent(PR_TRUE, NS_SIZE, this);
1455   sizeEvent.time = PR_IntervalNow();
1457   nsIntRect innerBounds;
1458   GetClientBounds(innerBounds);
1459   sizeEvent.windowSize = &innerBounds;
1460   sizeEvent.mWinWidth  = mBounds.width;
1461   sizeEvent.mWinHeight = mBounds.height;
1463   nsEventStatus status = nsEventStatus_eIgnore;
1464   DispatchEvent(&sizeEvent, status);
1466   NS_OBJC_END_TRY_ABORT_BLOCK;
1469 void nsCocoaWindow::SetMenuBar(nsMenuBarX *aMenuBar)
1471   if (mMenuBar)
1472     mMenuBar->SetParent(nsnull);
1473   mMenuBar = aMenuBar;
1475   // Only paint for active windows, or paint the hidden window menu bar if no
1476   // other menu bar has been painted yet so that some reasonable menu bar is
1477   // displayed when the app starts up.
1478   id windowDelegate = [mWindow delegate];
1479   if (mMenuBar &&
1480       ((!gSomeMenuBarPainted && nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar) ||
1481        (windowDelegate && [windowDelegate toplevelActiveState])))
1482     mMenuBar->Paint();
1485 NS_IMETHODIMP nsCocoaWindow::SetFocus(PRBool aState)
1487   if (mPopupContentView) {
1488     mPopupContentView->SetFocus(aState);
1489   }
1490   else if (aState && ([mWindow isVisible] || [mWindow isMiniaturized])) {
1491     [mWindow setAcceptsMouseMovedEvents:YES];
1492     [mWindow makeKeyAndOrderFront:nil];
1493     SendSetZLevelEvent();
1494   }
1496   return NS_OK;
1499 nsIntPoint nsCocoaWindow::WidgetToScreenOffset()
1501   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
1503   nsIntRect r = nsCocoaUtils::CocoaRectToGeckoRect([mWindow contentRectForFrameRect:[mWindow frame]]);
1505   return r.TopLeft();
1507   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0,0));
1510 nsIntPoint nsCocoaWindow::GetClientOffset()
1512   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
1514   nsIntRect clientRect;
1515   GetClientBounds(clientRect);
1517   return clientRect.TopLeft() - mBounds.TopLeft();
1519   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0, 0));
1522 nsIntSize nsCocoaWindow::ClientToWindowSize(const nsIntSize& aClientSize)
1524   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
1526   // this is only called for popups currently. If needed, expand this to support
1527   // other types of windows
1528   if (!IsPopupWithTitleBar())
1529     return aClientSize;
1531   NSRect rect(NSMakeRect(0.0, 0.0, aClientSize.width, aClientSize.height));
1533   NSRect inflatedRect = [mWindow frameRectForContentRect:rect];
1534   return nsCocoaUtils::CocoaRectToGeckoRect(inflatedRect).Size();
1536   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntSize(0,0));
1539 nsMenuBarX* nsCocoaWindow::GetMenuBar()
1541   return mMenuBar;
1544 NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener * aListener, 
1545                                                  nsIMenuRollup * aMenuRollup,
1546                                                  PRBool aDoCapture, 
1547                                                  PRBool aConsumeRollupEvent)
1549   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1551   gRollupListener = nsnull;
1552   NS_IF_RELEASE(gMenuRollup);
1553   NS_IF_RELEASE(gRollupWidget);
1554   
1555   if (aDoCapture) {
1556     gRollupListener = aListener;
1557     NS_IF_RELEASE(gMenuRollup);
1558     gMenuRollup = aMenuRollup;
1559     NS_IF_ADDREF(aMenuRollup);
1560     gRollupWidget = this;
1561     NS_ADDREF(this);
1563     gConsumeRollupEvent = aConsumeRollupEvent;
1565     // Sometimes more than one popup window can be visible at the same time
1566     // (e.g. nested non-native context menus, or the test case (attachment
1567     // 276885) for bmo bug 392389, which displays a non-native combo-box in
1568     // a non-native popup window).  In these cases the "active" popup window
1569     // (the one that corresponds to the current gRollupWidget) should be the
1570     // topmost -- the (nested) context menu the mouse is currently over, or
1571     // the combo-box's drop-down list (when it's displayed).  But (among
1572     // windows that have the same "level") OS X makes topmost the window that
1573     // last received a mouse-down event, which may be incorrect (in the combo-
1574     // box case, it makes topmost the window containing the combo-box).  So
1575     // here we fiddle with a non-native popup window's level to make sure the
1576     // "active" one is always above any other non-native popup windows that
1577     // may be visible.
1578     if (mWindow && (mWindowType == eWindowType_popup))
1579       SetPopupWindowLevel();
1580   } else {
1581     // XXXndeakin this doesn't make sense.
1582     // Why is the new window assumed to be a modal panel?
1583     if (mWindow && (mWindowType == eWindowType_popup))
1584       [mWindow setLevel:NSModalPanelWindowLevel];
1585   }
1586   
1587   return NS_OK;
1589   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1592 NS_IMETHODIMP nsCocoaWindow::GetAttention(PRInt32 aCycleCount)
1594   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1596   [NSApp requestUserAttention:NSInformationalRequest];
1597   return NS_OK;
1599   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1602 PRBool
1603 nsCocoaWindow::HasPendingInputEvent()
1605   return nsChildView::DoHasPendingInputEvent();
1608 NS_IMETHODIMP nsCocoaWindow::SetWindowShadowStyle(PRInt32 aStyle)
1610   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1612   mShadowStyle = aStyle;
1613   [mWindow setHasShadow:(aStyle != NS_STYLE_WINDOW_SHADOW_NONE)];
1614   AdjustWindowShadow();
1615   SetUpWindowFilter();
1617   return NS_OK;
1619   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1622 void nsCocoaWindow::SetShowsToolbarButton(PRBool aShow)
1624   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1626   [mWindow setShowsToolbarButton:aShow];
1628   NS_OBJC_END_TRY_ABORT_BLOCK;
1631 NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, PRBool aActive)
1633   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1635   // If they pass a color with a complete transparent alpha component, use the
1636   // native titlebar appearance.
1637   if (NS_GET_A(aColor) == 0) {
1638     [mWindow setTitlebarColor:nil forActiveWindow:(BOOL)aActive]; 
1639   } else {
1640     // Transform from sRGBA to monitor RGBA. This seems like it would make trying
1641     // to match the system appearance lame, so probably we just shouldn't color 
1642     // correct chrome.
1643     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
1644       qcms_transform *transform = gfxPlatform::GetCMSRGBATransform();
1645       if (transform) {
1646         PRUint8 color[3];
1647         color[0] = NS_GET_R(aColor);
1648         color[1] = NS_GET_G(aColor);
1649         color[2] = NS_GET_B(aColor);
1650         qcms_transform_data(transform, color, color, 1);
1651         aColor = NS_RGB(color[0], color[1], color[2]);
1652       }
1653     }
1655     [mWindow setTitlebarColor:[NSColor colorWithDeviceRed:NS_GET_R(aColor)/255.0
1656                                                     green:NS_GET_G(aColor)/255.0
1657                                                      blue:NS_GET_B(aColor)/255.0
1658                                                     alpha:NS_GET_A(aColor)/255.0]
1659               forActiveWindow:(BOOL)aActive];
1660   }
1661   return NS_OK;
1663   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1666 void nsCocoaWindow::SetDrawsInTitlebar(PRBool aState)
1668   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1670   [mWindow setDrawsContentsIntoWindowFrame:aState];
1672   NS_OBJC_END_TRY_ABORT_BLOCK;
1675 NS_IMETHODIMP nsCocoaWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
1676                                                         PRUint32 aNativeMessage,
1677                                                         PRUint32 aModifierFlags)
1679   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1681   if (mPopupContentView)
1682     return mPopupContentView->SynthesizeNativeMouseEvent(aPoint, aNativeMessage,
1683                                                          aModifierFlags);
1685   return NS_OK;
1687   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1690 gfxASurface* nsCocoaWindow::GetThebesSurface()
1692   if (mPopupContentView)
1693     return mPopupContentView->GetThebesSurface();
1694   return nsnull;
1697 NS_IMETHODIMP nsCocoaWindow::BeginSecureKeyboardInput()
1699   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1701   nsresult rv = nsBaseWidget::BeginSecureKeyboardInput();
1702   if (NS_SUCCEEDED(rv)) {
1703     ::EnableSecureEventInput();
1704   }
1705   return rv;
1707   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1710 NS_IMETHODIMP nsCocoaWindow::EndSecureKeyboardInput()
1712   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1714   nsresult rv = nsBaseWidget::EndSecureKeyboardInput();
1715   if (NS_SUCCEEDED(rv)) {
1716     ::DisableSecureEventInput();
1717   }
1718   return rv;
1720   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1723 // Callback used by the default titlebar and toolbar shading.
1724 // *aIn == 0 at the top of the titlebar/toolbar, *aIn == 1 at the bottom
1725 /* static */ void
1726 nsCocoaWindow::UnifiedShading(void* aInfo, const CGFloat* aIn, CGFloat* aOut)
1728   UnifiedGradientInfo* info = (UnifiedGradientInfo*)aInfo;
1729   // The gradient percentage at the bottom of the titlebar / top of the toolbar
1730   float start = info->titlebarHeight / (info->titlebarHeight + info->toolbarHeight - 1);
1731   const float startGrey = NativeGreyColorAsFloat(headerStartGrey, info->windowIsMain);
1732   const float endGrey = NativeGreyColorAsFloat(headerEndGrey, info->windowIsMain);
1733   // *aIn is the gradient percentage of the titlebar or toolbar gradient,
1734   // a is the gradient percentage of the whole unified gradient.
1735   float a = info->drawTitlebar ? *aIn * start : start + *aIn * (1 - start);
1736   float result = (1.0f - a) * startGrey + a * endGrey;
1737   aOut[0] = result;
1738   aOut[1] = result;
1739   aOut[2] = result;
1740   aOut[3] = 1.0f;
1743 void nsCocoaWindow::SetPopupWindowLevel()
1745   // Floating popups are at the floating level and hide when the window is
1746   // deactivated.
1747   if (mPopupLevel == ePopupLevelFloating) {
1748     [mWindow setLevel:NSFloatingWindowLevel];
1749     [mWindow setHidesOnDeactivate:YES];
1750   }
1751   else {
1752     // Otherwise, this is a top-level or parent popup. Parent popups always
1753     // appear just above their parent and essentially ignore the level.
1754     [mWindow setLevel:NSPopUpMenuWindowLevel];
1755     [mWindow setHidesOnDeactivate:NO];
1756   }
1759 PRBool nsCocoaWindow::IsChildInFailingLeftClickThrough(NSView *aChild)
1761   if ([aChild isKindOfClass:[ChildView class]]) {
1762     ChildView* childView = (ChildView*) aChild;
1763     if ([childView isInFailingLeftClickThrough])
1764       return PR_TRUE;
1765   }
1766   NSArray* subviews = [aChild subviews];
1767   if (subviews) {
1768     NSUInteger count = [subviews count];
1769     for (NSUInteger i = 0; i < count; ++i) {
1770       NSView* aView = (NSView*) [subviews objectAtIndex:i];
1771       if (IsChildInFailingLeftClickThrough(aView))
1772         return PR_TRUE;
1773     }
1774   }
1775   return PR_FALSE;
1778 // Don't focus a plugin if we're in a left click-through that will
1779 // fail (see [ChildView isInFailingLeftClickThrough]).  Called from
1780 // [ChildView shouldFocusPlugin].
1781 PRBool nsCocoaWindow::ShouldFocusPlugin()
1783   if (IsChildInFailingLeftClickThrough([mWindow contentView]))
1784     return PR_FALSE;
1786   return PR_TRUE;
1789 @implementation WindowDelegate
1791 // We try to find a gecko menu bar to paint. If one does not exist, just paint
1792 // the application menu by itself so that a window doesn't have some other
1793 // window's menu bar.
1794 + (void)paintMenubarForWindow:(NSWindow*)aWindow
1796   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1798   // make sure we only act on windows that have this kind of
1799   // object as a delegate
1800   id windowDelegate = [aWindow delegate];
1801   if ([windowDelegate class] != [self class])
1802     return;
1804   nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget];
1805   NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!");
1806   
1807   nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar();
1808   if (geckoMenuBar) {
1809     geckoMenuBar->Paint();
1810   }
1811   else {
1812     // sometimes we don't have a native application menu early in launching
1813     if (!sApplicationMenu)
1814       return;
1816     NSMenu* mainMenu = [NSApp mainMenu];
1817     NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
1819     // Create a new menu bar.
1820     // We create a GeckoNSMenu because all menu bar NSMenu objects should use that subclass for
1821     // key handling reasons.
1822     GeckoNSMenu* newMenuBar = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
1824     // move the application menu from the existing menu bar to the new one
1825     NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
1826     [mainMenu removeItemAtIndex:0];
1827     [newMenuBar insertItem:firstMenuItem atIndex:0];
1828     [firstMenuItem release];
1830     // set our new menu bar as the main menu
1831     [NSApp setMainMenu:newMenuBar];
1832     [newMenuBar release];
1833   }
1835   NS_OBJC_END_TRY_ABORT_BLOCK;
1838 - (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind
1840   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
1842   [super init];
1843   mGeckoWindow = geckoWind;
1844   mToplevelActiveState = PR_FALSE;
1845   mHasEverBeenZoomed = PR_FALSE;
1846   return self;
1848   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
1851 - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
1853   RollUpPopups();
1854   
1855   return proposedFrameSize;
1858 - (void)windowDidResize:(NSNotification *)aNotification
1860   if (!mGeckoWindow)
1861     return;
1863   // Resizing might have changed our zoom state.
1864   mGeckoWindow->DispatchSizeModeEvent();
1865   mGeckoWindow->ReportSizeEvent();
1868 - (void)windowDidBecomeMain:(NSNotification *)aNotification
1870   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1872   RollUpPopups();
1873   ChildViewMouseTracker::ReEvaluateMouseEnterState();
1875   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
1876   // app modally. If one of those is up then we want it to retain its menu bar.
1877   if ([NSApp _isRunningAppModal])
1878     return;
1879   NSWindow* window = [aNotification object];
1880   if (window)
1881     [WindowDelegate paintMenubarForWindow:window];
1883   NS_OBJC_END_TRY_ABORT_BLOCK;
1886 - (void)windowDidResignMain:(NSNotification *)aNotification
1888   RollUpPopups();
1889   ChildViewMouseTracker::ReEvaluateMouseEnterState();
1891   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
1892   // app modally. If one of those is up then we want it to retain its menu bar.
1893   if ([NSApp _isRunningAppModal])
1894     return;
1895   nsRefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
1896   if (hiddenWindowMenuBar) {
1897     // printf("painting hidden window menu bar due to window losing main status\n");
1898     hiddenWindowMenuBar->Paint();
1899   }
1902 - (void)windowDidBecomeKey:(NSNotification *)aNotification
1904   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1906   RollUpPopups();
1907   ChildViewMouseTracker::ReEvaluateMouseEnterState();
1909   NSWindow* window = [aNotification object];
1910   if ([window isSheet])
1911     [WindowDelegate paintMenubarForWindow:window];
1913   NS_OBJC_END_TRY_ABORT_BLOCK;
1916 - (void)windowDidResignKey:(NSNotification *)aNotification
1918   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1920   RollUpPopups();
1921   ChildViewMouseTracker::ReEvaluateMouseEnterState();
1923   // If a sheet just resigned key then we should paint the menu bar
1924   // for whatever window is now main.
1925   NSWindow* window = [aNotification object];
1926   if ([window isSheet])
1927     [WindowDelegate paintMenubarForWindow:[NSApp mainWindow]];
1929   NS_OBJC_END_TRY_ABORT_BLOCK;
1932 - (void)windowWillMove:(NSNotification *)aNotification
1934   RollUpPopups();
1937 - (void)windowDidMove:(NSNotification *)aNotification
1939   if (mGeckoWindow)
1940     mGeckoWindow->ReportMoveEvent();
1943 - (BOOL)windowShouldClose:(id)sender
1945   // We only want to send NS_XUL_CLOSE and let gecko close the window
1946   nsGUIEvent guiEvent(PR_TRUE, NS_XUL_CLOSE, mGeckoWindow);
1947   guiEvent.time = PR_IntervalNow();
1948   nsEventStatus status = nsEventStatus_eIgnore;
1949   mGeckoWindow->DispatchEvent(&guiEvent, status);
1950   return NO; // gecko will do it
1953 - (void)windowWillClose:(NSNotification *)aNotification
1955   RollUpPopups();
1958 - (void)windowWillMiniaturize:(NSNotification *)aNotification
1960   RollUpPopups();
1963 - (void)windowDidMiniaturize:(NSNotification *)aNotification
1965   if (mGeckoWindow)
1966     mGeckoWindow->DispatchSizeModeEvent();
1969 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
1971   if (mGeckoWindow)
1972     mGeckoWindow->DispatchSizeModeEvent();
1975 - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)proposedFrame
1977   if (!mHasEverBeenZoomed && [window isZoomed])
1978     return NO; // See bug 429954.
1980   mHasEverBeenZoomed = YES;
1981   return YES;
1984 - (void)sendFocusEvent:(PRUint32)eventType
1986   if (!mGeckoWindow)
1987     return;
1989   nsEventStatus status = nsEventStatus_eIgnore;
1990   nsGUIEvent focusGuiEvent(PR_TRUE, eventType, mGeckoWindow);
1991   focusGuiEvent.time = PR_IntervalNow();
1992   mGeckoWindow->DispatchEvent(&focusGuiEvent, status);
1995 - (void)didEndSheet:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
1997   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1999   // Note: 'contextInfo' (if it is set) is the window that is the parent of
2000   // the sheet.  The value of contextInfo is determined in
2001   // nsCocoaWindow::Show().  If it's set, 'contextInfo' is always the top-
2002   // level window, not another sheet itself.  But 'contextInfo' is nil if
2003   // our parent window is also a sheet -- in that case we shouldn't send
2004   // the top-level window any activate events (because it's our parent
2005   // window that needs to get these events, not the top-level window).
2006   [TopLevelWindowData deactivateInWindow:sheet];
2007   [sheet orderOut:self];
2008   if (contextInfo)
2009     [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo];
2011   NS_OBJC_END_TRY_ABORT_BLOCK;
2014 - (nsCocoaWindow*)geckoWidget
2016   return mGeckoWindow;
2019 - (PRBool)toplevelActiveState
2021   return mToplevelActiveState;
2024 - (void)sendToplevelActivateEvents
2026   if (!mToplevelActiveState) {
2027     [self sendFocusEvent:NS_ACTIVATE];
2028     mToplevelActiveState = PR_TRUE;
2029   }
2032 - (void)sendToplevelDeactivateEvents
2034   if (mToplevelActiveState) {
2035     [self sendFocusEvent:NS_DEACTIVATE];
2036     mToplevelActiveState = PR_FALSE;
2037   }
2040 @end
2042 static float
2043 GetDPI(NSWindow* aWindow)
2045   NSScreen* screen = [aWindow screen];
2046   if (!screen)
2047     return 96.0f;
2049   CGDirectDisplayID displayID =
2050     [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
2051   CGFloat heightMM = ::CGDisplayScreenSize(displayID).height;
2052   size_t heightPx = ::CGDisplayPixelsHigh(displayID);
2053   CGFloat scaleFactor = [aWindow userSpaceScaleFactor];
2054   if (scaleFactor < 0.01 || heightMM < 1 || heightPx < 1) {
2055     // Something extremely bogus is going on
2056     return 96.0f;
2057   }
2059   // Currently we don't do our own scaling to take account
2060   // of userSpaceScaleFactor, so every "pixel" we draw is actually
2061   // userSpaceScaleFactor screen pixels. So divide the screen height
2062   // by userSpaceScaleFactor to get the number of "device pixels"
2063   // available.
2064   return (heightPx / scaleFactor) / (heightMM / MM_PER_INCH_FLOAT);
2067 @implementation BaseWindow
2069 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
2071   [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag];
2072   mState = nil;
2073   mDrawsIntoWindowFrame = NO;
2074   mActiveTitlebarColor = nil;
2075   mInactiveTitlebarColor = nil;
2076   mScheduledShadowInvalidation = NO;
2077   mDPI = GetDPI(self);
2079   return self;
2082 - (void)dealloc
2084   [mActiveTitlebarColor release];
2085   [mInactiveTitlebarColor release];
2086   [super dealloc];
2089 static const NSString* kStateTitleKey = @"title";
2090 static const NSString* kStateDrawsContentsIntoWindowFrameKey = @"drawsContentsIntoWindowFrame";
2091 static const NSString* kStateActiveTitlebarColorKey = @"activeTitlebarColor";
2092 static const NSString* kStateInactiveTitlebarColorKey = @"inactiveTitlebarColor";
2093 static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
2095 - (void)importState:(NSDictionary*)aState
2097   [self setTitle:[aState objectForKey:kStateTitleKey]];
2098   [self setDrawsContentsIntoWindowFrame:[[aState objectForKey:kStateDrawsContentsIntoWindowFrameKey] boolValue]];
2099   [self setTitlebarColor:[aState objectForKey:kStateActiveTitlebarColorKey] forActiveWindow:YES];
2100   [self setTitlebarColor:[aState objectForKey:kStateInactiveTitlebarColorKey] forActiveWindow:NO];
2101   [self setShowsToolbarButton:[[aState objectForKey:kStateShowsToolbarButton] boolValue]];
2104 - (NSMutableDictionary*)exportState
2106   NSMutableDictionary* state = [NSMutableDictionary dictionaryWithCapacity:10];
2107   [state setObject:[self title] forKey:kStateTitleKey];
2108   [state setObject:[NSNumber numberWithBool:[self drawsContentsIntoWindowFrame]]
2109             forKey:kStateDrawsContentsIntoWindowFrameKey];
2110   NSColor* activeTitlebarColor = [self titlebarColorForActiveWindow:YES];
2111   if (activeTitlebarColor) {
2112     [state setObject:activeTitlebarColor forKey:kStateActiveTitlebarColorKey];
2113   }
2114   NSColor* inactiveTitlebarColor = [self titlebarColorForActiveWindow:NO];
2115   if (inactiveTitlebarColor) {
2116     [state setObject:inactiveTitlebarColor forKey:kStateInactiveTitlebarColorKey];
2117   }
2118   [state setObject:[NSNumber numberWithBool:[self showsToolbarButton]]
2119             forKey:kStateShowsToolbarButton];
2120   return state;
2123 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
2125   mDrawsIntoWindowFrame = aState;
2128 - (BOOL)drawsContentsIntoWindowFrame
2130   return mDrawsIntoWindowFrame;
2133 // Pass nil here to get the default appearance.
2134 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
2136   [aColor retain];
2137   if (aActive) {
2138     [mActiveTitlebarColor release];
2139     mActiveTitlebarColor = aColor;
2140   } else {
2141     [mInactiveTitlebarColor release];
2142     mInactiveTitlebarColor = aColor;
2143   }
2146 - (NSColor*)titlebarColorForActiveWindow:(BOOL)aActive
2148   return aActive ? mActiveTitlebarColor : mInactiveTitlebarColor;
2151 - (void)deferredInvalidateShadow
2153   if (mScheduledShadowInvalidation || [self isOpaque] || ![self hasShadow])
2154     return;
2156   [self performSelector:@selector(invalidateShadow) withObject:nil afterDelay:0];
2157   mScheduledShadowInvalidation = YES;
2160 - (void)invalidateShadow
2162   [super invalidateShadow];
2163   mScheduledShadowInvalidation = NO;
2166 - (float)getDPI
2168   return mDPI;
2171 - (BOOL)respondsToSelector:(SEL)aSelector
2173   // Claim the window doesn't respond to this so that the system
2174   // doesn't steal keyboard equivalents for it. Bug 613710.
2175   if (aSelector == @selector(cancelOperation:)) {
2176     return NO;
2177   }
2179   return [super respondsToSelector:aSelector];
2182 - (void) doCommandBySelector:(SEL)aSelector
2184   // We override this so that it won't beep if it can't act.
2185   // We want to control the beeping for missing or disabled
2186   // commands ourselves.
2187   [self tryToPerform:aSelector with:nil];
2190 @end
2192 // This class allows us to have a "unified toolbar" style window. It works like this:
2193 // 1) We set the window's style to textured.
2194 // 2) Because of this, the background color applies to the entire window, including
2195 //     the titlebar area. For normal textured windows, the default pattern is a 
2196 //    "brushed metal" image on Tiger and a unified gradient on Leopard.
2197 // 3) We set the background color to a custom NSColor subclass that knows how tall the window is.
2198 //    When -set is called on it, it sets a pattern (with a draw callback) as the fill. In that callback,
2199 //    it paints the the titlebar and background colors in the correct areas of the context it's given,
2200 //    which will fill the entire window (CG will tile it horizontally for us).
2201 // 4) Whenever the window's main state changes and when [window display] is called,
2202 //    Cocoa redraws the titlebar using the patternDraw callback function.
2204 // This class also provides us with a pill button to show/hide the toolbar.
2206 // Drawing the unified gradient in the titlebar and the toolbar works like this:
2207 // 1) In the style sheet we set the toolbar's -moz-appearance to -moz-mac-unified-toolbar.
2208 // 2) When the toolbar is visible and we paint the application chrome
2209 //    window, the array that Gecko passes nsChildView::UpdateThemeGeometries
2210 //    will contain an entry for the widget type NS_THEME_TOOLBAR or
2211 //    NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
2212 // 3) nsChildView::UpdateThemeGeometries finds the toolbar frame's ToolbarWindow
2213 //    and passes the toolbar frame's height to setUnifiedToolbarHeight.
2214 // 4) If the toolbar height has changed, a titlebar redraw is triggered and the
2215 //    upper part of the unified gradient is drawn in the titlebar.
2216 // 5) The lower part of the unified gradient in the toolbar is drawn during
2217 //    normal window content painting in nsNativeThemeCocoa::DrawUnifiedToolbar.
2219 // Whenever the unified gradient is drawn in the titlebar or the toolbar, both
2220 // titlebar height and toolbar height must be known in order to construct the
2221 // correct gradient (which is a linear gradient with the length
2222 // titlebarHeight + toolbarHeight - 1). But you can only get from the toolbar frame
2223 // to the containing window - the other direction doesn't work. That's why the
2224 // toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
2225 // query the window for its titlebar height when drawing the toolbar.
2226 @implementation ToolbarWindow
2228 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
2230   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2232   aStyle = aStyle | NSTexturedBackgroundWindowMask;
2233   if ((self = [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag])) {
2234     mColor = [[TitlebarAndBackgroundColor alloc] initWithWindow:self];
2235     // Bypass our guard method below.
2236     [super setBackgroundColor:mColor];
2237     mBackgroundColor = [NSColor whiteColor];
2239     mUnifiedToolbarHeight = 0.0f;
2241     // setBottomCornerRounded: is a private API call, so we check to make sure
2242     // we respond to it just in case.
2243     if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
2244       [self setBottomCornerRounded:NO];
2246     [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
2247     [self setContentBorderThickness:0.0f forEdge:NSMaxYEdge];
2248   }
2249   return self;
2251   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2254 - (void)dealloc
2256   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2258   [mColor release];
2259   [mBackgroundColor release];
2260   [super dealloc];
2262   NS_OBJC_END_TRY_ABORT_BLOCK;
2265 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
2267   [super setTitlebarColor:aColor forActiveWindow:aActive];
2268   [self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
2271 - (void)setBackgroundColor:(NSColor*)aColor
2273   [aColor retain];
2274   [mBackgroundColor release];
2275   mBackgroundColor = aColor;
2278 - (NSColor*)windowBackgroundColor
2280   return mBackgroundColor;
2283 - (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect
2285   [self setTitlebarNeedsDisplayInRect:aRect sync:NO];
2288 - (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync
2290   NSRect titlebarRect = [self titlebarRect];
2291   NSRect rect = NSIntersectionRect(titlebarRect, aRect);
2292   if (NSIsEmptyRect(rect))
2293     return;
2295   NSView* borderView = [[self contentView] superview];
2296   if (!borderView)
2297     return;
2299   if (aSync) {
2300     [borderView displayRect:rect];
2301   } else {
2302     [borderView setNeedsDisplayInRect:rect];
2303   }
2306 - (NSRect)titlebarRect
2308   return NSMakeRect(0, [[self contentView] bounds].size.height,
2309                     [self frame].size.width, [self titlebarHeight]);
2312 - (float)unifiedToolbarHeight
2314   return mUnifiedToolbarHeight;
2317 - (float)titlebarHeight
2319   NSRect frameRect = [self frame];
2320   return frameRect.size.height - [self contentRectForFrameRect:frameRect].size.height;
2323 - (void)setUnifiedToolbarHeight:(float)aHeight
2325   if ([self drawsContentsIntoWindowFrame] || aHeight == mUnifiedToolbarHeight)
2326     return;
2328   mUnifiedToolbarHeight = aHeight;
2330   // Update sheet positioning hint.
2331   [self setContentBorderThickness:mUnifiedToolbarHeight forEdge:NSMaxYEdge];
2333   // Redraw the title bar. If we're inside painting, we'll do it right now,
2334   // otherwise we'll just invalidate it.
2335   BOOL needSyncRedraw = ([NSView focusView] != nil);
2336   [self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw];
2339 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
2341   BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState);
2342   [super setDrawsContentsIntoWindowFrame:aState];
2343   if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) {
2344     WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
2345     nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
2346     if (geckoWindow) {
2347       // Re-layout our contents.
2348       geckoWindow->ReportSizeEvent();
2349     }
2350     [self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
2351   }
2354 // Returning YES here makes the setShowsToolbarButton method work even though
2355 // the window doesn't contain an NSToolbar.
2356 - (BOOL)_hasToolbar
2358   return YES;
2361 // Dispatch a toolbar pill button clicked message to Gecko.
2362 - (void)_toolbarPillButtonClicked:(id)sender
2364   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2366   RollUpPopups();
2368   if ([[self delegate] isKindOfClass:[WindowDelegate class]]) {
2369     WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
2370     nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
2371     if (!geckoWindow)
2372       return;
2373     nsEventStatus status = nsEventStatus_eIgnore;
2374     nsGUIEvent guiEvent(PR_TRUE, NS_OS_TOOLBAR, geckoWindow);
2375     guiEvent.time = PR_IntervalNow();
2376     geckoWindow->DispatchEvent(&guiEvent, status);
2377   }
2379   NS_OBJC_END_TRY_ABORT_BLOCK;
2382 // Retain and release "self" to avoid crashes when our widget (and its native
2383 // window) is closed as a result of processing a key equivalent (e.g.
2384 // Command+w or Command+q).  This workaround is only needed for a window
2385 // that can become key.
2386 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
2388   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2390   NSWindow *nativeWindow = [self retain];
2391   BOOL retval = [super performKeyEquivalent:theEvent];
2392   [nativeWindow release];
2393   return retval;
2395   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2398 - (void)sendEvent:(NSEvent *)anEvent
2400   NSEventType type = [anEvent type];
2401   
2402   switch (type) {
2403     case NSScrollWheel:
2404     case NSLeftMouseDown:
2405     case NSLeftMouseUp:
2406     case NSRightMouseDown:
2407     case NSRightMouseUp:
2408     case NSOtherMouseDown:
2409     case NSOtherMouseUp:
2410     case NSMouseMoved:
2411     case NSLeftMouseDragged:
2412     case NSRightMouseDragged:
2413     case NSOtherMouseDragged:
2414     {
2415       // Drop all mouse events if a modal window has appeared above us.
2416       // This helps make us behave as if the OS were running a "real" modal
2417       // event loop.
2418       id delegate = [self delegate];
2419       if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
2420         nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
2421         if (widget) {
2422           if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
2423             return;
2424           if (widget->HasModalDescendents())
2425             return;
2426         }
2427       }
2428       break;
2429     }
2430     default:
2431       break;
2432   }
2434   [super sendEvent:anEvent];
2437 @end
2439 // Custom NSColor subclass where most of the work takes place for drawing in
2440 // the titlebar area.
2441 @implementation TitlebarAndBackgroundColor
2443 - (id)initWithWindow:(ToolbarWindow*)aWindow
2445   if ((self = [super init])) {
2446     mWindow = aWindow; // weak ref to avoid a cycle
2447   }
2448   return self;
2451 // Our pattern width is 1 pixel. CoreGraphics can cache and tile for us.
2452 static const float sPatternWidth = 1.0f;
2454 static void
2455 DrawTitlebarGradient(CGContextRef aContext, float aTitlebarHeight,
2456                      float aTitlebarOrigin, float aToolbarHeight, BOOL aIsMain)
2458   // Create and draw a CGShading that uses nsCocoaWindow::UnifiedShading() as its callback.
2459   CGFunctionCallbacks callbacks = {0, nsCocoaWindow::UnifiedShading, NULL};
2460   UnifiedGradientInfo info = { aTitlebarHeight, aToolbarHeight, aIsMain, YES };
2461   CGFunctionRef function = CGFunctionCreate(&info, 1, NULL, 4, NULL, &callbacks);
2462   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
2463   CGShadingRef shading = CGShadingCreateAxial(colorSpace,
2464                                               CGPointMake(0.0f, aTitlebarOrigin + aTitlebarHeight),
2465                                               CGPointMake(0.0f, aTitlebarOrigin),
2466                                               function, NO, NO);
2467   CGColorSpaceRelease(colorSpace);
2468   CGFunctionRelease(function);
2469   CGContextDrawShading(aContext, shading);
2470   CGShadingRelease(shading);
2471   // Draw the one pixel border at the bottom of the titlebar.
2472   if (aToolbarHeight == 0) {
2473     CGRect borderRect = CGRectMake(0.0f, aTitlebarOrigin, sPatternWidth, 1.0f);
2474     DrawNativeGreyColorInRect(aContext, headerBorderGrey, borderRect, aIsMain);
2475   }
2478 // Pattern draw callback for standard titlebar gradients and solid titlebar colors
2479 static void
2480 RepeatedPatternDrawCallback(void* aInfo, CGContextRef aContext)
2482   ToolbarWindow *window = (ToolbarWindow*)aInfo;
2484   // Remember: this context is NOT flipped, so the origin is in the bottom left.
2485   float titlebarHeight = [window titlebarHeight];
2486   float titlebarOrigin = [window frame].size.height - titlebarHeight;
2488   [NSGraphicsContext saveGraphicsState];
2489   [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:aContext flipped:NO]];
2491   BOOL isMain = [window isMainWindow];
2492   NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain];
2493   if (!titlebarColor) {
2494     // If the titlebar color is nil, draw the default titlebar shading.
2495     DrawTitlebarGradient(aContext, titlebarHeight, titlebarOrigin,
2496                          [window unifiedToolbarHeight], isMain);
2497   } else {
2498     // If the titlebar color is not nil, just set and draw it normally.
2499     [titlebarColor set];
2500     NSRectFill(NSMakeRect(0.0f, titlebarOrigin, sPatternWidth, titlebarHeight));
2501   }
2503   // Draw the background color of the window everywhere but where the titlebar is.
2504   [[window windowBackgroundColor] set];
2505   NSRectFill(NSMakeRect(0.0f, 0.0f, 1.0f, titlebarOrigin));
2507   [NSGraphicsContext restoreGraphicsState];
2510 // Pattern draw callback for "drawsContentsIntoWindowFrame" windows
2511 static void
2512 ContentPatternDrawCallback(void* aInfo, CGContextRef aContext)
2514   ToolbarWindow *window = (ToolbarWindow*)aInfo;
2516   NSView* view = [[[window contentView] subviews] lastObject];
2517   if (!view || ![view isKindOfClass:[ChildView class]])
2518     return;
2520   // Gecko drawing assumes flippedness, but the current context isn't flipped
2521   // (because we're painting into the window's border view, which is not a
2522   // ChildView, so it isn't flpped).
2523   // So we need to set a flip transform.
2524   CGContextScaleCTM(aContext, 1.0f, -1.0f);
2525   CGContextTranslateCTM(aContext, 0.0f, -[window frame].size.height);
2527   NSRect titlebarRect = NSMakeRect(0, 0, [window frame].size.width, [window titlebarHeight]);
2528   [(ChildView*)view drawRect:titlebarRect inTitlebarContext:aContext];
2531 - (void)setFill
2533   CGPatternDrawPatternCallback cb = [mWindow drawsContentsIntoWindowFrame] ?
2534                                       &ContentPatternDrawCallback : &RepeatedPatternDrawCallback;
2535   float patternWidth = [mWindow drawsContentsIntoWindowFrame] ? [mWindow frame].size.width : sPatternWidth;
2537   CGPatternCallbacks callbacks = {0, cb, NULL};
2538   CGPatternRef pattern = CGPatternCreate(mWindow, CGRectMake(0.0f, 0.0f, patternWidth, [mWindow frame].size.height), 
2539                                          CGAffineTransformIdentity, patternWidth, [mWindow frame].size.height,
2540                                          kCGPatternTilingConstantSpacing, true, &callbacks);
2542   // Set the pattern as the fill, which is what we were asked to do. All our
2543   // drawing will take place in the patternDraw callback.
2544   CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
2545   CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2546   CGContextSetFillColorSpace(context, patternSpace);
2547   CGColorSpaceRelease(patternSpace);
2548   CGFloat component = 1.0f;
2549   CGContextSetFillPattern(context, pattern, &component);
2550   CGPatternRelease(pattern);
2553 - (void)set
2555   [self setFill];
2558 - (NSString*)colorSpaceName
2560   return NSDeviceRGBColorSpace;
2563 @end
2565 @implementation PopupWindow
2567 // The OS treats our custom popup windows very strangely -- many mouse events
2568 // sent to them never reach their target NSView objects.  (That these windows
2569 // are borderless and of level NSPopUpMenuWindowLevel may have something to do
2570 // with it.)  The best solution is to pre-empt the OS, as follows.  (All
2571 // events for a given NSWindow object go through its sendEvent: method.)
2572 - (void)sendEvent:(NSEvent *)anEvent
2574   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
2576   NSView *target = nil;
2577   NSView *contentView = nil;
2578   NSEventType type = [anEvent type];
2579   NSPoint windowLocation = NSZeroPoint;
2580   switch (type) {
2581     case NSScrollWheel:
2582     case NSLeftMouseDown:
2583     case NSLeftMouseUp:
2584     case NSRightMouseDown:
2585     case NSRightMouseUp:
2586     case NSOtherMouseDown:
2587     case NSOtherMouseUp:
2588     case NSMouseMoved:
2589     case NSLeftMouseDragged:
2590     case NSRightMouseDragged:
2591     case NSOtherMouseDragged:
2592       if ((contentView = [self contentView])) {
2593         // Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
2594         windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
2595         target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
2596         // If the hit test failed, the event is targeted here but is not over the window.
2597         // Send it to our content view.
2598         if (!target)
2599           target = contentView;
2600       }
2601       break;
2602     default:
2603       break;
2604   }
2605   if (target) {
2606     switch (type) {
2607       case NSScrollWheel:
2608         [target scrollWheel:anEvent];
2609         break;
2610       case NSLeftMouseUp:
2611         [target mouseUp:anEvent];
2612         break;
2613       case NSRightMouseDown:
2614         [target rightMouseDown:anEvent];
2615         break;
2616       case NSRightMouseUp:
2617         [target rightMouseUp:anEvent];
2618         break;
2619       case NSOtherMouseDown:
2620         [target otherMouseDown:anEvent];
2621         break;
2622       case NSOtherMouseUp:
2623         [target otherMouseUp:anEvent];
2624         break;
2625       case NSMouseMoved:
2626         [target mouseMoved:anEvent];
2627         break;
2628       case NSLeftMouseDragged:
2629         [target mouseDragged:anEvent];
2630         break;
2631       case NSRightMouseDragged:
2632         [target rightMouseDragged:anEvent];
2633         break;
2634       case NSOtherMouseDragged:
2635         [target otherMouseDragged:anEvent];
2636         break;
2637       default:
2638         [super sendEvent:anEvent];
2639         break;
2640     }
2641   } else {
2642     [super sendEvent:anEvent];
2643   }
2645   NS_OBJC_END_TRY_ABORT_BLOCK;
2648 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
2649       backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
2651   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
2653   mIsContextMenu = false;
2654   return [super initWithContentRect:contentRect styleMask:styleMask
2655           backing:bufferingType defer:deferCreation];
2657   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
2660 - (BOOL)isContextMenu
2662   return mIsContextMenu;
2665 - (void)setIsContextMenu:(BOOL)flag
2667   mIsContextMenu = flag;
2670 - (BOOL)canBecomeMainWindow
2672   // This is overriden because the default is 'yes' when a titlebar is present.
2673   return NO;
2676 @end
2678 // According to Apple's docs on [NSWindow canBecomeKeyWindow] and [NSWindow
2679 // canBecomeMainWindow], windows without a title bar or resize bar can't (by
2680 // default) become key or main.  But if a window can't become key, it can't
2681 // accept keyboard input (bmo bug 393250).  And it should also be possible for
2682 // an otherwise "ordinary" window to become main.  We need to override these
2683 // two methods to make this happen.
2684 @implementation BorderlessWindow
2686 - (BOOL)canBecomeKeyWindow
2688   return YES;
2691 - (void)sendEvent:(NSEvent *)anEvent
2693   NSEventType type = [anEvent type];
2694   
2695   switch (type) {
2696     case NSScrollWheel:
2697     case NSLeftMouseDown:
2698     case NSLeftMouseUp:
2699     case NSRightMouseDown:
2700     case NSRightMouseUp:
2701     case NSOtherMouseDown:
2702     case NSOtherMouseUp:
2703     case NSMouseMoved:
2704     case NSLeftMouseDragged:
2705     case NSRightMouseDragged:
2706     case NSOtherMouseDragged:
2707     {
2708       // Drop all mouse events if a modal window has appeared above us.
2709       // This helps make us behave as if the OS were running a "real" modal
2710       // event loop.
2711       id delegate = [self delegate];
2712       if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
2713         nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
2714         if (widget) {
2715           if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
2716             return;
2717           if (widget->HasModalDescendents())
2718             return;
2719         }
2720       }
2721       break;
2722     }
2723     default:
2724       break;
2725   }
2727   [super sendEvent:anEvent];
2730 // Apple's doc on this method says that the NSWindow class's default is not to
2731 // become main if the window isn't "visible" -- so we should replicate that
2732 // behavior here.  As best I can tell, the [NSWindow isVisible] method is an
2733 // accurate test of what Apple means by "visibility".
2734 - (BOOL)canBecomeMainWindow
2736   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2738   if (![self isVisible])
2739     return NO;
2740   return YES;
2742   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2745 // Retain and release "self" to avoid crashes when our widget (and its native
2746 // window) is closed as a result of processing a key equivalent (e.g.
2747 // Command+w or Command+q).  This workaround is only needed for a window
2748 // that can become key.
2749 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
2751   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
2753   NSWindow *nativeWindow = [self retain];
2754   BOOL retval = [super performKeyEquivalent:theEvent];
2755   [nativeWindow release];
2756   return retval;
2758   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
2761 @end