|  | // Copyright 2014 The Chromium Authors | 
|  | // 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/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/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; | 
|  | } | 
|  |  | 
|  | blink::mojom::UserActivationOption 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; | 
|  | } | 
|  |  | 
|  | const std::optional<std::string>& | 
|  | ProgrammaticScriptInjector::GetExecutionWorldId() const { | 
|  | return params_->injection->get_js()->world_id; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | blink::mojom::WantResultOption ProgrammaticScriptInjector::ExpectsResults() | 
|  | const { | 
|  | DCHECK(params_->injection->is_js()); | 
|  | return params_->injection->get_js()->wants_result; | 
|  | } | 
|  |  | 
|  | blink::mojom::PromiseResultOption | 
|  | ProgrammaticScriptInjector::ShouldWaitForPromise() const { | 
|  | DCHECK(params_->injection->is_js()); | 
|  | return params_->injection->get_js()->wait_for_promise; | 
|  | } | 
|  |  | 
|  | 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_origin_as_fallback_behavior); | 
|  | if (params_->is_web_view) { | 
|  | if (!frame->IsOutermostMainFrame()) { | 
|  | // 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::optional<base::Value> execution_result, | 
|  | mojom::RunLocation run_location) { | 
|  | DCHECK(!result_.has_value()); | 
|  | result_ = 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 |