Chromium Code Reviews| Index: chrome/browser/extensions/active_script_controller.cc |
| diff --git a/chrome/browser/extensions/active_script_controller.cc b/chrome/browser/extensions/active_script_controller.cc |
| index 36209aff27729a13b0aa4b29b2aaf815cfed78b6..d0041b526de241412f902fcb2cb4abbc0b0ff81d 100644 |
| --- a/chrome/browser/extensions/active_script_controller.cc |
| +++ b/chrome/browser/extensions/active_script_controller.cc |
| @@ -4,6 +4,9 @@ |
| #include "chrome/browser/extensions/active_script_controller.h" |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/memory/scoped_ptr.h" |
| #include "base/metrics/histogram.h" |
| #include "base/stl_util.h" |
| #include "chrome/browser/extensions/extension_action.h" |
| @@ -12,6 +15,7 @@ |
| #include "chrome/common/extensions/api/extension_action/action_info.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_entry.h" |
| +#include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/extension.h" |
| @@ -23,10 +27,25 @@ |
| namespace extensions { |
| +ActiveScriptController::PendingRequest::PendingRequest() : |
| + page_id(-1) { |
| +} |
| + |
| +ActiveScriptController::PendingRequest::PendingRequest( |
| + const base::Closure closure, |
| + int page_id) |
| + : closure(closure), |
| + page_id(page_id) { |
| +} |
| + |
| +ActiveScriptController::PendingRequest::~PendingRequest() { |
| +} |
| + |
| ActiveScriptController::ActiveScriptController( |
| content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents), |
| enabled_(FeatureSwitch::scripts_require_action()->IsEnabled()) { |
| + CHECK(web_contents); |
| } |
| ActiveScriptController::~ActiveScriptController() { |
| @@ -48,43 +67,51 @@ ActiveScriptController* ActiveScriptController::GetForWebContents( |
| return location_bar_controller->active_script_controller(); |
| } |
| -void ActiveScriptController::NotifyScriptExecuting( |
| - const std::string& extension_id, int page_id) { |
| - content::NavigationEntry* visible_entry = |
| - web_contents()->GetController().GetVisibleEntry(); |
| - if (!visible_entry || |
| - extensions_executing_scripts_.count(extension_id) || |
| - visible_entry->GetPageID() != page_id) { |
| - return; |
| - } |
| +bool ActiveScriptController::RequiresUserConsentForScriptInjection( |
| + const Extension* extension) { |
| + CHECK(extension); |
| + if (!PermissionsData::RequiresActionForScriptExecution(extension)) |
| + return false; |
| - const Extension* extension = |
| - ExtensionRegistry::Get(web_contents()->GetBrowserContext()) |
| - ->enabled_extensions().GetByID(extension_id); |
| - if (extension && |
| - PermissionsData::RequiresActionForScriptExecution(extension)) { |
| - extensions_executing_scripts_.insert(extension_id); |
| + if (!enabled_) |
| + permitted_extensions_.insert(extension->id()); |
| + |
| + // If the feature is not enabled, we automatically allow all extensions to |
| + // run scripts. |
|
not at google - send to devlin
2014/05/16 17:32:12
this comment looks misplaced
Devlin
2014/05/19 16:17:46
Done.
|
| + return permitted_extensions_.count(extension->id()) == 0; |
| +} |
| + |
| +void ActiveScriptController::RequestScriptInjection( |
| + const Extension* extension, |
| + int page_id, |
| + const base::Closure& callback) { |
| + CHECK(extension); |
| + PendingRequestList& list = pending_requests_[extension->id()]; |
| + list.push_back(PendingRequest(callback, page_id)); |
| + |
| + // If this was the first entry, notify the location bar that there's a new |
| + // icon. |
| + if (list.size() == 1u) |
| LocationBarController::NotifyChange(web_contents()); |
| - } |
| } |
| void ActiveScriptController::OnAdInjectionDetected( |
| const std::vector<std::string> ad_injectors) { |
| size_t num_preventable_ad_injectors = |
| base::STLSetIntersection<std::set<std::string> >( |
| - ad_injectors, extensions_executing_scripts_).size(); |
| + ad_injectors, permitted_extensions_).size(); |
| UMA_HISTOGRAM_COUNTS_100( |
| "Extensions.ActiveScriptController.PreventableAdInjectors", |
| num_preventable_ad_injectors); |
| UMA_HISTOGRAM_COUNTS_100( |
| - "Extensions.ActiveScriptController.PreventableAdInjectors", |
| + "Extensions.ActiveScriptController.UnpreventableAdInjectors", |
| ad_injectors.size() - num_preventable_ad_injectors); |
| } |
| ExtensionAction* ActiveScriptController::GetActionForExtension( |
| const Extension* extension) { |
| - if (!enabled_ || extensions_executing_scripts_.count(extension->id()) == 0) |
| + if (!enabled_ || pending_requests_.count(extension->id()) == 0) |
| return NULL; // No action for this extension. |
| ActiveScriptMap::iterator existing = |
| @@ -112,13 +139,70 @@ ExtensionAction* ActiveScriptController::GetActionForExtension( |
| LocationBarController::Action ActiveScriptController::OnClicked( |
| const Extension* extension) { |
| - DCHECK(extensions_executing_scripts_.count(extension->id()) > 0); |
| + DCHECK(extension); |
| + PendingRequestMap::iterator iter = |
| + pending_requests_.find(extension->id()); |
| + DCHECK(iter != pending_requests_.end()); |
| + |
| + content::NavigationEntry* visible_entry = |
| + web_contents()->GetController().GetVisibleEntry(); |
| + // Refuse to run if there's no visible entry, because we have no idea of |
| + // determining if it's the proper page. This should rarely, if ever, happen. |
| + if (!visible_entry) |
| + return LocationBarController::ACTION_NONE; |
| + |
| + int page_id = visible_entry->GetPageID(); |
| + |
| + // We add this to the list of permitted extensions and erase pending entries |
| + // *before* running them to guard against the crazy case where running the |
| + // callbacks adds more entries. |
| + permitted_extensions_.insert(extension->id()); |
| + PendingRequestList requests; |
| + iter->second.swap(requests); |
| + pending_requests_.erase(extension->id()); |
| + |
| + // Run all pending injections for the given extension. |
| + for (PendingRequestList::iterator request = requests.begin(); |
| + request != requests.end(); |
| + ++request) { |
| + // Only run if it's on the proper page. |
| + if (request->page_id == page_id) |
| + request->closure.Run(); |
| + } |
| + |
| + // Inform the location bar that the action is now gone. |
| + LocationBarController::NotifyChange(web_contents()); |
| + |
| return LocationBarController::ACTION_NONE; |
| } |
| void ActiveScriptController::OnNavigated() { |
| LogUMA(); |
| - extensions_executing_scripts_.clear(); |
| + permitted_extensions_.clear(); |
| + pending_requests_.clear(); |
| +} |
| + |
| +void ActiveScriptController::OnNotifyExtensionScriptExecution( |
| + const std::string& extension_id, |
| + int page_id) { |
| + if (!Extension::IdIsValid(extension_id)) { |
| + NOTREACHED() << "'" << extension_id << "' is not a valid id."; |
| + return; |
| + } |
| + |
| + const Extension* extension = |
| + ExtensionRegistry::Get(web_contents()->GetBrowserContext()) |
| + ->enabled_extensions().GetByID(extension_id); |
| + // We shouldn't allow extensions which are no longer enabled to run any |
| + // scripts. Ignore the request. |
| + if (!extension) |
| + return; |
| + |
| + // Right now, we allow all content scripts to execute, but notify the |
| + // controller of them. |
| + // TODO(rdevlin.cronin): Fix this in a future CL. |
| + if (RequiresUserConsentForScriptInjection(extension)) |
| + RequestScriptInjection(extension, page_id, base::Bind(&base::DoNothing)); |
| } |
| bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { |
| @@ -131,20 +215,21 @@ bool ActiveScriptController::OnMessageReceived(const IPC::Message& message) { |
| return handled; |
| } |
| -void ActiveScriptController::OnNotifyExtensionScriptExecution( |
| - const std::string& extension_id, |
| - int page_id) { |
| - if (!Extension::IdIsValid(extension_id)) { |
| - NOTREACHED() << "'" << extension_id << "' is not a valid id."; |
| - return; |
| - } |
| - NotifyScriptExecuting(extension_id, page_id); |
| -} |
| - |
| void ActiveScriptController::LogUMA() const { |
| UMA_HISTOGRAM_COUNTS_100( |
| "Extensions.ActiveScriptController.ShownActiveScriptsOnPage", |
| - extensions_executing_scripts_.size()); |
| + pending_requests_.size()); |
| + |
| + // We only log the permitted extensions metric if the feature is enabled, |
| + // because otherwise the data will be boring (100% allowed). |
| + if (enabled_) { |
| + UMA_HISTOGRAM_COUNTS_100( |
| + "Extensions.ActiveScriptController.PermittedExtensions", |
| + permitted_extensions_.size()); |
| + UMA_HISTOGRAM_COUNTS_100( |
| + "Extensions.ActiveScriptController.DeniedExtensions", |
| + pending_requests_.size()); |
| + } |
| } |
| } // namespace extensions |