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