1// Copyright (c) 2011 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#include "chrome/browser/ui/views/external_protocol_dialog.h" 6 7#include "base/metrics/histogram.h" 8#include "base/string_util.h" 9#include "base/threading/thread.h" 10#include "base/threading/thread_restrictions.h" 11#include "base/utf_string_conversions.h" 12#include "base/win/registry.h" 13#include "chrome/browser/external_protocol_handler.h" 14#include "chrome/browser/tab_contents/tab_util.h" 15#include "content/browser/tab_contents/tab_contents.h" 16#include "grit/chromium_strings.h" 17#include "grit/generated_resources.h" 18#include "ui/base/l10n/l10n_util.h" 19#include "ui/base/message_box_flags.h" 20#include "ui/base/text/text_elider.h" 21#include "views/controls/message_box_view.h" 22#include "views/window/window.h" 23 24namespace { 25 26const int kMessageWidth = 400; 27 28} // namespace 29 30/////////////////////////////////////////////////////////////////////////////// 31// ExternalProtocolHandler 32 33// static 34void ExternalProtocolHandler::RunExternalProtocolDialog( 35 const GURL& url, int render_process_host_id, int routing_id) { 36 std::wstring command = 37 ExternalProtocolDialog::GetApplicationForProtocol(url); 38 if (command.empty()) { 39 // ShellExecute won't do anything. Don't bother warning the user. 40 return; 41 } 42 TabContents* tab_contents = tab_util::GetTabContentsByID( 43 render_process_host_id, routing_id); 44 DCHECK(tab_contents); 45 ExternalProtocolDialog* handler = 46 new ExternalProtocolDialog(tab_contents, url, command); 47} 48 49/////////////////////////////////////////////////////////////////////////////// 50// ExternalProtocolDialog 51 52ExternalProtocolDialog::~ExternalProtocolDialog() { 53} 54 55////////////////////////////////////////////////////////////////////////////// 56// ExternalProtocolDialog, views::DialogDelegate implementation: 57 58int ExternalProtocolDialog::GetDefaultDialogButton() const { 59 return ui::MessageBoxFlags::DIALOGBUTTON_CANCEL; 60} 61 62std::wstring ExternalProtocolDialog::GetDialogButtonLabel( 63 ui::MessageBoxFlags::DialogButton button) const { 64 if (button == ui::MessageBoxFlags::DIALOGBUTTON_OK) 65 return UTF16ToWide( 66 l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_OK_BUTTON_TEXT)); 67 else 68 return UTF16ToWide( 69 l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_CANCEL_BUTTON_TEXT)); 70} 71 72std::wstring ExternalProtocolDialog::GetWindowTitle() const { 73 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_TITLE)); 74} 75 76void ExternalProtocolDialog::DeleteDelegate() { 77 delete this; 78} 79 80bool ExternalProtocolDialog::Cancel() { 81 // We also get called back here if the user closes the dialog or presses 82 // escape. In these cases it would be preferable to ignore the state of the 83 // check box but MessageBox doesn't distinguish this from pressing the cancel 84 // button. 85 if (message_box_view_->IsCheckBoxSelected()) { 86 ExternalProtocolHandler::SetBlockState( 87 url_.scheme(), ExternalProtocolHandler::BLOCK); 88 } 89 90 // Returning true closes the dialog. 91 return true; 92} 93 94bool ExternalProtocolDialog::Accept() { 95 // We record how long it takes the user to accept an external protocol. If 96 // users start accepting these dialogs too quickly, we should worry about 97 // clickjacking. 98 UMA_HISTOGRAM_LONG_TIMES("clickjacking.launch_url", 99 base::TimeTicks::Now() - creation_time_); 100 101 if (message_box_view_->IsCheckBoxSelected()) { 102 ExternalProtocolHandler::SetBlockState( 103 url_.scheme(), ExternalProtocolHandler::DONT_BLOCK); 104 } 105 106 ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_); 107 // Returning true closes the dialog. 108 return true; 109} 110 111views::View* ExternalProtocolDialog::GetContentsView() { 112 return message_box_view_; 113} 114 115/////////////////////////////////////////////////////////////////////////////// 116// ExternalProtocolDialog, private: 117 118ExternalProtocolDialog::ExternalProtocolDialog(TabContents* tab_contents, 119 const GURL& url, 120 const std::wstring& command) 121 : tab_contents_(tab_contents), 122 url_(url), 123 creation_time_(base::TimeTicks::Now()) { 124 const int kMaxUrlWithoutSchemeSize = 256; 125 const int kMaxCommandSize = 256; 126 string16 elided_url_without_scheme; 127 string16 elided_command; 128 ui::ElideString(ASCIIToUTF16(url.possibly_invalid_spec()), 129 kMaxUrlWithoutSchemeSize, &elided_url_without_scheme); 130 ui::ElideString(WideToUTF16Hack(command), kMaxCommandSize, &elided_command); 131 132 std::wstring message_text = UTF16ToWide(l10n_util::GetStringFUTF16( 133 IDS_EXTERNAL_PROTOCOL_INFORMATION, 134 ASCIIToUTF16(url.scheme() + ":"), 135 elided_url_without_scheme) + ASCIIToUTF16("\n\n")); 136 137 message_text += UTF16ToWide(l10n_util::GetStringFUTF16( 138 IDS_EXTERNAL_PROTOCOL_APPLICATION_TO_LAUNCH, 139 elided_command) + ASCIIToUTF16("\n\n")); 140 141 message_text += 142 UTF16ToWide(l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_WARNING)); 143 144 message_box_view_ = new views::MessageBoxView( 145 ui::MessageBoxFlags::kIsConfirmMessageBox, 146 message_text, 147 std::wstring(), 148 kMessageWidth); 149 message_box_view_->SetCheckBoxLabel(UTF16ToWide( 150 l10n_util::GetStringUTF16(IDS_EXTERNAL_PROTOCOL_CHECKBOX_TEXT))); 151 152 HWND root_hwnd; 153 if (tab_contents_) { 154 root_hwnd = GetAncestor(tab_contents_->GetContentNativeView(), GA_ROOT); 155 } else { 156 // Dialog is top level if we don't have a tab_contents associated with us. 157 root_hwnd = NULL; 158 } 159 160 views::Window::CreateChromeWindow(root_hwnd, gfx::Rect(), this)->Show(); 161} 162 163// static 164std::wstring ExternalProtocolDialog::GetApplicationForProtocol( 165 const GURL& url) { 166 // We shouldn't be accessing the registry from the UI thread, since it can go 167 // to disk. http://crbug.com/61996 168 base::ThreadRestrictions::ScopedAllowIO allow_io; 169 170 std::wstring url_spec = ASCIIToWide(url.possibly_invalid_spec()); 171 std::wstring cmd_key_path = 172 ASCIIToWide(url.scheme() + "\\shell\\open\\command"); 173 base::win::RegKey cmd_key(HKEY_CLASSES_ROOT, cmd_key_path.c_str(), KEY_READ); 174 size_t split_offset = url_spec.find(L':'); 175 if (split_offset == std::wstring::npos) 176 return std::wstring(); 177 std::wstring parameters = url_spec.substr(split_offset + 1, 178 url_spec.length() - 1); 179 std::wstring application_to_launch; 180 if (cmd_key.ReadValue(NULL, &application_to_launch) == ERROR_SUCCESS) { 181 ReplaceSubstringsAfterOffset(&application_to_launch, 0, L"%1", parameters); 182 return application_to_launch; 183 } else { 184 return std::wstring(); 185 } 186} 187