Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / cocoa / nsWindowMap.mm
blob30d56bba2920370c6603ad9329b09d1f7164a07a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsWindowMap.h"
7 #include "nsObjCExceptions.h"
8 #include "nsChildView.h"
9 #include "nsCocoaWindow.h"
11 @interface WindowDataMap (Private)
13 - (NSString*)keyForWindow:(NSWindow*)inWindow;
15 @end
17 @interface TopLevelWindowData (Private)
19 - (void)windowResignedKey:(NSNotification*)inNotification;
20 - (void)windowBecameKey:(NSNotification*)inNotification;
21 - (void)windowWillClose:(NSNotification*)inNotification;
23 @end
25 #pragma mark -
27 @implementation WindowDataMap
29 + (WindowDataMap*)sharedWindowDataMap {
30   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
32   static WindowDataMap* sWindowMap = nil;
33   if (!sWindowMap) sWindowMap = [[WindowDataMap alloc] init];
35   return sWindowMap;
37   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
40 - (id)init {
41   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
43   if ((self = [super init])) {
44     mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10];
45   }
46   return self;
48   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
51 - (void)dealloc {
52   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
54   [mWindowMap release];
55   [super dealloc];
57   NS_OBJC_END_TRY_IGNORE_BLOCK;
60 - (void)ensureDataForWindow:(NSWindow*)inWindow {
61   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
63   if (!inWindow || [self dataForWindow:inWindow]) return;
65   TopLevelWindowData* windowData =
66       [[TopLevelWindowData alloc] initWithWindow:inWindow];
67   [self setData:windowData forWindow:inWindow];  // takes ownership
68   [windowData release];
70   NS_OBJC_END_TRY_IGNORE_BLOCK;
73 - (id)dataForWindow:(NSWindow*)inWindow {
74   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
76   return [mWindowMap objectForKey:[self keyForWindow:inWindow]];
78   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
81 - (void)setData:(id)inData forWindow:(NSWindow*)inWindow {
82   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
84   [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]];
86   NS_OBJC_END_TRY_IGNORE_BLOCK;
89 - (void)removeDataForWindow:(NSWindow*)inWindow {
90   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
92   [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]];
94   NS_OBJC_END_TRY_IGNORE_BLOCK;
97 - (NSString*)keyForWindow:(NSWindow*)inWindow {
98   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
100   return [NSString stringWithFormat:@"%p", inWindow];
102   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
105 @end
107 //  TopLevelWindowData
109 //  This class holds data about top-level windows. We can't use a window
110 //  delegate, because an embedder may already have one.
112 @implementation TopLevelWindowData
114 - (id)initWithWindow:(NSWindow*)inWindow {
115   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
117   if ((self = [super init])) {
118     [NSNotificationCenter.defaultCenter
119         addObserver:self
120            selector:@selector(windowBecameKey:)
121                name:NSWindowDidBecomeKeyNotification
122              object:inWindow];
124     [NSNotificationCenter.defaultCenter
125         addObserver:self
126            selector:@selector(windowResignedKey:)
127                name:NSWindowDidResignKeyNotification
128              object:inWindow];
130     [NSNotificationCenter.defaultCenter
131         addObserver:self
132            selector:@selector(windowBecameMain:)
133                name:NSWindowDidBecomeMainNotification
134              object:inWindow];
136     [NSNotificationCenter.defaultCenter
137         addObserver:self
138            selector:@selector(windowResignedMain:)
139                name:NSWindowDidResignMainNotification
140              object:inWindow];
142     [NSNotificationCenter.defaultCenter
143         addObserver:self
144            selector:@selector(windowWillClose:)
145                name:NSWindowWillCloseNotification
146              object:inWindow];
147   }
148   return self;
150   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
153 - (void)dealloc {
154   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
156   [NSNotificationCenter.defaultCenter removeObserver:self];
157   [super dealloc];
159   NS_OBJC_END_TRY_IGNORE_BLOCK;
162 // As best I can tell, if the notification's object has a corresponding
163 // top-level widget (an nsCocoaWindow object), it has a delegate (set in
164 // nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise
165 // not (Camino didn't use top-level widgets (nsCocoaWindow objects) --
166 // only child widgets (nsChildView objects)).  (The notification is sent
167 // to windowBecameKey: or windowBecameMain: below.)
169 // For use with clients that (like Firefox) do use top-level widgets (and
170 // have NSWindow delegates of class WindowDelegate).
171 + (void)activateInWindow:(NSWindow*)aWindow {
172   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
174   WindowDelegate* delegate = (WindowDelegate*)aWindow.delegate;
175   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return;
177   if ([delegate toplevelActiveState]) return;
178   [delegate sendToplevelActivateEvents];
180   NS_OBJC_END_TRY_IGNORE_BLOCK;
183 // See comments above activateInWindow:
185 // If we're using top-level widgets (nsCocoaWindow objects), we send them
186 // NS_DEACTIVATE events (which propagate to child widgets (nsChildView
187 // objects) via nsWebShellWindow::HandleEvent()).
189 // For use with clients that (like Firefox) do use top-level widgets (and
190 // have NSWindow delegates of class WindowDelegate).
191 + (void)deactivateInWindow:(NSWindow*)aWindow {
192   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
194   WindowDelegate* delegate = (WindowDelegate*)aWindow.delegate;
195   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return;
197   if (![delegate toplevelActiveState]) return;
198   [delegate sendToplevelDeactivateEvents];
200   NS_OBJC_END_TRY_IGNORE_BLOCK;
203 // For use with clients that (like Camino) don't use top-level widgets (and
204 // don't have NSWindow delegates of class WindowDelegate).
205 + (void)activateInWindowViews:(NSWindow*)aWindow {
206   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
208   id firstResponder = aWindow.firstResponder;
209   if ([firstResponder isKindOfClass:[ChildView class]]) {
210     [firstResponder viewsWindowDidBecomeKey];
211   }
213   NS_OBJC_END_TRY_IGNORE_BLOCK;
216 // For use with clients that (like Camino) don't use top-level widgets (and
217 // don't have NSWindow delegates of class WindowDelegate).
218 + (void)deactivateInWindowViews:(NSWindow*)aWindow {
219   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
221   id firstResponder = aWindow.firstResponder;
222   if ([firstResponder isKindOfClass:[ChildView class]]) {
223     [firstResponder viewsWindowDidResignKey];
224   }
226   NS_OBJC_END_TRY_IGNORE_BLOCK;
229 // We make certain exceptions for top-level windows in non-embedders (see
230 // comment above windowBecameMain below).  And we need (elsewhere) to guard
231 // against sending duplicate events.  But in general the NS_ACTIVATE event
232 // should be sent when a native window becomes key, and the NS_DEACTIVATE
233 // event should be sent when it resignes key.
234 - (void)windowBecameKey:(NSNotification*)inNotification {
235   NSWindow* window = inNotification.object;
237   id delegate = window.delegate;
238   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
239     [TopLevelWindowData activateInWindowViews:window];
240   } else if (window.isSheet || window.isMainWindow) {
241     [TopLevelWindowData activateInWindow:window];
242   }
245 - (void)windowResignedKey:(NSNotification*)inNotification {
246   NSWindow* window = inNotification.object;
248   id delegate = window.delegate;
249   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
250     [TopLevelWindowData deactivateInWindowViews:window];
251   } else if (window.isSheet || window.isMainWindow) {
252     [TopLevelWindowData deactivateInWindow:window];
253   }
256 // The appearance of a top-level window depends on its main state (not its key
257 // state).  So (for non-embedders) we need to ensure that a top-level window
258 // is main when an NS_ACTIVATE event is sent to Gecko for it.
259 - (void)windowBecameMain:(NSNotification*)inNotification {
260   NSWindow* window = inNotification.object;
261   id delegate = window.delegate;
262   // Don't send events to a top-level window that has a sheet/modal-window open
263   // above it -- as far as Gecko is concerned, it's inactive, and stays so until
264   // the sheet/modal-window closes.
265   if (delegate && [delegate isKindOfClass:[WindowDelegate class]] &&
266       !window.attachedSheet && window.isKeyWindow) {
267     [TopLevelWindowData activateInWindow:window];
268   }
271 - (void)windowResignedMain:(NSNotification*)inNotification {
272   NSWindow* window = inNotification.object;
273   id delegate = window.delegate;
274   if (delegate && [delegate isKindOfClass:[WindowDelegate class]] &&
275       ![window attachedSheet] && ![NSApp modalWindow]) {
276     [TopLevelWindowData deactivateInWindow:window];
277   }
280 - (void)windowWillClose:(NSNotification*)inNotification {
281   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
283   // postpone our destruction
284   [[self retain] autorelease];
286   // remove ourselves from the window map (which owns us)
287   [[WindowDataMap sharedWindowDataMap]
288       removeDataForWindow:[inNotification object]];
290   NS_OBJC_END_TRY_IGNORE_BLOCK;
293 @end