widget/cocoa/nsCocoaWindow.mm

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsCocoaWindow.h"
     8 #include "NativeKeyBindings.h"
     9 #include "TextInputHandler.h"
    10 #include "nsObjCExceptions.h"
    11 #include "nsCOMPtr.h"
    12 #include "nsWidgetsCID.h"
    13 #include "nsIRollupListener.h"
    14 #include "nsChildView.h"
    15 #include "nsWindowMap.h"
    16 #include "nsAppShell.h"
    17 #include "nsIAppShellService.h"
    18 #include "nsIBaseWindow.h"
    19 #include "nsIInterfaceRequestorUtils.h"
    20 #include "nsIXULWindow.h"
    21 #include "nsToolkit.h"
    22 #include "nsIDOMWindow.h"
    23 #include "nsPIDOMWindow.h"
    24 #include "nsIDOMElement.h"
    25 #include "nsThreadUtils.h"
    26 #include "nsMenuBarX.h"
    27 #include "nsMenuUtilsX.h"
    28 #include "nsStyleConsts.h"
    29 #include "nsNativeThemeColors.h"
    30 #include "nsChildView.h"
    31 #include "nsCocoaFeatures.h"
    32 #include "nsIScreenManager.h"
    33 #include "nsIWidgetListener.h"
    34 #include "nsIPresShell.h"
    36 #include "gfxPlatform.h"
    37 #include "qcms.h"
    39 #include "mozilla/AutoRestore.h"
    40 #include "mozilla/BasicEvents.h"
    41 #include "mozilla/Preferences.h"
    42 #include <algorithm>
    44 namespace mozilla {
    45 namespace layers {
    46 class LayerManager;
    47 }
    48 }
    49 using namespace mozilla::layers;
    50 using namespace mozilla::widget;
    51 using namespace mozilla;
    53 // defined in nsAppShell.mm
    54 extern nsCocoaAppModalWindowList *gCocoaAppModalWindowList;
    56 int32_t gXULModalLevel = 0;
    58 // In principle there should be only one app-modal window at any given time.
    59 // But sometimes, despite our best efforts, another window appears above the
    60 // current app-modal window.  So we need to keep a linked list of app-modal
    61 // windows.  (A non-sheet window that appears above an app-modal window is
    62 // also made app-modal.)  See nsCocoaWindow::SetModal().
    63 nsCocoaWindowList *gGeckoAppModalWindowList = NULL;
    65 // defined in nsMenuBarX.mm
    66 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
    68 // defined in nsChildView.mm
    69 extern BOOL                gSomeMenuBarPainted;
    71 extern "C" {
    72   // CGSPrivate.h
    73   typedef NSInteger CGSConnection;
    74   typedef NSInteger CGSWindow;
    75   typedef NSUInteger CGSWindowFilterRef;
    76   extern CGSConnection _CGSDefaultConnection(void);
    77   extern CGError CGSSetWindowShadowAndRimParameters(const CGSConnection cid, CGSWindow wid, float standardDeviation, float density, int offsetX, int offsetY, unsigned int flags);
    78   extern CGError CGSSetWindowBackgroundBlurRadius(CGSConnection cid, CGSWindow wid, NSUInteger blur);
    79 }
    81 #define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
    83 NS_IMPL_ISUPPORTS_INHERITED(nsCocoaWindow, Inherited, nsPIWidgetCocoa)
    85 // A note on testing to see if your object is a sheet...
    86 // |mWindowType == eWindowType_sheet| is true if your gecko nsIWidget is a sheet
    87 // widget - whether or not the sheet is showing. |[mWindow isSheet]| will return
    88 // true *only when the sheet is actually showing*. Choose your test wisely.
    90 static void RollUpPopups()
    91 {
    92   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
    93   NS_ENSURE_TRUE_VOID(rollupListener);
    94   nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
    95   if (!rollupWidget)
    96     return;
    97   rollupListener->Rollup(0, nullptr, nullptr);
    98 }
   100 nsCocoaWindow::nsCocoaWindow()
   101 : mParent(nullptr)
   102 , mWindow(nil)
   103 , mDelegate(nil)
   104 , mSheetWindowParent(nil)
   105 , mPopupContentView(nil)
   106 , mShadowStyle(NS_STYLE_WINDOW_SHADOW_DEFAULT)
   107 , mBackingScaleFactor(0.0)
   108 , mAnimationType(nsIWidget::eGenericWindowAnimation)
   109 , mWindowMadeHere(false)
   110 , mSheetNeedsShow(false)
   111 , mFullScreen(false)
   112 , mInFullScreenTransition(false)
   113 , mModal(false)
   114 , mUsesNativeFullScreen(false)
   115 , mIsAnimationSuppressed(false)
   116 , mInReportMoveEvent(false)
   117 , mInResize(false)
   118 , mNumModalDescendents(0)
   119 {
   121 }
   123 void nsCocoaWindow::DestroyNativeWindow()
   124 {
   125   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   127   if (!mWindow)
   128     return;
   130   // We want to unhook the delegate here because we don't want events
   131   // sent to it after this object has been destroyed.
   132   [mWindow setDelegate:nil];
   133   [mWindow close];
   134   mWindow = nil;
   135   [mDelegate autorelease];
   137   NS_OBJC_END_TRY_ABORT_BLOCK;
   138 }
   140 nsCocoaWindow::~nsCocoaWindow()
   141 {
   142   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   144   // Notify the children that we're gone.  Popup windows (e.g. tooltips) can
   145   // have nsChildView children.  'kid' is an nsChildView object if and only if
   146   // its 'type' is 'eWindowType_child' or 'eWindowType_plugin'.
   147   // childView->ResetParent() can change our list of children while it's
   148   // being iterated, so the way we iterate the list must allow for this.
   149   for (nsIWidget* kid = mLastChild; kid;) {
   150     nsWindowType kidType = kid->WindowType();
   151     if (kidType == eWindowType_child || kidType == eWindowType_plugin) {
   152       nsChildView* childView = static_cast<nsChildView*>(kid);
   153       kid = kid->GetPrevSibling();
   154       childView->ResetParent();
   155     } else {
   156       nsCocoaWindow* childWindow = static_cast<nsCocoaWindow*>(kid);
   157       childWindow->mParent = nullptr;
   158       kid = kid->GetPrevSibling();
   159     }
   160   }
   162   if (mWindow && mWindowMadeHere) {
   163     DestroyNativeWindow();
   164   }
   166   NS_IF_RELEASE(mPopupContentView);
   168   // Deal with the possiblity that we're being destroyed while running modal.
   169   if (mModal) {
   170     NS_WARNING("Widget destroyed while running modal!");
   171     --gXULModalLevel;
   172     NS_ASSERTION(gXULModalLevel >= 0, "Wierdness setting modality!");
   173   }
   175   NS_OBJC_END_TRY_ABORT_BLOCK;
   176 }
   178 // Find the screen that overlaps aRect the most,
   179 // if none are found default to the mainScreen.
   180 static NSScreen *FindTargetScreenForRect(const nsIntRect& aRect)
   181 {
   182   NSScreen *targetScreen = [NSScreen mainScreen];
   183   NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator];
   184   int largestIntersectArea = 0;
   185   while (NSScreen *screen = [screenEnum nextObject]) {
   186     nsIntRect screenRect(nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame]));
   187     screenRect = screenRect.Intersect(aRect);
   188     int area = screenRect.width * screenRect.height;
   189     if (area > largestIntersectArea) {
   190       largestIntersectArea = area;
   191       targetScreen = screen;
   192     }
   193   }
   194   return targetScreen;
   195 }
   197 // fits the rect to the screen that contains the largest area of it,
   198 // or to aScreen if a screen is passed in
   199 // NB: this operates with aRect in global display pixels
   200 static void FitRectToVisibleAreaForScreen(nsIntRect &aRect, NSScreen *aScreen,
   201                                           bool aUsesNativeFullScreen)
   202 {
   203   if (!aScreen) {
   204     aScreen = FindTargetScreenForRect(aRect);
   205   }
   207   nsIntRect screenBounds(nsCocoaUtils::CocoaRectToGeckoRect([aScreen visibleFrame]));
   209   if (aRect.width > screenBounds.width) {
   210     aRect.width = screenBounds.width;
   211   }
   212   if (aRect.height > screenBounds.height) {
   213     aRect.height = screenBounds.height;
   214   }
   216   if (aRect.x - screenBounds.x + aRect.width > screenBounds.width) {
   217     aRect.x += screenBounds.width - (aRect.x - screenBounds.x + aRect.width);
   218   }
   219   if (aRect.y - screenBounds.y + aRect.height > screenBounds.height) {
   220     aRect.y += screenBounds.height - (aRect.y - screenBounds.y + aRect.height);
   221   }
   223   // If the left/top edge of the window is off the screen in either direction,
   224   // then set the window to start at the left/top edge of the screen.
   225   if (aRect.x < screenBounds.x || aRect.x > (screenBounds.x + screenBounds.width)) {
   226     aRect.x = screenBounds.x;
   227   }
   228   if (aRect.y < screenBounds.y || aRect.y > (screenBounds.y + screenBounds.height)) {
   229     aRect.y = screenBounds.y;
   230   }
   232   // If aRect is filling the screen and the window supports native (Lion-style)
   233   // fullscreen mode, reduce aRect's height and shift it down by 22 pixels
   234   // (nominally the height of the menu bar or of a window's title bar).  For
   235   // some reason this works around bug 740923.  Yes, it's a bodacious hack.
   236   // But until we know more it will have to do.
   237   if (aUsesNativeFullScreen && aRect.y == 0 && aRect.height == screenBounds.height) {
   238     aRect.y = 22;
   239     aRect.height -= 22;
   240   }
   241 }
   243 // Some applications like Camino use native popup windows
   244 // (native context menus, native tooltips)
   245 static bool UseNativePopupWindows()
   246 {
   247 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
   248   return true;
   249 #else
   250   return false;
   251 #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
   252 }
   254 // aRect here is specified in global display pixels
   255 nsresult nsCocoaWindow::Create(nsIWidget *aParent,
   256                                nsNativeWidget aNativeParent,
   257                                const nsIntRect &aRect,
   258                                nsDeviceContext *aContext,
   259                                nsWidgetInitData *aInitData)
   260 {
   261   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   263   // Because the hidden window is created outside of an event loop,
   264   // we have to provide an autorelease pool (see bug 559075).
   265   nsAutoreleasePool localPool;
   267   nsIntRect newBounds = aRect;
   268   FitRectToVisibleAreaForScreen(newBounds, nullptr, mUsesNativeFullScreen);
   270   // Set defaults which can be overriden from aInitData in BaseCreate
   271   mWindowType = eWindowType_toplevel;
   272   mBorderStyle = eBorderStyle_default;
   274   // Ensure that the toolkit is created.
   275   nsToolkit::GetToolkit();
   277   // newBounds is still display (global screen) pixels at this point;
   278   // fortunately, BaseCreate doesn't actually use it so we don't
   279   // need to worry about trying to convert it to device pixels
   280   // when we don't have a window (or dev context, perhaps) yet
   281   Inherited::BaseCreate(aParent, newBounds, aContext, aInitData);
   283   mParent = aParent;
   285   // Applications that use native popups don't want us to create popup windows.
   286   if ((mWindowType == eWindowType_popup) && UseNativePopupWindows())
   287     return NS_OK;
   289   nsresult rv =
   290     CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds),
   291                        mBorderStyle, false);
   292   NS_ENSURE_SUCCESS(rv, rv);
   294   if (mWindowType == eWindowType_popup) {
   295     if (aInitData->mMouseTransparent) {
   296       [mWindow setIgnoresMouseEvents:YES];
   297     }
   298     // now we can convert newBounds to device pixels for the window we created,
   299     // as the child view expects a rect expressed in the dev pix of its parent
   300     double scale = BackingScaleFactor();
   301     newBounds.x *= scale;
   302     newBounds.y *= scale;
   303     newBounds.width *= scale;
   304     newBounds.height *= scale;
   305     return CreatePopupContentView(newBounds, aContext);
   306   }
   308   mIsAnimationSuppressed = aInitData->mIsAnimationSuppressed;
   310   return NS_OK;
   312   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   313 }
   315 static unsigned int WindowMaskForBorderStyle(nsBorderStyle aBorderStyle)
   316 {
   317   bool allOrDefault = (aBorderStyle == eBorderStyle_all ||
   318                          aBorderStyle == eBorderStyle_default);
   320   /* Apple's docs on NSWindow styles say that "a window's style mask should
   321    * include NSTitledWindowMask if it includes any of the others [besides
   322    * NSBorderlessWindowMask]".  This implies that a borderless window
   323    * shouldn't have any other styles than NSBorderlessWindowMask.
   324    */
   325   if (!allOrDefault && !(aBorderStyle & eBorderStyle_title))
   326     return NSBorderlessWindowMask;
   328   unsigned int mask = NSTitledWindowMask;
   329   if (allOrDefault || aBorderStyle & eBorderStyle_close)
   330     mask |= NSClosableWindowMask;
   331   if (allOrDefault || aBorderStyle & eBorderStyle_minimize)
   332     mask |= NSMiniaturizableWindowMask;
   333   if (allOrDefault || aBorderStyle & eBorderStyle_resizeh)
   334     mask |= NSResizableWindowMask;
   336   return mask;
   337 }
   339 NS_IMETHODIMP nsCocoaWindow::ReparentNativeWidget(nsIWidget* aNewParent)
   340 {
   341   return NS_ERROR_NOT_IMPLEMENTED;
   342 }
   344 // If aRectIsFrameRect, aRect specifies the frame rect of the new window.
   345 // Otherwise, aRect.x/y specify the position of the window's frame relative to
   346 // the bottom of the menubar and aRect.width/height specify the size of the
   347 // content rect.
   348 nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect,
   349                                            nsBorderStyle aBorderStyle,
   350                                            bool aRectIsFrameRect)
   351 {
   352   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   354   // We default to NSBorderlessWindowMask, add features if needed.
   355   unsigned int features = NSBorderlessWindowMask;
   357   // Configure the window we will create based on the window type.
   358   switch (mWindowType)
   359   {
   360     case eWindowType_invisible:
   361     case eWindowType_child:
   362     case eWindowType_plugin:
   363       break;
   364     case eWindowType_popup:
   365       if (aBorderStyle != eBorderStyle_default && mBorderStyle & eBorderStyle_title) {
   366         features |= NSTitledWindowMask;
   367         if (aBorderStyle & eBorderStyle_close) {
   368           features |= NSClosableWindowMask;
   369         }
   370       }
   371       break;
   372     case eWindowType_toplevel:
   373     case eWindowType_dialog:
   374       features = WindowMaskForBorderStyle(aBorderStyle);
   375       break;
   376     case eWindowType_sheet:
   377       if (mParent->WindowType() != eWindowType_invisible &&
   378           aBorderStyle & eBorderStyle_resizeh) {
   379         features = NSResizableWindowMask;
   380       }
   381       else {
   382         features = NSMiniaturizableWindowMask;
   383       }
   384       features |= NSTitledWindowMask;
   385       break;
   386     default:
   387       NS_ERROR("Unhandled window type!");
   388       return NS_ERROR_FAILURE;
   389   }
   391   NSRect contentRect;
   393   if (aRectIsFrameRect) {
   394     contentRect = [NSWindow contentRectForFrameRect:aRect styleMask:features];
   395   } else {
   396     /* 
   397      * We pass a content area rect to initialize the native Cocoa window. The
   398      * content rect we give is the same size as the size we're given by gecko.
   399      * The origin we're given for non-popup windows is moved down by the height
   400      * of the menu bar so that an origin of (0,100) from gecko puts the window
   401      * 100 pixels below the top of the available desktop area. We also move the
   402      * origin down by the height of a title bar if it exists. This is so the
   403      * origin that gecko gives us for the top-left of  the window turns out to
   404      * be the top-left of the window we create. This is how it was done in
   405      * Carbon. If it ought to be different we'll probably need to look at all
   406      * the callers.
   407      *
   408      * Note: This means that if you put a secondary screen on top of your main
   409      * screen and open a window in the top screen, it'll be incorrectly shifted
   410      * down by the height of the menu bar. Same thing would happen in Carbon.
   411      *
   412      * Note: If you pass a rect with 0,0 for an origin, the window ends up in a
   413      * weird place for some reason. This stops that without breaking popups.
   414      */
   415     // Compensate for difference between frame and content area height (e.g. title bar).
   416     NSRect newWindowFrame = [NSWindow frameRectForContentRect:aRect styleMask:features];
   418     contentRect = aRect;
   419     contentRect.origin.y -= (newWindowFrame.size.height - aRect.size.height);
   421     if (mWindowType != eWindowType_popup)
   422       contentRect.origin.y -= [[NSApp mainMenu] menuBarHeight];
   423   }
   425   // NSLog(@"Top-level window being created at Cocoa rect: %f, %f, %f, %f\n",
   426   //       rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
   428   Class windowClass = [BaseWindow class];
   429   // If we have a titlebar on a top-level window, we want to be able to control the 
   430   // titlebar color (for unified windows), so use the special ToolbarWindow class. 
   431   // Note that we need to check the window type because we mark sheets as 
   432   // having titlebars.
   433   if ((mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) &&
   434       (features & NSTitledWindowMask))
   435     windowClass = [ToolbarWindow class];
   436   // If we're a popup window we need to use the PopupWindow class.
   437   else if (mWindowType == eWindowType_popup)
   438     windowClass = [PopupWindow class];
   439   // If we're a non-popup borderless window we need to use the
   440   // BorderlessWindow class.
   441   else if (features == NSBorderlessWindowMask)
   442     windowClass = [BorderlessWindow class];
   444   // Create the window
   445   mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features 
   446                                  backing:NSBackingStoreBuffered defer:YES];
   448   // setup our notification delegate. Note that setDelegate: does NOT retain.
   449   mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
   450   [mWindow setDelegate:mDelegate];
   452   // Make sure that the content rect we gave has been honored.
   453   NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
   454   if (!NSEqualRects([mWindow frame], wantedFrame)) {
   455     // This can happen when the window is not on the primary screen.
   456     [mWindow setFrame:wantedFrame display:NO];
   457   }
   458   UpdateBounds();
   460   if (mWindowType == eWindowType_invisible) {
   461     [mWindow setLevel:kCGDesktopWindowLevelKey];
   462   } else if (mWindowType == eWindowType_popup) {
   463     SetPopupWindowLevel();
   464     [mWindow setHasShadow:YES];
   465   }
   467   [mWindow setBackgroundColor:[NSColor clearColor]];
   468 #ifdef MOZ_B2G
   469   // In B2G, we don't create popups and we need OMTC to work (because out of
   470   // process compositing depends on it). Therefore, we don't need our windows
   471   // to be transparent.
   472   [mWindow setOpaque:YES];
   473 #else
   474   [mWindow setOpaque:NO];
   475 #endif
   476   [mWindow setContentMinSize:NSMakeSize(60, 60)];
   477   [mWindow disableCursorRects];
   479   // Make sure the window starts out not draggable by the background.
   480   // We will turn it on as necessary.
   481   [mWindow setMovableByWindowBackground:NO];
   483   [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
   484   mWindowMadeHere = true;
   486   return NS_OK;
   488   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   489 }
   491 NS_IMETHODIMP nsCocoaWindow::CreatePopupContentView(const nsIntRect &aRect,
   492                              nsDeviceContext *aContext)
   493 {
   494   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   496   // We need to make our content view a ChildView.
   497   mPopupContentView = new nsChildView();
   498   if (!mPopupContentView)
   499     return NS_ERROR_FAILURE;
   501   NS_ADDREF(mPopupContentView);
   503   nsIWidget* thisAsWidget = static_cast<nsIWidget*>(this);
   504   mPopupContentView->Create(thisAsWidget, nullptr, aRect, aContext, nullptr);
   506   ChildView* newContentView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET);
   507   [mWindow setContentView:newContentView];
   509   return NS_OK;
   511   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   512 }
   514 NS_IMETHODIMP nsCocoaWindow::Destroy()
   515 {
   516   // If we don't hide here we run into problems with panels, this is not ideal.
   517   // (Bug 891424)
   518   Show(false);
   520   if (mPopupContentView)
   521     mPopupContentView->Destroy();
   523   nsBaseWidget::Destroy();
   524   // nsBaseWidget::Destroy() calls GetParent()->RemoveChild(this). But we
   525   // don't implement GetParent(), so we need to do the equivalent here.
   526   if (mParent) {
   527     mParent->RemoveChild(this);
   528   }
   529   nsBaseWidget::OnDestroy();
   531   if (mFullScreen) {
   532     // On Lion we don't have to mess with the OS chrome when in Full Screen
   533     // mode.  But we do have to destroy the native window here (and not wait
   534     // for that to happen in our destructor).  We don't switch away from the
   535     // native window's space until the window is destroyed, and otherwise this
   536     // might not happen for several seconds (because at least one object
   537     // holding a reference to ourselves is usually waiting to be garbage-
   538     // collected).  See bug 757618.
   539     if (mUsesNativeFullScreen) {
   540       DestroyNativeWindow();
   541     } else if (mWindow) {
   542       nsCocoaUtils::HideOSChromeOnScreen(false, [mWindow screen]);
   543     }
   544   }
   546   return NS_OK;
   547 }
   549 nsIWidget* nsCocoaWindow::GetSheetWindowParent(void)
   550 {
   551   if (mWindowType != eWindowType_sheet)
   552     return nullptr;
   553   nsCocoaWindow *parent = static_cast<nsCocoaWindow*>(mParent);
   554   while (parent && (parent->mWindowType == eWindowType_sheet))
   555     parent = static_cast<nsCocoaWindow*>(parent->mParent);
   556   return parent;
   557 }
   559 void* nsCocoaWindow::GetNativeData(uint32_t aDataType)
   560 {
   561   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
   563   void* retVal = nullptr;
   565   switch (aDataType) {
   566     // to emulate how windows works, we always have to return a NSView
   567     // for NS_NATIVE_WIDGET
   568     case NS_NATIVE_WIDGET:
   569     case NS_NATIVE_DISPLAY:
   570       retVal = [mWindow contentView];
   571       break;
   573     case NS_NATIVE_WINDOW:
   574       retVal = mWindow;
   575       break;
   577     case NS_NATIVE_GRAPHIC:
   578       // There isn't anything that makes sense to return here,
   579       // and it doesn't matter so just return nullptr.
   580       NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a top-level window!");
   581       break;
   582   }
   584   return retVal;
   586   NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
   587 }
   589 bool nsCocoaWindow::IsVisible() const
   590 {
   591   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   593   return (mWindow && ([mWindow isVisibleOrBeingShown] || mSheetNeedsShow));
   595   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
   596 }
   598 NS_IMETHODIMP nsCocoaWindow::SetModal(bool aState)
   599 {
   600   if (!mWindow)
   601     return NS_OK;
   603   // This is used during startup (outside the event loop) when creating
   604   // the add-ons compatibility checking dialog and the profile manager UI;
   605   // therefore, it needs to provide an autorelease pool to avoid cocoa
   606   // objects leaking.
   607   nsAutoreleasePool localPool;
   609   mModal = aState;
   610   nsCocoaWindow *aParent = static_cast<nsCocoaWindow*>(mParent);
   611   if (aState) {
   612     ++gXULModalLevel;
   613     if (gCocoaAppModalWindowList)
   614       gCocoaAppModalWindowList->PushGecko(mWindow, this);
   615     // When a non-sheet window gets "set modal", make the window(s) that it
   616     // appears over behave as they should.  We can't rely on native methods to
   617     // do this, for the following reason:  The OS runs modal non-sheet windows
   618     // in an event loop (using [NSApplication runModalForWindow:] or similar
   619     // methods) that's incompatible with the modal event loop in nsXULWindow::
   620     // ShowModal() (each of these event loops is "exclusive", and can't run at
   621     // the same time as other (similar) event loops).
   622     if (mWindowType != eWindowType_sheet) {
   623       while (aParent) {
   624         if (aParent->mNumModalDescendents++ == 0) {
   625           NSWindow *aWindow = aParent->GetCocoaWindow();
   626           if (aParent->mWindowType != eWindowType_invisible) {
   627             [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:NO];
   628             [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:NO];
   629             [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO];
   630           }
   631         }
   632         aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
   633       }
   634       [mWindow setLevel:NSModalPanelWindowLevel];
   635       nsCocoaWindowList *windowList = new nsCocoaWindowList;
   636       if (windowList) {
   637         windowList->window = this; // Don't ADDREF
   638         windowList->prev = gGeckoAppModalWindowList;
   639         gGeckoAppModalWindowList = windowList;
   640       }
   641     }
   642   }
   643   else {
   644     --gXULModalLevel;
   645     NS_ASSERTION(gXULModalLevel >= 0, "Mismatched call to nsCocoaWindow::SetModal(false)!");
   646     if (gCocoaAppModalWindowList)
   647       gCocoaAppModalWindowList->PopGecko(mWindow, this);
   648     if (mWindowType != eWindowType_sheet) {
   649       while (aParent) {
   650         if (--aParent->mNumModalDescendents == 0) {
   651           NSWindow *aWindow = aParent->GetCocoaWindow();
   652           if (aParent->mWindowType != eWindowType_invisible) {
   653             [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:YES];
   654             [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:YES];
   655             [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:YES];
   656           }
   657         }
   658         NS_ASSERTION(aParent->mNumModalDescendents >= 0, "Widget hierarchy changed while modal!");
   659         aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
   660       }
   661       if (gGeckoAppModalWindowList) {
   662         NS_ASSERTION(gGeckoAppModalWindowList->window == this, "Widget hierarchy changed while modal!");
   663         nsCocoaWindowList *saved = gGeckoAppModalWindowList;
   664         gGeckoAppModalWindowList = gGeckoAppModalWindowList->prev;
   665         delete saved; // "window" not ADDREFed
   666       }
   667       if (mWindowType == eWindowType_popup)
   668         SetPopupWindowLevel();
   669       else
   670         [mWindow setLevel:NSNormalWindowLevel];
   671     }
   672   }
   673   return NS_OK;
   674 }
   676 // Hide or show this window
   677 NS_IMETHODIMP nsCocoaWindow::Show(bool bState)
   678 {
   679   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   681   if (!mWindow)
   682     return NS_OK;
   684   // We need to re-execute sometimes in order to bring already-visible
   685   // windows forward.
   686   if (!mSheetNeedsShow && !bState && ![mWindow isVisible])
   687     return NS_OK;
   689   // Protect against re-entering.
   690   if (bState && [mWindow isBeingShown])
   691     return NS_OK;
   693   [mWindow setBeingShown:bState];
   695   nsIWidget* parentWidget = mParent;
   696   nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget));
   697   NSWindow* nativeParentWindow = (parentWidget) ?
   698     (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) : nil;
   700   if (bState && !mBounds.IsEmpty()) {
   701     if (mPopupContentView) {
   702       // Ensure our content view is visible. We never need to hide it.
   703       mPopupContentView->Show(true);
   704     }
   706     if (mWindowType == eWindowType_sheet) {
   707       // bail if no parent window (its basically what we do in Carbon)
   708       if (!nativeParentWindow || !piParentWidget)
   709         return NS_ERROR_FAILURE;
   711       NSWindow* topNonSheetWindow = nativeParentWindow;
   713       // If this sheet is the child of another sheet, hide the parent so that
   714       // this sheet can be displayed. Leave the parent mSheetNeedsShow alone,
   715       // that is only used to handle sibling sheet contention. The parent will
   716       // return once there are no more child sheets.
   717       bool parentIsSheet = false;
   718       if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
   719           parentIsSheet) {
   720         piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
   721         [NSApp endSheet:nativeParentWindow];
   722       }
   724       nsCocoaWindow* sheetShown = nullptr;
   725       if (NS_SUCCEEDED(piParentWidget->GetChildSheet(true, &sheetShown)) &&
   726           (!sheetShown || sheetShown == this)) {
   727         // If this sheet is already the sheet actually being shown, don't
   728         // tell it to show again. Otherwise the number of calls to
   729         // [NSApp beginSheet...] won't match up with [NSApp endSheet...].
   730         if (![mWindow isVisible]) {
   731           mSheetNeedsShow = false;
   732           mSheetWindowParent = topNonSheetWindow;
   733           // Only set contextInfo if our parent isn't a sheet.
   734           NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
   735           [TopLevelWindowData deactivateInWindow:mSheetWindowParent];
   736           [NSApp beginSheet:mWindow
   737              modalForWindow:mSheetWindowParent
   738               modalDelegate:mDelegate
   739              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
   740                 contextInfo:contextInfo];
   741           [TopLevelWindowData activateInWindow:mWindow];
   742           SendSetZLevelEvent();
   743         }
   744       }
   745       else {
   746         // A sibling of this sheet is active, don't show this sheet yet.
   747         // When the active sheet hides, its brothers and sisters that have
   748         // mSheetNeedsShow set will have their opportunities to display.
   749         mSheetNeedsShow = true;
   750       }
   751     }
   752     else if (mWindowType == eWindowType_popup) {
   753       // If a popup window is shown after being hidden, it needs to be "reset"
   754       // for it to receive any mouse events aside from mouse-moved events
   755       // (because it was removed from the "window cache" when it was hidden
   756       // -- see below).  Setting the window number to -1 and then back to its
   757       // original value seems to accomplish this.  The idea was "borrowed"
   758       // from the Java Embedding Plugin.
   759       NSInteger windowNumber = [mWindow windowNumber];
   760       [mWindow _setWindowNumber:-1];
   761       [mWindow _setWindowNumber:windowNumber];
   762       // For reasons that aren't yet clear, calls to [NSWindow orderFront:] or
   763       // [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000)
   764       // creating CGSWindow", which in turn triggers an internal inconsistency
   765       // NSException.  These errors shouldn't be fatal.  So we need to wrap
   766       // calls to ...orderFront: in LOGONLY blocks.  See bmo bug 470864.
   767       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
   768       [[mWindow contentView] setNeedsDisplay:YES];
   769       [mWindow orderFront:nil];
   770       NS_OBJC_END_TRY_LOGONLY_BLOCK;
   771       SendSetZLevelEvent();
   772       AdjustWindowShadow();
   773       SetWindowBackgroundBlur();
   774       // If our popup window is a non-native context menu, tell the OS (and
   775       // other programs) that a menu has opened.  This is how the OS knows to
   776       // close other programs' context menus when ours open.
   777       if ([mWindow isKindOfClass:[PopupWindow class]] &&
   778           [(PopupWindow*) mWindow isContextMenu]) {
   779         [[NSDistributedNotificationCenter defaultCenter]
   780           postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
   781                         object:@"org.mozilla.gecko.PopupWindow"];
   782       }
   784       // If a parent window was supplied and this is a popup at the parent
   785       // level, set its child window. This will cause the child window to
   786       // appear above the parent and move when the parent does. Setting this
   787       // needs to happen after the _setWindowNumber calls above, otherwise the
   788       // window doesn't focus properly.
   789       if (nativeParentWindow && mPopupLevel == ePopupLevelParent)
   790         [nativeParentWindow addChildWindow:mWindow
   791                             ordered:NSWindowAbove];
   792     }
   793     else {
   794       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
   795       if (mWindowType == eWindowType_toplevel &&
   796           [mWindow respondsToSelector:@selector(setAnimationBehavior:)]) {
   797         NSWindowAnimationBehavior behavior;
   798         if (mIsAnimationSuppressed) {
   799           behavior = NSWindowAnimationBehaviorNone;
   800         } else {
   801           switch (mAnimationType) {
   802             case nsIWidget::eDocumentWindowAnimation:
   803               behavior = NSWindowAnimationBehaviorDocumentWindow;
   804               break;
   805             default:
   806               NS_NOTREACHED("unexpected mAnimationType value");
   807               // fall through
   808             case nsIWidget::eGenericWindowAnimation:
   809               behavior = NSWindowAnimationBehaviorDefault;
   810               break;
   811           }
   812         }
   813         [mWindow setAnimationBehavior:behavior];
   814       }
   815       [mWindow makeKeyAndOrderFront:nil];
   816       NS_OBJC_END_TRY_LOGONLY_BLOCK;
   817       SendSetZLevelEvent();
   818     }
   819   }
   820   else {
   821     // roll up any popups if a top-level window is going away
   822     if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog)
   823       RollUpPopups();
   825     // now get rid of the window/sheet
   826     if (mWindowType == eWindowType_sheet) {
   827       if (mSheetNeedsShow) {
   828         // This is an attempt to hide a sheet that never had a chance to
   829         // be shown. There's nothing to do other than make sure that it
   830         // won't show.
   831         mSheetNeedsShow = false;
   832       }
   833       else {
   834         // get sheet's parent *before* hiding the sheet (which breaks the linkage)
   835         NSWindow* sheetParent = mSheetWindowParent;
   837         // hide the sheet
   838         [NSApp endSheet:mWindow];
   840         [TopLevelWindowData deactivateInWindow:mWindow];
   842         nsCocoaWindow* siblingSheetToShow = nullptr;
   843         bool parentIsSheet = false;
   845         if (nativeParentWindow && piParentWidget &&
   846             NS_SUCCEEDED(piParentWidget->GetChildSheet(false, &siblingSheetToShow)) &&
   847             siblingSheetToShow) {
   848           // First, give sibling sheets an opportunity to show.
   849           siblingSheetToShow->Show(true);
   850         }
   851         else if (nativeParentWindow && piParentWidget &&
   852                  NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
   853                  parentIsSheet) {
   854           // Only set contextInfo if the parent of the parent sheet we're about
   855           // to restore isn't itself a sheet.
   856           NSWindow* contextInfo = sheetParent;
   857           nsIWidget* grandparentWidget = nil;
   858           if (NS_SUCCEEDED(piParentWidget->GetRealParent(&grandparentWidget)) && grandparentWidget) {
   859             nsCOMPtr<nsPIWidgetCocoa> piGrandparentWidget(do_QueryInterface(grandparentWidget));
   860             bool grandparentIsSheet = false;
   861             if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) &&
   862                 grandparentIsSheet) {
   863                 contextInfo = nil;
   864             }
   865           }
   866           // If there are no sibling sheets, but the parent is a sheet, restore
   867           // it.  It wasn't sent any deactivate events when it was hidden, so
   868           // don't call through Show, just let the OS put it back up.
   869           [NSApp beginSheet:nativeParentWindow
   870              modalForWindow:sheetParent
   871               modalDelegate:[nativeParentWindow delegate]
   872              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
   873                 contextInfo:contextInfo];
   874         }
   875         else {
   876           // Sheet, that was hard.  No more siblings or parents, going back
   877           // to a real window.
   878           NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
   879           [sheetParent makeKeyAndOrderFront:nil];
   880           NS_OBJC_END_TRY_LOGONLY_BLOCK;
   881         }
   882         SendSetZLevelEvent();
   883       }
   884     }
   885     else {
   886       // If the window is a popup window with a parent window we need to
   887       // unhook it here before ordering it out. When you order out the child
   888       // of a window it hides the parent window.
   889       if (mWindowType == eWindowType_popup && nativeParentWindow)
   890         [nativeParentWindow removeChildWindow:mWindow];
   892       [mWindow orderOut:nil];
   893       // Unless it's explicitly removed from NSApp's "window cache", a popup
   894       // window will keep receiving mouse-moved events even after it's been
   895       // "ordered out" (instead of the browser window that was underneath it,
   896       // until you click on that window).  This is bmo bug 378645, but it's
   897       // surely an Apple bug.  The "window cache" is an undocumented subsystem,
   898       // all of whose methods are included in the NSWindowCache category of
   899       // the NSApplication class (in header files generated using class-dump).
   900       // This workaround was "borrowed" from the Java Embedding Plugin (which
   901       // uses it for a different purpose).
   902       if (mWindowType == eWindowType_popup)
   903         [NSApp _removeWindowFromCache:mWindow];
   905       // If our popup window is a non-native context menu, tell the OS (and
   906       // other programs) that a menu has closed.
   907       if ([mWindow isKindOfClass:[PopupWindow class]] &&
   908           [(PopupWindow*) mWindow isContextMenu]) {
   909         [[NSDistributedNotificationCenter defaultCenter]
   910           postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification"
   911                         object:@"org.mozilla.gecko.PopupWindow"];
   912       }
   913     }
   914   }
   916   [mWindow setBeingShown:NO];
   918   return NS_OK;
   920   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   921 }
   923 struct ShadowParams {
   924   float standardDeviation;
   925   float density;
   926   int offsetX;
   927   int offsetY;
   928   unsigned int flags;
   929 };
   931 // These numbers have been determined by looking at the results of
   932 // CGSGetWindowShadowAndRimParameters for native window types.
   933 static const ShadowParams kWindowShadowParameters[] = {
   934   { 0.0f, 0.0f, 0, 0, 0 },        // none
   935   { 8.0f, 0.5f, 0, 6, 1 },        // default
   936   { 10.0f, 0.44f, 0, 10, 512 },   // menu
   937   { 8.0f, 0.5f, 0, 6, 1 },        // tooltip
   938   { 4.0f, 0.6f, 0, 4, 512 }       // sheet
   939 };
   941 // This method will adjust the window shadow style for popup windows after
   942 // they have been made visible. Before they're visible, their window number
   943 // might be -1, which is not useful.
   944 // We won't attempt to change the shadow for windows that can acquire key state
   945 // since OS X will reset the shadow whenever that happens.
   946 void
   947 nsCocoaWindow::AdjustWindowShadow()
   948 {
   949   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   951   if (!mWindow || ![mWindow isVisible] || ![mWindow hasShadow] ||
   952       [mWindow canBecomeKeyWindow] || [mWindow windowNumber] == -1)
   953     return;
   955   const ShadowParams& params = kWindowShadowParameters[mShadowStyle];
   956   CGSConnection cid = _CGSDefaultConnection();
   957   CGSSetWindowShadowAndRimParameters(cid, [mWindow windowNumber],
   958                                      params.standardDeviation, params.density,
   959                                      params.offsetX, params.offsetY,
   960                                      params.flags);
   962   NS_OBJC_END_TRY_ABORT_BLOCK;
   963 }
   965 static const NSUInteger kWindowBackgroundBlurRadius = 4;
   967 void
   968 nsCocoaWindow::SetWindowBackgroundBlur()
   969 {
   970   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   972   if (!mWindow || ![mWindow isVisible] || [mWindow windowNumber] == -1)
   973     return;
   975   // Only blur the background of menus and fake sheets.
   976   if (mShadowStyle != NS_STYLE_WINDOW_SHADOW_MENU &&
   977       mShadowStyle != NS_STYLE_WINDOW_SHADOW_SHEET)
   978     return;
   980   CGSConnection cid = _CGSDefaultConnection();
   981   CGSSetWindowBackgroundBlurRadius(cid, [mWindow windowNumber], kWindowBackgroundBlurRadius);
   983   NS_OBJC_END_TRY_ABORT_BLOCK;
   984 }
   986 nsresult
   987 nsCocoaWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
   988 {
   989   if (mPopupContentView) {
   990     mPopupContentView->ConfigureChildren(aConfigurations);
   991   }
   992   return NS_OK;
   993 }
   995 LayerManager*
   996 nsCocoaWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
   997                                LayersBackend aBackendHint,
   998                                LayerManagerPersistence aPersistence,
   999                                bool* aAllowRetaining)
  1001   if (mPopupContentView) {
  1002     return mPopupContentView->GetLayerManager(aShadowManager,
  1003                                               aBackendHint,
  1004                                               aPersistence,
  1005                                               aAllowRetaining);
  1007   return nullptr;
  1010 nsTransparencyMode nsCocoaWindow::GetTransparencyMode()
  1012   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1014   return (!mWindow || [mWindow isOpaque]) ? eTransparencyOpaque : eTransparencyTransparent;
  1016   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque);
  1019 // This is called from nsMenuPopupFrame when making a popup transparent.
  1020 // For other window types, nsChildView::SetTransparencyMode is used.
  1021 void nsCocoaWindow::SetTransparencyMode(nsTransparencyMode aMode)
  1023   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1025   if (!mWindow)
  1026     return;
  1028   BOOL isTransparent = aMode == eTransparencyTransparent;
  1029   BOOL currentTransparency = ![mWindow isOpaque];
  1030   if (isTransparent != currentTransparency) {
  1031     [mWindow setOpaque:!isTransparent];
  1032     [mWindow setBackgroundColor:(isTransparent ? [NSColor clearColor] : [NSColor whiteColor])];
  1035   NS_OBJC_END_TRY_ABORT_BLOCK;
  1038 NS_IMETHODIMP nsCocoaWindow::Enable(bool aState)
  1040   return NS_OK;
  1043 bool nsCocoaWindow::IsEnabled() const
  1045   return true;
  1048 #define kWindowPositionSlop 20
  1050 NS_IMETHODIMP nsCocoaWindow::ConstrainPosition(bool aAllowSlop,
  1051                                                int32_t *aX, int32_t *aY)
  1053   if (!mWindow || ![mWindow screen]) {
  1054     return NS_OK;
  1057   nsIntRect screenBounds;
  1059   int32_t width, height;
  1061   NSRect frame = [mWindow frame];
  1063   // zero size rects confuse the screen manager
  1064   width = std::max<int32_t>(frame.size.width, 1);
  1065   height = std::max<int32_t>(frame.size.height, 1);
  1067   nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
  1068   if (screenMgr) {
  1069     nsCOMPtr<nsIScreen> screen;
  1070     screenMgr->ScreenForRect(*aX, *aY, width, height, getter_AddRefs(screen));
  1072     if (screen) {
  1073       screen->GetRectDisplayPix(&(screenBounds.x), &(screenBounds.y),
  1074                                 &(screenBounds.width), &(screenBounds.height));
  1078   if (aAllowSlop) {
  1079     if (*aX < screenBounds.x - width + kWindowPositionSlop) {
  1080       *aX = screenBounds.x - width + kWindowPositionSlop;
  1081     } else if (*aX >= screenBounds.x + screenBounds.width - kWindowPositionSlop) {
  1082       *aX = screenBounds.x + screenBounds.width - kWindowPositionSlop;
  1085     if (*aY < screenBounds.y - height + kWindowPositionSlop) {
  1086       *aY = screenBounds.y - height + kWindowPositionSlop;
  1087     } else if (*aY >= screenBounds.y + screenBounds.height - kWindowPositionSlop) {
  1088       *aY = screenBounds.y + screenBounds.height - kWindowPositionSlop;
  1090   } else {
  1091     if (*aX < screenBounds.x) {
  1092       *aX = screenBounds.x;
  1093     } else if (*aX >= screenBounds.x + screenBounds.width - width) {
  1094       *aX = screenBounds.x + screenBounds.width - width;
  1097     if (*aY < screenBounds.y) {
  1098       *aY = screenBounds.y;
  1099     } else if (*aY >= screenBounds.y + screenBounds.height - height) {
  1100       *aY = screenBounds.y + screenBounds.height - height;
  1104   return NS_OK;
  1107 void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
  1109   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1111   // Popups can be smaller than (60, 60)
  1112   NSRect rect =
  1113     (mWindowType == eWindowType_popup) ? NSZeroRect : NSMakeRect(0.0, 0.0, 60, 60);
  1114   rect = [mWindow frameRectForContentRect:rect];
  1116   CGFloat scaleFactor = BackingScaleFactor();
  1118   SizeConstraints c = aConstraints;
  1119   c.mMinSize.width =
  1120     std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.width, scaleFactor),
  1121            c.mMinSize.width);
  1122   c.mMinSize.height =
  1123     std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.height, scaleFactor),
  1124            c.mMinSize.height);
  1126   NSSize minSize = {
  1127     nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.width, scaleFactor),
  1128     nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.height, scaleFactor)
  1129   };
  1130   [mWindow setMinSize:minSize];
  1132   NSSize maxSize = {
  1133     c.mMaxSize.width == NS_MAXSIZE ?
  1134       FLT_MAX : nsCocoaUtils::DevPixelsToCocoaPoints(c.mMaxSize.width, scaleFactor),
  1135     c.mMaxSize.height == NS_MAXSIZE ?
  1136       FLT_MAX : nsCocoaUtils::DevPixelsToCocoaPoints(c.mMaxSize.height, scaleFactor)
  1137   };
  1138   [mWindow setMaxSize:maxSize];
  1140   nsBaseWidget::SetSizeConstraints(c);
  1142   NS_OBJC_END_TRY_ABORT_BLOCK;
  1145 // Coordinates are global display pixels
  1146 NS_IMETHODIMP nsCocoaWindow::Move(double aX, double aY)
  1148   if (!mWindow) {
  1149     return NS_OK;
  1152   // The point we have is in Gecko coordinates (origin top-left). Convert
  1153   // it to Cocoa ones (origin bottom-left).
  1154   NSPoint coord = {
  1155     static_cast<float>(aX),
  1156     static_cast<float>(nsCocoaUtils::FlippedScreenY(NSToIntRound(aY)))
  1157   };
  1159   NSRect frame = [mWindow frame];
  1160   if (frame.origin.x != coord.x ||
  1161       frame.origin.y + frame.size.height != coord.y) {
  1162     [mWindow setFrameTopLeftPoint:coord];
  1165   return NS_OK;
  1168 // Position the window behind the given window
  1169 NS_METHOD nsCocoaWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
  1170                                      nsIWidget *aWidget, bool aActivate)
  1172   return NS_OK;
  1175 NS_METHOD nsCocoaWindow::SetSizeMode(int32_t aMode)
  1177   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1179   if (!mWindow)
  1180     return NS_OK;
  1182   // mSizeMode will be updated in DispatchSizeModeEvent, which will be called
  1183   // from a delegate method that handles the state change during one of the
  1184   // calls below.
  1185   nsSizeMode previousMode = mSizeMode;
  1187   if (aMode == nsSizeMode_Normal) {
  1188     if ([mWindow isMiniaturized])
  1189       [mWindow deminiaturize:nil];
  1190     else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
  1191       [mWindow zoom:nil];
  1193   else if (aMode == nsSizeMode_Minimized) {
  1194     if (![mWindow isMiniaturized])
  1195       [mWindow miniaturize:nil];
  1197   else if (aMode == nsSizeMode_Maximized) {
  1198     if ([mWindow isMiniaturized])
  1199       [mWindow deminiaturize:nil];
  1200     if (![mWindow isZoomed])
  1201       [mWindow zoom:nil];
  1203   else if (aMode == nsSizeMode_Fullscreen) {
  1204     if (!mFullScreen)
  1205       MakeFullScreen(true);
  1208   return NS_OK;
  1210   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1213 // This has to preserve the window's frame bounds.
  1214 // This method requires (as does the Windows impl.) that you call Resize shortly
  1215 // after calling HideWindowChrome. See bug 498835 for fixing this.
  1216 NS_IMETHODIMP nsCocoaWindow::HideWindowChrome(bool aShouldHide)
  1218   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1220   if (!mWindow || !mWindowMadeHere ||
  1221       (mWindowType != eWindowType_toplevel && mWindowType != eWindowType_dialog))
  1222     return NS_ERROR_FAILURE;
  1224   BOOL isVisible = [mWindow isVisible];
  1226   // Remove child windows.
  1227   NSArray* childWindows = [mWindow childWindows];
  1228   NSEnumerator* enumerator = [childWindows objectEnumerator];
  1229   NSWindow* child = nil;
  1230   while ((child = [enumerator nextObject])) {
  1231     [mWindow removeChildWindow:child];
  1234   // Remove the content view.
  1235   NSView* contentView = [mWindow contentView];
  1236   [contentView retain];
  1237   [contentView removeFromSuperviewWithoutNeedingDisplay];
  1239   // Save state (like window title).
  1240   NSMutableDictionary* state = [mWindow exportState];
  1242   // Recreate the window with the right border style.
  1243   NSRect frameRect = [mWindow frame];
  1244   DestroyNativeWindow();
  1245   nsresult rv = CreateNativeWindow(frameRect, aShouldHide ? eBorderStyle_none : mBorderStyle, true);
  1246   NS_ENSURE_SUCCESS(rv, rv);
  1248   // Re-import state.
  1249   [mWindow importState:state];
  1251   // Reparent the content view.
  1252   [mWindow setContentView:contentView];
  1253   [contentView release];
  1255   // Reparent child windows.
  1256   enumerator = [childWindows objectEnumerator];
  1257   while ((child = [enumerator nextObject])) {
  1258     [mWindow addChildWindow:child ordered:NSWindowAbove];
  1261   // Show the new window.
  1262   if (isVisible) {
  1263     rv = Show(true);
  1264     NS_ENSURE_SUCCESS(rv, rv);
  1267   return NS_OK;
  1269   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1272 void nsCocoaWindow::EnteredFullScreen(bool aFullScreen)
  1274   mInFullScreenTransition = false;
  1275   mFullScreen = aFullScreen;
  1276   DispatchSizeModeEvent();
  1279 NS_METHOD nsCocoaWindow::MakeFullScreen(bool aFullScreen)
  1281   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1283   if (!mWindow) {
  1284     return NS_OK;
  1287   // We will call into MakeFullScreen redundantly when entering/exiting
  1288   // fullscreen mode via OS X controls. When that happens we should just handle
  1289   // it gracefully - no need to ASSERT.
  1290   if (mFullScreen == aFullScreen) {
  1291     return NS_OK;
  1294   // If we're using native fullscreen mode and our native window is invisible,
  1295   // our attempt to go into fullscreen mode will fail with an assertion in
  1296   // system code, without [WindowDelegate windowDidFailToEnterFullScreen:]
  1297   // ever getting called.  To pre-empt this we bail here.  See bug 752294.
  1298   if (mUsesNativeFullScreen && aFullScreen && ![mWindow isVisible]) {
  1299     EnteredFullScreen(false);
  1300     return NS_OK;
  1303   mInFullScreenTransition = true;
  1305   if (mUsesNativeFullScreen) {
  1306     // Calling toggleFullScreen will result in windowDid(FailTo)?(Enter|Exit)FullScreen
  1307     // to be called from the OS. We will call EnteredFullScreen from those methods,
  1308     // where mFullScreen will be set and a sizemode event will be dispatched.
  1309     [mWindow toggleFullScreen:nil];
  1310   } else {
  1311     NSDisableScreenUpdates();
  1312     // The order here matters. When we exit full screen mode, we need to show the
  1313     // Dock first, otherwise the newly-created window won't have its minimize
  1314     // button enabled. See bug 526282.
  1315     nsCocoaUtils::HideOSChromeOnScreen(aFullScreen, [mWindow screen]);
  1316     nsresult rv = nsBaseWidget::MakeFullScreen(aFullScreen);
  1317     NSEnableScreenUpdates();
  1318     NS_ENSURE_SUCCESS(rv, rv);
  1320     EnteredFullScreen(aFullScreen);
  1323   return NS_OK;
  1325   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1328 // Coordinates are global display pixels
  1329 nsresult nsCocoaWindow::DoResize(double aX, double aY,
  1330                                  double aWidth, double aHeight,
  1331                                  bool aRepaint,
  1332                                  bool aConstrainToCurrentScreen)
  1334   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1336   if (!mWindow || mInResize) {
  1337     return NS_OK;
  1340   AutoRestore<bool> reentrantResizeGuard(mInResize);
  1341   mInResize = true;
  1343   // ConstrainSize operates in device pixels, so we need to convert using
  1344   // the backing scale factor here
  1345   CGFloat scale = BackingScaleFactor();
  1346   int32_t width = NSToIntRound(aWidth * scale);
  1347   int32_t height = NSToIntRound(aHeight * scale);
  1348   ConstrainSize(&width, &height);
  1350   nsIntRect newBounds(NSToIntRound(aX), NSToIntRound(aY),
  1351                       NSToIntRound(width / scale),
  1352                       NSToIntRound(height / scale));
  1354   // constrain to the screen that contains the largest area of the new rect
  1355   FitRectToVisibleAreaForScreen(newBounds,
  1356                                 aConstrainToCurrentScreen ?
  1357                                   [mWindow screen] : nullptr,
  1358                                 mUsesNativeFullScreen);
  1360   // convert requested bounds into Cocoa coordinate system
  1361   NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(newBounds);
  1363   NSRect frame = [mWindow frame];
  1364   BOOL isMoving = newFrame.origin.x != frame.origin.x ||
  1365                   newFrame.origin.y != frame.origin.y;
  1366   BOOL isResizing = newFrame.size.width != frame.size.width ||
  1367                     newFrame.size.height != frame.size.height;
  1369   if (!isMoving && !isResizing) {
  1370     return NS_OK;
  1373   // We ignore aRepaint -- we have to call display:YES, otherwise the
  1374   // title bar doesn't immediately get repainted and is displayed in
  1375   // the wrong place, leading to a visual jump.
  1376   [mWindow setFrame:newFrame display:YES];
  1378   return NS_OK;
  1380   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1383 // Coordinates are global display pixels
  1384 NS_IMETHODIMP nsCocoaWindow::Resize(double aX, double aY,
  1385                                     double aWidth, double aHeight,
  1386                                     bool aRepaint)
  1388   return DoResize(aX, aY, aWidth, aHeight, aRepaint, false);
  1391 // Coordinates are global display pixels
  1392 NS_IMETHODIMP nsCocoaWindow::Resize(double aWidth, double aHeight, bool aRepaint)
  1394   double invScale = 1.0 / GetDefaultScale().scale;
  1395   return DoResize(mBounds.x * invScale, mBounds.y * invScale,
  1396                   aWidth, aHeight, aRepaint, true);
  1399 NS_IMETHODIMP nsCocoaWindow::GetClientBounds(nsIntRect &aRect)
  1401   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1403   CGFloat scaleFactor = BackingScaleFactor();
  1404   if (!mWindow) {
  1405     aRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(NSZeroRect, scaleFactor);
  1406     return NS_OK;
  1409   NSRect r;
  1410   if ([mWindow isKindOfClass:[ToolbarWindow class]] &&
  1411       [(ToolbarWindow*)mWindow drawsContentsIntoWindowFrame]) {
  1412     r = [mWindow frame];
  1413   } else {
  1414     r = [mWindow contentRectForFrameRect:[mWindow frame]];
  1417   aRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(r, scaleFactor);
  1419   return NS_OK;
  1421   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1424 void
  1425 nsCocoaWindow::UpdateBounds()
  1427   NSRect frame = NSZeroRect;
  1428   if (mWindow) {
  1429     frame = [mWindow frame];
  1431   mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, BackingScaleFactor());
  1434 NS_IMETHODIMP nsCocoaWindow::GetScreenBounds(nsIntRect &aRect)
  1436   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1438 #ifdef DEBUG
  1439   nsIntRect r = nsCocoaUtils::CocoaRectToGeckoRectDevPix([mWindow frame], BackingScaleFactor());
  1440   NS_ASSERTION(mWindow && mBounds == r, "mBounds out of sync!");
  1441 #endif
  1443   aRect = mBounds;
  1444   return NS_OK;
  1446   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1449 double
  1450 nsCocoaWindow::GetDefaultScaleInternal()
  1452   return BackingScaleFactor();
  1455 static CGFloat
  1456 GetBackingScaleFactor(NSWindow* aWindow)
  1458   NSRect frame = [aWindow frame];
  1459   if (frame.size.width > 0 && frame.size.height > 0) {
  1460     return nsCocoaUtils::GetBackingScaleFactor(aWindow);
  1463   // For windows with zero width or height, the backingScaleFactor method
  1464   // is broken - it will always return 2 on a retina macbook, even when
  1465   // the window position implies it's on a non-hidpi external display
  1466   // (to the extent that a zero-area window can be said to be "on" a
  1467   // display at all!)
  1468   // And to make matters worse, Cocoa even fires a
  1469   // windowDidChangeBackingProperties notification with the
  1470   // NSBackingPropertyOldScaleFactorKey key when a window on an
  1471   // external display is resized to/from zero height, even though it hasn't
  1472   // really changed screens.
  1474   // This causes us to handle popup window sizing incorrectly when the
  1475   // popup is resized to zero height (bug 820327) - nsXULPopupManager
  1476   // becomes (incorrectly) convinced the popup has been explicitly forced
  1477   // to a non-default size and needs to have size attributes attached.
  1479   // Workaround: instead of asking the window, we'll find the screen it is on
  1480   // and ask that for *its* backing scale factor.
  1482   // (See bug 853252 and additional comments in windowDidChangeScreen: below
  1483   // for further complications this causes.)
  1485   // First, expand the rect so that it actually has a measurable area,
  1486   // for FindTargetScreenForRect to use.
  1487   if (frame.size.width == 0) {
  1488     frame.size.width = 1;
  1490   if (frame.size.height == 0) {
  1491     frame.size.height = 1;
  1494   // Then identify the screen it belongs to, and return its scale factor.
  1495   NSScreen *screen =
  1496     FindTargetScreenForRect(nsCocoaUtils::CocoaRectToGeckoRect(frame));
  1497   return nsCocoaUtils::GetBackingScaleFactor(screen);
  1500 CGFloat
  1501 nsCocoaWindow::BackingScaleFactor()
  1503   if (mBackingScaleFactor > 0.0) {
  1504     return mBackingScaleFactor;
  1506   if (!mWindow) {
  1507     return 1.0;
  1509   mBackingScaleFactor = GetBackingScaleFactor(mWindow);
  1510   return mBackingScaleFactor;
  1513 void
  1514 nsCocoaWindow::BackingScaleFactorChanged()
  1516   CGFloat newScale = GetBackingScaleFactor(mWindow);
  1518   // ignore notification if it hasn't really changed (or maybe we have
  1519   // disabled HiDPI mode via prefs)
  1520   if (mBackingScaleFactor == newScale) {
  1521     return;
  1524   if (mBackingScaleFactor > 0.0) {
  1525     // convert size constraints to the new device pixel coordinate space
  1526     double scaleFactor = newScale / mBackingScaleFactor;
  1527     mSizeConstraints.mMinSize.width =
  1528       NSToIntRound(mSizeConstraints.mMinSize.width * scaleFactor);
  1529     mSizeConstraints.mMinSize.height =
  1530       NSToIntRound(mSizeConstraints.mMinSize.height * scaleFactor);
  1531     if (mSizeConstraints.mMaxSize.width < NS_MAXSIZE) {
  1532       mSizeConstraints.mMaxSize.width =
  1533         std::min(NS_MAXSIZE,
  1534                NSToIntRound(mSizeConstraints.mMaxSize.width * scaleFactor));
  1536     if (mSizeConstraints.mMaxSize.height < NS_MAXSIZE) {
  1537       mSizeConstraints.mMaxSize.height =
  1538         std::min(NS_MAXSIZE,
  1539                NSToIntRound(mSizeConstraints.mMaxSize.height * scaleFactor));
  1543   mBackingScaleFactor = newScale;
  1545   if (!mWidgetListener || mWidgetListener->GetXULWindow()) {
  1546     return;
  1549   nsIPresShell* presShell = mWidgetListener->GetPresShell();
  1550   if (presShell) {
  1551     presShell->BackingScaleFactorChanged();
  1555 int32_t
  1556 nsCocoaWindow::RoundsWidgetCoordinatesTo()
  1558   if (BackingScaleFactor() == 2.0) {
  1559     return 2;
  1561   return 1;
  1564 NS_IMETHODIMP nsCocoaWindow::SetCursor(nsCursor aCursor)
  1566   if (mPopupContentView)
  1567     return mPopupContentView->SetCursor(aCursor);
  1569   return NS_OK;
  1572 NS_IMETHODIMP nsCocoaWindow::SetCursor(imgIContainer* aCursor,
  1573                                        uint32_t aHotspotX, uint32_t aHotspotY)
  1575   if (mPopupContentView)
  1576     return mPopupContentView->SetCursor(aCursor, aHotspotX, aHotspotY);
  1578   return NS_OK;
  1581 NS_IMETHODIMP nsCocoaWindow::SetTitle(const nsAString& aTitle)
  1583   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1585   if (!mWindow)
  1586     return NS_OK;
  1588   const nsString& strTitle = PromiseFlatString(aTitle);
  1589   NSString* title = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(strTitle.get())
  1590                                             length:strTitle.Length()];
  1592   if ([mWindow drawsContentsIntoWindowFrame] && ![mWindow wantsTitleDrawn]) {
  1593     // Don't cause invalidations.
  1594     [mWindow disableSetNeedsDisplay];
  1595     [mWindow setTitle:title];
  1596     [mWindow enableSetNeedsDisplay];
  1597   } else {
  1598     [mWindow setTitle:title];
  1601   return NS_OK;
  1603   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1606 NS_IMETHODIMP nsCocoaWindow::Invalidate(const nsIntRect & aRect)
  1608   if (mPopupContentView) {
  1609     return mPopupContentView->Invalidate(aRect);
  1612   return NS_OK;
  1615 // Pass notification of some drag event to Gecko
  1616 //
  1617 // The drag manager has let us know that something related to a drag has
  1618 // occurred in this window. It could be any number of things, ranging from 
  1619 // a drop, to a drag enter/leave, or a drag over event. The actual event
  1620 // is passed in |aMessage| and is passed along to our event hanlder so Gecko
  1621 // knows about it.
  1622 bool nsCocoaWindow::DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers)
  1624   return false;
  1627 NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent()
  1629   nsWindowZ placement = nsWindowZTop;
  1630   nsIWidget* actualBelow;
  1631   if (mWidgetListener)
  1632     mWidgetListener->ZLevelChanged(true, &placement, nullptr, &actualBelow);
  1633   return NS_OK;
  1636 NS_IMETHODIMP nsCocoaWindow::GetChildSheet(bool aShown, nsCocoaWindow** _retval)
  1638   nsIWidget* child = GetFirstChild();
  1640   while (child) {
  1641     if (child->WindowType() == eWindowType_sheet) {
  1642       // if it's a sheet, it must be an nsCocoaWindow
  1643       nsCocoaWindow* cocoaWindow = static_cast<nsCocoaWindow*>(child);
  1644       if (cocoaWindow->mWindow &&
  1645           ((aShown && [cocoaWindow->mWindow isVisible]) ||
  1646           (!aShown && cocoaWindow->mSheetNeedsShow))) {
  1647         *_retval = cocoaWindow;
  1648         return NS_OK;
  1651     child = child->GetNextSibling();
  1654   *_retval = nullptr;
  1656   return NS_OK;
  1659 NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent)
  1661   *parent = mParent;
  1662   return NS_OK;
  1665 NS_IMETHODIMP nsCocoaWindow::GetIsSheet(bool* isSheet)
  1667   mWindowType == eWindowType_sheet ? *isSheet = true : *isSheet = false;
  1668   return NS_OK;
  1671 NS_IMETHODIMP nsCocoaWindow::GetSheetWindowParent(NSWindow** sheetWindowParent)
  1673   *sheetWindowParent = mSheetWindowParent;
  1674   return NS_OK;
  1677 // Invokes callback and ProcessEvent methods on Event Listener object
  1678 NS_IMETHODIMP 
  1679 nsCocoaWindow::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus)
  1681   aStatus = nsEventStatus_eIgnore;
  1683   nsIWidget* aWidget = event->widget;
  1684   NS_IF_ADDREF(aWidget);
  1686   if (mWidgetListener)
  1687     aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents);
  1689   NS_IF_RELEASE(aWidget);
  1691   return NS_OK;
  1694 // aFullScreen should be the window's mFullScreen. We don't have access to that
  1695 // from here, so we need to pass it in. mFullScreen should be the canonical
  1696 // indicator that a window is currently full screen and it makes sense to keep
  1697 // all sizemode logic here.
  1698 static nsSizeMode
  1699 GetWindowSizeMode(NSWindow* aWindow, bool aFullScreen) {
  1700   if (aFullScreen)
  1701     return nsSizeMode_Fullscreen;
  1702   if ([aWindow isMiniaturized])
  1703     return nsSizeMode_Minimized;
  1704   if (([aWindow styleMask] & NSResizableWindowMask) && [aWindow isZoomed])
  1705     return nsSizeMode_Maximized;
  1706   return nsSizeMode_Normal;
  1709 void
  1710 nsCocoaWindow::ReportMoveEvent()
  1712   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1714   // Prevent recursion, which can become infinite (see bug 708278).  This
  1715   // can happen when the call to [NSWindow setFrameTopLeftPoint:] in
  1716   // nsCocoaWindow::Move() triggers an immediate NSWindowDidMove notification
  1717   // (and a call to [WindowDelegate windowDidMove:]).
  1718   if (mInReportMoveEvent) {
  1719     return;
  1721   mInReportMoveEvent = true;
  1723   UpdateBounds();
  1725   // Dispatch the move event to Gecko
  1726   NotifyWindowMoved(mBounds.x, mBounds.y);
  1728   mInReportMoveEvent = false;
  1730   NS_OBJC_END_TRY_ABORT_BLOCK;
  1733 void
  1734 nsCocoaWindow::DispatchSizeModeEvent()
  1736   if (!mWindow) {
  1737     return;
  1740   nsSizeMode newMode = GetWindowSizeMode(mWindow, mFullScreen);
  1742   // Don't dispatch a sizemode event if:
  1743   // 1. the window is transitioning to fullscreen
  1744   // 2. the new sizemode is the same as the current sizemode
  1745   if (mInFullScreenTransition || mSizeMode == newMode) {
  1746     return;
  1749   mSizeMode = newMode;
  1750   if (mWidgetListener) {
  1751     mWidgetListener->SizeModeChanged(newMode);
  1755 void
  1756 nsCocoaWindow::ReportSizeEvent()
  1758   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1760   UpdateBounds();
  1762   if (mWidgetListener) {
  1763     nsIntRect innerBounds;
  1764     GetClientBounds(innerBounds);
  1765     mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height);
  1768   NS_OBJC_END_TRY_ABORT_BLOCK;
  1771 void nsCocoaWindow::SetMenuBar(nsMenuBarX *aMenuBar)
  1773   if (mMenuBar)
  1774     mMenuBar->SetParent(nullptr);
  1775   if (!mWindow) {
  1776     mMenuBar = nullptr;
  1777     return;
  1779   mMenuBar = aMenuBar;
  1781   // Only paint for active windows, or paint the hidden window menu bar if no
  1782   // other menu bar has been painted yet so that some reasonable menu bar is
  1783   // displayed when the app starts up.
  1784   id windowDelegate = [mWindow delegate];
  1785   if (mMenuBar &&
  1786       ((!gSomeMenuBarPainted && nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar) ||
  1787        (windowDelegate && [windowDelegate toplevelActiveState])))
  1788     mMenuBar->Paint();
  1791 NS_IMETHODIMP nsCocoaWindow::SetFocus(bool aState)
  1793   if (!mWindow)
  1794     return NS_OK;
  1796   if (mPopupContentView) {
  1797     mPopupContentView->SetFocus(aState);
  1799   else if (aState && ([mWindow isVisible] || [mWindow isMiniaturized])) {
  1800     [mWindow makeKeyAndOrderFront:nil];
  1801     SendSetZLevelEvent();
  1804   return NS_OK;
  1807 nsIntPoint nsCocoaWindow::WidgetToScreenOffset()
  1809   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1811   NSRect rect = NSZeroRect;
  1812   nsIntRect r;
  1813   if (mWindow) {
  1814     rect = [mWindow contentRectForFrameRect:[mWindow frame]];
  1816   r = nsCocoaUtils::CocoaRectToGeckoRectDevPix(rect, BackingScaleFactor());
  1818   return r.TopLeft();
  1820   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0,0));
  1823 nsIntPoint nsCocoaWindow::GetClientOffset()
  1825   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1827   nsIntRect clientRect;
  1828   GetClientBounds(clientRect);
  1830   return clientRect.TopLeft() - mBounds.TopLeft();
  1832   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0, 0));
  1835 nsIntSize nsCocoaWindow::ClientToWindowSize(const nsIntSize& aClientSize)
  1837   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1839   if (!mWindow)
  1840     return nsIntSize(0, 0);
  1842   CGFloat backingScale = BackingScaleFactor();
  1843   nsIntRect r(0, 0, aClientSize.width, aClientSize.height);
  1844   NSRect rect = nsCocoaUtils::DevPixelsToCocoaPoints(r, backingScale);
  1846   NSRect inflatedRect = [mWindow frameRectForContentRect:rect];
  1847   return nsCocoaUtils::CocoaRectToGeckoRectDevPix(inflatedRect, backingScale).Size();
  1849   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntSize(0,0));
  1852 nsMenuBarX* nsCocoaWindow::GetMenuBar()
  1854   return mMenuBar;
  1857 NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture)
  1859   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1861   gRollupListener = nullptr;
  1863   if (aDoCapture) {
  1864     if (![NSApp isActive]) {
  1865       // We need to capture mouse event if we aren't
  1866       // the active application. We only set this up when needed
  1867       // because they cause spurious mouse event after crash
  1868       // and gdb sessions. See bug 699538.
  1869       nsToolkit::GetToolkit()->RegisterForAllProcessMouseEvents();
  1871     gRollupListener = aListener;
  1873     // Sometimes more than one popup window can be visible at the same time
  1874     // (e.g. nested non-native context menus, or the test case (attachment
  1875     // 276885) for bmo bug 392389, which displays a non-native combo-box in a
  1876     // non-native popup window).  In these cases the "active" popup window should
  1877     // be the topmost -- the (nested) context menu the mouse is currently over,
  1878     // or the combo-box's drop-down list (when it's displayed).  But (among
  1879     // windows that have the same "level") OS X makes topmost the window that
  1880     // last received a mouse-down event, which may be incorrect (in the combo-
  1881     // box case, it makes topmost the window containing the combo-box).  So
  1882     // here we fiddle with a non-native popup window's level to make sure the
  1883     // "active" one is always above any other non-native popup windows that
  1884     // may be visible.
  1885     if (mWindow && (mWindowType == eWindowType_popup))
  1886       SetPopupWindowLevel();
  1887   } else {
  1888     nsToolkit::GetToolkit()->UnregisterAllProcessMouseEventHandlers();
  1890     // XXXndeakin this doesn't make sense.
  1891     // Why is the new window assumed to be a modal panel?
  1892     if (mWindow && (mWindowType == eWindowType_popup))
  1893       [mWindow setLevel:NSModalPanelWindowLevel];
  1896   return NS_OK;
  1898   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1901 NS_IMETHODIMP nsCocoaWindow::GetAttention(int32_t aCycleCount)
  1903   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1905   [NSApp requestUserAttention:NSInformationalRequest];
  1906   return NS_OK;
  1908   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1911 bool
  1912 nsCocoaWindow::HasPendingInputEvent()
  1914   return nsChildView::DoHasPendingInputEvent();
  1917 NS_IMETHODIMP nsCocoaWindow::SetWindowShadowStyle(int32_t aStyle)
  1919   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1921   if (!mWindow)
  1922     return NS_OK;
  1924   mShadowStyle = aStyle;
  1925   [mWindow setHasShadow:(aStyle != NS_STYLE_WINDOW_SHADOW_NONE)];
  1926   AdjustWindowShadow();
  1927   SetWindowBackgroundBlur();
  1929   return NS_OK;
  1931   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1934 void nsCocoaWindow::SetShowsToolbarButton(bool aShow)
  1936   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1938   if (mWindow)
  1939     [mWindow setShowsToolbarButton:aShow];
  1941   NS_OBJC_END_TRY_ABORT_BLOCK;
  1944 void nsCocoaWindow::SetShowsFullScreenButton(bool aShow)
  1946   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1948   if (!mWindow || ![mWindow respondsToSelector:@selector(toggleFullScreen:)] ||
  1949       mUsesNativeFullScreen == aShow) {
  1950     return;
  1953   // If the window is currently in fullscreen mode, then we're going to
  1954   // transition out first, then set the collection behavior & toggle
  1955   // mUsesNativeFullScreen, then transtion back into fullscreen mode. This
  1956   // prevents us from getting into a conflicting state with MakeFullScreen
  1957   // where mUsesNativeFullScreen would lead us down the wrong path.
  1958   bool wasFullScreen = mFullScreen;
  1960   if (wasFullScreen) {
  1961     MakeFullScreen(false);
  1964   NSWindowCollectionBehavior newBehavior = [mWindow collectionBehavior];
  1965   if (aShow) {
  1966     newBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
  1967   } else {
  1968     newBehavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
  1970   [mWindow setCollectionBehavior:newBehavior];
  1971   mUsesNativeFullScreen = aShow;
  1973   if (wasFullScreen) {
  1974     MakeFullScreen(true);
  1977   NS_OBJC_END_TRY_ABORT_BLOCK;
  1980 void nsCocoaWindow::SetWindowAnimationType(nsIWidget::WindowAnimationType aType)
  1982   mAnimationType = aType;
  1985 void
  1986 nsCocoaWindow::SetDrawsTitle(bool aDrawTitle)
  1988   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1990   [mWindow setWantsTitleDrawn:aDrawTitle];
  1992   NS_OBJC_END_TRY_ABORT_BLOCK;
  1995 NS_IMETHODIMP nsCocoaWindow::SetNonClientMargins(nsIntMargin &margins)
  1997   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1999   SetDrawsInTitlebar(margins.top == 0);
  2001   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  2004 NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, bool aActive)
  2006   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  2008   if (!mWindow)
  2009     return NS_OK;
  2011   // If they pass a color with a complete transparent alpha component, use the
  2012   // native titlebar appearance.
  2013   if (NS_GET_A(aColor) == 0) {
  2014     [mWindow setTitlebarColor:nil forActiveWindow:(BOOL)aActive]; 
  2015   } else {
  2016     // Transform from sRGBA to monitor RGBA. This seems like it would make trying
  2017     // to match the system appearance lame, so probably we just shouldn't color 
  2018     // correct chrome.
  2019     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
  2020       qcms_transform *transform = gfxPlatform::GetCMSRGBATransform();
  2021       if (transform) {
  2022         uint8_t color[3];
  2023         color[0] = NS_GET_R(aColor);
  2024         color[1] = NS_GET_G(aColor);
  2025         color[2] = NS_GET_B(aColor);
  2026         qcms_transform_data(transform, color, color, 1);
  2027         aColor = NS_RGB(color[0], color[1], color[2]);
  2031     [mWindow setTitlebarColor:[NSColor colorWithDeviceRed:NS_GET_R(aColor)/255.0
  2032                                                     green:NS_GET_G(aColor)/255.0
  2033                                                      blue:NS_GET_B(aColor)/255.0
  2034                                                     alpha:NS_GET_A(aColor)/255.0]
  2035               forActiveWindow:(BOOL)aActive];
  2037   return NS_OK;
  2039   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  2042 void nsCocoaWindow::SetDrawsInTitlebar(bool aState)
  2044   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2046   if (mWindow)
  2047     [mWindow setDrawsContentsIntoWindowFrame:aState];
  2049   NS_OBJC_END_TRY_ABORT_BLOCK;
  2052 NS_IMETHODIMP nsCocoaWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
  2053                                                         uint32_t aNativeMessage,
  2054                                                         uint32_t aModifierFlags)
  2056   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  2058   if (mPopupContentView)
  2059     return mPopupContentView->SynthesizeNativeMouseEvent(aPoint, aNativeMessage,
  2060                                                          aModifierFlags);
  2062   return NS_OK;
  2064   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  2067 gfxASurface* nsCocoaWindow::GetThebesSurface()
  2069   if (mPopupContentView)
  2070     return mPopupContentView->GetThebesSurface();
  2071   return nullptr;
  2074 void nsCocoaWindow::SetPopupWindowLevel()
  2076   if (!mWindow)
  2077     return;
  2079   // Floating popups are at the floating level and hide when the window is
  2080   // deactivated.
  2081   if (mPopupLevel == ePopupLevelFloating) {
  2082     [mWindow setLevel:NSFloatingWindowLevel];
  2083     [mWindow setHidesOnDeactivate:YES];
  2085   else {
  2086     // Otherwise, this is a top-level or parent popup. Parent popups always
  2087     // appear just above their parent and essentially ignore the level.
  2088     [mWindow setLevel:NSPopUpMenuWindowLevel];
  2089     [mWindow setHidesOnDeactivate:NO];
  2093 NS_IMETHODIMP
  2094 nsCocoaWindow::NotifyIME(const IMENotification& aIMENotification)
  2096   switch (aIMENotification.mMessage) {
  2097     case NOTIFY_IME_OF_FOCUS:
  2098       if (mInputContext.IsPasswordEditor()) {
  2099         TextInputHandler::EnableSecureEventInput();
  2101       return NS_OK;
  2102     case NOTIFY_IME_OF_BLUR:
  2103       // When we're going to be deactive, we must disable the secure event input
  2104       // mode, see the Carbon Event Manager Reference.
  2105       TextInputHandler::EnsureSecureEventInputDisabled();
  2106       return NS_OK;
  2107     default:
  2108       return NS_ERROR_NOT_IMPLEMENTED;
  2112 NS_IMETHODIMP_(void)
  2113 nsCocoaWindow::SetInputContext(const InputContext& aContext,
  2114                                const InputContextAction& aAction)
  2116   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2118   if (mWindow &&
  2119       [mWindow firstResponder] == mWindow &&
  2120       [mWindow isKeyWindow] &&
  2121       [[NSApplication sharedApplication] isActive]) {
  2122     if (aContext.IsPasswordEditor()) {
  2123       TextInputHandler::EnableSecureEventInput();
  2124     } else {
  2125       TextInputHandler::EnsureSecureEventInputDisabled();
  2128   mInputContext = aContext;
  2130   NS_OBJC_END_TRY_ABORT_BLOCK;
  2133 NS_IMETHODIMP_(bool)
  2134 nsCocoaWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
  2135                                        const WidgetKeyboardEvent& aEvent,
  2136                                        DoCommandCallback aCallback,
  2137                                        void* aCallbackData)
  2139   NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
  2140   return keyBindings->Execute(aEvent, aCallback, aCallbackData);
  2144 @implementation WindowDelegate
  2146 // We try to find a gecko menu bar to paint. If one does not exist, just paint
  2147 // the application menu by itself so that a window doesn't have some other
  2148 // window's menu bar.
  2149 + (void)paintMenubarForWindow:(NSWindow*)aWindow
  2151   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2153   // make sure we only act on windows that have this kind of
  2154   // object as a delegate
  2155   id windowDelegate = [aWindow delegate];
  2156   if ([windowDelegate class] != [self class])
  2157     return;
  2159   nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget];
  2160   NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!");
  2162   nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar();
  2163   if (geckoMenuBar) {
  2164     geckoMenuBar->Paint();
  2166   else {
  2167     // sometimes we don't have a native application menu early in launching
  2168     if (!sApplicationMenu)
  2169       return;
  2171     NSMenu* mainMenu = [NSApp mainMenu];
  2172     NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
  2174     // Create a new menu bar.
  2175     // We create a GeckoNSMenu because all menu bar NSMenu objects should use that subclass for
  2176     // key handling reasons.
  2177     GeckoNSMenu* newMenuBar = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
  2179     // move the application menu from the existing menu bar to the new one
  2180     NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
  2181     [mainMenu removeItemAtIndex:0];
  2182     [newMenuBar insertItem:firstMenuItem atIndex:0];
  2183     [firstMenuItem release];
  2185     // set our new menu bar as the main menu
  2186     [NSApp setMainMenu:newMenuBar];
  2187     [newMenuBar release];
  2190   NS_OBJC_END_TRY_ABORT_BLOCK;
  2193 - (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind
  2195   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  2197   [super init];
  2198   mGeckoWindow = geckoWind;
  2199   mToplevelActiveState = false;
  2200   mHasEverBeenZoomed = false;
  2201   return self;
  2203   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  2206 - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
  2208   RollUpPopups();
  2210   return proposedFrameSize;
  2213 - (void)windowDidResize:(NSNotification *)aNotification
  2215   BaseWindow* window = [aNotification object];
  2216   [window updateTrackingArea];
  2218   if (!mGeckoWindow)
  2219     return;
  2221   // Resizing might have changed our zoom state.
  2222   mGeckoWindow->DispatchSizeModeEvent();
  2223   mGeckoWindow->ReportSizeEvent();
  2226 - (void)windowDidChangeScreen:(NSNotification *)aNotification
  2228   if (!mGeckoWindow)
  2229     return;
  2231   // Because of Cocoa's peculiar treatment of zero-size windows (see comments
  2232   // at GetBackingScaleFactor() above), we sometimes have a situation where
  2233   // our concept of backing scale (based on the screen where the zero-sized
  2234   // window is positioned) differs from Cocoa's idea (always based on the
  2235   // Retina screen, AFAICT, even when an external non-Retina screen is the
  2236   // primary display).
  2237   //
  2238   // As a result, if the window was created with zero size on an external
  2239   // display, but then made visible on the (secondary) Retina screen, we
  2240   // will *not* get a windowDidChangeBackingProperties notification for it.
  2241   // This leads to an incorrect GetDefaultScale(), and widget coordinate
  2242   // confusion, as per bug 853252.
  2243   //
  2244   // To work around this, we check for a backing scale mismatch when we
  2245   // receive a windowDidChangeScreen notification, as we will receive this
  2246   // even if Cocoa was already treating the zero-size window as having
  2247   // Retina backing scale.
  2248   NSWindow *window = (NSWindow *)[aNotification object];
  2249   if ([window respondsToSelector:@selector(backingScaleFactor)]) {
  2250     if (GetBackingScaleFactor(window) != mGeckoWindow->BackingScaleFactor()) {
  2251       mGeckoWindow->BackingScaleFactorChanged();
  2255   mGeckoWindow->ReportMoveEvent();
  2258 // Lion's full screen mode will bypass our internal fullscreen tracking, so
  2259 // we need to catch it when we transition and call our own methods, which in
  2260 // turn will fire "fullscreen" events.
  2261 - (void)windowDidEnterFullScreen:(NSNotification *)notification
  2263   if (!mGeckoWindow) {
  2264     return;
  2267   mGeckoWindow->EnteredFullScreen(true);
  2270 - (void)windowDidExitFullScreen:(NSNotification *)notification
  2272   if (!mGeckoWindow) {
  2273     return;
  2276   mGeckoWindow->EnteredFullScreen(false);
  2279 - (void)windowDidFailToEnterFullScreen:(NSWindow *)window
  2281   if (!mGeckoWindow) {
  2282     return;
  2285   mGeckoWindow->EnteredFullScreen(false);
  2288 - (void)windowDidFailToExitFullScreen:(NSWindow *)window
  2290   if (!mGeckoWindow) {
  2291     return;
  2294   mGeckoWindow->EnteredFullScreen(true);
  2297 - (void)windowDidBecomeMain:(NSNotification *)aNotification
  2299   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2301   RollUpPopups();
  2302   ChildViewMouseTracker::ReEvaluateMouseEnterState();
  2304   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
  2305   // app modally. If one of those is up then we want it to retain its menu bar.
  2306   if ([NSApp _isRunningAppModal])
  2307     return;
  2308   NSWindow* window = [aNotification object];
  2309   if (window)
  2310     [WindowDelegate paintMenubarForWindow:window];
  2312   NS_OBJC_END_TRY_ABORT_BLOCK;
  2315 - (void)windowDidResignMain:(NSNotification *)aNotification
  2317   RollUpPopups();
  2318   ChildViewMouseTracker::ReEvaluateMouseEnterState();
  2320   // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
  2321   // app modally. If one of those is up then we want it to retain its menu bar.
  2322   if ([NSApp _isRunningAppModal])
  2323     return;
  2324   nsRefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
  2325   if (hiddenWindowMenuBar) {
  2326     // printf("painting hidden window menu bar due to window losing main status\n");
  2327     hiddenWindowMenuBar->Paint();
  2331 - (void)windowDidBecomeKey:(NSNotification *)aNotification
  2333   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2335   RollUpPopups();
  2336   ChildViewMouseTracker::ReEvaluateMouseEnterState();
  2338   NSWindow* window = [aNotification object];
  2339   if ([window isSheet])
  2340     [WindowDelegate paintMenubarForWindow:window];
  2342   NS_OBJC_END_TRY_ABORT_BLOCK;
  2345 - (void)windowDidResignKey:(NSNotification *)aNotification
  2347   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2349   RollUpPopups();
  2350   ChildViewMouseTracker::ReEvaluateMouseEnterState();
  2352   // If a sheet just resigned key then we should paint the menu bar
  2353   // for whatever window is now main.
  2354   NSWindow* window = [aNotification object];
  2355   if ([window isSheet])
  2356     [WindowDelegate paintMenubarForWindow:[NSApp mainWindow]];
  2358   NS_OBJC_END_TRY_ABORT_BLOCK;
  2361 - (void)windowWillMove:(NSNotification *)aNotification
  2363   RollUpPopups();
  2366 - (void)windowDidMove:(NSNotification *)aNotification
  2368   if (mGeckoWindow)
  2369     mGeckoWindow->ReportMoveEvent();
  2372 - (BOOL)windowShouldClose:(id)sender
  2374   nsIWidgetListener* listener = mGeckoWindow ? mGeckoWindow->GetWidgetListener() : nullptr;
  2375   if (listener)
  2376     listener->RequestWindowClose(mGeckoWindow);
  2377   return NO; // gecko will do it
  2380 - (void)windowWillClose:(NSNotification *)aNotification
  2382   RollUpPopups();
  2385 - (void)windowWillMiniaturize:(NSNotification *)aNotification
  2387   RollUpPopups();
  2390 - (void)windowDidMiniaturize:(NSNotification *)aNotification
  2392   if (mGeckoWindow)
  2393     mGeckoWindow->DispatchSizeModeEvent();
  2396 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
  2398   if (mGeckoWindow)
  2399     mGeckoWindow->DispatchSizeModeEvent();
  2402 - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)proposedFrame
  2404   if (!mHasEverBeenZoomed && [window isZoomed])
  2405     return NO; // See bug 429954.
  2407   mHasEverBeenZoomed = YES;
  2408   return YES;
  2411 - (void)didEndSheet:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
  2413   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2415   // Note: 'contextInfo' (if it is set) is the window that is the parent of
  2416   // the sheet.  The value of contextInfo is determined in
  2417   // nsCocoaWindow::Show().  If it's set, 'contextInfo' is always the top-
  2418   // level window, not another sheet itself.  But 'contextInfo' is nil if
  2419   // our parent window is also a sheet -- in that case we shouldn't send
  2420   // the top-level window any activate events (because it's our parent
  2421   // window that needs to get these events, not the top-level window).
  2422   [TopLevelWindowData deactivateInWindow:sheet];
  2423   [sheet orderOut:self];
  2424   if (contextInfo)
  2425     [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo];
  2427   NS_OBJC_END_TRY_ABORT_BLOCK;
  2430 - (void)windowDidChangeBackingProperties:(NSNotification *)aNotification
  2432   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2434   NSWindow *window = (NSWindow *)[aNotification object];
  2436   if ([window respondsToSelector:@selector(backingScaleFactor)]) {
  2437     CGFloat oldFactor =
  2438       [[[aNotification userInfo]
  2439          objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
  2440     if ([window backingScaleFactor] != oldFactor) {
  2441       mGeckoWindow->BackingScaleFactorChanged();
  2445   NS_OBJC_END_TRY_ABORT_BLOCK;
  2448 - (nsCocoaWindow*)geckoWidget
  2450   return mGeckoWindow;
  2453 - (bool)toplevelActiveState
  2455   return mToplevelActiveState;
  2458 - (void)sendToplevelActivateEvents
  2460   if (!mToplevelActiveState && mGeckoWindow) {
  2461     nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener();
  2462     if (listener)
  2463       listener->WindowActivated();
  2464     mToplevelActiveState = true;
  2468 - (void)sendToplevelDeactivateEvents
  2470   if (mToplevelActiveState && mGeckoWindow) {
  2471     nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener();
  2472     if (listener)
  2473       listener->WindowDeactivated();
  2474     mToplevelActiveState = false;
  2478 @end
  2480 static float
  2481 GetDPI(NSWindow* aWindow)
  2483   NSScreen* screen = [aWindow screen];
  2484   if (!screen)
  2485     return 96.0f;
  2487   CGDirectDisplayID displayID =
  2488     [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
  2489   CGFloat heightMM = ::CGDisplayScreenSize(displayID).height;
  2490   size_t heightPx = ::CGDisplayPixelsHigh(displayID);
  2491   CGFloat scaleFactor = [aWindow userSpaceScaleFactor];
  2492   if (scaleFactor < 0.01 || heightMM < 1 || heightPx < 1) {
  2493     // Something extremely bogus is going on
  2494     return 96.0f;
  2497   // Currently we don't do our own scaling to take account
  2498   // of userSpaceScaleFactor, so every "pixel" we draw is actually
  2499   // userSpaceScaleFactor screen pixels. So divide the screen height
  2500   // by userSpaceScaleFactor to get the number of "device pixels"
  2501   // available.
  2502   float dpi = (heightPx / scaleFactor) / (heightMM / MM_PER_INCH_FLOAT);
  2504   // Account for HiDPI mode where Cocoa's "points" do not correspond to real
  2505   // device pixels
  2506   CGFloat backingScale = GetBackingScaleFactor(aWindow);
  2508   return dpi * backingScale;
  2511 @interface NSView(FrameViewMethodSwizzling)
  2512 - (NSPoint)FrameView__closeButtonOrigin;
  2513 - (NSPoint)FrameView__fullScreenButtonOrigin;
  2514 @end
  2516 @implementation NSView(FrameViewMethodSwizzling)
  2518 - (NSPoint)FrameView__closeButtonOrigin
  2520   NSPoint defaultPosition = [self FrameView__closeButtonOrigin];
  2521   if ([[self window] isKindOfClass:[ToolbarWindow class]]) {
  2522     return [(ToolbarWindow*)[self window] windowButtonsPositionWithDefaultPosition:defaultPosition];
  2524   return defaultPosition;
  2527 - (NSPoint)FrameView__fullScreenButtonOrigin
  2529   NSPoint defaultPosition = [self FrameView__fullScreenButtonOrigin];
  2530   if ([[self window] isKindOfClass:[ToolbarWindow class]]) {
  2531     return [(ToolbarWindow*)[self window] fullScreenButtonPositionWithDefaultPosition:defaultPosition];
  2533   return defaultPosition;
  2536 @end
  2538 static NSMutableSet *gSwizzledFrameViewClasses = nil;
  2540 @interface NSWindow(PrivateSetNeedsDisplayInRectMethod)
  2541  - (void)_setNeedsDisplayInRect:(NSRect)aRect;
  2542 @end
  2544 @interface BaseWindow(Private)
  2545 - (void)removeTrackingArea;
  2546 - (void)cursorUpdated:(NSEvent*)aEvent;
  2547 - (void)updateContentViewSize;
  2548 - (void)reflowTitlebarElements;
  2549 @end
  2551 @implementation BaseWindow
  2553 // The frame of a window is implemented using undocumented NSView subclasses.
  2554 // We offset the window buttons by overriding the methods _closeButtonOrigin
  2555 // and _fullScreenButtonOrigin on these frame view classes. The class which is
  2556 // used for a window is determined in the window's frameViewClassForStyleMask:
  2557 // method, so this is where we make sure that we have swizzled the method on
  2558 // all encountered classes.
  2559 + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask
  2561   Class frameViewClass = [super frameViewClassForStyleMask:styleMask];
  2563   if (!gSwizzledFrameViewClasses) {
  2564     gSwizzledFrameViewClasses = [[NSMutableSet setWithCapacity:3] retain];
  2565     if (!gSwizzledFrameViewClasses) {
  2566       return frameViewClass;
  2570   static IMP our_closeButtonOrigin =
  2571     class_getMethodImplementation([NSView class],
  2572                                   @selector(FrameView__closeButtonOrigin));
  2573   static IMP our_fullScreenButtonOrigin =
  2574     class_getMethodImplementation([NSView class],
  2575                                   @selector(FrameView__fullScreenButtonOrigin));
  2577   if (![gSwizzledFrameViewClasses containsObject:frameViewClass]) {
  2578     // Either of these methods might be implemented in both a subclass of
  2579     // NSFrameView and one of its own subclasses.  Which means that if we
  2580     // aren't careful we might end up swizzling the same method twice.
  2581     // Since method swizzling involves swapping pointers, this would break
  2582     // things.
  2583     IMP _closeButtonOrigin =
  2584       class_getMethodImplementation(frameViewClass,
  2585                                     @selector(_closeButtonOrigin));
  2586     if (_closeButtonOrigin && _closeButtonOrigin != our_closeButtonOrigin) {
  2587       nsToolkit::SwizzleMethods(frameViewClass, @selector(_closeButtonOrigin),
  2588                                 @selector(FrameView__closeButtonOrigin));
  2590     IMP _fullScreenButtonOrigin =
  2591       class_getMethodImplementation(frameViewClass,
  2592                                     @selector(_fullScreenButtonOrigin));
  2593     if (_fullScreenButtonOrigin &&
  2594         _fullScreenButtonOrigin != our_fullScreenButtonOrigin) {
  2595       nsToolkit::SwizzleMethods(frameViewClass, @selector(_fullScreenButtonOrigin),
  2596                                 @selector(FrameView__fullScreenButtonOrigin));
  2598     [gSwizzledFrameViewClasses addObject:frameViewClass];
  2601   return frameViewClass;
  2604 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
  2606   mDrawsIntoWindowFrame = NO;
  2607   [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag];
  2608   mState = nil;
  2609   mActiveTitlebarColor = nil;
  2610   mInactiveTitlebarColor = nil;
  2611   mScheduledShadowInvalidation = NO;
  2612   mDisabledNeedsDisplay = NO;
  2613   mDPI = GetDPI(self);
  2614   mTrackingArea = nil;
  2615   mBeingShown = NO;
  2616   mDrawTitle = NO;
  2617   [self updateTrackingArea];
  2619   return self;
  2622 - (void)setBeingShown:(BOOL)aValue
  2624   mBeingShown = aValue;
  2627 - (BOOL)isBeingShown
  2629   return mBeingShown;
  2632 - (BOOL)isVisibleOrBeingShown
  2634   return [super isVisible] || mBeingShown;
  2637 - (void)disableSetNeedsDisplay
  2639   mDisabledNeedsDisplay = YES;
  2642 - (void)enableSetNeedsDisplay
  2644   mDisabledNeedsDisplay = NO;
  2647 - (void)dealloc
  2649   [mActiveTitlebarColor release];
  2650   [mInactiveTitlebarColor release];
  2651   [self removeTrackingArea];
  2652   ChildViewMouseTracker::OnDestroyWindow(self);
  2653   [super dealloc];
  2656 static const NSString* kStateTitleKey = @"title";
  2657 static const NSString* kStateDrawsContentsIntoWindowFrameKey = @"drawsContentsIntoWindowFrame";
  2658 static const NSString* kStateActiveTitlebarColorKey = @"activeTitlebarColor";
  2659 static const NSString* kStateInactiveTitlebarColorKey = @"inactiveTitlebarColor";
  2660 static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
  2662 - (void)importState:(NSDictionary*)aState
  2664   [self setTitle:[aState objectForKey:kStateTitleKey]];
  2665   [self setDrawsContentsIntoWindowFrame:[[aState objectForKey:kStateDrawsContentsIntoWindowFrameKey] boolValue]];
  2666   [self setTitlebarColor:[aState objectForKey:kStateActiveTitlebarColorKey] forActiveWindow:YES];
  2667   [self setTitlebarColor:[aState objectForKey:kStateInactiveTitlebarColorKey] forActiveWindow:NO];
  2668   [self setShowsToolbarButton:[[aState objectForKey:kStateShowsToolbarButton] boolValue]];
  2671 - (NSMutableDictionary*)exportState
  2673   NSMutableDictionary* state = [NSMutableDictionary dictionaryWithCapacity:10];
  2674   [state setObject:[self title] forKey:kStateTitleKey];
  2675   [state setObject:[NSNumber numberWithBool:[self drawsContentsIntoWindowFrame]]
  2676             forKey:kStateDrawsContentsIntoWindowFrameKey];
  2677   NSColor* activeTitlebarColor = [self titlebarColorForActiveWindow:YES];
  2678   if (activeTitlebarColor) {
  2679     [state setObject:activeTitlebarColor forKey:kStateActiveTitlebarColorKey];
  2681   NSColor* inactiveTitlebarColor = [self titlebarColorForActiveWindow:NO];
  2682   if (inactiveTitlebarColor) {
  2683     [state setObject:inactiveTitlebarColor forKey:kStateInactiveTitlebarColorKey];
  2685   [state setObject:[NSNumber numberWithBool:[self showsToolbarButton]]
  2686             forKey:kStateShowsToolbarButton];
  2687   return state;
  2690 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
  2692   bool changed = (aState != mDrawsIntoWindowFrame);
  2693   mDrawsIntoWindowFrame = aState;
  2694   if (changed) {
  2695     [self updateContentViewSize];
  2696     [self reflowTitlebarElements];
  2700 - (BOOL)drawsContentsIntoWindowFrame
  2702   return mDrawsIntoWindowFrame;
  2705 - (void)setWantsTitleDrawn:(BOOL)aDrawTitle
  2707   mDrawTitle = aDrawTitle;
  2710 - (BOOL)wantsTitleDrawn
  2712   return mDrawTitle;
  2715 // Pass nil here to get the default appearance.
  2716 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
  2718   [aColor retain];
  2719   if (aActive) {
  2720     [mActiveTitlebarColor release];
  2721     mActiveTitlebarColor = aColor;
  2722   } else {
  2723     [mInactiveTitlebarColor release];
  2724     mInactiveTitlebarColor = aColor;
  2728 - (NSColor*)titlebarColorForActiveWindow:(BOOL)aActive
  2730   return aActive ? mActiveTitlebarColor : mInactiveTitlebarColor;
  2733 - (void)deferredInvalidateShadow
  2735   if (mScheduledShadowInvalidation || [self isOpaque] || ![self hasShadow])
  2736     return;
  2738   [self performSelector:@selector(invalidateShadow) withObject:nil afterDelay:0];
  2739   mScheduledShadowInvalidation = YES;
  2742 - (void)invalidateShadow
  2744   [super invalidateShadow];
  2745   mScheduledShadowInvalidation = NO;
  2748 - (float)getDPI
  2750   return mDPI;
  2753 - (NSView*)trackingAreaView
  2755   NSView* contentView = [self contentView];
  2756   return [contentView superview] ? [contentView superview] : contentView;
  2759 - (ChildView*)mainChildView
  2761   NSView *contentView = [self contentView];
  2762   // A PopupWindow's contentView is a ChildView object.
  2763   if ([contentView isKindOfClass:[ChildView class]]) {
  2764     return (ChildView*)contentView;
  2766   NSView* lastView = [[contentView subviews] lastObject];
  2767   if ([lastView isKindOfClass:[ChildView class]]) {
  2768     return (ChildView*)lastView;
  2770   return nil;
  2773 - (void)removeTrackingArea
  2775   if (mTrackingArea) {
  2776     [[self trackingAreaView] removeTrackingArea:mTrackingArea];
  2777     [mTrackingArea release];
  2778     mTrackingArea = nil;
  2782 - (void)updateTrackingArea
  2784   [self removeTrackingArea];
  2786   NSView* view = [self trackingAreaView];
  2787   const NSTrackingAreaOptions options =
  2788     NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways;
  2789   mTrackingArea = [[NSTrackingArea alloc] initWithRect:[view bounds]
  2790                                                options:options
  2791                                                  owner:self
  2792                                               userInfo:nil];
  2793   [view addTrackingArea:mTrackingArea];
  2796 - (void)mouseEntered:(NSEvent*)aEvent
  2798   ChildViewMouseTracker::MouseEnteredWindow(aEvent);
  2801 - (void)mouseExited:(NSEvent*)aEvent
  2803   ChildViewMouseTracker::MouseExitedWindow(aEvent);
  2806 - (void)mouseMoved:(NSEvent*)aEvent
  2808   ChildViewMouseTracker::MouseMoved(aEvent);
  2811 - (void)cursorUpdated:(NSEvent*)aEvent
  2813   // Nothing to do here, but NSTrackingArea wants us to implement this method.
  2816 - (void)_setNeedsDisplayInRect:(NSRect)aRect
  2818   // Prevent unnecessary invalidations due to moving NSViews (e.g. for plugins)
  2819   if (!mDisabledNeedsDisplay) {
  2820     // This method is only called by Cocoa, so when we're here, we know that
  2821     // it's available and don't need to check whether our superclass responds
  2822     // to the selector.
  2823     [super _setNeedsDisplayInRect:aRect];
  2827 - (void)updateContentViewSize
  2829   NSRect rect = [self contentRectForFrameRect:[self frame]];
  2830   [[self contentView] setFrameSize:rect.size];
  2833 // Possibly move the titlebar buttons.
  2834 - (void)reflowTitlebarElements
  2836   NSView *frameView = [[self contentView] superview];
  2837   if ([frameView respondsToSelector:@selector(_tileTitlebarAndRedisplay:)]) {
  2838     [frameView _tileTitlebarAndRedisplay:NO];
  2842 // Override methods that translate between content rect and frame rect.
  2843 - (NSRect)contentRectForFrameRect:(NSRect)aRect
  2845   if ([self drawsContentsIntoWindowFrame]) {
  2846     return aRect;
  2848   return [super contentRectForFrameRect:aRect];
  2851 - (NSRect)contentRectForFrameRect:(NSRect)aRect styleMask:(NSUInteger)aMask
  2853   if ([self drawsContentsIntoWindowFrame]) {
  2854     return aRect;
  2856   if ([super respondsToSelector:@selector(contentRectForFrameRect:styleMask:)]) {
  2857     return [super contentRectForFrameRect:aRect styleMask:aMask];
  2858   } else {
  2859     return [NSWindow contentRectForFrameRect:aRect styleMask:aMask];
  2863 - (NSRect)frameRectForContentRect:(NSRect)aRect
  2865   if ([self drawsContentsIntoWindowFrame]) {
  2866     return aRect;
  2868   return [super frameRectForContentRect:aRect];
  2871 - (NSRect)frameRectForContentRect:(NSRect)aRect styleMask:(NSUInteger)aMask
  2873   if ([self drawsContentsIntoWindowFrame]) {
  2874     return aRect;
  2876   if ([super respondsToSelector:@selector(frameRectForContentRect:styleMask:)]) {
  2877     return [super frameRectForContentRect:aRect styleMask:aMask];
  2878   } else {
  2879     return [NSWindow frameRectForContentRect:aRect styleMask:aMask];
  2883 - (void)setContentView:(NSView*)aView
  2885   [super setContentView:aView];
  2887   // Now move the contentView to the bottommost layer so that it's guaranteed
  2888   // to be under the window buttons.
  2889   NSView* frameView = [aView superview];
  2890   [aView removeFromSuperview];
  2891   [frameView addSubview:aView positioned:NSWindowBelow relativeTo:nil];
  2894 - (NSArray*)titlebarControls
  2896   // Return all subviews of the frameView which are not the content view.
  2897   NSView* frameView = [[self contentView] superview];
  2898   NSMutableArray* array = [[[frameView subviews] mutableCopy] autorelease];
  2899   [array removeObject:[self contentView]];
  2900   return array;
  2903 - (BOOL)respondsToSelector:(SEL)aSelector
  2905   // Claim the window doesn't respond to this so that the system
  2906   // doesn't steal keyboard equivalents for it. Bug 613710.
  2907   if (aSelector == @selector(cancelOperation:)) {
  2908     return NO;
  2911   return [super respondsToSelector:aSelector];
  2914 - (void) doCommandBySelector:(SEL)aSelector
  2916   // We override this so that it won't beep if it can't act.
  2917   // We want to control the beeping for missing or disabled
  2918   // commands ourselves.
  2919   [self tryToPerform:aSelector with:nil];
  2922 - (id)accessibilityAttributeValue:(NSString *)attribute
  2924   id retval = [super accessibilityAttributeValue:attribute];
  2926   // The following works around a problem with Text-to-Speech on OS X 10.7.
  2927   // See bug 674612 for more info.
  2928   //
  2929   // When accessibility is off, AXUIElementCopyAttributeValue(), when called
  2930   // on an AXApplication object to get its AXFocusedUIElement attribute,
  2931   // always returns an AXWindow object (the actual browser window -- never a
  2932   // mozAccessible object).  This also happens with accessibility turned on,
  2933   // if no other object in the browser window has yet been focused.  But if
  2934   // the browser window has a title bar (as it currently always does), the
  2935   // AXWindow object will always have four "accessible" children, one of which
  2936   // is an AXStaticText object (the title bar's "title"; the other three are
  2937   // the close, minimize and zoom buttons).  This means that (for complicated
  2938   // reasons, for which see bug 674612) Text-to-Speech on OS X 10.7 will often
  2939   // "speak" the window title, no matter what text is selected, or even if no
  2940   // text at all is selected.  (This always happens when accessibility is off.
  2941   // It doesn't happen in Firefox releases because Apple has (on OS X 10.7)
  2942   // special-cased the handling of apps whose CFBundleIdentifier is
  2943   // org.mozilla.firefox.)
  2944   //
  2945   // We work around this problem by only returning AXChildren that are
  2946   // mozAccessible object or are one of the titlebar's buttons (which
  2947   // instantiate subclasses of NSButtonCell).
  2948   if (nsCocoaFeatures::OnLionOrLater() && [retval isKindOfClass:[NSArray class]] &&
  2949       [attribute isEqualToString:@"AXChildren"]) {
  2950     NSMutableArray *holder = [NSMutableArray arrayWithCapacity:10];
  2951     [holder addObjectsFromArray:(NSArray *)retval];
  2952     NSUInteger count = [holder count];
  2953     for (NSInteger i = count - 1; i >= 0; --i) {
  2954       id item = [holder objectAtIndex:i];
  2955       // Remove anything from holder that isn't one of the titlebar's buttons
  2956       // (which instantiate subclasses of NSButtonCell) or a mozAccessible
  2957       // object (or one of its subclasses).
  2958       if (![item isKindOfClass:[NSButtonCell class]] &&
  2959           ![item respondsToSelector:@selector(hasRepresentedView)]) {
  2960         [holder removeObjectAtIndex:i];
  2963     retval = [NSArray arrayWithArray:holder];
  2966   return retval;
  2969 // If we were built on OS X 10.6 or with the 10.6 SDK and are running on Lion,
  2970 // the OS (specifically -[NSWindow sendEvent:]) won't send NSEventTypeGesture
  2971 // events to -[ChildView magnifyWithEvent:] as it should.  The following code
  2972 // gets around this.  See bug 863841.
  2973 #if !defined( MAC_OS_X_VERSION_10_7 ) || \
  2974     ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 )
  2975 - (void)sendEvent:(NSEvent *)anEvent
  2977   if ([ChildView isLionSmartMagnifyEvent: anEvent]) {
  2978     [[self mainChildView] magnifyWithEvent:anEvent];
  2979     return;
  2981   [super sendEvent:anEvent];
  2983 #endif
  2985 @end
  2987 // This class allows us to exercise control over the window's title bar. This
  2988 // allows for a "unified toolbar" look without having to extend the content
  2989 // area into the title bar. It works like this:
  2990 // 1) We set the window's style to textured.
  2991 // 2) Because of this, the background color applies to the entire window, including
  2992 //     the titlebar area. For normal textured windows, the default pattern is a 
  2993 //    "brushed metal" image on Tiger and a unified gradient on Leopard.
  2994 // 3) We set the background color to a custom NSColor subclass that knows how tall the window is.
  2995 //    When -set is called on it, it sets a pattern (with a draw callback) as the fill. In that callback,
  2996 //    it paints the the titlebar and background colors in the correct areas of the context it's given,
  2997 //    which will fill the entire window (CG will tile it horizontally for us).
  2998 // 4) Whenever the window's main state changes and when [window display] is called,
  2999 //    Cocoa redraws the titlebar using the patternDraw callback function.
  3000 //
  3001 // This class also provides us with a pill button to show/hide the toolbar up to 10.6.
  3002 //
  3003 // Drawing the unified gradient in the titlebar and the toolbar works like this:
  3004 // 1) In the style sheet we set the toolbar's -moz-appearance to toolbar or
  3005 //    -moz-mac-unified-toolbar.
  3006 // 2) When the toolbar is visible and we paint the application chrome
  3007 //    window, the array that Gecko passes nsChildView::UpdateThemeGeometries
  3008 //    will contain an entry for the widget type NS_THEME_TOOLBAR or
  3009 //    NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
  3010 // 3) nsChildView::UpdateThemeGeometries finds the toolbar frame's ToolbarWindow
  3011 //    and passes the toolbar frame's height to setUnifiedToolbarHeight.
  3012 // 4) If the toolbar height has changed, a titlebar redraw is triggered and the
  3013 //    upper part of the unified gradient is drawn in the titlebar.
  3014 // 5) The lower part of the unified gradient in the toolbar is drawn during
  3015 //    normal window content painting in nsNativeThemeCocoa::DrawUnifiedToolbar.
  3016 //
  3017 // Whenever the unified gradient is drawn in the titlebar or the toolbar, both
  3018 // titlebar height and toolbar height must be known in order to construct the
  3019 // correct gradient. But you can only get from the toolbar frame
  3020 // to the containing window - the other direction doesn't work. That's why the
  3021 // toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
  3022 // query the window for its titlebar height when drawing the toolbar.
  3023 //
  3024 // Note that in drawsContentsIntoWindowFrame mode, titlebar drawing works in a
  3025 // completely different way: In that mode, the window's mainChildView will
  3026 // cover the titlebar completely and nothing that happens in the window
  3027 // background will reach the screen.
  3028 @implementation ToolbarWindow
  3030 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
  3032   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  3034   aStyle = aStyle | NSTexturedBackgroundWindowMask;
  3035   if ((self = [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag])) {
  3036     mColor = [[TitlebarAndBackgroundColor alloc] initWithWindow:self];
  3037     // Bypass our guard method below.
  3038     [super setBackgroundColor:mColor];
  3039     mBackgroundColor = [[NSColor whiteColor] retain];
  3041     mUnifiedToolbarHeight = 22.0f;
  3042     mWindowButtonsRect = NSZeroRect;
  3043     mFullScreenButtonRect = NSZeroRect;
  3045     // setBottomCornerRounded: is a private API call, so we check to make sure
  3046     // we respond to it just in case.
  3047     if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
  3048       [self setBottomCornerRounded:nsCocoaFeatures::OnLionOrLater()];
  3050     [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
  3051     [self setContentBorderThickness:0.0f forEdge:NSMaxYEdge];
  3053   return self;
  3055   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  3058 - (void)dealloc
  3060   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3062   [mColor release];
  3063   [mBackgroundColor release];
  3064   [mTitlebarView release];
  3065   [super dealloc];
  3067   NS_OBJC_END_TRY_ABORT_BLOCK;
  3070 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
  3072   [super setTitlebarColor:aColor forActiveWindow:aActive];
  3073   [self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
  3076 - (void)setBackgroundColor:(NSColor*)aColor
  3078   [aColor retain];
  3079   [mBackgroundColor release];
  3080   mBackgroundColor = aColor;
  3083 - (NSColor*)windowBackgroundColor
  3085   return mBackgroundColor;
  3088 - (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect
  3090   [self setTitlebarNeedsDisplayInRect:aRect sync:NO];
  3093 - (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync
  3095   NSRect titlebarRect = [self titlebarRect];
  3096   NSRect rect = NSIntersectionRect(titlebarRect, aRect);
  3097   if (NSIsEmptyRect(rect))
  3098     return;
  3100   NSView* borderView = [[self contentView] superview];
  3101   if (!borderView)
  3102     return;
  3104   if (aSync) {
  3105     [borderView displayRect:rect];
  3106   } else {
  3107     [borderView setNeedsDisplayInRect:rect];
  3111 - (NSRect)titlebarRect
  3113   CGFloat titlebarHeight = [self titlebarHeight];
  3114   return NSMakeRect(0, [self frame].size.height - titlebarHeight,
  3115                     [self frame].size.width, titlebarHeight);
  3118 // Returns the unified height of titlebar + toolbar.
  3119 - (CGFloat)unifiedToolbarHeight
  3121   return mUnifiedToolbarHeight;
  3124 - (CGFloat)titlebarHeight
  3126   // We use the original content rect here, not what we return from
  3127   // [self contentRectForFrameRect:], because that would give us a
  3128   // titlebarHeight of zero in drawsContentsIntoWindowFrame mode.
  3129   NSRect frameRect = [self frame];
  3130   NSRect originalContentRect = [NSWindow contentRectForFrameRect:frameRect styleMask:[self styleMask]];
  3131   return NSMaxY(frameRect) - NSMaxY(originalContentRect);
  3134 // Stores the complete height of titlebar + toolbar.
  3135 - (void)setUnifiedToolbarHeight:(CGFloat)aHeight
  3137   if (aHeight == mUnifiedToolbarHeight)
  3138     return;
  3140   mUnifiedToolbarHeight = aHeight;
  3142   // Update sheet positioning hint
  3143   CGFloat topMargin = mUnifiedToolbarHeight - [self titlebarHeight];
  3144   [self setContentBorderThickness:topMargin forEdge:NSMaxYEdge];
  3146   // Redraw the title bar. If we're inside painting, we'll do it right now,
  3147   // otherwise we'll just invalidate it.
  3148   BOOL needSyncRedraw = ([NSView focusView] != nil);
  3149   [self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw];
  3152 // Extending the content area into the title bar works by resizing the
  3153 // mainChildView so that it covers the titlebar.
  3154 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
  3156   BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState);
  3157   [super setDrawsContentsIntoWindowFrame:aState];
  3158   if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) {
  3159     // Here we extend / shrink our mainChildView. We do that by firing a resize
  3160     // event which will cause the ChildView to be resized to the rect returned
  3161     // by nsCocoaWindow::GetClientBounds. GetClientBounds bases its return
  3162     // value on what we return from drawsContentsIntoWindowFrame.
  3163     WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
  3164     nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
  3165     if (geckoWindow) {
  3166       // Re-layout our contents.
  3167       geckoWindow->ReportSizeEvent();
  3170     // Resizing the content area causes a reflow which would send a synthesized
  3171     // mousemove event to the old mouse position relative to the top left
  3172     // corner of the content area. But the mouse has shifted relative to the
  3173     // content area, so that event would have wrong position information. So
  3174     // we'll send a mouse move event with the correct new position.
  3175     ChildViewMouseTracker::ResendLastMouseMoveEvent();
  3179 - (void)setWantsTitleDrawn:(BOOL)aDrawTitle
  3181   [super setWantsTitleDrawn:aDrawTitle];
  3182   [self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
  3185 - (void)placeWindowButtons:(NSRect)aRect
  3187   if (!NSEqualRects(mWindowButtonsRect, aRect)) {
  3188     mWindowButtonsRect = aRect;
  3189     [self reflowTitlebarElements];
  3193 - (NSPoint)windowButtonsPositionWithDefaultPosition:(NSPoint)aDefaultPosition
  3195   if ([self drawsContentsIntoWindowFrame] && !NSIsEmptyRect(mWindowButtonsRect)) {
  3196     return NSMakePoint(std::max(mWindowButtonsRect.origin.x, aDefaultPosition.x),
  3197                        std::min(mWindowButtonsRect.origin.y, aDefaultPosition.y));
  3199   return aDefaultPosition;
  3202 - (void)placeFullScreenButton:(NSRect)aRect
  3204   if (!NSEqualRects(mFullScreenButtonRect, aRect)) {
  3205     mFullScreenButtonRect = aRect;
  3206     [self reflowTitlebarElements];
  3210 - (NSPoint)fullScreenButtonPositionWithDefaultPosition:(NSPoint)aDefaultPosition;
  3212   if ([self drawsContentsIntoWindowFrame] && !NSIsEmptyRect(mFullScreenButtonRect)) {
  3213     return NSMakePoint(std::min(mFullScreenButtonRect.origin.x, aDefaultPosition.x),
  3214                        std::min(mFullScreenButtonRect.origin.y, aDefaultPosition.y));
  3216   return aDefaultPosition;
  3219 // Returning YES here makes the setShowsToolbarButton method work even though
  3220 // the window doesn't contain an NSToolbar.
  3221 - (BOOL)_hasToolbar
  3223   return YES;
  3226 // Dispatch a toolbar pill button clicked message to Gecko.
  3227 - (void)_toolbarPillButtonClicked:(id)sender
  3229   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  3231   RollUpPopups();
  3233   if ([[self delegate] isKindOfClass:[WindowDelegate class]]) {
  3234     WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
  3235     nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
  3236     if (!geckoWindow)
  3237       return;
  3239     nsIWidgetListener* listener = geckoWindow->GetWidgetListener();
  3240     if (listener)
  3241       listener->OSToolbarButtonPressed();
  3244   NS_OBJC_END_TRY_ABORT_BLOCK;
  3247 // Retain and release "self" to avoid crashes when our widget (and its native
  3248 // window) is closed as a result of processing a key equivalent (e.g.
  3249 // Command+w or Command+q).  This workaround is only needed for a window
  3250 // that can become key.
  3251 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
  3253   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3255   NSWindow *nativeWindow = [self retain];
  3256   BOOL retval = [super performKeyEquivalent:theEvent];
  3257   [nativeWindow release];
  3258   return retval;
  3260   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  3263 - (void)sendEvent:(NSEvent *)anEvent
  3265   NSEventType type = [anEvent type];
  3267   switch (type) {
  3268     case NSScrollWheel:
  3269     case NSLeftMouseDown:
  3270     case NSLeftMouseUp:
  3271     case NSRightMouseDown:
  3272     case NSRightMouseUp:
  3273     case NSOtherMouseDown:
  3274     case NSOtherMouseUp:
  3275     case NSMouseMoved:
  3276     case NSLeftMouseDragged:
  3277     case NSRightMouseDragged:
  3278     case NSOtherMouseDragged:
  3280       // Drop all mouse events if a modal window has appeared above us.
  3281       // This helps make us behave as if the OS were running a "real" modal
  3282       // event loop.
  3283       id delegate = [self delegate];
  3284       if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
  3285         nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
  3286         if (widget) {
  3287           if (type == NSMouseMoved) {
  3288             [[self mainChildView] updateWindowDraggableStateOnMouseMove:anEvent];
  3290           if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
  3291             return;
  3292           if (widget->HasModalDescendents())
  3293             return;
  3296       break;
  3298     default:
  3299       break;
  3302   [super sendEvent:anEvent];
  3305 @end
  3307 // Custom NSColor subclass where most of the work takes place for drawing in
  3308 // the titlebar area. Not used in drawsContentsIntoWindowFrame mode.
  3309 @implementation TitlebarAndBackgroundColor
  3311 - (id)initWithWindow:(ToolbarWindow*)aWindow
  3313   if ((self = [super init])) {
  3314     mWindow = aWindow; // weak ref to avoid a cycle
  3316   return self;
  3319 static void
  3320 DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect,
  3321                    CGFloat aUnifiedToolbarHeight, BOOL aIsMain)
  3323   if (aTitlebarRect.size.width * aTitlebarRect.size.height > CUIDRAW_MAX_AREA) {
  3324     return;
  3327   CUIDraw([NSWindow coreUIRenderer], aTitlebarRect, aContext,
  3328           (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
  3329             @"kCUIWidgetWindowFrame", @"widget",
  3330             @"regularwin", @"windowtype",
  3331             (aIsMain ? @"normal" : @"inactive"), @"state",
  3332             [NSNumber numberWithDouble:aUnifiedToolbarHeight], @"kCUIWindowFrameUnifiedTitleBarHeightKey",
  3333             [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawTitleSeparatorKey",
  3334             nil],
  3335           nil);
  3337   if (nsCocoaFeatures::OnLionOrLater()) {
  3338     // On Lion the call to CUIDraw doesn't draw the top pixel strip at some
  3339     // window widths. We don't want to have a flickering transparent line, so
  3340     // we overdraw it.
  3341     CGContextSetRGBFillColor(aContext, 0.95, 0.95, 0.95, 1);
  3342     CGContextFillRect(aContext, CGRectMake(0, CGRectGetMaxY(aTitlebarRect) - 1,
  3343                                            aTitlebarRect.size.width, 1));
  3347 // Pattern draw callback for standard titlebar gradients and solid titlebar colors
  3348 static void
  3349 TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
  3351   ToolbarWindow *window = (ToolbarWindow*)aInfo;
  3352   if (![window drawsContentsIntoWindowFrame]) {
  3353     NSRect titlebarRect = [window titlebarRect];
  3354     BOOL isMain = [window isMainWindow];
  3355     NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain];
  3356     if (!titlebarColor) {
  3357       // If the titlebar color is nil, draw the default titlebar shading.
  3358       DrawNativeTitlebar(aContext, NSRectToCGRect(titlebarRect),
  3359                          [window unifiedToolbarHeight], isMain);
  3360     } else {
  3361       // If the titlebar color is not nil, just set and draw it normally.
  3362       [NSGraphicsContext saveGraphicsState];
  3363       [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:aContext flipped:NO]];
  3364       [titlebarColor set];
  3365       NSRectFill(titlebarRect);
  3366       [NSGraphicsContext restoreGraphicsState];
  3371 - (void)setFill
  3373   float patternWidth = [mWindow frame].size.width;
  3375   CGPatternCallbacks callbacks = {0, &TitlebarDrawCallback, NULL};
  3376   CGPatternRef pattern = CGPatternCreate(mWindow, CGRectMake(0.0f, 0.0f, patternWidth, [mWindow frame].size.height), 
  3377                                          CGAffineTransformIdentity, patternWidth, [mWindow frame].size.height,
  3378                                          kCGPatternTilingConstantSpacing, true, &callbacks);
  3380   // Set the pattern as the fill, which is what we were asked to do. All our
  3381   // drawing will take place in the patternDraw callback.
  3382   CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
  3383   CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
  3384   CGContextSetFillColorSpace(context, patternSpace);
  3385   CGColorSpaceRelease(patternSpace);
  3386   CGFloat component = 1.0f;
  3387   CGContextSetFillPattern(context, pattern, &component);
  3388   CGPatternRelease(pattern);
  3391 - (void)set
  3393   [self setFill];
  3396 - (NSString*)colorSpaceName
  3398   return NSDeviceRGBColorSpace;
  3401 @end
  3403 @implementation PopupWindow
  3405 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
  3406       backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
  3408   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  3410   mIsContextMenu = false;
  3411   return [super initWithContentRect:contentRect styleMask:styleMask
  3412           backing:bufferingType defer:deferCreation];
  3414   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  3417 - (BOOL)isContextMenu
  3419   return mIsContextMenu;
  3422 - (void)setIsContextMenu:(BOOL)flag
  3424   mIsContextMenu = flag;
  3427 - (BOOL)canBecomeMainWindow
  3429   // This is overriden because the default is 'yes' when a titlebar is present.
  3430   return NO;
  3433 @end
  3435 // According to Apple's docs on [NSWindow canBecomeKeyWindow] and [NSWindow
  3436 // canBecomeMainWindow], windows without a title bar or resize bar can't (by
  3437 // default) become key or main.  But if a window can't become key, it can't
  3438 // accept keyboard input (bmo bug 393250).  And it should also be possible for
  3439 // an otherwise "ordinary" window to become main.  We need to override these
  3440 // two methods to make this happen.
  3441 @implementation BorderlessWindow
  3443 - (BOOL)canBecomeKeyWindow
  3445   return YES;
  3448 - (void)sendEvent:(NSEvent *)anEvent
  3450   NSEventType type = [anEvent type];
  3452   switch (type) {
  3453     case NSScrollWheel:
  3454     case NSLeftMouseDown:
  3455     case NSLeftMouseUp:
  3456     case NSRightMouseDown:
  3457     case NSRightMouseUp:
  3458     case NSOtherMouseDown:
  3459     case NSOtherMouseUp:
  3460     case NSMouseMoved:
  3461     case NSLeftMouseDragged:
  3462     case NSRightMouseDragged:
  3463     case NSOtherMouseDragged:
  3465       // Drop all mouse events if a modal window has appeared above us.
  3466       // This helps make us behave as if the OS were running a "real" modal
  3467       // event loop.
  3468       id delegate = [self delegate];
  3469       if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
  3470         nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
  3471         if (widget) {
  3472           if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
  3473             return;
  3474           if (widget->HasModalDescendents())
  3475             return;
  3478       break;
  3480     default:
  3481       break;
  3484   [super sendEvent:anEvent];
  3487 // Apple's doc on this method says that the NSWindow class's default is not to
  3488 // become main if the window isn't "visible" -- so we should replicate that
  3489 // behavior here.  As best I can tell, the [NSWindow isVisible] method is an
  3490 // accurate test of what Apple means by "visibility".
  3491 - (BOOL)canBecomeMainWindow
  3493   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3495   if (![self isVisible])
  3496     return NO;
  3497   return YES;
  3499   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  3502 // Retain and release "self" to avoid crashes when our widget (and its native
  3503 // window) is closed as a result of processing a key equivalent (e.g.
  3504 // Command+w or Command+q).  This workaround is only needed for a window
  3505 // that can become key.
  3506 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
  3508   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  3510   NSWindow *nativeWindow = [self retain];
  3511   BOOL retval = [super performKeyEquivalent:theEvent];
  3512   [nativeWindow release];
  3513   return retval;
  3515   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  3518 @end

mercurial