aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorstuartmorgan <stuartmorgan@4c0a9323-5329-0410-9bdc-e9ce6186880e>2009-04-29 13:50:53 +0000
committerstuartmorgan <stuartmorgan@4c0a9323-5329-0410-9bdc-e9ce6186880e>2009-04-29 13:50:53 +0000
commit33e8fad61dde4622d3f88ef9a2df38aa9620321a (patch)
treea770314d9d8297cdbbd4b80cdf30dc18a6f122c6 /src
parentMac framework fixes: Propagate server type from plist, and fix precedence of ... (diff)
downloadbreakpad-33e8fad61dde4622d3f88ef9a2df38aa9620321a.tar.xz
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
Diffstat (limited to 'src')
-rw-r--r--src/client/mac/Breakpad.xcodeproj/project.pbxproj20
-rw-r--r--src/client/mac/Framework/Breakpad.h11
-rw-r--r--src/client/mac/Framework/Breakpad.mm5
-rw-r--r--src/client/mac/sender/Breakpad.nib/classes.nib57
-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.nibbin0 -> 13747 bytes
-rw-r--r--src/client/mac/sender/English.lproj/Breakpad.nib/classes.nib33
-rw-r--r--src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nibbin10208 -> 0 bytes
-rw-r--r--src/client/mac/sender/English.lproj/Localizable.stringsbin1362 -> 1488 bytes
-rw-r--r--src/client/mac/sender/crash_report_sender.h34
-rw-r--r--src/client/mac/sender/crash_report_sender.m419
-rw-r--r--src/client/mac/testapp/Info.plist2
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
new file mode 100644
index 00000000..d0cbd3f2
--- /dev/null
+++ b/src/client/mac/sender/Breakpad.nib/keyedobjects.nib
Binary files differ
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
deleted file mode 100644
index 50dff43d..00000000
--- a/src/client/mac/sender/English.lproj/Breakpad.nib/keyedobjects.nib
+++ /dev/null
Binary files differ
diff --git a/src/client/mac/sender/English.lproj/Localizable.strings b/src/client/mac/sender/English.lproj/Localizable.strings
index aa2ca503..efc832ac 100644
--- a/src/client/mac/sender/English.lproj/Localizable.strings
+++ b/src/client/mac/sender/English.lproj/Localizable.strings
Binary files differ
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>