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 |