<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "chrome/browser/chrome_browser_application_mac.h"

#import "base/logging.h"
#import "base/mac/mac_util.h"
#import "base/mac/scoped_nsexception_enabler.h"
#import "base/metrics/histogram.h"
#import "base/memory/scoped_nsobject.h"
#import "base/sys_string_conversions.h"
#import "chrome/app/breakpad_mac.h"
#import "chrome/browser/app_controller_mac.h"
#include "chrome/browser/ui/browser_list.h"
#import "chrome/browser/ui/cocoa/objc_method_swizzle.h"
#import "chrome/browser/ui/cocoa/objc_zombie.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "content/browser/accessibility/browser_accessibility_state.h"
#include "content/browser/renderer_host/render_view_host.h"

// The implementation of NSExceptions break various assumptions in the
// Chrome code.  This category defines a replacement for
// -initWithName:reason:userInfo: for purposes of forcing a break in
// the debugger when an exception is raised.  -raise sounds more
// obvious to intercept, but it doesn't catch the original throw
// because the objc runtime doesn't use it.
@interface NSException (CrNSExceptionSwizzle)
- (id)crInitWithName:(NSString*)aName
              reason:(NSString*)aReason
            userInfo:(NSDictionary*)someUserInfo;
@end

static IMP gOriginalInitIMP = NULL;

@implementation NSException (CrNSExceptionSwizzle)
- (id)crInitWithName:(NSString*)aName
              reason:(NSString*)aReason
            userInfo:(NSDictionary*)someUserInfo {
  // Method only called when swizzled.
  DCHECK(_cmd == @selector(initWithName:reason:userInfo:));

  // Parts of Cocoa rely on creating and throwing exceptions. These are not
  // worth bugging-out over. It is very important that there be zero chance that
  // any Chromium code is on the stack; these must be created by Apple code and
  // then immediately consumed by Apple code.
  static NSString* const kAcceptableNSExceptionNames[] = {
    // If an object does not support an accessibility attribute, this will
    // get thrown.
    NSAccessibilityException,

    nil
  };

  BOOL found = NO;
  for (int i = 0; kAcceptableNSExceptionNames[i]; ++i) {
    if (aName == kAcceptableNSExceptionNames[i]) {
      found = YES;
    }
  }

  if (!found) {
    // Update breakpad with the exception info.
    static NSString* const kNSExceptionKey = @"nsexception";
    NSString* value =
        [NSString stringWithFormat:@"%@ reason %@", aName, aReason];
    SetCrashKeyValue(kNSExceptionKey, value);

    // Force crash for selected exceptions to generate crash dumps.
    BOOL fatal = NO;
    if (aName == NSInternalInconsistencyException) {
      NSString* const kNSMenuItemArrayBoundsCheck =
          @"Invalid parameter not satisfying: (index >= 0) && "
          @"(index < [_itemArray count])";
      if ([aReason isEqualToString:kNSMenuItemArrayBoundsCheck]) {
        fatal = YES;
      }

      NSString* const kNoWindowCheck = @"View is not in any window";
      if ([aReason isEqualToString:kNoWindowCheck]) {
        fatal = YES;
      }
    }

    // Mostly "unrecognized selector sent to (instance|class)".  A
    // very small number of things like inappropriate nil being passed.
    if (aName == NSInvalidArgumentException) {
      fatal = YES;

      // TODO(shess): http://crbug.com/85463 throws this exception
      // from ImageKit.  Our code is not on the stack, so it needs to
      // be whitelisted for now.
      NSString* const kNSURLInitNilCheck =
          @"*** -[NSURL initFileURLWithPath:isDirectory:]: "
          @"nil string parameter";
      if ([aReason isEqualToString:kNSURLInitNilCheck]) {
        fatal = NO;
      }
    }

    // Dear reader: Something you just did provoked an NSException.
    // NSException is implemented in terms of setjmp()/longjmp(),
    // which does poor things when combined with C++ scoping
    // (destructors are skipped).  Chrome should be NSException-free,
    // please check your backtrace and see if you can't file a bug
    // with a repro case.
    const bool allow = base::mac::GetNSExceptionsAllowed();
    if (fatal && !allow) {
      LOG(FATAL) << "Someone is trying to raise an exception!  "
                 << base::SysNSStringToUTF8(value);
    } else {
      // Make sure that developers see when their code throws
      // exceptions.
      DLOG(ERROR) << "Someone is trying to raise an exception!  "
                  << base::SysNSStringToUTF8(value);
      DCHECK(allow);
    }
  }

  // Forward to the original version.
  return gOriginalInitIMP(self, _cmd, aName, aReason, someUserInfo);
}
@end

static IMP gOriginalNSBundleLoadIMP = NULL;

@interface NSBundle (CrNSBundleSwizzle)
// -crLoad swizzles -load. It refuses to load parasitic third-party code
// located in certain directories, returning NO instead of attempting to load
// the bundle. This circumvents some of the mechanisms that third-party code
// attempts to use to inject itself into applications. Note that some of these
// mechanisms are unavailable to 64-bit applications anyway.
- (BOOL)crLoad;
@end

@implementation NSBundle (CrNSBundleSwizzle)
- (BOOL)crLoad {
  // Method only called when swizzled.
  DCHECK(_cmd == @selector(load));

  // ~/Library, /Library, and /Network/Library. Things in /System/Library
  // aren't blacklisted.
  NSArray* blockedPrefixes =
     NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
                                         NSUserDomainMask |
                                             NSLocalDomainMask |
                                             NSNetworkDomainMask,
                                         YES);

  // Everything in the suffix list has a trailing slash so as to only block
  // loading things contained in these directories.
  NSString* const blockedSuffixes[] = {
    // SIMBL - http://code.google.com/p/simbl/source/browse/src/SIMBL.{h,m}.
    // It attempts to inject itself via an AppleScript event.
    // http://code.google.com/p/simbl/source/browse/SIMBL%20Agent/SIMBLAgent.m
    @"Application Support/SIMBL/Plugins/",

#if !defined(__LP64__)
    // Contextual menu manager plug-ins are unavailable to 64-bit processes.
    // http://developer.apple.com/library/mac/releasenotes/Cocoa/AppKitOlderNotes.html#NSMenu
    @"Contextual Menu Items/",

    // Input managers are deprecated, would only be loaded under specific
    // circumstances, and are entirely unavailable to 64-bit processes.
    // http://developer.apple.com/library/mac/releasenotes/Cocoa/AppKitOlderNotes.html#NSInputManager
    @"Input Managers/",
#endif  // __LP64__

    // Don't load third-party scripting additions either.
    @"ScriptingAdditions/"

    // This list is intentionally incomplete. For example, it doesn't block
    // printer drivers or Internet plug-ins.
  };

  NSString* bundlePath = [self bundlePath];
  NSUInteger bundlePathLength = [bundlePath length];

  // Merge the prefix and suffix lists.
  for (NSString* blockedPrefix in blockedPrefixes) {
    for (size_t blockedSuffixIndex = 0;
         blockedSuffixIndex < arraysize(blockedSuffixes);
         ++blockedSuffixIndex) {
      NSString* blockedSuffix = blockedSuffixes[blockedSuffixIndex];
      NSString* blockedPath =
          [blockedPrefix stringByAppendingPathComponent:blockedSuffix];
      NSUInteger blockedPathLength = [blockedPath length];

      // Do a case-insensitive comparison because most users will be on
      // case-insensitive HFS+ filesystems and it's cheaper than asking the
      // disk. This is like [bundlePath hasPrefix:blockedPath] but is<commit_msg>Input mangers live in the "InputManagers" directory, not "Input Managers".<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "chrome/browser/chrome_browser_application_mac.h"

#import "base/logging.h"
#import "base/mac/mac_util.h"
#import "base/mac/scoped_nsexception_enabler.h"
#import "base/metrics/histogram.h"
#import "base/memory/scoped_nsobject.h"
#import "base/sys_string_conversions.h"
#import "chrome/app/breakpad_mac.h"
#import "chrome/browser/app_controller_mac.h"
#include "chrome/browser/ui/browser_list.h"
#import "chrome/browser/ui/cocoa/objc_method_swizzle.h"
#import "chrome/browser/ui/cocoa/objc_zombie.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "content/browser/accessibility/browser_accessibility_state.h"
#include "content/browser/renderer_host/render_view_host.h"

// The implementation of NSExceptions break various assumptions in the
// Chrome code.  This category defines a replacement for
// -initWithName:reason:userInfo: for purposes of forcing a break in
// the debugger when an exception is raised.  -raise sounds more
// obvious to intercept, but it doesn't catch the original throw
// because the objc runtime doesn't use it.
@interface NSException (CrNSExceptionSwizzle)
- (id)crInitWithName:(NSString*)aName
              reason:(NSString*)aReason
            userInfo:(NSDictionary*)someUserInfo;
@end

static IMP gOriginalInitIMP = NULL;

@implementation NSException (CrNSExceptionSwizzle)
- (id)crInitWithName:(NSString*)aName
              reason:(NSString*)aReason
            userInfo:(NSDictionary*)someUserInfo {
  // Method only called when swizzled.
  DCHECK(_cmd == @selector(initWithName:reason:userInfo:));

  // Parts of Cocoa rely on creating and throwing exceptions. These are not
  // worth bugging-out over. It is very important that there be zero chance that
  // any Chromium code is on the stack; these must be created by Apple code and
  // then immediately consumed by Apple code.
  static NSString* const kAcceptableNSExceptionNames[] = {
    // If an object does not support an accessibility attribute, this will
    // get thrown.
    NSAccessibilityException,

    nil
  };

  BOOL found = NO;
  for (int i = 0; kAcceptableNSExceptionNames[i]; ++i) {
    if (aName == kAcceptableNSExceptionNames[i]) {
      found = YES;
    }
  }

  if (!found) {
    // Update breakpad with the exception info.
    static NSString* const kNSExceptionKey = @"nsexception";
    NSString* value =
        [NSString stringWithFormat:@"%@ reason %@", aName, aReason];
    SetCrashKeyValue(kNSExceptionKey, value);

    // Force crash for selected exceptions to generate crash dumps.
    BOOL fatal = NO;
    if (aName == NSInternalInconsistencyException) {
      NSString* const kNSMenuItemArrayBoundsCheck =
          @"Invalid parameter not satisfying: (index >= 0) && "
          @"(index < [_itemArray count])";
      if ([aReason isEqualToString:kNSMenuItemArrayBoundsCheck]) {
        fatal = YES;
      }

      NSString* const kNoWindowCheck = @"View is not in any window";
      if ([aReason isEqualToString:kNoWindowCheck]) {
        fatal = YES;
      }
    }

    // Mostly "unrecognized selector sent to (instance|class)".  A
    // very small number of things like inappropriate nil being passed.
    if (aName == NSInvalidArgumentException) {
      fatal = YES;

      // TODO(shess): http://crbug.com/85463 throws this exception
      // from ImageKit.  Our code is not on the stack, so it needs to
      // be whitelisted for now.
      NSString* const kNSURLInitNilCheck =
          @"*** -[NSURL initFileURLWithPath:isDirectory:]: "
          @"nil string parameter";
      if ([aReason isEqualToString:kNSURLInitNilCheck]) {
        fatal = NO;
      }
    }

    // Dear reader: Something you just did provoked an NSException.
    // NSException is implemented in terms of setjmp()/longjmp(),
    // which does poor things when combined with C++ scoping
    // (destructors are skipped).  Chrome should be NSException-free,
    // please check your backtrace and see if you can't file a bug
    // with a repro case.
    const bool allow = base::mac::GetNSExceptionsAllowed();
    if (fatal && !allow) {
      LOG(FATAL) << "Someone is trying to raise an exception!  "
                 << base::SysNSStringToUTF8(value);
    } else {
      // Make sure that developers see when their code throws
      // exceptions.
      DLOG(ERROR) << "Someone is trying to raise an exception!  "
                  << base::SysNSStringToUTF8(value);
      DCHECK(allow);
    }
  }

  // Forward to the original version.
  return gOriginalInitIMP(self, _cmd, aName, aReason, someUserInfo);
}
@end

static IMP gOriginalNSBundleLoadIMP = NULL;

@interface NSBundle (CrNSBundleSwizzle)
// -crLoad swizzles -load. It refuses to load parasitic third-party code
// located in certain directories, returning NO instead of attempting to load
// the bundle. This circumvents some of the mechanisms that third-party code
// attempts to use to inject itself into applications. Note that some of these
// mechanisms are unavailable to 64-bit applications anyway.
- (BOOL)crLoad;
@end

@implementation NSBundle (CrNSBundleSwizzle)
- (BOOL)crLoad {
  // Method only called when swizzled.
  DCHECK(_cmd == @selector(load));

  // ~/Library, /Library, and /Network/Library. Things in /System/Library
  // aren't blacklisted.
  NSArray* blockedPrefixes =
     NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
                                         NSUserDomainMask |
                                             NSLocalDomainMask |
                                             NSNetworkDomainMask,
                                         YES);

  // Everything in the suffix list has a trailing slash so as to only block
  // loading things contained in these directories.
  NSString* const blockedSuffixes[] = {
    // SIMBL - http://code.google.com/p/simbl/source/browse/src/SIMBL.{h,m}.
    // It attempts to inject itself via an AppleScript event.
    // http://code.google.com/p/simbl/source/browse/SIMBL%20Agent/SIMBLAgent.m
    @"Application Support/SIMBL/Plugins/",

#if !defined(__LP64__)
    // Contextual menu manager plug-ins are unavailable to 64-bit processes.
    // http://developer.apple.com/library/mac/releasenotes/Cocoa/AppKitOlderNotes.html#NSMenu
    @"Contextual Menu Items/",

    // Input managers are deprecated, would only be loaded under specific
    // circumstances, and are entirely unavailable to 64-bit processes.
    // http://developer.apple.com/library/mac/releasenotes/Cocoa/AppKitOlderNotes.html#NSInputManager
    @"InputManagers/",
#endif  // __LP64__

    // Don't load third-party scripting additions either.
    @"ScriptingAdditions/"

    // This list is intentionally incomplete. For example, it doesn't block
    // printer drivers or Internet plug-ins.
  };

  NSString* bundlePath = [self bundlePath];
  NSUInteger bundlePathLength = [bundlePath length];

  // Merge the prefix and suffix lists.
  for (NSString* blockedPrefix in blockedPrefixes) {
    for (size_t blockedSuffixIndex = 0;
         blockedSuffixIndex < arraysize(blockedSuffixes);
         ++blockedSuffixIndex) {
      NSString* blockedSuffix = blockedSuffixes[blockedSuffixIndex];
      NSString* blockedPath =
          [blockedPrefix stringByAppendingPathComponent:blockedSuffix];
      NSUInteger blockedPathLength = [blockedPath length];

      // Do a case-insensitive comparison because most users will be on
      // case-insensitive HFS+ filesystems and it's cheaper than asking the
      // disk. This is like [bundlePath hasPrefix:blockedPath] but is
{'fraction_non_alphanumeric': 0.09663703131039815, 'fraction_numerical': 0.0033500837520938024, 'mean_word_length': 3.091460200316289, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6606496', 'n_tokens_mistral': 4587, 'n_tokens_neox': 4316, 'n_words': 1377}