// 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 #include #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/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/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebScriptSource.h" namespace extensions { ProgrammaticScriptInjector::ProgrammaticScriptInjector( const ExtensionMsg_ExecuteCode_Params& params, content::RenderFrame* render_frame) : params_(new ExtensionMsg_ExecuteCode_Params(params)), url_( ScriptContext::GetDataSourceURLForFrame(render_frame->GetWebFrame())), finished_(false) { if (url_.SchemeIs(url::kAboutScheme)) { origin_for_about_error_ = render_frame->GetWebFrame()->securityOrigin().toString().utf8(); } } ProgrammaticScriptInjector::~ProgrammaticScriptInjector() { } UserScript::InjectionType ProgrammaticScriptInjector::script_type() const { return UserScript::PROGRAMMATIC_SCRIPT; } bool ProgrammaticScriptInjector::ShouldExecuteInMainWorld() const { return params_->in_main_world; } bool ProgrammaticScriptInjector::IsUserGesture() const { return params_->user_gesture; } bool ProgrammaticScriptInjector::ExpectsResults() const { return params_->wants_result; } bool ProgrammaticScriptInjector::ShouldInjectJs( UserScript::RunLocation run_location) const { return GetRunLocation() == run_location && params_->is_javascript; } bool ProgrammaticScriptInjector::ShouldInjectCss( UserScript::RunLocation run_location) const { return GetRunLocation() == run_location && !params_->is_javascript; } PermissionsData::AccessType ProgrammaticScriptInjector::CanExecuteOnFrame( const InjectionHost* injection_host, blink::WebLocalFrame* frame, int tab_id) const { GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( frame, frame->document().url(), params_->match_about_blank); if (params_->is_web_view) { if (frame->parent()) { // This is a subframe inside , so allow it. return PermissionsData::ACCESS_ALLOWED; } return effective_document_url == params_->webview_src ? PermissionsData::ACCESS_ALLOWED : PermissionsData::ACCESS_DENIED; } DCHECK_EQ(injection_host->id().type(), HostID::EXTENSIONS); return injection_host->CanExecuteOnFrame( effective_document_url, content::RenderFrame::FromWebFrame(frame), tab_id, true /* is_declarative */); } std::vector ProgrammaticScriptInjector::GetJsSources( UserScript::RunLocation run_location) const { DCHECK_EQ(GetRunLocation(), run_location); DCHECK(params_->is_javascript); return std::vector( 1, blink::WebScriptSource( blink::WebString::fromUTF8(params_->code), params_->file_url)); } std::vector ProgrammaticScriptInjector::GetCssSources( UserScript::RunLocation run_location) const { DCHECK_EQ(GetRunLocation(), run_location); DCHECK(!params_->is_javascript); return std::vector(1, params_->code); } void ProgrammaticScriptInjector::GetRunInfo( ScriptsRunInfo* scripts_run_info, UserScript::RunLocation run_location) const { } void ProgrammaticScriptInjector::OnInjectionComplete( scoped_ptr execution_result, UserScript::RunLocation run_location, content::RenderFrame* render_frame) { DCHECK(results_.empty()); if (execution_result) results_.Append(std::move(execution_result)); Finish(std::string(), render_frame); } void ProgrammaticScriptInjector::OnWillNotInject( InjectFailureReason reason, content::RenderFrame* render_frame) { 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, render_frame); } bool ProgrammaticScriptInjector::CanShowUrlInError() const { if (params_->host_id.type() != HostID::EXTENSIONS) return false; const Extension* extension = RendererExtensionRegistry::Get()->GetByID(params_->host_id.id()); if (!extension) return false; return extension->permissions_data()->active_permissions().HasAPIPermission( APIPermission::kTab); } UserScript::RunLocation ProgrammaticScriptInjector::GetRunLocation() const { return static_cast(params_->run_at); } void ProgrammaticScriptInjector::Finish(const std::string& error, content::RenderFrame* render_frame) { DCHECK(!finished_); finished_ = true; // It's possible that the render frame was destroyed in the course of // injecting scripts. Don't respond if it was (the browser side watches for // frame deletions so nothing is left hanging). if (render_frame) { render_frame->Send( new ExtensionHostMsg_ExecuteCodeFinished( render_frame->GetRoutingID(), params_->request_id, error, url_, results_)); } } } // namespace extensions