| 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..a4dc3a551a6ee6be48086ac0d2f667f62b9551f0 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 the feature is not enabled, we automatically allow all extensions to
|
| + // run scripts.
|
| + if (!enabled_)
|
| + permitted_extensions_.insert(extension->id());
|
| +
|
| + 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
|
|
|