diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/mac/Breakpad.xcodeproj/project.pbxproj | 20 | ||||
-rw-r--r-- | src/client/mac/Framework/Breakpad.h | 11 | ||||
-rw-r--r-- | src/client/mac/Framework/Breakpad.mm | 5 | ||||
-rw-r--r-- | src/client/mac/sender/Breakpad.nib/classes.nib | 57 | ||||
-rw-r--r-- | src/client/mac/sender/Breakpad.nib/info.nib (renamed from src/client/mac/sender/English.lproj/Breakpad.nib/info.nib) | 6 | ||||
-rw-r--r-- | src/client/mac/sender/Breakpad.nib/keyedobjects.nib | bin | 0 -> 13747 bytes | |||
-rw-r--r-- | src/client/mac/sender/English.lproj/Breakpad.nib/classes.nib | 33 | ||||
-rw-r--r-- | src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib | bin | 10208 -> 0 bytes | |||
-rw-r--r-- | src/client/mac/sender/English.lproj/Localizable.strings | bin | 1362 -> 1488 bytes | |||
-rw-r--r-- | src/client/mac/sender/crash_report_sender.h | 34 | ||||
-rw-r--r-- | src/client/mac/sender/crash_report_sender.m | 419 | ||||
-rw-r--r-- | src/client/mac/testapp/Info.plist | 2 |
12 files changed, 380 insertions, 207 deletions
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 = "<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 = ( + 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 */, - F9267CB10F844BDD00A827EC /* Breakpad.nib */, - F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, - F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, ); 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 = ( 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 @@ +<?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> diff --git a/src/client/mac/sender/English.lproj/Breakpad.nib/info.nib b/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 @@ <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> diff --git a/src/client/mac/sender/Breakpad.nib/keyedobjects.nib b/src/client/mac/sender/Breakpad.nib/keyedobjects.nib Binary files differnew file mode 100644 index 00000000..d0cbd3f2 --- /dev/null +++ b/src/client/mac/sender/Breakpad.nib/keyedobjects.nib diff --git a/src/client/mac/sender/English.lproj/Breakpad.nib/classes.nib b/src/client/mac/sender/English.lproj/Breakpad.nib/classes.nib deleted file mode 100644 index 2636d457..00000000 --- a/src/client/mac/sender/English.lproj/Breakpad.nib/classes.nib +++ /dev/null @@ -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> diff --git a/src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib b/src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib Binary files differdeleted file mode 100644 index 50dff43d..00000000 --- a/src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib +++ /dev/null diff --git a/src/client/mac/sender/English.lproj/Localizable.strings b/src/client/mac/sender/English.lproj/Localizable.strings Binary files differindex aa2ca503..efc832ac 100644 --- a/src/client/mac/sender/English.lproj/Localizable.strings +++ b/src/client/mac/sender/English.lproj/Localizable.strings 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(); - int buttonPressed = NSAlertAlternateReturn; + // Get the timeout value for the notification. + NSTimeInterval timeout = [self messageTimeout]; - if (shouldRequestComments) { + int buttonPressed = NSAlertAlternateReturn; + // 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]]; - - // Add the request for email address. - [self setEmailMessage: - NSLocalizedStringFromTableInBundle(@"emailMsg", nil, bundle, @"")]; - - // Run the alert - 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]; - } + if (!didLoadNib) { + return NO; + } + + [self configureAlertWindowIncludingEmail:[self shouldRequestEmail]]; + + 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]; } } 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 @@ <string>NO</string> <key>BreakpadSendAndExit</key> <string>YES</string> + <key>BreakpadRequestEmail</key> + <string>YES</string> <key>BreakpadRequestComments</key> <string>YES</string> <key>BreakpadVendor</key> |