widget/cocoa/nsCocoaWindow.mm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/cocoa/nsCocoaWindow.mm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3518 @@
     1.4 +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsCocoaWindow.h"
    1.10 +
    1.11 +#include "NativeKeyBindings.h"
    1.12 +#include "TextInputHandler.h"
    1.13 +#include "nsObjCExceptions.h"
    1.14 +#include "nsCOMPtr.h"
    1.15 +#include "nsWidgetsCID.h"
    1.16 +#include "nsIRollupListener.h"
    1.17 +#include "nsChildView.h"
    1.18 +#include "nsWindowMap.h"
    1.19 +#include "nsAppShell.h"
    1.20 +#include "nsIAppShellService.h"
    1.21 +#include "nsIBaseWindow.h"
    1.22 +#include "nsIInterfaceRequestorUtils.h"
    1.23 +#include "nsIXULWindow.h"
    1.24 +#include "nsToolkit.h"
    1.25 +#include "nsIDOMWindow.h"
    1.26 +#include "nsPIDOMWindow.h"
    1.27 +#include "nsIDOMElement.h"
    1.28 +#include "nsThreadUtils.h"
    1.29 +#include "nsMenuBarX.h"
    1.30 +#include "nsMenuUtilsX.h"
    1.31 +#include "nsStyleConsts.h"
    1.32 +#include "nsNativeThemeColors.h"
    1.33 +#include "nsChildView.h"
    1.34 +#include "nsCocoaFeatures.h"
    1.35 +#include "nsIScreenManager.h"
    1.36 +#include "nsIWidgetListener.h"
    1.37 +#include "nsIPresShell.h"
    1.38 +
    1.39 +#include "gfxPlatform.h"
    1.40 +#include "qcms.h"
    1.41 +
    1.42 +#include "mozilla/AutoRestore.h"
    1.43 +#include "mozilla/BasicEvents.h"
    1.44 +#include "mozilla/Preferences.h"
    1.45 +#include <algorithm>
    1.46 +
    1.47 +namespace mozilla {
    1.48 +namespace layers {
    1.49 +class LayerManager;
    1.50 +}
    1.51 +}
    1.52 +using namespace mozilla::layers;
    1.53 +using namespace mozilla::widget;
    1.54 +using namespace mozilla;
    1.55 +
    1.56 +// defined in nsAppShell.mm
    1.57 +extern nsCocoaAppModalWindowList *gCocoaAppModalWindowList;
    1.58 +
    1.59 +int32_t gXULModalLevel = 0;
    1.60 +
    1.61 +// In principle there should be only one app-modal window at any given time.
    1.62 +// But sometimes, despite our best efforts, another window appears above the
    1.63 +// current app-modal window.  So we need to keep a linked list of app-modal
    1.64 +// windows.  (A non-sheet window that appears above an app-modal window is
    1.65 +// also made app-modal.)  See nsCocoaWindow::SetModal().
    1.66 +nsCocoaWindowList *gGeckoAppModalWindowList = NULL;
    1.67 +
    1.68 +// defined in nsMenuBarX.mm
    1.69 +extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
    1.70 +
    1.71 +// defined in nsChildView.mm
    1.72 +extern BOOL                gSomeMenuBarPainted;
    1.73 +
    1.74 +extern "C" {
    1.75 +  // CGSPrivate.h
    1.76 +  typedef NSInteger CGSConnection;
    1.77 +  typedef NSInteger CGSWindow;
    1.78 +  typedef NSUInteger CGSWindowFilterRef;
    1.79 +  extern CGSConnection _CGSDefaultConnection(void);
    1.80 +  extern CGError CGSSetWindowShadowAndRimParameters(const CGSConnection cid, CGSWindow wid, float standardDeviation, float density, int offsetX, int offsetY, unsigned int flags);
    1.81 +  extern CGError CGSSetWindowBackgroundBlurRadius(CGSConnection cid, CGSWindow wid, NSUInteger blur);
    1.82 +}
    1.83 +
    1.84 +#define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
    1.85 +
    1.86 +NS_IMPL_ISUPPORTS_INHERITED(nsCocoaWindow, Inherited, nsPIWidgetCocoa)
    1.87 +
    1.88 +// A note on testing to see if your object is a sheet...
    1.89 +// |mWindowType == eWindowType_sheet| is true if your gecko nsIWidget is a sheet
    1.90 +// widget - whether or not the sheet is showing. |[mWindow isSheet]| will return
    1.91 +// true *only when the sheet is actually showing*. Choose your test wisely.
    1.92 +
    1.93 +static void RollUpPopups()
    1.94 +{
    1.95 +  nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
    1.96 +  NS_ENSURE_TRUE_VOID(rollupListener);
    1.97 +  nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
    1.98 +  if (!rollupWidget)
    1.99 +    return;
   1.100 +  rollupListener->Rollup(0, nullptr, nullptr);
   1.101 +}
   1.102 +
   1.103 +nsCocoaWindow::nsCocoaWindow()
   1.104 +: mParent(nullptr)
   1.105 +, mWindow(nil)
   1.106 +, mDelegate(nil)
   1.107 +, mSheetWindowParent(nil)
   1.108 +, mPopupContentView(nil)
   1.109 +, mShadowStyle(NS_STYLE_WINDOW_SHADOW_DEFAULT)
   1.110 +, mBackingScaleFactor(0.0)
   1.111 +, mAnimationType(nsIWidget::eGenericWindowAnimation)
   1.112 +, mWindowMadeHere(false)
   1.113 +, mSheetNeedsShow(false)
   1.114 +, mFullScreen(false)
   1.115 +, mInFullScreenTransition(false)
   1.116 +, mModal(false)
   1.117 +, mUsesNativeFullScreen(false)
   1.118 +, mIsAnimationSuppressed(false)
   1.119 +, mInReportMoveEvent(false)
   1.120 +, mInResize(false)
   1.121 +, mNumModalDescendents(0)
   1.122 +{
   1.123 +
   1.124 +}
   1.125 +
   1.126 +void nsCocoaWindow::DestroyNativeWindow()
   1.127 +{
   1.128 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   1.129 +
   1.130 +  if (!mWindow)
   1.131 +    return;
   1.132 +
   1.133 +  // We want to unhook the delegate here because we don't want events
   1.134 +  // sent to it after this object has been destroyed.
   1.135 +  [mWindow setDelegate:nil];
   1.136 +  [mWindow close];
   1.137 +  mWindow = nil;
   1.138 +  [mDelegate autorelease];
   1.139 +
   1.140 +  NS_OBJC_END_TRY_ABORT_BLOCK;
   1.141 +}
   1.142 +
   1.143 +nsCocoaWindow::~nsCocoaWindow()
   1.144 +{
   1.145 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   1.146 +
   1.147 +  // Notify the children that we're gone.  Popup windows (e.g. tooltips) can
   1.148 +  // have nsChildView children.  'kid' is an nsChildView object if and only if
   1.149 +  // its 'type' is 'eWindowType_child' or 'eWindowType_plugin'.
   1.150 +  // childView->ResetParent() can change our list of children while it's
   1.151 +  // being iterated, so the way we iterate the list must allow for this.
   1.152 +  for (nsIWidget* kid = mLastChild; kid;) {
   1.153 +    nsWindowType kidType = kid->WindowType();
   1.154 +    if (kidType == eWindowType_child || kidType == eWindowType_plugin) {
   1.155 +      nsChildView* childView = static_cast<nsChildView*>(kid);
   1.156 +      kid = kid->GetPrevSibling();
   1.157 +      childView->ResetParent();
   1.158 +    } else {
   1.159 +      nsCocoaWindow* childWindow = static_cast<nsCocoaWindow*>(kid);
   1.160 +      childWindow->mParent = nullptr;
   1.161 +      kid = kid->GetPrevSibling();
   1.162 +    }
   1.163 +  }
   1.164 +
   1.165 +  if (mWindow && mWindowMadeHere) {
   1.166 +    DestroyNativeWindow();
   1.167 +  }
   1.168 +
   1.169 +  NS_IF_RELEASE(mPopupContentView);
   1.170 +
   1.171 +  // Deal with the possiblity that we're being destroyed while running modal.
   1.172 +  if (mModal) {
   1.173 +    NS_WARNING("Widget destroyed while running modal!");
   1.174 +    --gXULModalLevel;
   1.175 +    NS_ASSERTION(gXULModalLevel >= 0, "Wierdness setting modality!");
   1.176 +  }
   1.177 +
   1.178 +  NS_OBJC_END_TRY_ABORT_BLOCK;
   1.179 +}
   1.180 +
   1.181 +// Find the screen that overlaps aRect the most,
   1.182 +// if none are found default to the mainScreen.
   1.183 +static NSScreen *FindTargetScreenForRect(const nsIntRect& aRect)
   1.184 +{
   1.185 +  NSScreen *targetScreen = [NSScreen mainScreen];
   1.186 +  NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator];
   1.187 +  int largestIntersectArea = 0;
   1.188 +  while (NSScreen *screen = [screenEnum nextObject]) {
   1.189 +    nsIntRect screenRect(nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame]));
   1.190 +    screenRect = screenRect.Intersect(aRect);
   1.191 +    int area = screenRect.width * screenRect.height;
   1.192 +    if (area > largestIntersectArea) {
   1.193 +      largestIntersectArea = area;
   1.194 +      targetScreen = screen;
   1.195 +    }
   1.196 +  }
   1.197 +  return targetScreen;
   1.198 +}
   1.199 +
   1.200 +// fits the rect to the screen that contains the largest area of it,
   1.201 +// or to aScreen if a screen is passed in
   1.202 +// NB: this operates with aRect in global display pixels
   1.203 +static void FitRectToVisibleAreaForScreen(nsIntRect &aRect, NSScreen *aScreen,
   1.204 +                                          bool aUsesNativeFullScreen)
   1.205 +{
   1.206 +  if (!aScreen) {
   1.207 +    aScreen = FindTargetScreenForRect(aRect);
   1.208 +  }
   1.209 +
   1.210 +  nsIntRect screenBounds(nsCocoaUtils::CocoaRectToGeckoRect([aScreen visibleFrame]));
   1.211 +
   1.212 +  if (aRect.width > screenBounds.width) {
   1.213 +    aRect.width = screenBounds.width;
   1.214 +  }
   1.215 +  if (aRect.height > screenBounds.height) {
   1.216 +    aRect.height = screenBounds.height;
   1.217 +  }
   1.218 +  
   1.219 +  if (aRect.x - screenBounds.x + aRect.width > screenBounds.width) {
   1.220 +    aRect.x += screenBounds.width - (aRect.x - screenBounds.x + aRect.width);
   1.221 +  }
   1.222 +  if (aRect.y - screenBounds.y + aRect.height > screenBounds.height) {
   1.223 +    aRect.y += screenBounds.height - (aRect.y - screenBounds.y + aRect.height);
   1.224 +  }
   1.225 +
   1.226 +  // If the left/top edge of the window is off the screen in either direction,
   1.227 +  // then set the window to start at the left/top edge of the screen.
   1.228 +  if (aRect.x < screenBounds.x || aRect.x > (screenBounds.x + screenBounds.width)) {
   1.229 +    aRect.x = screenBounds.x;
   1.230 +  }
   1.231 +  if (aRect.y < screenBounds.y || aRect.y > (screenBounds.y + screenBounds.height)) {
   1.232 +    aRect.y = screenBounds.y;
   1.233 +  }
   1.234 +
   1.235 +  // If aRect is filling the screen and the window supports native (Lion-style)
   1.236 +  // fullscreen mode, reduce aRect's height and shift it down by 22 pixels
   1.237 +  // (nominally the height of the menu bar or of a window's title bar).  For
   1.238 +  // some reason this works around bug 740923.  Yes, it's a bodacious hack.
   1.239 +  // But until we know more it will have to do.
   1.240 +  if (aUsesNativeFullScreen && aRect.y == 0 && aRect.height == screenBounds.height) {
   1.241 +    aRect.y = 22;
   1.242 +    aRect.height -= 22;
   1.243 +  }
   1.244 +}
   1.245 +
   1.246 +// Some applications like Camino use native popup windows
   1.247 +// (native context menus, native tooltips)
   1.248 +static bool UseNativePopupWindows()
   1.249 +{
   1.250 +#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
   1.251 +  return true;
   1.252 +#else
   1.253 +  return false;
   1.254 +#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
   1.255 +}
   1.256 +
   1.257 +// aRect here is specified in global display pixels
   1.258 +nsresult nsCocoaWindow::Create(nsIWidget *aParent,
   1.259 +                               nsNativeWidget aNativeParent,
   1.260 +                               const nsIntRect &aRect,
   1.261 +                               nsDeviceContext *aContext,
   1.262 +                               nsWidgetInitData *aInitData)
   1.263 +{
   1.264 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   1.265 +
   1.266 +  // Because the hidden window is created outside of an event loop,
   1.267 +  // we have to provide an autorelease pool (see bug 559075).
   1.268 +  nsAutoreleasePool localPool;
   1.269 +
   1.270 +  nsIntRect newBounds = aRect;
   1.271 +  FitRectToVisibleAreaForScreen(newBounds, nullptr, mUsesNativeFullScreen);
   1.272 +
   1.273 +  // Set defaults which can be overriden from aInitData in BaseCreate
   1.274 +  mWindowType = eWindowType_toplevel;
   1.275 +  mBorderStyle = eBorderStyle_default;
   1.276 +
   1.277 +  // Ensure that the toolkit is created.
   1.278 +  nsToolkit::GetToolkit();
   1.279 +
   1.280 +  // newBounds is still display (global screen) pixels at this point;
   1.281 +  // fortunately, BaseCreate doesn't actually use it so we don't
   1.282 +  // need to worry about trying to convert it to device pixels
   1.283 +  // when we don't have a window (or dev context, perhaps) yet
   1.284 +  Inherited::BaseCreate(aParent, newBounds, aContext, aInitData);
   1.285 +
   1.286 +  mParent = aParent;
   1.287 +
   1.288 +  // Applications that use native popups don't want us to create popup windows.
   1.289 +  if ((mWindowType == eWindowType_popup) && UseNativePopupWindows())
   1.290 +    return NS_OK;
   1.291 +
   1.292 +  nsresult rv =
   1.293 +    CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds),
   1.294 +                       mBorderStyle, false);
   1.295 +  NS_ENSURE_SUCCESS(rv, rv);
   1.296 +
   1.297 +  if (mWindowType == eWindowType_popup) {
   1.298 +    if (aInitData->mMouseTransparent) {
   1.299 +      [mWindow setIgnoresMouseEvents:YES];
   1.300 +    }
   1.301 +    // now we can convert newBounds to device pixels for the window we created,
   1.302 +    // as the child view expects a rect expressed in the dev pix of its parent
   1.303 +    double scale = BackingScaleFactor();
   1.304 +    newBounds.x *= scale;
   1.305 +    newBounds.y *= scale;
   1.306 +    newBounds.width *= scale;
   1.307 +    newBounds.height *= scale;
   1.308 +    return CreatePopupContentView(newBounds, aContext);
   1.309 +  }
   1.310 +
   1.311 +  mIsAnimationSuppressed = aInitData->mIsAnimationSuppressed;
   1.312 +
   1.313 +  return NS_OK;
   1.314 +
   1.315 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   1.316 +}
   1.317 +
   1.318 +static unsigned int WindowMaskForBorderStyle(nsBorderStyle aBorderStyle)
   1.319 +{
   1.320 +  bool allOrDefault = (aBorderStyle == eBorderStyle_all ||
   1.321 +                         aBorderStyle == eBorderStyle_default);
   1.322 +
   1.323 +  /* Apple's docs on NSWindow styles say that "a window's style mask should
   1.324 +   * include NSTitledWindowMask if it includes any of the others [besides
   1.325 +   * NSBorderlessWindowMask]".  This implies that a borderless window
   1.326 +   * shouldn't have any other styles than NSBorderlessWindowMask.
   1.327 +   */
   1.328 +  if (!allOrDefault && !(aBorderStyle & eBorderStyle_title))
   1.329 +    return NSBorderlessWindowMask;
   1.330 +
   1.331 +  unsigned int mask = NSTitledWindowMask;
   1.332 +  if (allOrDefault || aBorderStyle & eBorderStyle_close)
   1.333 +    mask |= NSClosableWindowMask;
   1.334 +  if (allOrDefault || aBorderStyle & eBorderStyle_minimize)
   1.335 +    mask |= NSMiniaturizableWindowMask;
   1.336 +  if (allOrDefault || aBorderStyle & eBorderStyle_resizeh)
   1.337 +    mask |= NSResizableWindowMask;
   1.338 +
   1.339 +  return mask;
   1.340 +}
   1.341 +
   1.342 +NS_IMETHODIMP nsCocoaWindow::ReparentNativeWidget(nsIWidget* aNewParent)
   1.343 +{
   1.344 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.345 +}
   1.346 +
   1.347 +// If aRectIsFrameRect, aRect specifies the frame rect of the new window.
   1.348 +// Otherwise, aRect.x/y specify the position of the window's frame relative to
   1.349 +// the bottom of the menubar and aRect.width/height specify the size of the
   1.350 +// content rect.
   1.351 +nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect,
   1.352 +                                           nsBorderStyle aBorderStyle,
   1.353 +                                           bool aRectIsFrameRect)
   1.354 +{
   1.355 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   1.356 +
   1.357 +  // We default to NSBorderlessWindowMask, add features if needed.
   1.358 +  unsigned int features = NSBorderlessWindowMask;
   1.359 +
   1.360 +  // Configure the window we will create based on the window type.
   1.361 +  switch (mWindowType)
   1.362 +  {
   1.363 +    case eWindowType_invisible:
   1.364 +    case eWindowType_child:
   1.365 +    case eWindowType_plugin:
   1.366 +      break;
   1.367 +    case eWindowType_popup:
   1.368 +      if (aBorderStyle != eBorderStyle_default && mBorderStyle & eBorderStyle_title) {
   1.369 +        features |= NSTitledWindowMask;
   1.370 +        if (aBorderStyle & eBorderStyle_close) {
   1.371 +          features |= NSClosableWindowMask;
   1.372 +        }
   1.373 +      }
   1.374 +      break;
   1.375 +    case eWindowType_toplevel:
   1.376 +    case eWindowType_dialog:
   1.377 +      features = WindowMaskForBorderStyle(aBorderStyle);
   1.378 +      break;
   1.379 +    case eWindowType_sheet:
   1.380 +      if (mParent->WindowType() != eWindowType_invisible &&
   1.381 +          aBorderStyle & eBorderStyle_resizeh) {
   1.382 +        features = NSResizableWindowMask;
   1.383 +      }
   1.384 +      else {
   1.385 +        features = NSMiniaturizableWindowMask;
   1.386 +      }
   1.387 +      features |= NSTitledWindowMask;
   1.388 +      break;
   1.389 +    default:
   1.390 +      NS_ERROR("Unhandled window type!");
   1.391 +      return NS_ERROR_FAILURE;
   1.392 +  }
   1.393 +
   1.394 +  NSRect contentRect;
   1.395 +
   1.396 +  if (aRectIsFrameRect) {
   1.397 +    contentRect = [NSWindow contentRectForFrameRect:aRect styleMask:features];
   1.398 +  } else {
   1.399 +    /* 
   1.400 +     * We pass a content area rect to initialize the native Cocoa window. The
   1.401 +     * content rect we give is the same size as the size we're given by gecko.
   1.402 +     * The origin we're given for non-popup windows is moved down by the height
   1.403 +     * of the menu bar so that an origin of (0,100) from gecko puts the window
   1.404 +     * 100 pixels below the top of the available desktop area. We also move the
   1.405 +     * origin down by the height of a title bar if it exists. This is so the
   1.406 +     * origin that gecko gives us for the top-left of  the window turns out to
   1.407 +     * be the top-left of the window we create. This is how it was done in
   1.408 +     * Carbon. If it ought to be different we'll probably need to look at all
   1.409 +     * the callers.
   1.410 +     *
   1.411 +     * Note: This means that if you put a secondary screen on top of your main
   1.412 +     * screen and open a window in the top screen, it'll be incorrectly shifted
   1.413 +     * down by the height of the menu bar. Same thing would happen in Carbon.
   1.414 +     *
   1.415 +     * Note: If you pass a rect with 0,0 for an origin, the window ends up in a
   1.416 +     * weird place for some reason. This stops that without breaking popups.
   1.417 +     */
   1.418 +    // Compensate for difference between frame and content area height (e.g. title bar).
   1.419 +    NSRect newWindowFrame = [NSWindow frameRectForContentRect:aRect styleMask:features];
   1.420 +
   1.421 +    contentRect = aRect;
   1.422 +    contentRect.origin.y -= (newWindowFrame.size.height - aRect.size.height);
   1.423 +
   1.424 +    if (mWindowType != eWindowType_popup)
   1.425 +      contentRect.origin.y -= [[NSApp mainMenu] menuBarHeight];
   1.426 +  }
   1.427 +
   1.428 +  // NSLog(@"Top-level window being created at Cocoa rect: %f, %f, %f, %f\n",
   1.429 +  //       rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
   1.430 +
   1.431 +  Class windowClass = [BaseWindow class];
   1.432 +  // If we have a titlebar on a top-level window, we want to be able to control the 
   1.433 +  // titlebar color (for unified windows), so use the special ToolbarWindow class. 
   1.434 +  // Note that we need to check the window type because we mark sheets as 
   1.435 +  // having titlebars.
   1.436 +  if ((mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) &&
   1.437 +      (features & NSTitledWindowMask))
   1.438 +    windowClass = [ToolbarWindow class];
   1.439 +  // If we're a popup window we need to use the PopupWindow class.
   1.440 +  else if (mWindowType == eWindowType_popup)
   1.441 +    windowClass = [PopupWindow class];
   1.442 +  // If we're a non-popup borderless window we need to use the
   1.443 +  // BorderlessWindow class.
   1.444 +  else if (features == NSBorderlessWindowMask)
   1.445 +    windowClass = [BorderlessWindow class];
   1.446 +
   1.447 +  // Create the window
   1.448 +  mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features 
   1.449 +                                 backing:NSBackingStoreBuffered defer:YES];
   1.450 +
   1.451 +  // setup our notification delegate. Note that setDelegate: does NOT retain.
   1.452 +  mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
   1.453 +  [mWindow setDelegate:mDelegate];
   1.454 +
   1.455 +  // Make sure that the content rect we gave has been honored.
   1.456 +  NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
   1.457 +  if (!NSEqualRects([mWindow frame], wantedFrame)) {
   1.458 +    // This can happen when the window is not on the primary screen.
   1.459 +    [mWindow setFrame:wantedFrame display:NO];
   1.460 +  }
   1.461 +  UpdateBounds();
   1.462 +
   1.463 +  if (mWindowType == eWindowType_invisible) {
   1.464 +    [mWindow setLevel:kCGDesktopWindowLevelKey];
   1.465 +  } else if (mWindowType == eWindowType_popup) {
   1.466 +    SetPopupWindowLevel();
   1.467 +    [mWindow setHasShadow:YES];
   1.468 +  }
   1.469 +
   1.470 +  [mWindow setBackgroundColor:[NSColor clearColor]];
   1.471 +#ifdef MOZ_B2G
   1.472 +  // In B2G, we don't create popups and we need OMTC to work (because out of
   1.473 +  // process compositing depends on it). Therefore, we don't need our windows
   1.474 +  // to be transparent.
   1.475 +  [mWindow setOpaque:YES];
   1.476 +#else
   1.477 +  [mWindow setOpaque:NO];
   1.478 +#endif
   1.479 +  [mWindow setContentMinSize:NSMakeSize(60, 60)];
   1.480 +  [mWindow disableCursorRects];
   1.481 +
   1.482 +  // Make sure the window starts out not draggable by the background.
   1.483 +  // We will turn it on as necessary.
   1.484 +  [mWindow setMovableByWindowBackground:NO];
   1.485 +
   1.486 +  [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
   1.487 +  mWindowMadeHere = true;
   1.488 +
   1.489 +  return NS_OK;
   1.490 +
   1.491 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   1.492 +}
   1.493 +
   1.494 +NS_IMETHODIMP nsCocoaWindow::CreatePopupContentView(const nsIntRect &aRect,
   1.495 +                             nsDeviceContext *aContext)
   1.496 +{
   1.497 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   1.498 +
   1.499 +  // We need to make our content view a ChildView.
   1.500 +  mPopupContentView = new nsChildView();
   1.501 +  if (!mPopupContentView)
   1.502 +    return NS_ERROR_FAILURE;
   1.503 +
   1.504 +  NS_ADDREF(mPopupContentView);
   1.505 +
   1.506 +  nsIWidget* thisAsWidget = static_cast<nsIWidget*>(this);
   1.507 +  mPopupContentView->Create(thisAsWidget, nullptr, aRect, aContext, nullptr);
   1.508 +
   1.509 +  ChildView* newContentView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET);
   1.510 +  [mWindow setContentView:newContentView];
   1.511 +
   1.512 +  return NS_OK;
   1.513 +
   1.514 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   1.515 +}
   1.516 +
   1.517 +NS_IMETHODIMP nsCocoaWindow::Destroy()
   1.518 +{
   1.519 +  // If we don't hide here we run into problems with panels, this is not ideal.
   1.520 +  // (Bug 891424)
   1.521 +  Show(false);
   1.522 +
   1.523 +  if (mPopupContentView)
   1.524 +    mPopupContentView->Destroy();
   1.525 +
   1.526 +  nsBaseWidget::Destroy();
   1.527 +  // nsBaseWidget::Destroy() calls GetParent()->RemoveChild(this). But we
   1.528 +  // don't implement GetParent(), so we need to do the equivalent here.
   1.529 +  if (mParent) {
   1.530 +    mParent->RemoveChild(this);
   1.531 +  }
   1.532 +  nsBaseWidget::OnDestroy();
   1.533 +
   1.534 +  if (mFullScreen) {
   1.535 +    // On Lion we don't have to mess with the OS chrome when in Full Screen
   1.536 +    // mode.  But we do have to destroy the native window here (and not wait
   1.537 +    // for that to happen in our destructor).  We don't switch away from the
   1.538 +    // native window's space until the window is destroyed, and otherwise this
   1.539 +    // might not happen for several seconds (because at least one object
   1.540 +    // holding a reference to ourselves is usually waiting to be garbage-
   1.541 +    // collected).  See bug 757618.
   1.542 +    if (mUsesNativeFullScreen) {
   1.543 +      DestroyNativeWindow();
   1.544 +    } else if (mWindow) {
   1.545 +      nsCocoaUtils::HideOSChromeOnScreen(false, [mWindow screen]);
   1.546 +    }
   1.547 +  }
   1.548 +
   1.549 +  return NS_OK;
   1.550 +}
   1.551 +
   1.552 +nsIWidget* nsCocoaWindow::GetSheetWindowParent(void)
   1.553 +{
   1.554 +  if (mWindowType != eWindowType_sheet)
   1.555 +    return nullptr;
   1.556 +  nsCocoaWindow *parent = static_cast<nsCocoaWindow*>(mParent);
   1.557 +  while (parent && (parent->mWindowType == eWindowType_sheet))
   1.558 +    parent = static_cast<nsCocoaWindow*>(parent->mParent);
   1.559 +  return parent;
   1.560 +}
   1.561 +
   1.562 +void* nsCocoaWindow::GetNativeData(uint32_t aDataType)
   1.563 +{
   1.564 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
   1.565 +
   1.566 +  void* retVal = nullptr;
   1.567 +  
   1.568 +  switch (aDataType) {
   1.569 +    // to emulate how windows works, we always have to return a NSView
   1.570 +    // for NS_NATIVE_WIDGET
   1.571 +    case NS_NATIVE_WIDGET:
   1.572 +    case NS_NATIVE_DISPLAY:
   1.573 +      retVal = [mWindow contentView];
   1.574 +      break;
   1.575 +      
   1.576 +    case NS_NATIVE_WINDOW:
   1.577 +      retVal = mWindow;
   1.578 +      break;
   1.579 +      
   1.580 +    case NS_NATIVE_GRAPHIC:
   1.581 +      // There isn't anything that makes sense to return here,
   1.582 +      // and it doesn't matter so just return nullptr.
   1.583 +      NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a top-level window!");
   1.584 +      break;
   1.585 +  }
   1.586 +
   1.587 +  return retVal;
   1.588 +
   1.589 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
   1.590 +}
   1.591 +
   1.592 +bool nsCocoaWindow::IsVisible() const
   1.593 +{
   1.594 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   1.595 +
   1.596 +  return (mWindow && ([mWindow isVisibleOrBeingShown] || mSheetNeedsShow));
   1.597 +
   1.598 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
   1.599 +}
   1.600 +
   1.601 +NS_IMETHODIMP nsCocoaWindow::SetModal(bool aState)
   1.602 +{
   1.603 +  if (!mWindow)
   1.604 +    return NS_OK;
   1.605 +
   1.606 +  // This is used during startup (outside the event loop) when creating
   1.607 +  // the add-ons compatibility checking dialog and the profile manager UI;
   1.608 +  // therefore, it needs to provide an autorelease pool to avoid cocoa
   1.609 +  // objects leaking.
   1.610 +  nsAutoreleasePool localPool;
   1.611 +
   1.612 +  mModal = aState;
   1.613 +  nsCocoaWindow *aParent = static_cast<nsCocoaWindow*>(mParent);
   1.614 +  if (aState) {
   1.615 +    ++gXULModalLevel;
   1.616 +    if (gCocoaAppModalWindowList)
   1.617 +      gCocoaAppModalWindowList->PushGecko(mWindow, this);
   1.618 +    // When a non-sheet window gets "set modal", make the window(s) that it
   1.619 +    // appears over behave as they should.  We can't rely on native methods to
   1.620 +    // do this, for the following reason:  The OS runs modal non-sheet windows
   1.621 +    // in an event loop (using [NSApplication runModalForWindow:] or similar
   1.622 +    // methods) that's incompatible with the modal event loop in nsXULWindow::
   1.623 +    // ShowModal() (each of these event loops is "exclusive", and can't run at
   1.624 +    // the same time as other (similar) event loops).
   1.625 +    if (mWindowType != eWindowType_sheet) {
   1.626 +      while (aParent) {
   1.627 +        if (aParent->mNumModalDescendents++ == 0) {
   1.628 +          NSWindow *aWindow = aParent->GetCocoaWindow();
   1.629 +          if (aParent->mWindowType != eWindowType_invisible) {
   1.630 +            [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:NO];
   1.631 +            [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:NO];
   1.632 +            [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO];
   1.633 +          }
   1.634 +        }
   1.635 +        aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
   1.636 +      }
   1.637 +      [mWindow setLevel:NSModalPanelWindowLevel];
   1.638 +      nsCocoaWindowList *windowList = new nsCocoaWindowList;
   1.639 +      if (windowList) {
   1.640 +        windowList->window = this; // Don't ADDREF
   1.641 +        windowList->prev = gGeckoAppModalWindowList;
   1.642 +        gGeckoAppModalWindowList = windowList;
   1.643 +      }
   1.644 +    }
   1.645 +  }
   1.646 +  else {
   1.647 +    --gXULModalLevel;
   1.648 +    NS_ASSERTION(gXULModalLevel >= 0, "Mismatched call to nsCocoaWindow::SetModal(false)!");
   1.649 +    if (gCocoaAppModalWindowList)
   1.650 +      gCocoaAppModalWindowList->PopGecko(mWindow, this);
   1.651 +    if (mWindowType != eWindowType_sheet) {
   1.652 +      while (aParent) {
   1.653 +        if (--aParent->mNumModalDescendents == 0) {
   1.654 +          NSWindow *aWindow = aParent->GetCocoaWindow();
   1.655 +          if (aParent->mWindowType != eWindowType_invisible) {
   1.656 +            [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:YES];
   1.657 +            [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:YES];
   1.658 +            [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:YES];
   1.659 +          }
   1.660 +        }
   1.661 +        NS_ASSERTION(aParent->mNumModalDescendents >= 0, "Widget hierarchy changed while modal!");
   1.662 +        aParent = static_cast<nsCocoaWindow*>(aParent->mParent);
   1.663 +      }
   1.664 +      if (gGeckoAppModalWindowList) {
   1.665 +        NS_ASSERTION(gGeckoAppModalWindowList->window == this, "Widget hierarchy changed while modal!");
   1.666 +        nsCocoaWindowList *saved = gGeckoAppModalWindowList;
   1.667 +        gGeckoAppModalWindowList = gGeckoAppModalWindowList->prev;
   1.668 +        delete saved; // "window" not ADDREFed
   1.669 +      }
   1.670 +      if (mWindowType == eWindowType_popup)
   1.671 +        SetPopupWindowLevel();
   1.672 +      else
   1.673 +        [mWindow setLevel:NSNormalWindowLevel];
   1.674 +    }
   1.675 +  }
   1.676 +  return NS_OK;
   1.677 +}
   1.678 +
   1.679 +// Hide or show this window
   1.680 +NS_IMETHODIMP nsCocoaWindow::Show(bool bState)
   1.681 +{
   1.682 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   1.683 +
   1.684 +  if (!mWindow)
   1.685 +    return NS_OK;
   1.686 +
   1.687 +  // We need to re-execute sometimes in order to bring already-visible
   1.688 +  // windows forward.
   1.689 +  if (!mSheetNeedsShow && !bState && ![mWindow isVisible])
   1.690 +    return NS_OK;
   1.691 +
   1.692 +  // Protect against re-entering.
   1.693 +  if (bState && [mWindow isBeingShown])
   1.694 +    return NS_OK;
   1.695 +
   1.696 +  [mWindow setBeingShown:bState];
   1.697 +
   1.698 +  nsIWidget* parentWidget = mParent;
   1.699 +  nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget));
   1.700 +  NSWindow* nativeParentWindow = (parentWidget) ?
   1.701 +    (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) : nil;
   1.702 +
   1.703 +  if (bState && !mBounds.IsEmpty()) {
   1.704 +    if (mPopupContentView) {
   1.705 +      // Ensure our content view is visible. We never need to hide it.
   1.706 +      mPopupContentView->Show(true);
   1.707 +    }
   1.708 +
   1.709 +    if (mWindowType == eWindowType_sheet) {
   1.710 +      // bail if no parent window (its basically what we do in Carbon)
   1.711 +      if (!nativeParentWindow || !piParentWidget)
   1.712 +        return NS_ERROR_FAILURE;
   1.713 +
   1.714 +      NSWindow* topNonSheetWindow = nativeParentWindow;
   1.715 +      
   1.716 +      // If this sheet is the child of another sheet, hide the parent so that
   1.717 +      // this sheet can be displayed. Leave the parent mSheetNeedsShow alone,
   1.718 +      // that is only used to handle sibling sheet contention. The parent will
   1.719 +      // return once there are no more child sheets.
   1.720 +      bool parentIsSheet = false;
   1.721 +      if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
   1.722 +          parentIsSheet) {
   1.723 +        piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
   1.724 +        [NSApp endSheet:nativeParentWindow];
   1.725 +      }
   1.726 +
   1.727 +      nsCocoaWindow* sheetShown = nullptr;
   1.728 +      if (NS_SUCCEEDED(piParentWidget->GetChildSheet(true, &sheetShown)) &&
   1.729 +          (!sheetShown || sheetShown == this)) {
   1.730 +        // If this sheet is already the sheet actually being shown, don't
   1.731 +        // tell it to show again. Otherwise the number of calls to
   1.732 +        // [NSApp beginSheet...] won't match up with [NSApp endSheet...].
   1.733 +        if (![mWindow isVisible]) {
   1.734 +          mSheetNeedsShow = false;
   1.735 +          mSheetWindowParent = topNonSheetWindow;
   1.736 +          // Only set contextInfo if our parent isn't a sheet.
   1.737 +          NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
   1.738 +          [TopLevelWindowData deactivateInWindow:mSheetWindowParent];
   1.739 +          [NSApp beginSheet:mWindow
   1.740 +             modalForWindow:mSheetWindowParent
   1.741 +              modalDelegate:mDelegate
   1.742 +             didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
   1.743 +                contextInfo:contextInfo];
   1.744 +          [TopLevelWindowData activateInWindow:mWindow];
   1.745 +          SendSetZLevelEvent();
   1.746 +        }
   1.747 +      }
   1.748 +      else {
   1.749 +        // A sibling of this sheet is active, don't show this sheet yet.
   1.750 +        // When the active sheet hides, its brothers and sisters that have
   1.751 +        // mSheetNeedsShow set will have their opportunities to display.
   1.752 +        mSheetNeedsShow = true;
   1.753 +      }
   1.754 +    }
   1.755 +    else if (mWindowType == eWindowType_popup) {
   1.756 +      // If a popup window is shown after being hidden, it needs to be "reset"
   1.757 +      // for it to receive any mouse events aside from mouse-moved events
   1.758 +      // (because it was removed from the "window cache" when it was hidden
   1.759 +      // -- see below).  Setting the window number to -1 and then back to its
   1.760 +      // original value seems to accomplish this.  The idea was "borrowed"
   1.761 +      // from the Java Embedding Plugin.
   1.762 +      NSInteger windowNumber = [mWindow windowNumber];
   1.763 +      [mWindow _setWindowNumber:-1];
   1.764 +      [mWindow _setWindowNumber:windowNumber];
   1.765 +      // For reasons that aren't yet clear, calls to [NSWindow orderFront:] or
   1.766 +      // [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000)
   1.767 +      // creating CGSWindow", which in turn triggers an internal inconsistency
   1.768 +      // NSException.  These errors shouldn't be fatal.  So we need to wrap
   1.769 +      // calls to ...orderFront: in LOGONLY blocks.  See bmo bug 470864.
   1.770 +      NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
   1.771 +      [[mWindow contentView] setNeedsDisplay:YES];
   1.772 +      [mWindow orderFront:nil];
   1.773 +      NS_OBJC_END_TRY_LOGONLY_BLOCK;
   1.774 +      SendSetZLevelEvent();
   1.775 +      AdjustWindowShadow();
   1.776 +      SetWindowBackgroundBlur();
   1.777 +      // If our popup window is a non-native context menu, tell the OS (and
   1.778 +      // other programs) that a menu has opened.  This is how the OS knows to
   1.779 +      // close other programs' context menus when ours open.
   1.780 +      if ([mWindow isKindOfClass:[PopupWindow class]] &&
   1.781 +          [(PopupWindow*) mWindow isContextMenu]) {
   1.782 +        [[NSDistributedNotificationCenter defaultCenter]
   1.783 +          postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
   1.784 +                        object:@"org.mozilla.gecko.PopupWindow"];
   1.785 +      }
   1.786 +
   1.787 +      // If a parent window was supplied and this is a popup at the parent
   1.788 +      // level, set its child window. This will cause the child window to
   1.789 +      // appear above the parent and move when the parent does. Setting this
   1.790 +      // needs to happen after the _setWindowNumber calls above, otherwise the
   1.791 +      // window doesn't focus properly.
   1.792 +      if (nativeParentWindow && mPopupLevel == ePopupLevelParent)
   1.793 +        [nativeParentWindow addChildWindow:mWindow
   1.794 +                            ordered:NSWindowAbove];
   1.795 +    }
   1.796 +    else {
   1.797 +      NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
   1.798 +      if (mWindowType == eWindowType_toplevel &&
   1.799 +          [mWindow respondsToSelector:@selector(setAnimationBehavior:)]) {
   1.800 +        NSWindowAnimationBehavior behavior;
   1.801 +        if (mIsAnimationSuppressed) {
   1.802 +          behavior = NSWindowAnimationBehaviorNone;
   1.803 +        } else {
   1.804 +          switch (mAnimationType) {
   1.805 +            case nsIWidget::eDocumentWindowAnimation:
   1.806 +              behavior = NSWindowAnimationBehaviorDocumentWindow;
   1.807 +              break;
   1.808 +            default:
   1.809 +              NS_NOTREACHED("unexpected mAnimationType value");
   1.810 +              // fall through
   1.811 +            case nsIWidget::eGenericWindowAnimation:
   1.812 +              behavior = NSWindowAnimationBehaviorDefault;
   1.813 +              break;
   1.814 +          }
   1.815 +        }
   1.816 +        [mWindow setAnimationBehavior:behavior];
   1.817 +      }
   1.818 +      [mWindow makeKeyAndOrderFront:nil];
   1.819 +      NS_OBJC_END_TRY_LOGONLY_BLOCK;
   1.820 +      SendSetZLevelEvent();
   1.821 +    }
   1.822 +  }
   1.823 +  else {
   1.824 +    // roll up any popups if a top-level window is going away
   1.825 +    if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog)
   1.826 +      RollUpPopups();
   1.827 +
   1.828 +    // now get rid of the window/sheet
   1.829 +    if (mWindowType == eWindowType_sheet) {
   1.830 +      if (mSheetNeedsShow) {
   1.831 +        // This is an attempt to hide a sheet that never had a chance to
   1.832 +        // be shown. There's nothing to do other than make sure that it
   1.833 +        // won't show.
   1.834 +        mSheetNeedsShow = false;
   1.835 +      }
   1.836 +      else {
   1.837 +        // get sheet's parent *before* hiding the sheet (which breaks the linkage)
   1.838 +        NSWindow* sheetParent = mSheetWindowParent;
   1.839 +        
   1.840 +        // hide the sheet
   1.841 +        [NSApp endSheet:mWindow];
   1.842 +        
   1.843 +        [TopLevelWindowData deactivateInWindow:mWindow];
   1.844 +
   1.845 +        nsCocoaWindow* siblingSheetToShow = nullptr;
   1.846 +        bool parentIsSheet = false;
   1.847 +
   1.848 +        if (nativeParentWindow && piParentWidget &&
   1.849 +            NS_SUCCEEDED(piParentWidget->GetChildSheet(false, &siblingSheetToShow)) &&
   1.850 +            siblingSheetToShow) {
   1.851 +          // First, give sibling sheets an opportunity to show.
   1.852 +          siblingSheetToShow->Show(true);
   1.853 +        }
   1.854 +        else if (nativeParentWindow && piParentWidget &&
   1.855 +                 NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
   1.856 +                 parentIsSheet) {
   1.857 +          // Only set contextInfo if the parent of the parent sheet we're about
   1.858 +          // to restore isn't itself a sheet.
   1.859 +          NSWindow* contextInfo = sheetParent;
   1.860 +          nsIWidget* grandparentWidget = nil;
   1.861 +          if (NS_SUCCEEDED(piParentWidget->GetRealParent(&grandparentWidget)) && grandparentWidget) {
   1.862 +            nsCOMPtr<nsPIWidgetCocoa> piGrandparentWidget(do_QueryInterface(grandparentWidget));
   1.863 +            bool grandparentIsSheet = false;
   1.864 +            if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) &&
   1.865 +                grandparentIsSheet) {
   1.866 +                contextInfo = nil;
   1.867 +            }
   1.868 +          }
   1.869 +          // If there are no sibling sheets, but the parent is a sheet, restore
   1.870 +          // it.  It wasn't sent any deactivate events when it was hidden, so
   1.871 +          // don't call through Show, just let the OS put it back up.
   1.872 +          [NSApp beginSheet:nativeParentWindow
   1.873 +             modalForWindow:sheetParent
   1.874 +              modalDelegate:[nativeParentWindow delegate]
   1.875 +             didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
   1.876 +                contextInfo:contextInfo];
   1.877 +        }
   1.878 +        else {
   1.879 +          // Sheet, that was hard.  No more siblings or parents, going back
   1.880 +          // to a real window.
   1.881 +          NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
   1.882 +          [sheetParent makeKeyAndOrderFront:nil];
   1.883 +          NS_OBJC_END_TRY_LOGONLY_BLOCK;
   1.884 +        }
   1.885 +        SendSetZLevelEvent();
   1.886 +      }
   1.887 +    }
   1.888 +    else {
   1.889 +      // If the window is a popup window with a parent window we need to
   1.890 +      // unhook it here before ordering it out. When you order out the child
   1.891 +      // of a window it hides the parent window.
   1.892 +      if (mWindowType == eWindowType_popup && nativeParentWindow)
   1.893 +        [nativeParentWindow removeChildWindow:mWindow];
   1.894 +
   1.895 +      [mWindow orderOut:nil];
   1.896 +      // Unless it's explicitly removed from NSApp's "window cache", a popup
   1.897 +      // window will keep receiving mouse-moved events even after it's been
   1.898 +      // "ordered out" (instead of the browser window that was underneath it,
   1.899 +      // until you click on that window).  This is bmo bug 378645, but it's
   1.900 +      // surely an Apple bug.  The "window cache" is an undocumented subsystem,
   1.901 +      // all of whose methods are included in the NSWindowCache category of
   1.902 +      // the NSApplication class (in header files generated using class-dump).
   1.903 +      // This workaround was "borrowed" from the Java Embedding Plugin (which
   1.904 +      // uses it for a different purpose).
   1.905 +      if (mWindowType == eWindowType_popup)
   1.906 +        [NSApp _removeWindowFromCache:mWindow];
   1.907 +
   1.908 +      // If our popup window is a non-native context menu, tell the OS (and
   1.909 +      // other programs) that a menu has closed.
   1.910 +      if ([mWindow isKindOfClass:[PopupWindow class]] &&
   1.911 +          [(PopupWindow*) mWindow isContextMenu]) {
   1.912 +        [[NSDistributedNotificationCenter defaultCenter]
   1.913 +          postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification"
   1.914 +                        object:@"org.mozilla.gecko.PopupWindow"];
   1.915 +      }
   1.916 +    }
   1.917 +  }
   1.918 +
   1.919 +  [mWindow setBeingShown:NO];
   1.920 +
   1.921 +  return NS_OK;
   1.922 +
   1.923 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   1.924 +}
   1.925 +
   1.926 +struct ShadowParams {
   1.927 +  float standardDeviation;
   1.928 +  float density;
   1.929 +  int offsetX;
   1.930 +  int offsetY;
   1.931 +  unsigned int flags;
   1.932 +};
   1.933 +
   1.934 +// These numbers have been determined by looking at the results of
   1.935 +// CGSGetWindowShadowAndRimParameters for native window types.
   1.936 +static const ShadowParams kWindowShadowParameters[] = {
   1.937 +  { 0.0f, 0.0f, 0, 0, 0 },        // none
   1.938 +  { 8.0f, 0.5f, 0, 6, 1 },        // default
   1.939 +  { 10.0f, 0.44f, 0, 10, 512 },   // menu
   1.940 +  { 8.0f, 0.5f, 0, 6, 1 },        // tooltip
   1.941 +  { 4.0f, 0.6f, 0, 4, 512 }       // sheet
   1.942 +};
   1.943 +
   1.944 +// This method will adjust the window shadow style for popup windows after
   1.945 +// they have been made visible. Before they're visible, their window number
   1.946 +// might be -1, which is not useful.
   1.947 +// We won't attempt to change the shadow for windows that can acquire key state
   1.948 +// since OS X will reset the shadow whenever that happens.
   1.949 +void
   1.950 +nsCocoaWindow::AdjustWindowShadow()
   1.951 +{
   1.952 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   1.953 +
   1.954 +  if (!mWindow || ![mWindow isVisible] || ![mWindow hasShadow] ||
   1.955 +      [mWindow canBecomeKeyWindow] || [mWindow windowNumber] == -1)
   1.956 +    return;
   1.957 +
   1.958 +  const ShadowParams& params = kWindowShadowParameters[mShadowStyle];
   1.959 +  CGSConnection cid = _CGSDefaultConnection();
   1.960 +  CGSSetWindowShadowAndRimParameters(cid, [mWindow windowNumber],
   1.961 +                                     params.standardDeviation, params.density,
   1.962 +                                     params.offsetX, params.offsetY,
   1.963 +                                     params.flags);
   1.964 +
   1.965 +  NS_OBJC_END_TRY_ABORT_BLOCK;
   1.966 +}
   1.967 +
   1.968 +static const NSUInteger kWindowBackgroundBlurRadius = 4;
   1.969 +
   1.970 +void
   1.971 +nsCocoaWindow::SetWindowBackgroundBlur()
   1.972 +{
   1.973 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   1.974 +
   1.975 +  if (!mWindow || ![mWindow isVisible] || [mWindow windowNumber] == -1)
   1.976 +    return;
   1.977 +
   1.978 +  // Only blur the background of menus and fake sheets.
   1.979 +  if (mShadowStyle != NS_STYLE_WINDOW_SHADOW_MENU &&
   1.980 +      mShadowStyle != NS_STYLE_WINDOW_SHADOW_SHEET)
   1.981 +    return;
   1.982 +
   1.983 +  CGSConnection cid = _CGSDefaultConnection();
   1.984 +  CGSSetWindowBackgroundBlurRadius(cid, [mWindow windowNumber], kWindowBackgroundBlurRadius);
   1.985 +
   1.986 +  NS_OBJC_END_TRY_ABORT_BLOCK;
   1.987 +}
   1.988 +
   1.989 +nsresult
   1.990 +nsCocoaWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
   1.991 +{
   1.992 +  if (mPopupContentView) {
   1.993 +    mPopupContentView->ConfigureChildren(aConfigurations);
   1.994 +  }
   1.995 +  return NS_OK;
   1.996 +}
   1.997 +
   1.998 +LayerManager*
   1.999 +nsCocoaWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
  1.1000 +                               LayersBackend aBackendHint,
  1.1001 +                               LayerManagerPersistence aPersistence,
  1.1002 +                               bool* aAllowRetaining)
  1.1003 +{
  1.1004 +  if (mPopupContentView) {
  1.1005 +    return mPopupContentView->GetLayerManager(aShadowManager,
  1.1006 +                                              aBackendHint,
  1.1007 +                                              aPersistence,
  1.1008 +                                              aAllowRetaining);
  1.1009 +  }
  1.1010 +  return nullptr;
  1.1011 +}
  1.1012 +
  1.1013 +nsTransparencyMode nsCocoaWindow::GetTransparencyMode()
  1.1014 +{
  1.1015 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.1016 +
  1.1017 +  return (!mWindow || [mWindow isOpaque]) ? eTransparencyOpaque : eTransparencyTransparent;
  1.1018 +
  1.1019 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque);
  1.1020 +}
  1.1021 +
  1.1022 +// This is called from nsMenuPopupFrame when making a popup transparent.
  1.1023 +// For other window types, nsChildView::SetTransparencyMode is used.
  1.1024 +void nsCocoaWindow::SetTransparencyMode(nsTransparencyMode aMode)
  1.1025 +{
  1.1026 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1027 +
  1.1028 +  if (!mWindow)
  1.1029 +    return;
  1.1030 +
  1.1031 +  BOOL isTransparent = aMode == eTransparencyTransparent;
  1.1032 +  BOOL currentTransparency = ![mWindow isOpaque];
  1.1033 +  if (isTransparent != currentTransparency) {
  1.1034 +    [mWindow setOpaque:!isTransparent];
  1.1035 +    [mWindow setBackgroundColor:(isTransparent ? [NSColor clearColor] : [NSColor whiteColor])];
  1.1036 +  }
  1.1037 +
  1.1038 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1039 +}
  1.1040 +
  1.1041 +NS_IMETHODIMP nsCocoaWindow::Enable(bool aState)
  1.1042 +{
  1.1043 +  return NS_OK;
  1.1044 +}
  1.1045 +
  1.1046 +bool nsCocoaWindow::IsEnabled() const
  1.1047 +{
  1.1048 +  return true;
  1.1049 +}
  1.1050 +
  1.1051 +#define kWindowPositionSlop 20
  1.1052 +
  1.1053 +NS_IMETHODIMP nsCocoaWindow::ConstrainPosition(bool aAllowSlop,
  1.1054 +                                               int32_t *aX, int32_t *aY)
  1.1055 +{
  1.1056 +  if (!mWindow || ![mWindow screen]) {
  1.1057 +    return NS_OK;
  1.1058 +  }
  1.1059 +
  1.1060 +  nsIntRect screenBounds;
  1.1061 +
  1.1062 +  int32_t width, height;
  1.1063 +
  1.1064 +  NSRect frame = [mWindow frame];
  1.1065 +
  1.1066 +  // zero size rects confuse the screen manager
  1.1067 +  width = std::max<int32_t>(frame.size.width, 1);
  1.1068 +  height = std::max<int32_t>(frame.size.height, 1);
  1.1069 +
  1.1070 +  nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
  1.1071 +  if (screenMgr) {
  1.1072 +    nsCOMPtr<nsIScreen> screen;
  1.1073 +    screenMgr->ScreenForRect(*aX, *aY, width, height, getter_AddRefs(screen));
  1.1074 +
  1.1075 +    if (screen) {
  1.1076 +      screen->GetRectDisplayPix(&(screenBounds.x), &(screenBounds.y),
  1.1077 +                                &(screenBounds.width), &(screenBounds.height));
  1.1078 +    }
  1.1079 +  }
  1.1080 +
  1.1081 +  if (aAllowSlop) {
  1.1082 +    if (*aX < screenBounds.x - width + kWindowPositionSlop) {
  1.1083 +      *aX = screenBounds.x - width + kWindowPositionSlop;
  1.1084 +    } else if (*aX >= screenBounds.x + screenBounds.width - kWindowPositionSlop) {
  1.1085 +      *aX = screenBounds.x + screenBounds.width - kWindowPositionSlop;
  1.1086 +    }
  1.1087 +
  1.1088 +    if (*aY < screenBounds.y - height + kWindowPositionSlop) {
  1.1089 +      *aY = screenBounds.y - height + kWindowPositionSlop;
  1.1090 +    } else if (*aY >= screenBounds.y + screenBounds.height - kWindowPositionSlop) {
  1.1091 +      *aY = screenBounds.y + screenBounds.height - kWindowPositionSlop;
  1.1092 +    }
  1.1093 +  } else {
  1.1094 +    if (*aX < screenBounds.x) {
  1.1095 +      *aX = screenBounds.x;
  1.1096 +    } else if (*aX >= screenBounds.x + screenBounds.width - width) {
  1.1097 +      *aX = screenBounds.x + screenBounds.width - width;
  1.1098 +    }
  1.1099 +
  1.1100 +    if (*aY < screenBounds.y) {
  1.1101 +      *aY = screenBounds.y;
  1.1102 +    } else if (*aY >= screenBounds.y + screenBounds.height - height) {
  1.1103 +      *aY = screenBounds.y + screenBounds.height - height;
  1.1104 +    }
  1.1105 +  }
  1.1106 +
  1.1107 +  return NS_OK;
  1.1108 +}
  1.1109 +
  1.1110 +void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
  1.1111 +{
  1.1112 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1113 +
  1.1114 +  // Popups can be smaller than (60, 60)
  1.1115 +  NSRect rect =
  1.1116 +    (mWindowType == eWindowType_popup) ? NSZeroRect : NSMakeRect(0.0, 0.0, 60, 60);
  1.1117 +  rect = [mWindow frameRectForContentRect:rect];
  1.1118 +
  1.1119 +  CGFloat scaleFactor = BackingScaleFactor();
  1.1120 +
  1.1121 +  SizeConstraints c = aConstraints;
  1.1122 +  c.mMinSize.width =
  1.1123 +    std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.width, scaleFactor),
  1.1124 +           c.mMinSize.width);
  1.1125 +  c.mMinSize.height =
  1.1126 +    std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.height, scaleFactor),
  1.1127 +           c.mMinSize.height);
  1.1128 +
  1.1129 +  NSSize minSize = {
  1.1130 +    nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.width, scaleFactor),
  1.1131 +    nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.height, scaleFactor)
  1.1132 +  };
  1.1133 +  [mWindow setMinSize:minSize];
  1.1134 +
  1.1135 +  NSSize maxSize = {
  1.1136 +    c.mMaxSize.width == NS_MAXSIZE ?
  1.1137 +      FLT_MAX : nsCocoaUtils::DevPixelsToCocoaPoints(c.mMaxSize.width, scaleFactor),
  1.1138 +    c.mMaxSize.height == NS_MAXSIZE ?
  1.1139 +      FLT_MAX : nsCocoaUtils::DevPixelsToCocoaPoints(c.mMaxSize.height, scaleFactor)
  1.1140 +  };
  1.1141 +  [mWindow setMaxSize:maxSize];
  1.1142 +
  1.1143 +  nsBaseWidget::SetSizeConstraints(c);
  1.1144 +
  1.1145 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1146 +}
  1.1147 +
  1.1148 +// Coordinates are global display pixels
  1.1149 +NS_IMETHODIMP nsCocoaWindow::Move(double aX, double aY)
  1.1150 +{
  1.1151 +  if (!mWindow) {
  1.1152 +    return NS_OK;
  1.1153 +  }
  1.1154 +
  1.1155 +  // The point we have is in Gecko coordinates (origin top-left). Convert
  1.1156 +  // it to Cocoa ones (origin bottom-left).
  1.1157 +  NSPoint coord = {
  1.1158 +    static_cast<float>(aX),
  1.1159 +    static_cast<float>(nsCocoaUtils::FlippedScreenY(NSToIntRound(aY)))
  1.1160 +  };
  1.1161 +
  1.1162 +  NSRect frame = [mWindow frame];
  1.1163 +  if (frame.origin.x != coord.x ||
  1.1164 +      frame.origin.y + frame.size.height != coord.y) {
  1.1165 +    [mWindow setFrameTopLeftPoint:coord];
  1.1166 +  }
  1.1167 +
  1.1168 +  return NS_OK;
  1.1169 +}
  1.1170 +
  1.1171 +// Position the window behind the given window
  1.1172 +NS_METHOD nsCocoaWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
  1.1173 +                                     nsIWidget *aWidget, bool aActivate)
  1.1174 +{
  1.1175 +  return NS_OK;
  1.1176 +}
  1.1177 +
  1.1178 +NS_METHOD nsCocoaWindow::SetSizeMode(int32_t aMode)
  1.1179 +{
  1.1180 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1181 +
  1.1182 +  if (!mWindow)
  1.1183 +    return NS_OK;
  1.1184 +
  1.1185 +  // mSizeMode will be updated in DispatchSizeModeEvent, which will be called
  1.1186 +  // from a delegate method that handles the state change during one of the
  1.1187 +  // calls below.
  1.1188 +  nsSizeMode previousMode = mSizeMode;
  1.1189 +
  1.1190 +  if (aMode == nsSizeMode_Normal) {
  1.1191 +    if ([mWindow isMiniaturized])
  1.1192 +      [mWindow deminiaturize:nil];
  1.1193 +    else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
  1.1194 +      [mWindow zoom:nil];
  1.1195 +  }
  1.1196 +  else if (aMode == nsSizeMode_Minimized) {
  1.1197 +    if (![mWindow isMiniaturized])
  1.1198 +      [mWindow miniaturize:nil];
  1.1199 +  }
  1.1200 +  else if (aMode == nsSizeMode_Maximized) {
  1.1201 +    if ([mWindow isMiniaturized])
  1.1202 +      [mWindow deminiaturize:nil];
  1.1203 +    if (![mWindow isZoomed])
  1.1204 +      [mWindow zoom:nil];
  1.1205 +  }
  1.1206 +  else if (aMode == nsSizeMode_Fullscreen) {
  1.1207 +    if (!mFullScreen)
  1.1208 +      MakeFullScreen(true);
  1.1209 +  }
  1.1210 +
  1.1211 +  return NS_OK;
  1.1212 +
  1.1213 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1214 +}
  1.1215 +
  1.1216 +// This has to preserve the window's frame bounds.
  1.1217 +// This method requires (as does the Windows impl.) that you call Resize shortly
  1.1218 +// after calling HideWindowChrome. See bug 498835 for fixing this.
  1.1219 +NS_IMETHODIMP nsCocoaWindow::HideWindowChrome(bool aShouldHide)
  1.1220 +{
  1.1221 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1222 +
  1.1223 +  if (!mWindow || !mWindowMadeHere ||
  1.1224 +      (mWindowType != eWindowType_toplevel && mWindowType != eWindowType_dialog))
  1.1225 +    return NS_ERROR_FAILURE;
  1.1226 +
  1.1227 +  BOOL isVisible = [mWindow isVisible];
  1.1228 +
  1.1229 +  // Remove child windows.
  1.1230 +  NSArray* childWindows = [mWindow childWindows];
  1.1231 +  NSEnumerator* enumerator = [childWindows objectEnumerator];
  1.1232 +  NSWindow* child = nil;
  1.1233 +  while ((child = [enumerator nextObject])) {
  1.1234 +    [mWindow removeChildWindow:child];
  1.1235 +  }
  1.1236 +
  1.1237 +  // Remove the content view.
  1.1238 +  NSView* contentView = [mWindow contentView];
  1.1239 +  [contentView retain];
  1.1240 +  [contentView removeFromSuperviewWithoutNeedingDisplay];
  1.1241 +
  1.1242 +  // Save state (like window title).
  1.1243 +  NSMutableDictionary* state = [mWindow exportState];
  1.1244 +
  1.1245 +  // Recreate the window with the right border style.
  1.1246 +  NSRect frameRect = [mWindow frame];
  1.1247 +  DestroyNativeWindow();
  1.1248 +  nsresult rv = CreateNativeWindow(frameRect, aShouldHide ? eBorderStyle_none : mBorderStyle, true);
  1.1249 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1250 +
  1.1251 +  // Re-import state.
  1.1252 +  [mWindow importState:state];
  1.1253 +
  1.1254 +  // Reparent the content view.
  1.1255 +  [mWindow setContentView:contentView];
  1.1256 +  [contentView release];
  1.1257 +
  1.1258 +  // Reparent child windows.
  1.1259 +  enumerator = [childWindows objectEnumerator];
  1.1260 +  while ((child = [enumerator nextObject])) {
  1.1261 +    [mWindow addChildWindow:child ordered:NSWindowAbove];
  1.1262 +  }
  1.1263 +
  1.1264 +  // Show the new window.
  1.1265 +  if (isVisible) {
  1.1266 +    rv = Show(true);
  1.1267 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1268 +  }
  1.1269 +
  1.1270 +  return NS_OK;
  1.1271 +
  1.1272 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1273 +}
  1.1274 +
  1.1275 +void nsCocoaWindow::EnteredFullScreen(bool aFullScreen)
  1.1276 +{
  1.1277 +  mInFullScreenTransition = false;
  1.1278 +  mFullScreen = aFullScreen;
  1.1279 +  DispatchSizeModeEvent();
  1.1280 +}
  1.1281 +
  1.1282 +NS_METHOD nsCocoaWindow::MakeFullScreen(bool aFullScreen)
  1.1283 +{
  1.1284 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1285 +
  1.1286 +  if (!mWindow) {
  1.1287 +    return NS_OK;
  1.1288 +  }
  1.1289 +
  1.1290 +  // We will call into MakeFullScreen redundantly when entering/exiting
  1.1291 +  // fullscreen mode via OS X controls. When that happens we should just handle
  1.1292 +  // it gracefully - no need to ASSERT.
  1.1293 +  if (mFullScreen == aFullScreen) {
  1.1294 +    return NS_OK;
  1.1295 +  }
  1.1296 +
  1.1297 +  // If we're using native fullscreen mode and our native window is invisible,
  1.1298 +  // our attempt to go into fullscreen mode will fail with an assertion in
  1.1299 +  // system code, without [WindowDelegate windowDidFailToEnterFullScreen:]
  1.1300 +  // ever getting called.  To pre-empt this we bail here.  See bug 752294.
  1.1301 +  if (mUsesNativeFullScreen && aFullScreen && ![mWindow isVisible]) {
  1.1302 +    EnteredFullScreen(false);
  1.1303 +    return NS_OK;
  1.1304 +  }
  1.1305 +
  1.1306 +  mInFullScreenTransition = true;
  1.1307 +
  1.1308 +  if (mUsesNativeFullScreen) {
  1.1309 +    // Calling toggleFullScreen will result in windowDid(FailTo)?(Enter|Exit)FullScreen
  1.1310 +    // to be called from the OS. We will call EnteredFullScreen from those methods,
  1.1311 +    // where mFullScreen will be set and a sizemode event will be dispatched.
  1.1312 +    [mWindow toggleFullScreen:nil];
  1.1313 +  } else {
  1.1314 +    NSDisableScreenUpdates();
  1.1315 +    // The order here matters. When we exit full screen mode, we need to show the
  1.1316 +    // Dock first, otherwise the newly-created window won't have its minimize
  1.1317 +    // button enabled. See bug 526282.
  1.1318 +    nsCocoaUtils::HideOSChromeOnScreen(aFullScreen, [mWindow screen]);
  1.1319 +    nsresult rv = nsBaseWidget::MakeFullScreen(aFullScreen);
  1.1320 +    NSEnableScreenUpdates();
  1.1321 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1322 +
  1.1323 +    EnteredFullScreen(aFullScreen);
  1.1324 +  }
  1.1325 +
  1.1326 +  return NS_OK;
  1.1327 +
  1.1328 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1329 +}
  1.1330 +
  1.1331 +// Coordinates are global display pixels
  1.1332 +nsresult nsCocoaWindow::DoResize(double aX, double aY,
  1.1333 +                                 double aWidth, double aHeight,
  1.1334 +                                 bool aRepaint,
  1.1335 +                                 bool aConstrainToCurrentScreen)
  1.1336 +{
  1.1337 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1338 +
  1.1339 +  if (!mWindow || mInResize) {
  1.1340 +    return NS_OK;
  1.1341 +  }
  1.1342 +
  1.1343 +  AutoRestore<bool> reentrantResizeGuard(mInResize);
  1.1344 +  mInResize = true;
  1.1345 +
  1.1346 +  // ConstrainSize operates in device pixels, so we need to convert using
  1.1347 +  // the backing scale factor here
  1.1348 +  CGFloat scale = BackingScaleFactor();
  1.1349 +  int32_t width = NSToIntRound(aWidth * scale);
  1.1350 +  int32_t height = NSToIntRound(aHeight * scale);
  1.1351 +  ConstrainSize(&width, &height);
  1.1352 +
  1.1353 +  nsIntRect newBounds(NSToIntRound(aX), NSToIntRound(aY),
  1.1354 +                      NSToIntRound(width / scale),
  1.1355 +                      NSToIntRound(height / scale));
  1.1356 +
  1.1357 +  // constrain to the screen that contains the largest area of the new rect
  1.1358 +  FitRectToVisibleAreaForScreen(newBounds,
  1.1359 +                                aConstrainToCurrentScreen ?
  1.1360 +                                  [mWindow screen] : nullptr,
  1.1361 +                                mUsesNativeFullScreen);
  1.1362 +
  1.1363 +  // convert requested bounds into Cocoa coordinate system
  1.1364 +  NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(newBounds);
  1.1365 +
  1.1366 +  NSRect frame = [mWindow frame];
  1.1367 +  BOOL isMoving = newFrame.origin.x != frame.origin.x ||
  1.1368 +                  newFrame.origin.y != frame.origin.y;
  1.1369 +  BOOL isResizing = newFrame.size.width != frame.size.width ||
  1.1370 +                    newFrame.size.height != frame.size.height;
  1.1371 +
  1.1372 +  if (!isMoving && !isResizing) {
  1.1373 +    return NS_OK;
  1.1374 +  }
  1.1375 +
  1.1376 +  // We ignore aRepaint -- we have to call display:YES, otherwise the
  1.1377 +  // title bar doesn't immediately get repainted and is displayed in
  1.1378 +  // the wrong place, leading to a visual jump.
  1.1379 +  [mWindow setFrame:newFrame display:YES];
  1.1380 +
  1.1381 +  return NS_OK;
  1.1382 +
  1.1383 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1384 +}
  1.1385 +
  1.1386 +// Coordinates are global display pixels
  1.1387 +NS_IMETHODIMP nsCocoaWindow::Resize(double aX, double aY,
  1.1388 +                                    double aWidth, double aHeight,
  1.1389 +                                    bool aRepaint)
  1.1390 +{
  1.1391 +  return DoResize(aX, aY, aWidth, aHeight, aRepaint, false);
  1.1392 +}
  1.1393 +
  1.1394 +// Coordinates are global display pixels
  1.1395 +NS_IMETHODIMP nsCocoaWindow::Resize(double aWidth, double aHeight, bool aRepaint)
  1.1396 +{
  1.1397 +  double invScale = 1.0 / GetDefaultScale().scale;
  1.1398 +  return DoResize(mBounds.x * invScale, mBounds.y * invScale,
  1.1399 +                  aWidth, aHeight, aRepaint, true);
  1.1400 +}
  1.1401 +
  1.1402 +NS_IMETHODIMP nsCocoaWindow::GetClientBounds(nsIntRect &aRect)
  1.1403 +{
  1.1404 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1405 +
  1.1406 +  CGFloat scaleFactor = BackingScaleFactor();
  1.1407 +  if (!mWindow) {
  1.1408 +    aRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(NSZeroRect, scaleFactor);
  1.1409 +    return NS_OK;
  1.1410 +  }
  1.1411 +
  1.1412 +  NSRect r;
  1.1413 +  if ([mWindow isKindOfClass:[ToolbarWindow class]] &&
  1.1414 +      [(ToolbarWindow*)mWindow drawsContentsIntoWindowFrame]) {
  1.1415 +    r = [mWindow frame];
  1.1416 +  } else {
  1.1417 +    r = [mWindow contentRectForFrameRect:[mWindow frame]];
  1.1418 +  }
  1.1419 +
  1.1420 +  aRect = nsCocoaUtils::CocoaRectToGeckoRectDevPix(r, scaleFactor);
  1.1421 +
  1.1422 +  return NS_OK;
  1.1423 +
  1.1424 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1425 +}
  1.1426 +
  1.1427 +void
  1.1428 +nsCocoaWindow::UpdateBounds()
  1.1429 +{
  1.1430 +  NSRect frame = NSZeroRect;
  1.1431 +  if (mWindow) {
  1.1432 +    frame = [mWindow frame];
  1.1433 +  }
  1.1434 +  mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, BackingScaleFactor());
  1.1435 +}
  1.1436 +
  1.1437 +NS_IMETHODIMP nsCocoaWindow::GetScreenBounds(nsIntRect &aRect)
  1.1438 +{
  1.1439 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1440 +
  1.1441 +#ifdef DEBUG
  1.1442 +  nsIntRect r = nsCocoaUtils::CocoaRectToGeckoRectDevPix([mWindow frame], BackingScaleFactor());
  1.1443 +  NS_ASSERTION(mWindow && mBounds == r, "mBounds out of sync!");
  1.1444 +#endif
  1.1445 +
  1.1446 +  aRect = mBounds;
  1.1447 +  return NS_OK;
  1.1448 +
  1.1449 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1450 +}
  1.1451 +
  1.1452 +double
  1.1453 +nsCocoaWindow::GetDefaultScaleInternal()
  1.1454 +{
  1.1455 +  return BackingScaleFactor();
  1.1456 +}
  1.1457 +
  1.1458 +static CGFloat
  1.1459 +GetBackingScaleFactor(NSWindow* aWindow)
  1.1460 +{
  1.1461 +  NSRect frame = [aWindow frame];
  1.1462 +  if (frame.size.width > 0 && frame.size.height > 0) {
  1.1463 +    return nsCocoaUtils::GetBackingScaleFactor(aWindow);
  1.1464 +  }
  1.1465 +
  1.1466 +  // For windows with zero width or height, the backingScaleFactor method
  1.1467 +  // is broken - it will always return 2 on a retina macbook, even when
  1.1468 +  // the window position implies it's on a non-hidpi external display
  1.1469 +  // (to the extent that a zero-area window can be said to be "on" a
  1.1470 +  // display at all!)
  1.1471 +  // And to make matters worse, Cocoa even fires a
  1.1472 +  // windowDidChangeBackingProperties notification with the
  1.1473 +  // NSBackingPropertyOldScaleFactorKey key when a window on an
  1.1474 +  // external display is resized to/from zero height, even though it hasn't
  1.1475 +  // really changed screens.
  1.1476 +
  1.1477 +  // This causes us to handle popup window sizing incorrectly when the
  1.1478 +  // popup is resized to zero height (bug 820327) - nsXULPopupManager
  1.1479 +  // becomes (incorrectly) convinced the popup has been explicitly forced
  1.1480 +  // to a non-default size and needs to have size attributes attached.
  1.1481 +
  1.1482 +  // Workaround: instead of asking the window, we'll find the screen it is on
  1.1483 +  // and ask that for *its* backing scale factor.
  1.1484 +
  1.1485 +  // (See bug 853252 and additional comments in windowDidChangeScreen: below
  1.1486 +  // for further complications this causes.)
  1.1487 +
  1.1488 +  // First, expand the rect so that it actually has a measurable area,
  1.1489 +  // for FindTargetScreenForRect to use.
  1.1490 +  if (frame.size.width == 0) {
  1.1491 +    frame.size.width = 1;
  1.1492 +  }
  1.1493 +  if (frame.size.height == 0) {
  1.1494 +    frame.size.height = 1;
  1.1495 +  }
  1.1496 +
  1.1497 +  // Then identify the screen it belongs to, and return its scale factor.
  1.1498 +  NSScreen *screen =
  1.1499 +    FindTargetScreenForRect(nsCocoaUtils::CocoaRectToGeckoRect(frame));
  1.1500 +  return nsCocoaUtils::GetBackingScaleFactor(screen);
  1.1501 +}
  1.1502 +
  1.1503 +CGFloat
  1.1504 +nsCocoaWindow::BackingScaleFactor()
  1.1505 +{
  1.1506 +  if (mBackingScaleFactor > 0.0) {
  1.1507 +    return mBackingScaleFactor;
  1.1508 +  }
  1.1509 +  if (!mWindow) {
  1.1510 +    return 1.0;
  1.1511 +  }
  1.1512 +  mBackingScaleFactor = GetBackingScaleFactor(mWindow);
  1.1513 +  return mBackingScaleFactor;
  1.1514 +}
  1.1515 +
  1.1516 +void
  1.1517 +nsCocoaWindow::BackingScaleFactorChanged()
  1.1518 +{
  1.1519 +  CGFloat newScale = GetBackingScaleFactor(mWindow);
  1.1520 +
  1.1521 +  // ignore notification if it hasn't really changed (or maybe we have
  1.1522 +  // disabled HiDPI mode via prefs)
  1.1523 +  if (mBackingScaleFactor == newScale) {
  1.1524 +    return;
  1.1525 +  }
  1.1526 +
  1.1527 +  if (mBackingScaleFactor > 0.0) {
  1.1528 +    // convert size constraints to the new device pixel coordinate space
  1.1529 +    double scaleFactor = newScale / mBackingScaleFactor;
  1.1530 +    mSizeConstraints.mMinSize.width =
  1.1531 +      NSToIntRound(mSizeConstraints.mMinSize.width * scaleFactor);
  1.1532 +    mSizeConstraints.mMinSize.height =
  1.1533 +      NSToIntRound(mSizeConstraints.mMinSize.height * scaleFactor);
  1.1534 +    if (mSizeConstraints.mMaxSize.width < NS_MAXSIZE) {
  1.1535 +      mSizeConstraints.mMaxSize.width =
  1.1536 +        std::min(NS_MAXSIZE,
  1.1537 +               NSToIntRound(mSizeConstraints.mMaxSize.width * scaleFactor));
  1.1538 +    }
  1.1539 +    if (mSizeConstraints.mMaxSize.height < NS_MAXSIZE) {
  1.1540 +      mSizeConstraints.mMaxSize.height =
  1.1541 +        std::min(NS_MAXSIZE,
  1.1542 +               NSToIntRound(mSizeConstraints.mMaxSize.height * scaleFactor));
  1.1543 +    }
  1.1544 +  }
  1.1545 +
  1.1546 +  mBackingScaleFactor = newScale;
  1.1547 +
  1.1548 +  if (!mWidgetListener || mWidgetListener->GetXULWindow()) {
  1.1549 +    return;
  1.1550 +  }
  1.1551 +
  1.1552 +  nsIPresShell* presShell = mWidgetListener->GetPresShell();
  1.1553 +  if (presShell) {
  1.1554 +    presShell->BackingScaleFactorChanged();
  1.1555 +  }
  1.1556 +}
  1.1557 +
  1.1558 +int32_t
  1.1559 +nsCocoaWindow::RoundsWidgetCoordinatesTo()
  1.1560 +{
  1.1561 +  if (BackingScaleFactor() == 2.0) {
  1.1562 +    return 2;
  1.1563 +  }
  1.1564 +  return 1;
  1.1565 +}
  1.1566 +
  1.1567 +NS_IMETHODIMP nsCocoaWindow::SetCursor(nsCursor aCursor)
  1.1568 +{
  1.1569 +  if (mPopupContentView)
  1.1570 +    return mPopupContentView->SetCursor(aCursor);
  1.1571 +
  1.1572 +  return NS_OK;
  1.1573 +}
  1.1574 +
  1.1575 +NS_IMETHODIMP nsCocoaWindow::SetCursor(imgIContainer* aCursor,
  1.1576 +                                       uint32_t aHotspotX, uint32_t aHotspotY)
  1.1577 +{
  1.1578 +  if (mPopupContentView)
  1.1579 +    return mPopupContentView->SetCursor(aCursor, aHotspotX, aHotspotY);
  1.1580 +
  1.1581 +  return NS_OK;
  1.1582 +}
  1.1583 +
  1.1584 +NS_IMETHODIMP nsCocoaWindow::SetTitle(const nsAString& aTitle)
  1.1585 +{
  1.1586 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1587 +
  1.1588 +  if (!mWindow)
  1.1589 +    return NS_OK;
  1.1590 +
  1.1591 +  const nsString& strTitle = PromiseFlatString(aTitle);
  1.1592 +  NSString* title = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(strTitle.get())
  1.1593 +                                            length:strTitle.Length()];
  1.1594 +
  1.1595 +  if ([mWindow drawsContentsIntoWindowFrame] && ![mWindow wantsTitleDrawn]) {
  1.1596 +    // Don't cause invalidations.
  1.1597 +    [mWindow disableSetNeedsDisplay];
  1.1598 +    [mWindow setTitle:title];
  1.1599 +    [mWindow enableSetNeedsDisplay];
  1.1600 +  } else {
  1.1601 +    [mWindow setTitle:title];
  1.1602 +  }
  1.1603 +
  1.1604 +  return NS_OK;
  1.1605 +
  1.1606 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1607 +}
  1.1608 +
  1.1609 +NS_IMETHODIMP nsCocoaWindow::Invalidate(const nsIntRect & aRect)
  1.1610 +{
  1.1611 +  if (mPopupContentView) {
  1.1612 +    return mPopupContentView->Invalidate(aRect);
  1.1613 +  }
  1.1614 +
  1.1615 +  return NS_OK;
  1.1616 +}
  1.1617 +
  1.1618 +// Pass notification of some drag event to Gecko
  1.1619 +//
  1.1620 +// The drag manager has let us know that something related to a drag has
  1.1621 +// occurred in this window. It could be any number of things, ranging from 
  1.1622 +// a drop, to a drag enter/leave, or a drag over event. The actual event
  1.1623 +// is passed in |aMessage| and is passed along to our event hanlder so Gecko
  1.1624 +// knows about it.
  1.1625 +bool nsCocoaWindow::DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers)
  1.1626 +{
  1.1627 +  return false;
  1.1628 +}
  1.1629 +
  1.1630 +NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent()
  1.1631 +{
  1.1632 +  nsWindowZ placement = nsWindowZTop;
  1.1633 +  nsIWidget* actualBelow;
  1.1634 +  if (mWidgetListener)
  1.1635 +    mWidgetListener->ZLevelChanged(true, &placement, nullptr, &actualBelow);
  1.1636 +  return NS_OK;
  1.1637 +}
  1.1638 +
  1.1639 +NS_IMETHODIMP nsCocoaWindow::GetChildSheet(bool aShown, nsCocoaWindow** _retval)
  1.1640 +{
  1.1641 +  nsIWidget* child = GetFirstChild();
  1.1642 +
  1.1643 +  while (child) {
  1.1644 +    if (child->WindowType() == eWindowType_sheet) {
  1.1645 +      // if it's a sheet, it must be an nsCocoaWindow
  1.1646 +      nsCocoaWindow* cocoaWindow = static_cast<nsCocoaWindow*>(child);
  1.1647 +      if (cocoaWindow->mWindow &&
  1.1648 +          ((aShown && [cocoaWindow->mWindow isVisible]) ||
  1.1649 +          (!aShown && cocoaWindow->mSheetNeedsShow))) {
  1.1650 +        *_retval = cocoaWindow;
  1.1651 +        return NS_OK;
  1.1652 +      }
  1.1653 +    }
  1.1654 +    child = child->GetNextSibling();
  1.1655 +  }
  1.1656 +
  1.1657 +  *_retval = nullptr;
  1.1658 +
  1.1659 +  return NS_OK;
  1.1660 +}
  1.1661 +
  1.1662 +NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent)
  1.1663 +{
  1.1664 +  *parent = mParent;
  1.1665 +  return NS_OK;
  1.1666 +}
  1.1667 +
  1.1668 +NS_IMETHODIMP nsCocoaWindow::GetIsSheet(bool* isSheet)
  1.1669 +{
  1.1670 +  mWindowType == eWindowType_sheet ? *isSheet = true : *isSheet = false;
  1.1671 +  return NS_OK;
  1.1672 +}
  1.1673 +
  1.1674 +NS_IMETHODIMP nsCocoaWindow::GetSheetWindowParent(NSWindow** sheetWindowParent)
  1.1675 +{
  1.1676 +  *sheetWindowParent = mSheetWindowParent;
  1.1677 +  return NS_OK;
  1.1678 +}
  1.1679 +
  1.1680 +// Invokes callback and ProcessEvent methods on Event Listener object
  1.1681 +NS_IMETHODIMP 
  1.1682 +nsCocoaWindow::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus)
  1.1683 +{
  1.1684 +  aStatus = nsEventStatus_eIgnore;
  1.1685 +
  1.1686 +  nsIWidget* aWidget = event->widget;
  1.1687 +  NS_IF_ADDREF(aWidget);
  1.1688 +
  1.1689 +  if (mWidgetListener)
  1.1690 +    aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents);
  1.1691 +
  1.1692 +  NS_IF_RELEASE(aWidget);
  1.1693 +
  1.1694 +  return NS_OK;
  1.1695 +}
  1.1696 +
  1.1697 +// aFullScreen should be the window's mFullScreen. We don't have access to that
  1.1698 +// from here, so we need to pass it in. mFullScreen should be the canonical
  1.1699 +// indicator that a window is currently full screen and it makes sense to keep
  1.1700 +// all sizemode logic here.
  1.1701 +static nsSizeMode
  1.1702 +GetWindowSizeMode(NSWindow* aWindow, bool aFullScreen) {
  1.1703 +  if (aFullScreen)
  1.1704 +    return nsSizeMode_Fullscreen;
  1.1705 +  if ([aWindow isMiniaturized])
  1.1706 +    return nsSizeMode_Minimized;
  1.1707 +  if (([aWindow styleMask] & NSResizableWindowMask) && [aWindow isZoomed])
  1.1708 +    return nsSizeMode_Maximized;
  1.1709 +  return nsSizeMode_Normal;
  1.1710 +}
  1.1711 +
  1.1712 +void
  1.1713 +nsCocoaWindow::ReportMoveEvent()
  1.1714 +{
  1.1715 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1716 +
  1.1717 +  // Prevent recursion, which can become infinite (see bug 708278).  This
  1.1718 +  // can happen when the call to [NSWindow setFrameTopLeftPoint:] in
  1.1719 +  // nsCocoaWindow::Move() triggers an immediate NSWindowDidMove notification
  1.1720 +  // (and a call to [WindowDelegate windowDidMove:]).
  1.1721 +  if (mInReportMoveEvent) {
  1.1722 +    return;
  1.1723 +  }
  1.1724 +  mInReportMoveEvent = true;
  1.1725 +
  1.1726 +  UpdateBounds();
  1.1727 +
  1.1728 +  // Dispatch the move event to Gecko
  1.1729 +  NotifyWindowMoved(mBounds.x, mBounds.y);
  1.1730 +
  1.1731 +  mInReportMoveEvent = false;
  1.1732 +
  1.1733 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1734 +}
  1.1735 +
  1.1736 +void
  1.1737 +nsCocoaWindow::DispatchSizeModeEvent()
  1.1738 +{
  1.1739 +  if (!mWindow) {
  1.1740 +    return;
  1.1741 +  }
  1.1742 +
  1.1743 +  nsSizeMode newMode = GetWindowSizeMode(mWindow, mFullScreen);
  1.1744 +
  1.1745 +  // Don't dispatch a sizemode event if:
  1.1746 +  // 1. the window is transitioning to fullscreen
  1.1747 +  // 2. the new sizemode is the same as the current sizemode
  1.1748 +  if (mInFullScreenTransition || mSizeMode == newMode) {
  1.1749 +    return;
  1.1750 +  }
  1.1751 +
  1.1752 +  mSizeMode = newMode;
  1.1753 +  if (mWidgetListener) {
  1.1754 +    mWidgetListener->SizeModeChanged(newMode);
  1.1755 +  }
  1.1756 +}
  1.1757 +
  1.1758 +void
  1.1759 +nsCocoaWindow::ReportSizeEvent()
  1.1760 +{
  1.1761 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1762 +
  1.1763 +  UpdateBounds();
  1.1764 +
  1.1765 +  if (mWidgetListener) {
  1.1766 +    nsIntRect innerBounds;
  1.1767 +    GetClientBounds(innerBounds);
  1.1768 +    mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height);
  1.1769 +  }
  1.1770 +
  1.1771 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1772 +}
  1.1773 +
  1.1774 +void nsCocoaWindow::SetMenuBar(nsMenuBarX *aMenuBar)
  1.1775 +{
  1.1776 +  if (mMenuBar)
  1.1777 +    mMenuBar->SetParent(nullptr);
  1.1778 +  if (!mWindow) {
  1.1779 +    mMenuBar = nullptr;
  1.1780 +    return;
  1.1781 +  }
  1.1782 +  mMenuBar = aMenuBar;
  1.1783 +
  1.1784 +  // Only paint for active windows, or paint the hidden window menu bar if no
  1.1785 +  // other menu bar has been painted yet so that some reasonable menu bar is
  1.1786 +  // displayed when the app starts up.
  1.1787 +  id windowDelegate = [mWindow delegate];
  1.1788 +  if (mMenuBar &&
  1.1789 +      ((!gSomeMenuBarPainted && nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar) ||
  1.1790 +       (windowDelegate && [windowDelegate toplevelActiveState])))
  1.1791 +    mMenuBar->Paint();
  1.1792 +}
  1.1793 +
  1.1794 +NS_IMETHODIMP nsCocoaWindow::SetFocus(bool aState)
  1.1795 +{
  1.1796 +  if (!mWindow)
  1.1797 +    return NS_OK;
  1.1798 +
  1.1799 +  if (mPopupContentView) {
  1.1800 +    mPopupContentView->SetFocus(aState);
  1.1801 +  }
  1.1802 +  else if (aState && ([mWindow isVisible] || [mWindow isMiniaturized])) {
  1.1803 +    [mWindow makeKeyAndOrderFront:nil];
  1.1804 +    SendSetZLevelEvent();
  1.1805 +  }
  1.1806 +
  1.1807 +  return NS_OK;
  1.1808 +}
  1.1809 +
  1.1810 +nsIntPoint nsCocoaWindow::WidgetToScreenOffset()
  1.1811 +{
  1.1812 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.1813 +
  1.1814 +  NSRect rect = NSZeroRect;
  1.1815 +  nsIntRect r;
  1.1816 +  if (mWindow) {
  1.1817 +    rect = [mWindow contentRectForFrameRect:[mWindow frame]];
  1.1818 +  }
  1.1819 +  r = nsCocoaUtils::CocoaRectToGeckoRectDevPix(rect, BackingScaleFactor());
  1.1820 +
  1.1821 +  return r.TopLeft();
  1.1822 +
  1.1823 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0,0));
  1.1824 +}
  1.1825 +
  1.1826 +nsIntPoint nsCocoaWindow::GetClientOffset()
  1.1827 +{
  1.1828 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.1829 +
  1.1830 +  nsIntRect clientRect;
  1.1831 +  GetClientBounds(clientRect);
  1.1832 +
  1.1833 +  return clientRect.TopLeft() - mBounds.TopLeft();
  1.1834 +
  1.1835 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0, 0));
  1.1836 +}
  1.1837 +
  1.1838 +nsIntSize nsCocoaWindow::ClientToWindowSize(const nsIntSize& aClientSize)
  1.1839 +{
  1.1840 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.1841 +
  1.1842 +  if (!mWindow)
  1.1843 +    return nsIntSize(0, 0);
  1.1844 +
  1.1845 +  CGFloat backingScale = BackingScaleFactor();
  1.1846 +  nsIntRect r(0, 0, aClientSize.width, aClientSize.height);
  1.1847 +  NSRect rect = nsCocoaUtils::DevPixelsToCocoaPoints(r, backingScale);
  1.1848 +
  1.1849 +  NSRect inflatedRect = [mWindow frameRectForContentRect:rect];
  1.1850 +  return nsCocoaUtils::CocoaRectToGeckoRectDevPix(inflatedRect, backingScale).Size();
  1.1851 +
  1.1852 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntSize(0,0));
  1.1853 +}
  1.1854 +
  1.1855 +nsMenuBarX* nsCocoaWindow::GetMenuBar()
  1.1856 +{
  1.1857 +  return mMenuBar;
  1.1858 +}
  1.1859 +
  1.1860 +NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture)
  1.1861 +{
  1.1862 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1863 +
  1.1864 +  gRollupListener = nullptr;
  1.1865 +  
  1.1866 +  if (aDoCapture) {
  1.1867 +    if (![NSApp isActive]) {
  1.1868 +      // We need to capture mouse event if we aren't
  1.1869 +      // the active application. We only set this up when needed
  1.1870 +      // because they cause spurious mouse event after crash
  1.1871 +      // and gdb sessions. See bug 699538.
  1.1872 +      nsToolkit::GetToolkit()->RegisterForAllProcessMouseEvents();
  1.1873 +    }
  1.1874 +    gRollupListener = aListener;
  1.1875 +
  1.1876 +    // Sometimes more than one popup window can be visible at the same time
  1.1877 +    // (e.g. nested non-native context menus, or the test case (attachment
  1.1878 +    // 276885) for bmo bug 392389, which displays a non-native combo-box in a
  1.1879 +    // non-native popup window).  In these cases the "active" popup window should
  1.1880 +    // be the topmost -- the (nested) context menu the mouse is currently over,
  1.1881 +    // or the combo-box's drop-down list (when it's displayed).  But (among
  1.1882 +    // windows that have the same "level") OS X makes topmost the window that
  1.1883 +    // last received a mouse-down event, which may be incorrect (in the combo-
  1.1884 +    // box case, it makes topmost the window containing the combo-box).  So
  1.1885 +    // here we fiddle with a non-native popup window's level to make sure the
  1.1886 +    // "active" one is always above any other non-native popup windows that
  1.1887 +    // may be visible.
  1.1888 +    if (mWindow && (mWindowType == eWindowType_popup))
  1.1889 +      SetPopupWindowLevel();
  1.1890 +  } else {
  1.1891 +    nsToolkit::GetToolkit()->UnregisterAllProcessMouseEventHandlers();
  1.1892 +
  1.1893 +    // XXXndeakin this doesn't make sense.
  1.1894 +    // Why is the new window assumed to be a modal panel?
  1.1895 +    if (mWindow && (mWindowType == eWindowType_popup))
  1.1896 +      [mWindow setLevel:NSModalPanelWindowLevel];
  1.1897 +  }
  1.1898 +  
  1.1899 +  return NS_OK;
  1.1900 +
  1.1901 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1902 +}
  1.1903 +
  1.1904 +NS_IMETHODIMP nsCocoaWindow::GetAttention(int32_t aCycleCount)
  1.1905 +{
  1.1906 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1907 +
  1.1908 +  [NSApp requestUserAttention:NSInformationalRequest];
  1.1909 +  return NS_OK;
  1.1910 +
  1.1911 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1912 +}
  1.1913 +
  1.1914 +bool
  1.1915 +nsCocoaWindow::HasPendingInputEvent()
  1.1916 +{
  1.1917 +  return nsChildView::DoHasPendingInputEvent();
  1.1918 +}
  1.1919 +
  1.1920 +NS_IMETHODIMP nsCocoaWindow::SetWindowShadowStyle(int32_t aStyle)
  1.1921 +{
  1.1922 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.1923 +
  1.1924 +  if (!mWindow)
  1.1925 +    return NS_OK;
  1.1926 +
  1.1927 +  mShadowStyle = aStyle;
  1.1928 +  [mWindow setHasShadow:(aStyle != NS_STYLE_WINDOW_SHADOW_NONE)];
  1.1929 +  AdjustWindowShadow();
  1.1930 +  SetWindowBackgroundBlur();
  1.1931 +
  1.1932 +  return NS_OK;
  1.1933 +
  1.1934 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.1935 +}
  1.1936 +
  1.1937 +void nsCocoaWindow::SetShowsToolbarButton(bool aShow)
  1.1938 +{
  1.1939 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1940 +
  1.1941 +  if (mWindow)
  1.1942 +    [mWindow setShowsToolbarButton:aShow];
  1.1943 +
  1.1944 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1945 +}
  1.1946 +
  1.1947 +void nsCocoaWindow::SetShowsFullScreenButton(bool aShow)
  1.1948 +{
  1.1949 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1950 +
  1.1951 +  if (!mWindow || ![mWindow respondsToSelector:@selector(toggleFullScreen:)] ||
  1.1952 +      mUsesNativeFullScreen == aShow) {
  1.1953 +    return;
  1.1954 +  }
  1.1955 +
  1.1956 +  // If the window is currently in fullscreen mode, then we're going to
  1.1957 +  // transition out first, then set the collection behavior & toggle
  1.1958 +  // mUsesNativeFullScreen, then transtion back into fullscreen mode. This
  1.1959 +  // prevents us from getting into a conflicting state with MakeFullScreen
  1.1960 +  // where mUsesNativeFullScreen would lead us down the wrong path.
  1.1961 +  bool wasFullScreen = mFullScreen;
  1.1962 +
  1.1963 +  if (wasFullScreen) {
  1.1964 +    MakeFullScreen(false);
  1.1965 +  }
  1.1966 +
  1.1967 +  NSWindowCollectionBehavior newBehavior = [mWindow collectionBehavior];
  1.1968 +  if (aShow) {
  1.1969 +    newBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
  1.1970 +  } else {
  1.1971 +    newBehavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
  1.1972 +  }
  1.1973 +  [mWindow setCollectionBehavior:newBehavior];
  1.1974 +  mUsesNativeFullScreen = aShow;
  1.1975 +
  1.1976 +  if (wasFullScreen) {
  1.1977 +    MakeFullScreen(true);
  1.1978 +  }
  1.1979 +
  1.1980 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1981 +}
  1.1982 +
  1.1983 +void nsCocoaWindow::SetWindowAnimationType(nsIWidget::WindowAnimationType aType)
  1.1984 +{
  1.1985 +  mAnimationType = aType;
  1.1986 +}
  1.1987 +
  1.1988 +void
  1.1989 +nsCocoaWindow::SetDrawsTitle(bool aDrawTitle)
  1.1990 +{
  1.1991 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1992 +
  1.1993 +  [mWindow setWantsTitleDrawn:aDrawTitle];
  1.1994 +
  1.1995 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1996 +}
  1.1997 +
  1.1998 +NS_IMETHODIMP nsCocoaWindow::SetNonClientMargins(nsIntMargin &margins)
  1.1999 +{
  1.2000 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.2001 +
  1.2002 +  SetDrawsInTitlebar(margins.top == 0);
  1.2003 +
  1.2004 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.2005 +}
  1.2006 +
  1.2007 +NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, bool aActive)
  1.2008 +{
  1.2009 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.2010 +
  1.2011 +  if (!mWindow)
  1.2012 +    return NS_OK;
  1.2013 +
  1.2014 +  // If they pass a color with a complete transparent alpha component, use the
  1.2015 +  // native titlebar appearance.
  1.2016 +  if (NS_GET_A(aColor) == 0) {
  1.2017 +    [mWindow setTitlebarColor:nil forActiveWindow:(BOOL)aActive]; 
  1.2018 +  } else {
  1.2019 +    // Transform from sRGBA to monitor RGBA. This seems like it would make trying
  1.2020 +    // to match the system appearance lame, so probably we just shouldn't color 
  1.2021 +    // correct chrome.
  1.2022 +    if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
  1.2023 +      qcms_transform *transform = gfxPlatform::GetCMSRGBATransform();
  1.2024 +      if (transform) {
  1.2025 +        uint8_t color[3];
  1.2026 +        color[0] = NS_GET_R(aColor);
  1.2027 +        color[1] = NS_GET_G(aColor);
  1.2028 +        color[2] = NS_GET_B(aColor);
  1.2029 +        qcms_transform_data(transform, color, color, 1);
  1.2030 +        aColor = NS_RGB(color[0], color[1], color[2]);
  1.2031 +      }
  1.2032 +    }
  1.2033 +
  1.2034 +    [mWindow setTitlebarColor:[NSColor colorWithDeviceRed:NS_GET_R(aColor)/255.0
  1.2035 +                                                    green:NS_GET_G(aColor)/255.0
  1.2036 +                                                     blue:NS_GET_B(aColor)/255.0
  1.2037 +                                                    alpha:NS_GET_A(aColor)/255.0]
  1.2038 +              forActiveWindow:(BOOL)aActive];
  1.2039 +  }
  1.2040 +  return NS_OK;
  1.2041 +
  1.2042 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.2043 +}
  1.2044 +
  1.2045 +void nsCocoaWindow::SetDrawsInTitlebar(bool aState)
  1.2046 +{
  1.2047 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2048 +
  1.2049 +  if (mWindow)
  1.2050 +    [mWindow setDrawsContentsIntoWindowFrame:aState];
  1.2051 +
  1.2052 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2053 +}
  1.2054 +
  1.2055 +NS_IMETHODIMP nsCocoaWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
  1.2056 +                                                        uint32_t aNativeMessage,
  1.2057 +                                                        uint32_t aModifierFlags)
  1.2058 +{
  1.2059 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.2060 +
  1.2061 +  if (mPopupContentView)
  1.2062 +    return mPopupContentView->SynthesizeNativeMouseEvent(aPoint, aNativeMessage,
  1.2063 +                                                         aModifierFlags);
  1.2064 +
  1.2065 +  return NS_OK;
  1.2066 +
  1.2067 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.2068 +}
  1.2069 +
  1.2070 +gfxASurface* nsCocoaWindow::GetThebesSurface()
  1.2071 +{
  1.2072 +  if (mPopupContentView)
  1.2073 +    return mPopupContentView->GetThebesSurface();
  1.2074 +  return nullptr;
  1.2075 +}
  1.2076 +
  1.2077 +void nsCocoaWindow::SetPopupWindowLevel()
  1.2078 +{
  1.2079 +  if (!mWindow)
  1.2080 +    return;
  1.2081 +
  1.2082 +  // Floating popups are at the floating level and hide when the window is
  1.2083 +  // deactivated.
  1.2084 +  if (mPopupLevel == ePopupLevelFloating) {
  1.2085 +    [mWindow setLevel:NSFloatingWindowLevel];
  1.2086 +    [mWindow setHidesOnDeactivate:YES];
  1.2087 +  }
  1.2088 +  else {
  1.2089 +    // Otherwise, this is a top-level or parent popup. Parent popups always
  1.2090 +    // appear just above their parent and essentially ignore the level.
  1.2091 +    [mWindow setLevel:NSPopUpMenuWindowLevel];
  1.2092 +    [mWindow setHidesOnDeactivate:NO];
  1.2093 +  }
  1.2094 +}
  1.2095 +
  1.2096 +NS_IMETHODIMP
  1.2097 +nsCocoaWindow::NotifyIME(const IMENotification& aIMENotification)
  1.2098 +{
  1.2099 +  switch (aIMENotification.mMessage) {
  1.2100 +    case NOTIFY_IME_OF_FOCUS:
  1.2101 +      if (mInputContext.IsPasswordEditor()) {
  1.2102 +        TextInputHandler::EnableSecureEventInput();
  1.2103 +      }
  1.2104 +      return NS_OK;
  1.2105 +    case NOTIFY_IME_OF_BLUR:
  1.2106 +      // When we're going to be deactive, we must disable the secure event input
  1.2107 +      // mode, see the Carbon Event Manager Reference.
  1.2108 +      TextInputHandler::EnsureSecureEventInputDisabled();
  1.2109 +      return NS_OK;
  1.2110 +    default:
  1.2111 +      return NS_ERROR_NOT_IMPLEMENTED;
  1.2112 +  }
  1.2113 +}
  1.2114 +
  1.2115 +NS_IMETHODIMP_(void)
  1.2116 +nsCocoaWindow::SetInputContext(const InputContext& aContext,
  1.2117 +                               const InputContextAction& aAction)
  1.2118 +{
  1.2119 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2120 +
  1.2121 +  if (mWindow &&
  1.2122 +      [mWindow firstResponder] == mWindow &&
  1.2123 +      [mWindow isKeyWindow] &&
  1.2124 +      [[NSApplication sharedApplication] isActive]) {
  1.2125 +    if (aContext.IsPasswordEditor()) {
  1.2126 +      TextInputHandler::EnableSecureEventInput();
  1.2127 +    } else {
  1.2128 +      TextInputHandler::EnsureSecureEventInputDisabled();
  1.2129 +    }
  1.2130 +  }
  1.2131 +  mInputContext = aContext;
  1.2132 +
  1.2133 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2134 +}
  1.2135 +
  1.2136 +NS_IMETHODIMP_(bool)
  1.2137 +nsCocoaWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
  1.2138 +                                       const WidgetKeyboardEvent& aEvent,
  1.2139 +                                       DoCommandCallback aCallback,
  1.2140 +                                       void* aCallbackData)
  1.2141 +{
  1.2142 +  NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
  1.2143 +  return keyBindings->Execute(aEvent, aCallback, aCallbackData);
  1.2144 +}
  1.2145 +
  1.2146 +
  1.2147 +@implementation WindowDelegate
  1.2148 +
  1.2149 +// We try to find a gecko menu bar to paint. If one does not exist, just paint
  1.2150 +// the application menu by itself so that a window doesn't have some other
  1.2151 +// window's menu bar.
  1.2152 ++ (void)paintMenubarForWindow:(NSWindow*)aWindow
  1.2153 +{
  1.2154 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2155 +
  1.2156 +  // make sure we only act on windows that have this kind of
  1.2157 +  // object as a delegate
  1.2158 +  id windowDelegate = [aWindow delegate];
  1.2159 +  if ([windowDelegate class] != [self class])
  1.2160 +    return;
  1.2161 +
  1.2162 +  nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget];
  1.2163 +  NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!");
  1.2164 +  
  1.2165 +  nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar();
  1.2166 +  if (geckoMenuBar) {
  1.2167 +    geckoMenuBar->Paint();
  1.2168 +  }
  1.2169 +  else {
  1.2170 +    // sometimes we don't have a native application menu early in launching
  1.2171 +    if (!sApplicationMenu)
  1.2172 +      return;
  1.2173 +
  1.2174 +    NSMenu* mainMenu = [NSApp mainMenu];
  1.2175 +    NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
  1.2176 +
  1.2177 +    // Create a new menu bar.
  1.2178 +    // We create a GeckoNSMenu because all menu bar NSMenu objects should use that subclass for
  1.2179 +    // key handling reasons.
  1.2180 +    GeckoNSMenu* newMenuBar = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
  1.2181 +
  1.2182 +    // move the application menu from the existing menu bar to the new one
  1.2183 +    NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
  1.2184 +    [mainMenu removeItemAtIndex:0];
  1.2185 +    [newMenuBar insertItem:firstMenuItem atIndex:0];
  1.2186 +    [firstMenuItem release];
  1.2187 +
  1.2188 +    // set our new menu bar as the main menu
  1.2189 +    [NSApp setMainMenu:newMenuBar];
  1.2190 +    [newMenuBar release];
  1.2191 +  }
  1.2192 +
  1.2193 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2194 +}
  1.2195 +
  1.2196 +- (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind
  1.2197 +{
  1.2198 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1.2199 +
  1.2200 +  [super init];
  1.2201 +  mGeckoWindow = geckoWind;
  1.2202 +  mToplevelActiveState = false;
  1.2203 +  mHasEverBeenZoomed = false;
  1.2204 +  return self;
  1.2205 +
  1.2206 +  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1.2207 +}
  1.2208 +
  1.2209 +- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
  1.2210 +{
  1.2211 +  RollUpPopups();
  1.2212 +  
  1.2213 +  return proposedFrameSize;
  1.2214 +}
  1.2215 +
  1.2216 +- (void)windowDidResize:(NSNotification *)aNotification
  1.2217 +{
  1.2218 +  BaseWindow* window = [aNotification object];
  1.2219 +  [window updateTrackingArea];
  1.2220 +
  1.2221 +  if (!mGeckoWindow)
  1.2222 +    return;
  1.2223 +
  1.2224 +  // Resizing might have changed our zoom state.
  1.2225 +  mGeckoWindow->DispatchSizeModeEvent();
  1.2226 +  mGeckoWindow->ReportSizeEvent();
  1.2227 +}
  1.2228 +
  1.2229 +- (void)windowDidChangeScreen:(NSNotification *)aNotification
  1.2230 +{
  1.2231 +  if (!mGeckoWindow)
  1.2232 +    return;
  1.2233 +
  1.2234 +  // Because of Cocoa's peculiar treatment of zero-size windows (see comments
  1.2235 +  // at GetBackingScaleFactor() above), we sometimes have a situation where
  1.2236 +  // our concept of backing scale (based on the screen where the zero-sized
  1.2237 +  // window is positioned) differs from Cocoa's idea (always based on the
  1.2238 +  // Retina screen, AFAICT, even when an external non-Retina screen is the
  1.2239 +  // primary display).
  1.2240 +  //
  1.2241 +  // As a result, if the window was created with zero size on an external
  1.2242 +  // display, but then made visible on the (secondary) Retina screen, we
  1.2243 +  // will *not* get a windowDidChangeBackingProperties notification for it.
  1.2244 +  // This leads to an incorrect GetDefaultScale(), and widget coordinate
  1.2245 +  // confusion, as per bug 853252.
  1.2246 +  //
  1.2247 +  // To work around this, we check for a backing scale mismatch when we
  1.2248 +  // receive a windowDidChangeScreen notification, as we will receive this
  1.2249 +  // even if Cocoa was already treating the zero-size window as having
  1.2250 +  // Retina backing scale.
  1.2251 +  NSWindow *window = (NSWindow *)[aNotification object];
  1.2252 +  if ([window respondsToSelector:@selector(backingScaleFactor)]) {
  1.2253 +    if (GetBackingScaleFactor(window) != mGeckoWindow->BackingScaleFactor()) {
  1.2254 +      mGeckoWindow->BackingScaleFactorChanged();
  1.2255 +    }
  1.2256 +  }
  1.2257 +
  1.2258 +  mGeckoWindow->ReportMoveEvent();
  1.2259 +}
  1.2260 +
  1.2261 +// Lion's full screen mode will bypass our internal fullscreen tracking, so
  1.2262 +// we need to catch it when we transition and call our own methods, which in
  1.2263 +// turn will fire "fullscreen" events.
  1.2264 +- (void)windowDidEnterFullScreen:(NSNotification *)notification
  1.2265 +{
  1.2266 +  if (!mGeckoWindow) {
  1.2267 +    return;
  1.2268 +  }
  1.2269 +
  1.2270 +  mGeckoWindow->EnteredFullScreen(true);
  1.2271 +}
  1.2272 +
  1.2273 +- (void)windowDidExitFullScreen:(NSNotification *)notification
  1.2274 +{
  1.2275 +  if (!mGeckoWindow) {
  1.2276 +    return;
  1.2277 +  }
  1.2278 +
  1.2279 +  mGeckoWindow->EnteredFullScreen(false);
  1.2280 +}
  1.2281 +
  1.2282 +- (void)windowDidFailToEnterFullScreen:(NSWindow *)window
  1.2283 +{
  1.2284 +  if (!mGeckoWindow) {
  1.2285 +    return;
  1.2286 +  }
  1.2287 +
  1.2288 +  mGeckoWindow->EnteredFullScreen(false);
  1.2289 +}
  1.2290 +
  1.2291 +- (void)windowDidFailToExitFullScreen:(NSWindow *)window
  1.2292 +{
  1.2293 +  if (!mGeckoWindow) {
  1.2294 +    return;
  1.2295 +  }
  1.2296 +
  1.2297 +  mGeckoWindow->EnteredFullScreen(true);
  1.2298 +}
  1.2299 +
  1.2300 +- (void)windowDidBecomeMain:(NSNotification *)aNotification
  1.2301 +{
  1.2302 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2303 +
  1.2304 +  RollUpPopups();
  1.2305 +  ChildViewMouseTracker::ReEvaluateMouseEnterState();
  1.2306 +
  1.2307 +  // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
  1.2308 +  // app modally. If one of those is up then we want it to retain its menu bar.
  1.2309 +  if ([NSApp _isRunningAppModal])
  1.2310 +    return;
  1.2311 +  NSWindow* window = [aNotification object];
  1.2312 +  if (window)
  1.2313 +    [WindowDelegate paintMenubarForWindow:window];
  1.2314 +
  1.2315 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2316 +}
  1.2317 +
  1.2318 +- (void)windowDidResignMain:(NSNotification *)aNotification
  1.2319 +{
  1.2320 +  RollUpPopups();
  1.2321 +  ChildViewMouseTracker::ReEvaluateMouseEnterState();
  1.2322 +
  1.2323 +  // [NSApp _isRunningAppModal] will return true if we're running an OS dialog
  1.2324 +  // app modally. If one of those is up then we want it to retain its menu bar.
  1.2325 +  if ([NSApp _isRunningAppModal])
  1.2326 +    return;
  1.2327 +  nsRefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
  1.2328 +  if (hiddenWindowMenuBar) {
  1.2329 +    // printf("painting hidden window menu bar due to window losing main status\n");
  1.2330 +    hiddenWindowMenuBar->Paint();
  1.2331 +  }
  1.2332 +}
  1.2333 +
  1.2334 +- (void)windowDidBecomeKey:(NSNotification *)aNotification
  1.2335 +{
  1.2336 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2337 +
  1.2338 +  RollUpPopups();
  1.2339 +  ChildViewMouseTracker::ReEvaluateMouseEnterState();
  1.2340 +
  1.2341 +  NSWindow* window = [aNotification object];
  1.2342 +  if ([window isSheet])
  1.2343 +    [WindowDelegate paintMenubarForWindow:window];
  1.2344 +
  1.2345 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2346 +}
  1.2347 +
  1.2348 +- (void)windowDidResignKey:(NSNotification *)aNotification
  1.2349 +{
  1.2350 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2351 +
  1.2352 +  RollUpPopups();
  1.2353 +  ChildViewMouseTracker::ReEvaluateMouseEnterState();
  1.2354 +
  1.2355 +  // If a sheet just resigned key then we should paint the menu bar
  1.2356 +  // for whatever window is now main.
  1.2357 +  NSWindow* window = [aNotification object];
  1.2358 +  if ([window isSheet])
  1.2359 +    [WindowDelegate paintMenubarForWindow:[NSApp mainWindow]];
  1.2360 +
  1.2361 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2362 +}
  1.2363 +
  1.2364 +- (void)windowWillMove:(NSNotification *)aNotification
  1.2365 +{
  1.2366 +  RollUpPopups();
  1.2367 +}
  1.2368 +
  1.2369 +- (void)windowDidMove:(NSNotification *)aNotification
  1.2370 +{
  1.2371 +  if (mGeckoWindow)
  1.2372 +    mGeckoWindow->ReportMoveEvent();
  1.2373 +}
  1.2374 +
  1.2375 +- (BOOL)windowShouldClose:(id)sender
  1.2376 +{
  1.2377 +  nsIWidgetListener* listener = mGeckoWindow ? mGeckoWindow->GetWidgetListener() : nullptr;
  1.2378 +  if (listener)
  1.2379 +    listener->RequestWindowClose(mGeckoWindow);
  1.2380 +  return NO; // gecko will do it
  1.2381 +}
  1.2382 +
  1.2383 +- (void)windowWillClose:(NSNotification *)aNotification
  1.2384 +{
  1.2385 +  RollUpPopups();
  1.2386 +}
  1.2387 +
  1.2388 +- (void)windowWillMiniaturize:(NSNotification *)aNotification
  1.2389 +{
  1.2390 +  RollUpPopups();
  1.2391 +}
  1.2392 +
  1.2393 +- (void)windowDidMiniaturize:(NSNotification *)aNotification
  1.2394 +{
  1.2395 +  if (mGeckoWindow)
  1.2396 +    mGeckoWindow->DispatchSizeModeEvent();
  1.2397 +}
  1.2398 +
  1.2399 +- (void)windowDidDeminiaturize:(NSNotification *)aNotification
  1.2400 +{
  1.2401 +  if (mGeckoWindow)
  1.2402 +    mGeckoWindow->DispatchSizeModeEvent();
  1.2403 +}
  1.2404 +
  1.2405 +- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)proposedFrame
  1.2406 +{
  1.2407 +  if (!mHasEverBeenZoomed && [window isZoomed])
  1.2408 +    return NO; // See bug 429954.
  1.2409 +
  1.2410 +  mHasEverBeenZoomed = YES;
  1.2411 +  return YES;
  1.2412 +}
  1.2413 +
  1.2414 +- (void)didEndSheet:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
  1.2415 +{
  1.2416 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2417 +
  1.2418 +  // Note: 'contextInfo' (if it is set) is the window that is the parent of
  1.2419 +  // the sheet.  The value of contextInfo is determined in
  1.2420 +  // nsCocoaWindow::Show().  If it's set, 'contextInfo' is always the top-
  1.2421 +  // level window, not another sheet itself.  But 'contextInfo' is nil if
  1.2422 +  // our parent window is also a sheet -- in that case we shouldn't send
  1.2423 +  // the top-level window any activate events (because it's our parent
  1.2424 +  // window that needs to get these events, not the top-level window).
  1.2425 +  [TopLevelWindowData deactivateInWindow:sheet];
  1.2426 +  [sheet orderOut:self];
  1.2427 +  if (contextInfo)
  1.2428 +    [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo];
  1.2429 +
  1.2430 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2431 +}
  1.2432 +
  1.2433 +- (void)windowDidChangeBackingProperties:(NSNotification *)aNotification
  1.2434 +{
  1.2435 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2436 +
  1.2437 +  NSWindow *window = (NSWindow *)[aNotification object];
  1.2438 +
  1.2439 +  if ([window respondsToSelector:@selector(backingScaleFactor)]) {
  1.2440 +    CGFloat oldFactor =
  1.2441 +      [[[aNotification userInfo]
  1.2442 +         objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
  1.2443 +    if ([window backingScaleFactor] != oldFactor) {
  1.2444 +      mGeckoWindow->BackingScaleFactorChanged();
  1.2445 +    }
  1.2446 +  }
  1.2447 +
  1.2448 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2449 +}
  1.2450 +
  1.2451 +- (nsCocoaWindow*)geckoWidget
  1.2452 +{
  1.2453 +  return mGeckoWindow;
  1.2454 +}
  1.2455 +
  1.2456 +- (bool)toplevelActiveState
  1.2457 +{
  1.2458 +  return mToplevelActiveState;
  1.2459 +}
  1.2460 +
  1.2461 +- (void)sendToplevelActivateEvents
  1.2462 +{
  1.2463 +  if (!mToplevelActiveState && mGeckoWindow) {
  1.2464 +    nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener();
  1.2465 +    if (listener)
  1.2466 +      listener->WindowActivated();
  1.2467 +    mToplevelActiveState = true;
  1.2468 +  }
  1.2469 +}
  1.2470 +
  1.2471 +- (void)sendToplevelDeactivateEvents
  1.2472 +{
  1.2473 +  if (mToplevelActiveState && mGeckoWindow) {
  1.2474 +    nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener();
  1.2475 +    if (listener)
  1.2476 +      listener->WindowDeactivated();
  1.2477 +    mToplevelActiveState = false;
  1.2478 +  }
  1.2479 +}
  1.2480 +
  1.2481 +@end
  1.2482 +
  1.2483 +static float
  1.2484 +GetDPI(NSWindow* aWindow)
  1.2485 +{
  1.2486 +  NSScreen* screen = [aWindow screen];
  1.2487 +  if (!screen)
  1.2488 +    return 96.0f;
  1.2489 +
  1.2490 +  CGDirectDisplayID displayID =
  1.2491 +    [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
  1.2492 +  CGFloat heightMM = ::CGDisplayScreenSize(displayID).height;
  1.2493 +  size_t heightPx = ::CGDisplayPixelsHigh(displayID);
  1.2494 +  CGFloat scaleFactor = [aWindow userSpaceScaleFactor];
  1.2495 +  if (scaleFactor < 0.01 || heightMM < 1 || heightPx < 1) {
  1.2496 +    // Something extremely bogus is going on
  1.2497 +    return 96.0f;
  1.2498 +  }
  1.2499 +
  1.2500 +  // Currently we don't do our own scaling to take account
  1.2501 +  // of userSpaceScaleFactor, so every "pixel" we draw is actually
  1.2502 +  // userSpaceScaleFactor screen pixels. So divide the screen height
  1.2503 +  // by userSpaceScaleFactor to get the number of "device pixels"
  1.2504 +  // available.
  1.2505 +  float dpi = (heightPx / scaleFactor) / (heightMM / MM_PER_INCH_FLOAT);
  1.2506 +
  1.2507 +  // Account for HiDPI mode where Cocoa's "points" do not correspond to real
  1.2508 +  // device pixels
  1.2509 +  CGFloat backingScale = GetBackingScaleFactor(aWindow);
  1.2510 +
  1.2511 +  return dpi * backingScale;
  1.2512 +}
  1.2513 +
  1.2514 +@interface NSView(FrameViewMethodSwizzling)
  1.2515 +- (NSPoint)FrameView__closeButtonOrigin;
  1.2516 +- (NSPoint)FrameView__fullScreenButtonOrigin;
  1.2517 +@end
  1.2518 +
  1.2519 +@implementation NSView(FrameViewMethodSwizzling)
  1.2520 +
  1.2521 +- (NSPoint)FrameView__closeButtonOrigin
  1.2522 +{
  1.2523 +  NSPoint defaultPosition = [self FrameView__closeButtonOrigin];
  1.2524 +  if ([[self window] isKindOfClass:[ToolbarWindow class]]) {
  1.2525 +    return [(ToolbarWindow*)[self window] windowButtonsPositionWithDefaultPosition:defaultPosition];
  1.2526 +  }
  1.2527 +  return defaultPosition;
  1.2528 +}
  1.2529 +
  1.2530 +- (NSPoint)FrameView__fullScreenButtonOrigin
  1.2531 +{
  1.2532 +  NSPoint defaultPosition = [self FrameView__fullScreenButtonOrigin];
  1.2533 +  if ([[self window] isKindOfClass:[ToolbarWindow class]]) {
  1.2534 +    return [(ToolbarWindow*)[self window] fullScreenButtonPositionWithDefaultPosition:defaultPosition];
  1.2535 +  }
  1.2536 +  return defaultPosition;
  1.2537 +}
  1.2538 +
  1.2539 +@end
  1.2540 +
  1.2541 +static NSMutableSet *gSwizzledFrameViewClasses = nil;
  1.2542 +
  1.2543 +@interface NSWindow(PrivateSetNeedsDisplayInRectMethod)
  1.2544 + - (void)_setNeedsDisplayInRect:(NSRect)aRect;
  1.2545 +@end
  1.2546 +
  1.2547 +@interface BaseWindow(Private)
  1.2548 +- (void)removeTrackingArea;
  1.2549 +- (void)cursorUpdated:(NSEvent*)aEvent;
  1.2550 +- (void)updateContentViewSize;
  1.2551 +- (void)reflowTitlebarElements;
  1.2552 +@end
  1.2553 +
  1.2554 +@implementation BaseWindow
  1.2555 +
  1.2556 +// The frame of a window is implemented using undocumented NSView subclasses.
  1.2557 +// We offset the window buttons by overriding the methods _closeButtonOrigin
  1.2558 +// and _fullScreenButtonOrigin on these frame view classes. The class which is
  1.2559 +// used for a window is determined in the window's frameViewClassForStyleMask:
  1.2560 +// method, so this is where we make sure that we have swizzled the method on
  1.2561 +// all encountered classes.
  1.2562 ++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask
  1.2563 +{
  1.2564 +  Class frameViewClass = [super frameViewClassForStyleMask:styleMask];
  1.2565 +
  1.2566 +  if (!gSwizzledFrameViewClasses) {
  1.2567 +    gSwizzledFrameViewClasses = [[NSMutableSet setWithCapacity:3] retain];
  1.2568 +    if (!gSwizzledFrameViewClasses) {
  1.2569 +      return frameViewClass;
  1.2570 +    }
  1.2571 +  }
  1.2572 +
  1.2573 +  static IMP our_closeButtonOrigin =
  1.2574 +    class_getMethodImplementation([NSView class],
  1.2575 +                                  @selector(FrameView__closeButtonOrigin));
  1.2576 +  static IMP our_fullScreenButtonOrigin =
  1.2577 +    class_getMethodImplementation([NSView class],
  1.2578 +                                  @selector(FrameView__fullScreenButtonOrigin));
  1.2579 +
  1.2580 +  if (![gSwizzledFrameViewClasses containsObject:frameViewClass]) {
  1.2581 +    // Either of these methods might be implemented in both a subclass of
  1.2582 +    // NSFrameView and one of its own subclasses.  Which means that if we
  1.2583 +    // aren't careful we might end up swizzling the same method twice.
  1.2584 +    // Since method swizzling involves swapping pointers, this would break
  1.2585 +    // things.
  1.2586 +    IMP _closeButtonOrigin =
  1.2587 +      class_getMethodImplementation(frameViewClass,
  1.2588 +                                    @selector(_closeButtonOrigin));
  1.2589 +    if (_closeButtonOrigin && _closeButtonOrigin != our_closeButtonOrigin) {
  1.2590 +      nsToolkit::SwizzleMethods(frameViewClass, @selector(_closeButtonOrigin),
  1.2591 +                                @selector(FrameView__closeButtonOrigin));
  1.2592 +    }
  1.2593 +    IMP _fullScreenButtonOrigin =
  1.2594 +      class_getMethodImplementation(frameViewClass,
  1.2595 +                                    @selector(_fullScreenButtonOrigin));
  1.2596 +    if (_fullScreenButtonOrigin &&
  1.2597 +        _fullScreenButtonOrigin != our_fullScreenButtonOrigin) {
  1.2598 +      nsToolkit::SwizzleMethods(frameViewClass, @selector(_fullScreenButtonOrigin),
  1.2599 +                                @selector(FrameView__fullScreenButtonOrigin));
  1.2600 +    }
  1.2601 +    [gSwizzledFrameViewClasses addObject:frameViewClass];
  1.2602 +  }
  1.2603 +
  1.2604 +  return frameViewClass;
  1.2605 +}
  1.2606 +
  1.2607 +- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
  1.2608 +{
  1.2609 +  mDrawsIntoWindowFrame = NO;
  1.2610 +  [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag];
  1.2611 +  mState = nil;
  1.2612 +  mActiveTitlebarColor = nil;
  1.2613 +  mInactiveTitlebarColor = nil;
  1.2614 +  mScheduledShadowInvalidation = NO;
  1.2615 +  mDisabledNeedsDisplay = NO;
  1.2616 +  mDPI = GetDPI(self);
  1.2617 +  mTrackingArea = nil;
  1.2618 +  mBeingShown = NO;
  1.2619 +  mDrawTitle = NO;
  1.2620 +  [self updateTrackingArea];
  1.2621 +
  1.2622 +  return self;
  1.2623 +}
  1.2624 +
  1.2625 +- (void)setBeingShown:(BOOL)aValue
  1.2626 +{
  1.2627 +  mBeingShown = aValue;
  1.2628 +}
  1.2629 +
  1.2630 +- (BOOL)isBeingShown
  1.2631 +{
  1.2632 +  return mBeingShown;
  1.2633 +}
  1.2634 +
  1.2635 +- (BOOL)isVisibleOrBeingShown
  1.2636 +{
  1.2637 +  return [super isVisible] || mBeingShown;
  1.2638 +}
  1.2639 +
  1.2640 +- (void)disableSetNeedsDisplay
  1.2641 +{
  1.2642 +  mDisabledNeedsDisplay = YES;
  1.2643 +}
  1.2644 +
  1.2645 +- (void)enableSetNeedsDisplay
  1.2646 +{
  1.2647 +  mDisabledNeedsDisplay = NO;
  1.2648 +}
  1.2649 +
  1.2650 +- (void)dealloc
  1.2651 +{
  1.2652 +  [mActiveTitlebarColor release];
  1.2653 +  [mInactiveTitlebarColor release];
  1.2654 +  [self removeTrackingArea];
  1.2655 +  ChildViewMouseTracker::OnDestroyWindow(self);
  1.2656 +  [super dealloc];
  1.2657 +}
  1.2658 +
  1.2659 +static const NSString* kStateTitleKey = @"title";
  1.2660 +static const NSString* kStateDrawsContentsIntoWindowFrameKey = @"drawsContentsIntoWindowFrame";
  1.2661 +static const NSString* kStateActiveTitlebarColorKey = @"activeTitlebarColor";
  1.2662 +static const NSString* kStateInactiveTitlebarColorKey = @"inactiveTitlebarColor";
  1.2663 +static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
  1.2664 +
  1.2665 +- (void)importState:(NSDictionary*)aState
  1.2666 +{
  1.2667 +  [self setTitle:[aState objectForKey:kStateTitleKey]];
  1.2668 +  [self setDrawsContentsIntoWindowFrame:[[aState objectForKey:kStateDrawsContentsIntoWindowFrameKey] boolValue]];
  1.2669 +  [self setTitlebarColor:[aState objectForKey:kStateActiveTitlebarColorKey] forActiveWindow:YES];
  1.2670 +  [self setTitlebarColor:[aState objectForKey:kStateInactiveTitlebarColorKey] forActiveWindow:NO];
  1.2671 +  [self setShowsToolbarButton:[[aState objectForKey:kStateShowsToolbarButton] boolValue]];
  1.2672 +}
  1.2673 +
  1.2674 +- (NSMutableDictionary*)exportState
  1.2675 +{
  1.2676 +  NSMutableDictionary* state = [NSMutableDictionary dictionaryWithCapacity:10];
  1.2677 +  [state setObject:[self title] forKey:kStateTitleKey];
  1.2678 +  [state setObject:[NSNumber numberWithBool:[self drawsContentsIntoWindowFrame]]
  1.2679 +            forKey:kStateDrawsContentsIntoWindowFrameKey];
  1.2680 +  NSColor* activeTitlebarColor = [self titlebarColorForActiveWindow:YES];
  1.2681 +  if (activeTitlebarColor) {
  1.2682 +    [state setObject:activeTitlebarColor forKey:kStateActiveTitlebarColorKey];
  1.2683 +  }
  1.2684 +  NSColor* inactiveTitlebarColor = [self titlebarColorForActiveWindow:NO];
  1.2685 +  if (inactiveTitlebarColor) {
  1.2686 +    [state setObject:inactiveTitlebarColor forKey:kStateInactiveTitlebarColorKey];
  1.2687 +  }
  1.2688 +  [state setObject:[NSNumber numberWithBool:[self showsToolbarButton]]
  1.2689 +            forKey:kStateShowsToolbarButton];
  1.2690 +  return state;
  1.2691 +}
  1.2692 +
  1.2693 +- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
  1.2694 +{
  1.2695 +  bool changed = (aState != mDrawsIntoWindowFrame);
  1.2696 +  mDrawsIntoWindowFrame = aState;
  1.2697 +  if (changed) {
  1.2698 +    [self updateContentViewSize];
  1.2699 +    [self reflowTitlebarElements];
  1.2700 +  }
  1.2701 +}
  1.2702 +
  1.2703 +- (BOOL)drawsContentsIntoWindowFrame
  1.2704 +{
  1.2705 +  return mDrawsIntoWindowFrame;
  1.2706 +}
  1.2707 +
  1.2708 +- (void)setWantsTitleDrawn:(BOOL)aDrawTitle
  1.2709 +{
  1.2710 +  mDrawTitle = aDrawTitle;
  1.2711 +}
  1.2712 +
  1.2713 +- (BOOL)wantsTitleDrawn
  1.2714 +{
  1.2715 +  return mDrawTitle;
  1.2716 +}
  1.2717 +
  1.2718 +// Pass nil here to get the default appearance.
  1.2719 +- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
  1.2720 +{
  1.2721 +  [aColor retain];
  1.2722 +  if (aActive) {
  1.2723 +    [mActiveTitlebarColor release];
  1.2724 +    mActiveTitlebarColor = aColor;
  1.2725 +  } else {
  1.2726 +    [mInactiveTitlebarColor release];
  1.2727 +    mInactiveTitlebarColor = aColor;
  1.2728 +  }
  1.2729 +}
  1.2730 +
  1.2731 +- (NSColor*)titlebarColorForActiveWindow:(BOOL)aActive
  1.2732 +{
  1.2733 +  return aActive ? mActiveTitlebarColor : mInactiveTitlebarColor;
  1.2734 +}
  1.2735 +
  1.2736 +- (void)deferredInvalidateShadow
  1.2737 +{
  1.2738 +  if (mScheduledShadowInvalidation || [self isOpaque] || ![self hasShadow])
  1.2739 +    return;
  1.2740 +
  1.2741 +  [self performSelector:@selector(invalidateShadow) withObject:nil afterDelay:0];
  1.2742 +  mScheduledShadowInvalidation = YES;
  1.2743 +}
  1.2744 +
  1.2745 +- (void)invalidateShadow
  1.2746 +{
  1.2747 +  [super invalidateShadow];
  1.2748 +  mScheduledShadowInvalidation = NO;
  1.2749 +}
  1.2750 +
  1.2751 +- (float)getDPI
  1.2752 +{
  1.2753 +  return mDPI;
  1.2754 +}
  1.2755 +
  1.2756 +- (NSView*)trackingAreaView
  1.2757 +{
  1.2758 +  NSView* contentView = [self contentView];
  1.2759 +  return [contentView superview] ? [contentView superview] : contentView;
  1.2760 +}
  1.2761 +
  1.2762 +- (ChildView*)mainChildView
  1.2763 +{
  1.2764 +  NSView *contentView = [self contentView];
  1.2765 +  // A PopupWindow's contentView is a ChildView object.
  1.2766 +  if ([contentView isKindOfClass:[ChildView class]]) {
  1.2767 +    return (ChildView*)contentView;
  1.2768 +  }
  1.2769 +  NSView* lastView = [[contentView subviews] lastObject];
  1.2770 +  if ([lastView isKindOfClass:[ChildView class]]) {
  1.2771 +    return (ChildView*)lastView;
  1.2772 +  }
  1.2773 +  return nil;
  1.2774 +}
  1.2775 +
  1.2776 +- (void)removeTrackingArea
  1.2777 +{
  1.2778 +  if (mTrackingArea) {
  1.2779 +    [[self trackingAreaView] removeTrackingArea:mTrackingArea];
  1.2780 +    [mTrackingArea release];
  1.2781 +    mTrackingArea = nil;
  1.2782 +  }
  1.2783 +}
  1.2784 +
  1.2785 +- (void)updateTrackingArea
  1.2786 +{
  1.2787 +  [self removeTrackingArea];
  1.2788 +
  1.2789 +  NSView* view = [self trackingAreaView];
  1.2790 +  const NSTrackingAreaOptions options =
  1.2791 +    NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways;
  1.2792 +  mTrackingArea = [[NSTrackingArea alloc] initWithRect:[view bounds]
  1.2793 +                                               options:options
  1.2794 +                                                 owner:self
  1.2795 +                                              userInfo:nil];
  1.2796 +  [view addTrackingArea:mTrackingArea];
  1.2797 +}
  1.2798 +
  1.2799 +- (void)mouseEntered:(NSEvent*)aEvent
  1.2800 +{
  1.2801 +  ChildViewMouseTracker::MouseEnteredWindow(aEvent);
  1.2802 +}
  1.2803 +
  1.2804 +- (void)mouseExited:(NSEvent*)aEvent
  1.2805 +{
  1.2806 +  ChildViewMouseTracker::MouseExitedWindow(aEvent);
  1.2807 +}
  1.2808 +
  1.2809 +- (void)mouseMoved:(NSEvent*)aEvent
  1.2810 +{
  1.2811 +  ChildViewMouseTracker::MouseMoved(aEvent);
  1.2812 +}
  1.2813 +
  1.2814 +- (void)cursorUpdated:(NSEvent*)aEvent
  1.2815 +{
  1.2816 +  // Nothing to do here, but NSTrackingArea wants us to implement this method.
  1.2817 +}
  1.2818 +
  1.2819 +- (void)_setNeedsDisplayInRect:(NSRect)aRect
  1.2820 +{
  1.2821 +  // Prevent unnecessary invalidations due to moving NSViews (e.g. for plugins)
  1.2822 +  if (!mDisabledNeedsDisplay) {
  1.2823 +    // This method is only called by Cocoa, so when we're here, we know that
  1.2824 +    // it's available and don't need to check whether our superclass responds
  1.2825 +    // to the selector.
  1.2826 +    [super _setNeedsDisplayInRect:aRect];
  1.2827 +  }
  1.2828 +}
  1.2829 +
  1.2830 +- (void)updateContentViewSize
  1.2831 +{
  1.2832 +  NSRect rect = [self contentRectForFrameRect:[self frame]];
  1.2833 +  [[self contentView] setFrameSize:rect.size];
  1.2834 +}
  1.2835 +
  1.2836 +// Possibly move the titlebar buttons.
  1.2837 +- (void)reflowTitlebarElements
  1.2838 +{
  1.2839 +  NSView *frameView = [[self contentView] superview];
  1.2840 +  if ([frameView respondsToSelector:@selector(_tileTitlebarAndRedisplay:)]) {
  1.2841 +    [frameView _tileTitlebarAndRedisplay:NO];
  1.2842 +  }
  1.2843 +}
  1.2844 +
  1.2845 +// Override methods that translate between content rect and frame rect.
  1.2846 +- (NSRect)contentRectForFrameRect:(NSRect)aRect
  1.2847 +{
  1.2848 +  if ([self drawsContentsIntoWindowFrame]) {
  1.2849 +    return aRect;
  1.2850 +  }
  1.2851 +  return [super contentRectForFrameRect:aRect];
  1.2852 +}
  1.2853 +
  1.2854 +- (NSRect)contentRectForFrameRect:(NSRect)aRect styleMask:(NSUInteger)aMask
  1.2855 +{
  1.2856 +  if ([self drawsContentsIntoWindowFrame]) {
  1.2857 +    return aRect;
  1.2858 +  }
  1.2859 +  if ([super respondsToSelector:@selector(contentRectForFrameRect:styleMask:)]) {
  1.2860 +    return [super contentRectForFrameRect:aRect styleMask:aMask];
  1.2861 +  } else {
  1.2862 +    return [NSWindow contentRectForFrameRect:aRect styleMask:aMask];
  1.2863 +  }
  1.2864 +}
  1.2865 +
  1.2866 +- (NSRect)frameRectForContentRect:(NSRect)aRect
  1.2867 +{
  1.2868 +  if ([self drawsContentsIntoWindowFrame]) {
  1.2869 +    return aRect;
  1.2870 +  }
  1.2871 +  return [super frameRectForContentRect:aRect];
  1.2872 +}
  1.2873 +
  1.2874 +- (NSRect)frameRectForContentRect:(NSRect)aRect styleMask:(NSUInteger)aMask
  1.2875 +{
  1.2876 +  if ([self drawsContentsIntoWindowFrame]) {
  1.2877 +    return aRect;
  1.2878 +  }
  1.2879 +  if ([super respondsToSelector:@selector(frameRectForContentRect:styleMask:)]) {
  1.2880 +    return [super frameRectForContentRect:aRect styleMask:aMask];
  1.2881 +  } else {
  1.2882 +    return [NSWindow frameRectForContentRect:aRect styleMask:aMask];
  1.2883 +  }
  1.2884 +}
  1.2885 +
  1.2886 +- (void)setContentView:(NSView*)aView
  1.2887 +{
  1.2888 +  [super setContentView:aView];
  1.2889 +
  1.2890 +  // Now move the contentView to the bottommost layer so that it's guaranteed
  1.2891 +  // to be under the window buttons.
  1.2892 +  NSView* frameView = [aView superview];
  1.2893 +  [aView removeFromSuperview];
  1.2894 +  [frameView addSubview:aView positioned:NSWindowBelow relativeTo:nil];
  1.2895 +}
  1.2896 +
  1.2897 +- (NSArray*)titlebarControls
  1.2898 +{
  1.2899 +  // Return all subviews of the frameView which are not the content view.
  1.2900 +  NSView* frameView = [[self contentView] superview];
  1.2901 +  NSMutableArray* array = [[[frameView subviews] mutableCopy] autorelease];
  1.2902 +  [array removeObject:[self contentView]];
  1.2903 +  return array;
  1.2904 +}
  1.2905 +
  1.2906 +- (BOOL)respondsToSelector:(SEL)aSelector
  1.2907 +{
  1.2908 +  // Claim the window doesn't respond to this so that the system
  1.2909 +  // doesn't steal keyboard equivalents for it. Bug 613710.
  1.2910 +  if (aSelector == @selector(cancelOperation:)) {
  1.2911 +    return NO;
  1.2912 +  }
  1.2913 +
  1.2914 +  return [super respondsToSelector:aSelector];
  1.2915 +}
  1.2916 +
  1.2917 +- (void) doCommandBySelector:(SEL)aSelector
  1.2918 +{
  1.2919 +  // We override this so that it won't beep if it can't act.
  1.2920 +  // We want to control the beeping for missing or disabled
  1.2921 +  // commands ourselves.
  1.2922 +  [self tryToPerform:aSelector with:nil];
  1.2923 +}
  1.2924 +
  1.2925 +- (id)accessibilityAttributeValue:(NSString *)attribute
  1.2926 +{
  1.2927 +  id retval = [super accessibilityAttributeValue:attribute];
  1.2928 +
  1.2929 +  // The following works around a problem with Text-to-Speech on OS X 10.7.
  1.2930 +  // See bug 674612 for more info.
  1.2931 +  //
  1.2932 +  // When accessibility is off, AXUIElementCopyAttributeValue(), when called
  1.2933 +  // on an AXApplication object to get its AXFocusedUIElement attribute,
  1.2934 +  // always returns an AXWindow object (the actual browser window -- never a
  1.2935 +  // mozAccessible object).  This also happens with accessibility turned on,
  1.2936 +  // if no other object in the browser window has yet been focused.  But if
  1.2937 +  // the browser window has a title bar (as it currently always does), the
  1.2938 +  // AXWindow object will always have four "accessible" children, one of which
  1.2939 +  // is an AXStaticText object (the title bar's "title"; the other three are
  1.2940 +  // the close, minimize and zoom buttons).  This means that (for complicated
  1.2941 +  // reasons, for which see bug 674612) Text-to-Speech on OS X 10.7 will often
  1.2942 +  // "speak" the window title, no matter what text is selected, or even if no
  1.2943 +  // text at all is selected.  (This always happens when accessibility is off.
  1.2944 +  // It doesn't happen in Firefox releases because Apple has (on OS X 10.7)
  1.2945 +  // special-cased the handling of apps whose CFBundleIdentifier is
  1.2946 +  // org.mozilla.firefox.)
  1.2947 +  //
  1.2948 +  // We work around this problem by only returning AXChildren that are
  1.2949 +  // mozAccessible object or are one of the titlebar's buttons (which
  1.2950 +  // instantiate subclasses of NSButtonCell).
  1.2951 +  if (nsCocoaFeatures::OnLionOrLater() && [retval isKindOfClass:[NSArray class]] &&
  1.2952 +      [attribute isEqualToString:@"AXChildren"]) {
  1.2953 +    NSMutableArray *holder = [NSMutableArray arrayWithCapacity:10];
  1.2954 +    [holder addObjectsFromArray:(NSArray *)retval];
  1.2955 +    NSUInteger count = [holder count];
  1.2956 +    for (NSInteger i = count - 1; i >= 0; --i) {
  1.2957 +      id item = [holder objectAtIndex:i];
  1.2958 +      // Remove anything from holder that isn't one of the titlebar's buttons
  1.2959 +      // (which instantiate subclasses of NSButtonCell) or a mozAccessible
  1.2960 +      // object (or one of its subclasses).
  1.2961 +      if (![item isKindOfClass:[NSButtonCell class]] &&
  1.2962 +          ![item respondsToSelector:@selector(hasRepresentedView)]) {
  1.2963 +        [holder removeObjectAtIndex:i];
  1.2964 +      }
  1.2965 +    }
  1.2966 +    retval = [NSArray arrayWithArray:holder];
  1.2967 +  }
  1.2968 +
  1.2969 +  return retval;
  1.2970 +}
  1.2971 +
  1.2972 +// If we were built on OS X 10.6 or with the 10.6 SDK and are running on Lion,
  1.2973 +// the OS (specifically -[NSWindow sendEvent:]) won't send NSEventTypeGesture
  1.2974 +// events to -[ChildView magnifyWithEvent:] as it should.  The following code
  1.2975 +// gets around this.  See bug 863841.
  1.2976 +#if !defined( MAC_OS_X_VERSION_10_7 ) || \
  1.2977 +    ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 )
  1.2978 +- (void)sendEvent:(NSEvent *)anEvent
  1.2979 +{
  1.2980 +  if ([ChildView isLionSmartMagnifyEvent: anEvent]) {
  1.2981 +    [[self mainChildView] magnifyWithEvent:anEvent];
  1.2982 +    return;
  1.2983 +  }
  1.2984 +  [super sendEvent:anEvent];
  1.2985 +}
  1.2986 +#endif
  1.2987 +
  1.2988 +@end
  1.2989 +
  1.2990 +// This class allows us to exercise control over the window's title bar. This
  1.2991 +// allows for a "unified toolbar" look without having to extend the content
  1.2992 +// area into the title bar. It works like this:
  1.2993 +// 1) We set the window's style to textured.
  1.2994 +// 2) Because of this, the background color applies to the entire window, including
  1.2995 +//     the titlebar area. For normal textured windows, the default pattern is a 
  1.2996 +//    "brushed metal" image on Tiger and a unified gradient on Leopard.
  1.2997 +// 3) We set the background color to a custom NSColor subclass that knows how tall the window is.
  1.2998 +//    When -set is called on it, it sets a pattern (with a draw callback) as the fill. In that callback,
  1.2999 +//    it paints the the titlebar and background colors in the correct areas of the context it's given,
  1.3000 +//    which will fill the entire window (CG will tile it horizontally for us).
  1.3001 +// 4) Whenever the window's main state changes and when [window display] is called,
  1.3002 +//    Cocoa redraws the titlebar using the patternDraw callback function.
  1.3003 +//
  1.3004 +// This class also provides us with a pill button to show/hide the toolbar up to 10.6.
  1.3005 +//
  1.3006 +// Drawing the unified gradient in the titlebar and the toolbar works like this:
  1.3007 +// 1) In the style sheet we set the toolbar's -moz-appearance to toolbar or
  1.3008 +//    -moz-mac-unified-toolbar.
  1.3009 +// 2) When the toolbar is visible and we paint the application chrome
  1.3010 +//    window, the array that Gecko passes nsChildView::UpdateThemeGeometries
  1.3011 +//    will contain an entry for the widget type NS_THEME_TOOLBAR or
  1.3012 +//    NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
  1.3013 +// 3) nsChildView::UpdateThemeGeometries finds the toolbar frame's ToolbarWindow
  1.3014 +//    and passes the toolbar frame's height to setUnifiedToolbarHeight.
  1.3015 +// 4) If the toolbar height has changed, a titlebar redraw is triggered and the
  1.3016 +//    upper part of the unified gradient is drawn in the titlebar.
  1.3017 +// 5) The lower part of the unified gradient in the toolbar is drawn during
  1.3018 +//    normal window content painting in nsNativeThemeCocoa::DrawUnifiedToolbar.
  1.3019 +//
  1.3020 +// Whenever the unified gradient is drawn in the titlebar or the toolbar, both
  1.3021 +// titlebar height and toolbar height must be known in order to construct the
  1.3022 +// correct gradient. But you can only get from the toolbar frame
  1.3023 +// to the containing window - the other direction doesn't work. That's why the
  1.3024 +// toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
  1.3025 +// query the window for its titlebar height when drawing the toolbar.
  1.3026 +//
  1.3027 +// Note that in drawsContentsIntoWindowFrame mode, titlebar drawing works in a
  1.3028 +// completely different way: In that mode, the window's mainChildView will
  1.3029 +// cover the titlebar completely and nothing that happens in the window
  1.3030 +// background will reach the screen.
  1.3031 +@implementation ToolbarWindow
  1.3032 +
  1.3033 +- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
  1.3034 +{
  1.3035 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1.3036 +
  1.3037 +  aStyle = aStyle | NSTexturedBackgroundWindowMask;
  1.3038 +  if ((self = [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag])) {
  1.3039 +    mColor = [[TitlebarAndBackgroundColor alloc] initWithWindow:self];
  1.3040 +    // Bypass our guard method below.
  1.3041 +    [super setBackgroundColor:mColor];
  1.3042 +    mBackgroundColor = [[NSColor whiteColor] retain];
  1.3043 +
  1.3044 +    mUnifiedToolbarHeight = 22.0f;
  1.3045 +    mWindowButtonsRect = NSZeroRect;
  1.3046 +    mFullScreenButtonRect = NSZeroRect;
  1.3047 +
  1.3048 +    // setBottomCornerRounded: is a private API call, so we check to make sure
  1.3049 +    // we respond to it just in case.
  1.3050 +    if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
  1.3051 +      [self setBottomCornerRounded:nsCocoaFeatures::OnLionOrLater()];
  1.3052 +
  1.3053 +    [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
  1.3054 +    [self setContentBorderThickness:0.0f forEdge:NSMaxYEdge];
  1.3055 +  }
  1.3056 +  return self;
  1.3057 +
  1.3058 +  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1.3059 +}
  1.3060 +
  1.3061 +- (void)dealloc
  1.3062 +{
  1.3063 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3064 +
  1.3065 +  [mColor release];
  1.3066 +  [mBackgroundColor release];
  1.3067 +  [mTitlebarView release];
  1.3068 +  [super dealloc];
  1.3069 +
  1.3070 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3071 +}
  1.3072 +
  1.3073 +- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive
  1.3074 +{
  1.3075 +  [super setTitlebarColor:aColor forActiveWindow:aActive];
  1.3076 +  [self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
  1.3077 +}
  1.3078 +
  1.3079 +- (void)setBackgroundColor:(NSColor*)aColor
  1.3080 +{
  1.3081 +  [aColor retain];
  1.3082 +  [mBackgroundColor release];
  1.3083 +  mBackgroundColor = aColor;
  1.3084 +}
  1.3085 +
  1.3086 +- (NSColor*)windowBackgroundColor
  1.3087 +{
  1.3088 +  return mBackgroundColor;
  1.3089 +}
  1.3090 +
  1.3091 +- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect
  1.3092 +{
  1.3093 +  [self setTitlebarNeedsDisplayInRect:aRect sync:NO];
  1.3094 +}
  1.3095 +
  1.3096 +- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync
  1.3097 +{
  1.3098 +  NSRect titlebarRect = [self titlebarRect];
  1.3099 +  NSRect rect = NSIntersectionRect(titlebarRect, aRect);
  1.3100 +  if (NSIsEmptyRect(rect))
  1.3101 +    return;
  1.3102 +
  1.3103 +  NSView* borderView = [[self contentView] superview];
  1.3104 +  if (!borderView)
  1.3105 +    return;
  1.3106 +
  1.3107 +  if (aSync) {
  1.3108 +    [borderView displayRect:rect];
  1.3109 +  } else {
  1.3110 +    [borderView setNeedsDisplayInRect:rect];
  1.3111 +  }
  1.3112 +}
  1.3113 +
  1.3114 +- (NSRect)titlebarRect
  1.3115 +{
  1.3116 +  CGFloat titlebarHeight = [self titlebarHeight];
  1.3117 +  return NSMakeRect(0, [self frame].size.height - titlebarHeight,
  1.3118 +                    [self frame].size.width, titlebarHeight);
  1.3119 +}
  1.3120 +
  1.3121 +// Returns the unified height of titlebar + toolbar.
  1.3122 +- (CGFloat)unifiedToolbarHeight
  1.3123 +{
  1.3124 +  return mUnifiedToolbarHeight;
  1.3125 +}
  1.3126 +
  1.3127 +- (CGFloat)titlebarHeight
  1.3128 +{
  1.3129 +  // We use the original content rect here, not what we return from
  1.3130 +  // [self contentRectForFrameRect:], because that would give us a
  1.3131 +  // titlebarHeight of zero in drawsContentsIntoWindowFrame mode.
  1.3132 +  NSRect frameRect = [self frame];
  1.3133 +  NSRect originalContentRect = [NSWindow contentRectForFrameRect:frameRect styleMask:[self styleMask]];
  1.3134 +  return NSMaxY(frameRect) - NSMaxY(originalContentRect);
  1.3135 +}
  1.3136 +
  1.3137 +// Stores the complete height of titlebar + toolbar.
  1.3138 +- (void)setUnifiedToolbarHeight:(CGFloat)aHeight
  1.3139 +{
  1.3140 +  if (aHeight == mUnifiedToolbarHeight)
  1.3141 +    return;
  1.3142 +
  1.3143 +  mUnifiedToolbarHeight = aHeight;
  1.3144 +
  1.3145 +  // Update sheet positioning hint
  1.3146 +  CGFloat topMargin = mUnifiedToolbarHeight - [self titlebarHeight];
  1.3147 +  [self setContentBorderThickness:topMargin forEdge:NSMaxYEdge];
  1.3148 +
  1.3149 +  // Redraw the title bar. If we're inside painting, we'll do it right now,
  1.3150 +  // otherwise we'll just invalidate it.
  1.3151 +  BOOL needSyncRedraw = ([NSView focusView] != nil);
  1.3152 +  [self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw];
  1.3153 +}
  1.3154 +
  1.3155 +// Extending the content area into the title bar works by resizing the
  1.3156 +// mainChildView so that it covers the titlebar.
  1.3157 +- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
  1.3158 +{
  1.3159 +  BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState);
  1.3160 +  [super setDrawsContentsIntoWindowFrame:aState];
  1.3161 +  if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) {
  1.3162 +    // Here we extend / shrink our mainChildView. We do that by firing a resize
  1.3163 +    // event which will cause the ChildView to be resized to the rect returned
  1.3164 +    // by nsCocoaWindow::GetClientBounds. GetClientBounds bases its return
  1.3165 +    // value on what we return from drawsContentsIntoWindowFrame.
  1.3166 +    WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
  1.3167 +    nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
  1.3168 +    if (geckoWindow) {
  1.3169 +      // Re-layout our contents.
  1.3170 +      geckoWindow->ReportSizeEvent();
  1.3171 +    }
  1.3172 +
  1.3173 +    // Resizing the content area causes a reflow which would send a synthesized
  1.3174 +    // mousemove event to the old mouse position relative to the top left
  1.3175 +    // corner of the content area. But the mouse has shifted relative to the
  1.3176 +    // content area, so that event would have wrong position information. So
  1.3177 +    // we'll send a mouse move event with the correct new position.
  1.3178 +    ChildViewMouseTracker::ResendLastMouseMoveEvent();
  1.3179 +  }
  1.3180 +}
  1.3181 +
  1.3182 +- (void)setWantsTitleDrawn:(BOOL)aDrawTitle
  1.3183 +{
  1.3184 +  [super setWantsTitleDrawn:aDrawTitle];
  1.3185 +  [self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
  1.3186 +}
  1.3187 +
  1.3188 +- (void)placeWindowButtons:(NSRect)aRect
  1.3189 +{
  1.3190 +  if (!NSEqualRects(mWindowButtonsRect, aRect)) {
  1.3191 +    mWindowButtonsRect = aRect;
  1.3192 +    [self reflowTitlebarElements];
  1.3193 +  }
  1.3194 +}
  1.3195 +
  1.3196 +- (NSPoint)windowButtonsPositionWithDefaultPosition:(NSPoint)aDefaultPosition
  1.3197 +{
  1.3198 +  if ([self drawsContentsIntoWindowFrame] && !NSIsEmptyRect(mWindowButtonsRect)) {
  1.3199 +    return NSMakePoint(std::max(mWindowButtonsRect.origin.x, aDefaultPosition.x),
  1.3200 +                       std::min(mWindowButtonsRect.origin.y, aDefaultPosition.y));
  1.3201 +  }
  1.3202 +  return aDefaultPosition;
  1.3203 +}
  1.3204 +
  1.3205 +- (void)placeFullScreenButton:(NSRect)aRect
  1.3206 +{
  1.3207 +  if (!NSEqualRects(mFullScreenButtonRect, aRect)) {
  1.3208 +    mFullScreenButtonRect = aRect;
  1.3209 +    [self reflowTitlebarElements];
  1.3210 +  }
  1.3211 +}
  1.3212 +
  1.3213 +- (NSPoint)fullScreenButtonPositionWithDefaultPosition:(NSPoint)aDefaultPosition;
  1.3214 +{
  1.3215 +  if ([self drawsContentsIntoWindowFrame] && !NSIsEmptyRect(mFullScreenButtonRect)) {
  1.3216 +    return NSMakePoint(std::min(mFullScreenButtonRect.origin.x, aDefaultPosition.x),
  1.3217 +                       std::min(mFullScreenButtonRect.origin.y, aDefaultPosition.y));
  1.3218 +  }
  1.3219 +  return aDefaultPosition;
  1.3220 +}
  1.3221 +
  1.3222 +// Returning YES here makes the setShowsToolbarButton method work even though
  1.3223 +// the window doesn't contain an NSToolbar.
  1.3224 +- (BOOL)_hasToolbar
  1.3225 +{
  1.3226 +  return YES;
  1.3227 +}
  1.3228 +
  1.3229 +// Dispatch a toolbar pill button clicked message to Gecko.
  1.3230 +- (void)_toolbarPillButtonClicked:(id)sender
  1.3231 +{
  1.3232 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3233 +
  1.3234 +  RollUpPopups();
  1.3235 +
  1.3236 +  if ([[self delegate] isKindOfClass:[WindowDelegate class]]) {
  1.3237 +    WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
  1.3238 +    nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
  1.3239 +    if (!geckoWindow)
  1.3240 +      return;
  1.3241 +
  1.3242 +    nsIWidgetListener* listener = geckoWindow->GetWidgetListener();
  1.3243 +    if (listener)
  1.3244 +      listener->OSToolbarButtonPressed();
  1.3245 +  }
  1.3246 +
  1.3247 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3248 +}
  1.3249 +
  1.3250 +// Retain and release "self" to avoid crashes when our widget (and its native
  1.3251 +// window) is closed as a result of processing a key equivalent (e.g.
  1.3252 +// Command+w or Command+q).  This workaround is only needed for a window
  1.3253 +// that can become key.
  1.3254 +- (BOOL)performKeyEquivalent:(NSEvent*)theEvent
  1.3255 +{
  1.3256 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.3257 +
  1.3258 +  NSWindow *nativeWindow = [self retain];
  1.3259 +  BOOL retval = [super performKeyEquivalent:theEvent];
  1.3260 +  [nativeWindow release];
  1.3261 +  return retval;
  1.3262 +
  1.3263 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  1.3264 +}
  1.3265 +
  1.3266 +- (void)sendEvent:(NSEvent *)anEvent
  1.3267 +{
  1.3268 +  NSEventType type = [anEvent type];
  1.3269 +  
  1.3270 +  switch (type) {
  1.3271 +    case NSScrollWheel:
  1.3272 +    case NSLeftMouseDown:
  1.3273 +    case NSLeftMouseUp:
  1.3274 +    case NSRightMouseDown:
  1.3275 +    case NSRightMouseUp:
  1.3276 +    case NSOtherMouseDown:
  1.3277 +    case NSOtherMouseUp:
  1.3278 +    case NSMouseMoved:
  1.3279 +    case NSLeftMouseDragged:
  1.3280 +    case NSRightMouseDragged:
  1.3281 +    case NSOtherMouseDragged:
  1.3282 +    {
  1.3283 +      // Drop all mouse events if a modal window has appeared above us.
  1.3284 +      // This helps make us behave as if the OS were running a "real" modal
  1.3285 +      // event loop.
  1.3286 +      id delegate = [self delegate];
  1.3287 +      if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
  1.3288 +        nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
  1.3289 +        if (widget) {
  1.3290 +          if (type == NSMouseMoved) {
  1.3291 +            [[self mainChildView] updateWindowDraggableStateOnMouseMove:anEvent];
  1.3292 +          }
  1.3293 +          if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
  1.3294 +            return;
  1.3295 +          if (widget->HasModalDescendents())
  1.3296 +            return;
  1.3297 +        }
  1.3298 +      }
  1.3299 +      break;
  1.3300 +    }
  1.3301 +    default:
  1.3302 +      break;
  1.3303 +  }
  1.3304 +
  1.3305 +  [super sendEvent:anEvent];
  1.3306 +}
  1.3307 +
  1.3308 +@end
  1.3309 +
  1.3310 +// Custom NSColor subclass where most of the work takes place for drawing in
  1.3311 +// the titlebar area. Not used in drawsContentsIntoWindowFrame mode.
  1.3312 +@implementation TitlebarAndBackgroundColor
  1.3313 +
  1.3314 +- (id)initWithWindow:(ToolbarWindow*)aWindow
  1.3315 +{
  1.3316 +  if ((self = [super init])) {
  1.3317 +    mWindow = aWindow; // weak ref to avoid a cycle
  1.3318 +  }
  1.3319 +  return self;
  1.3320 +}
  1.3321 +
  1.3322 +static void
  1.3323 +DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect,
  1.3324 +                   CGFloat aUnifiedToolbarHeight, BOOL aIsMain)
  1.3325 +{
  1.3326 +  if (aTitlebarRect.size.width * aTitlebarRect.size.height > CUIDRAW_MAX_AREA) {
  1.3327 +    return;
  1.3328 +  }
  1.3329 +
  1.3330 +  CUIDraw([NSWindow coreUIRenderer], aTitlebarRect, aContext,
  1.3331 +          (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
  1.3332 +            @"kCUIWidgetWindowFrame", @"widget",
  1.3333 +            @"regularwin", @"windowtype",
  1.3334 +            (aIsMain ? @"normal" : @"inactive"), @"state",
  1.3335 +            [NSNumber numberWithDouble:aUnifiedToolbarHeight], @"kCUIWindowFrameUnifiedTitleBarHeightKey",
  1.3336 +            [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawTitleSeparatorKey",
  1.3337 +            nil],
  1.3338 +          nil);
  1.3339 +
  1.3340 +  if (nsCocoaFeatures::OnLionOrLater()) {
  1.3341 +    // On Lion the call to CUIDraw doesn't draw the top pixel strip at some
  1.3342 +    // window widths. We don't want to have a flickering transparent line, so
  1.3343 +    // we overdraw it.
  1.3344 +    CGContextSetRGBFillColor(aContext, 0.95, 0.95, 0.95, 1);
  1.3345 +    CGContextFillRect(aContext, CGRectMake(0, CGRectGetMaxY(aTitlebarRect) - 1,
  1.3346 +                                           aTitlebarRect.size.width, 1));
  1.3347 +  }
  1.3348 +}
  1.3349 +
  1.3350 +// Pattern draw callback for standard titlebar gradients and solid titlebar colors
  1.3351 +static void
  1.3352 +TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
  1.3353 +{
  1.3354 +  ToolbarWindow *window = (ToolbarWindow*)aInfo;
  1.3355 +  if (![window drawsContentsIntoWindowFrame]) {
  1.3356 +    NSRect titlebarRect = [window titlebarRect];
  1.3357 +    BOOL isMain = [window isMainWindow];
  1.3358 +    NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain];
  1.3359 +    if (!titlebarColor) {
  1.3360 +      // If the titlebar color is nil, draw the default titlebar shading.
  1.3361 +      DrawNativeTitlebar(aContext, NSRectToCGRect(titlebarRect),
  1.3362 +                         [window unifiedToolbarHeight], isMain);
  1.3363 +    } else {
  1.3364 +      // If the titlebar color is not nil, just set and draw it normally.
  1.3365 +      [NSGraphicsContext saveGraphicsState];
  1.3366 +      [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:aContext flipped:NO]];
  1.3367 +      [titlebarColor set];
  1.3368 +      NSRectFill(titlebarRect);
  1.3369 +      [NSGraphicsContext restoreGraphicsState];
  1.3370 +    }
  1.3371 +  }
  1.3372 +}
  1.3373 +
  1.3374 +- (void)setFill
  1.3375 +{
  1.3376 +  float patternWidth = [mWindow frame].size.width;
  1.3377 +
  1.3378 +  CGPatternCallbacks callbacks = {0, &TitlebarDrawCallback, NULL};
  1.3379 +  CGPatternRef pattern = CGPatternCreate(mWindow, CGRectMake(0.0f, 0.0f, patternWidth, [mWindow frame].size.height), 
  1.3380 +                                         CGAffineTransformIdentity, patternWidth, [mWindow frame].size.height,
  1.3381 +                                         kCGPatternTilingConstantSpacing, true, &callbacks);
  1.3382 +
  1.3383 +  // Set the pattern as the fill, which is what we were asked to do. All our
  1.3384 +  // drawing will take place in the patternDraw callback.
  1.3385 +  CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
  1.3386 +  CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
  1.3387 +  CGContextSetFillColorSpace(context, patternSpace);
  1.3388 +  CGColorSpaceRelease(patternSpace);
  1.3389 +  CGFloat component = 1.0f;
  1.3390 +  CGContextSetFillPattern(context, pattern, &component);
  1.3391 +  CGPatternRelease(pattern);
  1.3392 +}
  1.3393 +
  1.3394 +- (void)set
  1.3395 +{
  1.3396 +  [self setFill];
  1.3397 +}
  1.3398 +
  1.3399 +- (NSString*)colorSpaceName
  1.3400 +{
  1.3401 +  return NSDeviceRGBColorSpace;
  1.3402 +}
  1.3403 +
  1.3404 +@end
  1.3405 +
  1.3406 +@implementation PopupWindow
  1.3407 +
  1.3408 +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
  1.3409 +      backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
  1.3410 +{
  1.3411 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1.3412 +
  1.3413 +  mIsContextMenu = false;
  1.3414 +  return [super initWithContentRect:contentRect styleMask:styleMask
  1.3415 +          backing:bufferingType defer:deferCreation];
  1.3416 +
  1.3417 +  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1.3418 +}
  1.3419 +
  1.3420 +- (BOOL)isContextMenu
  1.3421 +{
  1.3422 +  return mIsContextMenu;
  1.3423 +}
  1.3424 +
  1.3425 +- (void)setIsContextMenu:(BOOL)flag
  1.3426 +{
  1.3427 +  mIsContextMenu = flag;
  1.3428 +}
  1.3429 +
  1.3430 +- (BOOL)canBecomeMainWindow
  1.3431 +{
  1.3432 +  // This is overriden because the default is 'yes' when a titlebar is present.
  1.3433 +  return NO;
  1.3434 +}
  1.3435 +
  1.3436 +@end
  1.3437 +
  1.3438 +// According to Apple's docs on [NSWindow canBecomeKeyWindow] and [NSWindow
  1.3439 +// canBecomeMainWindow], windows without a title bar or resize bar can't (by
  1.3440 +// default) become key or main.  But if a window can't become key, it can't
  1.3441 +// accept keyboard input (bmo bug 393250).  And it should also be possible for
  1.3442 +// an otherwise "ordinary" window to become main.  We need to override these
  1.3443 +// two methods to make this happen.
  1.3444 +@implementation BorderlessWindow
  1.3445 +
  1.3446 +- (BOOL)canBecomeKeyWindow
  1.3447 +{
  1.3448 +  return YES;
  1.3449 +}
  1.3450 +
  1.3451 +- (void)sendEvent:(NSEvent *)anEvent
  1.3452 +{
  1.3453 +  NSEventType type = [anEvent type];
  1.3454 +  
  1.3455 +  switch (type) {
  1.3456 +    case NSScrollWheel:
  1.3457 +    case NSLeftMouseDown:
  1.3458 +    case NSLeftMouseUp:
  1.3459 +    case NSRightMouseDown:
  1.3460 +    case NSRightMouseUp:
  1.3461 +    case NSOtherMouseDown:
  1.3462 +    case NSOtherMouseUp:
  1.3463 +    case NSMouseMoved:
  1.3464 +    case NSLeftMouseDragged:
  1.3465 +    case NSRightMouseDragged:
  1.3466 +    case NSOtherMouseDragged:
  1.3467 +    {
  1.3468 +      // Drop all mouse events if a modal window has appeared above us.
  1.3469 +      // This helps make us behave as if the OS were running a "real" modal
  1.3470 +      // event loop.
  1.3471 +      id delegate = [self delegate];
  1.3472 +      if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
  1.3473 +        nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
  1.3474 +        if (widget) {
  1.3475 +          if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
  1.3476 +            return;
  1.3477 +          if (widget->HasModalDescendents())
  1.3478 +            return;
  1.3479 +        }
  1.3480 +      }
  1.3481 +      break;
  1.3482 +    }
  1.3483 +    default:
  1.3484 +      break;
  1.3485 +  }
  1.3486 +
  1.3487 +  [super sendEvent:anEvent];
  1.3488 +}
  1.3489 +
  1.3490 +// Apple's doc on this method says that the NSWindow class's default is not to
  1.3491 +// become main if the window isn't "visible" -- so we should replicate that
  1.3492 +// behavior here.  As best I can tell, the [NSWindow isVisible] method is an
  1.3493 +// accurate test of what Apple means by "visibility".
  1.3494 +- (BOOL)canBecomeMainWindow
  1.3495 +{
  1.3496 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.3497 +
  1.3498 +  if (![self isVisible])
  1.3499 +    return NO;
  1.3500 +  return YES;
  1.3501 +
  1.3502 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  1.3503 +}
  1.3504 +
  1.3505 +// Retain and release "self" to avoid crashes when our widget (and its native
  1.3506 +// window) is closed as a result of processing a key equivalent (e.g.
  1.3507 +// Command+w or Command+q).  This workaround is only needed for a window
  1.3508 +// that can become key.
  1.3509 +- (BOOL)performKeyEquivalent:(NSEvent*)theEvent
  1.3510 +{
  1.3511 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.3512 +
  1.3513 +  NSWindow *nativeWindow = [self retain];
  1.3514 +  BOOL retval = [super performKeyEquivalent:theEvent];
  1.3515 +  [nativeWindow release];
  1.3516 +  return retval;
  1.3517 +
  1.3518 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
  1.3519 +}
  1.3520 +
  1.3521 +@end

mercurial