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

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: 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
26 ActiveScriptController::ActiveScriptController( 30 ActiveScriptController::ActiveScriptController(
27 content::WebContents* web_contents) 31 content::WebContents* web_contents)
28 : content::WebContentsObserver(web_contents), 32 : content::WebContentsObserver(web_contents),
29 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) { 33 enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) {
34 CHECK(web_contents);
30 } 35 }
31 36
32 ActiveScriptController::~ActiveScriptController() { 37 ActiveScriptController::~ActiveScriptController() {
33 LogUMA(); 38 LogUMA();
34 } 39 }
35 40
36 // static 41 // static
37 ActiveScriptController* ActiveScriptController::GetForWebContents( 42 ActiveScriptController* ActiveScriptController::GetForWebContents(
38 content::WebContents* web_contents) { 43 content::WebContents* web_contents) {
39 if (!web_contents) 44 if (!web_contents)
40 return NULL; 45 return NULL;
41 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); 46 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents);
42 if (!tab_helper) 47 if (!tab_helper)
43 return NULL; 48 return NULL;
44 LocationBarController* location_bar_controller = 49 LocationBarController* location_bar_controller =
45 tab_helper->location_bar_controller(); 50 tab_helper->location_bar_controller();
46 // This should never be NULL. 51 // This should never be NULL.
47 DCHECK(location_bar_controller); 52 DCHECK(location_bar_controller);
48 return location_bar_controller->active_script_controller(); 53 return location_bar_controller->active_script_controller();
49 } 54 }
50 55
51 void ActiveScriptController::NotifyScriptExecuting( 56 void ActiveScriptController::InjectScriptIfHasPermission(
52 const std::string& extension_id, int page_id) { 57 const std::string& extension_id,
58 int page_id,
59 const base::Closure& callback) {
53 content::NavigationEntry* visible_entry = 60 content::NavigationEntry* visible_entry =
54 web_contents()->GetController().GetVisibleEntry(); 61 web_contents()->GetController().GetVisibleEntry();
55 if (!visible_entry || 62 if (!visible_entry || visible_entry->GetPageID() != page_id)
56 extensions_executing_scripts_.count(extension_id) ||
57 visible_entry->GetPageID() != page_id) {
58 return; 63 return;
59 }
60 64
61 const Extension* extension = 65 const Extension* extension =
62 ExtensionRegistry::Get(web_contents()->GetBrowserContext()) 66 ExtensionRegistry::Get(web_contents()->GetBrowserContext())
63 ->enabled_extensions().GetByID(extension_id); 67 ->enabled_extensions().GetByID(extension_id);
64 if (extension && 68 if (!extension)
65 PermissionsData::RequiresActionForScriptExecution(extension)) { 69 return;
66 extensions_executing_scripts_.insert(extension_id); 70
67 LocationBarController::NotifyChange(web_contents()); 71 AddOrProcessRequest(extension, callback);
68 }
69 } 72 }
70 73
71 void ActiveScriptController::OnAdInjectionDetected( 74 void ActiveScriptController::OnAdInjectionDetected(
72 const std::vector<std::string> ad_injectors) { 75 const std::vector<std::string> ad_injectors) {
73 size_t num_preventable_ad_injectors = 76 size_t num_preventable_ad_injectors =
74 base::STLSetIntersection<std::set<std::string> >( 77 base::STLSetIntersection<std::set<std::string> >(
75 ad_injectors, extensions_executing_scripts_).size(); 78 ad_injectors, permitted_extensions_).size();
not at google - send to devlin 2014/05/15 22:21:44 if the feature isn't enabled then *every* extensio
Devlin 2014/05/15 23:52:35 Yep, definitely a bug there with the permitted. I
79
80 // We only log the permitted extensions metric if the feature is enabled,
81 // because otherwise the data will be boring (100% allowed).
not at google - send to devlin 2014/05/15 22:21:44 I would have expected 0%
Devlin 2014/05/15 23:52:35 Well, we implicitly allow script injection when it
82 if (enabled_) {
83 UMA_HISTOGRAM_COUNTS_100(
84 "Extensions.ActiveScriptController.PermittedExtensions",
85 permitted_extensions_.size());
86 UMA_HISTOGRAM_COUNTS_100(
87 "Extensions.ActiveScriptController.DeniedExtensions",
88 pending_requests_.size());
89 }
76 90
77 UMA_HISTOGRAM_COUNTS_100( 91 UMA_HISTOGRAM_COUNTS_100(
78 "Extensions.ActiveScriptController.PreventableAdInjectors", 92 "Extensions.ActiveScriptController.PreventableAdInjectors",
79 num_preventable_ad_injectors); 93 num_preventable_ad_injectors);
80 UMA_HISTOGRAM_COUNTS_100( 94 UMA_HISTOGRAM_COUNTS_100(
81 "Extensions.ActiveScriptController.PreventableAdInjectors", 95 "Extensions.ActiveScriptController.PreventableAdInjectors",
82 ad_injectors.size() - num_preventable_ad_injectors); 96 ad_injectors.size() - num_preventable_ad_injectors);
83 } 97 }
84 98
85 ExtensionAction* ActiveScriptController::GetActionForExtension( 99 ExtensionAction* ActiveScriptController::GetActionForExtension(
86 const Extension* extension) { 100 const Extension* extension) {
87 if (!enabled_ || extensions_executing_scripts_.count(extension->id()) == 0) 101 if (!enabled_ || pending_requests_.count(extension->id()) == 0)
88 return NULL; // No action for this extension. 102 return NULL; // No action for this extension.
89 103
90 ActiveScriptMap::iterator existing = 104 ActiveScriptMap::iterator existing =
91 active_script_actions_.find(extension->id()); 105 active_script_actions_.find(extension->id());
92 if (existing != active_script_actions_.end()) 106 if (existing != active_script_actions_.end())
93 return existing->second.get(); 107 return existing->second.get();
94 108
95 linked_ptr<ExtensionAction> action(new ExtensionAction( 109 linked_ptr<ExtensionAction> action(new ExtensionAction(
96 extension->id(), ActionInfo::TYPE_PAGE, ActionInfo())); 110 extension->id(), ActionInfo::TYPE_PAGE, ActionInfo()));
97 action->SetTitle(ExtensionAction::kDefaultTabId, extension->name()); 111 action->SetTitle(ExtensionAction::kDefaultTabId, extension->name());
98 action->SetIsVisible(ExtensionAction::kDefaultTabId, true); 112 action->SetIsVisible(ExtensionAction::kDefaultTabId, true);
99 113
100 const ActionInfo* action_info = ActionInfo::GetPageActionInfo(extension); 114 const ActionInfo* action_info = ActionInfo::GetPageActionInfo(extension);
101 if (!action_info) 115 if (!action_info)
102 action_info = ActionInfo::GetBrowserActionInfo(extension); 116 action_info = ActionInfo::GetBrowserActionInfo(extension);
103 117
104 if (action_info && !action_info->default_icon.empty()) { 118 if (action_info && !action_info->default_icon.empty()) {
105 action->set_default_icon( 119 action->set_default_icon(
106 make_scoped_ptr(new ExtensionIconSet(action_info->default_icon))); 120 make_scoped_ptr(new ExtensionIconSet(action_info->default_icon)));
107 } 121 }
108 122
109 active_script_actions_[extension->id()] = action; 123 active_script_actions_[extension->id()] = action;
110 return action.get(); 124 return action.get();
111 } 125 }
112 126
113 LocationBarController::Action ActiveScriptController::OnClicked( 127 LocationBarController::Action ActiveScriptController::OnClicked(
114 const Extension* extension) { 128 const Extension* extension) {
115 DCHECK(extensions_executing_scripts_.count(extension->id()) > 0); 129 DCHECK(extension);
130 PendingRequestMap::iterator iter =
131 pending_requests_.find(extension->id());
132 DCHECK(iter != pending_requests_.end());
133
134 // Run all pending injections for the given extension.
135 PendingRequestList& list = iter->second;
136 for (PendingRequestList::iterator request = list.begin();
137 request != list.end();
138 ++request) {
139 request->Run();
140 }
141
142 // Remove the extension's pending request entry, and indicate that it is
143 // allowed to run on the current page.
144 pending_requests_.erase(extension->id());
145 permitted_extensions_.insert(extension->id());
not at google - send to devlin 2014/05/15 22:21:44 this code (lines 134...145) has the potential, in
Devlin 2014/05/15 23:52:35 That would indeed be _crazy_ circumstances. But w
146
not at google - send to devlin 2014/05/15 22:21:44 also, TODO grant active-tab-esque permission.
Devlin 2014/05/15 23:52:35 Done.
147 // Inform the location bar that the action is now gone.
not at google - send to devlin 2014/05/15 22:21:44 something to consider in future (just occurred to
Devlin 2014/05/15 23:52:35 Ooh, that sounds like fun...
148 LocationBarController::NotifyChange(web_contents());
149
116 return LocationBarController::ACTION_NONE; 150 return LocationBarController::ACTION_NONE;
117 } 151 }
118 152
119 void ActiveScriptController::OnNavigated() { 153 void ActiveScriptController::OnNavigated() {
120 LogUMA(); 154 LogUMA();
121 extensions_executing_scripts_.clear(); 155 permitted_extensions_.clear();
156 pending_requests_.clear();
157 }
158
159 void ActiveScriptController::OnNotifyExtensionScriptExecution(
160 const std::string& extension_id,
161 int page_id) {
162 if (!Extension::IdIsValid(extension_id)) {
163 NOTREACHED() << "'" << extension_id << "' is not a valid id.";
164 return;
165 }
166
167 InjectScriptIfHasPermission(
168 extension_id,
169 page_id,
170 base::Closure(base::Bind(&base::DoNothing)));
171 }
172
173 void ActiveScriptController::AddOrProcessRequest(
174 const Extension* extension,
175 const base::Closure& request) {
176 // If the feature is not enabled, we automatically allow all extensions to
177 // run scripts.
not at google - send to devlin 2014/05/15 22:21:44 how did this get above the RequiresActionForScript
Devlin 2014/05/15 23:52:35 Whoops, good point. Let's fix that... Done.
178 if (!enabled_)
179 permitted_extensions_.insert(extension->id());
180
181 // If the extension does not require permissions, run it immediately.
not at google - send to devlin 2014/05/15 22:21:44 TODO take into account tab-specific permissions
Devlin 2014/05/15 23:52:35 Done. (added comment in .h above permitted_extensi
182 if (!PermissionsData::RequiresActionForScriptExecution(extension) ||
183 permitted_extensions_.count(extension->id())) {
184 request.Run();
185 return;
186 }
187
188 PendingRequestList* list = &pending_requests_[extension->id()];
not at google - send to devlin 2014/05/15 22:21:44 nit: why not just PendingRequestList& list?
Devlin 2014/05/15 23:52:35 Only 'cuz I never see non-const & in chrome. But,
not at google - send to devlin 2014/05/16 00:08:32 indeed.
189 list->push_back(request);
190
191 // If this was the first entry, notify the location bar that there's a new
192 // icon.
193 if (list->size() == 1u)
194 LocationBarController::NotifyChange(web_contents());
122 } 195 }
123 196
124 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { 197 bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) {
125 bool handled = true; 198 bool handled = true;
126 IPC_BEGIN_MESSAGE_MAP(ActiveScriptController, message) 199 IPC_BEGIN_MESSAGE_MAP(ActiveScriptController, message)
127 IPC_MESSAGE_HANDLER(ExtensionHostMsg_NotifyExtensionScriptExecution, 200 IPC_MESSAGE_HANDLER(ExtensionHostMsg_NotifyExtensionScriptExecution,
128 OnNotifyExtensionScriptExecution) 201 OnNotifyExtensionScriptExecution)
129 IPC_MESSAGE_UNHANDLED(handled = false) 202 IPC_MESSAGE_UNHANDLED(handled = false)
130 IPC_END_MESSAGE_MAP() 203 IPC_END_MESSAGE_MAP()
131 return handled; 204 return handled;
132 } 205 }
133 206
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 { 207 void ActiveScriptController::LogUMA() const {
145 UMA_HISTOGRAM_COUNTS_100( 208 UMA_HISTOGRAM_COUNTS_100(
146 "Extensions.ActiveScriptController.ShownActiveScriptsOnPage", 209 "Extensions.ActiveScriptController.ShownActiveScriptsOnPage",
147 extensions_executing_scripts_.size()); 210 pending_requests_.size());
148 } 211 }
149 212
150 } // namespace extensions 213 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698