// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "chrome/browser/ui/cocoa/external_protocol_dialog.h" #include "base/message_loop.h" #include "base/metrics/histogram.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/external_protocol_handler.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util_mac.h" #include "ui/base/text/text_elider.h" /////////////////////////////////////////////////////////////////////////////// // ExternalProtocolHandler // static void ExternalProtocolHandler::RunExternalProtocolDialog( const GURL& url, int render_process_host_id, int routing_id) { [[ExternalProtocolDialogController alloc] initWithGURL:&url]; } /////////////////////////////////////////////////////////////////////////////// // ExternalProtocolDialogController @interface ExternalProtocolDialogController(Private) - (void)alertEnded:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void*)contextInfo; - (string16)appNameForProtocol; @end @implementation ExternalProtocolDialogController - (id)initWithGURL:(const GURL*)url { DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); url_ = *url; creation_time_ = base::Time::Now(); string16 appName = [self appNameForProtocol]; if (appName.length() == 0) { // No registered apps for this protocol; give up and go home. [self autorelease]; return nil; } alert_ = [[NSAlert alloc] init]; [alert_ setMessageText: l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_TITLE)]; NSButton* allowButton = [alert_ addButtonWithTitle: l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_OK_BUTTON_TEXT)]; [allowButton setKeyEquivalent:@""]; // disallow as default [alert_ addButtonWithTitle: l10n_util::GetNSStringWithFixup( IDS_EXTERNAL_PROTOCOL_CANCEL_BUTTON_TEXT)]; const int kMaxUrlWithoutSchemeSize = 256; string16 elided_url_without_scheme; ui::ElideString(ASCIIToUTF16(url_.possibly_invalid_spec()), kMaxUrlWithoutSchemeSize, &elided_url_without_scheme); NSString* urlString = l10n_util::GetNSStringFWithFixup( IDS_EXTERNAL_PROTOCOL_INFORMATION, ASCIIToUTF16(url_.scheme() + ":"), elided_url_without_scheme); NSString* appString = l10n_util::GetNSStringFWithFixup( IDS_EXTERNAL_PROTOCOL_APPLICATION_TO_LAUNCH, appName); NSString* warningString = l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_WARNING); NSString* informativeText = [NSString stringWithFormat:@"%@\n\n%@\n\n%@", urlString, appString, warningString]; [alert_ setInformativeText:informativeText]; [alert_ setShowsSuppressionButton:YES]; [[alert_ suppressionButton] setTitle: l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_CHECKBOX_TEXT)]; [alert_ beginSheetModalForWindow:nil // nil here makes it app-modal modalDelegate:self didEndSelector:@selector(alertEnded:returnCode:contextInfo:) contextInfo:nil]; return self; } - (void)dealloc { [alert_ release]; [super dealloc]; } - (void)alertEnded:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void*)contextInfo { ExternalProtocolHandler::BlockState blockState = ExternalProtocolHandler::UNKNOWN; switch (returnCode) { case NSAlertFirstButtonReturn: blockState = ExternalProtocolHandler::DONT_BLOCK; break; case NSAlertSecondButtonReturn: blockState = ExternalProtocolHandler::BLOCK; break; default: NOTREACHED(); } // Set the "don't warn me again" info. if ([[alert_ suppressionButton] state] == NSOnState) ExternalProtocolHandler::SetBlockState(url_.scheme(), blockState); if (blockState == ExternalProtocolHandler::DONT_BLOCK) { UMA_HISTOGRAM_LONG_TIMES("clickjacking.launch_url", base::Time::Now() - creation_time_); ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_); } [self autorelease]; } - (string16)appNameForProtocol { NSURL* url = [NSURL URLWithString: base::SysUTF8ToNSString(url_.possibly_invalid_spec())]; CFURLRef openingApp = NULL; OSStatus status = LSGetApplicationForURL((CFURLRef)url, kLSRolesAll, NULL, &openingApp); if (status != noErr) { // likely kLSApplicationNotFoundErr return string16(); } NSString* appPath = [(NSURL*)openingApp path]; CFRelease(openingApp); // NOT A BUG; LSGetApplicationForURL retains for us NSString* appDisplayName = [[NSFileManager defaultManager] displayNameAtPath:appPath]; return base::SysNSStringToUTF16(appDisplayName); } @end