[email protected] | 65c8114 | 2012-07-31 19:44:43 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 4 | |
[email protected] | ed2b100 | 2011-05-25 14:12:10 | [diff] [blame] | 5 | #include "chrome/browser/external_protocol/external_protocol_handler.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 6 | |
avi | 6846aef | 2015-12-26 01:09:38 | [diff] [blame] | 7 | #include <stddef.h> |
Will Cassella | c79ca56 | 2020-07-25 04:03:09 | [diff] [blame] | 8 | #include <utility> |
avi | 6846aef | 2015-12-26 01:09:38 | [diff] [blame] | 9 | |
[email protected] | 317c58f0 | 2011-11-09 02:15:03 | [diff] [blame] | 10 | #include "base/bind.h" |
Hans Wennborg | 1790e6b | 2020-04-24 19:10:33 | [diff] [blame] | 11 | #include "base/check_op.h" |
dominickn | 0c9a506 | 2016-10-06 20:49:00 | [diff] [blame] | 12 | #include "base/metrics/histogram_macros.h" |
Hans Wennborg | 1790e6b | 2020-04-24 19:10:33 | [diff] [blame] | 13 | #include "base/notreached.h" |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 14 | #include "base/stl_util.h" |
[email protected] | d883056 | 2013-06-10 22:01:54 | [diff] [blame] | 15 | #include "base/strings/string_util.h" |
Callum May | 48091d5 | 2019-07-03 20:09:12 | [diff] [blame] | 16 | #include "build/build_config.h" |
Yuta Hijikata | 235fc62b | 2020-12-08 03:48:32 | [diff] [blame] | 17 | #include "build/chromeos_buildflags.h" |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 18 | #include "chrome/browser/external_protocol/auto_launch_protocols_policy_handler.h" |
[email protected] | 14a000d | 2010-04-29 21:44:24 | [diff] [blame] | 19 | #include "chrome/browser/platform_util.h" |
[email protected] | 7f0a3efa | 2013-12-12 17:16:12 | [diff] [blame] | 20 | #include "chrome/browser/profiles/profile.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 21 | #include "chrome/common/pref_names.h" |
brettw | b1fc1b8 | 2016-02-02 00:19:08 | [diff] [blame] | 22 | #include "components/prefs/pref_registry_simple.h" |
| 23 | #include "components/prefs/pref_service.h" |
| 24 | #include "components/prefs/scoped_user_pref_update.h" |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 25 | #include "components/url_matcher/url_matcher.h" |
Yann Dago | e65b7ee | 2022-01-04 19:01:35 | [diff] [blame] | 26 | #include "components/url_matcher/url_util.h" |
[email protected] | ed10dd1 | 2011-12-07 12:03:42 | [diff] [blame] | 27 | #include "content/public/browser/browser_thread.h" |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 28 | #include "content/public/browser/weak_document_ptr.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 29 | #include "net/base/escape.h" |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 30 | #include "services/network/public/cpp/is_potentially_trustworthy.h" |
Hans Wennborg | 3e67bab | 2021-04-08 11:34:31 | [diff] [blame] | 31 | #include "third_party/blink/public/mojom/devtools/console_message.mojom.h" |
[email protected] | 761fa470 | 2013-07-02 15:25:15 | [diff] [blame] | 32 | #include "url/gurl.h" |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 33 | #include "url/origin.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 34 | |
Xiaohan Wang | 5b9ac9a | 2022-01-15 22:52:41 | [diff] [blame] | 35 | #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA) && \ |
| 36 | !BUILDFLAG(IS_CHROMEOS_ASH) |
Yasmin | 85bccc56 | 2019-08-12 17:25:01 | [diff] [blame] | 37 | #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h" |
Richard Knoll | 2355d93 | 2019-07-19 16:57:29 | [diff] [blame] | 38 | #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h" |
Benjamin Lerman | 5389aa6 | 2021-08-03 10:11:51 | [diff] [blame] | 39 | #endif |
| 40 | |
Xiaohan Wang | 5b9ac9a | 2022-01-15 22:52:41 | [diff] [blame] | 41 | #if !BUILDFLAG(IS_ANDROID) |
Lei Zhang | ddd2248 | 2021-05-11 08:07:32 | [diff] [blame] | 42 | #include "chrome/browser/ui/browser.h" |
Callum May | 48091d5 | 2019-07-03 20:09:12 | [diff] [blame] | 43 | #include "chrome/browser/ui/browser_finder.h" |
| 44 | #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 45 | #endif |
| 46 | |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 47 | namespace { |
| 48 | |
Eric Lawrence | 1c3cc83 | 2020-12-18 11:57:36 | [diff] [blame] | 49 | // Anti-flood protection controls whether we accept requests for launching |
| 50 | // external protocols. Set to false each time an external protocol is requested, |
| 51 | // and set back to true on each user gesture, extension API call, and navigation |
| 52 | // to an external handler via bookmarks or the omnibox. This variable should |
| 53 | // only be accessed from the UI thread. |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 54 | bool g_accept_requests = true; |
[email protected] | 86fad30d | 2014-07-29 21:39:27 | [diff] [blame] | 55 | |
Daniel Bratell | af82221 | 2018-03-13 11:15:06 | [diff] [blame] | 56 | ExternalProtocolHandler::Delegate* g_external_protocol_handler_delegate = |
| 57 | nullptr; |
John Abd-El-Malek | a67add8 | 2018-03-09 18:22:01 | [diff] [blame] | 58 | |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 59 | constexpr const char* kDeniedSchemes[] = { |
Alex Moshchuk | 902f802b | 2019-08-21 05:28:28 | [diff] [blame] | 60 | "afp", |
| 61 | "data", |
| 62 | "disk", |
| 63 | "disks", |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 64 | // ShellExecuting file:///C:/WINDOWS/system32/notepad.exe will simply |
| 65 | // execute the file specified! Hopefully we won't see any "file" schemes |
| 66 | // because we think of file:// URLs as handled URLs, but better to be safe |
| 67 | // than to let an attacker format the user's hard drive. |
Alex Moshchuk | 902f802b | 2019-08-21 05:28:28 | [diff] [blame] | 68 | "file", |
| 69 | "hcp", |
| 70 | "ie.http", |
| 71 | "javascript", |
Alex Moshchuk | 5576e53 | 2021-08-18 19:47:41 | [diff] [blame] | 72 | "mk", |
Alex Moshchuk | 902f802b | 2019-08-21 05:28:28 | [diff] [blame] | 73 | "ms-help", |
| 74 | "nntp", |
| 75 | "res", |
| 76 | "shell", |
| 77 | "vbscript", |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 78 | // view-source is a special case in chrome. When it comes through an |
| 79 | // iframe or a redirect, it looks like an external protocol, but we don't |
| 80 | // want to shellexecute it. |
Alex Moshchuk | 902f802b | 2019-08-21 05:28:28 | [diff] [blame] | 81 | "view-source", |
| 82 | "vnd.ms.radio", |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 83 | }; |
| 84 | |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 85 | constexpr const char* kAllowedSchemes[] = { |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 86 | "mailto", "news", "snews", |
| 87 | }; |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 88 | |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 89 | void AddMessageToConsole(const content::WeakDocumentPtr& document, |
| 90 | blink::mojom::ConsoleMessageLevel level, |
| 91 | const std::string& message) { |
| 92 | if (content::RenderFrameHost* rfh = document.AsRenderFrameHostIfValid()) |
| 93 | rfh->AddMessageToConsole(level, message); |
| 94 | } |
| 95 | |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 96 | // Functions enabling unit testing. Using a NULL delegate will use the default |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 97 | // behavior; if a delegate is provided it will be used instead. |
| 98 | scoped_refptr<shell_integration::DefaultProtocolClientWorker> CreateShellWorker( |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 99 | const std::string& protocol, |
| 100 | ExternalProtocolHandler::Delegate* delegate) { |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 101 | if (delegate) |
Will Cassella | c79ca56 | 2020-07-25 04:03:09 | [diff] [blame] | 102 | return delegate->CreateShellWorker(protocol); |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 103 | return base::MakeRefCounted<shell_integration::DefaultProtocolClientWorker>( |
Will Cassella | c79ca56 | 2020-07-25 04:03:09 | [diff] [blame] | 104 | protocol); |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | ExternalProtocolHandler::BlockState GetBlockStateWithDelegate( |
| 108 | const std::string& scheme, |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 109 | const url::Origin* initiating_origin, |
ramyasharma | 2c618e17 | 2017-02-06 05:41:34 | [diff] [blame] | 110 | ExternalProtocolHandler::Delegate* delegate, |
| 111 | Profile* profile) { |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 112 | if (delegate) |
| 113 | return delegate->GetBlockState(scheme, profile); |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 114 | return ExternalProtocolHandler::GetBlockState(scheme, initiating_origin, |
| 115 | profile); |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | void RunExternalProtocolDialogWithDelegate( |
| 119 | const GURL& url, |
Elly Fong-Jones | ea38f4ef | 2019-04-15 16:26:39 | [diff] [blame] | 120 | content::WebContents* web_contents, |
qinmin | 7573e42 | 2015-05-06 18:42:31 | [diff] [blame] | 121 | ui::PageTransition page_transition, |
| 122 | bool has_user_gesture, |
Anton Bikineev | 46bbb97 | 2021-05-15 17:53:53 | [diff] [blame] | 123 | const absl::optional<url::Origin>& initiating_origin, |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 124 | content::WeakDocumentPtr initiator_document, |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 125 | ExternalProtocolHandler::Delegate* delegate) { |
Elly Fong-Jones | ea38f4ef | 2019-04-15 16:26:39 | [diff] [blame] | 126 | DCHECK(web_contents); |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 127 | if (delegate) { |
Elly Fong-Jones | ea38f4ef | 2019-04-15 16:26:39 | [diff] [blame] | 128 | delegate->RunExternalProtocolDialog(url, web_contents, page_transition, |
Emily Stark | 13b66bdf | 2019-10-04 17:11:45 | [diff] [blame] | 129 | has_user_gesture, initiating_origin); |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 130 | return; |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 131 | } |
Eric Lawrence | 89eb579 | 2020-06-24 20:02:06 | [diff] [blame] | 132 | |
Xiaohan Wang | 5b9ac9a | 2022-01-15 22:52:41 | [diff] [blame] | 133 | #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) |
Eric Lawrence | 89eb579 | 2020-06-24 20:02:06 | [diff] [blame] | 134 | // If the Shell does not have a registered name for the protocol, |
| 135 | // attempting to invoke the protocol will fail. |
| 136 | if (shell_integration::GetApplicationNameForProtocol(url).empty()) { |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 137 | AddMessageToConsole( |
| 138 | initiator_document, blink::mojom::ConsoleMessageLevel::kError, |
Eric Lawrence | 89eb579 | 2020-06-24 20:02:06 | [diff] [blame] | 139 | "Failed to launch '" + url.possibly_invalid_spec() + |
| 140 | "' because the scheme does not have a registered handler."); |
| 141 | return; |
| 142 | } |
| 143 | #endif |
| 144 | |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 145 | ExternalProtocolHandler::RunExternalProtocolDialog( |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 146 | url, web_contents, page_transition, has_user_gesture, initiating_origin, |
| 147 | std::move(initiator_document)); |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | void LaunchUrlWithoutSecurityCheckWithDelegate( |
| 151 | const GURL& url, |
Trent Apted | 7225e16 | 2018-05-10 08:33:35 | [diff] [blame] | 152 | content::WebContents* web_contents, |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 153 | content::WeakDocumentPtr initiator_document, |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 154 | ExternalProtocolHandler::Delegate* delegate) { |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 155 | if (delegate) { |
davidsac | 1fe2ac646 | 2016-12-20 01:27:41 | [diff] [blame] | 156 | delegate->LaunchUrlWithoutSecurityCheck(url, web_contents); |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 157 | return; |
[email protected] | 7f0a3efa | 2013-12-12 17:16:12 | [diff] [blame] | 158 | } |
Trent Apted | 7225e16 | 2018-05-10 08:33:35 | [diff] [blame] | 159 | |
| 160 | // |web_contents| is only passed in to find browser context. Do not assume |
| 161 | // that the external protocol request came from the main frame. |
| 162 | if (!web_contents) |
| 163 | return; |
| 164 | |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 165 | AddMessageToConsole( |
| 166 | initiator_document, blink::mojom::ConsoleMessageLevel::kInfo, |
Eric Lawrence | 89eb579 | 2020-06-24 20:02:06 | [diff] [blame] | 167 | "Launched external handler for '" + url.possibly_invalid_spec() + "'."); |
| 168 | |
Trent Apted | 7225e16 | 2018-05-10 08:33:35 | [diff] [blame] | 169 | platform_util::OpenExternal( |
| 170 | Profile::FromBrowserContext(web_contents->GetBrowserContext()), url); |
Callum May | 48091d5 | 2019-07-03 20:09:12 | [diff] [blame] | 171 | |
Xiaohan Wang | 5b9ac9a | 2022-01-15 22:52:41 | [diff] [blame] | 172 | #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH) |
Callum May | 48091d5 | 2019-07-03 20:09:12 | [diff] [blame] | 173 | // If the protocol navigation occurs in a new tab, close it. |
| 174 | // Avoid calling CloseContents if the tab is not in this browser's tab strip |
| 175 | // model; this can happen if the protocol was initiated by something |
| 176 | // internal to Chrome. |
| 177 | Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
| 178 | if (browser && web_contents->GetController().IsInitialNavigation() && |
| 179 | browser->tab_strip_model()->count() > 1 && |
| 180 | browser->tab_strip_model()->GetIndexOfWebContents(web_contents) != |
| 181 | TabStripModel::kNoTab) { |
| 182 | web_contents->Close(); |
| 183 | } |
| 184 | #endif |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 185 | } |
| 186 | |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 187 | // When we are about to launch a URL with the default OS level application, we |
| 188 | // check if the external application will be us. If it is we just ignore the |
| 189 | // request. |
| 190 | void OnDefaultProtocolClientWorkerFinished( |
| 191 | const GURL& escaped_url, |
Jeremy Roman | 111cdbb | 2021-06-10 19:42:52 | [diff] [blame] | 192 | content::WebContents::Getter web_contents_getter, |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 193 | bool prompt_user, |
| 194 | ui::PageTransition page_transition, |
| 195 | bool has_user_gesture, |
Anton Bikineev | 46bbb97 | 2021-05-15 17:53:53 | [diff] [blame] | 196 | const absl::optional<url::Origin>& initiating_origin, |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 197 | content::WeakDocumentPtr initiator_document, |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 198 | ExternalProtocolHandler::Delegate* delegate, |
pmonette | b920414 | 2016-03-08 20:02:44 | [diff] [blame] | 199 | shell_integration::DefaultWebClientState state) { |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 200 | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 201 | |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 202 | if (delegate) |
| 203 | delegate->FinishedProcessingCheck(); |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 204 | |
Jeremy Roman | 111cdbb | 2021-06-10 19:42:52 | [diff] [blame] | 205 | content::WebContents* web_contents = web_contents_getter.Run(); |
Richard Knoll | 2355d93 | 2019-07-19 16:57:29 | [diff] [blame] | 206 | |
| 207 | // The default handler is hidden if it is Chrome itself, as nothing will |
| 208 | // happen if it is selected (since this is invoked by the external protocol |
| 209 | // handling flow). |
| 210 | bool chrome_is_default_handler = state == shell_integration::IS_DEFAULT; |
| 211 | |
Richard Knoll | 5e85eb12 | 2019-09-05 11:15:33 | [diff] [blame] | 212 | // On ChromeOS, Click to Call is integrated into the external protocol dialog. |
Xiaohan Wang | 5b9ac9a | 2022-01-15 22:52:41 | [diff] [blame] | 213 | #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA) && \ |
| 214 | !BUILDFLAG(IS_CHROMEOS_ASH) |
Himanshu Jaju | 84497314 | 2019-08-21 06:24:19 | [diff] [blame] | 215 | if (web_contents && ShouldOfferClickToCallForURL( |
| 216 | web_contents->GetBrowserContext(), escaped_url)) { |
Richard Knoll | 2355d93 | 2019-07-19 16:57:29 | [diff] [blame] | 217 | // Handle tel links by opening the Click to Call dialog. This will call back |
| 218 | // into LaunchUrlWithoutSecurityCheck if the user selects a system handler. |
Richard Knoll | 8bd8d19 | 2019-10-15 14:01:12 | [diff] [blame] | 219 | ClickToCallUiController::ShowDialog(web_contents, initiating_origin, |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 220 | std::move(initiator_document), |
Richard Knoll | 8bd8d19 | 2019-10-15 14:01:12 | [diff] [blame] | 221 | escaped_url, chrome_is_default_handler); |
Richard Knoll | 2355d93 | 2019-07-19 16:57:29 | [diff] [blame] | 222 | return; |
| 223 | } |
| 224 | #endif |
| 225 | |
| 226 | if (chrome_is_default_handler) { |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 227 | if (delegate) |
| 228 | delegate->BlockRequest(); |
| 229 | return; |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 230 | } |
| 231 | |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 232 | // If we get here, either we are not the default or we cannot work out |
| 233 | // what the default is, so we proceed. |
| 234 | if (prompt_user) { |
Elly Fong-Jones | ea38f4ef | 2019-04-15 16:26:39 | [diff] [blame] | 235 | // Never prompt the user without a web_contents. |
| 236 | if (!web_contents) |
| 237 | return; |
| 238 | |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 239 | // Ask the user if they want to allow the protocol. This will call |
| 240 | // LaunchUrlWithoutSecurityCheck if the user decides to accept the |
| 241 | // protocol. |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 242 | RunExternalProtocolDialogWithDelegate( |
| 243 | escaped_url, web_contents, page_transition, has_user_gesture, |
| 244 | initiating_origin, std::move(initiator_document), delegate); |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 245 | return; |
| 246 | } |
| 247 | |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 248 | LaunchUrlWithoutSecurityCheckWithDelegate( |
| 249 | escaped_url, web_contents, std::move(initiator_document), delegate); |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 250 | } |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 251 | |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 252 | bool IsSchemeOriginPairAllowedByPolicy(const std::string& scheme, |
| 253 | const url::Origin* initiating_origin, |
| 254 | PrefService* prefs) { |
Todd Sahl | c98ade6 | 2020-07-29 23:37:49 | [diff] [blame] | 255 | if (!initiating_origin) |
| 256 | return false; |
| 257 | |
Austin Sullivan | 7d219a25 | 2021-12-20 14:55:31 | [diff] [blame] | 258 | const base::Value* exempted_protocols = |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 259 | prefs->GetList(prefs::kAutoLaunchProtocolsFromOrigins); |
| 260 | if (!exempted_protocols) |
| 261 | return false; |
| 262 | |
| 263 | const base::Value* origin_patterns = nullptr; |
Daniel Cheng | 354945d | 2022-02-02 23:39:17 | [diff] [blame] | 264 | for (const base::Value& entry : exempted_protocols->GetListDeprecated()) { |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 265 | const base::DictionaryValue& protocol_origins_map = |
| 266 | base::Value::AsDictionaryValue(entry); |
| 267 | const std::string* protocol = protocol_origins_map.FindStringKey( |
| 268 | policy::AutoLaunchProtocolsPolicyHandler::kProtocolNameKey); |
| 269 | DCHECK(protocol); |
| 270 | if (*protocol == scheme) { |
| 271 | origin_patterns = protocol_origins_map.FindListKey( |
| 272 | policy::AutoLaunchProtocolsPolicyHandler::kOriginListKey); |
| 273 | break; |
| 274 | } |
| 275 | } |
| 276 | if (!origin_patterns) |
| 277 | return false; |
| 278 | |
| 279 | url_matcher::URLMatcher matcher; |
| 280 | url_matcher::URLMatcherConditionSet::ID id(0); |
Yann Dago | e65b7ee | 2022-01-04 19:01:35 | [diff] [blame] | 281 | url_matcher::util::AddFilters(&matcher, true /* allowed */, &id, |
| 282 | &base::Value::AsListValue(*origin_patterns)); |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 283 | |
| 284 | auto matching_set = matcher.MatchURL(initiating_origin->GetURL()); |
| 285 | return !matching_set.empty(); |
| 286 | } |
| 287 | |
[email protected] | 65c8114 | 2012-07-31 19:44:43 | [diff] [blame] | 288 | } // namespace |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 289 | |
ramyasharma | ef194a5 | 2017-02-08 03:30:22 | [diff] [blame] | 290 | const char ExternalProtocolHandler::kHandleStateMetric[] = |
| 291 | "BrowserDialogs.ExternalProtocol.HandleState"; |
| 292 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 293 | // static |
John Abd-El-Malek | a67add8 | 2018-03-09 18:22:01 | [diff] [blame] | 294 | void ExternalProtocolHandler::SetDelegateForTesting(Delegate* delegate) { |
Daniel Bratell | af82221 | 2018-03-13 11:15:06 | [diff] [blame] | 295 | g_external_protocol_handler_delegate = delegate; |
John Abd-El-Malek | a67add8 | 2018-03-09 18:22:01 | [diff] [blame] | 296 | } |
| 297 | |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 298 | bool ExternalProtocolHandler::MayRememberAllowDecisionsForThisOrigin( |
| 299 | const url::Origin* initiating_origin) { |
| 300 | return initiating_origin && |
| 301 | network::IsOriginPotentiallyTrustworthy(*initiating_origin); |
| 302 | } |
| 303 | |
| 304 | // static. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 305 | ExternalProtocolHandler::BlockState ExternalProtocolHandler::GetBlockState( |
ramyasharma | 2c618e17 | 2017-02-06 05:41:34 | [diff] [blame] | 306 | const std::string& scheme, |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 307 | const url::Origin* initiating_origin, |
ramyasharma | 2c618e17 | 2017-02-06 05:41:34 | [diff] [blame] | 308 | Profile* profile) { |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 309 | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 310 | |
Eric Lawrence | d2a713a | 2019-07-15 19:14:05 | [diff] [blame] | 311 | // If we are being flooded with requests, block the request. |
[email protected] | 86fad30d | 2014-07-29 21:39:27 | [diff] [blame] | 312 | if (!g_accept_requests) |
[email protected] | e7eaedde | 2009-09-25 20:09:49 | [diff] [blame] | 313 | return BLOCK; |
| 314 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 315 | if (scheme.length() == 1) { |
| 316 | // We have a URL that looks something like: |
| 317 | // C:/WINDOWS/system32/notepad.exe |
| 318 | // ShellExecuting this URL will cause the specified program to be executed. |
| 319 | return BLOCK; |
| 320 | } |
| 321 | |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 322 | // Always block the hard-coded denied schemes. |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 323 | for (size_t i = 0; i < base::size(kDeniedSchemes); ++i) { |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 324 | if (kDeniedSchemes[i] == scheme) |
| 325 | return BLOCK; |
| 326 | } |
| 327 | |
| 328 | // Always allow the hard-coded allowed schemes. |
Avi Drissman | 5f0fb8c | 2018-12-25 23:20:49 | [diff] [blame] | 329 | for (size_t i = 0; i < base::size(kAllowedSchemes); ++i) { |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 330 | if (kAllowedSchemes[i] == scheme) |
| 331 | return DONT_BLOCK; |
| 332 | } |
| 333 | |
ramyasharma | 2c618e17 | 2017-02-06 05:41:34 | [diff] [blame] | 334 | PrefService* profile_prefs = profile->GetPrefs(); |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 335 | if (profile_prefs) { // May be NULL during testing. |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 336 | if (IsSchemeOriginPairAllowedByPolicy(scheme, initiating_origin, |
| 337 | profile_prefs)) { |
| 338 | return DONT_BLOCK; |
| 339 | } |
| 340 | |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 341 | if (MayRememberAllowDecisionsForThisOrigin(initiating_origin)) { |
| 342 | // Check if there is a matching {Origin+Protocol} pair exemption: |
Austin Sullivan | 7d219a25 | 2021-12-20 14:55:31 | [diff] [blame] | 343 | const base::Value* allowed_origin_protocol_pairs = |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 344 | profile_prefs->GetDictionary( |
| 345 | prefs::kProtocolHandlerPerOriginAllowedProtocols); |
| 346 | const base::Value* allowed_protocols_for_origin = |
| 347 | allowed_origin_protocol_pairs->FindDictKey( |
| 348 | initiating_origin->Serialize()); |
| 349 | if (allowed_protocols_for_origin) { |
Anton Bikineev | 46bbb97 | 2021-05-15 17:53:53 | [diff] [blame] | 350 | absl::optional<bool> allow = |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 351 | allowed_protocols_for_origin->FindBoolKey(scheme); |
| 352 | if (allow.has_value() && allow.value()) |
| 353 | return DONT_BLOCK; |
| 354 | } |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 355 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | return UNKNOWN; |
| 359 | } |
| 360 | |
| 361 | // static |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 362 | // This is only called when the "remember" check box is selected from the |
| 363 | // External Protocol Prompt dialog, and that check box is only shown when there |
| 364 | // is a non-empty, potentially-trustworthy initiating origin. |
| 365 | void ExternalProtocolHandler::SetBlockState( |
| 366 | const std::string& scheme, |
| 367 | const url::Origin& initiating_origin, |
| 368 | BlockState state, |
| 369 | Profile* profile) { |
Ben Wells | cafcc828 | 2017-09-21 06:57:57 | [diff] [blame] | 370 | // Setting the state to BLOCK is no longer supported through the UI. |
| 371 | DCHECK_NE(state, BLOCK); |
| 372 | |
[email protected] | 9829948 | 2009-10-06 19:33:07 | [diff] [blame] | 373 | // Set in the stored prefs. |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 374 | if (MayRememberAllowDecisionsForThisOrigin(&initiating_origin)) { |
| 375 | PrefService* profile_prefs = profile->GetPrefs(); |
| 376 | if (profile_prefs) { // May be NULL during testing. |
Alex Turner | 0e3da514 | 2022-01-14 00:23:33 | [diff] [blame] | 377 | DictionaryPrefUpdate update_allowed_origin_protocol_pairs( |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 378 | profile_prefs, prefs::kProtocolHandlerPerOriginAllowedProtocols); |
| 379 | |
| 380 | const std::string serialized_origin = initiating_origin.Serialize(); |
| 381 | base::Value* allowed_protocols_for_origin = |
| 382 | update_allowed_origin_protocol_pairs->FindDictKey(serialized_origin); |
| 383 | if (!allowed_protocols_for_origin) { |
| 384 | update_allowed_origin_protocol_pairs->SetKey( |
| 385 | serialized_origin, base::Value(base::Value::Type::DICTIONARY)); |
| 386 | allowed_protocols_for_origin = |
| 387 | update_allowed_origin_protocol_pairs->FindDictKey( |
| 388 | serialized_origin); |
| 389 | } |
| 390 | if (state == DONT_BLOCK) { |
| 391 | allowed_protocols_for_origin->SetBoolKey(scheme, true); |
| 392 | } else { |
| 393 | allowed_protocols_for_origin->RemoveKey(scheme); |
| 394 | if (allowed_protocols_for_origin->DictEmpty()) |
| 395 | update_allowed_origin_protocol_pairs->RemoveKey(serialized_origin); |
| 396 | } |
| 397 | } |
[email protected] | 9829948 | 2009-10-06 19:33:07 | [diff] [blame] | 398 | } |
Elly Fong-Jones | 0257b4cf | 2019-10-21 19:54:45 | [diff] [blame] | 399 | |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 400 | if (g_external_protocol_handler_delegate) { |
| 401 | g_external_protocol_handler_delegate->OnSetBlockState( |
| 402 | scheme, initiating_origin, state); |
| 403 | } |
[email protected] | 9829948 | 2009-10-06 19:33:07 | [diff] [blame] | 404 | } |
| 405 | |
| 406 | // static |
Emily Stark | 13b66bdf | 2019-10-04 17:11:45 | [diff] [blame] | 407 | void ExternalProtocolHandler::LaunchUrl( |
| 408 | const GURL& url, |
Jeremy Roman | 111cdbb | 2021-06-10 19:42:52 | [diff] [blame] | 409 | content::WebContents::Getter web_contents_getter, |
Emily Stark | 13b66bdf | 2019-10-04 17:11:45 | [diff] [blame] | 410 | ui::PageTransition page_transition, |
| 411 | bool has_user_gesture, |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 412 | const absl::optional<url::Origin>& initiating_origin, |
| 413 | content::WeakDocumentPtr initiator_document) { |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 414 | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
[email protected] | e7eaedde | 2009-09-25 20:09:49 | [diff] [blame] | 415 | |
Eric Lawrence | 1c3cc83 | 2020-12-18 11:57:36 | [diff] [blame] | 416 | // Disable anti-flood protection if the user is invoking a bookmark or |
| 417 | // navigating directly using the omnibox. |
| 418 | if (!g_accept_requests && |
| 419 | (PageTransitionCoreTypeIs(page_transition, |
| 420 | ui::PAGE_TRANSITION_AUTO_BOOKMARK) || |
| 421 | PageTransitionCoreTypeIs(page_transition, ui::PAGE_TRANSITION_TYPED))) { |
| 422 | g_accept_requests = true; |
| 423 | } |
| 424 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 425 | // Escape the input scheme to be sure that the command does not |
| 426 | // have parameters unexpected by the external program. |
Matt Giuca | 36fd3c9 | 2017-11-27 01:12:35 | [diff] [blame] | 427 | // TODO(mgiuca): This essentially amounts to "remove illegal characters from |
| 428 | // the URL", something that probably should be done by the GURL constructor |
| 429 | // itself. The GURL constructor does do it in some cases (e.g., mailto) but |
| 430 | // not in general. https://crbug.com/788244. |
[email protected] | c83d7d1 | 2011-11-06 14:34:29 | [diff] [blame] | 431 | std::string escaped_url_string = net::EscapeExternalHandlerValue(url.spec()); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 432 | GURL escaped_url(escaped_url_string); |
ramyasharma | 2c618e17 | 2017-02-06 05:41:34 | [diff] [blame] | 433 | |
Jeremy Roman | 111cdbb | 2021-06-10 19:42:52 | [diff] [blame] | 434 | content::WebContents* web_contents = web_contents_getter.Run(); |
ramyasharma | 2c618e17 | 2017-02-06 05:41:34 | [diff] [blame] | 435 | Profile* profile = nullptr; |
| 436 | if (web_contents) // Maybe NULL during testing. |
| 437 | profile = Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
Daniel Bratell | af82221 | 2018-03-13 11:15:06 | [diff] [blame] | 438 | BlockState block_state = GetBlockStateWithDelegate( |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 439 | escaped_url.scheme(), base::OptionalOrNullptr(initiating_origin), |
| 440 | g_external_protocol_handler_delegate, profile); |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 441 | if (block_state == BLOCK) { |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 442 | AddMessageToConsole( |
| 443 | initiator_document, blink::mojom::ConsoleMessageLevel::kError, |
Eric Lawrence | 89eb579 | 2020-06-24 20:02:06 | [diff] [blame] | 444 | "Not allowed to launch '" + url.possibly_invalid_spec() + "'" + |
| 445 | (g_accept_requests ? "." : " because a user gesture is required.")); |
| 446 | |
Daniel Bratell | af82221 | 2018-03-13 11:15:06 | [diff] [blame] | 447 | if (g_external_protocol_handler_delegate) |
| 448 | g_external_protocol_handler_delegate->BlockRequest(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 449 | return; |
| 450 | } |
| 451 | |
[email protected] | 86fad30d | 2014-07-29 21:39:27 | [diff] [blame] | 452 | g_accept_requests = false; |
| 453 | |
Anton Bikineev | 46bbb97 | 2021-05-15 17:53:53 | [diff] [blame] | 454 | absl::optional<url::Origin> initiating_origin_or_precursor; |
Emily Stark | 7cfe6fc | 2020-02-19 05:19:01 | [diff] [blame] | 455 | if (initiating_origin) { |
| 456 | // Transform the initiating origin to its precursor origin if it is |
| 457 | // opaque. |initiating_origin| is shown in the UI to attribute the external |
| 458 | // protocol request to a particular site, and showing an opaque origin isn't |
| 459 | // useful. |
| 460 | if (initiating_origin->opaque()) { |
| 461 | initiating_origin_or_precursor = url::Origin::Create( |
| 462 | initiating_origin->GetTupleOrPrecursorTupleIfOpaque().GetURL()); |
| 463 | } else { |
| 464 | initiating_origin_or_precursor = initiating_origin; |
| 465 | } |
| 466 | } |
| 467 | |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 468 | // The worker creates tasks with references to itself and puts them into |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 469 | // message loops. |
Will Cassella | c79ca56 | 2020-07-25 04:03:09 | [diff] [blame] | 470 | shell_integration::DefaultWebClientWorkerCallback callback = base::BindOnce( |
Emily Stark | 7cfe6fc | 2020-02-19 05:19:01 | [diff] [blame] | 471 | &OnDefaultProtocolClientWorkerFinished, escaped_url, |
Jeremy Roman | 111cdbb | 2021-06-10 19:42:52 | [diff] [blame] | 472 | std::move(web_contents_getter), block_state == UNKNOWN, page_transition, |
| 473 | has_user_gesture, initiating_origin_or_precursor, |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 474 | std::move(initiator_document), g_external_protocol_handler_delegate); |
[email protected] | 956eabb | 2011-09-23 05:04:38 | [diff] [blame] | 475 | |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 476 | // Start the check process running. This will send tasks to a worker task |
| 477 | // runner and when the answer is known will send the result back to |
pmonette | 586ab5b3 | 2016-03-07 19:50:37 | [diff] [blame] | 478 | // OnDefaultProtocolClientWorkerFinished(). |
Will Cassella | c79ca56 | 2020-07-25 04:03:09 | [diff] [blame] | 479 | CreateShellWorker(escaped_url.scheme(), g_external_protocol_handler_delegate) |
| 480 | ->StartCheckIsDefault(std::move(callback)); |
[email protected] | 10f57b9 | 2009-09-03 21:33:21 | [diff] [blame] | 481 | } |
| 482 | |
| 483 | // static |
[email protected] | 7f0a3efa | 2013-12-12 17:16:12 | [diff] [blame] | 484 | void ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck( |
| 485 | const GURL& url, |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 486 | content::WebContents* web_contents, |
| 487 | content::WeakDocumentPtr initiator_document) { |
Julian Pastarmov | 38081dc | 2020-05-21 03:11:17 | [diff] [blame] | 488 | // Escape the input scheme to be sure that the command does not |
| 489 | // have parameters unexpected by the external program. The url passed in the |
| 490 | // |url| parameter might already be escaped but the EscapeExternalHandlerValue |
| 491 | // is idempotent so it is safe to apply it again. |
| 492 | // TODO(788244): This essentially amounts to "remove illegal characters from |
| 493 | // the URL", something that probably should be done by the GURL constructor |
| 494 | // itself. |
| 495 | std::string escaped_url_string = net::EscapeExternalHandlerValue(url.spec()); |
| 496 | GURL escaped_url(escaped_url_string); |
| 497 | |
Trent Apted | 7225e16 | 2018-05-10 08:33:35 | [diff] [blame] | 498 | LaunchUrlWithoutSecurityCheckWithDelegate( |
Jeremy Roman | 04ad4e3f | 2021-12-22 18:54:54 | [diff] [blame] | 499 | escaped_url, web_contents, std::move(initiator_document), |
| 500 | g_external_protocol_handler_delegate); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 501 | } |
| 502 | |
| 503 | // static |
[email protected] | 86fad30d | 2014-07-29 21:39:27 | [diff] [blame] | 504 | void ExternalProtocolHandler::PermitLaunchUrl() { |
Lei Zhang | 943bceea | 2017-09-26 04:46:53 | [diff] [blame] | 505 | DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 506 | |
[email protected] | 86fad30d | 2014-07-29 21:39:27 | [diff] [blame] | 507 | g_accept_requests = true; |
| 508 | } |
dominickn | 0c9a506 | 2016-10-06 20:49:00 | [diff] [blame] | 509 | |
| 510 | // static |
ramyasharma | ef194a5 | 2017-02-08 03:30:22 | [diff] [blame] | 511 | void ExternalProtocolHandler::RecordHandleStateMetrics(bool checkbox_selected, |
| 512 | BlockState block_state) { |
| 513 | HandleState handle_state = DONT_LAUNCH; |
| 514 | switch (block_state) { |
| 515 | case DONT_BLOCK: |
| 516 | handle_state = checkbox_selected ? CHECKED_LAUNCH : LAUNCH; |
| 517 | break; |
| 518 | case BLOCK: |
Ben Wells | f227110 | 2017-09-06 03:07:56 | [diff] [blame] | 519 | handle_state = |
| 520 | checkbox_selected ? CHECKED_DONT_LAUNCH_DEPRECATED : DONT_LAUNCH; |
ramyasharma | ef194a5 | 2017-02-08 03:30:22 | [diff] [blame] | 521 | break; |
| 522 | case UNKNOWN: |
| 523 | NOTREACHED(); |
| 524 | return; |
| 525 | } |
Ben Wells | f227110 | 2017-09-06 03:07:56 | [diff] [blame] | 526 | DCHECK_NE(CHECKED_DONT_LAUNCH_DEPRECATED, handle_state); |
ramyasharma | ef194a5 | 2017-02-08 03:30:22 | [diff] [blame] | 527 | UMA_HISTOGRAM_ENUMERATION(kHandleStateMetric, handle_state, |
| 528 | HANDLE_STATE_LAST); |
dominickn | 0c9a506 | 2016-10-06 20:49:00 | [diff] [blame] | 529 | } |
| 530 | |
| 531 | // static |
| 532 | void ExternalProtocolHandler::RegisterPrefs(PrefRegistrySimple* registry) { |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 533 | registry->RegisterDictionaryPref( |
| 534 | prefs::kProtocolHandlerPerOriginAllowedProtocols); |
Todd Sahl | 1395963 | 2020-06-18 07:40:11 | [diff] [blame] | 535 | |
| 536 | registry->RegisterListPref(prefs::kAutoLaunchProtocolsFromOrigins); |
dominickn | 0c9a506 | 2016-10-06 20:49:00 | [diff] [blame] | 537 | } |
ramyasharma | 561a9cd | 2017-02-19 07:49:25 | [diff] [blame] | 538 | |
| 539 | // static |
| 540 | void ExternalProtocolHandler::ClearData(Profile* profile) { |
| 541 | PrefService* prefs = profile->GetPrefs(); |
Todd Sahl | 057d9c6 | 2020-04-17 18:41:14 | [diff] [blame] | 542 | prefs->ClearPref(prefs::kProtocolHandlerPerOriginAllowedProtocols); |
ramyasharma | 561a9cd | 2017-02-19 07:49:25 | [diff] [blame] | 543 | } |