| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/external_protocol_handler.h" | 5 #include "chrome/browser/external_protocol_handler.h" |
| 6 | 6 |
| 7 #include "build/build_config.h" | 7 #include "build/build_config.h" |
| 8 | 8 |
| 9 #if defined(OS_WIN) | |
| 10 #include <windows.h> | |
| 11 #include <shellapi.h> | |
| 12 #endif | |
| 13 | |
| 14 #include <set> | 9 #include <set> |
| 15 | 10 |
| 16 #include "base/logging.h" | 11 #include "base/logging.h" |
| 17 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
| 18 #include "base/string_util.h" | 13 #include "base/string_util.h" |
| 19 #include "base/thread.h" | 14 #include "base/thread.h" |
| 20 #include "chrome/browser/browser.h" | 15 #include "chrome/browser/browser.h" |
| 21 #include "chrome/browser/browser_process_impl.h" | 16 #include "chrome/browser/browser_process_impl.h" |
| 17 #include "chrome/common/platform_util.h" |
| 22 #include "chrome/common/pref_service.h" | 18 #include "chrome/common/pref_service.h" |
| 23 #include "chrome/common/pref_names.h" | 19 #include "chrome/common/pref_names.h" |
| 24 #include "googleurl/src/gurl.h" | 20 #include "googleurl/src/gurl.h" |
| 25 #include "net/base/escape.h" | 21 #include "net/base/escape.h" |
| 26 | 22 |
| 27 #if defined(OS_WIN) | 23 #if defined(OS_WIN) |
| 28 #include "base/registry.h" | |
| 29 #include "chrome/browser/views/external_protocol_dialog.h" | 24 #include "chrome/browser/views/external_protocol_dialog.h" |
| 30 #elif defined(OS_MACOSX) | |
| 31 #include <ApplicationServices/ApplicationServices.h> | |
| 32 #include "base/scoped_cftyperef.h" | |
| 33 #include "base/sys_string_conversions.h" | |
| 34 #endif | 25 #endif |
| 35 | 26 |
| 36 // static | 27 // static |
| 37 void ExternalProtocolHandler::PrepopulateDictionary(DictionaryValue* win_pref) { | 28 void ExternalProtocolHandler::PrepopulateDictionary(DictionaryValue* win_pref) { |
| 38 static bool is_warm = false; | 29 static bool is_warm = false; |
| 39 if (is_warm) | 30 if (is_warm) |
| 40 return; | 31 return; |
| 41 is_warm = true; | 32 is_warm = true; |
| 42 | 33 |
| 43 static const wchar_t* const denied_schemes[] = { | 34 static const wchar_t* const denied_schemes[] = { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 return should_block ? BLOCK : DONT_BLOCK; | 101 return should_block ? BLOCK : DONT_BLOCK; |
| 111 } | 102 } |
| 112 | 103 |
| 113 return UNKNOWN; | 104 return UNKNOWN; |
| 114 } | 105 } |
| 115 | 106 |
| 116 // static | 107 // static |
| 117 void ExternalProtocolHandler::LaunchUrl(const GURL& url, | 108 void ExternalProtocolHandler::LaunchUrl(const GURL& url, |
| 118 int render_process_host_id, | 109 int render_process_host_id, |
| 119 int tab_contents_id) { | 110 int tab_contents_id) { |
| 120 #if !defined(OS_LINUX) | |
| 121 // Escape the input scheme to be sure that the command does not | 111 // Escape the input scheme to be sure that the command does not |
| 122 // have parameters unexpected by the external program. | 112 // have parameters unexpected by the external program. |
| 123 std::string escaped_url_string = EscapeExternalHandlerValue(url.spec()); | 113 std::string escaped_url_string = EscapeExternalHandlerValue(url.spec()); |
| 124 GURL escaped_url(escaped_url_string); | 114 GURL escaped_url(escaped_url_string); |
| 125 BlockState block_state = GetBlockState(ASCIIToWide(escaped_url.scheme())); | 115 BlockState block_state = GetBlockState(ASCIIToWide(escaped_url.scheme())); |
| 126 if (block_state == BLOCK) | 116 if (block_state == BLOCK) |
| 127 return; | 117 return; |
| 128 | 118 |
| 129 #if defined(OS_WIN) | 119 #if defined(OS_WIN) |
| 130 if (block_state == UNKNOWN) { | 120 if (block_state == UNKNOWN) { |
| 131 std::wstring command = ExternalProtocolDialog::GetApplicationForProtocol( | 121 std::wstring command = ExternalProtocolDialog::GetApplicationForProtocol( |
| 132 escaped_url); | 122 escaped_url); |
| 133 if (command.empty()) { | 123 if (command.empty()) { |
| 134 // ShellExecute won't do anything. Don't bother warning the user. | 124 // ShellExecute won't do anything. Don't bother warning the user. |
| 135 return; | 125 return; |
| 136 } | 126 } |
| 137 | 127 |
| 138 // Ask the user if they want to allow the protocol. This will call | 128 // Ask the user if they want to allow the protocol. This will call |
| 139 // LaunchUrlWithoutSecurityCheck if the user decides to accept the protocol. | 129 // LaunchUrlWithoutSecurityCheck if the user decides to accept the protocol. |
| 140 ExternalProtocolDialog::RunExternalProtocolDialog(escaped_url, | 130 ExternalProtocolDialog::RunExternalProtocolDialog(escaped_url, |
| 141 command, | 131 command, |
| 142 render_process_host_id, | 132 render_process_host_id, |
| 143 tab_contents_id); | 133 tab_contents_id); |
| 144 return; | 134 return; |
| 145 } | 135 } |
| 146 #else | 136 #else |
| 147 // For now, allow only whitelisted protocols to fire. | 137 // For now, allow only whitelisted protocols to fire. |
| 148 // TODO(port): implement dialog for Mac | 138 // TODO(port): implement dialog for Mac/Linux. |
| 139 // See http://code.google.com/p/chromium/issues/detail?id=20731. |
| 149 if (block_state == UNKNOWN) | 140 if (block_state == UNKNOWN) |
| 150 return; | 141 return; |
| 151 #endif | 142 #endif |
| 152 | 143 |
| 153 // Put this work on the file thread since ShellExecute may block for a | 144 // Otherwise the protocol is white-listed, so go ahead and launch. |
| 154 // significant amount of time. | 145 #if defined(OS_MACOSX) |
| 146 // This must run on the main thread on OS X. |
| 147 LaunchUrlWithoutSecurityCheck(escaped_url); |
| 148 #else |
| 149 // Otherwise put this work on the file thread. On Windows ShellExecute may |
| 150 // block for a significant amount of time, and it shouldn't hurt on Linux. |
| 155 MessageLoop* loop = g_browser_process->file_thread()->message_loop(); | 151 MessageLoop* loop = g_browser_process->file_thread()->message_loop(); |
| 156 if (loop == NULL) { | 152 if (loop == NULL) { |
| 157 return; | 153 return; |
| 158 } | 154 } |
| 159 | 155 |
| 160 // Otherwise the protocol is white-listed, so go ahead and launch. | |
| 161 loop->PostTask(FROM_HERE, | 156 loop->PostTask(FROM_HERE, |
| 162 NewRunnableFunction( | 157 NewRunnableFunction( |
| 163 &ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck, | 158 &ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck, |
| 164 escaped_url)); | 159 escaped_url)); |
| 165 #else | |
| 166 // TODO(port): Implement launching external handler. | |
| 167 NOTIMPLEMENTED(); | |
| 168 #endif | 160 #endif |
| 169 } | 161 } |
| 170 | 162 |
| 171 // static | 163 // static |
| 172 void ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(const GURL& url) { | 164 void ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(const GURL& url) { |
| 173 #if defined(OS_WIN) | 165 platform_util::OpenExternal(url); |
| 174 // Quote the input scheme to be sure that the command does not have | |
| 175 // parameters unexpected by the external program. This url should already | |
| 176 // have been escaped. | |
| 177 std::string escaped_url = url.spec(); | |
| 178 escaped_url.insert(0, "\""); | |
| 179 escaped_url += "\""; | |
| 180 | |
| 181 // According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp: | |
| 182 // "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in | |
| 183 // ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6 | |
| 184 // support URLS of 2083 chars in length, 2K is safe." | |
| 185 const size_t kMaxUrlLength = 2048; | |
| 186 if (escaped_url.length() > kMaxUrlLength) { | |
| 187 NOTREACHED(); | |
| 188 return; | |
| 189 } | |
| 190 | |
| 191 RegKey key; | |
| 192 std::wstring registry_path = ASCIIToWide(url.scheme()) + | |
| 193 L"\\shell\\open\\command"; | |
| 194 key.Open(HKEY_CLASSES_ROOT, registry_path.c_str()); | |
| 195 if (key.Valid()) { | |
| 196 DWORD size = 0; | |
| 197 key.ReadValue(NULL, NULL, &size); | |
| 198 if (size <= 2) { | |
| 199 // ShellExecute crashes the process when the command is empty. | |
| 200 // We check for "2" because it always returns the trailing NULL. | |
| 201 // TODO(nsylvain): we should also add a dialog to warn on errors. See | |
| 202 // bug 1136923. | |
| 203 return; | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 if (reinterpret_cast<ULONG_PTR>(ShellExecuteA(NULL, "open", | |
| 208 escaped_url.c_str(), NULL, NULL, | |
| 209 SW_SHOWNORMAL)) <= 32) { | |
| 210 // We fail to execute the call. We could display a message to the user. | |
| 211 // TODO(nsylvain): we should also add a dialog to warn on errors. See | |
| 212 // bug 1136923. | |
| 213 return; | |
| 214 } | |
| 215 #elif defined(OS_MACOSX) | |
| 216 scoped_cftyperef<CFStringRef> string_ref( | |
| 217 base::SysUTF8ToCFStringRef(url.spec())); | |
| 218 if (!string_ref) | |
| 219 return; | |
| 220 | |
| 221 scoped_cftyperef<CFURLRef> url_ref(CFURLCreateWithString(kCFAllocatorDefault, | |
| 222 string_ref, | |
| 223 NULL)); | |
| 224 if (!url_ref) | |
| 225 return; | |
| 226 | |
| 227 LSOpenCFURLRef(url_ref, NULL); | |
| 228 #elif defined(OS_LINUX) | |
| 229 // TODO(port): Implement launching external handler. | |
| 230 NOTIMPLEMENTED(); | |
| 231 #endif | |
| 232 } | 166 } |
| 233 | 167 |
| 234 // static | 168 // static |
| 235 void ExternalProtocolHandler::RegisterPrefs(PrefService* prefs) { | 169 void ExternalProtocolHandler::RegisterPrefs(PrefService* prefs) { |
| 236 prefs->RegisterDictionaryPref(prefs::kExcludedSchemes); | 170 prefs->RegisterDictionaryPref(prefs::kExcludedSchemes); |
| 237 } | 171 } |
| OLD | NEW |