| // 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/programmatic_script_injector.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/values.h" |
| #include "content/public/common/url_constants.h" |
| #include "content/public/renderer/render_frame.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/extension_messages.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "extensions/common/mojom/host_id.mojom.h" |
| #include "extensions/common/permissions/api_permission.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "extensions/common/script_constants.h" |
| #include "extensions/renderer/injection_host.h" |
| #include "extensions/renderer/renderer_extension_registry.h" |
| #include "extensions/renderer/script_context.h" |
| #include "third_party/blink/public/platform/web_string.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" |
| |
| namespace extensions { |
| |
| ProgrammaticScriptInjector::ProgrammaticScriptInjector( |
| mojom::ExecuteCodeParamsPtr params, |
| mojom::LocalFrame::ExecuteCodeCallback callback) |
| : params_(std::move(params)), callback_(std::move(callback)) {} |
| |
| ProgrammaticScriptInjector::~ProgrammaticScriptInjector() { |
| } |
| |
| mojom::InjectionType ProgrammaticScriptInjector::script_type() const { |
| return mojom::InjectionType::kProgrammaticScript; |
| } |
| |
| bool ProgrammaticScriptInjector::IsUserGesture() const { |
| DCHECK(params_->injection->is_js()); |
| return params_->injection->get_js()->user_gesture; |
| } |
| |
| mojom::ExecutionWorld ProgrammaticScriptInjector::GetExecutionWorld() const { |
| DCHECK(params_->injection->is_js()); |
| return params_->injection->get_js()->world; |
| } |
| |
| mojom::CSSOrigin ProgrammaticScriptInjector::GetCssOrigin() const { |
| DCHECK(params_->injection->is_css()); |
| return params_->injection->get_css()->css_origin; |
| } |
| |
| mojom::CSSInjection::Operation |
| ProgrammaticScriptInjector::GetCSSInjectionOperation() const { |
| DCHECK(params_->injection->is_css()); |
| return params_->injection->get_css()->operation; |
| } |
| |
| bool ProgrammaticScriptInjector::ExpectsResults() const { |
| DCHECK(params_->injection->is_js()); |
| return params_->injection->get_js()->wants_result; |
| } |
| |
| bool ProgrammaticScriptInjector::ShouldInjectJs( |
| mojom::RunLocation run_location, |
| const std::set<std::string>& executing_scripts) const { |
| return params_->run_at == run_location && params_->injection->is_js(); |
| } |
| |
| bool ProgrammaticScriptInjector::ShouldInjectOrRemoveCss( |
| mojom::RunLocation run_location, |
| const std::set<std::string>& injected_stylesheets) const { |
| return params_->run_at == run_location && params_->injection->is_css(); |
| } |
| |
| PermissionsData::PageAccess ProgrammaticScriptInjector::CanExecuteOnFrame( |
| const InjectionHost* injection_host, |
| blink::WebLocalFrame* frame, |
| int tab_id) { |
| // Note: we calculate url_ now and not in the constructor because we don't |
| // have the URL at that point when loads start. The browser issues the request |
| // and only when it has a response does the renderer see the provisional data |
| // source which the method below uses. |
| url_ = ScriptContext::GetDocumentLoaderURLForFrame(frame); |
| if (url_.SchemeIs(url::kAboutScheme)) { |
| origin_for_about_error_ = frame->GetSecurityOrigin().ToString().Utf8(); |
| } |
| GURL effective_document_url = |
| ScriptContext::GetEffectiveDocumentURLForInjection( |
| frame, frame->GetDocument().Url(), |
| params_->match_about_blank |
| ? MatchOriginAsFallbackBehavior::kMatchForAboutSchemeAndClimbTree |
| : MatchOriginAsFallbackBehavior::kNever); |
| if (params_->is_web_view) { |
| if (frame->Parent()) { |
| // This is a subframe inside <webview>, so allow it. |
| return PermissionsData::PageAccess::kAllowed; |
| } |
| |
| return effective_document_url == params_->webview_src |
| ? PermissionsData::PageAccess::kAllowed |
| : PermissionsData::PageAccess::kDenied; |
| } |
| DCHECK_EQ(injection_host->id().type, mojom::HostID::HostType::kExtensions); |
| |
| return injection_host->CanExecuteOnFrame( |
| effective_document_url, |
| content::RenderFrame::FromWebFrame(frame), |
| tab_id, |
| true /* is_declarative */); |
| } |
| |
| std::vector<blink::WebScriptSource> ProgrammaticScriptInjector::GetJsSources( |
| mojom::RunLocation run_location, |
| std::set<std::string>* executing_scripts, |
| size_t* num_injected_js_scripts) const { |
| DCHECK_EQ(params_->run_at, run_location); |
| DCHECK(params_->injection->is_js()); |
| |
| auto& js_injection = params_->injection->get_js(); |
| std::vector<blink::WebScriptSource> sources; |
| sources.reserve(js_injection->sources.size()); |
| for (const auto& source : js_injection->sources) { |
| sources.emplace_back(blink::WebString::FromUTF8(source->code), |
| source->script_url); |
| } |
| |
| return sources; |
| } |
| |
| std::vector<ScriptInjector::CSSSource> |
| ProgrammaticScriptInjector::GetCssSources( |
| mojom::RunLocation run_location, |
| std::set<std::string>* injected_stylesheets, |
| size_t* num_injected_stylesheets) const { |
| DCHECK_EQ(params_->run_at, run_location); |
| DCHECK(params_->injection->is_css()); |
| |
| auto& css_injection = params_->injection->get_css(); |
| std::vector<CSSSource> sources; |
| sources.reserve(css_injection->sources.size()); |
| for (const auto& source : css_injection->sources) { |
| blink::WebStyleSheetKey style_sheet_key; |
| if (source->key) |
| style_sheet_key = blink::WebString::FromASCII(*source->key); |
| sources.push_back( |
| CSSSource{blink::WebString::FromUTF8(source->code), style_sheet_key}); |
| } |
| |
| return sources; |
| } |
| |
| void ProgrammaticScriptInjector::OnInjectionComplete( |
| std::unique_ptr<base::Value> execution_result, |
| mojom::RunLocation run_location) { |
| DCHECK(!result_.has_value()); |
| if (execution_result) { |
| result_ = base::Value::FromUniquePtrValue(std::move(execution_result)); |
| } |
| Finish(std::string()); |
| } |
| |
| void ProgrammaticScriptInjector::OnWillNotInject(InjectFailureReason reason) { |
| std::string error; |
| switch (reason) { |
| case NOT_ALLOWED: |
| if (!CanShowUrlInError()) { |
| error = manifest_errors::kCannotAccessPage; |
| } else if (!origin_for_about_error_.empty()) { |
| error = ErrorUtils::FormatErrorMessage( |
| manifest_errors::kCannotAccessAboutUrl, url_.spec(), |
| origin_for_about_error_); |
| } else { |
| error = ErrorUtils::FormatErrorMessage( |
| manifest_errors::kCannotAccessPageWithUrl, url_.spec()); |
| } |
| break; |
| case EXTENSION_REMOVED: // no special error here. |
| case WONT_INJECT: |
| break; |
| } |
| Finish(error); |
| } |
| |
| bool ProgrammaticScriptInjector::CanShowUrlInError() const { |
| if (params_->host_id->type != mojom::HostID::HostType::kExtensions) |
| return false; |
| const Extension* extension = |
| RendererExtensionRegistry::Get()->GetByID(params_->host_id->id); |
| if (!extension) |
| return false; |
| return extension->permissions_data()->active_permissions().HasAPIPermission( |
| mojom::APIPermissionID::kTab); |
| } |
| |
| void ProgrammaticScriptInjector::Finish(const std::string& error) { |
| DCHECK(!finished_); |
| finished_ = true; |
| |
| if (callback_) |
| std::move(callback_).Run(error, url_, std::move(result_)); |
| } |
| |
| } // namespace extensions |