Clean up Chrome OS Networking error logging
[chromium-blink-merge.git] / chrome / browser / external_protocol / external_protocol_handler.cc
blob78334d9e362f9eb323201965f96a44430301fb9e
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 #include "chrome/browser/external_protocol/external_protocol_handler.h"
7 #include <set>
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/prefs/pref_registry_simple.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/prefs/scoped_user_pref_update.h"
15 #include "base/strings/string_util.h"
16 #include "base/threading/thread.h"
17 #include "build/build_config.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/platform_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/tab_contents/tab_util.h"
22 #include "chrome/common/pref_names.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/web_contents.h"
25 #include "net/base/escape.h"
26 #include "url/gurl.h"
28 using content::BrowserThread;
30 namespace {
32 // Functions enabling unit testing. Using a NULL delegate will use the default
33 // behavior; if a delegate is provided it will be used instead.
34 ShellIntegration::DefaultProtocolClientWorker* CreateShellWorker(
35 ShellIntegration::DefaultWebClientObserver* observer,
36 const std::string& protocol,
37 ExternalProtocolHandler::Delegate* delegate) {
38 if (!delegate)
39 return new ShellIntegration::DefaultProtocolClientWorker(observer,
40 protocol);
42 return delegate->CreateShellWorker(observer, protocol);
45 ExternalProtocolHandler::BlockState GetBlockStateWithDelegate(
46 const std::string& scheme,
47 ExternalProtocolHandler::Delegate* delegate,
48 bool initiated_by_user_gesture) {
49 if (!delegate)
50 return ExternalProtocolHandler::GetBlockState(scheme,
51 initiated_by_user_gesture);
53 return delegate->GetBlockState(scheme, initiated_by_user_gesture);
56 void RunExternalProtocolDialogWithDelegate(
57 const GURL& url,
58 int render_process_host_id,
59 int routing_id,
60 ExternalProtocolHandler::Delegate* delegate) {
61 if (!delegate) {
62 ExternalProtocolHandler::RunExternalProtocolDialog(url,
63 render_process_host_id,
64 routing_id);
65 } else {
66 delegate->RunExternalProtocolDialog(url, render_process_host_id,
67 routing_id);
71 void LaunchUrlWithoutSecurityCheckWithDelegate(
72 const GURL& url,
73 int render_process_host_id,
74 int tab_contents_id,
75 ExternalProtocolHandler::Delegate* delegate) {
76 if (!delegate) {
77 ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(
78 url, render_process_host_id, tab_contents_id);
79 } else {
80 delegate->LaunchUrlWithoutSecurityCheck(url);
84 // When we are about to launch a URL with the default OS level application,
85 // we check if that external application will be us. If it is we just ignore
86 // the request.
87 class ExternalDefaultProtocolObserver
88 : public ShellIntegration::DefaultWebClientObserver {
89 public:
90 ExternalDefaultProtocolObserver(const GURL& escaped_url,
91 int render_process_host_id,
92 int tab_contents_id,
93 bool prompt_user,
94 ExternalProtocolHandler::Delegate* delegate)
95 : delegate_(delegate),
96 escaped_url_(escaped_url),
97 render_process_host_id_(render_process_host_id),
98 tab_contents_id_(tab_contents_id),
99 prompt_user_(prompt_user) {}
101 virtual void SetDefaultWebClientUIState(
102 ShellIntegration::DefaultWebClientUIState state) OVERRIDE {
103 DCHECK(base::MessageLoopForUI::IsCurrent());
105 // If we are still working out if we're the default, or we've found
106 // out we definately are the default, we end here.
107 if (state == ShellIntegration::STATE_PROCESSING) {
108 return;
111 if (delegate_)
112 delegate_->FinishedProcessingCheck();
114 if (state == ShellIntegration::STATE_IS_DEFAULT) {
115 if (delegate_)
116 delegate_->BlockRequest();
117 return;
120 // If we get here, either we are not the default or we cannot work out
121 // what the default is, so we proceed.
122 if (prompt_user_) {
123 // Ask the user if they want to allow the protocol. This will call
124 // LaunchUrlWithoutSecurityCheck if the user decides to accept the
125 // protocol.
126 RunExternalProtocolDialogWithDelegate(escaped_url_,
127 render_process_host_id_, tab_contents_id_, delegate_);
128 return;
131 LaunchUrlWithoutSecurityCheckWithDelegate(
132 escaped_url_, render_process_host_id_, tab_contents_id_, delegate_);
135 virtual bool IsOwnedByWorker() OVERRIDE { return true; }
137 private:
138 ExternalProtocolHandler::Delegate* delegate_;
139 GURL escaped_url_;
140 int render_process_host_id_;
141 int tab_contents_id_;
142 bool prompt_user_;
145 } // namespace
147 // static
148 void ExternalProtocolHandler::PrepopulateDictionary(
149 base::DictionaryValue* win_pref) {
150 static bool is_warm = false;
151 if (is_warm)
152 return;
153 is_warm = true;
155 static const char* const denied_schemes[] = {
156 "afp",
157 "data",
158 "disk",
159 "disks",
160 // ShellExecuting file:///C:/WINDOWS/system32/notepad.exe will simply
161 // execute the file specified! Hopefully we won't see any "file" schemes
162 // because we think of file:// URLs as handled URLs, but better to be safe
163 // than to let an attacker format the user's hard drive.
164 "file",
165 "hcp",
166 "javascript",
167 "ms-help",
168 "nntp",
169 "shell",
170 "vbscript",
171 // view-source is a special case in chrome. When it comes through an
172 // iframe or a redirect, it looks like an external protocol, but we don't
173 // want to shellexecute it.
174 "view-source",
175 "vnd.ms.radio",
178 static const char* const allowed_schemes[] = {
179 "mailto",
180 "news",
181 "snews",
182 #if defined(OS_WIN)
183 "ms-windows-store",
184 #endif
187 bool should_block;
188 for (size_t i = 0; i < arraysize(denied_schemes); ++i) {
189 if (!win_pref->GetBoolean(denied_schemes[i], &should_block)) {
190 win_pref->SetBoolean(denied_schemes[i], true);
194 for (size_t i = 0; i < arraysize(allowed_schemes); ++i) {
195 if (!win_pref->GetBoolean(allowed_schemes[i], &should_block)) {
196 win_pref->SetBoolean(allowed_schemes[i], false);
201 // static
202 ExternalProtocolHandler::BlockState ExternalProtocolHandler::GetBlockState(
203 const std::string& scheme,
204 bool initiated_by_user_gesture) {
205 if (!initiated_by_user_gesture)
206 return BLOCK;
208 if (scheme.length() == 1) {
209 // We have a URL that looks something like:
210 // C:/WINDOWS/system32/notepad.exe
211 // ShellExecuting this URL will cause the specified program to be executed.
212 return BLOCK;
215 // Check the stored prefs.
216 // TODO(pkasting): http://b/1119651 This kind of thing should go in the
217 // preferences on the profile, not in the local state.
218 PrefService* pref = g_browser_process->local_state();
219 if (pref) { // May be NULL during testing.
220 DictionaryPrefUpdate update_excluded_schemas(pref, prefs::kExcludedSchemes);
222 // Warm up the dictionary if needed.
223 PrepopulateDictionary(update_excluded_schemas.Get());
225 bool should_block;
226 if (update_excluded_schemas->GetBoolean(scheme, &should_block))
227 return should_block ? BLOCK : DONT_BLOCK;
230 return UNKNOWN;
233 // static
234 void ExternalProtocolHandler::SetBlockState(const std::string& scheme,
235 BlockState state) {
236 // Set in the stored prefs.
237 // TODO(pkasting): http://b/1119651 This kind of thing should go in the
238 // preferences on the profile, not in the local state.
239 PrefService* pref = g_browser_process->local_state();
240 if (pref) { // May be NULL during testing.
241 DictionaryPrefUpdate update_excluded_schemas(pref, prefs::kExcludedSchemes);
243 if (state == UNKNOWN) {
244 update_excluded_schemas->Remove(scheme, NULL);
245 } else {
246 update_excluded_schemas->SetBoolean(scheme, (state == BLOCK));
251 // static
252 void ExternalProtocolHandler::LaunchUrlWithDelegate(
253 const GURL& url,
254 int render_process_host_id,
255 int tab_contents_id,
256 Delegate* delegate,
257 bool initiated_by_user_gesture) {
258 DCHECK(base::MessageLoopForUI::IsCurrent());
260 // Escape the input scheme to be sure that the command does not
261 // have parameters unexpected by the external program.
262 std::string escaped_url_string = net::EscapeExternalHandlerValue(url.spec());
263 GURL escaped_url(escaped_url_string);
264 BlockState block_state = GetBlockStateWithDelegate(escaped_url.scheme(),
265 delegate,
266 initiated_by_user_gesture);
267 if (block_state == BLOCK) {
268 if (delegate)
269 delegate->BlockRequest();
270 return;
273 // The worker creates tasks with references to itself and puts them into
274 // message loops. When no tasks are left it will delete the observer and
275 // eventually be deleted itself.
276 ShellIntegration::DefaultWebClientObserver* observer =
277 new ExternalDefaultProtocolObserver(url,
278 render_process_host_id,
279 tab_contents_id,
280 block_state == UNKNOWN,
281 delegate);
282 scoped_refptr<ShellIntegration::DefaultProtocolClientWorker> worker =
283 CreateShellWorker(observer, escaped_url.scheme(), delegate);
285 // Start the check process running. This will send tasks to the FILE thread
286 // and when the answer is known will send the result back to the observer on
287 // the UI thread.
288 worker->StartCheckIsDefault();
291 // static
292 void ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(
293 const GURL& url,
294 int render_process_host_id,
295 int tab_contents_id) {
296 content::WebContents* web_contents = tab_util::GetWebContentsByID(
297 render_process_host_id, tab_contents_id);
298 if (!web_contents)
299 return;
301 platform_util::OpenExternal(
302 Profile::FromBrowserContext(web_contents->GetBrowserContext()), url);
305 // static
306 void ExternalProtocolHandler::RegisterPrefs(PrefRegistrySimple* registry) {
307 registry->RegisterDictionaryPref(prefs::kExcludedSchemes);