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