• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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