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:
parent
73afbc7302
commit
33e8fad61d
@ -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 = (
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
|
||||
|
57
src/client/mac/sender/Breakpad.nib/classes.nib
generated
Normal file
57
src/client/mac/sender/Breakpad.nib/classes.nib
generated
Normal 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>
|
@ -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>
|
BIN
src/client/mac/sender/Breakpad.nib/keyedobjects.nib
generated
Normal file
BIN
src/client/mac/sender/Breakpad.nib/keyedobjects.nib
generated
Normal file
Binary file not shown.
@ -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>
|
Binary file not shown.
Binary file not shown.
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user