Commit 90d8dd14 authored by catmullings's avatar catmullings Committed by Commit bot

Keeps track of scripts injected by extensions installed. Checks if a script is

already injected, and injects a script only if it has not been injected.

BUG=248627
TEST= Run target browser_tests with
--gtest_filter="ExtensionApiTest.ContentScript*"

Review-Url: https://codereview.chromium.org/2116613002
Cr-Commit-Position: refs/heads/master@{#407016}
parent 19677cb9
......@@ -335,6 +335,43 @@ class ContentScriptCssInjectionTest : public ExtensionApiTest {
}
};
IN_PROC_BROWSER_TEST_F(ExtensionApiTest,
ContentScriptDuplicateScriptInjection) {
host_resolver()->AddRule("maps.google.com", "127.0.0.1");
ASSERT_TRUE(StartEmbeddedTestServer());
GURL url(
base::StringPrintf("http://maps.google.com:%i/extensions/test_file.html",
embedded_test_server()->port()));
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(
"content_scripts/duplicate_script_injection")));
ui_test_utils::NavigateToURL(browser(), url);
// Test that a script that matches two separate, yet overlapping match
// patterns is only injected once.
bool scripts_injected_once = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.domAutomationController.send("
"document.getElementsByClassName('injected-once')"
".length == 1)",
&scripts_injected_once));
ASSERT_TRUE(scripts_injected_once);
// Test that a script injected at two different load process times, document
// idle and document end, is injected exactly twice.
bool scripts_injected_twice = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.domAutomationController.send("
"document.getElementsByClassName('injected-twice')"
".length == 2)",
&scripts_injected_twice));
ASSERT_TRUE(scripts_injected_twice);
}
IN_PROC_BROWSER_TEST_F(ContentScriptCssInjectionTest,
ContentScriptInjectsStyles) {
ASSERT_TRUE(StartEmbeddedTestServer());
......
{
"name": "duplicate script injection",
"version": "0.0.1",
"manifest_version": 2,
"description": "Tests that content scripts assigned to two overlapping url matches are not injected twice. Note this can be extended to N matches",
"background": {},
"content_scripts": [
{
"matches": ["*://*.google.com/*"],
"js": ["should_run_once.js"],
"run_at": "document_end"
},
{
"matches": ["*://maps.google.com/*"],
"js": ["should_run_once.js", "should_run_twice.js"],
"run_at": "document_end"
},
{
"matches": ["*://maps.google.com/*"],
"js": ["should_run_twice.js"],
"run_at": "document_idle"
}
]
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var should_be_injected_once = document.createElement('div');
should_be_injected_once.className = 'injected-once';
document.body.appendChild(should_be_injected_once);
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var should_be_injected_twice = document.createElement('div');
should_be_injected_twice.className = 'injected-twice';
document.body.appendChild(should_be_injected_twice);
......@@ -94,7 +94,8 @@ PermissionsData::AccessType ProgrammaticScriptInjector::CanExecuteOnFrame(
}
std::vector<blink::WebScriptSource> ProgrammaticScriptInjector::GetJsSources(
UserScript::RunLocation run_location) const {
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const {
DCHECK_EQ(GetRunLocation(), run_location);
DCHECK(params_->is_javascript);
......@@ -105,7 +106,8 @@ std::vector<blink::WebScriptSource> ProgrammaticScriptInjector::GetJsSources(
}
std::vector<std::string> ProgrammaticScriptInjector::GetCssSources(
UserScript::RunLocation run_location) const {
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const {
DCHECK_EQ(GetRunLocation(), run_location);
DCHECK(!params_->is_javascript);
......
......@@ -40,9 +40,11 @@ class ProgrammaticScriptInjector : public ScriptInjector {
blink::WebLocalFrame* web_frame,
int tab_id) const override;
std::vector<blink::WebScriptSource> GetJsSources(
UserScript::RunLocation run_location) const override;
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const override;
std::vector<std::string> GetCssSources(
UserScript::RunLocation run_location) const override;
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const override;
void GetRunInfo(ScriptsRunInfo* scripts_run_info,
UserScript::RunLocation run_location) const override;
void OnInjectionComplete(std::unique_ptr<base::Value> execution_result,
......
......@@ -219,9 +219,9 @@ ScriptInjection::InjectionResult ScriptInjection::Inject(
DCHECK(should_inject_js || should_inject_css);
if (should_inject_js)
InjectJs();
InjectJs(scripts_run_info);
if (should_inject_css)
InjectCss();
InjectCss(scripts_run_info);
complete_ = did_inject_js_ || !should_inject_js;
......@@ -237,11 +237,11 @@ ScriptInjection::InjectionResult ScriptInjection::Inject(
return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED;
}
void ScriptInjection::InjectJs() {
void ScriptInjection::InjectJs(ScriptsRunInfo* scripts_run_info) {
DCHECK(!did_inject_js_);
blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();
std::vector<blink::WebScriptSource> sources =
injector_->GetJsSources(run_location_);
injector_->GetJsSources(run_location_, scripts_run_info);
bool in_main_world = injector_->ShouldExecuteInMainWorld();
int world_id = in_main_world
? DOMActivityLogger::kMainWorldId
......@@ -311,9 +311,9 @@ void ScriptInjection::OnJsInjectionCompleted(
}
}
void ScriptInjection::InjectCss() {
void ScriptInjection::InjectCss(ScriptsRunInfo* scripts_run_info) {
std::vector<std::string> css_sources =
injector_->GetCssSources(run_location_);
injector_->GetCssSources(run_location_, scripts_run_info);
blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();
for (const std::string& css : css_sources)
web_frame->document().insertStyleSheet(blink::WebString::fromUTF8(css));
......
......@@ -98,14 +98,14 @@ class ScriptInjection {
InjectionResult Inject(ScriptsRunInfo* scripts_run_info);
// Inject any JS scripts into the frame for the injection.
void InjectJs();
void InjectJs(ScriptsRunInfo* scripts_run_info);
// Called when JS injection for the given frame has been completed.
void OnJsInjectionCompleted(
const blink::WebVector<v8::Local<v8::Value> >& results);
// Inject any CSS source into the frame for the injection.
void InjectCss();
void InjectCss(ScriptsRunInfo* scripts_run_info);
// Notify that we will not inject, and mark it as acknowledged.
void NotifyWillNotInject(ScriptInjector::InjectFailureReason reason);
......
......@@ -65,12 +65,14 @@ class ScriptInjector {
// Returns the javascript sources to inject at the given |run_location|.
// Only called if ShouldInjectJs() is true.
virtual std::vector<blink::WebScriptSource> GetJsSources(
UserScript::RunLocation run_location) const = 0;
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const = 0;
// Returns the css to inject at the given |run_location|.
// Only called if ShouldInjectCss() is true.
virtual std::vector<std::string> GetCssSources(
UserScript::RunLocation run_location) const = 0;
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const = 0;
// Fill scriptrs run info based on information about injection.
virtual void GetRunInfo(
......
......@@ -40,6 +40,8 @@ struct ScriptsRunInfo {
ExecutingScriptsMap executing_scripts;
// The elapsed time since the ScriptsRunInfo was constructed.
base::ElapsedTimer timer;
// A list of extension urls of executing scripts.
std::set<GURL> injected_scripts;
// Log information about a given script run. If |send_script_activity| is
// true, this also informs the browser of the script run.
......
......@@ -197,7 +197,8 @@ PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame(
}
std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
UserScript::RunLocation run_location) const {
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const {
std::vector<blink::WebScriptSource> sources;
if (!script_)
return sources;
......@@ -209,6 +210,11 @@ std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
for (UserScript::FileList::const_iterator iter = js_scripts.begin();
iter != js_scripts.end();
++iter) {
const GURL& script_url = iter->url();
// Check if the script is already injected.
if (scripts_run_info->injected_scripts.count(script_url) != 0)
continue;
std::string content = iter->GetContent().as_string();
// We add this dumb function wrapper for user scripts to emulate what
......@@ -218,7 +224,9 @@ std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
content += kUserScriptTail;
}
sources.push_back(blink::WebScriptSource(
blink::WebString::fromUTF8(content), iter->url()));
blink::WebString::fromUTF8(content), script_url));
scripts_run_info->injected_scripts.insert(script_url);
}
// Emulate Greasemonkey API for scripts that were converted to extension
......@@ -230,7 +238,8 @@ std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
}
std::vector<std::string> UserScriptInjector::GetCssSources(
UserScript::RunLocation run_location) const {
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const {
DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
std::vector<std::string> sources;
......@@ -238,10 +247,18 @@ std::vector<std::string> UserScriptInjector::GetCssSources(
return sources;
const UserScript::FileList& css_scripts = script_->css_scripts();
for (UserScript::FileList::const_iterator iter = css_scripts.begin();
iter != css_scripts.end();
++iter) {
sources.push_back(iter->GetContent().as_string());
const GURL& script_url = iter->url();
// Check if the stylesheet is already injected.
if (scripts_run_info->injected_scripts.find(script_url) ==
scripts_run_info->injected_scripts.end()) {
sources.push_back(iter->GetContent().as_string());
scripts_run_info->injected_scripts.insert(script_url);
}
}
return sources;
}
......
......@@ -48,9 +48,11 @@ class UserScriptInjector : public ScriptInjector,
blink::WebLocalFrame* web_frame,
int tab_id) const override;
std::vector<blink::WebScriptSource> GetJsSources(
UserScript::RunLocation run_location) const override;
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const override;
std::vector<std::string> GetCssSources(
UserScript::RunLocation run_location) const override;
UserScript::RunLocation run_location,
ScriptsRunInfo* scripts_run_info) const override;
void GetRunInfo(ScriptsRunInfo* scripts_run_info,
UserScript::RunLocation run_location) const override;
void OnInjectionComplete(std::unique_ptr<base::Value> execution_result,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment