Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/app_controller_mac.h" | 5 #import "chrome/browser/app_controller_mac.h" |
| 6 | 6 |
| 7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
| 10 #include "base/mac/foundation_util.h" | 10 #include "base/mac/foundation_util.h" |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 158 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty. | 158 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty. |
| 159 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, | 159 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, |
| 160 new PrefsSyncTask(), 1500); | 160 new PrefsSyncTask(), 1500); |
| 161 } | 161 } |
| 162 | 162 |
| 163 } // anonymous namespace | 163 } // anonymous namespace |
| 164 | 164 |
| 165 const AEEventClass kAECloudPrintInstallClass = 'GCPi'; | 165 const AEEventClass kAECloudPrintInstallClass = 'GCPi'; |
| 166 const AEEventClass kAECloudPrintUninstallClass = 'GCPu'; | 166 const AEEventClass kAECloudPrintUninstallClass = 'GCPu'; |
| 167 | 167 |
| 168 @interface NSWindow (AppControllerHelpers) | |
| 169 - (BOOL)isDescendantOf:(NSWindow*)other; | |
| 170 @end | |
| 171 | |
| 172 @implementation NSWindow (AppControllerHelpers) | |
| 173 // Returns true if |self| is a descendant of the |other| window. | |
| 174 - (BOOL)isDescendantOf:(NSWindow*)other { | |
| 175 NSWindow* parent; | |
| 176 do { | |
| 177 parent = [self parentWindow]; | |
| 178 if (parent == other) | |
| 179 return YES; | |
| 180 } while (parent); | |
| 181 | |
| 182 return NO; | |
| 183 } | |
|
Scott Hess - ex-Googler
2011/10/25 23:40:59
I think this should probably be -isDescendentOfWin
Ilya Sherman
2011/10/26 23:22:40
I just added this because I wasn't sure if we coul
| |
| 184 @end | |
| 185 | |
| 168 @interface AppController (Private) | 186 @interface AppController (Private) |
| 169 - (void)initMenuState; | 187 - (void)initMenuState; |
| 170 - (void)initProfileMenu; | 188 - (void)initProfileMenu; |
| 171 - (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item; | 189 - (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item; |
| 172 - (void)registerServicesMenuTypesTo:(NSApplication*)app; | 190 - (void)registerServicesMenuTypesTo:(NSApplication*)app; |
| 173 - (void)openUrls:(const std::vector<GURL>&)urls; | 191 - (void)openUrls:(const std::vector<GURL>&)urls; |
| 174 - (void)getUrl:(NSAppleEventDescriptor*)event | 192 - (void)getUrl:(NSAppleEventDescriptor*)event |
| 175 withReply:(NSAppleEventDescriptor*)reply; | 193 withReply:(NSAppleEventDescriptor*)reply; |
| 176 - (void)submitCloudPrintJob:(NSAppleEventDescriptor*)event; | 194 - (void)submitCloudPrintJob:(NSAppleEventDescriptor*)event; |
| 177 - (void)installCloudPrint:(NSAppleEventDescriptor*)event; | 195 - (void)installCloudPrint:(NSAppleEventDescriptor*)event; |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 345 if (!BrowserList::HasBrowserWithProfile([self lastProfile])) { | 363 if (!BrowserList::HasBrowserWithProfile([self lastProfile])) { |
| 346 // As we're shutting down, we need to nuke the TabRestoreService, which | 364 // As we're shutting down, we need to nuke the TabRestoreService, which |
| 347 // will start the shutdown of the NavigationControllers and allow for | 365 // will start the shutdown of the NavigationControllers and allow for |
| 348 // proper shutdown. If we don't do this, Chrome won't shut down cleanly, | 366 // proper shutdown. If we don't do this, Chrome won't shut down cleanly, |
| 349 // and may end up crashing when some thread tries to use the IO thread (or | 367 // and may end up crashing when some thread tries to use the IO thread (or |
| 350 // another thread) that is no longer valid. | 368 // another thread) that is no longer valid. |
| 351 TabRestoreServiceFactory::ResetForProfile([self lastProfile]); | 369 TabRestoreServiceFactory::ResetForProfile([self lastProfile]); |
| 352 } | 370 } |
| 353 } | 371 } |
| 354 | 372 |
| 355 // Helper routine to get the window controller if the key window is a tabbed | |
| 356 // window, or nil if not. Examples of non-tabbed windows are "about" or | |
| 357 // "preferences". | |
| 358 - (TabWindowController*)keyWindowTabController { | |
| 359 NSWindowController* keyWindowController = | |
| 360 [[NSApp keyWindow] windowController]; | |
| 361 if ([keyWindowController isKindOfClass:[TabWindowController class]]) | |
| 362 return (TabWindowController*)keyWindowController; | |
| 363 | |
| 364 return nil; | |
| 365 } | |
| 366 | |
| 367 // Helper routine to get the window controller if the main window is a tabbed | |
| 368 // window, or nil if not. Examples of non-tabbed windows are "about" or | |
| 369 // "preferences". | |
| 370 - (TabWindowController*)mainWindowTabController { | |
| 371 NSWindowController* mainWindowController = | |
| 372 [[NSApp mainWindow] windowController]; | |
| 373 if ([mainWindowController isKindOfClass:[TabWindowController class]]) | |
| 374 return (TabWindowController*)mainWindowController; | |
| 375 | |
| 376 return nil; | |
| 377 } | |
| 378 | |
| 379 // If the window has a tab controller, make "close window" be cmd-shift-w, | 373 // If the window has a tab controller, make "close window" be cmd-shift-w, |
| 380 // otherwise leave it as the normal cmd-w. Capitalization of the key equivalent | 374 // otherwise leave it as the normal cmd-w. Capitalization of the key equivalent |
| 381 // affects whether the shift modifer is used. | 375 // affects whether the shift modifer is used. |
| 382 - (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)hasTabs { | 376 - (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)hasTabs { |
| 383 [closeWindowMenuItem_ setKeyEquivalent:(hasTabs ? @"W" : @"w")]; | 377 [closeWindowMenuItem_ setKeyEquivalent:(hasTabs ? @"W" : @"w")]; |
| 384 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask]; | 378 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask]; |
| 385 } | 379 } |
| 386 | 380 |
| 387 // If the window has a tab controller, make "close tab" take over cmd-w, | 381 // If the window has a tab controller, make "close tab" take over cmd-w, |
| 388 // otherwise it shouldn't have any key-equivalent because it should be disabled. | 382 // otherwise it shouldn't have any key-equivalent because it should be disabled. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 399 // Explicitly remove any command-key equivalents from the close tab/window | 393 // Explicitly remove any command-key equivalents from the close tab/window |
| 400 // menus so that nothing can go haywire if we get a user action during pending | 394 // menus so that nothing can go haywire if we get a user action during pending |
| 401 // updates. | 395 // updates. |
| 402 - (void)clearCloseMenuItemKeyEquivalents { | 396 - (void)clearCloseMenuItemKeyEquivalents { |
| 403 [closeTabMenuItem_ setKeyEquivalent:@""]; | 397 [closeTabMenuItem_ setKeyEquivalent:@""]; |
| 404 [closeTabMenuItem_ setKeyEquivalentModifierMask:0]; | 398 [closeTabMenuItem_ setKeyEquivalentModifierMask:0]; |
| 405 [closeWindowMenuItem_ setKeyEquivalent:@""]; | 399 [closeWindowMenuItem_ setKeyEquivalent:@""]; |
| 406 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0]; | 400 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0]; |
| 407 } | 401 } |
| 408 | 402 |
| 409 // See if we have a window with tabs open, and adjust the key equivalents for | 403 // See if the focused window window has tabs, and adjust the key equivalents for |
| 410 // Close Tab/Close Window accordingly. | 404 // Close Tab/Close Window accordingly. |
| 411 - (void)fixCloseMenuItemKeyEquivalents:(NSWindow*)window { | 405 - (void)fixCloseMenuItemKeyEquivalents { |
| 412 fileMenuUpdatePending_ = NO; | 406 fileMenuUpdatePending_ = NO; |
| 413 TabWindowController* tabController = [self keyWindowTabController]; | 407 |
| 414 if (!tabController && ![NSApp keyWindow]) { | 408 NSWindow* window = [NSApp keyWindow]; |
| 415 // There might be a small amount of time where there is no key window, | 409 NSWindow* mainWindow = [NSApp mainWindow]; |
| 416 // so just use our main browser window if there is one. | 410 if (!window || [window isDescendantOf:mainWindow]) { |
|
Scott Hess - ex-Googler
2011/10/25 23:40:59
Wow, can window be nil and mainWindow not nil? Cr
Ilya Sherman
2011/10/26 23:22:40
Apparently, assuming the comment that used to be h
| |
| 417 tabController = [self mainWindowTabController]; | 411 // If the key window is a child of the main window (e.g. a bubble), the main |
| 412 // window should be the one that handles the close menu item action. | |
| 413 // Also, there might be a small amount of time where there is no key window; | |
| 414 // in that case as well, just use our main browser window if there is one. | |
| 415 // You might think that we should just always use the main window, but the | |
| 416 // "About Chrome" window serves as a counterexample. | |
| 417 window = mainWindow; | |
| 418 } | 418 } |
| 419 BOOL hasTabs = !!tabController; | |
| 420 | 419 |
| 420 BOOL hasTabs = | |
| 421 [[window windowController] isKindOfClass:[TabWindowController class]]; | |
| 421 [self adjustCloseWindowMenuItemKeyEquivalent:hasTabs]; | 422 [self adjustCloseWindowMenuItemKeyEquivalent:hasTabs]; |
| 422 [self adjustCloseTabMenuItemKeyEquivalent:hasTabs]; | 423 [self adjustCloseTabMenuItemKeyEquivalent:hasTabs]; |
| 423 } | 424 } |
| 424 | 425 |
| 425 // Fix up the "close tab/close window" command-key equivalents. We do this | 426 // Fix up the "close tab/close window" command-key equivalents. We do this |
| 426 // after a delay to ensure that window layer state has been set by the time | 427 // after a delay to ensure that window layer state has been set by the time |
| 427 // we do the enabling. This should only be called on the main thread, code that | 428 // we do the enabling. This should only be called on the main thread, code that |
| 428 // calls this (even as a side-effect) from other threads needs to be fixed. | 429 // calls this (even as a side-effect) from other threads needs to be fixed. |
| 429 - (void)delayedFixCloseMenuItemKeyEquivalents:(NSNotification*)notify { | 430 - (void)delayedFixCloseMenuItemKeyEquivalents { |
| 430 DCHECK([NSThread isMainThread]); | 431 DCHECK([NSThread isMainThread]); |
| 431 if (!fileMenuUpdatePending_) { | 432 if (!fileMenuUpdatePending_) { |
| 432 // The OS prefers keypresses to timers, so it's possible that a cmd-w | 433 // The OS prefers keypresses to timers, so it's possible that a cmd-w |
| 433 // can sneak in before this timer fires. In order to prevent that from | 434 // can sneak in before this timer fires. In order to prevent that from |
| 434 // having any bad consequences, just clear the keys combos altogether. They | 435 // having any bad consequences, just clear the keys combos altogether. They |
| 435 // will be reset when the timer eventually fires. | 436 // will be reset when the timer eventually fires. |
| 436 if ([NSThread isMainThread]) { | 437 if ([NSThread isMainThread]) { |
| 437 fileMenuUpdatePending_ = YES; | 438 fileMenuUpdatePending_ = YES; |
| 438 [self clearCloseMenuItemKeyEquivalents]; | 439 [self clearCloseMenuItemKeyEquivalents]; |
| 439 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents:) | 440 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents) |
| 440 withObject:[notify object] | 441 withObject:nil |
| 441 afterDelay:0]; | 442 afterDelay:0]; |
| 442 } else { | 443 } else { |
| 443 // This shouldn't be happening, but if it does, force it to the main | 444 // This shouldn't be happening, but if it does, force it to the main |
| 444 // thread to avoid dropping the update. Don't mess with | 445 // thread to avoid dropping the update. Don't mess with |
| 445 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and | 446 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and |
| 446 // there could be a race between the selector finishing and setting the | 447 // there could be a race between the selector finishing and setting the |
| 447 // flag. | 448 // flag. |
| 448 [self | 449 [self |
| 449 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents:) | 450 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents) |
| 450 withObject:[notify object] | 451 withObject:nil |
| 451 waitUntilDone:NO]; | 452 waitUntilDone:NO]; |
| 452 } | 453 } |
| 453 } | 454 } |
| 454 } | 455 } |
| 455 | 456 |
| 456 // Called when we get a notification about the window layering changing to | 457 // Called when we get a notification about the window layering changing to |
| 457 // update the UI based on the new main window. | 458 // update the UI based on the new main window. |
| 458 - (void)windowLayeringDidChange:(NSNotification*)notify { | 459 - (void)windowLayeringDidChange:(NSNotification*)notify { |
| 459 [self delayedFixCloseMenuItemKeyEquivalents:notify]; | 460 [self delayedFixCloseMenuItemKeyEquivalents]; |
| 460 | 461 |
| 461 if ([notify name] == NSWindowDidResignKeyNotification) { | 462 if ([notify name] == NSWindowDidResignKeyNotification) { |
| 462 // If a window is closed, this notification is fired but |[NSApp keyWindow]| | 463 // If a window is closed, this notification is fired but |[NSApp keyWindow]| |
| 463 // returns nil regardless of whether any suitable candidates for the key | 464 // returns nil regardless of whether any suitable candidates for the key |
| 464 // window remain. It seems that the new key window for the app is not set | 465 // window remain. It seems that the new key window for the app is not set |
| 465 // until after this notification is fired, so a check is performed after the | 466 // until after this notification is fired, so a check is performed after the |
| 466 // run loop is allowed to spin. | 467 // run loop is allowed to spin. |
| 467 [self performSelector:@selector(checkForAnyKeyWindows) | 468 [self performSelector:@selector(checkForAnyKeyWindows) |
| 468 withObject:nil | 469 withObject:nil |
| 469 afterDelay:0.0]; | 470 afterDelay:0.0]; |
| (...skipping 813 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1283 | 1284 |
| 1284 } // namespace browser | 1285 } // namespace browser |
| 1285 | 1286 |
| 1286 namespace app_controller_mac { | 1287 namespace app_controller_mac { |
| 1287 | 1288 |
| 1288 bool IsOpeningNewWindow() { | 1289 bool IsOpeningNewWindow() { |
| 1289 return g_is_opening_new_window; | 1290 return g_is_opening_new_window; |
| 1290 } | 1291 } |
| 1291 | 1292 |
| 1292 } // namespace app_controller_mac | 1293 } // namespace app_controller_mac |
| OLD | NEW |