[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 | #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_RUNNER_H_ |
| 6 | #define CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_RUNNER_H_ |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 7 | |
avi | a2f4804a | 2015-12-24 23:11:13 | [diff] [blame] | 8 | #include <stdint.h> |
| 9 | |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 10 | #include <map> |
| 11 | #include <set> |
| 12 | #include <string> |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 13 | #include <vector> |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 14 | |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 15 | #include "base/callback.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 16 | #include "base/compiler_specific.h" |
avi | a2f4804a | 2015-12-24 23:11:13 | [diff] [blame] | 17 | #include "base/macros.h" |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 18 | #include "base/memory/weak_ptr.h" |
rdevlin.cronin | 91f162a1 | 2014-09-03 16:48:40 | [diff] [blame] | 19 | #include "base/scoped_observer.h" |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 20 | #include "chrome/browser/extensions/extension_action.h" |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 21 | #include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 22 | #include "content/public/browser/web_contents_observer.h" |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 23 | #include "extensions/browser/blocked_action_type.h" |
Evan Stade | 75872a6 | 2019-09-06 21:17:38 | [diff] [blame] | 24 | #include "extensions/browser/extension_registry.h" |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 25 | #include "extensions/browser/extension_registry_observer.h" |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 26 | #include "extensions/common/permissions/permissions_data.h" |
| 27 | #include "extensions/common/user_script.h" |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 28 | |
| 29 | namespace content { |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 30 | class BrowserContext; |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 31 | class WebContents; |
| 32 | } |
| 33 | |
| 34 | namespace IPC { |
| 35 | class Message; |
| 36 | } |
| 37 | |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 38 | namespace extensions { |
| 39 | class Extension; |
| 40 | |
| 41 | // The provider for ExtensionActions corresponding to scripts which are actively |
| 42 | // running or need permission. |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 43 | class ExtensionActionRunner : public content::WebContentsObserver, |
| 44 | public ExtensionRegistryObserver { |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 45 | public: |
Karan Bhatia | ededa7cf | 2018-09-05 19:00:30 | [diff] [blame] | 46 | enum class PageAccess { |
| 47 | RUN_ON_CLICK, |
| 48 | RUN_ON_SITE, |
| 49 | RUN_ON_ALL_SITES, |
| 50 | }; |
| 51 | |
Devlin Cronin | 5c5abd24 | 2018-08-30 22:49:50 | [diff] [blame] | 52 | class TestObserver { |
| 53 | public: |
| 54 | virtual void OnBlockedActionAdded() = 0; |
| 55 | }; |
| 56 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 57 | explicit ExtensionActionRunner(content::WebContents* web_contents); |
| 58 | ~ExtensionActionRunner() override; |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 59 | |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 60 | // Returns the ExtensionActionRunner for the given |web_contents|, or null |
[email protected] | eac223a | 2014-05-13 17:39:57 | [diff] [blame] | 61 | // if one does not exist. |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 62 | static ExtensionActionRunner* GetForWebContents( |
[email protected] | eac223a | 2014-05-13 17:39:57 | [diff] [blame] | 63 | content::WebContents* web_contents); |
| 64 | |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 65 | // Executes the action for the given |extension| and returns any further |
| 66 | // action (like showing a popup) that should be taken. If |
| 67 | // |grant_tab_permissions| is true, this will also grant activeTab to the |
| 68 | // extension (so this should only be done if this is through a direct user |
| 69 | // action). |
| 70 | ExtensionAction::ShowAction RunAction(const Extension* extension, |
| 71 | bool grant_tab_permissions); |
| 72 | |
Karan Bhatia | ededa7cf | 2018-09-05 19:00:30 | [diff] [blame] | 73 | // Notifies the ExtensionActionRunner that the page access for |extension| has |
| 74 | // changed. |
| 75 | void HandlePageAccessModified(const Extension* extension, |
| 76 | PageAccess current_access, |
| 77 | PageAccess new_access); |
rdevlin.cronin | 343fd10 | 2016-03-17 00:24:54 | [diff] [blame] | 78 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 79 | // Notifies the ExtensionActionRunner that an extension has been granted |
[email protected] | 11814f5 | 2014-05-23 06:50:35 | [diff] [blame] | 80 | // active tab permissions. This will run any pending injections for that |
| 81 | // extension. |
| 82 | void OnActiveTabPermissionGranted(const Extension* extension); |
| 83 | |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 84 | // Called when a webRequest event for the given |extension| was blocked. |
| 85 | void OnWebRequestBlocked(const Extension* extension); |
| 86 | |
| 87 | // Returns a bitmask of BlockedActionType for the actions that have been |
| 88 | // blocked for the given extension. |
| 89 | int GetBlockedActions(const Extension* extension); |
| 90 | |
| 91 | // Returns true if the given |extension| has any blocked actions. |
rdevlin.cronin | 91f162a1 | 2014-09-03 16:48:40 | [diff] [blame] | 92 | bool WantsToRun(const Extension* extension); |
rdevlin.cronin | 5094223 | 2014-08-27 17:40:56 | [diff] [blame] | 93 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 94 | // Runs any blocked actions the extension has, but does not handle any page |
| 95 | // refreshes for document_start/webRequest. |
| 96 | void RunForTesting(const Extension* extension); |
| 97 | |
rdevlin.cronin | 1f87703 | 2015-02-20 00:12:42 | [diff] [blame] | 98 | int num_page_requests() const { return num_page_requests_; } |
| 99 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 100 | void set_default_bubble_close_action_for_testing( |
dcheng | c963c714 | 2016-04-08 03:55:22 | [diff] [blame] | 101 | std::unique_ptr<ToolbarActionsBarBubbleDelegate::CloseAction> action) { |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 102 | default_bubble_close_action_for_testing_ = std::move(action); |
| 103 | } |
Devlin Cronin | 5c5abd24 | 2018-08-30 22:49:50 | [diff] [blame] | 104 | void set_observer_for_testing(TestObserver* observer) { |
| 105 | test_observer_ = observer; |
| 106 | } |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 107 | |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 108 | #if defined(UNIT_TEST) |
| 109 | // Only used in tests. |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame] | 110 | PermissionsData::PageAccess RequiresUserConsentForScriptInjectionForTesting( |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 111 | const Extension* extension, |
| 112 | UserScript::InjectionType type) { |
| 113 | return RequiresUserConsentForScriptInjection(extension, type); |
| 114 | } |
| 115 | void RequestScriptInjectionForTesting(const Extension* extension, |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 116 | UserScript::RunLocation run_location, |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 117 | const base::Closure& callback) { |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 118 | return RequestScriptInjection(extension, run_location, callback); |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 119 | } |
Devlin Cronin | 516528f | 2018-07-23 20:16:45 | [diff] [blame] | 120 | void ClearInjectionsForTesting(const Extension& extension) { |
| 121 | pending_scripts_.erase(extension.id()); |
| 122 | } |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 123 | #endif // defined(UNIT_TEST) |
| 124 | |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 125 | private: |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 126 | struct PendingScript { |
| 127 | PendingScript(UserScript::RunLocation run_location, |
| 128 | const base::Closure& permit_script); |
vmpstr | b8aacbe | 2016-02-26 02:00:48 | [diff] [blame] | 129 | PendingScript(const PendingScript& other); |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 130 | ~PendingScript(); |
| 131 | |
| 132 | // The run location that the script wants to inject at. |
| 133 | UserScript::RunLocation run_location; |
| 134 | |
| 135 | // The callback to run when the script is permitted by the user. |
| 136 | base::Closure permit_script; |
| 137 | }; |
| 138 | |
| 139 | using PendingScriptList = std::vector<PendingScript>; |
| 140 | using PendingScriptMap = std::map<std::string, PendingScriptList>; |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 141 | |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 142 | // Returns true if the extension requesting script injection requires |
| 143 | // user consent. If this is true, the caller should then register a request |
| 144 | // via RequestScriptInjection(). |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame] | 145 | PermissionsData::PageAccess RequiresUserConsentForScriptInjection( |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 146 | const Extension* extension, |
| 147 | UserScript::InjectionType type); |
| 148 | |
| 149 | // |callback|. The only assumption that can be made about when (or if) |
| 150 | // |callback| is run is that, if it is run, it will run on the current page. |
| 151 | void RequestScriptInjection(const Extension* extension, |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 152 | UserScript::RunLocation run_location, |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 153 | const base::Closure& callback); |
| 154 | |
[email protected] | 11814f5 | 2014-05-23 06:50:35 | [diff] [blame] | 155 | // Runs any pending injections for the corresponding extension. |
rdevlin.cronin | acb74592 | 2016-02-17 20:37:44 | [diff] [blame] | 156 | void RunPendingScriptsForExtension(const Extension* extension); |
[email protected] | 11814f5 | 2014-05-23 06:50:35 | [diff] [blame] | 157 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 158 | // Handle the RequestScriptInjectionPermission message. |
avi | a2f4804a | 2015-12-24 23:11:13 | [diff] [blame] | 159 | void OnRequestScriptInjectionPermission(const std::string& extension_id, |
| 160 | UserScript::InjectionType script_type, |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 161 | UserScript::RunLocation run_location, |
avi | a2f4804a | 2015-12-24 23:11:13 | [diff] [blame] | 162 | int64_t request_id); |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 163 | |
| 164 | // Grants permission for the given request to run. |
avi | a2f4804a | 2015-12-24 23:11:13 | [diff] [blame] | 165 | void PermitScriptInjection(int64_t request_id); |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 166 | |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 167 | // Notifies the ExtensionActionAPI of a change (either that an extension now |
| 168 | // wants permission to run, or that it has been run). |
| 169 | void NotifyChange(const Extension* extension); |
| 170 | |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 171 | // Log metrics. |
| 172 | void LogUMA() const; |
| 173 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 174 | // Shows the bubble to prompt the user to refresh the page to run the blocked |
Karan Bhatia | ededa7cf | 2018-09-05 19:00:30 | [diff] [blame] | 175 | // actions for the given |extension|. |callback| is invoked when the bubble is |
| 176 | // closed. |
| 177 | void ShowBlockedActionBubble( |
| 178 | const Extension* extension, |
| 179 | const base::Callback<void(ToolbarActionsBarBubbleDelegate::CloseAction)>& |
| 180 | callback); |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 181 | |
Karan Bhatia | ededa7cf | 2018-09-05 19:00:30 | [diff] [blame] | 182 | // Called when the blocked actions bubble invoked to run the extension action |
| 183 | // is closed. |
| 184 | void OnBlockedActionBubbleForRunActionClosed( |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 185 | const std::string& extension_id, |
| 186 | ToolbarActionsBarBubbleDelegate::CloseAction action); |
| 187 | |
Karan Bhatia | ededa7cf | 2018-09-05 19:00:30 | [diff] [blame] | 188 | // Called when the blocked actions bubble invoked for the page access grant is |
| 189 | // closed. |
| 190 | void OnBlockedActionBubbleForPageAccessGrantClosed( |
| 191 | const std::string& extension_id, |
| 192 | const GURL& page_url, |
| 193 | PageAccess current_access, |
| 194 | PageAccess new_access, |
| 195 | ToolbarActionsBarBubbleDelegate::CloseAction action); |
| 196 | |
| 197 | // Handles permission changes necessary for page access modification of the |
| 198 | // |extension|. |
| 199 | void UpdatePageAccessSettings(const Extension* extension, |
| 200 | PageAccess current_access, |
| 201 | PageAccess new_access); |
| 202 | |
| 203 | // Runs any actions that were blocked for the given |extension|. As a |
| 204 | // requirement, this will grant activeTab permission to the extension. |
| 205 | void RunBlockedActions(const Extension* extension); |
| 206 | |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 207 | // content::WebContentsObserver implementation. |
rdevlin.cronin | 45dca7f | 2015-06-08 19:47:03 | [diff] [blame] | 208 | bool OnMessageReceived(const IPC::Message& message, |
| 209 | content::RenderFrameHost* render_frame_host) override; |
jam | 29737c4 | 2017-02-01 16:26:08 | [diff] [blame] | 210 | void DidFinishNavigation( |
| 211 | content::NavigationHandle* navigation_handle) override; |
Devlin Cronin | 1d634324 | 2018-06-05 02:16:41 | [diff] [blame] | 212 | void WebContentsDestroyed() override; |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 213 | |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 214 | // ExtensionRegistryObserver: |
dcheng | ae36a4a | 2014-10-21 12:36:36 | [diff] [blame] | 215 | void OnExtensionUnloaded(content::BrowserContext* browser_context, |
| 216 | const Extension* extension, |
limasdf | 0deef204 | 2017-05-03 19:17:17 | [diff] [blame] | 217 | UnloadedExtensionReason reason) override; |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 218 | |
rdevlin.cronin | 1f87703 | 2015-02-20 00:12:42 | [diff] [blame] | 219 | // The total number of requests from the renderer on the current page, |
| 220 | // including any that are pending or were immediately granted. |
| 221 | // Right now, used only in tests. |
| 222 | int num_page_requests_; |
| 223 | |
rdevlin.cronin | 699ca6ff | 2014-09-29 23:59:57 | [diff] [blame] | 224 | // The associated browser context. |
| 225 | content::BrowserContext* browser_context_; |
| 226 | |
rdevlin.cronin | b8dffe56 | 2015-02-07 00:58:01 | [diff] [blame] | 227 | // Whether or not the feature was used for any extensions. This may not be the |
| 228 | // case if the user never enabled the scripts-require-action flag. |
| 229 | bool was_used_on_page_; |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 230 | |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 231 | // The map of extension_id:pending_request of all pending script requests. |
| 232 | PendingScriptMap pending_scripts_; |
| 233 | |
| 234 | // A set of ids for which the webRequest API was blocked on the page. |
| 235 | std::set<std::string> web_request_blocked_; |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 236 | |
[email protected] | ac02ac5 | 2014-05-20 01:11:26 | [diff] [blame] | 237 | // The extensions which have been granted permission to run on the given page. |
| 238 | // TODO(rdevlin.cronin): Right now, this just keeps track of extensions that |
| 239 | // have been permitted to run on the page via this interface. Instead, it |
| 240 | // should incorporate more fully with ActiveTab. |
| 241 | std::set<std::string> permitted_extensions_; |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 242 | |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 243 | // If true, ignore active tab being granted rather than running pending |
| 244 | // actions. |
| 245 | bool ignore_active_tab_granted_; |
| 246 | |
| 247 | // If non-null, the bubble action to simulate for testing. |
dcheng | c963c714 | 2016-04-08 03:55:22 | [diff] [blame] | 248 | std::unique_ptr<ToolbarActionsBarBubbleDelegate::CloseAction> |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 249 | default_bubble_close_action_for_testing_; |
| 250 | |
Devlin Cronin | 5c5abd24 | 2018-08-30 22:49:50 | [diff] [blame] | 251 | TestObserver* test_observer_; |
| 252 | |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 253 | ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> |
Evan Stade | 75872a6 | 2019-09-06 21:17:38 | [diff] [blame] | 254 | extension_registry_observer_{this}; |
rdevlin.cronin | 6e7e5edc | 2014-08-29 16:23:23 | [diff] [blame] | 255 | |
Jeremy Roman | 495db68 | 2019-07-12 16:03:24 | [diff] [blame] | 256 | base::WeakPtrFactory<ExtensionActionRunner> weak_factory_{this}; |
rdevlin.cronin | 4a78c48b | 2016-03-24 00:02:29 | [diff] [blame] | 257 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 258 | DISALLOW_COPY_AND_ASSIGN(ExtensionActionRunner); |
[email protected] | 39ef0a7c5 | 2014-05-11 01:40:00 | [diff] [blame] | 259 | }; |
| 260 | |
| 261 | } // namespace extensions |
| 262 | |
rdevlin.cronin | 8408b4f9 | 2016-03-15 19:14:14 | [diff] [blame] | 263 | #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_RUNNER_H_ |