blob: 844847452e01560f684c3b2beab23604375637bf [file] [log] [blame]
[email protected]39ef0a7c52014-05-11 01:40:001// 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.cronin8408b4f92016-03-15 19:14:145#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_RUNNER_H_
6#define CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_RUNNER_H_
[email protected]39ef0a7c52014-05-11 01:40:007
avia2f4804a2015-12-24 23:11:138#include <stdint.h>
9
[email protected]39ef0a7c52014-05-11 01:40:0010#include <map>
11#include <set>
12#include <string>
[email protected]ac02ac52014-05-20 01:11:2613#include <vector>
[email protected]39ef0a7c52014-05-11 01:40:0014
[email protected]ac02ac52014-05-20 01:11:2615#include "base/callback.h"
[email protected]39ef0a7c52014-05-11 01:40:0016#include "base/compiler_specific.h"
avia2f4804a2015-12-24 23:11:1317#include "base/macros.h"
rdevlin.cronin4a78c48b2016-03-24 00:02:2918#include "base/memory/weak_ptr.h"
rdevlin.cronin91f162a12014-09-03 16:48:4019#include "base/scoped_observer.h"
rdevlin.cronin343fd102016-03-17 00:24:5420#include "chrome/browser/extensions/extension_action.h"
rdevlin.cronin4a78c48b2016-03-24 00:02:2921#include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h"
[email protected]39ef0a7c52014-05-11 01:40:0022#include "content/public/browser/web_contents_observer.h"
rdevlin.cronin8d034e52016-02-02 22:46:3223#include "extensions/browser/blocked_action_type.h"
Evan Stade75872a62019-09-06 21:17:3824#include "extensions/browser/extension_registry.h"
rdevlin.cronin6e7e5edc2014-08-29 16:23:2325#include "extensions/browser/extension_registry_observer.h"
[email protected]23a85362014-07-07 23:26:1926#include "extensions/common/permissions/permissions_data.h"
27#include "extensions/common/user_script.h"
[email protected]39ef0a7c52014-05-11 01:40:0028
29namespace content {
rdevlin.cronin699ca6ff2014-09-29 23:59:5730class BrowserContext;
[email protected]39ef0a7c52014-05-11 01:40:0031class WebContents;
32}
33
34namespace IPC {
35class Message;
36}
37
[email protected]39ef0a7c52014-05-11 01:40:0038namespace extensions {
39class Extension;
40
41// The provider for ExtensionActions corresponding to scripts which are actively
42// running or need permission.
rdevlin.cronin8408b4f92016-03-15 19:14:1443class ExtensionActionRunner : public content::WebContentsObserver,
44 public ExtensionRegistryObserver {
[email protected]39ef0a7c52014-05-11 01:40:0045 public:
Karan Bhatiaededa7cf2018-09-05 19:00:3046 enum class PageAccess {
47 RUN_ON_CLICK,
48 RUN_ON_SITE,
49 RUN_ON_ALL_SITES,
50 };
51
Devlin Cronin5c5abd242018-08-30 22:49:5052 class TestObserver {
53 public:
54 virtual void OnBlockedActionAdded() = 0;
55 };
56
rdevlin.cronin8408b4f92016-03-15 19:14:1457 explicit ExtensionActionRunner(content::WebContents* web_contents);
58 ~ExtensionActionRunner() override;
[email protected]39ef0a7c52014-05-11 01:40:0059
rdevlin.cronin343fd102016-03-17 00:24:5460 // Returns the ExtensionActionRunner for the given |web_contents|, or null
[email protected]eac223a2014-05-13 17:39:5761 // if one does not exist.
rdevlin.cronin8408b4f92016-03-15 19:14:1462 static ExtensionActionRunner* GetForWebContents(
[email protected]eac223a2014-05-13 17:39:5763 content::WebContents* web_contents);
64
rdevlin.cronin343fd102016-03-17 00:24:5465 // 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 Bhatiaededa7cf2018-09-05 19:00:3073 // 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.cronin343fd102016-03-17 00:24:5478
rdevlin.cronin8408b4f92016-03-15 19:14:1479 // Notifies the ExtensionActionRunner that an extension has been granted
[email protected]11814f52014-05-23 06:50:3580 // active tab permissions. This will run any pending injections for that
81 // extension.
82 void OnActiveTabPermissionGranted(const Extension* extension);
83
rdevlin.cronin8d034e52016-02-02 22:46:3284 // 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.cronin91f162a12014-09-03 16:48:4092 bool WantsToRun(const Extension* extension);
rdevlin.cronin50942232014-08-27 17:40:5693
rdevlin.cronin4a78c48b2016-03-24 00:02:2994 // 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.cronin1f877032015-02-20 00:12:4298 int num_page_requests() const { return num_page_requests_; }
99
rdevlin.cronin4a78c48b2016-03-24 00:02:29100 void set_default_bubble_close_action_for_testing(
dchengc963c7142016-04-08 03:55:22101 std::unique_ptr<ToolbarActionsBarBubbleDelegate::CloseAction> action) {
rdevlin.cronin4a78c48b2016-03-24 00:02:29102 default_bubble_close_action_for_testing_ = std::move(action);
103 }
Devlin Cronin5c5abd242018-08-30 22:49:50104 void set_observer_for_testing(TestObserver* observer) {
105 test_observer_ = observer;
106 }
rdevlin.cronin4a78c48b2016-03-24 00:02:29107
[email protected]23a85362014-07-07 23:26:19108#if defined(UNIT_TEST)
109 // Only used in tests.
Devlin Cronin3e532b82018-05-03 21:27:19110 PermissionsData::PageAccess RequiresUserConsentForScriptInjectionForTesting(
[email protected]23a85362014-07-07 23:26:19111 const Extension* extension,
112 UserScript::InjectionType type) {
113 return RequiresUserConsentForScriptInjection(extension, type);
114 }
115 void RequestScriptInjectionForTesting(const Extension* extension,
rdevlin.cronin8d034e52016-02-02 22:46:32116 UserScript::RunLocation run_location,
[email protected]23a85362014-07-07 23:26:19117 const base::Closure& callback) {
rdevlin.cronin8d034e52016-02-02 22:46:32118 return RequestScriptInjection(extension, run_location, callback);
[email protected]23a85362014-07-07 23:26:19119 }
Devlin Cronin516528f2018-07-23 20:16:45120 void ClearInjectionsForTesting(const Extension& extension) {
121 pending_scripts_.erase(extension.id());
122 }
[email protected]23a85362014-07-07 23:26:19123#endif // defined(UNIT_TEST)
124
[email protected]39ef0a7c52014-05-11 01:40:00125 private:
rdevlin.cronin8d034e52016-02-02 22:46:32126 struct PendingScript {
127 PendingScript(UserScript::RunLocation run_location,
128 const base::Closure& permit_script);
vmpstrb8aacbe2016-02-26 02:00:48129 PendingScript(const PendingScript& other);
rdevlin.cronin8d034e52016-02-02 22:46:32130 ~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]ac02ac52014-05-20 01:11:26141
[email protected]23a85362014-07-07 23:26:19142 // 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 Cronin3e532b82018-05-03 21:27:19145 PermissionsData::PageAccess RequiresUserConsentForScriptInjection(
[email protected]23a85362014-07-07 23:26:19146 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.cronin8d034e52016-02-02 22:46:32152 UserScript::RunLocation run_location,
[email protected]23a85362014-07-07 23:26:19153 const base::Closure& callback);
154
[email protected]11814f52014-05-23 06:50:35155 // Runs any pending injections for the corresponding extension.
rdevlin.croninacb745922016-02-17 20:37:44156 void RunPendingScriptsForExtension(const Extension* extension);
[email protected]11814f52014-05-23 06:50:35157
[email protected]ac2f89372014-06-23 21:44:25158 // Handle the RequestScriptInjectionPermission message.
avia2f4804a2015-12-24 23:11:13159 void OnRequestScriptInjectionPermission(const std::string& extension_id,
160 UserScript::InjectionType script_type,
rdevlin.cronin8d034e52016-02-02 22:46:32161 UserScript::RunLocation run_location,
avia2f4804a2015-12-24 23:11:13162 int64_t request_id);
[email protected]0d8d6972014-06-03 22:41:02163
164 // Grants permission for the given request to run.
avia2f4804a2015-12-24 23:11:13165 void PermitScriptInjection(int64_t request_id);
[email protected]39ef0a7c52014-05-11 01:40:00166
rdevlin.cronin699ca6ff2014-09-29 23:59:57167 // 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.cronin6e7e5edc2014-08-29 16:23:23171 // Log metrics.
172 void LogUMA() const;
173
rdevlin.cronin4a78c48b2016-03-24 00:02:29174 // Shows the bubble to prompt the user to refresh the page to run the blocked
Karan Bhatiaededa7cf2018-09-05 19:00:30175 // 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.cronin4a78c48b2016-03-24 00:02:29181
Karan Bhatiaededa7cf2018-09-05 19:00:30182 // Called when the blocked actions bubble invoked to run the extension action
183 // is closed.
184 void OnBlockedActionBubbleForRunActionClosed(
rdevlin.cronin4a78c48b2016-03-24 00:02:29185 const std::string& extension_id,
186 ToolbarActionsBarBubbleDelegate::CloseAction action);
187
Karan Bhatiaededa7cf2018-09-05 19:00:30188 // 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]ac02ac52014-05-20 01:11:26207 // content::WebContentsObserver implementation.
rdevlin.cronin45dca7f2015-06-08 19:47:03208 bool OnMessageReceived(const IPC::Message& message,
209 content::RenderFrameHost* render_frame_host) override;
jam29737c42017-02-01 16:26:08210 void DidFinishNavigation(
211 content::NavigationHandle* navigation_handle) override;
Devlin Cronin1d6343242018-06-05 02:16:41212 void WebContentsDestroyed() override;
[email protected]ac02ac52014-05-20 01:11:26213
rdevlin.cronin6e7e5edc2014-08-29 16:23:23214 // ExtensionRegistryObserver:
dchengae36a4a2014-10-21 12:36:36215 void OnExtensionUnloaded(content::BrowserContext* browser_context,
216 const Extension* extension,
limasdf0deef2042017-05-03 19:17:17217 UnloadedExtensionReason reason) override;
[email protected]39ef0a7c52014-05-11 01:40:00218
rdevlin.cronin1f877032015-02-20 00:12:42219 // 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.cronin699ca6ff2014-09-29 23:59:57224 // The associated browser context.
225 content::BrowserContext* browser_context_;
226
rdevlin.croninb8dffe562015-02-07 00:58:01227 // 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]39ef0a7c52014-05-11 01:40:00230
rdevlin.cronin8d034e52016-02-02 22:46:32231 // 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]39ef0a7c52014-05-11 01:40:00236
[email protected]ac02ac52014-05-20 01:11:26237 // 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]39ef0a7c52014-05-11 01:40:00242
rdevlin.cronin4a78c48b2016-03-24 00:02:29243 // 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.
dchengc963c7142016-04-08 03:55:22248 std::unique_ptr<ToolbarActionsBarBubbleDelegate::CloseAction>
rdevlin.cronin4a78c48b2016-03-24 00:02:29249 default_bubble_close_action_for_testing_;
250
Devlin Cronin5c5abd242018-08-30 22:49:50251 TestObserver* test_observer_;
252
rdevlin.cronin6e7e5edc2014-08-29 16:23:23253 ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
Evan Stade75872a62019-09-06 21:17:38254 extension_registry_observer_{this};
rdevlin.cronin6e7e5edc2014-08-29 16:23:23255
Jeremy Roman495db682019-07-12 16:03:24256 base::WeakPtrFactory<ExtensionActionRunner> weak_factory_{this};
rdevlin.cronin4a78c48b2016-03-24 00:02:29257
rdevlin.cronin8408b4f92016-03-15 19:14:14258 DISALLOW_COPY_AND_ASSIGN(ExtensionActionRunner);
[email protected]39ef0a7c52014-05-11 01:40:00259};
260
261} // namespace extensions
262
rdevlin.cronin8408b4f92016-03-15 19:14:14263#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_RUNNER_H_