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.
5 #import "chrome/browser/ui/cocoa/external_protocol_dialog.h"
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/gfx/text_elider.h"
18 ///////////////////////////////////////////////////////////////////////////////
19 // ExternalProtocolHandler
22 void ExternalProtocolHandler::RunExternalProtocolDialog(
23 const GURL& url, int render_process_host_id, int routing_id) {
24 [[ExternalProtocolDialogController alloc] initWithGURL:&url];
27 ///////////////////////////////////////////////////////////////////////////////
28 // ExternalProtocolDialogController
30 @interface ExternalProtocolDialogController(Private)
31 - (void)alertEnded:(NSAlert *)alert
32 returnCode:(int)returnCode
33 contextInfo:(void*)contextInfo;
34 - (string16)appNameForProtocol;
37 @implementation ExternalProtocolDialogController
38 - (id)initWithGURL:(const GURL*)url {
39 DCHECK_EQ(base::MessageLoop::TYPE_UI, base::MessageLoop::current()->type());
41 if (!(self = [super init]))
45 creation_time_ = base::Time::Now();
47 string16 appName = [self appNameForProtocol];
48 if (appName.length() == 0) {
49 // No registered apps for this protocol; give up and go home.
54 alert_ = [[NSAlert alloc] init];
56 [alert_ setMessageText:
57 l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_TITLE)];
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)];
66 const int kMaxUrlWithoutSchemeSize = 256;
67 string16 elided_url_without_scheme;
68 gfx::ElideString(ASCIIToUTF16(url_.possibly_invalid_spec()),
69 kMaxUrlWithoutSchemeSize, &elided_url_without_scheme);
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,
78 NSString* warningString =
79 l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_WARNING);
80 NSString* informativeText =
81 [NSString stringWithFormat:@"%@\n\n%@\n\n%@",
86 [alert_ setInformativeText:informativeText];
88 [alert_ setShowsSuppressionButton:YES];
89 [[alert_ suppressionButton] setTitle:
90 l10n_util::GetNSStringWithFixup(IDS_EXTERNAL_PROTOCOL_CHECKBOX_TEXT)];
92 [alert_ beginSheetModalForWindow:nil // nil here makes it app-modal
94 didEndSelector:@selector(alertEnded:returnCode:contextInfo:)
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;
115 case NSAlertSecondButtonReturn:
116 blockState = ExternalProtocolHandler::BLOCK;
122 // Set the "don't warn me again" info.
123 if ([[alert_ suppressionButton] state] == NSOnState)
124 ExternalProtocolHandler::SetBlockState(url_.scheme(), blockState);
126 if (blockState == ExternalProtocolHandler::DONT_BLOCK) {
127 UMA_HISTOGRAM_LONG_TIMES("clickjacking.launch_url",
128 base::Time::Now() - creation_time_);
130 ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_);
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,
144 if (status != noErr) {
145 // likely kLSApplicationNotFoundErr
148 NSString* appPath = [(NSURL*)openingApp path];
149 CFRelease(openingApp); // NOT A BUG; LSGetApplicationForURL retains for us
150 NSString* appDisplayName =
151 [[NSFileManager defaultManager] displayNameAtPath:appPath];
153 return base::SysNSStringToUTF16(appDisplayName);