blob: 79361557d869f80a3bbc25321d039f8a4f696cab [file] [log] [blame]
// Copyright 2014 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.
#include "extensions/renderer/user_script_injector.h"
#include <vector>
#include "base/lazy_instance.h"
#include "content/public/common/url_constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/scripts_run_info.h"
#include "grit/extensions_renderer_resources.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "ui/base/resource/resource_bundle.h"
#include "url/gurl.h"
namespace extensions {
namespace {
// These two strings are injected before and after the Greasemonkey API and
// user script to wrap it in an anonymous scope.
const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
const char kUserScriptTail[] = "\n})(window);";
// Greasemonkey API source that is injected with the scripts.
struct GreasemonkeyApiJsString {
GreasemonkeyApiJsString();
blink::WebScriptSource GetSource() const;
private:
std::string source_;
};
// The below constructor, monstrous as it is, just makes a WebScriptSource from
// the GreasemonkeyApiJs resource.
GreasemonkeyApiJsString::GreasemonkeyApiJsString()
: source_(ResourceBundle::GetSharedInstance()
.GetRawDataResource(IDR_GREASEMONKEY_API_JS)
.as_string()) {
}
blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const {
return blink::WebScriptSource(blink::WebString::fromUTF8(source_));
}
base::LazyInstance<GreasemonkeyApiJsString> g_greasemonkey_api =
LAZY_INSTANCE_INITIALIZER;
} // namespace
UserScriptInjector::UserScriptInjector(
const UserScript* script,
UserScriptSet* script_list)
: script_(script),
script_id_(script_->id()),
extension_id_(script_->extension_id()),
user_script_set_observer_(this) {
user_script_set_observer_.Add(script_list);
}
UserScriptInjector::~UserScriptInjector() {
}
void UserScriptInjector::OnUserScriptsUpdated(
const std::set<std::string>& changed_extensions,
const std::vector<UserScript*>& scripts) {
// If the extension causing this injection changed, then this injection
// will be removed, and there's no guarantee the backing script still exists.
if (changed_extensions.count(extension_id_) > 0)
return;
for (std::vector<UserScript*>::const_iterator iter = scripts.begin();
iter != scripts.end();
++iter) {
// We need to compare to |script_id_| (and not to script_->id()) because the
// old |script_| may be deleted by now.
if ((*iter)->id() == script_id_) {
script_ = *iter;
break;
}
}
}
UserScript::InjectionType UserScriptInjector::script_type() const {
return UserScript::CONTENT_SCRIPT;
}
bool UserScriptInjector::ShouldExecuteInChildFrames() const {
return false;
}
bool UserScriptInjector::ShouldExecuteInMainWorld() const {
return false;
}
bool UserScriptInjector::IsUserGesture() const {
return false;
}
bool UserScriptInjector::ExpectsResults() const {
return false;
}
bool UserScriptInjector::ShouldInjectJs(
UserScript::RunLocation run_location) const {
return script_->run_location() == run_location &&
!script_->js_scripts().empty();
}
bool UserScriptInjector::ShouldInjectCss(
UserScript::RunLocation run_location) const {
return run_location == UserScript::DOCUMENT_START &&
!script_->css_scripts().empty();
}
PermissionsData::AccessType UserScriptInjector::CanExecuteOnFrame(
const Extension* extension,
blink::WebFrame* web_frame,
int tab_id,
const GURL& top_url) const {
// If we don't have a tab id, we have no UI surface to ask for user consent.
// For now, we treat this as an automatic allow.
if (tab_id == -1)
return PermissionsData::ACCESS_ALLOWED;
GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL(
web_frame, web_frame->document().url(), script_->match_about_blank());
return extension->permissions_data()->GetContentScriptAccess(
extension,
effective_document_url,
top_url,
tab_id,
-1, // no process id
NULL /* ignore error */);
}
std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
UserScript::RunLocation run_location) const {
DCHECK_EQ(script_->run_location(), run_location);
std::vector<blink::WebScriptSource> sources;
const UserScript::FileList& js_scripts = script_->js_scripts();
bool is_standalone_or_emulate_greasemonkey =
script_->is_standalone() || script_->emulate_greasemonkey();
for (UserScript::FileList::const_iterator iter = js_scripts.begin();
iter != js_scripts.end();
++iter) {
std::string content = iter->GetContent().as_string();
// We add this dumb function wrapper for standalone user script to
// emulate what Greasemonkey does.
// TODO(aa): I think that maybe "is_standalone" scripts don't exist
// anymore. Investigate.
if (is_standalone_or_emulate_greasemonkey) {
content.insert(0, kUserScriptHead);
content += kUserScriptTail;
}
sources.push_back(blink::WebScriptSource(
blink::WebString::fromUTF8(content), iter->url()));
}
// Emulate Greasemonkey API for scripts that were converted to extensions
// and "standalone" user scripts.
if (is_standalone_or_emulate_greasemonkey)
sources.insert(sources.begin(), g_greasemonkey_api.Get().GetSource());
return sources;
}
std::vector<std::string> UserScriptInjector::GetCssSources(
UserScript::RunLocation run_location) const {
DCHECK_EQ(UserScript::DOCUMENT_START, run_location);
std::vector<std::string> 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());
}
return sources;
}
void UserScriptInjector::OnInjectionComplete(
scoped_ptr<base::ListValue> execution_results,
ScriptsRunInfo* scripts_run_info,
UserScript::RunLocation run_location) {
if (ShouldInjectJs(run_location)) {
const UserScript::FileList& js_scripts = script_->js_scripts();
scripts_run_info->num_js += js_scripts.size();
for (UserScript::FileList::const_iterator iter = js_scripts.begin();
iter != js_scripts.end();
++iter) {
scripts_run_info->executing_scripts[extension_id_].insert(
iter->url().path());
}
}
if (ShouldInjectCss(run_location))
scripts_run_info->num_css += script_->css_scripts().size();
}
void UserScriptInjector::OnWillNotInject(InjectFailureReason reason) {
}
} // namespace extensions