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