Run canExecute before executing delete command.
[chromium-blink-merge.git] / chrome / browser / chrome_browser_application_mac.mm
blobe648774a16a5bc68567109486ab69b097578272b
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
3 // found in the LICENSE file.
5 #import "chrome/browser/chrome_browser_application_mac.h"
7 #import "base/auto_reset.h"
8 #include "base/debug/crash_logging.h"
9 #include "base/debug/stack_trace.h"
10 #import "base/logging.h"
11 #import "base/mac/scoped_nsexception_enabler.h"
12 #import "base/mac/scoped_nsobject.h"
13 #import "base/mac/scoped_objc_class_swizzler.h"
14 #import "base/metrics/histogram.h"
15 #include "base/profiler/scoped_tracker.h"
16 #include "base/strings/stringprintf.h"
17 #import "base/strings/sys_string_conversions.h"
18 #import "chrome/browser/app_controller_mac.h"
19 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
20 #include "chrome/common/crash_keys.h"
21 #import "chrome/common/mac/objc_zombie.h"
22 #include "content/public/browser/browser_accessibility_state.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/web_contents.h"
26 namespace {
28 // Tracking for cases being hit by -crInitWithName:reason:userInfo:.
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,
37   // Always keep this at the end.
38   EXCEPTION_MAX,
41 void RecordExceptionEvent(ExceptionEventType event_type) {
42   UMA_HISTOGRAM_ENUMERATION("OSX.ExceptionHandlerEvents",
43                             event_type, EXCEPTION_MAX);
46 }  // namespace
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
60 static IMP gOriginalInitIMP = NULL;
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);
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   };
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   }
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());
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       }
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     }
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;
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       }
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       }
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     }
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   }
169   // Forward to the original version.
170   return gOriginalInitIMP(self, _cmd, aName, aReason, someUserInfo);
172 @end
174 namespace chrome_browser_application_mac {
176 // Maximum number of known named exceptions we'll support.  There is
177 // no central registration, but I only find about 75 possibilities in
178 // the system frameworks, and many of them are probably not
179 // interesting to track in aggregate (those relating to distributed
180 // objects, for instance).
181 const size_t kKnownNSExceptionCount = 25;
183 const size_t kUnknownNSException = kKnownNSExceptionCount;
185 size_t BinForException(NSException* exception) {
186   // A list of common known exceptions.  The list position will
187   // determine where they live in the histogram, so never move them
188   // around, only add to the end.
189   static NSString* const kKnownNSExceptionNames[] = {
190     // Grab-bag exception, not very common.  CFArray (or other
191     // container) mutated while being enumerated is one case seen in
192     // production.
193     NSGenericException,
195     // Out-of-range on NSString or NSArray.  Quite common.
196     NSRangeException,
198     // Invalid arg to method, unrecognized selector.  Quite common.
199     NSInvalidArgumentException,
201     // malloc() returned null in object creation, I think.  Turns out
202     // to be very uncommon in production, because of the OOM killer.
203     NSMallocException,
205     // This contains things like windowserver errors, trying to draw
206     // views which aren't in windows, unable to read nib files.  By
207     // far the most common exception seen on the crash server.
208     NSInternalInconsistencyException,
210     nil
211   };
213   // Make sure our array hasn't outgrown our abilities to track it.
214   DCHECK_LE(arraysize(kKnownNSExceptionNames), kKnownNSExceptionCount);
216   NSString* name = [exception name];
217   for (int i = 0; kKnownNSExceptionNames[i]; ++i) {
218     if (name == kKnownNSExceptionNames[i]) {
219       return i;
220     }
221   }
222   return kUnknownNSException;
225 void RecordExceptionWithUma(NSException* exception) {
226   UMA_HISTOGRAM_ENUMERATION("OSX.NSException",
227       BinForException(exception), kUnknownNSException);
230 void RegisterBrowserCrApp() {
231   [BrowserCrApplication sharedApplication];
234 void Terminate() {
235   [NSApp terminate:nil];
238 void CancelTerminate() {
239   [NSApp cancelTerminate:nil];
242 }  // namespace chrome_browser_application_mac
244 namespace {
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();
258 }  // namespace
260 // These methods are being exposed for the purposes of overriding.
261 // Used to determine when a Panel window can become the key window.
262 @interface NSApplication (PanelsCanBecomeKey)
263 - (void)_cycleWindowsReversed:(BOOL)arg1;
264 - (id)_removeWindow:(NSWindow*)window;
265 - (id)_setKeyWindow:(NSWindow*)window;
266 @end
268 @interface BrowserCrApplication (PrivateInternal)
270 // This must be called under the protection of previousKeyWindowsLock_.
271 - (void)removePreviousKeyWindow:(NSWindow*)window;
273 @end
275 @implementation BrowserCrApplication
277 + (void)initialize {
278   // Turn all deallocated Objective-C objects into zombies, keeping
279   // the most recent 10,000 of them on the treadmill.
280   ObjcEvilDoers::ZombieEnable(true, 10000);
283 - (id)init {
284   SwizzleInit();
285   self = [super init];
287   // Sanity check to alert if overridden methods are not supported.
288   DCHECK([NSApplication
289       instancesRespondToSelector:@selector(_cycleWindowsReversed:)]);
290   DCHECK([NSApplication
291       instancesRespondToSelector:@selector(_removeWindow:)]);
292   DCHECK([NSApplication
293       instancesRespondToSelector:@selector(_setKeyWindow:)]);
295   return self;
298 // Initialize NSApplication using the custom subclass.  Check whether NSApp
299 // was already initialized using another class, because that would break
300 // some things.
301 + (NSApplication*)sharedApplication {
302   NSApplication* app = [super sharedApplication];
304   // +sharedApplication initializes the global NSApp, so if a specific
305   // NSApplication subclass is requested, require that to be the one
306   // delivered.  The practical effect is to require a consistent NSApp
307   // across the executable.
308   CHECK([NSApp isKindOfClass:self])
309       << "NSApp must be of type " << [[self className] UTF8String]
310       << ", not " << [[NSApp className] UTF8String];
312   // If the message loop was initialized before NSApp is setup, the
313   // message pump will be setup incorrectly.  Failing this implies
314   // that RegisterBrowserCrApp() should be called earlier.
315   CHECK(base::MessagePumpMac::UsingCrApp())
316       << "MessagePumpMac::Create() is using the wrong pump implementation"
317       << " for " << [[self className] UTF8String];
319   return app;
322 ////////////////////////////////////////////////////////////////////////////////
323 // HISTORICAL COMMENT (by viettrungluu, from
324 // http://codereview.chromium.org/1520006 with mild editing):
326 // A quick summary of the state of things (before the changes to shutdown):
328 // Currently, we are totally hosed (put in a bad state in which Cmd-W does the
329 // wrong thing, and which will probably eventually lead to a crash) if we begin
330 // quitting but termination is aborted for some reason.
332 // I currently know of two ways in which termination can be aborted:
333 // (1) Common case: a window has an onbeforeunload handler which pops up a
334 //     "leave web page" dialog, and the user answers "no, don't leave".
335 // (2) Uncommon case: popups are enabled (in Content Settings, i.e., the popup
336 //     blocker is disabled), and some nasty web page pops up a new window on
337 //     closure.
339 // I don't know of other ways in which termination can be aborted, but they may
340 // exist (or may be added in the future, for that matter).
342 // My CL [see above] does the following:
343 // a. Should prevent being put in a bad state (which breaks Cmd-W and leads to
344 //    crash) under all circumstances.
345 // b. Should completely handle (1) properly.
346 // c. Doesn't (yet) handle (2) properly and puts it in a weird state (but not
347 //    that bad).
348 // d. Any other ways of aborting termination would put it in that weird state.
350 // c. can be fixed by having the global flag reset on browser creation or
351 // similar (and doing so might also fix some possible d.'s as well). I haven't
352 // done this yet since I haven't thought about it carefully and since it's a
353 // corner case.
355 // The weird state: a state in which closing the last window quits the browser.
356 // This might be a bit annoying, but it's not dangerous in any way.
357 ////////////////////////////////////////////////////////////////////////////////
359 // |-terminate:| is the entry point for orderly "quit" operations in Cocoa. This
360 // includes the application menu's quit menu item and keyboard equivalent, the
361 // application's dock icon menu's quit menu item, "quit" (not "force quit") in
362 // the Activity Monitor, and quits triggered by user logout and system restart
363 // and shutdown.
365 // The default |-terminate:| implementation ends the process by calling exit(),
366 // and thus never leaves the main run loop. This is unsuitable for Chrome since
367 // Chrome depends on leaving the main run loop to perform an orderly shutdown.
368 // We support the normal |-terminate:| interface by overriding the default
369 // implementation. Our implementation, which is very specific to the needs of
370 // Chrome, works by asking the application delegate to terminate using its
371 // |-tryToTerminateApplication:| method.
373 // |-tryToTerminateApplication:| differs from the standard
374 // |-applicationShouldTerminate:| in that no special event loop is run in the
375 // case that immediate termination is not possible (e.g., if dialog boxes
376 // allowing the user to cancel have to be shown). Instead, this method sets a
377 // flag and tries to close all browsers. This flag causes the closure of the
378 // final browser window to begin actual tear-down of the application.
379 // Termination is cancelled by resetting this flag. The standard
380 // |-applicationShouldTerminate:| is not supported, and code paths leading to it
381 // must be redirected.
383 // When the last browser has been destroyed, the BrowserList calls
384 // chrome::OnAppExiting(), which is the point of no return. That will cause
385 // the NSApplicationWillTerminateNotification to be posted, which ends the
386 // NSApplication event loop, so final post- MessageLoop::Run() work is done
387 // before exiting.
388 - (void)terminate:(id)sender {
389   AppController* appController = static_cast<AppController*>([NSApp delegate]);
390   [appController tryToTerminateApplication:self];
391   // Return, don't exit. The application is responsible for exiting on its own.
394 - (void)cancelTerminate:(id)sender {
395   AppController* appController = static_cast<AppController*>([NSApp delegate]);
396   [appController stopTryingToTerminateApplication:self];
399 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)sender {
400   // The Dock menu contains an automagic section where you can select
401   // amongst open windows.  If a window is closed via JavaScript while
402   // the menu is up, the menu item for that window continues to exist.
403   // When a window is selected this method is called with the
404   // now-freed window as |aTarget|.  Short-circuit the call if
405   // |aTarget| is not a valid window.
406   if (anAction == @selector(_selectWindow:)) {
407     // Not using -[NSArray containsObject:] because |aTarget| may be a
408     // freed object.
409     BOOL found = NO;
410     for (NSWindow* window in [self windows]) {
411       if (window == aTarget) {
412         found = YES;
413         break;
414       }
415     }
416     if (!found) {
417       return NO;
418     }
419   }
421   // When a Cocoa control is wired to a freed object, we get crashers
422   // in the call to |super| with no useful information in the
423   // backtrace.  Attempt to add some useful information.
425   // If the action is something generic like -commandDispatch:, then
426   // the tag is essential.
427   NSInteger tag = 0;
428   if ([sender isKindOfClass:[NSControl class]]) {
429     tag = [sender tag];
430     if (tag == 0 || tag == -1) {
431       tag = [sender selectedTag];
432     }
433   } else if ([sender isKindOfClass:[NSMenuItem class]]) {
434     tag = [sender tag];
435   }
437   NSString* actionString = NSStringFromSelector(anAction);
438   std::string value = base::StringPrintf("%s tag %ld sending %s to %p",
439       [[sender className] UTF8String],
440       static_cast<long>(tag),
441       [actionString UTF8String],
442       aTarget);
444   base::debug::ScopedCrashKey key(crash_keys::mac::kSendAction, value);
446   // Certain third-party code, such as print drivers, can still throw
447   // exceptions and Chromium cannot fix them.  This provides a way to
448   // work around those on a spot basis.
449   bool enableNSExceptions = false;
451   // http://crbug.com/80686 , an Epson printer driver.
452   if (anAction == @selector(selectPDE:)) {
453     enableNSExceptions = true;
454   }
456   // Minimize the window by keeping this close to the super call.
457   scoped_ptr<base::mac::ScopedNSExceptionEnabler> enabler;
458   if (enableNSExceptions)
459     enabler.reset(new base::mac::ScopedNSExceptionEnabler());
460   return [super sendAction:anAction to:aTarget from:sender];
463 - (BOOL)isHandlingSendEvent {
464   return handlingSendEvent_;
467 - (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
468   handlingSendEvent_ = handlingSendEvent;
471 - (void)sendEvent:(NSEvent*)event {
472   // tracked_objects::ScopedTracker does not support parameterized
473   // instrumentations, so a big switch with each bunch instrumented is required.
474   switch (event.type) {
475     case NSLeftMouseDown:
476     case NSLeftMouseUp:
477     case NSRightMouseDown:
478     case NSRightMouseUp:
479     case NSMouseMoved:
480     case NSLeftMouseDragged:
481     case NSRightMouseDragged:
482     case NSMouseEntered:
483     case NSMouseExited:
484     case NSOtherMouseDown:
485     case NSOtherMouseUp:
486     case NSOtherMouseDragged: {
487       tracked_objects::ScopedTracker tracking_profile(
488           FROM_HERE_WITH_EXPLICIT_FUNCTION(
489               "463272 -[BrowserCrApplication sendEvent:] Mouse"));
490       base::mac::ScopedSendingEvent sendingEventScoper;
491       [super sendEvent:event];
492       break;
493     }
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     }
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     }
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     }
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     }
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     }
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   }
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);
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];
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);
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;
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]);
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;
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;
625     reportingException = NO;
626   }
628   [super reportException:anException];
631 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
632   // This is an undocument attribute that's set when VoiceOver is turned on/off.
633   if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) {
634     content::BrowserAccessibilityState* accessibility_state =
635         content::BrowserAccessibilityState::GetInstance();
636     if ([value intValue] == 1)
637       accessibility_state->OnScreenReaderDetected();
638     else
639       accessibility_state->DisableAccessibility();
640   }
641   return [super accessibilitySetValue:value forAttribute:attribute];
644 - (void)_cycleWindowsReversed:(BOOL)arg1 {
645   base::AutoReset<BOOL> pin(&cyclingWindows_, YES);
646   [super _cycleWindowsReversed:arg1];
649 - (BOOL)isCyclingWindows {
650   return cyclingWindows_;
653 - (id)_removeWindow:(NSWindow*)window {
654   // Note _removeWindow is called from -[NSWindow dealloc], which can happen at
655   // unpredictable times due to reference counting. Just update state.
656   {
657     base::AutoLock lock(previousKeyWindowsLock_);
658     [self removePreviousKeyWindow:window];
659   }
660   return [super _removeWindow:window];
663 - (id)_setKeyWindow:(NSWindow*)window {
664   // |window| is nil when the current key window is being closed.
665   // A separate call follows with a new value when a new key window is set.
666   // Closed windows are not tracked in previousKeyWindows_.
667   if (window != nil) {
668     base::AutoLock lock(previousKeyWindowsLock_);
669     [self removePreviousKeyWindow:window];
670     NSWindow* currentKeyWindow = [self keyWindow];
671     if (currentKeyWindow != nil && currentKeyWindow != window)
672       previousKeyWindows_.push_back(currentKeyWindow);
673   }
675   return [super _setKeyWindow:window];
678 - (NSWindow*)previousKeyWindow {
679   base::AutoLock lock(previousKeyWindowsLock_);
680   return previousKeyWindows_.empty() ? nil : previousKeyWindows_.back();
683 - (void)removePreviousKeyWindow:(NSWindow*)window {
684   previousKeyWindowsLock_.AssertAcquired();
685   std::vector<NSWindow*>::iterator window_iterator =
686       std::find(previousKeyWindows_.begin(),
687                 previousKeyWindows_.end(),
688                 window);
689   if (window_iterator != previousKeyWindows_.end()) {
690     previousKeyWindows_.erase(window_iterator);
691   }
694 @end