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 |