Home | History | Annotate | Download | only in cocoa
      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