summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/client/crashreporter_osx.mm
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-04-01 13:55:00 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-04-01 13:55:00 -0400
commitce3979c721ba378a448bfbe3671c99d993cbc801 (patch)
treee200d5225bcecef5f974b946a58277fddd24e89c /toolkit/crashreporter/client/crashreporter_osx.mm
parentf6c16cff36048c583ca0e1d019b622336ca861a0 (diff)
parentff2f287f82630ab3887d7d5c1e64e5b888ea0beb (diff)
downloadUXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar.gz
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar.lz
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.tar.xz
UXP-ce3979c721ba378a448bfbe3671c99d993cbc801.zip
Merge branch 'master' into Sync-weave
Diffstat (limited to 'toolkit/crashreporter/client/crashreporter_osx.mm')
-rw-r--r--toolkit/crashreporter/client/crashreporter_osx.mm922
1 files changed, 0 insertions, 922 deletions
diff --git a/toolkit/crashreporter/client/crashreporter_osx.mm b/toolkit/crashreporter/client/crashreporter_osx.mm
deleted file mode 100644
index 0768d6da3..000000000
--- a/toolkit/crashreporter/client/crashreporter_osx.mm
+++ /dev/null
@@ -1,922 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#import <Cocoa/Cocoa.h>
-#import <CoreFoundation/CoreFoundation.h>
-#include "crashreporter.h"
-#include "crashreporter_osx.h"
-#include <crt_externs.h>
-#include <spawn.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <sstream>
-
-using std::string;
-using std::vector;
-using std::ostringstream;
-
-using namespace CrashReporter;
-
-static NSAutoreleasePool* gMainPool;
-static CrashReporterUI* gUI = 0;
-static StringTable gFiles;
-static StringTable gQueryParameters;
-static string gURLParameter;
-static string gSendURL;
-static vector<string> gRestartArgs;
-static bool gDidTrySend = false;
-static bool gRTLlayout = false;
-
-static cpu_type_t pref_cpu_types[2] = {
-#if defined(__i386__)
- CPU_TYPE_X86,
-#elif defined(__x86_64__)
- CPU_TYPE_X86_64,
-#elif defined(__ppc__)
- CPU_TYPE_POWERPC,
-#endif
- CPU_TYPE_ANY };
-
-#define NSSTR(s) [NSString stringWithUTF8String:(s).c_str()]
-
-static NSString* Str(const char* aName)
-{
- string str = gStrings[aName];
- if (str.empty()) str = "?";
- return NSSTR(str);
-}
-
-static bool RestartApplication()
-{
- vector<char*> argv(gRestartArgs.size() + 1);
-
- posix_spawnattr_t spawnattr;
- if (posix_spawnattr_init(&spawnattr) != 0) {
- return false;
- }
-
- // Set spawn attributes.
- size_t attr_count = sizeof(pref_cpu_types) / sizeof(pref_cpu_types[0]);
- size_t attr_ocount = 0;
- if (posix_spawnattr_setbinpref_np(&spawnattr,
- attr_count,
- pref_cpu_types,
- &attr_ocount) != 0 ||
- attr_ocount != attr_count) {
- posix_spawnattr_destroy(&spawnattr);
- return false;
- }
-
- unsigned int i;
- for (i = 0; i < gRestartArgs.size(); i++) {
- argv[i] = (char*)gRestartArgs[i].c_str();
- }
- argv[i] = 0;
-
- char **env = NULL;
- char ***nsEnv = _NSGetEnviron();
- if (nsEnv)
- env = *nsEnv;
- int result = posix_spawnp(NULL,
- argv[0],
- NULL,
- &spawnattr,
- &argv[0],
- env);
-
- posix_spawnattr_destroy(&spawnattr);
-
- return result == 0;
-}
-
-@implementation CrashReporterUI
-
--(void)awakeFromNib
-{
- gUI = self;
- [mWindow center];
-
- [mWindow setTitle:[[NSBundle mainBundle]
- objectForInfoDictionaryKey:@"CFBundleName"]];
-}
-
--(void)showCrashUI:(const StringTable&)files
- queryParameters:(const StringTable&)queryParameters
- sendURL:(const string&)sendURL
-{
- gFiles = files;
- gQueryParameters = queryParameters;
- gSendURL = sendURL;
-
- [mWindow setTitle:Str(ST_CRASHREPORTERTITLE)];
- [mHeaderLabel setStringValue:Str(ST_CRASHREPORTERHEADER)];
-
- NSRect viewReportFrame = [mViewReportButton frame];
- [mViewReportButton setTitle:Str(ST_VIEWREPORT)];
- [mViewReportButton sizeToFit];
- if (gRTLlayout) {
- // sizeToFit will keep the left side fixed, so realign
- float oldWidth = viewReportFrame.size.width;
- viewReportFrame = [mViewReportButton frame];
- viewReportFrame.origin.x += oldWidth - viewReportFrame.size.width;
- [mViewReportButton setFrame: viewReportFrame];
- }
-
- [mSubmitReportButton setTitle:Str(ST_CHECKSUBMIT)];
- [mIncludeURLButton setTitle:Str(ST_CHECKURL)];
- [mEmailMeButton setTitle:Str(ST_CHECKEMAIL)];
- [mViewReportOkButton setTitle:Str(ST_OK)];
-
- [mCommentText setPlaceholder:Str(ST_COMMENTGRAYTEXT)];
- if (gRTLlayout)
- [mCommentText toggleBaseWritingDirection:self];
- [[mEmailText cell] setPlaceholderString:Str(ST_EMAILGRAYTEXT)];
-
- if (gQueryParameters.find("URL") != gQueryParameters.end()) {
- // save the URL value in case the checkbox gets unchecked
- gURLParameter = gQueryParameters["URL"];
- }
- else {
- // no URL specified, hide checkbox
- [mIncludeURLButton removeFromSuperview];
- // shrink window to fit
- NSRect frame = [mWindow frame];
- NSRect includeURLFrame = [mIncludeURLButton frame];
- NSRect emailFrame = [mEmailMeButton frame];
- int buttonMask = [mViewReportButton autoresizingMask];
- int checkMask = [mSubmitReportButton autoresizingMask];
- int commentScrollMask = [mCommentScrollView autoresizingMask];
-
- [mViewReportButton setAutoresizingMask:NSViewMinYMargin];
- [mSubmitReportButton setAutoresizingMask:NSViewMinYMargin];
- [mCommentScrollView setAutoresizingMask:NSViewMinYMargin];
-
- // remove all the space in between
- frame.size.height -= includeURLFrame.origin.y - emailFrame.origin.y;
- [mWindow setFrame:frame display: true animate:NO];
-
- [mViewReportButton setAutoresizingMask:buttonMask];
- [mSubmitReportButton setAutoresizingMask:checkMask];
- [mCommentScrollView setAutoresizingMask:commentScrollMask];
- }
-
- // resize some buttons horizontally and possibly some controls vertically
- [self doInitialResizing];
-
- // load default state of submit checkbox
- // we don't just do this via IB because we want the default to be
- // off a certain percentage of the time
- BOOL submitChecked = NO;
- NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
- if (nil != [userDefaults objectForKey:@"submitReport"]) {
- submitChecked = [userDefaults boolForKey:@"submitReport"];
- }
- else {
- // use compile-time specified enable percentage
- submitChecked = ShouldEnableSending();
- [userDefaults setBool:submitChecked forKey:@"submitReport"];
- }
- [mSubmitReportButton setState:(submitChecked ? NSOnState : NSOffState)];
-
- [self updateSubmit];
- [self updateURL];
- [self updateEmail];
-
- [mWindow makeKeyAndOrderFront:nil];
-}
-
--(void)showErrorUI:(const string&)message
-{
- [self setView: mErrorView animate: NO];
-
- [mErrorHeaderLabel setStringValue:Str(ST_CRASHREPORTERHEADER)];
- [self setStringFitVertically:mErrorLabel
- string:NSSTR(message)
- resizeWindow:YES];
- [mErrorCloseButton setTitle:Str(ST_OK)];
-
- [mErrorCloseButton setKeyEquivalent:@"\r"];
- [mWindow makeFirstResponder:mErrorCloseButton];
- [mWindow makeKeyAndOrderFront:nil];
-}
-
--(void)showReportInfo
-{
- NSDictionary* boldAttr = [NSDictionary
- dictionaryWithObject:
- [NSFont boldSystemFontOfSize:
- [NSFont smallSystemFontSize]]
- forKey:NSFontAttributeName];
- NSDictionary* normalAttr = [NSDictionary
- dictionaryWithObject:
- [NSFont systemFontOfSize:
- [NSFont smallSystemFontSize]]
- forKey:NSFontAttributeName];
-
- [mViewReportTextView setString:@""];
- for (StringTable::iterator iter = gQueryParameters.begin();
- iter != gQueryParameters.end();
- iter++) {
- NSAttributedString* key = [[NSAttributedString alloc]
- initWithString:NSSTR(iter->first + ": ")
- attributes:boldAttr];
- NSAttributedString* value = [[NSAttributedString alloc]
- initWithString:NSSTR(iter->second + "\n")
- attributes:normalAttr];
- [[mViewReportTextView textStorage] appendAttributedString: key];
- [[mViewReportTextView textStorage] appendAttributedString: value];
- [key release];
- [value release];
- }
-
- NSAttributedString* extra = [[NSAttributedString alloc]
- initWithString:NSSTR("\n" + gStrings[ST_EXTRAREPORTINFO])
- attributes:normalAttr];
- [[mViewReportTextView textStorage] appendAttributedString: extra];
- [extra release];
-}
-
-- (void)maybeSubmitReport
-{
- if ([mSubmitReportButton state] == NSOnState) {
- [self setStringFitVertically:mProgressText
- string:Str(ST_REPORTDURINGSUBMIT)
- resizeWindow:YES];
- // disable all the controls
- [self enableControls:NO];
- [mSubmitReportButton setEnabled:NO];
- [mRestartButton setEnabled:NO];
- [mCloseButton setEnabled:NO];
- [mProgressIndicator startAnimation:self];
- gDidTrySend = true;
- [self sendReport];
- } else {
- [NSApp terminate:self];
- }
-}
-
-- (void)closeMeDown:(id)unused
-{
- [NSApp terminate:self];
-}
-
--(IBAction)submitReportClicked:(id)sender
-{
- [self updateSubmit];
- NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
- [userDefaults setBool:([mSubmitReportButton state] == NSOnState)
- forKey:@"submitReport"];
- [userDefaults synchronize];
-}
-
--(IBAction)viewReportClicked:(id)sender
-{
- [self showReportInfo];
- [NSApp beginSheet:mViewReportWindow modalForWindow:mWindow
- modalDelegate:nil didEndSelector:nil contextInfo:nil];
-}
-
-- (IBAction)viewReportOkClicked:(id)sender
-{
- [mViewReportWindow orderOut:nil];
- [NSApp endSheet:mViewReportWindow];
-}
-
--(IBAction)closeClicked:(id)sender
-{
- [self maybeSubmitReport];
-}
-
--(IBAction)restartClicked:(id)sender
-{
- RestartApplication();
- [self maybeSubmitReport];
-}
-
-- (IBAction)includeURLClicked:(id)sender
-{
- [self updateURL];
-}
-
--(IBAction)emailMeClicked:(id)sender
-{
- [self updateEmail];
-}
-
--(void)controlTextDidChange:(NSNotification *)note
-{
- [self updateEmail];
-}
-
-- (void)textDidChange:(NSNotification *)aNotification
-{
- // update comment parameter
- if ([[[mCommentText textStorage] mutableString] length] > 0)
- gQueryParameters["Comments"] = [[[mCommentText textStorage] mutableString]
- UTF8String];
- else
- gQueryParameters.erase("Comments");
-}
-
-// Limit the comment field to 500 bytes in UTF-8
-- (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
-{
- // current string length + replacement text length - replaced range length
- if (([[aTextView string]
- lengthOfBytesUsingEncoding:NSUTF8StringEncoding]
- + [replacementString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]
- - [[[aTextView string] substringWithRange:affectedCharRange]
- lengthOfBytesUsingEncoding:NSUTF8StringEncoding])
- > MAX_COMMENT_LENGTH) {
- return NO;
- }
- return YES;
-}
-
-- (void)doInitialResizing
-{
- NSRect windowFrame = [mWindow frame];
- NSRect restartFrame = [mRestartButton frame];
- NSRect closeFrame = [mCloseButton frame];
- // resize close button to fit text
- float oldCloseWidth = closeFrame.size.width;
- [mCloseButton setTitle:Str(ST_QUIT)];
- [mCloseButton sizeToFit];
- closeFrame = [mCloseButton frame];
- // move close button left if it grew
- if (!gRTLlayout) {
- closeFrame.origin.x -= closeFrame.size.width - oldCloseWidth;
- }
-
- if (gRestartArgs.size() == 0) {
- [mRestartButton removeFromSuperview];
- if (!gRTLlayout) {
- closeFrame.origin.x = restartFrame.origin.x +
- (restartFrame.size.width - closeFrame.size.width);
- }
- else {
- closeFrame.origin.x = restartFrame.origin.x;
- }
- [mCloseButton setFrame: closeFrame];
- [mCloseButton setKeyEquivalent:@"\r"];
- } else {
- [mRestartButton setTitle:Str(ST_RESTART)];
- // resize "restart" button
- float oldRestartWidth = restartFrame.size.width;
- [mRestartButton sizeToFit];
- restartFrame = [mRestartButton frame];
- if (!gRTLlayout) {
- // move left by the amount that the button grew
- restartFrame.origin.x -= restartFrame.size.width - oldRestartWidth;
- closeFrame.origin.x -= restartFrame.size.width - oldRestartWidth;
- }
- else {
- // shift the close button right in RTL
- closeFrame.origin.x += restartFrame.size.width - oldRestartWidth;
- }
- [mRestartButton setFrame: restartFrame];
- [mCloseButton setFrame: closeFrame];
- // possibly resize window if both buttons no longer fit
- // leave 20 px from either side of the window, and 12 px
- // between the buttons
- float neededWidth = closeFrame.size.width + restartFrame.size.width +
- 2*20 + 12;
-
- if (neededWidth > windowFrame.size.width) {
- windowFrame.size.width = neededWidth;
- [mWindow setFrame:windowFrame display: true animate: NO];
- }
- [mRestartButton setKeyEquivalent:@"\r"];
- }
-
- NSButton *checkboxes[] = {
- mSubmitReportButton,
- mIncludeURLButton,
- mEmailMeButton
- };
-
- for (int i=0; i<3; i++) {
- NSRect frame = [checkboxes[i] frame];
- [checkboxes[i] sizeToFit];
- if (gRTLlayout) {
- // sizeToFit will keep the left side fixed, so realign
- float oldWidth = frame.size.width;
- frame = [checkboxes[i] frame];
- frame.origin.x += oldWidth - frame.size.width;
- [checkboxes[i] setFrame: frame];
- }
- // keep existing spacing on left side, + 20 px spare on right
- float neededWidth = frame.origin.x + frame.size.width + 20;
- if (neededWidth > windowFrame.size.width) {
- windowFrame.size.width = neededWidth;
- [mWindow setFrame:windowFrame display: true animate: NO];
- }
- }
-
- // do this down here because we may have made the window wider
- // up above
- [self setStringFitVertically:mDescriptionLabel
- string:Str(ST_CRASHREPORTERDESCRIPTION)
- resizeWindow:YES];
-
- // now pin all the controls (except quit/submit) in place,
- // if we lengthen the window after this, it's just to lengthen
- // the progress text, so nothing above that text should move.
- NSView* views[] = {
- mSubmitReportButton,
- mViewReportButton,
- mCommentScrollView,
- mIncludeURLButton,
- mEmailMeButton,
- mEmailText,
- mProgressIndicator,
- mProgressText
- };
- for (unsigned int i=0; i<sizeof(views)/sizeof(views[0]); i++) {
- [views[i] setAutoresizingMask:NSViewMinYMargin];
- }
-}
-
--(float)setStringFitVertically:(NSControl*)control
- string:(NSString*)str
- resizeWindow:(BOOL)resizeWindow
-{
- // hack to make the text field grow vertically
- NSRect frame = [control frame];
- float oldHeight = frame.size.height;
-
- frame.size.height = 10000;
- NSSize oldCellSize = [[control cell] cellSizeForBounds: frame];
- [control setStringValue: str];
- NSSize newCellSize = [[control cell] cellSizeForBounds: frame];
-
- float delta = newCellSize.height - oldCellSize.height;
- frame.origin.y -= delta;
- frame.size.height = oldHeight + delta;
- [control setFrame: frame];
-
- if (resizeWindow) {
- NSRect frame = [mWindow frame];
- frame.origin.y -= delta;
- frame.size.height += delta;
- [mWindow setFrame:frame display: true animate: NO];
- }
-
- return delta;
-}
-
--(void)setView: (NSView*)v animate: (BOOL)animate
-{
- NSRect frame = [mWindow frame];
-
- NSRect oldViewFrame = [[mWindow contentView] frame];
- NSRect newViewFrame = [v frame];
-
- frame.origin.y += oldViewFrame.size.height - newViewFrame.size.height;
- frame.size.height -= oldViewFrame.size.height - newViewFrame.size.height;
-
- frame.origin.x += oldViewFrame.size.width - newViewFrame.size.width;
- frame.size.width -= oldViewFrame.size.width - newViewFrame.size.width;
-
- [mWindow setContentView:v];
- [mWindow setFrame:frame display:true animate:animate];
-}
-
-- (void)enableControls:(BOOL)enabled
-{
- [mViewReportButton setEnabled:enabled];
- [mIncludeURLButton setEnabled:enabled];
- [mEmailMeButton setEnabled:enabled];
- [mCommentText setEnabled:enabled];
- [mCommentScrollView setHasVerticalScroller:enabled];
- [self updateEmail];
-}
-
--(void)updateSubmit
-{
- if ([mSubmitReportButton state] == NSOnState) {
- [self setStringFitVertically:mProgressText
- string:Str(ST_REPORTPRESUBMIT)
- resizeWindow:YES];
- [mProgressText setHidden:NO];
- // enable all the controls
- [self enableControls:YES];
- }
- else {
- // not submitting, disable all the controls under
- // the submit checkbox, and hide the status text
- [mProgressText setHidden:YES];
- [self enableControls:NO];
- }
-}
-
--(void)updateURL
-{
- if ([mIncludeURLButton state] == NSOnState && !gURLParameter.empty()) {
- gQueryParameters["URL"] = gURLParameter;
- } else {
- gQueryParameters.erase("URL");
- }
-}
-
--(void)updateEmail
-{
- if ([mEmailMeButton state] == NSOnState &&
- [mSubmitReportButton state] == NSOnState) {
- NSString* email = [mEmailText stringValue];
- gQueryParameters["Email"] = [email UTF8String];
- [mEmailText setEnabled:YES];
- } else {
- gQueryParameters.erase("Email");
- [mEmailText setEnabled:NO];
- }
-}
-
--(void)sendReport
-{
- if (![self setupPost]) {
- LogMessage("Crash report submission failed: could not set up POST data");
- [self setStringFitVertically:mProgressText
- string:Str(ST_SUBMITFAILED)
- resizeWindow:YES];
- // quit after 5 seconds
- [self performSelector:@selector(closeMeDown:) withObject:nil
- afterDelay:5.0];
- }
-
- [NSThread detachNewThreadSelector:@selector(uploadThread:)
- toTarget:self
- withObject:mPost];
-}
-
--(bool)setupPost
-{
- NSURL* url = [NSURL URLWithString:[NSSTR(gSendURL) stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
- if (!url) return false;
-
- mPost = [[HTTPMultipartUpload alloc] initWithURL: url];
- if (!mPost) return false;
-
- NSMutableDictionary* parameters =
- [[NSMutableDictionary alloc] initWithCapacity: gQueryParameters.size()];
- if (!parameters) return false;
-
- StringTable::const_iterator end = gQueryParameters.end();
- for (StringTable::const_iterator i = gQueryParameters.begin();
- i != end;
- i++) {
- NSString* key = NSSTR(i->first);
- NSString* value = NSSTR(i->second);
- if (key && value) {
- [parameters setObject: value forKey: key];
- } else {
- ostringstream message;
- message << "Warning: skipping annotation '" << i->first
- << "' due to malformed UTF-8 encoding";
- LogMessage(message.str());
- }
- }
-
- for (StringTable::const_iterator i = gFiles.begin();
- i != gFiles.end();
- i++) {
- [mPost addFileAtPath: NSSTR(i->second) name: NSSTR(i->first)];
- }
-
- [mPost setParameters: parameters];
- [parameters release];
-
- return true;
-}
-
--(void)uploadComplete:(NSData*)data
-{
- NSHTTPURLResponse* response = [mPost response];
- [mPost release];
-
- bool success;
- string reply;
- if (!data || !response || [response statusCode] != 200) {
- success = false;
- reply = "";
-
- // if data is nil, we probably logged an error in uploadThread
- if (data != nil && response != nil) {
- ostringstream message;
- message << "Crash report submission failed: server returned status "
- << [response statusCode];
- LogMessage(message.str());
- }
- } else {
- success = true;
- LogMessage("Crash report submitted successfully");
-
- NSString* encodingName = [response textEncodingName];
- NSStringEncoding encoding;
- if (encodingName) {
- encoding = CFStringConvertEncodingToNSStringEncoding(
- CFStringConvertIANACharSetNameToEncoding((CFStringRef)encodingName));
- } else {
- encoding = NSISOLatin1StringEncoding;
- }
- NSString* r = [[NSString alloc] initWithData: data encoding: encoding];
- reply = [r UTF8String];
- [r release];
- }
-
- SendCompleted(success, reply);
-
- [mProgressIndicator stopAnimation:self];
- if (success) {
- [self setStringFitVertically:mProgressText
- string:Str(ST_REPORTSUBMITSUCCESS)
- resizeWindow:YES];
- } else {
- [self setStringFitVertically:mProgressText
- string:Str(ST_SUBMITFAILED)
- resizeWindow:YES];
- }
- // quit after 5 seconds
- [self performSelector:@selector(closeMeDown:) withObject:nil
- afterDelay:5.0];
-}
-
--(void)uploadThread:(HTTPMultipartUpload*)post
-{
- NSAutoreleasePool* autoreleasepool = [[NSAutoreleasePool alloc] init];
- NSError* error = nil;
- NSData* data = [post send: &error];
- if (error) {
- data = nil;
- NSString* errorDesc = [error localizedDescription];
- string message = [errorDesc UTF8String];
- LogMessage("Crash report submission failed: " + message);
- }
-
- [self performSelectorOnMainThread: @selector(uploadComplete:)
- withObject: data
- waitUntilDone: YES];
-
- [autoreleasepool release];
-}
-
-// to get auto-quit when we close the window
--(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication
-{
- return YES;
-}
-
--(void)applicationWillTerminate:(NSNotification *)aNotification
-{
- // since we use [NSApp terminate:] we never return to main,
- // so do our cleanup here
- if (!gDidTrySend)
- DeleteDump();
-}
-
-@end
-
-@implementation TextViewWithPlaceHolder
-
-- (BOOL)becomeFirstResponder
-{
- [self setNeedsDisplay:YES];
- return [super becomeFirstResponder];
-}
-
-- (void)drawRect:(NSRect)rect
-{
- [super drawRect:rect];
- if (mPlaceHolderString && [[self string] isEqualToString:@""] &&
- self != [[self window] firstResponder])
- [mPlaceHolderString drawInRect:[self frame]];
-}
-
-- (BOOL)resignFirstResponder
-{
- [self setNeedsDisplay:YES];
- return [super resignFirstResponder];
-}
-
-- (void)setPlaceholder:(NSString*)placeholder
-{
- NSColor* txtColor = [NSColor disabledControlTextColor];
- NSDictionary* txtDict = [NSDictionary
- dictionaryWithObjectsAndKeys:txtColor,
- NSForegroundColorAttributeName, nil];
- mPlaceHolderString = [[NSMutableAttributedString alloc]
- initWithString:placeholder attributes:txtDict];
- if (gRTLlayout)
- [mPlaceHolderString setAlignment:NSRightTextAlignment
- range:NSMakeRange(0, [placeholder length])];
-
-}
-
-- (void)insertTab:(id)sender
-{
- // don't actually want to insert tabs, just tab to next control
- [[self window] selectNextKeyView:sender];
-}
-
-- (void)insertBacktab:(id)sender
-{
- [[self window] selectPreviousKeyView:sender];
-}
-
-- (void)setEnabled:(BOOL)enabled
-{
- [self setSelectable:enabled];
- [self setEditable:enabled];
- if (![[self string] isEqualToString:@""]) {
- NSAttributedString* colorString;
- NSColor* txtColor;
- if (enabled)
- txtColor = [NSColor textColor];
- else
- txtColor = [NSColor disabledControlTextColor];
- NSDictionary *txtDict = [NSDictionary
- dictionaryWithObjectsAndKeys:txtColor,
- NSForegroundColorAttributeName, nil];
- colorString = [[NSAttributedString alloc]
- initWithString:[self string]
- attributes:txtDict];
- [[self textStorage] setAttributedString: colorString];
- [self setInsertionPointColor:txtColor];
- [colorString release];
- }
-}
-
-- (void)dealloc
-{
- [mPlaceHolderString release];
- [super dealloc];
-}
-
-@end
-
-/* === Crashreporter UI Functions === */
-
-bool UIInit()
-{
- gMainPool = [[NSAutoreleasePool alloc] init];
- [NSApplication sharedApplication];
-
- if (gStrings.find("isRTL") != gStrings.end() &&
- gStrings["isRTL"] == "yes")
- gRTLlayout = true;
-
- [NSBundle loadNibNamed:(gRTLlayout ? @"MainMenuRTL" : @"MainMenu")
- owner:NSApp];
-
- return true;
-}
-
-void UIShutdown()
-{
- [gMainPool release];
-}
-
-void UIShowDefaultUI()
-{
- [gUI showErrorUI: gStrings[ST_CRASHREPORTERDEFAULT]];
- [NSApp run];
-}
-
-bool UIShowCrashUI(const StringTable& files,
- const StringTable& queryParameters,
- const string& sendURL,
- const vector<string>& restartArgs)
-{
- gRestartArgs = restartArgs;
-
- [gUI showCrashUI: files
- queryParameters: queryParameters
- sendURL: sendURL];
- [NSApp run];
-
- return gDidTrySend;
-}
-
-void UIError_impl(const string& message)
-{
- if (!gUI) {
- // UI failed to initialize, printing is the best we can do
- printf("Error: %s\n", message.c_str());
- return;
- }
-
- [gUI showErrorUI: message];
- [NSApp run];
-}
-
-bool UIGetIniPath(string& path)
-{
- NSString* tmpPath = [NSString stringWithUTF8String:gArgv[0]];
- NSString* iniName = [tmpPath lastPathComponent];
- iniName = [iniName stringByAppendingPathExtension:@"ini"];
- tmpPath = [tmpPath stringByDeletingLastPathComponent];
- tmpPath = [tmpPath stringByDeletingLastPathComponent];
- tmpPath = [tmpPath stringByAppendingPathComponent:@"Resources"];
- tmpPath = [tmpPath stringByAppendingPathComponent:iniName];
- path = [tmpPath UTF8String];
- return true;
-}
-
-bool UIGetSettingsPath(const string& vendor,
- const string& product,
- string& settingsPath)
-{
- FSRef foundRef;
- OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType,
- kCreateFolder, &foundRef);
- if (err != noErr)
- return false;
-
- unsigned char path[PATH_MAX];
- FSRefMakePath(&foundRef, path, sizeof(path));
- NSString* destPath = [NSString stringWithUTF8String:reinterpret_cast<char*>(path)];
-
- // Note that MacOS ignores the vendor when creating the profile hierarchy -
- // all application preferences directories live alongside one another in
- // ~/Library/Application Support/
- destPath = [destPath stringByAppendingPathComponent: NSSTR(product)];
- // Thunderbird stores its profile in ~/Library/Thunderbird,
- // but we're going to put stuff in ~/Library/Application Support/Thunderbird
- // anyway, so we have to ensure that path exists.
- string tempPath = [destPath UTF8String];
- if (!UIEnsurePathExists(tempPath))
- return false;
-
- destPath = [destPath stringByAppendingPathComponent: @"Crash Reports"];
-
- settingsPath = [destPath UTF8String];
-
- return true;
-}
-
-bool UIEnsurePathExists(const string& path)
-{
- int ret = mkdir(path.c_str(), S_IRWXU);
- int e = errno;
- if (ret == -1 && e != EEXIST)
- return false;
-
- return true;
-}
-
-bool UIFileExists(const string& path)
-{
- struct stat sb;
- int ret = stat(path.c_str(), &sb);
- if (ret == -1 || !(sb.st_mode & S_IFREG))
- return false;
-
- return true;
-}
-
-bool UIMoveFile(const string& file, const string& newfile)
-{
- if (!rename(file.c_str(), newfile.c_str()))
- return true;
- if (errno != EXDEV)
- return false;
-
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSString *source = [fileManager stringWithFileSystemRepresentation:file.c_str() length:file.length()];
- NSString *dest = [fileManager stringWithFileSystemRepresentation:newfile.c_str() length:newfile.length()];
- if (!source || !dest)
- return false;
-
- [fileManager moveItemAtPath:source toPath:dest error:NULL];
- return UIFileExists(newfile);
-}
-
-bool UIDeleteFile(const string& file)
-{
- return (unlink(file.c_str()) != -1);
-}
-
-std::ifstream* UIOpenRead(const string& filename)
-{
- return new std::ifstream(filename.c_str(), std::ios::in);
-}
-
-std::ofstream* UIOpenWrite(const string& filename,
- bool append, // append=false
- bool binary) // binary=false
-{
- std::ios_base::openmode mode = std::ios::out;
-
- if (append) {
- mode = mode | std::ios::app;
- }
-
- if (binary) {
- mode = mode | std::ios::binary;
- }
-
- return new std::ofstream(filename.c_str(), mode);
-}