| 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/chrome_browser_application_mac.h" | 5 #import "chrome/browser/chrome_browser_application_mac.h" |
| 6 | 6 |
| 7 #import "base/logging.h" | 7 #import "base/logging.h" |
| 8 #import "base/mac/scoped_nsexception_enabler.h" |
| 8 #import "base/metrics/histogram.h" | 9 #import "base/metrics/histogram.h" |
| 9 #import "base/memory/scoped_nsobject.h" | 10 #import "base/memory/scoped_nsobject.h" |
| 10 #import "base/sys_string_conversions.h" | 11 #import "base/sys_string_conversions.h" |
| 11 #import "chrome/app/breakpad_mac.h" | 12 #import "chrome/app/breakpad_mac.h" |
| 12 #include "chrome/browser/accessibility/browser_accessibility_state.h" | 13 #include "chrome/browser/accessibility/browser_accessibility_state.h" |
| 13 #import "chrome/browser/app_controller_mac.h" | 14 #import "chrome/browser/app_controller_mac.h" |
| 14 #include "chrome/browser/ui/browser_list.h" | 15 #include "chrome/browser/ui/browser_list.h" |
| 15 #import "chrome/browser/ui/cocoa/objc_method_swizzle.h" | 16 #import "chrome/browser/ui/cocoa/objc_method_swizzle.h" |
| 16 #import "chrome/browser/ui/cocoa/objc_zombie.h" | 17 #import "chrome/browser/ui/cocoa/objc_zombie.h" |
| 17 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 18 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 if (aName == NSInvalidArgumentException) { | 87 if (aName == NSInvalidArgumentException) { |
| 87 fatal = YES; | 88 fatal = YES; |
| 88 } | 89 } |
| 89 | 90 |
| 90 // Dear reader: Something you just did provoked an NSException. | 91 // Dear reader: Something you just did provoked an NSException. |
| 91 // NSException is implemented in terms of setjmp()/longjmp(), | 92 // NSException is implemented in terms of setjmp()/longjmp(), |
| 92 // which does poor things when combined with C++ scoping | 93 // which does poor things when combined with C++ scoping |
| 93 // (destructors are skipped). Chrome should be NSException-free, | 94 // (destructors are skipped). Chrome should be NSException-free, |
| 94 // please check your backtrace and see if you can't file a bug | 95 // please check your backtrace and see if you can't file a bug |
| 95 // with a repro case. | 96 // with a repro case. |
| 96 if (fatal) { | 97 const bool allow = base::mac::GetNSExceptionsAllowed(); |
| 98 if (fatal && !allow) { |
| 97 LOG(FATAL) << "Someone is trying to raise an exception! " | 99 LOG(FATAL) << "Someone is trying to raise an exception! " |
| 98 << base::SysNSStringToUTF8(value); | 100 << base::SysNSStringToUTF8(value); |
| 99 } else { | 101 } else { |
| 100 // Make sure that developers see when their code throws | 102 // Make sure that developers see when their code throws |
| 101 // exceptions. | 103 // exceptions. |
| 102 DLOG(ERROR) << "Someone is trying to raise an exception! " | 104 DLOG(ERROR) << "Someone is trying to raise an exception! " |
| 103 << base::SysNSStringToUTF8(value); | 105 << base::SysNSStringToUTF8(value); |
| 104 NOTREACHED(); | 106 DCHECK(allow); |
| 105 } | 107 } |
| 106 } | 108 } |
| 107 | 109 |
| 108 // Forward to the original version. | 110 // Forward to the original version. |
| 109 return gOriginalInitIMP(self, _cmd, aName, aReason, someUserInfo); | 111 return gOriginalInitIMP(self, _cmd, aName, aReason, someUserInfo); |
| 110 } | 112 } |
| 111 @end | 113 @end |
| 112 | 114 |
| 113 namespace chrome_browser_application_mac { | 115 namespace chrome_browser_application_mac { |
| 114 | 116 |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 } else if ([sender isKindOfClass:[NSMenuItem class]]) { | 324 } else if ([sender isKindOfClass:[NSMenuItem class]]) { |
| 323 tag = [sender tag]; | 325 tag = [sender tag]; |
| 324 } | 326 } |
| 325 | 327 |
| 326 NSString* actionString = NSStringFromSelector(anAction); | 328 NSString* actionString = NSStringFromSelector(anAction); |
| 327 NSString* value = | 329 NSString* value = |
| 328 [NSString stringWithFormat:@"%@ tag %d sending %@ to %p", | 330 [NSString stringWithFormat:@"%@ tag %d sending %@ to %p", |
| 329 [sender className], tag, actionString, aTarget]; | 331 [sender className], tag, actionString, aTarget]; |
| 330 | 332 |
| 331 ScopedCrashKey key(kActionKey, value); | 333 ScopedCrashKey key(kActionKey, value); |
| 334 |
| 335 // Certain third-party code, such as print drivers, can still throw |
| 336 // exceptions and Chromium cannot fix them. This provides a way to |
| 337 // work around those on a spot basis. |
| 338 bool enableNSExceptions = false; |
| 339 |
| 340 // http://crbug.com/80686 , an Epson printer driver. |
| 341 if (anAction == @selector(selectPDE:)) { |
| 342 enableNSExceptions = true; |
| 343 } |
| 344 |
| 345 // Minimize the window by keeping this close to the super call. |
| 346 scoped_ptr<base::mac::ScopedNSExceptionEnabler> enabler(NULL); |
| 347 if (enableNSExceptions) |
| 348 enabler.reset(new base::mac::ScopedNSExceptionEnabler()); |
| 332 return [super sendAction:anAction to:aTarget from:sender]; | 349 return [super sendAction:anAction to:aTarget from:sender]; |
| 333 } | 350 } |
| 334 | 351 |
| 335 // NSExceptions which are caught by the event loop are logged here. | 352 // NSExceptions which are caught by the event loop are logged here. |
| 336 // NSException uses setjmp/longjmp, which can be very bad for C++, so | 353 // NSException uses setjmp/longjmp, which can be very bad for C++, so |
| 337 // we attempt to track and report them. | 354 // we attempt to track and report them. |
| 338 - (void)reportException:(NSException *)anException { | 355 - (void)reportException:(NSException *)anException { |
| 339 // If we throw an exception in this code, we can create an infinite | 356 // If we throw an exception in this code, we can create an infinite |
| 340 // loop. If we throw out of the if() without resetting | 357 // loop. If we throw out of the if() without resetting |
| 341 // |reportException|, we'll stop reporting exceptions for this run. | 358 // |reportException|, we'll stop reporting exceptions for this run. |
| 342 static BOOL reportingException = NO; | 359 static BOOL reportingException = NO; |
| 343 DCHECK(!reportingException); | 360 DCHECK(!reportingException); |
| 344 if (!reportingException) { | 361 if (!reportingException) { |
| 345 reportingException = YES; | 362 reportingException = YES; |
| 346 chrome_browser_application_mac::RecordExceptionWithUma(anException); | 363 chrome_browser_application_mac::RecordExceptionWithUma(anException); |
| 347 | 364 |
| 348 // http://crbug.com/45928 is a bug about needing to double-close | 365 // http://crbug.com/45928 is a bug about needing to double-close |
| 349 // windows sometimes. One theory is that |-isHandlingSendEvent| | 366 // windows sometimes. One theory is that |-isHandlingSendEvent| |
| 350 // gets latched to always return |YES|. Since scopers are used to | 367 // gets latched to always return |YES|. Since scopers are used to |
| 351 // manipulate that value, that should not be possible. One way to | 368 // manipulate that value, that should not be possible. One way to |
| 352 // sidestep scopers is setjmp/longjmp (see above). The following | 369 // sidestep scopers is setjmp/longjmp (see above). The following |
| 353 // is to "fix" this while the more fundamental concern is | 370 // is to "fix" this while the more fundamental concern is |
| 354 // addressed elsewhere. | 371 // addressed elsewhere. |
| 355 [self clearIsHandlingSendEvent]; | 372 [self clearIsHandlingSendEvent]; |
| 356 | 373 |
| 374 // If |ScopedNSExceptionEnabler| is used to allow exceptions, and an |
| 375 // uncaught exception is thrown, it will throw past all of the scopers. |
| 376 // Reset the flag so that future exceptions are not masked. |
| 377 base::mac::SetNSExceptionsAllowed(false); |
| 378 |
| 357 // Store some human-readable information in breakpad keys in case | 379 // Store some human-readable information in breakpad keys in case |
| 358 // there is a crash. Since breakpad does not provide infinite | 380 // there is a crash. Since breakpad does not provide infinite |
| 359 // storage, we track two exceptions. The first exception thrown | 381 // storage, we track two exceptions. The first exception thrown |
| 360 // is tracked because it may be the one which caused the system to | 382 // is tracked because it may be the one which caused the system to |
| 361 // go off the rails. The last exception thrown is tracked because | 383 // go off the rails. The last exception thrown is tracked because |
| 362 // it may be the one most directly associated with the crash. | 384 // it may be the one most directly associated with the crash. |
| 363 static NSString* const kFirstExceptionKey = @"firstexception"; | 385 static NSString* const kFirstExceptionKey = @"firstexception"; |
| 364 static BOOL trackedFirstException = NO; | 386 static BOOL trackedFirstException = NO; |
| 365 static NSString* const kLastExceptionKey = @"lastexception"; | 387 static NSString* const kLastExceptionKey = @"lastexception"; |
| 366 | 388 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 if (RenderViewHost* rvh = contents->render_view_host()) { | 421 if (RenderViewHost* rvh = contents->render_view_host()) { |
| 400 rvh->EnableRendererAccessibility(); | 422 rvh->EnableRendererAccessibility(); |
| 401 } | 423 } |
| 402 } | 424 } |
| 403 } | 425 } |
| 404 } | 426 } |
| 405 return [super accessibilitySetValue:value forAttribute:attribute]; | 427 return [super accessibilitySetValue:value forAttribute:attribute]; |
| 406 } | 428 } |
| 407 | 429 |
| 408 @end | 430 @end |
| OLD | NEW |