blob: b01e77c21f3d5acc560b48c8e84de5909ff12849 [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/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/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(
const ExtensionMsg_ExecuteCode_Params& params)
: params_(new ExtensionMsg_ExecuteCode_Params(params)),
finished_(false) {
}
ProgrammaticScriptInjector::~ProgrammaticScriptInjector() {
}
UserScript::InjectionType ProgrammaticScriptInjector::script_type()
const {
return UserScript::PROGRAMMATIC_SCRIPT;
}
bool ProgrammaticScriptInjector::IsUserGesture() const {
return params_->user_gesture;
}
CSSOrigin ProgrammaticScriptInjector::GetCssOrigin() const {
return params_->css_origin;
}
bool ProgrammaticScriptInjector::IsRemovingCSS() const {
return params_->action_type == UserScript::ActionType::REMOVE_CSS;
}
bool ProgrammaticScriptInjector::IsAddingCSS() const {
return params_->action_type == UserScript::ActionType::ADD_CSS;
}
const base::Optional<std::string>
ProgrammaticScriptInjector::GetInjectionKey() const {
return params_->injection_key;
}
bool ProgrammaticScriptInjector::ExpectsResults() const {
return params_->wants_result;
}
bool ProgrammaticScriptInjector::ShouldInjectJs(
UserScript::RunLocation run_location,
const std::set<std::string>& executing_scripts) const {
return params_->run_at == run_location &&
params_->action_type == UserScript::ActionType::ADD_JAVASCRIPT;
}
bool ProgrammaticScriptInjector::ShouldInjectOrRemoveCss(
UserScript::RunLocation run_location,
const std::set<std::string>& injected_stylesheets) const {
return params_->run_at == run_location &&
(params_->action_type == UserScript::ActionType::ADD_CSS ||
params_->action_type == UserScript::ActionType::REMOVE_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(), HostID::EXTENSIONS);
return injection_host->CanExecuteOnFrame(
effective_document_url,
content::RenderFrame::FromWebFrame(frame),
tab_id,
true /* is_declarative */);
}
std::vector<blink::WebScriptSource> ProgrammaticScriptInjector::GetJsSources(
UserScript::RunLocation run_location,
std::set<std::string>* executing_scripts,
size_t* num_injected_js_scripts) const {
DCHECK_EQ(params_->run_at, run_location);
DCHECK_EQ(params_->action_type, UserScript::ActionType::ADD_JAVASCRIPT);
return std::vector<blink::WebScriptSource>(
1, blink::WebScriptSource(blink::WebString::FromUTF8(params_->code),
params_->script_url));
}
std::vector<blink::WebString> ProgrammaticScriptInjector::GetCssSources(
UserScript::RunLocation run_location,
std::set<std::string>* injected_stylesheets,
size_t* num_injected_stylesheets) const {
DCHECK_EQ(params_->run_at, run_location);
DCHECK(params_->action_type == UserScript::ActionType::ADD_CSS ||
params_->action_type == UserScript::ActionType::REMOVE_CSS);
return std::vector<blink::WebString>(
1, blink::WebString::FromUTF8(params_->code));
}
void ProgrammaticScriptInjector::OnInjectionComplete(
std::unique_ptr<base::Value> execution_result,
UserScript::RunLocation run_location,
content::RenderFrame* render_frame) {
DCHECK(!result_.has_value());
if (execution_result) {
result_ = base::Value::FromUniquePtrValue(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);
}
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_,
result_));
}
}
} // namespace extensions