123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- // 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 <tuple>
- #include <vector>
- #include "base/check.h"
- #include "base/lazy_instance.h"
- #include "base/no_destructor.h"
- #include "content/public/common/url_constants.h"
- #include "content/public/renderer/render_frame.h"
- #include "content/public/renderer/render_thread.h"
- #include "extensions/common/extension.h"
- #include "extensions/common/mojom/guest_view.mojom.h"
- #include "extensions/common/permissions/permissions_data.h"
- #include "extensions/grit/extensions_renderer_resources.h"
- #include "extensions/renderer/extension_frame_helper.h"
- #include "extensions/renderer/injection_host.h"
- #include "extensions/renderer/script_context.h"
- #include "extensions/renderer/scripts_run_info.h"
- #include "ipc/ipc_sync_channel.h"
- #include "third_party/blink/public/web/web_document.h"
- #include "third_party/blink/public/web/web_local_frame.h"
- #include "third_party/blink/public/web/web_script_source.h"
- #include "ui/base/resource/resource_bundle.h"
- #include "url/gurl.h"
- namespace extensions {
- namespace {
- struct RoutingInfoKey {
- int routing_id;
- std::string script_id;
- RoutingInfoKey(int routing_id, std::string script_id)
- : routing_id(routing_id), script_id(std::move(script_id)) {}
- bool operator<(const RoutingInfoKey& other) const {
- return std::tie(routing_id, script_id) <
- std::tie(other.routing_id, other.script_id);
- }
- };
- using RoutingInfoMap = std::map<RoutingInfoKey, bool>;
- // A map records whether a given |script_id| from a webview-added user script
- // is allowed to inject on the render of given |routing_id|.
- // Once a script is added, the decision of whether or not allowed to inject
- // won't be changed.
- // After removed by the webview, the user scipt will also be removed
- // from the render. Therefore, there won't be any query from the same
- // |script_id| and |routing_id| pair.
- base::LazyInstance<RoutingInfoMap>::DestructorAtExit g_routing_info_map =
- LAZY_INSTANCE_INITIALIZER;
- // Greasemonkey API source that is injected with the scripts.
- struct GreasemonkeyApiJsString {
- GreasemonkeyApiJsString();
- blink::WebScriptSource GetSource() const;
- private:
- blink::WebString source_;
- };
- // The below constructor, monstrous as it is, just makes a WebScriptSource from
- // the GreasemonkeyApiJs resource.
- GreasemonkeyApiJsString::GreasemonkeyApiJsString() {
- std::string greasemonky_api_js(
- ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
- IDR_GREASEMONKEY_API_JS));
- source_ = blink::WebString::FromUTF8(greasemonky_api_js);
- }
- blink::WebScriptSource GreasemonkeyApiJsString::GetSource() const {
- return blink::WebScriptSource(source_);
- }
- base::LazyInstance<GreasemonkeyApiJsString>::Leaky g_greasemonkey_api =
- LAZY_INSTANCE_INITIALIZER;
- bool ShouldInjectScripts(const UserScript::FileList& scripts,
- const std::set<std::string>& injected_files) {
- for (const std::unique_ptr<UserScript::File>& file : scripts) {
- // Check if the script is already injected.
- if (injected_files.count(file->url().path()) == 0) {
- return true;
- }
- }
- return false;
- }
- mojom::GuestView* GetGuestView() {
- static base::NoDestructor<mojo::AssociatedRemote<mojom::GuestView>>
- guest_view;
- if (!*guest_view) {
- content::RenderThread::Get()->GetChannel()->GetRemoteAssociatedInterface(
- guest_view.get());
- }
- return guest_view->get();
- }
- } // namespace
- UserScriptInjector::UserScriptInjector(const UserScript* script,
- UserScriptSet* script_list,
- bool is_declarative)
- : script_(script),
- user_script_set_(script_list),
- script_id_(script_->id()),
- host_id_(script_->host_id()),
- is_declarative_(is_declarative) {
- user_script_set_observation_.Observe(script_list);
- }
- UserScriptInjector::~UserScriptInjector() {
- }
- void UserScriptInjector::OnUserScriptsUpdated() {
- // When user scripts are updated, this means the host causing this injection
- // has changed. All old script pointers are invalidated and this injection
- // will be removed as there's no guarantee the backing script still exists.
- script_ = nullptr;
- }
- void UserScriptInjector::OnUserScriptSetDestroyed() {
- user_script_set_observation_.Reset();
- // Invalidate the script pointer as the UserScriptSet which this script
- // belongs to has been destroyed.
- script_ = nullptr;
- }
- mojom::InjectionType UserScriptInjector::script_type() const {
- return mojom::InjectionType::kContentScript;
- }
- blink::mojom::UserActivationOption UserScriptInjector::IsUserGesture() const {
- return blink::mojom::UserActivationOption::kDoNotActivate;
- }
- mojom::ExecutionWorld UserScriptInjector::GetExecutionWorld() const {
- return script_->execution_world();
- }
- blink::mojom::WantResultOption UserScriptInjector::ExpectsResults() const {
- return blink::mojom::WantResultOption::kNoResult;
- }
- blink::mojom::PromiseResultOption UserScriptInjector::ShouldWaitForPromise()
- const {
- return blink::mojom::PromiseResultOption::kDoNotWait;
- }
- mojom::CSSOrigin UserScriptInjector::GetCssOrigin() const {
- return mojom::CSSOrigin::kAuthor;
- }
- mojom::CSSInjection::Operation UserScriptInjector::GetCSSInjectionOperation()
- const {
- DCHECK(script_);
- DCHECK(!script_->css_scripts().empty());
- return mojom::CSSInjection::Operation::kAdd;
- }
- bool UserScriptInjector::ShouldInjectJs(
- mojom::RunLocation run_location,
- const std::set<std::string>& executing_scripts) const {
- return script_ && script_->run_location() == run_location &&
- !script_->js_scripts().empty() &&
- ShouldInjectScripts(script_->js_scripts(), executing_scripts);
- }
- bool UserScriptInjector::ShouldInjectOrRemoveCss(
- mojom::RunLocation run_location,
- const std::set<std::string>& injected_stylesheets) const {
- return script_ && run_location == mojom::RunLocation::kDocumentStart &&
- !script_->css_scripts().empty() &&
- ShouldInjectScripts(script_->css_scripts(), injected_stylesheets);
- }
- PermissionsData::PageAccess UserScriptInjector::CanExecuteOnFrame(
- const InjectionHost* injection_host,
- blink::WebLocalFrame* web_frame,
- int tab_id) {
- // There is no harm in allowing the injection when the script is gone,
- // because there is nothing to inject.
- if (!script_)
- return PermissionsData::PageAccess::kAllowed;
- if (script_->consumer_instance_type() ==
- UserScript::ConsumerInstanceType::WEBVIEW) {
- int routing_id =
- content::RenderFrame::FromWebFrame(web_frame)->GetRoutingID();
- RoutingInfoKey key(routing_id, script_->id());
- RoutingInfoMap& map = g_routing_info_map.Get();
- auto iter = map.find(key);
- bool allowed = false;
- if (iter != map.end()) {
- allowed = iter->second;
- } else {
- // Perform a sync mojo call to the browser to check if this is allowed.
- // This is not ideal, but is mitigated by the fact that this is only done
- // for webviews, and then only once per host.
- // TODO(hanxi): Find a more efficient way to do this.
- auto* guest_view = GetGuestView();
- if (guest_view) {
- guest_view->CanExecuteContentScript(routing_id, script_->id(),
- &allowed);
- }
- map.insert(std::pair<RoutingInfoKey, bool>(key, allowed));
- }
- return allowed ? PermissionsData::PageAccess::kAllowed
- : PermissionsData::PageAccess::kDenied;
- }
- GURL effective_document_url =
- ScriptContext::GetEffectiveDocumentURLForInjection(
- web_frame, web_frame->GetDocument().Url(),
- script_->match_origin_as_fallback());
- return injection_host->CanExecuteOnFrame(
- effective_document_url,
- content::RenderFrame::FromWebFrame(web_frame),
- tab_id,
- is_declarative_);
- }
- std::vector<blink::WebScriptSource> UserScriptInjector::GetJsSources(
- mojom::RunLocation run_location,
- std::set<std::string>* executing_scripts,
- size_t* num_injected_js_scripts) const {
- DCHECK(script_);
- std::vector<blink::WebScriptSource> sources;
- DCHECK_EQ(script_->run_location(), run_location);
- const UserScript::FileList& js_scripts = script_->js_scripts();
- sources.reserve(js_scripts.size() +
- (script_->emulate_greasemonkey() ? 1 : 0));
- // Emulate Greasemonkey API for scripts that were converted to extension
- // user scripts.
- if (script_->emulate_greasemonkey())
- sources.push_back(g_greasemonkey_api.Get().GetSource());
- for (const std::unique_ptr<UserScript::File>& file : js_scripts) {
- const GURL& script_url = file->url();
- // Check if the script is already injected.
- if (executing_scripts->count(script_url.path()) != 0)
- continue;
- sources.push_back(blink::WebScriptSource(
- user_script_set_->GetJsSource(*file, script_->emulate_greasemonkey()),
- script_url));
- ++*num_injected_js_scripts;
- executing_scripts->insert(script_url.path());
- }
- return sources;
- }
- std::vector<ScriptInjector::CSSSource> UserScriptInjector::GetCssSources(
- mojom::RunLocation run_location,
- std::set<std::string>* injected_stylesheets,
- size_t* num_injected_stylesheets) const {
- DCHECK(script_);
- DCHECK_EQ(mojom::RunLocation::kDocumentStart, run_location);
- std::vector<CSSSource> sources;
- const UserScript::FileList& css_scripts = script_->css_scripts();
- sources.reserve(css_scripts.size());
- for (const std::unique_ptr<UserScript::File>& file : script_->css_scripts()) {
- const std::string& stylesheet_path = file->url().path();
- // Check if the stylesheet is already injected.
- if (injected_stylesheets->count(stylesheet_path) != 0)
- continue;
- sources.push_back(CSSSource{user_script_set_->GetCssSource(*file),
- blink::WebStyleSheetKey()});
- injected_stylesheets->insert(stylesheet_path);
- }
- *num_injected_stylesheets += sources.size();
- return sources;
- }
- void UserScriptInjector::OnInjectionComplete(
- std::unique_ptr<base::Value> execution_result,
- mojom::RunLocation run_location) {}
- void UserScriptInjector::OnWillNotInject(InjectFailureReason reason) {}
- } // namespace extensions
|