blob: 3c9b938f27968c8aad806ceab25b73e300c99c0b [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 <vector>
#include "base/values.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/renderer/injection_host.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 {
// Watches for the deletion of a RenderFrame, after which is_valid will return
// false.
class ProgrammaticScriptInjector::FrameWatcher
: public content::RenderFrameObserver {
public:
explicit FrameWatcher(content::RenderFrame* render_frame)
: content::RenderFrameObserver(render_frame), is_valid_(true) {}
~FrameWatcher() override {}
bool is_frame_valid() const { return is_valid_; }
private:
void FrameDetached() override { is_valid_ = false; }
void OnDestruct() override { is_valid_ = false; }
bool is_valid_;
DISALLOW_COPY_AND_ASSIGN(FrameWatcher);
};
ProgrammaticScriptInjector::ProgrammaticScriptInjector(
const ExtensionMsg_ExecuteCode_Params& params,
content::RenderFrame* render_frame)
: params_(new ExtensionMsg_ExecuteCode_Params(params)),
url_(
ScriptContext::GetDataSourceURLForFrame(render_frame->GetWebFrame())),
frame_watcher_(new FrameWatcher(render_frame)),
finished_(false) {
effective_url_ = ScriptContext::GetEffectiveDocumentURL(
render_frame->GetWebFrame(), url_, params.match_about_blank);
}
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 <webview>, 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<blink::WebScriptSource> ProgrammaticScriptInjector::GetJsSources(
UserScript::RunLocation run_location) const {
DCHECK_EQ(GetRunLocation(), run_location);
DCHECK(params_->is_javascript);
return std::vector<blink::WebScriptSource>(
1,
blink::WebScriptSource(
blink::WebString::fromUTF8(params_->code), params_->file_url));
}
std::vector<std::string> ProgrammaticScriptInjector::GetCssSources(
UserScript::RunLocation run_location) const {
DCHECK_EQ(GetRunLocation(), run_location);
DCHECK(!params_->is_javascript);
return std::vector<std::string>(1, params_->code);
}
void ProgrammaticScriptInjector::GetRunInfo(
ScriptsRunInfo* scripts_run_info,
UserScript::RunLocation run_location) const {
}
void ProgrammaticScriptInjector::OnInjectionComplete(
scoped_ptr<base::Value> execution_result,
UserScript::RunLocation run_location) {
DCHECK(results_.empty());
if (execution_result)
results_.Append(execution_result.Pass());
Finish(std::string());
}
void ProgrammaticScriptInjector::OnWillNotInject(InjectFailureReason reason) {
std::string error;
switch (reason) {
case NOT_ALLOWED:
if (url_.SchemeIs(url::kAboutScheme)) {
error = ErrorUtils::FormatErrorMessage(
manifest_errors::kCannotAccessAboutUrl, url_.spec(),
effective_url_.GetOrigin().spec());
} else {
error = ErrorUtils::FormatErrorMessage(
manifest_errors::kCannotAccessPage, url_.spec());
}
break;
case EXTENSION_REMOVED: // no special error here.
case WONT_INJECT:
break;
}
Finish(error);
}
UserScript::RunLocation ProgrammaticScriptInjector::GetRunLocation() const {
return static_cast<UserScript::RunLocation>(params_->run_at);
}
void ProgrammaticScriptInjector::Finish(const std::string& error) {
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 (frame_watcher_->is_frame_valid()) {
frame_watcher_->render_frame()->Send(
new ExtensionHostMsg_ExecuteCodeFinished(
frame_watcher_->render_frame()->GetRoutingID(), params_->request_id,
error, url_, results_));
}
}
} // namespace extensions