From 22734848ea2124d2e8c75a472f71ae20a69fb116 Mon Sep 17 00:00:00 2001 From: nealsid Date: Tue, 21 Jul 2009 00:10:57 +0000 Subject: [PATCH] Port fixes from internal Google Breakpad to SVN. A=preston, nealsid R=Stuart, Preston git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@360 4c0a9323-5329-0410-9bdc-e9ce6186880e --- .../mac/Breakpad.xcodeproj/project.pbxproj | 4 + src/client/mac/Framework/Breakpad.h | 11 +- src/client/mac/Framework/Breakpad.mm | 14 +- src/client/mac/crash_generation/Inspector.mm | 21 +- .../mac/sender/Breakpad.nib/classes.nib | 14 +- src/client/mac/sender/Breakpad.nib/info.nib | 6 +- .../mac/sender/Breakpad.nib/keyedobjects.nib | Bin 13747 -> 14674 bytes .../sender/English.lproj/Localizable.strings | Bin 1488 -> 2270 bytes src/client/mac/sender/crash_report_sender.h | 49 +++- src/client/mac/sender/crash_report_sender.m | 215 ++++++++++++++++-- src/client/mac/sender/goArrow.png | Bin 0 -> 3591 bytes .../mac/tests/BreakpadFramework_Test.mm | 5 +- 12 files changed, 287 insertions(+), 52 deletions(-) create mode 100644 src/client/mac/sender/goArrow.png diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj index 97659d32..2dbbdb40 100644 --- a/src/client/mac/Breakpad.xcodeproj/project.pbxproj +++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj @@ -88,6 +88,7 @@ F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE3400F82C68300608B94 /* exception_handler_test.cc */; }; F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; }; + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = F9B6309F100FF96B00D0F4AC /* goArrow.png */; }; F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; }; F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; }; F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; }; @@ -288,6 +289,7 @@ F93DE3400F82C68300608B94 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = handler/exception_handler_test.cc; sourceTree = ""; }; F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = ""; }; F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = ""; }; + F9B6309F100FF96B00D0F4AC /* goArrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goArrow.png; path = sender/goArrow.png; sourceTree = ""; }; F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = ""; }; F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = ""; }; @@ -535,6 +537,7 @@ F92C56A60ECE04B6009BE4BA /* sender */ = { isa = PBXGroup; children = ( + F9B6309F100FF96B00D0F4AC /* goArrow.png */, F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, F945849C0F280E3C009A47BF /* Localizable.strings */, @@ -813,6 +816,7 @@ 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */, 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */, 3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */, + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/client/mac/Framework/Breakpad.h b/src/client/mac/Framework/Breakpad.h index ab4d6220..6b5ce66a 100644 --- a/src/client/mac/Framework/Breakpad.h +++ b/src/client/mac/Framework/Breakpad.h @@ -89,6 +89,7 @@ extern "C" { #define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" #define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" #define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" +#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" // Optional user-defined function to dec to decide if we should handle // this crash or forward it along. @@ -215,7 +216,7 @@ typedef bool (*BreakpadFilterCallback)(int exception_type, //============================================================================= // The following are NOT user-supplied but are documented here for // completeness. They are calculated by Breakpad during initialization & -// crash-dump generation. +// crash-dump generation, or entered in by the user. // // BREAKPAD_PROCESS_START_TIME The time the process started. // @@ -242,6 +243,12 @@ typedef bool (*BreakpadFilterCallback)(int exception_type, // server without leaking Breakpad's // internal values. // +// BREAKPAD_ON_DEMAND Used internally to indicate to the +// Reporter that we're sending on-demand, +// not as result of a crash. +// +// BREAKPAD_COMMENTS The text the user provided as comments. +// Only used in crash_report_sender. // Returns a new BreakpadRef object on success, NULL otherwise. BreakpadRef BreakpadCreate(NSDictionary *parameters); @@ -286,7 +293,7 @@ void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key); // necessary. Note that as mentioned above there are limits on both // the number of keys and their length. void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key, - NSString *value); + NSString *value); // This method will remove a previously-added parameter from the // upload parameter set. diff --git a/src/client/mac/Framework/Breakpad.mm b/src/client/mac/Framework/Breakpad.mm index 551f9785..2528d99f 100644 --- a/src/client/mac/Framework/Breakpad.mm +++ b/src/client/mac/Framework/Breakpad.mm @@ -409,7 +409,6 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; - NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL]; NSString *requestUserText = [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; @@ -451,7 +450,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { vendor = @"Vendor not specified"; } - // Normalize the values + // Normalize the values. if (skipConfirm) { skipConfirm = [skipConfirm uppercaseString]; @@ -504,7 +503,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { [resourcePath stringByAppendingPathComponent:@"Inspector"]; } - // Verify that there is an Inspector tool + // Verify that there is an Inspector tool. if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { DEBUGLOG(stderr, "Cannot find Inspector tool\n"); return false; @@ -517,7 +516,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath]; } - // Verify that there is a Reporter application + // Verify that there is a Reporter application. if (![[NSFileManager defaultManager] fileExistsAtPath:reporterPathString]) { DEBUGLOG(stderr, "Cannot find Reporter tool\n"); @@ -588,11 +587,6 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { } } - if (reportEmail) { - dictionary.SetKeyValue(BREAKPAD_EMAIL, - [reportEmail UTF8String]); - } - if (serverParameters) { // For each key-value pair, call BreakpadAddUploadParameter() NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; @@ -633,7 +627,9 @@ void Breakpad::RemoveKeyValue(NSString *key) { //============================================================================= void Breakpad::GenerateAndSendReport() { + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); HandleException(0, 0, 0, mach_thread_self()); + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); } //============================================================================= diff --git a/src/client/mac/crash_generation/Inspector.mm b/src/client/mac/crash_generation/Inspector.mm index 5c5c2218..d328ee5d 100644 --- a/src/client/mac/crash_generation/Inspector.mm +++ b/src/client/mac/crash_generation/Inspector.mm @@ -319,6 +319,12 @@ kern_return_t Inspector::ReadMessages() { printf("parameter count = %d\n", info.parameter_count); #endif + // In certain situations where multiple crash requests come + // through quickly, we can end up with the mach IPC messages not + // coming through correctly. Since we don't know what parameters + // we've missed, we can't do much besides abort the crash dump + // situation in this case. + unsigned int parameters_read = 0; // The initial message contains the number of key value pairs that // we are expected to read. // Read each key/value pair, one mach message per key/value pair. @@ -329,6 +335,14 @@ kern_return_t Inspector::ReadMessages() { if(result == KERN_SUCCESS) { KeyValueMessageData &key_value_data = (KeyValueMessageData&)*message.GetData(); + // If we get a blank key, make sure we don't increment the + // parameter count; in some cases (notably on-demand generation + // many times in a short period of time) caused the Mach IPC + // messages to not come through correctly. + if (strlen(key_value_data.key) == 0) { + continue; + } + parameters_read++; config_params_.SetKeyValue(key_value_data.key, key_value_data.value); } else { @@ -336,6 +350,11 @@ kern_return_t Inspector::ReadMessages() { break; } } + if (parameters_read != info.parameter_count) { + DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash " + "dump generation.", parameters_read, info.parameter_count); + return KERN_FAILURE; + } } return result; @@ -379,7 +398,7 @@ bool Inspector::InspectTask() { SetCrashTimeParameters(); // If the client app has not specified a minidump directory, // use a default of Library// - if (0 == strlen(minidumpDirectory)) { + if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { NSArray *libraryDirectories = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, diff --git a/src/client/mac/sender/Breakpad.nib/classes.nib b/src/client/mac/sender/Breakpad.nib/classes.nib index ebc5ab40..c9d43a38 100644 --- a/src/client/mac/sender/Breakpad.nib/classes.nib +++ b/src/client/mac/sender/Breakpad.nib/classes.nib @@ -4,6 +4,14 @@ IBClasses + + CLASS + LengthLimitingTextField + LANGUAGE + ObjC + SUPERCLASS + NSTextField + ACTIONS @@ -26,10 +34,14 @@ NSButton commentMessage_ NSTextField + commentsEntryField_ + LengthLimitingTextField + countdownLabel_ + NSTextField dialogTitle_ NSTextField emailEntryField_ - NSView + LengthLimitingTextField emailLabel_ NSTextField emailMessage_ diff --git a/src/client/mac/sender/Breakpad.nib/info.nib b/src/client/mac/sender/Breakpad.nib/info.nib index 21bd6319..e39a8e54 100644 --- a/src/client/mac/sender/Breakpad.nib/info.nib +++ b/src/client/mac/sender/Breakpad.nib/info.nib @@ -3,17 +3,17 @@ IBFramework Version - 672 + 676 IBLastKnownRelativeProjectPath ../Breakpad.xcodeproj IBOldestOS 5 IBOpenObjects - 2 + 132 IBSystem Version - 9G55 + 9J61 targetFramework IBCocoaFramework diff --git a/src/client/mac/sender/Breakpad.nib/keyedobjects.nib b/src/client/mac/sender/Breakpad.nib/keyedobjects.nib index d0cbd3f24e2a9e3bb76f28d9c4aadec5398cdac7..e370206c166426f1ec3ff2a9578fe891ac3a5190 100644 GIT binary patch literal 14674 zcmeHt33L12`8bjf66ZD-oDl|`Tng$gaDEu~PPr0LK`(xfCQrIf-O z0Tovk6%bqi(Tj+P+x5CDC@NRQ1wjM^6&G;d^(uVNOwv-sd(ZbjpXYqvcg}6kNis9< z`YgZa_dN5~xA?rFaCY|F2qS_R#3B`nLt3P(aW0U8A#cE6pJ)i?w7 zmrI^-$cXTjcfNttNHeRXK#1OKaEIL#*KtW@l@q0QsUbhsX_o=pQ9MdOIcNwPibkPJ zQ9c@n>e1zB5n2vUR-il3o#=09JGu|;ME9cy(PQWz=qdC(dIi0S-a^OG`{*O|CHe|| zjebBsq2JK&7-0s-VKq*`y|D_XU<>Yx`{67s;2b;@55ptyNIVwTpcXtGyKyUS#}T{| zUxU}+wRjW04R6DDz9sxsqH( zt|n{9Eo2?JmE1;dCwGu-`^f?F26=}Z zB1g$Fa)O*9ACoiWbMg)Omi$D1Bfm3*VHgdgW%NuEV`eU4EKDlHGd89VGmyz-a+q9Z z7?a0b%8X_vFeS`o%tWS)na0#G)0sJpo2h3crkU|G0j8C?fmzM0VQyh=Wi~O7F#lve zWIkd}F&{IZFrPA?F{ha`%;(G(%$Lkp%-75}%(u*U%=gR>%#X}Z%x^4ahp=Aua&{i; zW1Cq&8(>@5`D~C4v0=8AUBI@n3)yyd5!=BoW;@Xtb_u(brEG+~f?dWgXIHRSvRAQJ zqg&8->`L|;_FDEj_ImaP_C|IUyP92tr?P9=n{X9-GrOMM$lgBH>2LM<-sg-c5$RD9 z;*bFukqMd6C8!rlM!iuAvY=FyhSE_6vLYVYkR3UY6S+_y)ED(b{m}q45M`n)Bp?xG zSC>^*1Onk|r^n|Gg=UpiPL3KkSZ_!%<3jnq6smUnS|zxXQdXJo@kpVNx8CdXhTA85 z8)DC(Z(+di2hRYX3DuTWmbsgykUS3jE)K#)TOc@3ZWSF-eli2*g8}59@IdMC?vl!A2}S5{f=h4uKPXSjEif zTB02&iC`Q3Cp{g(Jk`**sEU3@&(OCcID;Y@N8gNKEB6Nd{7uk8E*gXe!yc7=$$OwI zWojyZ!&P#zkNM$|+_EQMx7fB2#07Bm8loDE%DL*YPkbjdYcI)I&(6ch$L z0e83b?CT+|5W z=mp&sNujXU@0P6uwptlz4SJ+$WtEfM^^%YFraDS!@?kU&diqc^@}mH1LGw`%g-{r^ zq6Mf8EkvMoKv-FE6;h}r;BSzEFoYr|Fz7hlcnYjbzU98Nz5G_GV5k(Q0bs0##vbA3B)@{?3-bE7h ztE872u!gRJpR4P*UL75S24!dRIk}ygd`HgUp_%-k+|JHp=t|htRp@H85?zC?Mc1M0 z(GBQEv#bIHo#{i+JtUHx66hF`%(5%$C<#4ePC4}KM6b{EcwG= z?#8mpaw+HyGl zfrw-amK_!*3I=@Duy3VP*IY%C&)p8T*1f)5Xxw8~Ba6=>dI>dgDO`kd)J&7$Q^&nz z)F6aR=iu+kE5@FKF)Jm1L)2juGmy<@Gunc-qPx&GbT_&Olqw4vzGVLbMb1^WMU9TJ z17IllqXq+Oo;j)2<85${54yo3qE2xy+OY-gKqH~63>xKA5~KHbp$G0l51`#(VzRqL zS1oS@F7g!!lP{ueA3_grLl2`z(4*+@Xe7)YZ4EN&l8lT%QX8nM+Z<~{u%&3by67m6 zqbIhaC(x5?HS{_d^nN-TEO)E{BU%A%M@KDp$PlpH9K~|8 zWg{Nk*?Cy8+_z&Ey9ymd@1R5IFnSjqLC>J0=vW7@w!TJmB+s+AmW6Nl^ZWvX=XFYdIn9AXvvK z^fCGbeTqIqr_mYoIr;+JUztcYM|twf7Pm)|eMi2KjYhU(AZNZnqXN-R>fqw2o!Y7^ z+e2Zgd8Q|7T9Y>{Q76Yt%oc`5|AqrAlY#w5bZQH%X5@v7 z`UMvCEBb;uXg$=M?9tvmiS<9>$p5g-O&d6=#!s6niCKL$^I*7OS>l zRhK=+#H_RdHMB$pI?3ywr--wzhaF)pvf_BKqfD@)ydzPN#fjZ8n+rg@2Pa_;8?X_Z zWT?fx&>jVLCI`KZ0NcLvQ7)AZqaqb(HgH?n)Us%59hW>w@;8Q?CV88^fbxyqs|TC! z=*SzE$q!cyYq)G7R9=8GBm~@>=^AP> z@{nrLQFJ7|i6b1QYbgTj>F5}io5|-6QQ&9raDbn|d0-|n_JV8alx=7~2xT^|!*c+= zbhMVvq1PQp^KdnfYn{s9k4gN7%YAXUW%Tf`QW7E zXbH9aDcs}bctzAf3uvJN_jTMQ9UVE~XM={yjy8A*aF1cxe}>>|WCX|8gOe@B ztI#n8oQuF)^1)jmp1SG`cu{#Uu)x~@`sLe!r}2uR^6rL)AS3|1H^g^&6AuBGZ}a+m zyxZpswDGMWUIsk3CoFg5y?)-~4oQ5Q#QPn7ner$hFg+;@v%4F-E#8nvUX28)hrn0DP>_|k@~yy!GWnKP-vU6Wp4|b0 zfDp9$VK`4FKUWGigFa!JRv6mnZEW?q1%67aH#EOh;+wq!Uj)B-A57Q^>#CNCYJIB@ z+AoMsSQ-e{dwEz3tV7_-TKyj09p)Rn?&b>?8g0NY2>4pVEzkg92}XiBV1fX|dL9^8 zNa+T9XbrUhQ}9BL#fLnS4``gc8sX2iehXfQ*DDM-s`X+z-hiDk#RI^~ijRiwp)$b! z`^b&&1idf8TVr~k0D2#1WR;*?J5GipC3cMXvsHxfc6@ISs?m#HMkk)PJ;ma3vK5pE z+shf&&3o|!3iG{y_u}0$@5K+}NARQY{TNz@m*OXI3w{!usVop|cKZ~vs(||P7@ef0 z+(ga^&^`(1WQhTN+M-bXBHp(J?~{ods*OtipFu@%3K4}wtMQ9{}@T0^H# zRgkAAQ3kGr-=juq;J6;-0w1T&(ri9k$jes#a$-E=1U`8eJ{esRWQYKQ7wZ~wE_@1q z+?8_yc=e()=`2~-@d{PU%^3_DjwU%d|Fa|qe}%sWWzUW#IdYN!x~!wK;j^~3(Cr6T zP)u-2>i-V5|BQbDZ2uQ}?L4;Qa|!@oLsx33JBGdWF{~v7uooKC|A8FiI<$nS(Q8Bl z5uy$POC|NttD*Cpu8xUx%2uK$Nra;fw2AsI5*j{-_7XF>g!EF_6U+hNEYZ0Jtdr$d zugFby)4W05_?$E3PaRbOXp(`B5Pdg5H-e)!$klhs0M*+8rvZ5j1I;aNe>*R`&}jK-ljLjRy$yh?-ns2CfW)^11CVYv{}OggEvT~bb(&-0P*GqHdC@d??6Lg1~{-0IXpQb7J98MyAuz~a= z{iA7$?9Pyocp)LV9I_YL*|KDJ13Hu&&0-Zg1V4x7bauYm17&YOt8gkhNU~+5C4<1< zlEB~Ea37=DeHJT_I}hBhNlS+SCn|@`O5$_)zy2#XI_6*cH7GjfU-~sTI_6*X^&jya z;1sw!8AUF|&1B4Ze8=ZVA?SMsy^^lb&}IeS>#4sBri%gJp@IJo_)a*$_lbb-lh8sy za3A1%fG&s5Q0S2htHoprow9{gkV@J@7hHtxq!6%u8mWn5yF5N%`+ORdZOyM(TAs|h z6z(%9F9$;Wf57!Q#0?X9VgxKi!?eZ7ja7&@S0H114=BG7g7aHi6!_&QNI}408N`85 zhWS>%w6H~icwf8FgBg$};+2_!V{B%uiqZ<{TF+8hRsrPCF+U?vbtT<(S5im}jMB%7o(@0H6TmA!&k~yirb!LLiN0bcvQOrELbBpvWXD)iGodRo_iI$YRn- zmXM`{(uK60E}|WDG3|s`3a}xc#FrNp^DS}HdDt?^bhgpI-F`3L4|XgK~$!z)&8S5qYR0FNG(|_y6O__jiwc?H@+I$V(xu-%B1Qk3`KC z1%U?kr`OXPWQzpa8cUCJV;RaISV)()&_xAK@)YpyIpo=xm);1>`#SJaxprB*a3L@x z5O+gJi0w`;Rdnr689sN&PYJ=NX>0Bu! z>~hV&M>7CJD>(?we9-LH8BLNKN*s^}oNq%8L%DD%d5_)(ElTJnDA$IX0&N$RY)3?` z?KpXVD|tU!+y=jl*0JT628CVK?e3Z7QBfoLNEuZ}?@%h`(c=Gz?nY=XNrFbFVR=nZ z||tQlM=Kw0!{Jp+`Mf$#5^&*%D}Mg~sEc@q@LBrCc;S$Sz5dUM8sGyz~^~n9U4O z)L2U&nHH5xPZff2lbCE6oS6Y?o0Vg5d38#xP^$H;qg_Qvl72m~nUxGagq#!4B-E+#PO`Ykly7 zSgBT~DK;9r?6%SB=z$OVEX?x^eHv8r6x1Z)Y)6MdfUg<5*|nxZCFEiZ9a zm%%Al0F%rNG@7YpW-_xOwg~g!Z8zPwnVCgj*v!t%_G)JKk%mSv3zOjqz1&*^HibRXy1?e7AfoC%m5^|1N29?v%z})Nb65we;QVG=Y z8PMQp@PvGTkW|2kTKX#3kdM9&>lp#EoCAjMhxo7nsH8=n1zzPdt0+rfgX&X{s0xpm zwaiTjUVlPCsIm#l!VM+u&XhLttPs z76QCLE&2RKwBJU&e~Vm6>e}V)$}S;_L3tYfgJ{3JA3)-Bup=wnfnV}7D4LHZh2TMZ zUBV|ROOfa%m@4R!+xXHW{~CIf;mnRl2&%wgtT<_Pm1bCfy89B1BV zPB15#59mSq4n0H<(|73+`W`(>kJ01weR_hPq#w`^=|}Vw{g{42KLt8i8HU=1hnHU~ z@%?&4;QqCM8GJtl`uo>$_6qqeBrm_U;G;;*_q(t(*dJ24t1lSszqRiFTeWUbSbBMD zsHt03e>e`%f2Pdy`-b?*ZGQQMS@{Ke`#;Z>VE&W~<}W;7f9zk|0|%h&_Txo!|F;JB z|J}=#|JLCChws9`L#)xT$IEez?+R>C4(^IRf8U7j_t#h0VpY4pw89n}>@Tgb#RePv*B09PU!bC#KSoo9c}HLS^`9y06>=9| z&bR8g45dDxT=4xaU`BGi0gQvc02MDt5S0=8cafW4=o?$)gaU{|Q`fz-^1WWT7u0LB zV)s}^CJ;H0%rwF=#W|>gp{PtId8ic5uuOuZiBnNIng--&GG2lvK$S|Sd$W)mP9s$T zsi_5uxEPK&0$Gx24IFNq4Tl?R;K1WdNG9q~wM-S^h+`1Qp$-l;J_iRGU!@xrYPbQ` zwhj(5ZifSoJApFY4kS?v~wYpTg%R5XR)){I(820X6soG+rUcfT(*&I zqF>N2=~wh?`VIY-en-EjKhPiPPxNQ{3;mV;i~dG`r)TN82u2YM$HC#4D-*$N1gj!A zE`rq&tchT41nVL=K7tb>I5C3t5u6mkTm%~;*cid42sTIXB@x^!f|Db-cLb+IuqA?1 zBRDOB)46Oef!oY2<~o(PvT(-lcWwx`mV1U<#~Hb;+!F2qZYj5(Tg_#0X6_E|CTEu**+F2*thwaA>U^7{P z&0z<@Iq+ei+fks`@u0D(pshJ@GCat3!ddUt?5*rh_EGjZb`QIkeU*KiJ;{E;e$9TX z>aQB8%2J7{9MvGz5Y;f%aMeiFrK&Nid{v=poNBzPL^V-Ws+z2tqN-F)RZUY(SJkR! zsr;(Nsuim1RX3_utJbP+Rz0eENp(PVNOeMWTJ@dkTpW&L;#6_!I9*(PTw+{OTxQ&` zxWc$`apU7k;wHtF#g)fZ#LbJlGw!~)=i?5=eH{0TI$qsN&8r3VSaqqoRy{{uuWnG! zRX3?GSNqg{b&EQv4y)VMSEz4NZ&Tl=en|bK`f2q(^~>sis^3+Ap#DVtrTQ!NH|k$C zjE2*sYx-z%G$S-cnu(fe8jr@O32Cm;Y|z}RxlePy<^j!vnzu9uHHS3sYTnZv)g0HH z(EP4Br-iIh8>iK1b=m~2OIxg+p`ELpr)|~-wDYwgZL4;XcCmJ~c8m5d?N043?StBV z+Sj#5wV!H#)c&mfRr{OvtPbhaI#rNA%hC zmAW;$jk=w>M|IEXUe%q{eXILk_oMD--LLVE_~G#*<1dXL6Q3Vn7+)MeKE5P=Vti?Q zWBii%d*YvneC;Tnp-h}%SjwPH<_$g79n3R~FI3RIwVsYY( z#D>I{#9(4LaY5q3#LmQ}iIK!*i7OJXN?e(EZQ{1X`x75Xd?N9w#AgzpOWc$ALgHDy zMsL>J^`d^bexiQ5zE(d=U#EBL>-7!#M!i=*Pv5K$=;!Mf=@;vl>LdDV^w;Uv={M*% z>TlEEq2H{3UcXoWqW&fQEBgKVSM{&!KS|Og>5>wX^hsQjF)2AI1)TLU?s4u(?rH8> zZV$JYdy#vId*6Tz#K0Qj3>t&ZkYq3z(hZ{xV+{p{B15rZyrIM}(cm$xH{5O5W!PrFSBR-4wE?l5gO9Wfm>9XFjYePH^~bjtLJ=`+(A(-)>6 zO+TCdW%}K0F{ha`%)HrdcAEQ`MRSfh&pg6B$~@X!XLg%CX35-WzT7;|+-we*uQqQt z?=bH)?=tT;KV*I+d2{kB$^T4#E%`w5o5^n{A4)!)d?fj3^6}&!d#Cp1d)s?Ed-v(x zulIo71AAwsL{c74c{1hclxI_(PuY|5Ldw3Bms9qqyqfY(%HfpvQjVqkn(|x9Sqrid z3u}q9Bv}lWUY6b#i)EzcQp*@izNOGoWGS{xu*|VUEXyn_ELT}pTCTOMvaGQjvV3Ye zZTZgfL#j44H}%rglGKT*rKyus%Tp^-t5T<>)}+ozotY}7Hl~JC7o;vsU6i^wbqQ1o zHl;q6dLWHWbEOre)ugqjMbcKLtxnsNwmEHU+P1WN({`jimiBnslW9+pQL}5emed0^e;2wGWurp&ls4Il_6&2WDL#7%P7j2oY9mqKjVgsbs3v7c4h3# zIGk}L<7me5j1w7OW}LHPC`_uXYOB%eu)3^$t^KV7tz)ev)^cl;wcXlb?X)hnMy%Ic zZ@1oQ-D17VdbjmK>mKV1)_vBOt^2Kqt)E%XSii7-W&MUHJj=)N8eYdI^Zob%d?qjO z+5BjJGCzf{$Ji_Ewf1`ZBKt=BHv2vH?e-n^ zo%UV!-S&sG;rb%JGTgGshXn7mlwS-#ET={NVV>@r&bM zj^7>UoY=`YRZg{2>ohve&R$NZGs`*FS>P;mPI6W{{mu^Oa_36t8s|;UJ_N>CJxmkg%`B@9H7G^EVT9&mU>zb_VvTn#)m$f15tbha}utJ=m z5p+U=pcgp7D42y_LT|w$qzM@UFW3dA&`0Pe3=lE}LC6+zg~7s5Ax{_~j1oo*V}$~t zNGKL22$u%|+z)#6(5W^tXkLEI?bCf*@#7PpGq#Cyc;;tp}AxJ!IYd|Z4& i+$+8)?h_A)Z;Ee;N5zxTm&k-fzuC>v@2*er>;DBGej2&} literal 13747 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 YiO)iO)*);M1& delta 90 zcmca7c!7IDl3)fyDnlYe3Xm*faAU}2D4BebNqFM4Ansg-Vup0M(EZ7e7+rXk81fkM m8G?a|^B7Vl{%vMfVzA!)oOv?i*Hh*K)VFUod`Wv(W diff --git a/src/client/mac/sender/crash_report_sender.h b/src/client/mac/sender/crash_report_sender.h index f62e7613..ca5b3079 100644 --- a/src/client/mac/sender/crash_report_sender.h +++ b/src/client/mac/sender/crash_report_sender.h @@ -41,6 +41,24 @@ extern NSString *const kGoogleServerType; extern NSString *const kSocorroServerType; extern NSString *const kDefaultServerType; + +// We're sublcassing NSTextField in order to override a particular +// method (see the implementation) that lets us reject changes if they +// are longer than a particular length. Bindings would normally solve +// this problem, but when we implemented a validation method, and +// returned NO for strings that were too long, the UI was not updated +// right away, which was a poor user experience. The UI would be +// updated as soon as the text field lost first responder status, +// which isn't soon enough. It is a known bug that the UI KVO didn't +// work in the middle of a validation. +@interface LengthLimitingTextField : NSTextField { + @private + unsigned int maximumLength_; +} + +- (void) setMaximumLength:(unsigned int)maxLength; +@end + @interface Reporter : NSObject { @public IBOutlet NSWindow *alertWindow_; // The alert window @@ -50,26 +68,34 @@ extern NSString *const kDefaultServerType; 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_; + IBOutlet NSTextField *dialogTitle_; + IBOutlet NSTextField *commentMessage_; + IBOutlet NSTextField *emailMessage_; + IBOutlet NSTextField *emailLabel_; + IBOutlet NSTextField *privacyLinkLabel_; + IBOutlet NSButton *sendButton_; + IBOutlet NSButton *cancelButton_; + IBOutlet LengthLimitingTextField *emailEntryField_; + IBOutlet LengthLimitingTextField *commentsEntryField_; + IBOutlet NSTextField *countdownLabel_; + IBOutlet NSView *privacyLinkArrow_; // Text field bindings, for user input. NSString *commentsValue_; // Comments from the user NSString *emailValue_; // Email from the user - + NSString *countdownMessage_; // Message indicating time + // left for input. @private int configFile_; // File descriptor for config file NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) NSData *minidumpContents_; // The data in the minidump (STRONG) NSData *logFileData_; // An NSdata for the tar, // bz2'd log file. + NSTimeInterval remainingDialogTime_; // Keeps track of how long + // we have until we cancel + // the dialog + NSTimer *messageTimer_; // Timer we use to update + // the dialog NSMutableDictionary *serverDictionary_; // The dictionary mapping a // server type name to a // dictionary of server @@ -107,4 +133,7 @@ extern NSString *const kDefaultServerType; - (NSString *)emailValue; - (void)setEmailValue:(NSString *)value; +- (NSString *)countdownMessage; +- (void)setCountdownMessage:(NSString *)value; + @end diff --git a/src/client/mac/sender/crash_report_sender.m b/src/client/mac/sender/crash_report_sender.m index 5d76290c..31c35fe5 100644 --- a/src/client/mac/sender/crash_report_sender.m +++ b/src/client/mac/sender/crash_report_sender.m @@ -42,8 +42,11 @@ #define kLastSubmission @"LastSubmission" const int kMinidumpFileLengthLimit = 800000; +const int kUserCommentsMaxLength = 1500; +const int kEmailMaxLength = 64; -#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys" +#define kApplePrefsSyncExcludeAllKey \ + @"com.apple.PreferenceSync.ExcludeAllSyncKeys" NSString *const kGoogleServerType = @"google"; NSString *const kSocorroServerType = @"socorro"; @@ -174,6 +177,9 @@ NSString *const kDefaultServerType = @"google"; // Returns YES if we should send the report without asking the user first. - (BOOL)shouldSubmitSilently; +// Returns YES if the minidump was generated on demand. +- (BOOL)isOnDemand; + // Returns YES if we should ask the user to provide comments. - (BOOL)shouldRequestComments; @@ -187,11 +193,11 @@ NSString *const kDefaultServerType = @"google"; // Returns the short description of the crash, suitable for use as a dialog // title (e.g., "The application Foo has quit unexpectedly"). -- (NSString*)shortCrashDialogMessage; +- (NSString*)shortDialogMessage; // Return explanatory text about the crash and the reporter, suitable for the // body text of a dialog. -- (NSString*)explanatoryCrashDialogText; +- (NSString*)explanatoryDialogText; // Returns the amount of time the UI should be shown before timing out. - (NSTimeInterval)messageTimeout; @@ -207,7 +213,7 @@ NSString *const kDefaultServerType = @"google"; - (void)removeEmailPrompt; // Run an alert window with the given timeout. Returns -// NSAlertButtonDefault if the timeout is exceeded. A timeout of 0 +// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0 // queues the message immediately in the modal run loop. - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout; @@ -235,6 +241,16 @@ NSString *const kDefaultServerType = @"google"; // will be uploaded to the crash server. - (void)addServerParameter:(id)value forKey:(NSString *)key; +// This method is used to periodically update the UI with how many +// seconds are left in the dialog display. +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer; + +// When we receive this notification, it means that the user has +// begun editing the email address or comments field, and we disable +// the timers so that the user has as long as they want to type +// in their comments/email. +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification; + @end @implementation Reporter @@ -261,6 +277,7 @@ NSString *const kDefaultServerType = @"google"; - (id)initWithConfigurationFD:(int)fd { if ((self = [super init])) { configFile_ = fd; + remainingDialogTime_ = 0; } // Because the reporter is embedded in the framework (and many copies @@ -545,8 +562,8 @@ NSString *const kDefaultServerType = @"google"; } } else { // Create an alert panel to tell the user something happened - NSPanel* alert = NSGetAlertPanel([self shortCrashDialogMessage], - [self explanatoryCrashDialogText], + NSPanel* alert = NSGetAlertPanel([self shortDialogMessage], + [self explanatoryDialogText], NSLocalizedString(@"sendReportButton", @""), NSLocalizedString(@"cancelButton", @""), nil); @@ -566,11 +583,11 @@ NSString *const kDefaultServerType = @"google"; // "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]]; + [dialogTitle_ setStringValue:[self shortDialogMessage]]; // Localize the explanatory text field. [commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@", - [self explanatoryCrashDialogText], + [self explanatoryDialogText], NSLocalizedString(@"commentsMsg", @"")]]; float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit]; [headerBox_ breakpad_shiftVertically:commentHeightDelta]; @@ -615,9 +632,13 @@ NSString *const kDefaultServerType = @"google"; - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout { // Queue a |stopModal| message to be performed in |timeout| seconds. if (timeout > 0.001) { - [NSApp performSelector:@selector(stopModal) - withObject:nil - afterDelay:timeout]; + remainingDialogTime_ = timeout; + SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:); + messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0 + target:self + selector:updateSelector + userInfo:nil + repeats:YES]; } // Run the window modally and wait for either a |stopModal| message or a @@ -625,12 +646,6 @@ NSString *const kDefaultServerType = @"google"; [NSApp activateIgnoringOtherApps:YES]; int returnMethod = [NSApp runModalForWindow:window]; - // Cancel the pending |stopModal| message. - if (returnMethod != NSRunStoppedResponse) { - [NSObject cancelPreviousPerformRequestsWithTarget:NSApp - selector:@selector(stopModal) - object:nil]; - } return returnMethod; } @@ -667,8 +682,10 @@ NSString *const kDefaultServerType = @"google"; textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector { BOOL result = NO; - // If the user has entered text, don't end editing on "return" - if (commandSelector == @selector(insertNewline:) + // If the user has entered text on the comment field, don't end + // editing on "return". + if (control == commentsEntryField_ && + commandSelector == @selector(insertNewline:) && [[textView string] length] > 0) { [textView insertNewlineIgnoringFieldEditor:self]; result = YES; @@ -676,6 +693,50 @@ doCommandBySelector:(SEL)commandSelector { return result; } +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification { + [messageTimer_ invalidate]; + [self setCountdownMessage:@""]; +} + +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer { + remainingDialogTime_ -= 1; + + NSString *countdownMessage; + NSString *formatString; + + int displayedTimeLeft; // This can be either minutes or seconds. + + if (remainingDialogTime_ > 59) { + // calculate minutes remaining for UI purposes + displayedTimeLeft = (remainingDialogTime_ / 60); + + if (displayedTimeLeft == 1) { + formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @""); + } + } else { + displayedTimeLeft = remainingDialogTime_; + if (remainingDialogTime_ == 1) { + formatString = NSLocalizedString(@"countdownMsgSecondSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @""); + } + } + countdownMessage = [NSString stringWithFormat:formatString, + displayedTimeLeft]; + if (remainingDialogTime_ <= 30) { + [countdownLabel_ setTextColor:[NSColor redColor]]; + } + [self setCountdownMessage:countdownMessage]; + if (remainingDialogTime_ <= 0) { + [messageTimer_ invalidate]; + [NSApp stopModal]; + } +} + + + #pragma mark Accessors #pragma mark - //============================================================================= @@ -702,6 +763,17 @@ doCommandBySelector:(SEL)commandSelector { } } +- (NSString *)countdownMessage { + return [[countdownMessage_ retain] autorelease]; +} + +- (void)setCountdownMessage:(NSString *)value { + if (countdownMessage_ != value) { + [countdownMessage_ release]; + countdownMessage_ = [value copy]; + } +} + #pragma mark - //============================================================================= - (BOOL)reportIntervalElapsed { @@ -730,6 +802,11 @@ doCommandBySelector:(SEL)commandSelector { return YES; } +- (BOOL)isOnDemand { + return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND] + isEqualToString:@"YES"]; +} + - (BOOL)shouldSubmitSilently { return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]; @@ -745,21 +822,40 @@ doCommandBySelector:(SEL)commandSelector { isEqualToString:@"YES"]; } -- (NSString*)shortCrashDialogMessage { +- (NSString*)shortDialogMessage { NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; if (![displayName length]) displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; - return [NSString stringWithFormat:NSLocalizedString(@"headerFmt", @""), - displayName]; + if ([self isOnDemand]) { + return [NSString + stringWithFormat:NSLocalizedString(@"noCrashDialogHeader", @""), + displayName]; + } else { + return [NSString + stringWithFormat:NSLocalizedString(@"crashDialogHeader", @""), + displayName]; + } } -- (NSString*)explanatoryCrashDialogText { +- (NSString*)explanatoryDialogText { + NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + if (![displayName length]) + displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR]; if (![vendor length]) vendor = @"unknown vendor"; - return [NSString stringWithFormat:NSLocalizedString(@"msgFmt", @""), vendor]; + if ([self isOnDemand]) { + return [NSString + stringWithFormat:NSLocalizedString(@"noCrashDialogMsg", @""), + vendor, displayName]; + } else { + return [NSString + stringWithFormat:NSLocalizedString(@"crashDialogMsg", @""), + vendor]; + } } - (NSTimeInterval)messageTimeout { @@ -940,6 +1036,77 @@ doCommandBySelector:(SEL)commandSelector { [super dealloc]; } +- (void)awakeFromNib { + [emailEntryField_ setMaximumLength:kEmailMaxLength]; + [commentsEntryField_ setMaximumLength:kUserCommentsMaxLength]; +} + +@end + +//============================================================================= +@implementation LengthLimitingTextField + +- (void) setMaximumLength:(unsigned int)maxLength { + maximumLength_ = maxLength; +} + +// This is the method we're overriding in NSTextField, which lets us +// limit the user's input if it makes the string too long. +- (BOOL) textView:(NSTextView *)textView +shouldChangeTextInRange:(NSRange)affectedCharRange + replacementString:(NSString *)replacementString { + + // Sometimes the range comes in invalid, so reject if we can't + // figure out if the replacement text is too long. + if (affectedCharRange.location == NSNotFound) { + return NO; + } + // Figure out what the new string length would be, taking into + // account user selections. + int newStringLength = + [[textView string] length] - affectedCharRange.length + + [replacementString length]; + if (newStringLength > maximumLength_) { + return NO; + } else { + return YES; + } +} + +// Cut, copy, and paste have to be caught specifically since there is no menu. +- (BOOL)performKeyEquivalent:(NSEvent*)event { + // Only handle the key equivalent if |self| is the text field with focus. + NSText* fieldEditor = [self currentEditor]; + if (fieldEditor != nil) { + // Check for a single "Command" modifier + unsigned int modifiers = [event modifierFlags]; + modifiers &= NSDeviceIndependentModifierFlagsMask; + if (modifiers == NSCommandKeyMask) { + // Now, check for Select All, Cut, Copy, or Paste key equivalents. + NSString* characters = [event characters]; + // Select All is Command-A. + if ([characters isEqualToString:@"a"]) { + [fieldEditor selectAll:self]; + return YES; + // Cut is Command-X. + } else if ([characters isEqualToString:@"x"]) { + [fieldEditor cut:self]; + return YES; + // Copy is Command-C. + } else if ([characters isEqualToString:@"c"]) { + [fieldEditor copy:self]; + return YES; + // Paste is Command-V. + } else if ([characters isEqualToString:@"v"]) { + [fieldEditor paste:self]; + return YES; + } + } + } + // Let the super class handle the rest (e.g. Command-Period will cancel). + return [super performKeyEquivalent:event]; +} + @end //============================================================================= diff --git a/src/client/mac/sender/goArrow.png b/src/client/mac/sender/goArrow.png new file mode 100644 index 0000000000000000000000000000000000000000..f318a56711d43d1b8925f1c26e709da0872dd607 GIT binary patch literal 3591 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