Home | History | Annotate | Download | only in cocoa
      1 // Copyright (c) 2010 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/external_protocol_dialog.h"
      6 
      7 #include "base/message_loop.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/string_util.h"
     10 #include "base/sys_string_conversions.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/external_protocol_handler.h"
     13 #include "grit/chromium_strings.h"
     14 #include "grit/generated_resources.h"
     15 #include "ui/base/l10n/l10n_util_mac.h"
     16 #include "ui/base/text/text_elider.h"
     17 
     18 ///////////////////////////////////////////////////////////////////////////////
     19 // ExternalProtocolHandler
     20 
     21 // static
     22 void ExternalProtocolHandler::RunExternalProtocolDialog(
     23     const GURL& url, int render_process_host_id, int routing_id) {
     24   [[ExternalProtocolDialogController alloc] initWithGURL:&url];
     25 }
     26 
     27 ///////////////////////////////////////////////////////////////////////////////
     28 // ExternalProtocolDialogController
     29 
     30 @interface ExternalProtocolDialogController(Private)
     31 - (void)alertEnded:(NSAlert *)alert
     32         returnCode:(int)returnCode
     33        contextInfo:(void*)contextInfo;
     34 - (string16)appNameForProtocol;
     35 @end
     36 
     37 @implementation ExternalProtocolDialogController
     38 - (id)initWithGURL:(const GURL*)url {
     39   DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
     40 
     41   url_ = *url;
     42   creation_time_ = base::Time::Now();
     43 
     44   string16 appName = [self appNameForProtocol];
     45   if (appName.length() == 0) {
     46     // No registered apps for this protocol; give up and go home.
     47     [self autorelease];
     48     return nil;
     49   }
     50 
     51   alert_ = [[NSAlert alloc] init];
     52 
     53   [alert_ setMessageText:
     54       l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_TITLE)];
     55 
     56   NSButton* allowButton = [alert_ addButtonWithTitle:
     57       l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_OK_BUTTON_TEXT)];
     58   [allowButton setKeyEquivalent:@""];  // disallow as default
     59   [alert_ addButtonWithTitle:
     60       l10n_util::GetNSStringWithFixup(
     61         IDS_EXTERNAL_PROTOCOL_CANCEL_BUTTON_TEXT)];
     62 
     63   const int kMaxUrlWithoutSchemeSize = 256;
     64   string16 elided_url_without_scheme;
     65   ui::ElideString(ASCIIToUTF16(url_.possibly_invalid_spec()),
     66                   kMaxUrlWithoutSchemeSize, &elided_url_without_scheme);
     67 
     68   NSString* urlString = l10n_util::GetNSStringFWithFixup(
     69       IDS_EXTERNAL_PROTOCOL_INFORMATION,
     70       ASCIIToUTF16(url_.scheme() + ":"),
     71       elided_url_without_scheme);
     72   NSString* appString = l10n_util::GetNSStringFWithFixup(
     73       IDS_EXTERNAL_PROTOCOL_APPLICATION_TO_LAUNCH,
     74       appName);
     75   NSString* warningString =
     76       l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_WARNING);
     77   NSString* informativeText =
     78       [NSString stringWithFormat:@"%@\n\n%@\n\n%@",
     79                                  urlString,
     80                                  appString,
     81                                  warningString];
     82 
     83   [alert_ setInformativeText:informativeText];
     84 
     85   [alert_ setShowsSuppressionButton:YES];
     86   [[alert_ suppressionButton] setTitle:
     87       l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_CHECKBOX_TEXT)];
     88 
     89   [alert_ beginSheetModalForWindow:nil  // nil here makes it app-modal
     90                      modalDelegate:self
     91                     didEndSelector:@selector(alertEnded:returnCode:contextInfo:)
     92                        contextInfo:nil];
     93 
     94   return self;
     95 }
     96 
     97 - (void)dealloc {
     98   [alert_ release];
     99 
    100   [super dealloc];
    101 }
    102 
    103 - (void)alertEnded:(NSAlert *)alert
    104         returnCode:(int)returnCode
    105        contextInfo:(void*)contextInfo {
    106   ExternalProtocolHandler::BlockState blockState =
    107       ExternalProtocolHandler::UNKNOWN;
    108   switch (returnCode) {
    109     case NSAlertFirstButtonReturn:
    110       blockState = ExternalProtocolHandler::DONT_BLOCK;
    111       break;
    112     case NSAlertSecondButtonReturn:
    113       blockState = ExternalProtocolHandler::BLOCK;
    114       break;
    115     default:
    116       NOTREACHED();
    117   }
    118 
    119   // Set the "don't warn me again" info.
    120   if ([[alert_ suppressionButton] state] == NSOnState)
    121     ExternalProtocolHandler::SetBlockState(url_.scheme(), blockState);
    122 
    123   if (blockState == ExternalProtocolHandler::DONT_BLOCK) {
    124     UMA_HISTOGRAM_LONG_TIMES("clickjacking.launch_url",
    125                              base::Time::Now() - creation_time_);
    126 
    127     ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_);
    128   }
    129 
    130   [self autorelease];
    131 }
    132 
    133 - (string16)appNameForProtocol {
    134   NSURL* url = [NSURL URLWithString:
    135       base::SysUTF8ToNSString(url_.possibly_invalid_spec())];
    136   CFURLRef openingApp = NULL;
    137   OSStatus status = LSGetApplicationForURL((CFURLRef)url,
    138                                            kLSRolesAll,
    139                                            NULL,
    140                                            &openingApp);
    141   if (status != noErr) {
    142     // likely kLSApplicationNotFoundErr
    143     return string16();
    144   }
    145   NSString* appPath = [(NSURL*)openingApp path];
    146   CFRelease(openingApp);  // NOT A BUG; LSGetApplicationForURL retains for us
    147   NSString* appDisplayName =
    148       [[NSFileManager defaultManager] displayNameAtPath:appPath];
    149 
    150   return base::SysNSStringToUTF16(appDisplayName);
    151 }
    152 
    153 @end
    154