Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Side by Side Diff: chrome/browser/extensions/active_script_controller.cc

Issue 286003004: Block tabs.executeScript() from executing until user grants permission (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Epic master rebase Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/active_script_controller.h" 5 #include "chrome/browser/extensions/active_script_controller.h"
6 6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/memory/scoped_ptr.h"
7 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
8 #include "base/stl_util.h" 11 #include "base/stl_util.h"
9 #include "chrome/browser/extensions/extension_action.h" 12 #include "chrome/browser/extensions/extension_action.h"
10 #include "chrome/browser/extensions/location_bar_controller.h" 13 #include "chrome/browser/extensions/location_bar_controller.h"
11 #include "chrome/browser/extensions/tab_helper.h" 14 #include "chrome/browser/extensions/tab_helper.h"
12 #include "chrome/common/extensions/api/extension_action/action_info.h" 15 #include "chrome/common/extensions/api/extension_action/action_info.h"
13 #include "content/public/browser/navigation_controller.h" 16 #include "content/public/browser/navigation_controller.h"
14 #include "content/public/browser/navigation_entry.h" 17 #include "content/public/browser/navigation_entry.h"
18 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/web_contents.h" 19 #include "content/public/browser/web_contents.h"
16 #include "extensions/browser/extension_registry.h" 20 #include "extensions/browser/extension_registry.h"
17 #include "extensions/common/extension.h" 21 #include "extensions/common/extension.h"
18 #include "extensions/common/extension_messages.h" 22 #include "extensions/common/extension_messages.h"
19 #include "extensions/common/extension_set.h" 23 #include "extensions/common/extension_set.h"
20 #include "extensions/common/feature_switch.h" 24 #include "extensions/common/feature_switch.h"
21 #include "extensions/common/permissions/permissions_data.h" 25 #include "extensions/common/permissions/permissions_data.h"
22 #include "ipc/ipc_message_macros.h" 26 #include "ipc/ipc_message_macros.h"
23 27
24 namespace extensions { 28 namespace extensions {
25 29
30 ActiveScriptController::PendingRequest::PendingRequest() :
31 page_id(-1) {
32 }
33
34 ActiveScriptController::PendingRequest::PendingRequest(
35 const base::Closure& closure,
36 int page_id)
37 : closure(closure),
38 page_id(page_id) {
39 }
40
41 ActiveScriptController::PendingRequest::~PendingRequest() {
42 }
43
26 ActiveScriptController::ActiveScriptController( 44 ActiveScriptController::ActiveScriptController(
27 content::WebContents* web_contents) 45 content::WebContents* web_contents)
28 : content::WebContentsObserver(web_contents), 46 : content::WebContentsObserver(web_contents),
29 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) { 47 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) {
48 CHECK(web_contents);
30 } 49 }
31 50
32 ActiveScriptController::~ActiveScriptController() { 51 ActiveScriptController::~ActiveScriptController() {
33 LogUMA(); 52 LogUMA();
34 } 53 }
35 54
36 // static 55 // static
37 ActiveScriptController* ActiveScriptController::GetForWebContents( 56 ActiveScriptController* ActiveScriptController::GetForWebContents(
38 content::WebContents* web_contents) { 57 content::WebContents* web_contents) {
39 if (!web_contents) 58 if (!web_contents)
40 return NULL; 59 return NULL;
41 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); 60 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents);
42 if (!tab_helper) 61 if (!tab_helper)
43 return NULL; 62 return NULL;
44 LocationBarController* location_bar_controller = 63 LocationBarController* location_bar_controller =
45 tab_helper->location_bar_controller(); 64 tab_helper->location_bar_controller();
46 // This should never be NULL. 65 // This should never be NULL.
47 DCHECK(location_bar_controller); 66 DCHECK(location_bar_controller);
48 return location_bar_controller->active_script_controller(); 67 return location_bar_controller->active_script_controller();
49 } 68 }
50 69
51 void ActiveScriptController::NotifyScriptExecuting( 70 bool ActiveScriptController::RequiresUserConsentForScriptInjection(
52 const std::string& extension_id, int page_id) { 71 const Extension* extension) {
53 content::NavigationEntry* visible_entry = 72 CHECK(extension);
54 web_contents()->GetController().GetVisibleEntry(); 73 if (!PermissionsData::RequiresActionForScriptExecution(extension))
55 if (!visible_entry || 74 return false;
56 extensions_executing_scripts_.count(extension_id) ||
57 visible_entry->GetPageID() != page_id) {
58 return;
59 }
60 75
61 const Extension* extension = 76 // If the feature is not enabled, we automatically allow all extensions to
62 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) 77 // run scripts.
63 ->enabled_extensions().GetByID(extension_id); 78 if (!enabled_)
64 if (extension && 79 permitted_extensions_.insert(extension->id());
65 PermissionsData::RequiresActionForScriptExecution(extension)) { 80
66 extensions_executing_scripts_.insert(extension_id); 81 return permitted_extensions_.count(extension->id()) == 0;
82 }
83
84 void ActiveScriptController::RequestScriptInjection(
85 const Extension* extension,
86 int page_id,
87 const base::Closure& callback) {
88 CHECK(extension);
89 PendingRequestList& list = pending_requests_[extension->id()];
90 list.push_back(PendingRequest(callback, page_id));
91
92 // If this was the first entry, notify the location bar that there's a new
93 // icon.
94 if (list.size() == 1u)
67 LocationBarController::NotifyChange(web_contents()); 95 LocationBarController::NotifyChange(web_contents());
68 }
69 } 96 }
70 97
71 void ActiveScriptController::OnAdInjectionDetected( 98 void ActiveScriptController::OnAdInjectionDetected(
72 const std::vector<std::string> ad_injectors) { 99 const std::vector<std::string> ad_injectors) {
73 size_t num_preventable_ad_injectors = 100 size_t num_preventable_ad_injectors =
74 base::STLSetIntersection<std::set<std::string> >( 101 base::STLSetIntersection<std::set<std::string> >(
75 ad_injectors, extensions_executing_scripts_).size(); 102 ad_injectors, permitted_extensions_).size();
76 103
77 UMA_HISTOGRAM_COUNTS_100( 104 UMA_HISTOGRAM_COUNTS_100(
78 "Extensions.ActiveScriptController.PreventableAdInjectors", 105 "Extensions.ActiveScriptController.PreventableAdInjectors",
79 num_preventable_ad_injectors); 106 num_preventable_ad_injectors);
80 UMA_HISTOGRAM_COUNTS_100( 107 UMA_HISTOGRAM_COUNTS_100(
81 "Extensions.ActiveScriptController.PreventableAdInjectors", 108 "Extensions.ActiveScriptController.UnpreventableAdInjectors",
82 ad_injectors.size() - num_preventable_ad_injectors); 109 ad_injectors.size() - num_preventable_ad_injectors);
83 } 110 }
84 111
85 ExtensionAction* ActiveScriptController::GetActionForExtension( 112 ExtensionAction* ActiveScriptController::GetActionForExtension(
86 const Extension* extension) { 113 const Extension* extension) {
87 if (!enabled_ || extensions_executing_scripts_.count(extension->id()) == 0) 114 if (!enabled_ || pending_requests_.count(extension->id()) == 0)
88 return NULL; // No action for this extension. 115 return NULL; // No action for this extension.
89 116
90 ActiveScriptMap::iterator existing = 117 ActiveScriptMap::iterator existing =
91 active_script_actions_.find(extension->id()); 118 active_script_actions_.find(extension->id());
92 if (existing != active_script_actions_.end()) 119 if (existing != active_script_actions_.end())
93 return existing->second.get(); 120 return existing->second.get();
94 121
95 linked_ptr<ExtensionAction> action(new ExtensionAction( 122 linked_ptr<ExtensionAction> action(new ExtensionAction(
96 extension->id(), ActionInfo::TYPE_PAGE, ActionInfo())); 123 extension->id(), ActionInfo::TYPE_PAGE, ActionInfo()));
97 action->SetTitle(ExtensionAction::kDefaultTabId, extension->name()); 124 action->SetTitle(ExtensionAction::kDefaultTabId, extension->name());
98 action->SetIsVisible(ExtensionAction::kDefaultTabId, true); 125 action->SetIsVisible(ExtensionAction::kDefaultTabId, true);
99 126
100 const ActionInfo* action_info = ActionInfo::GetPageActionInfo(extension); 127 const ActionInfo* action_info = ActionInfo::GetPageActionInfo(extension);
101 if (!action_info) 128 if (!action_info)
102 action_info = ActionInfo::GetBrowserActionInfo(extension); 129 action_info = ActionInfo::GetBrowserActionInfo(extension);
103 130
104 if (action_info && !action_info->default_icon.empty()) { 131 if (action_info && !action_info->default_icon.empty()) {
105 action->set_default_icon( 132 action->set_default_icon(
106 make_scoped_ptr(new ExtensionIconSet(action_info->default_icon))); 133 make_scoped_ptr(new ExtensionIconSet(action_info->default_icon)));
107 } 134 }
108 135
109 active_script_actions_[extension->id()] = action; 136 active_script_actions_[extension->id()] = action;
110 return action.get(); 137 return action.get();
111 } 138 }
112 139
113 LocationBarController::Action ActiveScriptController::OnClicked( 140 LocationBarController::Action ActiveScriptController::OnClicked(
114 const Extension* extension) { 141 const Extension* extension) {
115 DCHECK(extensions_executing_scripts_.count(extension->id()) > 0); 142 DCHECK(extension);
143 PendingRequestMap::iterator iter =
144 pending_requests_.find(extension->id());
145 DCHECK(iter != pending_requests_.end());
146
147 content::NavigationEntry* visible_entry =
148 web_contents()->GetController().GetVisibleEntry();
149 // Refuse to run if there's no visible entry, because we have no idea of
150 // determining if it's the proper page. This should rarely, if ever, happen.
151 if (!visible_entry)
152 return LocationBarController::ACTION_NONE;
153
154 int page_id = visible_entry->GetPageID();
155
156 // We add this to the list of permitted extensions and erase pending entries
157 // *before* running them to guard against the crazy case where running the
158 // callbacks adds more entries.
159 permitted_extensions_.insert(extension->id());
160 PendingRequestList requests;
161 iter->second.swap(requests);
162 pending_requests_.erase(extension->id());
163
164 // Run all pending injections for the given extension.
165 for (PendingRequestList::iterator request = requests.begin();
166 request != requests.end();
167 ++request) {
168 // Only run if it's on the proper page.
169 if (request->page_id == page_id)
170 request->closure.Run();
171 }
172
173 // Inform the location bar that the action is now gone.
174 LocationBarController::NotifyChange(web_contents());
175
116 return LocationBarController::ACTION_NONE; 176 return LocationBarController::ACTION_NONE;
117 } 177 }
118 178
119 void ActiveScriptController::OnNavigated() { 179 void ActiveScriptController::OnNavigated() {
120 LogUMA(); 180 LogUMA();
121 extensions_executing_scripts_.clear(); 181 permitted_extensions_.clear();
182 pending_requests_.clear();
183 }
184
185 void ActiveScriptController::OnNotifyExtensionScriptExecution(
186 const std::string& extension_id,
187 int page_id) {
188 if (!Extension::IdIsValid(extension_id)) {
189 NOTREACHED() << "'" << extension_id << "' is not a valid id.";
190 return;
191 }
192
193 const Extension* extension =
194 ExtensionRegistry::Get(web_contents()->GetBrowserContext())
195 ->enabled_extensions().GetByID(extension_id);
196 // We shouldn't allow extensions which are no longer enabled to run any
197 // scripts. Ignore the request.
198 if (!extension)
199 return;
200
201 // Right now, we allow all content scripts to execute, but notify the
202 // controller of them.
203 // TODO(rdevlin.cronin): Fix this in a future CL.
204 if (RequiresUserConsentForScriptInjection(extension))
205 RequestScriptInjection(extension, page_id, base::Bind(&base::DoNothing));
122 } 206 }
123 207
124 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { 208 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) {
125 bool handled = true; 209 bool handled = true;
126 IPC_BEGIN_MESSAGE_MAP(ActiveScriptController, message) 210 IPC_BEGIN_MESSAGE_MAP(ActiveScriptController, message)
127 IPC_MESSAGE_HANDLER(ExtensionHostMsg_NotifyExtensionScriptExecution, 211 IPC_MESSAGE_HANDLER(ExtensionHostMsg_NotifyExtensionScriptExecution,
128 OnNotifyExtensionScriptExecution) 212 OnNotifyExtensionScriptExecution)
129 IPC_MESSAGE_UNHANDLED(handled = false) 213 IPC_MESSAGE_UNHANDLED(handled = false)
130 IPC_END_MESSAGE_MAP() 214 IPC_END_MESSAGE_MAP()
131 return handled; 215 return handled;
132 } 216 }
133 217
134 void ActiveScriptController::OnNotifyExtensionScriptExecution(
135 const std::string& extension_id,
136 int page_id) {
137 if (!Extension::IdIsValid(extension_id)) {
138 NOTREACHED() << "'" << extension_id << "' is not a valid id.";
139 return;
140 }
141 NotifyScriptExecuting(extension_id, page_id);
142 }
143
144 void ActiveScriptController::LogUMA() const { 218 void ActiveScriptController::LogUMA() const {
145 UMA_HISTOGRAM_COUNTS_100( 219 UMA_HISTOGRAM_COUNTS_100(
146 "Extensions.ActiveScriptController.ShownActiveScriptsOnPage", 220 "Extensions.ActiveScriptController.ShownActiveScriptsOnPage",
147 extensions_executing_scripts_.size()); 221 pending_requests_.size());
222
223 // We only log the permitted extensions metric if the feature is enabled,
224 // because otherwise the data will be boring (100% allowed).
225 if (enabled_) {
226 UMA_HISTOGRAM_COUNTS_100(
227 "Extensions.ActiveScriptController.PermittedExtensions",
228 permitted_extensions_.size());
229 UMA_HISTOGRAM_COUNTS_100(
230 "Extensions.ActiveScriptController.DeniedExtensions",
231 pending_requests_.size());
232 }
148 } 233 }
149 234
150 } // namespace extensions 235 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698