Mac reporter improvements:

- Made localization for UI entirely string-based, with flexible layout based on the size of the strings inserted.
- Made the request for an email address optional.
- Fixed a bug that would prevent comments or email from being collected if the text field were still focused.
- Refactored askUserPermissionToSend.


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@335 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
stuartmorgan 2009-04-29 13:50:53 +00:00
parent 73afbc7302
commit 33e8fad61d
12 changed files with 379 additions and 206 deletions

View File

@ -26,10 +26,10 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */; };
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; };
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; };
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
F9267CB30F844BDD00A827EC /* Breakpad.nib in Resources */ = {isa = PBXBuildFile; fileRef = F9267CB10F844BDD00A827EC /* Breakpad.nib */; };
F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */; };
F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53B70ECCE7B3009BE4BA /* Inspector.mm */; };
F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
@ -224,10 +224,10 @@
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = "<group>"; };
3329D4EC0FA16D820007BBC5 /* Breakpad.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Breakpad.nib; path = sender/Breakpad.nib; sourceTree = "<group>"; };
33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F9267CB20F844BDD00A827EC /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = sender/English.lproj/Breakpad.nib; sourceTree = "<group>"; };
F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = "<group>"; };
F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = "<group>"; };
F92C53540ECCE349009BE4BA /* Inspector */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Inspector; sourceTree = BUILT_PRODUCTS_DIR; };
@ -531,13 +531,13 @@
F92C56A60ECE04B6009BE4BA /* sender */ = {
isa = PBXGroup;
children = (
F945849C0F280E3C009A47BF /* Localizable.strings */,
33880C7E0F9E097100817F82 /* InfoPlist.strings */,
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */,
F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */,
F9267CB10F844BDD00A827EC /* Breakpad.nib */,
F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
F945849C0F280E3C009A47BF /* Localizable.strings */,
33880C7E0F9E097100817F82 /* InfoPlist.strings */,
3329D4EC0FA16D820007BBC5 /* Breakpad.nib */,
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */,
F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */,
);
name = sender;
sourceTree = "<group>";
@ -806,8 +806,8 @@
files = (
F945849E0F280E3C009A47BF /* Localizable.strings in Resources */,
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */,
F9267CB30F844BDD00A827EC /* Breakpad.nib in Resources */,
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */,
3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1078,14 +1078,6 @@
name = InfoPlist.strings;
sourceTree = "<group>";
};
F9267CB10F844BDD00A827EC /* Breakpad.nib */ = {
isa = PBXVariantGroup;
children = (
F9267CB20F844BDD00A827EC /* English */,
);
name = Breakpad.nib;
sourceTree = "<group>";
};
F945849C0F280E3C009A47BF /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (

View File

@ -76,9 +76,10 @@ extern "C" {
"BreakpadReporterExeLocation"
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
#define BREAKPAD_EMAIL "BreakpadEmail"
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
#define BREAKPAD_COMMENTS "BreakpadComments"
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
#define BREAKPAD_EMAIL "BreakpadEmail"
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
// TODO(nealsid) find a better way to support server-specific
// parameters without having to rebuild Breakpad
@ -163,8 +164,12 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// should be uploaded at crash time.
//
// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a
// text box for the user to enter comments as
// well as a name and email address.
// text box for the user to enter comments.
// Default: NO
//
// BREAKPAD_REQUEST_EMAIL If true and BREAKPAD_REQUEST_COMMENTS is also
// true, the message dialog will have a text
// box for the user to enter their email address.
// Default: NO
//
// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to

View File

@ -407,6 +407,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL];
NSString *requestUserText =
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
NSString *vendor =
[parameters objectForKey:@BREAKPAD_VENDOR];
NSString *dumpSubdirectory =
@ -549,8 +550,8 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
[logFileTailSize UTF8String]);
dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
[requestUserText UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VENDOR,
[vendor UTF8String]);
dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
[dumpSubdirectory UTF8String]);

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>cancel</key>
<string>id</string>
<key>sendReport</key>
<string>id</string>
<key>showPrivacyPolicy</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Reporter</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>alertWindow_</key>
<string>NSWindow</string>
<key>cancelButton_</key>
<string>NSButton</string>
<key>commentMessage_</key>
<string>NSTextField</string>
<key>dialogTitle_</key>
<string>NSTextField</string>
<key>emailEntryField_</key>
<string>NSView</string>
<key>emailLabel_</key>
<string>NSTextField</string>
<key>emailMessage_</key>
<string>NSTextField</string>
<key>emailSectionBox_</key>
<string>NSBox</string>
<key>headerBox_</key>
<string>NSBox</string>
<key>preEmailBox_</key>
<string>NSBox</string>
<key>privacyLinkArrow_</key>
<string>NSView</string>
<key>privacyLinkLabel_</key>
<string>NSTextField</string>
<key>sendButton_</key>
<string>NSButton</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -3,9 +3,9 @@
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>670</string>
<string>672</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../../Breakpad.xcodeproj</string>
<string>../Breakpad.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
@ -13,7 +13,7 @@
<integer>2</integer>
</array>
<key>IBSystem Version</key>
<string>9F33</string>
<string>9G55</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>

Binary file not shown.

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>cancel</key>
<string>id</string>
<key>sendReport</key>
<string>id</string>
<key>showPrivacyPolicy</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Reporter</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>alertWindow</key>
<string>NSWindow</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -43,16 +43,25 @@ extern NSString *const kSocorroServerType;
extern NSString *const kDefaultServerType;
@interface Reporter : NSObject {
@public
IBOutlet NSWindow *alertWindow; // The alert window
IBOutlet NSWindow *alertWindow_; // The alert window
// Values bound in the XIB
NSString *headerMessage_; // Message notifying of the
// crash
NSString *reportMessage_; // Message explaining the
// crash report
// Grouping boxes used for resizing.
IBOutlet NSBox *headerBox_;
IBOutlet NSBox *preEmailBox_;
IBOutlet NSBox *emailSectionBox_;
// Localized elements (or things that need to be moved during localization).
IBOutlet NSTextField *dialogTitle_;
IBOutlet NSTextField *commentMessage_;
IBOutlet NSTextField *emailMessage_;
IBOutlet NSTextField *emailLabel_;
IBOutlet NSTextField *privacyLinkLabel_;
IBOutlet NSButton *sendButton_;
IBOutlet NSButton *cancelButton_;
IBOutlet NSView *emailEntryField_;
IBOutlet NSView *privacyLinkArrow_;
// Text field bindings, for user input.
NSString *commentsValue_; // Comments from the user
NSString *emailMessage_; // Message requesting user
// email
NSString *emailValue_; // Email from the user
@private
@ -90,18 +99,9 @@ extern NSString *const kDefaultServerType;
- (BOOL)setPostParametersFromDictionary:(NSMutableDictionary *)crashParameters;
// Accessors to make bindings work
- (NSString *)headerMessage;
- (void)setHeaderMessage:(NSString *)value;
- (NSString *)reportMessage;
- (void)setReportMessage:(NSString *)value;
- (NSString *)commentsValue;
- (void)setCommentsValue:(NSString *)value;
- (NSString *)emailMessage;
- (void)setEmailMessage:(NSString *)value;
- (NSString *)emailValue;
- (void)setEmailValue:(NSString *)value;

View File

@ -49,6 +49,112 @@ NSString *const kGoogleServerType = @"google";
NSString *const kSocorroServerType = @"socorro";
NSString *const kDefaultServerType = @"google";
#pragma mark -
@interface NSView (ResizabilityExtentions)
// Shifts the view vertically by the given amount.
- (void)breakpad_shiftVertically:(float)offset;
// Shifts the view horizontally by the given amount.
- (void)breakpad_shiftHorizontally:(float)offset;
@end
@implementation NSView (ResizabilityExtentions)
- (void)breakpad_shiftVertically:(float)offset {
NSPoint origin = [self frame].origin;
origin.y += offset;
[self setFrameOrigin:origin];
}
- (void)breakpad_shiftHorizontally:(float)offset {
NSPoint origin = [self frame].origin;
origin.x += offset;
[self setFrameOrigin:origin];
}
@end
@interface NSWindow (ResizabilityExtentions)
// Adjusts the window height by heightDelta relative to its current height,
// keeping all the content at the same size.
- (void)breakpad_adjustHeight:(float)heightDelta;
@end
@implementation NSWindow (ResizabilityExtentions)
- (void)breakpad_adjustHeight:(float)heightDelta {
[[self contentView] setAutoresizesSubviews:NO];
NSRect windowFrame = [self frame];
windowFrame.size.height += heightDelta;
[self setFrame:windowFrame display:YES];
// For some reason the content view is resizing, but not adjusting its origin,
// so correct it manually.
[[self contentView] setFrameOrigin:NSMakePoint(0, 0)];
[[self contentView] setAutoresizesSubviews:YES];
}
@end
@interface NSTextField (ResizabilityExtentions)
// Grows or shrinks the height of the field to the minimum required to show the
// current text, preserving the existing width and origin.
// Returns the change in height.
- (float)breakpad_adjustHeightToFit;
// Grows or shrinks the width of the field to the minimum required to show the
// current text, preserving the existing height and origin.
// Returns the change in width.
- (float)breakpad_adjustWidthToFit;
@end
@implementation NSTextField (ResizabilityExtentions)
- (float)breakpad_adjustHeightToFit {
NSRect oldFrame = [self frame];
// sizeToFit will blow out the width rather than making the field taller, so
// we do it manually.
NSSize newSize = [[self cell] cellSizeForBounds:oldFrame];
NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y,
NSWidth(oldFrame), newSize.height);
[self setFrame:newFrame];
return newSize.height - NSHeight(oldFrame);
}
- (float)breakpad_adjustWidthToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
return NSWidth([self frame]) - NSWidth(oldFrame);
}
@end
@interface NSButton (ResizabilityExtentions)
// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a
// minimum width of 70, while preserving the right edge location.
// Returns the change in width.
- (float)breakpad_smartSizeToFit;
@end
@implementation NSButton (ResizabilityExtentions)
- (float)breakpad_smartSizeToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
NSRect newFrame = [self frame];
// sizeToFit gives much worse results that IB's Size to Fit option. This is
// the amount of padding IB adds over a sizeToFit, empirically determined.
const float kExtraPaddingAmount = 12;
const float kMinButtonWidth = 70; // The default button size in IB.
newFrame.size.width = NSWidth(newFrame) + kExtraPaddingAmount;
if (NSWidth(newFrame) < kMinButtonWidth)
newFrame.size.width = kMinButtonWidth;
// Preserve the right edge location.
newFrame.origin.x = NSMaxX(oldFrame) - NSWidth(newFrame);
[self setFrame:newFrame];
return NSWidth(newFrame) - NSWidth(oldFrame);
}
@end
#pragma mark -
@interface Reporter(PrivateMethods)
+ (uid_t)consoleUID;
@ -61,8 +167,44 @@ NSString *const kDefaultServerType = @"google";
- (BOOL)readMinidumpData;
- (BOOL)readLogFileData;
- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport;
- (BOOL)shouldSubmitReport;
// Returns YES if it has been long enough since the last report that we should
// submit a report for this crash.
- (BOOL)reportIntervalElapsed;
// Returns YES if we should send the report without asking the user first.
- (BOOL)shouldSubmitSilently;
// Returns YES if we should ask the user to provide comments.
- (BOOL)shouldRequestComments;
// Returns YES if we should ask the user to provide an email address.
- (BOOL)shouldRequestEmail;
// Shows UI to the user to ask for permission to send and any extra information
// we've been instructed to request. Returns YES if the user allows the report
// to be sent.
- (BOOL)askUserPermissionToSend;
// Returns the short description of the crash, suitable for use as a dialog
// title (e.g., "The application Foo has quit unexpectedly").
- (NSString*)shortCrashDialogMessage;
// Return explanatory text about the crash and the reporter, suitable for the
// body text of a dialog.
- (NSString*)explanatoryCrashDialogText;
// Returns the amount of time the UI should be shown before timing out.
- (NSTimeInterval)messageTimeout;
// Preps the comment-prompting alert window for display:
// * localizes all the elements
// * resizes and adjusts layout as necessary for localization
// * removes the email section if includeEmail is NO
- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail;
// Rmevoes the email section of the dialog, adjusting the rest of the window
// as necessary.
- (void)removeEmailPrompt;
// Run an alert window with the given timeout. Returns
// NSAlertButtonDefault if the timeout is exceeded. A timeout of 0
@ -341,105 +483,39 @@ NSString *const kDefaultServerType = @"google";
}
//=============================================================================
- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport {
// Send without confirmation
if ([[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]) {
GTMLoggerDebug(@"Skipping confirmation and sending report");
return YES;
}
// Determine if we should create a text box for user feedback
BOOL shouldRequestComments =
[[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
isEqual:@"YES"];
NSString *display = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![display length])
display = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
if (![vendor length])
vendor = @"Vendor not specified";
NSBundle *bundle = [NSBundle mainBundle];
[self setHeaderMessage:[NSString stringWithFormat:
NSLocalizedStringFromTableInBundle(@"headerFmt", nil,
bundle,
@""), display]];
NSString *defaultButtonTitle = nil;
NSString *otherButtonTitle = nil;
// Get the localized alert strings
// If we're going to submit a report, prompt the user if this is okay.
// Otherwise, just let them know that the app crashed.
if (shouldSubmitReport) {
NSString *msgFormat = NSLocalizedStringFromTableInBundle(@"msg",
nil,
bundle, @"");
[self setReportMessage:[NSString stringWithFormat:msgFormat, vendor]];
defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"sendReportButton",
nil, bundle, @"");
otherButtonTitle = NSLocalizedStringFromTableInBundle(@"cancelButton", nil,
bundle, @"");
} else {
[self setReportMessage:NSLocalizedStringFromTableInBundle(@"noSendMsg", nil,
bundle, @"")];
defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"noSendButton",
nil, bundle, @"");
}
// Get the timeout value for the notification.
NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]
floatValue];
// Show the notification for at least one minute (but allow 0, since it means
// no timeout).
if (timeout > 0.001 && timeout < 60.0) {
timeout = 60.0;
}
- (BOOL)askUserPermissionToSend {
// Initialize Cocoa, needed to display the alert
NSApplicationLoad();
// Get the timeout value for the notification.
NSTimeInterval timeout = [self messageTimeout];
int buttonPressed = NSAlertAlternateReturn;
if (shouldRequestComments) {
// Determine whether we should create a text box for user feedback.
if ([self shouldRequestComments]) {
BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
if (didLoadNib) {
// Append the request for comments to the |reportMessage| string.
NSString *commentsMessage =
NSLocalizedStringFromTableInBundle(@"commentsMsg", nil, bundle, @"");
[self setReportMessage:[NSString stringWithFormat:@"%@\n\n%@",
[self reportMessage],
commentsMessage]];
if (!didLoadNib) {
return NO;
}
// Add the request for email address.
[self setEmailMessage:
NSLocalizedStringFromTableInBundle(@"emailMsg", nil, bundle, @"")];
[self configureAlertWindowIncludingEmail:[self shouldRequestEmail]];
// Run the alert
buttonPressed = [self runModalWindow:alertWindow withTimeout:timeout];
buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout];
// Extract info from the user into the parameters_ dictionary
if ([self commentsValue]) {
[parameters_ setObject:[self commentsValue]
forKey:@BREAKPAD_COMMENTS];
}
if ([self emailValue]) {
[parameters_ setObject:[self emailValue]
forKey:@BREAKPAD_EMAIL];
}
// Extract info from the user into the parameters_ dictionary
if ([self commentsValue]) {
[parameters_ setObject:[self commentsValue] forKey:@BREAKPAD_COMMENTS];
}
if ([self emailValue]) {
[parameters_ setObject:[self emailValue] forKey:@BREAKPAD_EMAIL];
}
} else {
// Create an alert panel to tell the user something happened
NSPanel* alert = NSGetAlertPanel([self headerMessage],
[self reportMessage],
defaultButtonTitle,
otherButtonTitle, nil);
NSPanel* alert = NSGetAlertPanel([self shortCrashDialogMessage],
[self explanatoryCrashDialogText],
NSLocalizedString(@"sendReportButton", @""),
NSLocalizedString(@"cancelButton", @""),
nil);
// Pop the alert with an automatic timeout, and wait for the response
buttonPressed = [self runModalWindow:alert withTimeout:timeout];
@ -450,6 +526,58 @@ NSString *const kDefaultServerType = @"google";
return buttonPressed == NSAlertDefaultReturn;
}
- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail {
// Swap in localized values, making size adjustments to impacted elements as
// we go. Remember that the origin is in the bottom left, so elements above
// "fall" as text areas are shrunk from their overly-large IB sizes.
// Localize the header. No resizing needed, as it has plenty of room.
[dialogTitle_ setStringValue:[self shortCrashDialogMessage]];
// Localize the explanatory text field.
[commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
[self explanatoryCrashDialogText],
NSLocalizedString(@"commentsMsg", @"")]];
float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
[headerBox_ breakpad_shiftVertically:commentHeightDelta];
[alertWindow_ breakpad_adjustHeight:commentHeightDelta];
// Either localize the email explanation field or remove the whole email
// section depending on whether or not we are asking for email.
if (includeEmail) {
[emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")];
float emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
[preEmailBox_ breakpad_shiftVertically:emailHeightDelta];
[alertWindow_ breakpad_adjustHeight:emailHeightDelta];
} else {
[self removeEmailPrompt]; // Handles necessary resizing.
}
// Localize the email label, and shift the associated text field.
[emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")];
float emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
[emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
// Localize the privacy policy label, and keep it right-aligned to the arrow.
[privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
float privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit];
[privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)];
// Localize the buttons, and keep the cancel button at the right distance.
[sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")];
float sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
[cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)];
[cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")];
[cancelButton_ breakpad_smartSizeToFit];
}
- (void)removeEmailPrompt {
[emailSectionBox_ setHidden:YES];
float emailSectionHeight = NSHeight([emailSectionBox_ frame]);
[preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)];
[alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)];
}
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
// Queue a |stopModal| message to be performed in |timeout| seconds.
if (timeout > 0.001) {
@ -473,7 +601,11 @@ NSString *const kDefaultServerType = @"google";
}
- (IBAction)sendReport:(id)sender {
[alertWindow orderOut:self];
// Force the text fields to end editing so text for the currently focused
// field will be commited.
[alertWindow_ makeFirstResponder:alertWindow_];
[alertWindow_ orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertDefaultReturn];
@ -482,7 +614,7 @@ NSString *const kDefaultServerType = @"google";
// UI Button Actions
//=============================================================================
- (IBAction)cancel:(id)sender {
[alertWindow orderOut:self];
[alertWindow_ orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertAlternateReturn];
@ -491,8 +623,7 @@ NSString *const kDefaultServerType = @"google";
- (IBAction)showPrivacyPolicy:(id)sender {
// Get the localized privacy policy URL and open it in the default browser.
NSURL* privacyPolicyURL =
[NSURL URLWithString:NSLocalizedStringFromTableInBundle(
@"privacyPolicyURL", nil, [NSBundle mainBundle], @"")];
[NSURL URLWithString:NSLocalizedString(@"privacyPolicyURL", @"")];
[[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL];
}
@ -511,29 +642,9 @@ doCommandBySelector:(SEL)commandSelector {
return result;
}
// Accessors
#pragma mark Accessors
#pragma mark -
//=============================================================================
- (NSString *)headerMessage {
return [[headerMessage_ retain] autorelease];
}
- (void)setHeaderMessage:(NSString *)value {
if (headerMessage_ != value) {
[headerMessage_ autorelease];
headerMessage_ = [value copy];
}
}
- (NSString *)reportMessage {
return [[reportMessage_ retain] autorelease];
}
- (void)setReportMessage:(NSString *)value {
if (reportMessage_ != value) {
[reportMessage_ autorelease];
reportMessage_ = [value copy];
}
}
- (NSString *)commentsValue {
return [[commentsValue_ retain] autorelease];
@ -541,35 +652,25 @@ doCommandBySelector:(SEL)commandSelector {
- (void)setCommentsValue:(NSString *)value {
if (commentsValue_ != value) {
[commentsValue_ autorelease];
[commentsValue_ release];
commentsValue_ = [value copy];
}
}
- (NSString *)emailMessage {
return [[emailMessage_ retain] autorelease];
}
- (void)setEmailMessage:(NSString *)value {
if (emailMessage_ != value) {
[emailMessage_ autorelease];
emailMessage_ = [value copy];
}
}
- (NSString *)emailValue {
return [[emailValue_ retain] autorelease];
}
- (void)setEmailValue:(NSString *)value {
if (emailValue_ != value) {
[emailValue_ autorelease];
[emailValue_ release];
emailValue_ = [value copy];
}
}
#pragma mark -
//=============================================================================
- (BOOL)shouldSubmitReport {
- (BOOL)reportIntervalElapsed {
float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
floatValue];
NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
@ -595,6 +696,49 @@ doCommandBySelector:(SEL)commandSelector {
return YES;
}
- (BOOL)shouldSubmitSilently {
return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM]
isEqualToString:@"YES"];
}
- (BOOL)shouldRequestComments {
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
isEqualToString:@"YES"];
}
- (BOOL)shouldRequestEmail {
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_EMAIL]
isEqualToString:@"YES"];
}
- (NSString*)shortCrashDialogMessage {
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![displayName length])
displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
return [NSString stringWithFormat:NSLocalizedString(@"headerFmt", @""),
displayName];
}
- (NSString*)explanatoryCrashDialogText {
NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
if (![vendor length])
vendor = @"unknown vendor";
return [NSString stringWithFormat:NSLocalizedString(@"msgFmt", @""), vendor];
}
- (NSTimeInterval)messageTimeout {
// Get the timeout value for the notification.
NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]
floatValue];
// Require a timeout of at least a minute (except 0, which means no timeout).
if (timeout > 0.001 && timeout < 60.0) {
timeout = 60.0;
}
return timeout;
}
- (void)createServerParameterDictionaries {
serverDictionary_ = [[NSMutableDictionary alloc] init];
socorroDictionary_ = [[NSMutableDictionary alloc] init];
@ -796,12 +940,17 @@ int main(int argc, const char *argv[]) {
[reporter readLogFileData];
// only submit a report if we have not recently crashed in the past
BOOL shouldSubmitReport = [reporter shouldSubmitReport];
BOOL shouldSubmitReport = [reporter reportIntervalElapsed];
BOOL okayToSend = NO;
// ask user if we should send
if (shouldSubmitReport) {
okayToSend = [reporter askUserPermissionToSend:shouldSubmitReport];
if ([reporter shouldSubmitSilently]) {
GTMLoggerDebug(@"Skipping confirmation and sending report");
okayToSend = YES;
} else {
okayToSend = [reporter askUserPermissionToSend];
}
}
// If we're running as root, switch over to nobody

View File

@ -36,6 +36,8 @@
<string>NO</string>
<key>BreakpadSendAndExit</key>
<string>YES</string>
<key>BreakpadRequestEmail</key>
<string>YES</string>
<key>BreakpadRequestComments</key>
<string>YES</string>
<key>BreakpadVendor</key>