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