[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 1 | // Copyright 2014 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 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 5 | #include "chrome/browser/extensions/extension_action_runner.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 6 | |
dcheng | c963c714 | 2016-04-08 03:55:22 | [diff] [blame] | 7 | #include <memory> |
| 8 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 9 | #include "base/auto_reset.h" |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 10 | #include "base/bind.h" |
| 11 | #include "base/bind_helpers.h" |
fdoray | 283082bd | 2016-06-02 20:18:46 | [diff] [blame] | 12 | #include "base/location.h" |
asvitkine | aa06031 | 2016-09-01 22:44:13 | [diff] [blame] | 13 | #include "base/metrics/histogram_macros.h" |
fdoray | 283082bd | 2016-06-02 20:18:46 | [diff] [blame] | 14 | #include "base/single_thread_task_runner.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 15 | #include "base/stl_util.h" |
fdoray | 283082bd | 2016-06-02 20:18:46 | [diff] [blame] | 16 | #include "base/threading/thread_task_runner_handle.h" |
[email protected] | 78cd68e | 2014-05-22 20:33:52 | [diff] [blame] | 17 | #include "chrome/browser/extensions/active_tab_permission_granter.h" |
rdevlin.cronin | aeffd18 | 2014-08-26 17:04:00 | [diff] [blame] | 18 | #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 19 | #include "chrome/browser/extensions/extension_action.h" |
[email protected] | 6a24a039 | 2014-08-12 21:31:33 | [diff] [blame] | 20 | #include "chrome/browser/extensions/extension_action_manager.h" |
[email protected] | e167058 | 2014-08-15 23:05:41 | [diff] [blame] | 21 | #include "chrome/browser/extensions/permissions_updater.h" |
[email protected] | eac223a | 2014-05-13 17:39:57 | [diff] [blame] | 22 | #include "chrome/browser/extensions/tab_helper.h" |
[email protected] | 6a24a039 | 2014-08-12 21:31:33 | [diff] [blame] | 23 | #include "chrome/browser/profiles/profile.h" |
[email protected] | e3f90c60 | 2014-08-18 12:41:59 | [diff] [blame] | 24 | #include "chrome/browser/sessions/session_tab_helper.h" |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 25 | #include "chrome/browser/ui/browser_finder.h" |
| 26 | #include "chrome/browser/ui/browser_window.h" |
| 27 | #include "chrome/browser/ui/extensions/blocked_action_bubble_delegate.h" |
| 28 | #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 29 | #include "chrome/common/extensions/api/extension_action/action_info.h" |
[email protected] | fdd2837 | 2014-08-21 02:27:26 | [diff] [blame] | 30 | #include "components/crx_file/id_util.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 31 | #include "content/public/browser/navigation_controller.h" |
| 32 | #include "content/public/browser/navigation_entry.h" |
jam | 29737c4 | 2017-02-01 16:26:08 | [diff] [blame] | 33 | #include "content/public/browser/navigation_handle.h" |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 34 | #include "content/public/browser/render_view_host.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 35 | #include "content/public/browser/web_contents.h" |
| 36 | #include "extensions/browser/extension_registry.h" |
| 37 | #include "extensions/common/extension.h" |
| 38 | #include "extensions/common/extension_messages.h" |
| 39 | #include "extensions/common/extension_set.h" |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 40 | #include "extensions/common/manifest.h" |
[email protected] | e167058 | 2014-08-15 23:05:41 | [diff] [blame] | 41 | #include "extensions/common/permissions/permission_set.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 42 | #include "extensions/common/permissions/permissions_data.h" |
| 43 | #include "ipc/ipc_message_macros.h" |
| 44 | |
| 45 | namespace extensions { |
| 46 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 47 | namespace { |
| 48 | |
| 49 | // The blocked actions that require a page refresh to run. |
| 50 | const int kRefreshRequiredActionsMask = |
| 51 | BLOCKED_ACTION_WEB_REQUEST | BLOCKED_ACTION_SCRIPT_AT_START; |
| 52 | } |
| 53 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 54 | ExtensionActionRunner::PendingScript::PendingScript( |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 55 | UserScript::RunLocation run_location, |
| 56 | const base::Closure& permit_script) |
| 57 | : run_location(run_location), permit_script(permit_script) {} |
| 58 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 59 | ExtensionActionRunner::PendingScript::PendingScript( |
vmpstr | b8aacbe | 2016-02-26 02:00:48 | [diff] [blame] | 60 | const PendingScript& other) = default; |
| 61 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 62 | ExtensionActionRunner::PendingScript::~PendingScript() {} |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 63 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 64 | ExtensionActionRunner::ExtensionActionRunner(content::WebContents* web_contents) |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 65 | : content::WebContentsObserver(web_contents), |
rdevlin.cronin | 1f87703 | 2015-02-20 00:12:42 | [diff] [blame] | 66 | num_page_requests_(0), |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 67 | browser_context_(web_contents->GetBrowserContext()), |
rdevlin.cronin | b8dffe56 | 2015-02-07 00:58:01 | [diff] [blame] | 68 | was_used_on_page_(false), |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 69 | ignore_active_tab_granted_(false), |
| 70 | extension_registry_observer_(this), |
| 71 | weak_factory_(this) { |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 72 | CHECK(web_contents); |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 73 | extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 74 | } |
| 75 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 76 | ExtensionActionRunner::~ExtensionActionRunner() { |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 77 | LogUMA(); |
| 78 | } |
| 79 | |
[email protected] | eac223a | 2014-05-13 17:39:57 | [diff] [blame] | 80 | // static |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 81 | ExtensionActionRunner* ExtensionActionRunner::GetForWebContents( |
[email protected] | eac223a | 2014-05-13 17:39:57 | [diff] [blame] | 82 | content::WebContents* web_contents) { |
| 83 | if (!web_contents) |
| 84 | return NULL; |
| 85 | TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 86 | return tab_helper ? tab_helper->extension_action_runner() : NULL; |
[email protected] | eac223a | 2014-05-13 17:39:57 | [diff] [blame] | 87 | } |
| 88 | |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 89 | ExtensionAction::ShowAction ExtensionActionRunner::RunAction( |
| 90 | const Extension* extension, |
| 91 | bool grant_tab_permissions) { |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 92 | if (grant_tab_permissions) { |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 93 | int blocked = GetBlockedActions(extension); |
| 94 | if ((blocked & kRefreshRequiredActionsMask) != 0) { |
| 95 | ShowBlockedActionBubble(extension); |
| 96 | return ExtensionAction::ACTION_NONE; |
| 97 | } |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 98 | TabHelper::FromWebContents(web_contents()) |
| 99 | ->active_tab_permission_granter() |
| 100 | ->GrantIfRequested(extension); |
| 101 | // If the extension had blocked actions, granting active tab will have |
| 102 | // run the extension. Don't execute further since clicking should run |
| 103 | // blocked actions *or* the normal extension action, not both. |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 104 | if (blocked != BLOCKED_ACTION_NONE) |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 105 | return ExtensionAction::ACTION_NONE; |
| 106 | } |
| 107 | |
| 108 | ExtensionAction* extension_action = |
| 109 | ExtensionActionManager::Get(browser_context_) |
| 110 | ->GetExtensionAction(*extension); |
| 111 | |
| 112 | // Anything that gets here should have a page or browser action. |
| 113 | DCHECK(extension_action); |
Mikel Astiz | 2f127c42 | 2018-04-05 19:10:27 | [diff] [blame] | 114 | int tab_id = SessionTabHelper::IdForTab(web_contents()).id(); |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 115 | if (!extension_action->GetIsVisible(tab_id)) |
| 116 | return ExtensionAction::ACTION_NONE; |
| 117 | |
| 118 | if (extension_action->HasPopup(tab_id)) |
| 119 | return ExtensionAction::ACTION_SHOW_POPUP; |
| 120 | |
| 121 | ExtensionActionAPI::Get(browser_context_) |
isandrk | 1296202 | 2017-05-04 15:27:33 | [diff] [blame] | 122 | ->DispatchExtensionActionClicked(*extension_action, web_contents(), |
| 123 | extension); |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 124 | return ExtensionAction::ACTION_NONE; |
[email protected] | 11814f5 | 2014-05-23 06:50:35 | [diff] [blame] | 125 | } |
| 126 | |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 127 | void ExtensionActionRunner::RunBlockedActions(const Extension* extension) { |
skyostil | 32fa6b3f9 | 2016-08-12 13:15:43 | [diff] [blame] | 128 | DCHECK(base::ContainsKey(pending_scripts_, extension->id()) || |
rdevlin.cronin | acb74592 | 2016-02-17 20:37:44 | [diff] [blame] | 129 | web_request_blocked_.count(extension->id()) != 0); |
| 130 | |
| 131 | // Clicking to run the extension counts as granting it permission to run on |
| 132 | // the given tab. |
| 133 | // The extension may already have active tab at this point, but granting |
| 134 | // it twice is essentially a no-op. |
| 135 | TabHelper::FromWebContents(web_contents()) |
| 136 | ->active_tab_permission_granter() |
| 137 | ->GrantIfRequested(extension); |
| 138 | |
| 139 | RunPendingScriptsForExtension(extension); |
| 140 | web_request_blocked_.erase(extension->id()); |
| 141 | |
| 142 | // The extension ran, so we need to tell the ExtensionActionAPI that we no |
| 143 | // longer want to act. |
| 144 | NotifyChange(extension); |
rdevlin.cronin | e9c7112 | 2014-08-25 23:47:21 | [diff] [blame] | 145 | } |
| 146 | |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 147 | void ExtensionActionRunner::OnActiveTabPermissionGranted( |
| 148 | const Extension* extension) { |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 149 | if (!ignore_active_tab_granted_ && WantsToRun(extension)) |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 150 | RunBlockedActions(extension); |
| 151 | } |
| 152 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 153 | void ExtensionActionRunner::OnWebRequestBlocked(const Extension* extension) { |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 154 | web_request_blocked_.insert(extension->id()); |
| 155 | } |
| 156 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 157 | int ExtensionActionRunner::GetBlockedActions(const Extension* extension) { |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 158 | int blocked_actions = BLOCKED_ACTION_NONE; |
| 159 | if (web_request_blocked_.count(extension->id()) != 0) |
| 160 | blocked_actions |= BLOCKED_ACTION_WEB_REQUEST; |
| 161 | auto iter = pending_scripts_.find(extension->id()); |
| 162 | if (iter != pending_scripts_.end()) { |
| 163 | for (const PendingScript& script : iter->second) { |
| 164 | switch (script.run_location) { |
| 165 | case UserScript::DOCUMENT_START: |
| 166 | blocked_actions |= BLOCKED_ACTION_SCRIPT_AT_START; |
| 167 | break; |
| 168 | case UserScript::DOCUMENT_END: |
| 169 | case UserScript::DOCUMENT_IDLE: |
| 170 | case UserScript::BROWSER_DRIVEN: |
| 171 | blocked_actions |= BLOCKED_ACTION_SCRIPT_OTHER; |
| 172 | break; |
| 173 | case UserScript::UNDEFINED: |
| 174 | case UserScript::RUN_DEFERRED: |
| 175 | case UserScript::RUN_LOCATION_LAST: |
| 176 | NOTREACHED(); |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | return blocked_actions; |
| 182 | } |
| 183 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 184 | bool ExtensionActionRunner::WantsToRun(const Extension* extension) { |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 185 | return GetBlockedActions(extension) != BLOCKED_ACTION_NONE; |
[email protected] | e167058 | 2014-08-15 23:05:41 | [diff] [blame] | 186 | } |
| 187 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 188 | void ExtensionActionRunner::RunForTesting(const Extension* extension) { |
| 189 | if (WantsToRun(extension)) { |
| 190 | TabHelper::FromWebContents(web_contents()) |
| 191 | ->active_tab_permission_granter() |
| 192 | ->GrantIfRequested(extension); |
| 193 | } |
| 194 | } |
| 195 | |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame^] | 196 | PermissionsData::PageAccess |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 197 | ExtensionActionRunner::RequiresUserConsentForScriptInjection( |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 198 | const Extension* extension, |
| 199 | UserScript::InjectionType type) { |
| 200 | CHECK(extension); |
| 201 | |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 202 | // Allow the extension if it's been explicitly granted permission. |
| 203 | if (permitted_extensions_.count(extension->id()) > 0) |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame^] | 204 | return PermissionsData::PageAccess::kAllowed; |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 205 | |
| 206 | GURL url = web_contents()->GetVisibleURL(); |
Mikel Astiz | 2f127c42 | 2018-04-05 19:10:27 | [diff] [blame] | 207 | int tab_id = SessionTabHelper::IdForTab(web_contents()).id(); |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 208 | switch (type) { |
| 209 | case UserScript::CONTENT_SCRIPT: |
| 210 | return extension->permissions_data()->GetContentScriptAccess( |
rob | 889ceeb | 2016-01-25 19:41:08 | [diff] [blame] | 211 | extension, url, tab_id, nullptr); |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 212 | case UserScript::PROGRAMMATIC_SCRIPT: |
rob | 889ceeb | 2016-01-25 19:41:08 | [diff] [blame] | 213 | return extension->permissions_data()->GetPageAccess(extension, url, |
| 214 | tab_id, nullptr); |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 215 | } |
| 216 | |
| 217 | NOTREACHED(); |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame^] | 218 | return PermissionsData::PageAccess::kDenied; |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 219 | } |
| 220 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 221 | void ExtensionActionRunner::RequestScriptInjection( |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 222 | const Extension* extension, |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 223 | UserScript::RunLocation run_location, |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 224 | const base::Closure& callback) { |
| 225 | CHECK(extension); |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 226 | PendingScriptList& list = pending_scripts_[extension->id()]; |
| 227 | list.push_back(PendingScript(run_location, callback)); |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 228 | |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 229 | // If this was the first entry, we need to notify that a new extension wants |
| 230 | // to run. |
| 231 | if (list.size() == 1u) |
| 232 | NotifyChange(extension); |
rdevlin.cronin | b8dffe56 | 2015-02-07 00:58:01 | [diff] [blame] | 233 | |
| 234 | was_used_on_page_ = true; |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 235 | } |
| 236 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 237 | void ExtensionActionRunner::RunPendingScriptsForExtension( |
[email protected] | 11814f5 | 2014-05-23 06:50:35 | [diff] [blame] | 238 | const Extension* extension) { |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 239 | DCHECK(extension); |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 240 | |
| 241 | content::NavigationEntry* visible_entry = |
| 242 | web_contents()->GetController().GetVisibleEntry(); |
| 243 | // Refuse to run if there's no visible entry, because we have no idea of |
| 244 | // determining if it's the proper page. This should rarely, if ever, happen. |
| 245 | if (!visible_entry) |
[email protected] | 11814f5 | 2014-05-23 06:50:35 | [diff] [blame] | 246 | return; |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 247 | |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 248 | // We add this to the list of permitted extensions and erase pending entries |
| 249 | // *before* running them to guard against the crazy case where running the |
| 250 | // callbacks adds more entries. |
| 251 | permitted_extensions_.insert(extension->id()); |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 252 | |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 253 | PendingScriptMap::iterator iter = pending_scripts_.find(extension->id()); |
| 254 | if (iter == pending_scripts_.end()) |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 255 | return; |
| 256 | |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 257 | PendingScriptList scripts; |
| 258 | iter->second.swap(scripts); |
| 259 | pending_scripts_.erase(extension->id()); |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 260 | |
| 261 | // Run all pending injections for the given extension. |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 262 | for (PendingScript& pending_script : scripts) |
| 263 | pending_script.permit_script.Run(); |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 264 | } |
| 265 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 266 | void ExtensionActionRunner::OnRequestScriptInjectionPermission( |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 267 | const std::string& extension_id, |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 268 | UserScript::InjectionType script_type, |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 269 | UserScript::RunLocation run_location, |
avi | a2f4804a | 2015-12-24 23:11:13 | [diff] [blame] | 270 | int64_t request_id) { |
[email protected] | fdd2837 | 2014-08-21 02:27:26 | [diff] [blame] | 271 | if (!crx_file::id_util::IdIsValid(extension_id)) { |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 272 | NOTREACHED() << "'" << extension_id << "' is not a valid id."; |
| 273 | return; |
| 274 | } |
| 275 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 276 | const Extension* extension = ExtensionRegistry::Get(browser_context_) |
| 277 | ->enabled_extensions() |
| 278 | .GetByID(extension_id); |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 279 | // We shouldn't allow extensions which are no longer enabled to run any |
| 280 | // scripts. Ignore the request. |
| 281 | if (!extension) |
| 282 | return; |
| 283 | |
rdevlin.cronin | 1f87703 | 2015-02-20 00:12:42 | [diff] [blame] | 284 | ++num_page_requests_; |
| 285 | |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 286 | switch (RequiresUserConsentForScriptInjection(extension, script_type)) { |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame^] | 287 | case PermissionsData::PageAccess::kAllowed: |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 288 | PermitScriptInjection(request_id); |
| 289 | break; |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame^] | 290 | case PermissionsData::PageAccess::kWithheld: |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 291 | // This base::Unretained() is safe, because the callback is only invoked |
| 292 | // by this object. |
| 293 | RequestScriptInjection( |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 294 | extension, run_location, |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 295 | base::Bind(&ExtensionActionRunner::PermitScriptInjection, |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 296 | base::Unretained(this), request_id)); |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 297 | break; |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame^] | 298 | case PermissionsData::PageAccess::kDenied: |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 299 | // We should usually only get a "deny access" if the page changed (as the |
| 300 | // renderer wouldn't have requested permission if the answer was always |
| 301 | // "no"). Just let the request fizzle and die. |
| 302 | break; |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 303 | } |
| 304 | } |
| 305 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 306 | void ExtensionActionRunner::PermitScriptInjection(int64_t request_id) { |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 307 | // This only sends the response to the renderer - the process of adding the |
| 308 | // extension to the list of |permitted_extensions_| is done elsewhere. |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 309 | // TODO(devlin): Instead of sending this to all frames, we should include the |
| 310 | // routing_id in the permission request message, and send only to the proper |
| 311 | // frame (sending it to all frames doesn't hurt, but isn't as efficient). |
| 312 | web_contents()->SendToAllFrames(new ExtensionMsg_PermitScriptInjection( |
| 313 | MSG_ROUTING_NONE, // Routing id is set by the |web_contents|. |
| 314 | request_id)); |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 315 | } |
| 316 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 317 | void ExtensionActionRunner::NotifyChange(const Extension* extension) { |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 318 | ExtensionActionAPI* extension_action_api = |
| 319 | ExtensionActionAPI::Get(browser_context_); |
| 320 | ExtensionAction* extension_action = |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 321 | ExtensionActionManager::Get(browser_context_) |
| 322 | ->GetExtensionAction(*extension); |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 323 | // If the extension has an action, we need to notify that it's updated. |
| 324 | if (extension_action) { |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 325 | extension_action_api->NotifyChange(extension_action, web_contents(), |
| 326 | browser_context_); |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 327 | } |
| 328 | |
| 329 | // We also notify that page actions may have changed. |
| 330 | extension_action_api->NotifyPageActionsChanged(web_contents()); |
| 331 | } |
| 332 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 333 | void ExtensionActionRunner::LogUMA() const { |
rdevlin.cronin | b8dffe56 | 2015-02-07 00:58:01 | [diff] [blame] | 334 | // We only log the permitted extensions metric if the feature was used at all |
| 335 | // on the page, because otherwise the data will be boring. |
| 336 | if (was_used_on_page_) { |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 337 | UMA_HISTOGRAM_COUNTS_100( |
| 338 | "Extensions.ActiveScriptController.PermittedExtensions", |
| 339 | permitted_extensions_.size()); |
| 340 | UMA_HISTOGRAM_COUNTS_100( |
| 341 | "Extensions.ActiveScriptController.DeniedExtensions", |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 342 | pending_scripts_.size()); |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 343 | } |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 344 | } |
| 345 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 346 | void ExtensionActionRunner::ShowBlockedActionBubble( |
| 347 | const Extension* extension) { |
| 348 | Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); |
| 349 | ToolbarActionsBar* toolbar_actions_bar = |
| 350 | browser ? browser->window()->GetToolbarActionsBar() : nullptr; |
| 351 | if (toolbar_actions_bar) { |
| 352 | auto callback = |
| 353 | base::Bind(&ExtensionActionRunner::OnBlockedActionBubbleClosed, |
| 354 | weak_factory_.GetWeakPtr(), extension->id()); |
| 355 | if (default_bubble_close_action_for_testing_) { |
fdoray | 283082bd | 2016-06-02 20:18:46 | [diff] [blame] | 356 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 357 | FROM_HERE, |
tzik | 8d880ee | 2017-04-20 19:46:24 | [diff] [blame] | 358 | base::BindOnce(callback, *default_bubble_close_action_for_testing_)); |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 359 | } else { |
ricea | 91d6fc12 | 2016-08-30 08:47:14 | [diff] [blame] | 360 | toolbar_actions_bar->ShowToolbarActionBubble( |
Jinho Bang | b5216cec | 2018-01-17 19:43:11 | [diff] [blame] | 361 | std::make_unique<BlockedActionBubbleDelegate>(callback, |
ricea | 91d6fc12 | 2016-08-30 08:47:14 | [diff] [blame] | 362 | extension->id())); |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 363 | } |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | void ExtensionActionRunner::OnBlockedActionBubbleClosed( |
| 368 | const std::string& extension_id, |
| 369 | ToolbarActionsBarBubbleDelegate::CloseAction action) { |
| 370 | // If the user agreed to refresh the page, do so. |
| 371 | if (action == ToolbarActionsBarBubbleDelegate::CLOSE_EXECUTE) { |
| 372 | const Extension* extension = ExtensionRegistry::Get(browser_context_) |
| 373 | ->enabled_extensions() |
| 374 | .GetByID(extension_id); |
| 375 | if (!extension) |
| 376 | return; |
| 377 | { |
| 378 | // Ignore the active tab permission being granted because we don't want |
| 379 | // to run scripts right before we refresh the page. |
| 380 | base::AutoReset<bool> ignore_active_tab(&ignore_active_tab_granted_, |
| 381 | true); |
| 382 | TabHelper::FromWebContents(web_contents()) |
| 383 | ->active_tab_permission_granter() |
| 384 | ->GrantIfRequested(extension); |
| 385 | } |
toyoshim | 6142d96f | 2016-12-19 09:07:25 | [diff] [blame] | 386 | web_contents()->GetController().Reload(content::ReloadType::NORMAL, false); |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 387 | } |
| 388 | } |
| 389 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 390 | bool ExtensionActionRunner::OnMessageReceived( |
rdevlin.cronin | 45dca7f | 2015-06-08 19:47:03 | [diff] [blame] | 391 | const IPC::Message& message, |
| 392 | content::RenderFrameHost* render_frame_host) { |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 393 | bool handled = true; |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 394 | IPC_BEGIN_MESSAGE_MAP(ExtensionActionRunner, message) |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 395 | IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestScriptInjectionPermission, |
| 396 | OnRequestScriptInjectionPermission) |
| 397 | IPC_MESSAGE_UNHANDLED(handled = false) |
| 398 | IPC_END_MESSAGE_MAP() |
| 399 | return handled; |
| 400 | } |
| 401 | |
jam | 29737c4 | 2017-02-01 16:26:08 | [diff] [blame] | 402 | void ExtensionActionRunner::DidFinishNavigation( |
| 403 | content::NavigationHandle* navigation_handle) { |
| 404 | if (!navigation_handle->IsInMainFrame() || |
| 405 | !navigation_handle->HasCommitted() || |
eugenebut | a11672fb | 2017-03-07 17:13:51 | [diff] [blame] | 406 | navigation_handle->IsSameDocument()) { |
rdevlin.cronin | 5094223 | 2014-08-27 17:40:56 | [diff] [blame] | 407 | return; |
jam | 29737c4 | 2017-02-01 16:26:08 | [diff] [blame] | 408 | } |
rdevlin.cronin | 5094223 | 2014-08-27 17:40:56 | [diff] [blame] | 409 | |
| 410 | LogUMA(); |
rdevlin.cronin | 1f87703 | 2015-02-20 00:12:42 | [diff] [blame] | 411 | num_page_requests_ = 0; |
rdevlin.cronin | 5094223 | 2014-08-27 17:40:56 | [diff] [blame] | 412 | permitted_extensions_.clear(); |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 413 | pending_scripts_.clear(); |
| 414 | web_request_blocked_.clear(); |
rdevlin.cronin | b8dffe56 | 2015-02-07 00:58:01 | [diff] [blame] | 415 | was_used_on_page_ = false; |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 416 | weak_factory_.InvalidateWeakPtrs(); |
rdevlin.cronin | 5094223 | 2014-08-27 17:40:56 | [diff] [blame] | 417 | } |
| 418 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 419 | void ExtensionActionRunner::OnExtensionUnloaded( |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 420 | content::BrowserContext* browser_context, |
| 421 | const Extension* extension, |
limasdf | 0deef204 | 2017-05-03 19:17:17 | [diff] [blame] | 422 | UnloadedExtensionReason reason) { |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 423 | PendingScriptMap::iterator iter = pending_scripts_.find(extension->id()); |
| 424 | if (iter != pending_scripts_.end()) { |
| 425 | pending_scripts_.erase(iter); |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 426 | ExtensionActionAPI::Get(browser_context_) |
| 427 | ->NotifyPageActionsChanged(web_contents()); |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 428 | } |
| 429 | } |
| 430 | |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 431 | } // namespace extensions |