1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/cocoa/nsWindowMap.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,311 @@ 1.4 +/* -*- Mode: 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 "nsWindowMap.h" 1.10 +#include "nsObjCExceptions.h" 1.11 +#include "nsChildView.h" 1.12 +#include "nsCocoaWindow.h" 1.13 + 1.14 +@interface WindowDataMap(Private) 1.15 + 1.16 +- (NSString*)keyForWindow:(NSWindow*)inWindow; 1.17 + 1.18 +@end 1.19 + 1.20 +@interface TopLevelWindowData(Private) 1.21 + 1.22 +- (void)windowResignedKey:(NSNotification*)inNotification; 1.23 +- (void)windowBecameKey:(NSNotification*)inNotification; 1.24 +- (void)windowWillClose:(NSNotification*)inNotification; 1.25 + 1.26 +@end 1.27 + 1.28 +#pragma mark - 1.29 + 1.30 +@implementation WindowDataMap 1.31 + 1.32 ++ (WindowDataMap*)sharedWindowDataMap 1.33 +{ 1.34 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.35 + 1.36 + static WindowDataMap* sWindowMap = nil; 1.37 + if (!sWindowMap) 1.38 + sWindowMap = [[WindowDataMap alloc] init]; 1.39 + 1.40 + return sWindowMap; 1.41 + 1.42 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.43 +} 1.44 + 1.45 +- (id)init 1.46 +{ 1.47 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.48 + 1.49 + if ((self = [super init])) { 1.50 + mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10]; 1.51 + } 1.52 + return self; 1.53 + 1.54 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.55 +} 1.56 + 1.57 +- (void)dealloc 1.58 +{ 1.59 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.60 + 1.61 + [mWindowMap release]; 1.62 + [super dealloc]; 1.63 + 1.64 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.65 +} 1.66 + 1.67 +- (void)ensureDataForWindow:(NSWindow*)inWindow 1.68 +{ 1.69 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.70 + 1.71 + if (!inWindow || [self dataForWindow:inWindow]) 1.72 + return; 1.73 + 1.74 + TopLevelWindowData* windowData = [[TopLevelWindowData alloc] initWithWindow:inWindow]; 1.75 + [self setData:windowData forWindow:inWindow]; // takes ownership 1.76 + [windowData release]; 1.77 + 1.78 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.79 +} 1.80 + 1.81 +- (id)dataForWindow:(NSWindow*)inWindow 1.82 +{ 1.83 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.84 + 1.85 + return [mWindowMap objectForKey:[self keyForWindow:inWindow]]; 1.86 + 1.87 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.88 +} 1.89 + 1.90 +- (void)setData:(id)inData forWindow:(NSWindow*)inWindow 1.91 +{ 1.92 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.93 + 1.94 + [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]]; 1.95 + 1.96 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.97 +} 1.98 + 1.99 +- (void)removeDataForWindow:(NSWindow*)inWindow 1.100 +{ 1.101 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.102 + 1.103 + [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]]; 1.104 + 1.105 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.106 +} 1.107 + 1.108 +- (NSString*)keyForWindow:(NSWindow*)inWindow 1.109 +{ 1.110 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.111 + 1.112 + return [NSString stringWithFormat:@"%p", inWindow]; 1.113 + 1.114 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.115 +} 1.116 + 1.117 +@end 1.118 + 1.119 +// TopLevelWindowData 1.120 +// 1.121 +// This class holds data about top-level windows. We can't use a window 1.122 +// delegate, because an embedder may already have one. 1.123 + 1.124 +@implementation TopLevelWindowData 1.125 + 1.126 +- (id)initWithWindow:(NSWindow*)inWindow 1.127 +{ 1.128 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.129 + 1.130 + if ((self = [super init])) { 1.131 + [[NSNotificationCenter defaultCenter] addObserver:self 1.132 + selector:@selector(windowBecameKey:) 1.133 + name:NSWindowDidBecomeKeyNotification 1.134 + object:inWindow]; 1.135 + 1.136 + [[NSNotificationCenter defaultCenter] addObserver:self 1.137 + selector:@selector(windowResignedKey:) 1.138 + name:NSWindowDidResignKeyNotification 1.139 + object:inWindow]; 1.140 + 1.141 + [[NSNotificationCenter defaultCenter] addObserver:self 1.142 + selector:@selector(windowBecameMain:) 1.143 + name:NSWindowDidBecomeMainNotification 1.144 + object:inWindow]; 1.145 + 1.146 + [[NSNotificationCenter defaultCenter] addObserver:self 1.147 + selector:@selector(windowResignedMain:) 1.148 + name:NSWindowDidResignMainNotification 1.149 + object:inWindow]; 1.150 + 1.151 + [[NSNotificationCenter defaultCenter] addObserver:self 1.152 + selector:@selector(windowWillClose:) 1.153 + name:NSWindowWillCloseNotification 1.154 + object:inWindow]; 1.155 + } 1.156 + return self; 1.157 + 1.158 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.159 +} 1.160 + 1.161 +- (void)dealloc 1.162 +{ 1.163 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.164 + 1.165 + [[NSNotificationCenter defaultCenter] removeObserver:self]; 1.166 + [super dealloc]; 1.167 + 1.168 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.169 +} 1.170 + 1.171 +// As best I can tell, if the notification's object has a corresponding 1.172 +// top-level widget (an nsCocoaWindow object), it has a delegate (set in 1.173 +// nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise 1.174 +// not (Camino doesn't use top-level widgets (nsCocoaWindow objects) -- 1.175 +// only child widgets (nsChildView objects)). (The notification is sent 1.176 +// to windowBecameKey: or windowBecameMain: below.) 1.177 +// 1.178 +// For use with clients that (like Firefox) do use top-level widgets (and 1.179 +// have NSWindow delegates of class WindowDelegate). 1.180 ++ (void)activateInWindow:(NSWindow*)aWindow 1.181 +{ 1.182 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.183 + 1.184 + WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate]; 1.185 + if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) 1.186 + return; 1.187 + 1.188 + if ([delegate toplevelActiveState]) 1.189 + return; 1.190 + [delegate sendToplevelActivateEvents]; 1.191 + 1.192 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.193 +} 1.194 + 1.195 +// See comments above activateInWindow: 1.196 +// 1.197 +// If we're using top-level widgets (nsCocoaWindow objects), we send them 1.198 +// NS_DEACTIVATE events (which propagate to child widgets (nsChildView 1.199 +// objects) via nsWebShellWindow::HandleEvent()). 1.200 +// 1.201 +// For use with clients that (like Firefox) do use top-level widgets (and 1.202 +// have NSWindow delegates of class WindowDelegate). 1.203 ++ (void)deactivateInWindow:(NSWindow*)aWindow 1.204 +{ 1.205 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.206 + 1.207 + WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate]; 1.208 + if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) 1.209 + return; 1.210 + 1.211 + if (![delegate toplevelActiveState]) 1.212 + return; 1.213 + [delegate sendToplevelDeactivateEvents]; 1.214 + 1.215 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.216 +} 1.217 + 1.218 +// For use with clients that (like Camino) don't use top-level widgets (and 1.219 +// don't have NSWindow delegates of class WindowDelegate). 1.220 ++ (void)activateInWindowViews:(NSWindow*)aWindow 1.221 +{ 1.222 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.223 + 1.224 + id firstResponder = [aWindow firstResponder]; 1.225 + if ([firstResponder isKindOfClass:[ChildView class]]) 1.226 + [firstResponder viewsWindowDidBecomeKey]; 1.227 + 1.228 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.229 +} 1.230 + 1.231 +// For use with clients that (like Camino) don't use top-level widgets (and 1.232 +// don't have NSWindow delegates of class WindowDelegate). 1.233 ++ (void)deactivateInWindowViews:(NSWindow*)aWindow 1.234 +{ 1.235 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.236 + 1.237 + id firstResponder = [aWindow firstResponder]; 1.238 + if ([firstResponder isKindOfClass:[ChildView class]]) 1.239 + [firstResponder viewsWindowDidResignKey]; 1.240 + 1.241 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.242 +} 1.243 + 1.244 +// We make certain exceptions for top-level windows in non-embedders (see 1.245 +// comment above windowBecameMain below). And we need (elsewhere) to guard 1.246 +// against sending duplicate events. But in general the NS_ACTIVATE event 1.247 +// should be sent when a native window becomes key, and the NS_DEACTIVATE 1.248 +// event should be sent when it resignes key. 1.249 +- (void)windowBecameKey:(NSNotification*)inNotification 1.250 +{ 1.251 + NSWindow* window = (NSWindow*)[inNotification object]; 1.252 + 1.253 + id delegate = [window delegate]; 1.254 + if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { 1.255 + [TopLevelWindowData activateInWindowViews:window]; 1.256 + } else if ([window isSheet]) { 1.257 + [TopLevelWindowData activateInWindow:window]; 1.258 + } 1.259 + 1.260 + [[window contentView] setNeedsDisplay:YES]; 1.261 +} 1.262 + 1.263 +- (void)windowResignedKey:(NSNotification*)inNotification 1.264 +{ 1.265 + NSWindow* window = (NSWindow*)[inNotification object]; 1.266 + 1.267 + id delegate = [window delegate]; 1.268 + if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { 1.269 + [TopLevelWindowData deactivateInWindowViews:window]; 1.270 + } else if ([window isSheet]) { 1.271 + [TopLevelWindowData deactivateInWindow:window]; 1.272 + } 1.273 + 1.274 + [[window contentView] setNeedsDisplay:YES]; 1.275 +} 1.276 + 1.277 +// The appearance of a top-level window depends on its main state (not its key 1.278 +// state). So (for non-embedders) we need to ensure that a top-level window 1.279 +// is main when an NS_ACTIVATE event is sent to Gecko for it. 1.280 +- (void)windowBecameMain:(NSNotification*)inNotification 1.281 +{ 1.282 + NSWindow* window = (NSWindow*)[inNotification object]; 1.283 + 1.284 + id delegate = [window delegate]; 1.285 + // Don't send events to a top-level window that has a sheet open above it -- 1.286 + // as far as Gecko is concerned, it's inactive, and stays so until the sheet 1.287 + // closes. 1.288 + if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet]) 1.289 + [TopLevelWindowData activateInWindow:window]; 1.290 +} 1.291 + 1.292 +- (void)windowResignedMain:(NSNotification*)inNotification 1.293 +{ 1.294 + NSWindow* window = (NSWindow*)[inNotification object]; 1.295 + 1.296 + id delegate = [window delegate]; 1.297 + if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet]) 1.298 + [TopLevelWindowData deactivateInWindow:window]; 1.299 +} 1.300 + 1.301 +- (void)windowWillClose:(NSNotification*)inNotification 1.302 +{ 1.303 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.304 + 1.305 + // postpone our destruction 1.306 + [[self retain] autorelease]; 1.307 + 1.308 + // remove ourselves from the window map (which owns us) 1.309 + [[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]]; 1.310 + 1.311 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.312 +} 1.313 + 1.314 +@end