Skip to content

Commit

Permalink
[Mac] Allow NSExceptions in certain cases.
Browse files Browse the repository at this point in the history
Thirdy-party print drivers seem to be a source of NSExceptions which
Chromium will never be able to fix.  ScopedNSExceptionEnabler causes
the code which makes throwing an NSException fatal to allow throws.
The flag will be reset in -reportException: in most cases.

For now, allow exceptions to be thrown for -selectPDE: (bug 80686) and
PrintingContextMac::AskUserForSettings() (bug 82589).

BUG=80686, 82589
TEST=Monitor crash server.


Review URL: http://codereview.chromium.org/7038010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86503 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
shess@chromium.org committed May 24, 2011
1 parent 3aca95d commit 39e3b95
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 2 deletions.
2 changes: 2 additions & 0 deletions base/base.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@
'mac/scoped_cftyperef.h',
'mac/scoped_nsautorelease_pool.h',
'mac/scoped_nsautorelease_pool.mm',
'mac/scoped_nsexception_enabler.h',
'mac/scoped_nsexception_enabler.mm',
'mach_ipc_mac.h',
'mach_ipc_mac.mm',
'memory/linked_ptr.h',
Expand Down
46 changes: 46 additions & 0 deletions base/mac/scoped_nsexception_enabler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.

#ifndef BASE_MAC_SCOPED_NSEXCEPTION_ENABLER_H_
#define BASE_MAC_SCOPED_NSEXCEPTION_ENABLER_H_
#pragma once

#include "base/basictypes.h"

namespace base {
namespace mac {

// BrowserCrApplication attempts to restrict throwing of NSExceptions
// because they interact badly with C++ scoping rules. Unfortunately,
// there are some cases where exceptions must be supported, such as
// when third-party printer drivers are used. These helpers can be
// used to enable exceptions for narrow windows.

// Make it easy to safely allow NSException to be thrown in a limited
// scope. Note that if an exception is thrown, then this object will
// not be appropriately destructed! If the exception ends up in the
// top-level event loop, things are cleared in -reportException:. If
// the exception is caught at a lower level, a higher level scoper
// should eventually reset things.
class ScopedNSExceptionEnabler {
public:
ScopedNSExceptionEnabler();
~ScopedNSExceptionEnabler();

private:
bool was_enabled_;

DISALLOW_COPY_AND_ASSIGN(ScopedNSExceptionEnabler);
};

// Access the exception setting for the current thread. This is for
// the support code in BrowserCrApplication, other code should use
// the scoper.
bool GetNSExceptionsAllowed();
void SetNSExceptionsAllowed(bool allowed);

} // namespace mac
} // namespace base

#endif // BASE_MAC_SCOPED_NSEXCEPTION_ENABLER_H_
44 changes: 44 additions & 0 deletions base/mac/scoped_nsexception_enabler.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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 "base/mac/scoped_nsexception_enabler.h"

#import "base/lazy_instance.h"
#import "base/threading/thread_local.h"

// To make the |g_exceptionsAllowed| declaration readable.
using base::LazyInstance;
using base::LeakyLazyInstanceTraits;
using base::ThreadLocalBoolean;

namespace {

// Whether to allow NSExceptions to be raised on the current thread.
LazyInstance<ThreadLocalBoolean, LeakyLazyInstanceTraits<ThreadLocalBoolean> >
g_exceptionsAllowed(base::LINKER_INITIALIZED);

} // namespace

namespace base {
namespace mac {

bool GetNSExceptionsAllowed() {
return g_exceptionsAllowed.Get().Get();
}

void SetNSExceptionsAllowed(bool allowed) {
return g_exceptionsAllowed.Get().Set(allowed);
}

ScopedNSExceptionEnabler::ScopedNSExceptionEnabler() {
was_enabled_ = GetNSExceptionsAllowed();
SetNSExceptionsAllowed(true);
}

ScopedNSExceptionEnabler::~ScopedNSExceptionEnabler() {
SetNSExceptionsAllowed(was_enabled_);
}

} // namespace mac
} // namespace base
26 changes: 24 additions & 2 deletions chrome/browser/chrome_browser_application_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#import "chrome/browser/chrome_browser_application_mac.h"

#import "base/logging.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"
Expand Down Expand Up @@ -93,15 +94,16 @@ - (id)chromeInitWithName:(NSString*)aName
// (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.
if (fatal) {
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);
NOTREACHED();
DCHECK(allow);
}
}

Expand Down Expand Up @@ -329,6 +331,21 @@ - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)sender {
[sender className], tag, actionString, aTarget];

ScopedCrashKey key(kActionKey, value);

// Certain third-party code, such as print drivers, can still throw
// exceptions and Chromium cannot fix them. This provides a way to
// work around those on a spot basis.
bool enableNSExceptions = false;

// http://crbug.com/80686 , an Epson printer driver.
if (anAction == @selector(selectPDE:)) {
enableNSExceptions = true;
}

// Minimize the window by keeping this close to the super call.
scoped_ptr<base::mac::ScopedNSExceptionEnabler> enabler(NULL);
if (enableNSExceptions)
enabler.reset(new base::mac::ScopedNSExceptionEnabler());
return [super sendAction:anAction to:aTarget from:sender];
}

Expand All @@ -354,6 +371,11 @@ - (void)reportException:(NSException *)anException {
// addressed elsewhere.
[self clearIsHandlingSendEvent];

// If |ScopedNSExceptionEnabler| is used to allow exceptions, and an
// uncaught exception is thrown, it will throw past all of the scopers.
// Reset the flag so that future exceptions are not masked.
base::mac::SetNSExceptionsAllowed(false);

// Store some human-readable information in breakpad keys in case
// there is a crash. Since breakpad does not provide infinite
// storage, we track two exceptions. The first exception thrown
Expand Down
11 changes: 11 additions & 0 deletions printing/printing_context_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsexception_enabler.h"
#include "base/sys_string_conversions.h"
#include "base/values.h"
#include "printing/print_settings_initializer_mac.h"
Expand Down Expand Up @@ -36,6 +38,15 @@
int max_pages,
bool has_selection,
PrintSettingsCallback* callback) {
// Third-party print drivers seem to be an area prone to raising exceptions.
// This will allow exceptions to be raised, but does not handle them. The
// NSPrintPanel appears to have appropriate NSException handlers.
base::mac::ScopedNSExceptionEnabler enabler;

// Exceptions can also happen when the NSPrintPanel is being
// deallocated, so it must be autoreleased within this scope.
base::mac::ScopedNSAutoreleasePool pool;

DCHECK([NSThread isMainThread]);

// We deliberately don't feed max_pages into the dialog, because setting
Expand Down

0 comments on commit 39e3b95

Please sign in to comment.