Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include <objc/objc-exception.h> | |
| 8 | |
| 7 #import "base/auto_reset.h" | 9 #import "base/auto_reset.h" |
| 8 #include "base/debug/crash_logging.h" | 10 #include "base/debug/crash_logging.h" |
| 9 #include "base/debug/stack_trace.h" | 11 #include "base/debug/stack_trace.h" |
| 10 #import "base/logging.h" | 12 #import "base/logging.h" |
| 13 #include "base/mac/call_with_eh_frame.h" | |
| 11 #import "base/mac/scoped_nsexception_enabler.h" | 14 #import "base/mac/scoped_nsexception_enabler.h" |
| 12 #import "base/mac/scoped_nsobject.h" | 15 #import "base/mac/scoped_nsobject.h" |
| 13 #import "base/mac/scoped_objc_class_swizzler.h" | 16 #import "base/mac/scoped_objc_class_swizzler.h" |
| 14 #import "base/metrics/histogram.h" | 17 #import "base/metrics/histogram.h" |
| 15 #include "base/profiler/scoped_tracker.h" | 18 #include "base/profiler/scoped_tracker.h" |
| 16 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 17 #import "base/strings/sys_string_conversions.h" | 20 #import "base/strings/sys_string_conversions.h" |
| 18 #import "chrome/browser/app_controller_mac.h" | 21 #import "chrome/browser/app_controller_mac.h" |
| 19 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 22 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" |
| 20 #include "chrome/common/crash_keys.h" | 23 #include "chrome/common/crash_keys.h" |
| 21 #import "chrome/common/mac/objc_zombie.h" | 24 #import "chrome/common/mac/objc_zombie.h" |
| 22 #include "content/public/browser/browser_accessibility_state.h" | 25 #include "content/public/browser/browser_accessibility_state.h" |
| 23 #include "content/public/browser/render_view_host.h" | 26 #include "content/public/browser/render_view_host.h" |
| 24 #include "content/public/browser/web_contents.h" | 27 #include "content/public/browser/web_contents.h" |
| 25 | 28 |
| 26 namespace { | 29 namespace chrome_browser_application_mac { |
| 27 | 30 |
| 28 // Tracking for cases being hit by -crInitWithName:reason:userInfo:. | 31 objc_exception_preprocessor g_next_preprocessor = nullptr; |
| 29 enum ExceptionEventType { | |
| 30 EXCEPTION_ACCESSIBILITY = 0, | |
| 31 EXCEPTION_MENU_ITEM_BOUNDS_CHECK, | |
| 32 EXCEPTION_VIEW_NOT_IN_WINDOW, | |
| 33 EXCEPTION_NSURL_INIT_NIL, | |
| 34 EXCEPTION_NSDATADETECTOR_NIL_STRING, | |
| 35 EXCEPTION_NSREGULAREXPRESSION_NIL_STRING, | |
| 36 | |
| 37 // Always keep this at the end. | |
| 38 EXCEPTION_MAX, | |
| 39 }; | |
| 40 | |
| 41 void RecordExceptionEvent(ExceptionEventType event_type) { | |
| 42 UMA_HISTOGRAM_ENUMERATION("OSX.ExceptionHandlerEvents", | |
| 43 event_type, EXCEPTION_MAX); | |
| 44 } | |
| 45 | |
| 46 } // namespace | |
| 47 | |
| 48 // The implementation of NSExceptions break various assumptions in the | |
| 49 // Chrome code. This category defines a replacement for | |
| 50 // -initWithName:reason:userInfo: for purposes of forcing a break in | |
| 51 // the debugger when an exception is raised. -raise sounds more | |
| 52 // obvious to intercept, but it doesn't catch the original throw | |
| 53 // because the objc runtime doesn't use it. | |
| 54 @interface NSException (CrNSExceptionSwizzle) | |
| 55 - (id)crInitWithName:(NSString*)aName | |
| 56 reason:(NSString*)aReason | |
| 57 userInfo:(NSDictionary*)someUserInfo; | |
| 58 @end | |
| 59 | |
| 60 static IMP gOriginalInitIMP = NULL; | |
| 61 | |
| 62 @implementation NSException (CrNSExceptionSwizzle) | |
| 63 - (id)crInitWithName:(NSString*)aName | |
| 64 reason:(NSString*)aReason | |
| 65 userInfo:(NSDictionary*)someUserInfo { | |
| 66 // Method only called when swizzled. | |
| 67 DCHECK(_cmd == @selector(initWithName:reason:userInfo:)); | |
| 68 DCHECK(gOriginalInitIMP); | |
| 69 | |
| 70 // Parts of Cocoa rely on creating and throwing exceptions. These are not | |
| 71 // worth bugging-out over. It is very important that there be zero chance that | |
| 72 // any Chromium code is on the stack; these must be created by Apple code and | |
| 73 // then immediately consumed by Apple code. | |
| 74 static NSString* const kAcceptableNSExceptionNames[] = { | |
| 75 // If an object does not support an accessibility attribute, this will | |
| 76 // get thrown. | |
| 77 NSAccessibilityException, | |
| 78 }; | |
| 79 | |
| 80 BOOL found = NO; | |
| 81 for (size_t i = 0; i < arraysize(kAcceptableNSExceptionNames); ++i) { | |
| 82 if (aName == kAcceptableNSExceptionNames[i]) { | |
| 83 found = YES; | |
| 84 RecordExceptionEvent(EXCEPTION_ACCESSIBILITY); | |
| 85 break; | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 if (!found) { | |
| 90 // Update breakpad with the exception info. | |
| 91 std::string value = base::StringPrintf("%s reason %s", | |
| 92 [aName UTF8String], [aReason UTF8String]); | |
| 93 base::debug::SetCrashKeyValue(crash_keys::mac::kNSException, value); | |
| 94 base::debug::SetCrashKeyToStackTrace(crash_keys::mac::kNSExceptionTrace, | |
| 95 base::debug::StackTrace()); | |
| 96 | |
| 97 // Force crash for selected exceptions to generate crash dumps. | |
| 98 BOOL fatal = NO; | |
| 99 if (aName == NSInternalInconsistencyException) { | |
| 100 NSString* const kNSMenuItemArrayBoundsCheck = | |
| 101 @"Invalid parameter not satisfying: (index >= 0) && " | |
| 102 @"(index < [_itemArray count])"; | |
| 103 if ([aReason isEqualToString:kNSMenuItemArrayBoundsCheck]) { | |
| 104 RecordExceptionEvent(EXCEPTION_MENU_ITEM_BOUNDS_CHECK); | |
| 105 fatal = YES; | |
| 106 } | |
| 107 | |
| 108 NSString* const kNoWindowCheck = @"View is not in any window"; | |
| 109 if ([aReason isEqualToString:kNoWindowCheck]) { | |
| 110 RecordExceptionEvent(EXCEPTION_VIEW_NOT_IN_WINDOW); | |
| 111 fatal = YES; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 // Mostly "unrecognized selector sent to (instance|class)". A | |
| 116 // very small number of things like inappropriate nil being passed. | |
| 117 if (aName == NSInvalidArgumentException) { | |
| 118 fatal = YES; | |
| 119 | |
| 120 // TODO(shess): http://crbug.com/85463 throws this exception | |
| 121 // from ImageKit. Our code is not on the stack, so it needs to | |
| 122 // be whitelisted for now. | |
| 123 NSString* const kNSURLInitNilCheck = | |
| 124 @"*** -[NSURL initFileURLWithPath:isDirectory:]: " | |
| 125 @"nil string parameter"; | |
| 126 if ([aReason isEqualToString:kNSURLInitNilCheck]) { | |
| 127 RecordExceptionEvent(EXCEPTION_NSURL_INIT_NIL); | |
| 128 fatal = NO; | |
| 129 } | |
| 130 | |
| 131 // <http://crbug.com/316759> OSX 10.9 fails trying to extract | |
| 132 // structure from a string. | |
| 133 NSString* const kNSDataDetectorNilCheck = | |
| 134 @"*** -[NSDataDetector enumerateMatchesInString:" | |
| 135 @"options:range:usingBlock:]: nil argument"; | |
| 136 if ([aReason isEqualToString:kNSDataDetectorNilCheck]) { | |
| 137 RecordExceptionEvent(EXCEPTION_NSDATADETECTOR_NIL_STRING); | |
| 138 fatal = NO; | |
| 139 } | |
| 140 | |
| 141 // <http://crbug.com/466076> OSX 10.10 moved the method. | |
| 142 NSString* const kNSRegularExpressionNilCheck = | |
| 143 @"*** -[NSRegularExpression enumerateMatchesInString:" | |
| 144 @"options:range:usingBlock:]: nil argument"; | |
| 145 if ([aReason isEqualToString:kNSRegularExpressionNilCheck]) { | |
| 146 RecordExceptionEvent(EXCEPTION_NSREGULAREXPRESSION_NIL_STRING); | |
| 147 fatal = NO; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 // Dear reader: Something you just did provoked an NSException. | |
| 152 // NSException is implemented in terms of setjmp()/longjmp(), | |
| 153 // which does poor things when combined with C++ scoping | |
| 154 // (destructors are skipped). Chrome should be NSException-free, | |
| 155 // please check your backtrace and see if you can't file a bug | |
| 156 // with a repro case. | |
| 157 const bool allow = base::mac::GetNSExceptionsAllowed(); | |
| 158 if (fatal && !allow) { | |
| 159 LOG(FATAL) << "Someone is trying to raise an exception! " | |
| 160 << value; | |
| 161 } else { | |
| 162 // Make sure that developers see when their code throws | |
| 163 // exceptions. | |
| 164 DCHECK(allow) << "Someone is trying to raise an exception! " | |
| 165 << value; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 // Forward to the original version. | |
| 170 return gOriginalInitIMP(self, _cmd, aName, aReason, someUserInfo); | |
| 171 } | |
| 172 @end | |
| 173 | |
| 174 namespace chrome_browser_application_mac { | |
| 175 | 32 |
| 176 // Maximum number of known named exceptions we'll support. There is | 33 // Maximum number of known named exceptions we'll support. There is |
| 177 // no central registration, but I only find about 75 possibilities in | 34 // no central registration, but I only find about 75 possibilities in |
| 178 // the system frameworks, and many of them are probably not | 35 // the system frameworks, and many of them are probably not |
| 179 // interesting to track in aggregate (those relating to distributed | 36 // interesting to track in aggregate (those relating to distributed |
| 180 // objects, for instance). | 37 // objects, for instance). |
| 181 const size_t kKnownNSExceptionCount = 25; | 38 const size_t kKnownNSExceptionCount = 25; |
| 182 | 39 |
| 183 const size_t kUnknownNSException = kKnownNSExceptionCount; | 40 const size_t kUnknownNSException = kKnownNSExceptionCount; |
| 184 | 41 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 } | 77 } |
| 221 } | 78 } |
| 222 return kUnknownNSException; | 79 return kUnknownNSException; |
| 223 } | 80 } |
| 224 | 81 |
| 225 void RecordExceptionWithUma(NSException* exception) { | 82 void RecordExceptionWithUma(NSException* exception) { |
| 226 UMA_HISTOGRAM_ENUMERATION("OSX.NSException", | 83 UMA_HISTOGRAM_ENUMERATION("OSX.NSException", |
| 227 BinForException(exception), kUnknownNSException); | 84 BinForException(exception), kUnknownNSException); |
| 228 } | 85 } |
| 229 | 86 |
| 87 id ExceptionPreprocessor(id exception) { | |
| 88 static bool seen_first_exception = false; | |
| 89 | |
| 90 RecordExceptionWithUma(exception); | |
| 91 | |
| 92 const char* const kExceptionKey = | |
| 93 seen_first_exception ? crash_keys::mac::kLastNSException | |
| 94 : crash_keys::mac::kFirstNSException; | |
| 95 NSString* value = [NSString stringWithFormat:@"%@ reason %@", | |
| 96 [exception name], [exception reason]]; | |
| 97 base::debug::SetCrashKeyValue(kExceptionKey, [value UTF8String]); | |
| 98 | |
| 99 const char* const kExceptionTraceKey = | |
| 100 seen_first_exception ? crash_keys::mac::kLastNSExceptionTrace | |
| 101 : crash_keys::mac::kFirstNSExceptionTrace; | |
| 102 // This exception preprocessor runs prior to the one in libobjc, which sets | |
| 103 // the -[NSException callStackReturnAddresses]. | |
| 104 base::debug::SetCrashKeyToStackTrace(kExceptionTraceKey, | |
| 105 base::debug::StackTrace()); | |
| 106 | |
| 107 seen_first_exception = true; | |
| 108 | |
| 109 // Forward to the original version. | |
| 110 if (g_next_preprocessor) | |
| 111 return g_next_preprocessor(exception); | |
| 112 return exception; | |
| 113 } | |
| 114 | |
| 230 void RegisterBrowserCrApp() { | 115 void RegisterBrowserCrApp() { |
| 231 [BrowserCrApplication sharedApplication]; | 116 [BrowserCrApplication sharedApplication]; |
| 232 }; | 117 }; |
| 233 | 118 |
| 234 void Terminate() { | 119 void Terminate() { |
| 235 [NSApp terminate:nil]; | 120 [NSApp terminate:nil]; |
| 236 } | 121 } |
| 237 | 122 |
| 238 void CancelTerminate() { | 123 void CancelTerminate() { |
| 239 [NSApp cancelTerminate:nil]; | 124 [NSApp cancelTerminate:nil]; |
| 240 } | 125 } |
| 241 | 126 |
| 242 } // namespace chrome_browser_application_mac | 127 } // namespace chrome_browser_application_mac |
| 243 | 128 |
| 244 namespace { | |
| 245 | |
| 246 void SwizzleInit() { | |
| 247 // Do-nothing wrapper so that we can arrange to only swizzle | |
| 248 // -[NSException raise] when DCHECK() is turned on (as opposed to | |
| 249 // replicating the preprocess logic which turns DCHECK() on). | |
| 250 CR_DEFINE_STATIC_LOCAL(base::mac::ScopedObjCClassSwizzler, | |
| 251 swizzle_exception, | |
| 252 ([NSException class], | |
| 253 @selector(initWithName:reason:userInfo:), | |
| 254 @selector(crInitWithName:reason:userInfo:))); | |
| 255 gOriginalInitIMP = swizzle_exception.GetOriginalImplementation(); | |
| 256 } | |
| 257 | |
| 258 } // namespace | |
| 259 | |
| 260 // These methods are being exposed for the purposes of overriding. | 129 // These methods are being exposed for the purposes of overriding. |
| 261 // Used to determine when a Panel window can become the key window. | 130 // Used to determine when a Panel window can become the key window. |
| 262 @interface NSApplication (PanelsCanBecomeKey) | 131 @interface NSApplication (PanelsCanBecomeKey) |
| 263 - (void)_cycleWindowsReversed:(BOOL)arg1; | 132 - (void)_cycleWindowsReversed:(BOOL)arg1; |
| 264 - (id)_removeWindow:(NSWindow*)window; | 133 - (id)_removeWindow:(NSWindow*)window; |
| 265 - (id)_setKeyWindow:(NSWindow*)window; | 134 - (id)_setKeyWindow:(NSWindow*)window; |
| 266 @end | 135 @end |
| 267 | 136 |
| 268 @interface BrowserCrApplication (PrivateInternal) | 137 @interface BrowserCrApplication (PrivateInternal) |
| 269 | 138 |
| 270 // This must be called under the protection of previousKeyWindowsLock_. | 139 // This must be called under the protection of previousKeyWindowsLock_. |
| 271 - (void)removePreviousKeyWindow:(NSWindow*)window; | 140 - (void)removePreviousKeyWindow:(NSWindow*)window; |
| 272 | 141 |
| 273 @end | 142 @end |
| 274 | 143 |
| 275 @implementation BrowserCrApplication | 144 @implementation BrowserCrApplication |
| 276 | 145 |
| 277 + (void)initialize { | 146 + (void)initialize { |
| 278 // Turn all deallocated Objective-C objects into zombies, keeping | 147 // Turn all deallocated Objective-C objects into zombies, keeping |
| 279 // the most recent 10,000 of them on the treadmill. | 148 // the most recent 10,000 of them on the treadmill. |
| 280 ObjcEvilDoers::ZombieEnable(true, 10000); | 149 ObjcEvilDoers::ZombieEnable(true, 10000); |
| 150 | |
| 151 chrome_browser_application_mac::g_next_preprocessor = | |
| 152 objc_setExceptionPreprocessor( | |
| 153 &chrome_browser_application_mac::ExceptionPreprocessor); | |
|
Scott Hess - ex-Googler
2015/07/06 22:34:04
Suggest checking the global before calling this, i
Robert Sesek
2015/07/07 22:26:17
Done.
| |
| 281 } | 154 } |
| 282 | 155 |
| 283 - (id)init { | 156 - (id)init { |
| 284 SwizzleInit(); | |
| 285 self = [super init]; | 157 self = [super init]; |
| 286 | 158 |
| 287 // Sanity check to alert if overridden methods are not supported. | 159 // Sanity check to alert if overridden methods are not supported. |
| 288 DCHECK([NSApplication | 160 DCHECK([NSApplication |
| 289 instancesRespondToSelector:@selector(_cycleWindowsReversed:)]); | 161 instancesRespondToSelector:@selector(_cycleWindowsReversed:)]); |
| 290 DCHECK([NSApplication | 162 DCHECK([NSApplication |
| 291 instancesRespondToSelector:@selector(_removeWindow:)]); | 163 instancesRespondToSelector:@selector(_removeWindow:)]); |
| 292 DCHECK([NSApplication | 164 DCHECK([NSApplication |
| 293 instancesRespondToSelector:@selector(_setKeyWindow:)]); | 165 instancesRespondToSelector:@selector(_setKeyWindow:)]); |
| 294 | 166 |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 462 | 334 |
| 463 - (BOOL)isHandlingSendEvent { | 335 - (BOOL)isHandlingSendEvent { |
| 464 return handlingSendEvent_; | 336 return handlingSendEvent_; |
| 465 } | 337 } |
| 466 | 338 |
| 467 - (void)setHandlingSendEvent:(BOOL)handlingSendEvent { | 339 - (void)setHandlingSendEvent:(BOOL)handlingSendEvent { |
| 468 handlingSendEvent_ = handlingSendEvent; | 340 handlingSendEvent_ = handlingSendEvent; |
| 469 } | 341 } |
| 470 | 342 |
| 471 - (void)sendEvent:(NSEvent*)event { | 343 - (void)sendEvent:(NSEvent*)event { |
| 472 // tracked_objects::ScopedTracker does not support parameterized | 344 base::mac::CallWithEHFrame(^{ |
| 473 // instrumentations, so a big switch with each bunch instrumented is required. | 345 // tracked_objects::ScopedTracker does not support parameterized |
| 474 switch (event.type) { | 346 // instrumentations, so a big switch with each bunch instrumented is |
| 475 case NSLeftMouseDown: | 347 // required. |
| 476 case NSLeftMouseUp: | 348 switch (event.type) { |
| 477 case NSRightMouseDown: | 349 case NSLeftMouseDown: |
| 478 case NSRightMouseUp: | 350 case NSLeftMouseUp: |
| 479 case NSMouseMoved: | 351 case NSRightMouseDown: |
| 480 case NSLeftMouseDragged: | 352 case NSRightMouseUp: |
| 481 case NSRightMouseDragged: | 353 case NSMouseMoved: |
| 482 case NSMouseEntered: | 354 case NSLeftMouseDragged: |
| 483 case NSMouseExited: | 355 case NSRightMouseDragged: |
| 484 case NSOtherMouseDown: | 356 case NSMouseEntered: |
| 485 case NSOtherMouseUp: | 357 case NSMouseExited: |
| 486 case NSOtherMouseDragged: { | 358 case NSOtherMouseDown: |
| 487 tracked_objects::ScopedTracker tracking_profile( | 359 case NSOtherMouseUp: |
| 488 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 360 case NSOtherMouseDragged: { |
| 489 "463272 -[BrowserCrApplication sendEvent:] Mouse")); | 361 tracked_objects::ScopedTracker tracking_profile( |
| 490 base::mac::ScopedSendingEvent sendingEventScoper; | 362 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| 491 [super sendEvent:event]; | 363 "463272 -[BrowserCrApplication sendEvent:] Mouse")); |
| 492 break; | 364 base::mac::ScopedSendingEvent sendingEventScoper; |
| 365 [super sendEvent:event]; | |
| 366 break; | |
| 367 } | |
| 368 | |
| 369 case NSKeyDown: | |
| 370 case NSKeyUp: { | |
| 371 tracked_objects::ScopedTracker tracking_profile( | |
| 372 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 373 "463272 -[BrowserCrApplication sendEvent:] Key")); | |
| 374 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 375 [super sendEvent:event]; | |
| 376 break; | |
| 377 } | |
| 378 | |
| 379 case NSScrollWheel: { | |
| 380 tracked_objects::ScopedTracker tracking_profile( | |
| 381 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 382 "463272 -[BrowserCrApplication sendEvent:] ScrollWheel")); | |
| 383 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 384 [super sendEvent:event]; | |
| 385 break; | |
| 386 } | |
| 387 | |
| 388 case NSEventTypeGesture: | |
| 389 case NSEventTypeMagnify: | |
| 390 case NSEventTypeSwipe: | |
| 391 case NSEventTypeRotate: | |
| 392 case NSEventTypeBeginGesture: | |
| 393 case NSEventTypeEndGesture: { | |
| 394 tracked_objects::ScopedTracker tracking_profile( | |
| 395 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 396 "463272 -[BrowserCrApplication sendEvent:] Gesture")); | |
| 397 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 398 [super sendEvent:event]; | |
| 399 break; | |
| 400 } | |
| 401 | |
| 402 case NSAppKitDefined: { | |
| 403 tracked_objects::ScopedTracker tracking_profile( | |
| 404 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 405 "463272 -[BrowserCrApplication sendEvent:] AppKit")); | |
| 406 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 407 [super sendEvent:event]; | |
| 408 break; | |
| 409 } | |
| 410 | |
| 411 case NSSystemDefined: { | |
| 412 tracked_objects::ScopedTracker tracking_profile( | |
| 413 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 414 "463272 -[BrowserCrApplication sendEvent:] System")); | |
| 415 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 416 [super sendEvent:event]; | |
| 417 break; | |
| 418 } | |
| 419 | |
| 420 default: { | |
| 421 tracked_objects::ScopedTracker tracking_profile( | |
| 422 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 423 "463272 -[BrowserCrApplication sendEvent:] Other")); | |
| 424 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 425 [super sendEvent:event]; | |
| 426 } | |
| 493 } | 427 } |
| 494 | 428 }); |
| 495 case NSKeyDown: | |
| 496 case NSKeyUp: { | |
| 497 tracked_objects::ScopedTracker tracking_profile( | |
| 498 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 499 "463272 -[BrowserCrApplication sendEvent:] Key")); | |
| 500 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 501 [super sendEvent:event]; | |
| 502 break; | |
| 503 } | |
| 504 | |
| 505 case NSScrollWheel: { | |
| 506 tracked_objects::ScopedTracker tracking_profile( | |
| 507 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 508 "463272 -[BrowserCrApplication sendEvent:] ScrollWheel")); | |
| 509 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 510 [super sendEvent:event]; | |
| 511 break; | |
| 512 } | |
| 513 | |
| 514 case NSEventTypeGesture: | |
| 515 case NSEventTypeMagnify: | |
| 516 case NSEventTypeSwipe: | |
| 517 case NSEventTypeRotate: | |
| 518 case NSEventTypeBeginGesture: | |
| 519 case NSEventTypeEndGesture: { | |
| 520 tracked_objects::ScopedTracker tracking_profile( | |
| 521 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 522 "463272 -[BrowserCrApplication sendEvent:] Gesture")); | |
| 523 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 524 [super sendEvent:event]; | |
| 525 break; | |
| 526 } | |
| 527 | |
| 528 case NSAppKitDefined: { | |
| 529 tracked_objects::ScopedTracker tracking_profile( | |
| 530 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 531 "463272 -[BrowserCrApplication sendEvent:] AppKit")); | |
| 532 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 533 [super sendEvent:event]; | |
| 534 break; | |
| 535 } | |
| 536 | |
| 537 case NSSystemDefined: { | |
| 538 tracked_objects::ScopedTracker tracking_profile( | |
| 539 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 540 "463272 -[BrowserCrApplication sendEvent:] System")); | |
| 541 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 542 [super sendEvent:event]; | |
| 543 break; | |
| 544 } | |
| 545 | |
| 546 default: { | |
| 547 tracked_objects::ScopedTracker tracking_profile( | |
| 548 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 549 "463272 -[BrowserCrApplication sendEvent:] Other")); | |
| 550 base::mac::ScopedSendingEvent sendingEventScoper; | |
| 551 [super sendEvent:event]; | |
| 552 } | |
| 553 } | |
| 554 } | |
| 555 | |
| 556 // NSExceptions which are caught by the event loop are logged here. | |
| 557 // NSException uses setjmp/longjmp, which can be very bad for C++, so | |
| 558 // we attempt to track and report them. | |
| 559 - (void)reportException:(NSException *)anException { | |
| 560 // If we throw an exception in this code, we can create an infinite | |
| 561 // loop. If we throw out of the if() without resetting | |
| 562 // |reportException|, we'll stop reporting exceptions for this run. | |
| 563 static BOOL reportingException = NO; | |
| 564 DCHECK(!reportingException); | |
| 565 if (!reportingException) { | |
| 566 reportingException = YES; | |
| 567 chrome_browser_application_mac::RecordExceptionWithUma(anException); | |
| 568 | |
| 569 // http://crbug.com/45928 is a bug about needing to double-close | |
| 570 // windows sometimes. One theory is that |-isHandlingSendEvent| | |
| 571 // gets latched to always return |YES|. Since scopers are used to | |
| 572 // manipulate that value, that should not be possible. One way to | |
| 573 // sidestep scopers is setjmp/longjmp (see above). The following | |
| 574 // is to "fix" this while the more fundamental concern is | |
| 575 // addressed elsewhere. | |
| 576 [self setHandlingSendEvent:NO]; | |
| 577 | |
| 578 // If |ScopedNSExceptionEnabler| is used to allow exceptions, and an | |
| 579 // uncaught exception is thrown, it will throw past all of the scopers. | |
| 580 // Reset the flag so that future exceptions are not masked. | |
| 581 base::mac::SetNSExceptionsAllowed(false); | |
| 582 | |
| 583 // Store some human-readable information in breakpad keys in case | |
| 584 // there is a crash. Since breakpad does not provide infinite | |
| 585 // storage, we track two exceptions. The first exception thrown | |
| 586 // is tracked because it may be the one which caused the system to | |
| 587 // go off the rails. The last exception thrown is tracked because | |
| 588 // it may be the one most directly associated with the crash. | |
| 589 static BOOL trackedFirstException = NO; | |
| 590 | |
| 591 const char* const kExceptionKey = | |
| 592 trackedFirstException ? crash_keys::mac::kLastNSException | |
| 593 : crash_keys::mac::kFirstNSException; | |
| 594 NSString* value = [NSString stringWithFormat:@"%@ reason %@", | |
| 595 [anException name], [anException reason]]; | |
| 596 base::debug::SetCrashKeyValue(kExceptionKey, [value UTF8String]); | |
| 597 | |
| 598 // Encode the callstack from point of throw. | |
| 599 // TODO(shess): Our swizzle plus the 23-frame limit plus Cocoa | |
| 600 // overhead may make this less than useful. If so, perhaps skip | |
| 601 // some items and/or use two keys. | |
| 602 const char* const kExceptionBtKey = | |
| 603 trackedFirstException ? crash_keys::mac::kLastNSExceptionTrace | |
| 604 : crash_keys::mac::kFirstNSExceptionTrace; | |
| 605 NSArray* addressArray = [anException callStackReturnAddresses]; | |
| 606 NSUInteger addressCount = [addressArray count]; | |
| 607 if (addressCount) { | |
| 608 // SetCrashKeyFromAddresses() only encodes 23, so that's a natural limit. | |
| 609 const NSUInteger kAddressCountMax = 23; | |
| 610 void* addresses[kAddressCountMax]; | |
| 611 if (addressCount > kAddressCountMax) | |
| 612 addressCount = kAddressCountMax; | |
| 613 | |
| 614 for (NSUInteger i = 0; i < addressCount; ++i) { | |
| 615 addresses[i] = reinterpret_cast<void*>( | |
| 616 [[addressArray objectAtIndex:i] unsignedIntegerValue]); | |
| 617 } | |
| 618 base::debug::SetCrashKeyFromAddresses( | |
| 619 kExceptionBtKey, addresses, static_cast<size_t>(addressCount)); | |
| 620 } else { | |
| 621 base::debug::ClearCrashKey(kExceptionBtKey); | |
| 622 } | |
| 623 trackedFirstException = YES; | |
| 624 | |
| 625 reportingException = NO; | |
| 626 } | |
| 627 | |
| 628 [super reportException:anException]; | |
| 629 } | 429 } |
| 630 | 430 |
| 631 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { | 431 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { |
| 632 // This is an undocument attribute that's set when VoiceOver is turned on/off. | 432 // This is an undocument attribute that's set when VoiceOver is turned on/off. |
| 633 if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) { | 433 if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) { |
| 634 content::BrowserAccessibilityState* accessibility_state = | 434 content::BrowserAccessibilityState* accessibility_state = |
| 635 content::BrowserAccessibilityState::GetInstance(); | 435 content::BrowserAccessibilityState::GetInstance(); |
| 636 if ([value intValue] == 1) | 436 if ([value intValue] == 1) |
| 637 accessibility_state->OnScreenReaderDetected(); | 437 accessibility_state->OnScreenReaderDetected(); |
| 638 else | 438 else |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 685 std::vector<NSWindow*>::iterator window_iterator = | 485 std::vector<NSWindow*>::iterator window_iterator = |
| 686 std::find(previousKeyWindows_.begin(), | 486 std::find(previousKeyWindows_.begin(), |
| 687 previousKeyWindows_.end(), | 487 previousKeyWindows_.end(), |
| 688 window); | 488 window); |
| 689 if (window_iterator != previousKeyWindows_.end()) { | 489 if (window_iterator != previousKeyWindows_.end()) { |
| 690 previousKeyWindows_.erase(window_iterator); | 490 previousKeyWindows_.erase(window_iterator); |
| 691 } | 491 } |
| 692 } | 492 } |
| 693 | 493 |
| 694 @end | 494 @end |
| OLD | NEW |