From 33e8fad61dde4622d3f88ef9a2df38aa9620321a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 29 Apr 2009 13:50:53 +0000 Subject: [PATCH] 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 --- .../mac/Breakpad.xcodeproj/project.pbxproj | 24 +- src/client/mac/Framework/Breakpad.h | 11 +- src/client/mac/Framework/Breakpad.mm | 5 +- .../mac/sender/Breakpad.nib/classes.nib | 57 +++ .../{English.lproj => }/Breakpad.nib/info.nib | 6 +- .../mac/sender/Breakpad.nib/keyedobjects.nib | Bin 0 -> 13747 bytes .../English.lproj/Breakpad.nib/classes.nib | 33 -- .../Breakpad.nib/keyedobjects.nib | Bin 10208 -> 0 bytes .../sender/English.lproj/Localizable.strings | Bin 1362 -> 1488 bytes src/client/mac/sender/crash_report_sender.h | 34 +- src/client/mac/sender/crash_report_sender.m | 413 ++++++++++++------ src/client/mac/testapp/Info.plist | 2 + 12 files changed, 379 insertions(+), 206 deletions(-) create mode 100644 src/client/mac/sender/Breakpad.nib/classes.nib rename src/client/mac/sender/{English.lproj => }/Breakpad.nib/info.nib (84%) create mode 100644 src/client/mac/sender/Breakpad.nib/keyedobjects.nib delete mode 100644 src/client/mac/sender/English.lproj/Breakpad.nib/classes.nib delete mode 100644 src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj index a9ddd017..7d8e5b8d 100644 --- a/src/client/mac/Breakpad.xcodeproj/project.pbxproj +++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj @@ -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 = ""; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = ""; }; + 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Breakpad.nib; path = sender/Breakpad.nib; sourceTree = ""; }; 33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = ""; }; 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = ""; }; 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 = ""; }; F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = ""; }; F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = ""; }; 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 = ""; @@ -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 = ""; }; - F9267CB10F844BDD00A827EC /* Breakpad.nib */ = { - isa = PBXVariantGroup; - children = ( - F9267CB20F844BDD00A827EC /* English */, - ); - name = Breakpad.nib; - sourceTree = ""; - }; F945849C0F280E3C009A47BF /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( diff --git a/src/client/mac/Framework/Breakpad.h b/src/client/mac/Framework/Breakpad.h index 483b8769..88be8162 100644 --- a/src/client/mac/Framework/Breakpad.h +++ b/src/client/mac/Framework/Breakpad.h @@ -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 diff --git a/src/client/mac/Framework/Breakpad.mm b/src/client/mac/Framework/Breakpad.mm index 0635e189..ed3d385e 100644 --- a/src/client/mac/Framework/Breakpad.mm +++ b/src/client/mac/Framework/Breakpad.mm @@ -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]); diff --git a/src/client/mac/sender/Breakpad.nib/classes.nib b/src/client/mac/sender/Breakpad.nib/classes.nib new file mode 100644 index 00000000..ebc5ab40 --- /dev/null +++ b/src/client/mac/sender/Breakpad.nib/classes.nib @@ -0,0 +1,57 @@ + + + + + IBClasses + + + ACTIONS + + cancel + id + sendReport + id + showPrivacyPolicy + id + + CLASS + Reporter + LANGUAGE + ObjC + OUTLETS + + alertWindow_ + NSWindow + cancelButton_ + NSButton + commentMessage_ + NSTextField + dialogTitle_ + NSTextField + emailEntryField_ + NSView + emailLabel_ + NSTextField + emailMessage_ + NSTextField + emailSectionBox_ + NSBox + headerBox_ + NSBox + preEmailBox_ + NSBox + privacyLinkArrow_ + NSView + privacyLinkLabel_ + NSTextField + sendButton_ + NSButton + + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/src/client/mac/sender/English.lproj/Breakpad.nib/info.nib b/src/client/mac/sender/Breakpad.nib/info.nib similarity index 84% rename from src/client/mac/sender/English.lproj/Breakpad.nib/info.nib rename to src/client/mac/sender/Breakpad.nib/info.nib index 55565cda..21bd6319 100644 --- a/src/client/mac/sender/English.lproj/Breakpad.nib/info.nib +++ b/src/client/mac/sender/Breakpad.nib/info.nib @@ -3,9 +3,9 @@ IBFramework Version - 670 + 672 IBLastKnownRelativeProjectPath - ../../Breakpad.xcodeproj + ../Breakpad.xcodeproj IBOldestOS 5 IBOpenObjects @@ -13,7 +13,7 @@ 2 IBSystem Version - 9F33 + 9G55 targetFramework IBCocoaFramework diff --git a/src/client/mac/sender/Breakpad.nib/keyedobjects.nib b/src/client/mac/sender/Breakpad.nib/keyedobjects.nib new file mode 100644 index 0000000000000000000000000000000000000000..d0cbd3f24e2a9e3bb76f28d9c4aadec5398cdac7 GIT binary patch literal 13747 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 dB7G@+CH*M - - - - IBClasses - - - ACTIONS - - cancel - id - sendReport - id - showPrivacyPolicy - id - - CLASS - Reporter - LANGUAGE - ObjC - OUTLETS - - alertWindow - NSWindow - - SUPERCLASS - NSObject - - - IBVersion - 1 - - diff --git a/src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib b/src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib deleted file mode 100644 index 50dff43de03b08894b3d5c42efafaf2e40555519..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10208 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 diff --git a/src/client/mac/sender/crash_report_sender.h b/src/client/mac/sender/crash_report_sender.h index 955bd86d..b3169538 100644 --- a/src/client/mac/sender/crash_report_sender.h +++ b/src/client/mac/sender/crash_report_sender.h @@ -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; diff --git a/src/client/mac/sender/crash_report_sender.m b/src/client/mac/sender/crash_report_sender.m index ebf31c20..43d1da08 100644 --- a/src/client/mac/sender/crash_report_sender.m +++ b/src/client/mac/sender/crash_report_sender.m @@ -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 diff --git a/src/client/mac/testapp/Info.plist b/src/client/mac/testapp/Info.plist index 586fb5eb..2794c44b 100644 --- a/src/client/mac/testapp/Info.plist +++ b/src/client/mac/testapp/Info.plist @@ -36,6 +36,8 @@ NO BreakpadSendAndExit YES + BreakpadRequestEmail + YES BreakpadRequestComments YES BreakpadVendor