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