| // 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 |