1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "chrome/browser/ui/cocoa/bug_report_window_controller.h" 6 7#include "base/mac/mac_util.h" 8#include "base/sys_string_conversions.h" 9#include "chrome/browser/bug_report_util.h" 10#include "chrome/browser/ui/window_snapshot/window_snapshot.h" 11#include "content/browser/tab_contents/tab_contents.h" 12#include "content/browser/tab_contents/tab_contents_view.h" 13#include "grit/chromium_strings.h" 14#include "grit/generated_resources.h" 15#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" 16#include "ui/base/l10n/l10n_util_mac.h" 17 18@implementation BugReportWindowController 19 20@synthesize bugDescription = bugDescription_; 21@synthesize bugTypeIndex = bugTypeIndex_; 22@synthesize pageURL = pageURL_; 23@synthesize pageTitle = pageTitle_; 24@synthesize sendScreenshot = sendScreenshot_; 25@synthesize disableScreenshotCheckbox = disableScreenshotCheckbox_; 26@synthesize bugTypeList = bugTypeList_; 27 28- (id)initWithTabContents:(TabContents*)currentTab 29 profile:(Profile*)profile { 30 NSString* nibpath = [base::mac::MainAppBundle() pathForResource:@"ReportBug" 31 ofType:@"nib"]; 32 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { 33 currentTab_ = currentTab; 34 profile_ = profile; 35 36 // The order of strings in this array must match the order of the bug types 37 // declared below in the bugTypeFromIndex function. 38 bugTypeList_ = [[NSMutableArray alloc] initWithObjects: 39 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_CHROME_MISBEHAVES), 40 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_SOMETHING_MISSING), 41 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_BROWSER_CRASH), 42 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_OTHER_PROBLEM), 43 nil]; 44 45 pngHeight_ = 0; 46 pngWidth_ = 0; 47 48 if (currentTab_ != NULL) { 49 // Get data from current tab, if one exists. This dialog could be called 50 // from the main menu with no tab contents, so currentTab_ is not 51 // guaranteed to be non-NULL. 52 // TODO(mirandac): This dialog should be a tab-modal sheet if a browser 53 // window exists. 54 [self setSendScreenshot:YES]; 55 [self setDisableScreenshotCheckbox:NO]; 56 // Insert menu items about bugs related to specific pages. 57 [bugTypeList_ insertObjects: 58 [NSArray arrayWithObjects: 59 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_PAGE_WONT_LOAD), 60 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_PAGE_LOOKS_ODD), 61 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_PHISHING_PAGE), 62 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_CANT_SIGN_IN), 63 nil] 64 atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 4)]]; 65 66 [self setPageURL:base::SysUTF8ToNSString( 67 currentTab_->controller().GetActiveEntry()->url().spec())]; 68 [self setPageTitle:base::SysUTF16ToNSString(currentTab_->GetTitle())]; 69 gfx::Rect pngRect = browser::GrabWindowSnapshot( 70 currentTab_->view()->GetTopLevelNativeWindow(), &pngData_); 71 pngWidth_ = pngRect.width(); 72 pngHeight_ = pngRect.height(); 73 } else { 74 // If no current tab exists, create a menu without the "broken page" 75 // options, with page URL and title empty, and screenshot disabled. 76 [self setSendScreenshot:NO]; 77 [self setDisableScreenshotCheckbox:YES]; 78 } 79 } 80 return self; 81} 82 83- (void)dealloc { 84 [pageURL_ release]; 85 [pageTitle_ release]; 86 [bugDescription_ release]; 87 [bugTypeList_ release]; 88 [bugTypeDictionary_ release]; 89 [super dealloc]; 90} 91 92// Delegate callback so that closing the window deletes the controller. 93- (void)windowWillClose:(NSNotification*)notification { 94 [self autorelease]; 95} 96 97- (void)closeDialog { 98 [NSApp stopModal]; 99 [[self window] close]; 100} 101 102- (void)runModalDialog { 103 NSWindow* bugReportWindow = [self window]; 104 [bugReportWindow center]; 105 [NSApp runModalForWindow:bugReportWindow]; 106} 107 108- (IBAction)sendReport:(id)sender { 109 if ([self isPhishingReport]) { 110 BugReportUtil::ReportPhishing(currentTab_, 111 pageURL_ ? base::SysNSStringToUTF8(pageURL_) : ""); 112 } else { 113 BugReportUtil::SendReport( 114 profile_, 115 [self bugTypeFromIndex], 116 base::SysNSStringToUTF8(pageURL_), 117 base::SysNSStringToUTF8(bugDescription_), 118 sendScreenshot_ && !pngData_.empty() ? 119 reinterpret_cast<const char *>(&(pngData_[0])) : NULL, 120 pngData_.size(), pngWidth_, pngHeight_); 121 } 122 [self closeDialog]; 123} 124 125- (IBAction)cancel:(id)sender { 126 [self closeDialog]; 127} 128 129- (BOOL)isPhishingReport { 130 return [self bugTypeFromIndex] == BugReportUtil::PHISHING_PAGE; 131} 132 133- (int)bugTypeFromIndex { 134 // The order of these bugs must match the ordering in the bugTypeList_, 135 // and thereby the menu in the popup button in the dialog box. 136 const BugReportUtil::BugType typesForMenuIndices[] = { 137 BugReportUtil::PAGE_WONT_LOAD, 138 BugReportUtil::PAGE_LOOKS_ODD, 139 BugReportUtil::PHISHING_PAGE, 140 BugReportUtil::CANT_SIGN_IN, 141 BugReportUtil::CHROME_MISBEHAVES, 142 BugReportUtil::SOMETHING_MISSING, 143 BugReportUtil::BROWSER_CRASH, 144 BugReportUtil::OTHER_PROBLEM 145 }; 146 // The bugs for the shorter menu start at index 4. 147 NSUInteger adjustedBugTypeIndex_ = [bugTypeList_ count] == 8 ? bugTypeIndex_ : 148 bugTypeIndex_ + 4; 149 DCHECK_LT(adjustedBugTypeIndex_, arraysize(typesForMenuIndices)); 150 return typesForMenuIndices[adjustedBugTypeIndex_]; 151} 152 153// Custom setter to update the UI for different bug types. 154- (void)setBugTypeIndex:(NSUInteger)bugTypeIndex { 155 bugTypeIndex_ = bugTypeIndex; 156 157 // The "send" button's title is based on the type of report. 158 NSString* buttonTitle = [self isPhishingReport] ? 159 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_SEND_PHISHING_REPORT) : 160 l10n_util::GetNSStringWithFixup(IDS_BUGREPORT_SEND_REPORT); 161 if (![buttonTitle isEqualTo:[sendReportButton_ title]]) { 162 NSRect sendFrame1 = [sendReportButton_ frame]; 163 NSRect cancelFrame1 = [cancelButton_ frame]; 164 165 [sendReportButton_ setTitle:buttonTitle]; 166 CGFloat deltaWidth = 167 [GTMUILocalizerAndLayoutTweaker sizeToFitView:sendReportButton_].width; 168 169 NSRect sendFrame2 = [sendReportButton_ frame]; 170 sendFrame2.origin.x -= deltaWidth; 171 NSRect cancelFrame2 = cancelFrame1; 172 cancelFrame2.origin.x -= deltaWidth; 173 174 // Since the buttons get updated/resize, use a quick animation so it is 175 // a little less jarring in the UI. 176 NSDictionary* sendReportButtonResize = 177 [NSDictionary dictionaryWithObjectsAndKeys: 178 sendReportButton_, NSViewAnimationTargetKey, 179 [NSValue valueWithRect:sendFrame1], NSViewAnimationStartFrameKey, 180 [NSValue valueWithRect:sendFrame2], NSViewAnimationEndFrameKey, 181 nil]; 182 NSDictionary* cancelButtonResize = 183 [NSDictionary dictionaryWithObjectsAndKeys: 184 cancelButton_, NSViewAnimationTargetKey, 185 [NSValue valueWithRect:cancelFrame1], NSViewAnimationStartFrameKey, 186 [NSValue valueWithRect:cancelFrame2], NSViewAnimationEndFrameKey, 187 nil]; 188 NSAnimation* animation = 189 [[[NSViewAnimation alloc] initWithViewAnimations: 190 [NSArray arrayWithObjects:sendReportButtonResize, cancelButtonResize, 191 nil]] autorelease]; 192 const NSTimeInterval kQuickTransitionInterval = 0.1; 193 [animation setDuration:kQuickTransitionInterval]; 194 [animation startAnimation]; 195 196 // Save or reload description when moving between phishing page and other 197 // bug report types. 198 if ([self isPhishingReport]) { 199 saveBugDescription_.reset([[self bugDescription] retain]); 200 [self setBugDescription:nil]; 201 saveSendScreenshot_ = sendScreenshot_; 202 [self setSendScreenshot:NO]; 203 } else { 204 [self setBugDescription:saveBugDescription_.get()]; 205 saveBugDescription_.reset(); 206 [self setSendScreenshot:saveSendScreenshot_]; 207 } 208 } 209} 210 211- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView 212 doCommandBySelector:(SEL)commandSelector { 213 if (commandSelector == @selector(insertNewline:)) { 214 [textView insertNewlineIgnoringFieldEditor:self]; 215 return YES; 216 } 217 return NO; 218} 219 220// BugReportWindowController needs to change the title of the Send Report 221// button when the user chooses the phishing bug type, so we need to bind 222// the function that changes the button title to the bug type key. 223+ (NSSet*)keyPathsForValuesAffectingValueForKey:(NSString*)key { 224 NSSet* paths = [super keyPathsForValuesAffectingValueForKey:key]; 225 if ([key isEqualToString:@"isPhishingReport"]) { 226 paths = [paths setByAddingObject:@"bugTypeIndex"]; 227 } 228 return paths; 229} 230 231@end 232 233