summaryrefslogtreecommitdiff
path: root/chromium/extensions/browser/script_executor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/extensions/browser/script_executor.cc')
-rw-r--r--chromium/extensions/browser/script_executor.cc278
1 files changed, 278 insertions, 0 deletions
diff --git a/chromium/extensions/browser/script_executor.cc b/chromium/extensions/browser/script_executor.cc
new file mode 100644
index 00000000000..230bca3b3f8
--- /dev/null
+++ b/chromium/extensions/browser/script_executor.cc
@@ -0,0 +1,278 @@
+// 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/browser/script_executor.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "extensions/browser/extension_api_frame_id_map.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/script_execution_observer.h"
+#include "extensions/common/extension_messages.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_message_macros.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace extensions {
+
+namespace {
+
+const char* kRendererDestroyed = "The tab was closed.";
+const char* kFrameRemoved = "The frame was removed.";
+
+// A handler for a single injection request. On creation this will send the
+// injection request to the renderer, and it will be destroyed after either the
+// corresponding response comes from the renderer, or the renderer is destroyed.
+class Handler : public content::WebContentsObserver {
+ public:
+ Handler(base::ObserverList<ScriptExecutionObserver>* script_observers,
+ content::WebContents* web_contents,
+ const ExtensionMsg_ExecuteCode_Params& params,
+ ScriptExecutor::FrameScope scope,
+ int frame_id,
+ const ScriptExecutor::ExecuteScriptCallback& callback)
+ : content::WebContentsObserver(web_contents),
+ script_observers_(AsWeakPtr(script_observers)),
+ host_id_(params.host_id),
+ request_id_(params.request_id),
+ include_sub_frames_(scope == ScriptExecutor::INCLUDE_SUB_FRAMES),
+ root_rfh_(ExtensionApiFrameIdMap::GetRenderFrameHostById(web_contents,
+ frame_id)),
+ root_is_main_frame_(root_rfh_ ? !root_rfh_->GetParent() : false),
+ callback_(callback) {
+ if (root_rfh_) {
+ if (include_sub_frames_) {
+ web_contents->ForEachFrame(base::Bind(&Handler::SendExecuteCode,
+ base::Unretained(this), params));
+ } else {
+ SendExecuteCode(params, root_rfh_);
+ }
+ }
+
+ if (pending_render_frames_.empty())
+ Finish();
+ }
+
+ private:
+ // This class manages its own lifetime.
+ ~Handler() override {}
+
+ // content::WebContentsObserver:
+ void WebContentsDestroyed() override { Finish(); }
+
+ bool OnMessageReceived(const IPC::Message& message,
+ content::RenderFrameHost* render_frame_host) override {
+ // Unpack by hand to check the request_id, since there may be multiple
+ // requests in flight but only one is for this.
+ if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID)
+ return false;
+
+ int message_request_id;
+ base::PickleIterator iter(message);
+ CHECK(iter.ReadInt(&message_request_id));
+
+ if (message_request_id != request_id_)
+ return false;
+
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(Handler, message, render_frame_host)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
+ OnExecuteCodeFinished)
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ void RenderFrameDeleted(
+ content::RenderFrameHost* render_frame_host) override {
+ if (pending_render_frames_.erase(render_frame_host) == 1 &&
+ pending_render_frames_.empty()) {
+ Finish();
+ }
+ }
+
+ // Sends an ExecuteCode message to the given frame host, and increments
+ // the number of pending messages.
+ void SendExecuteCode(const ExtensionMsg_ExecuteCode_Params& params,
+ content::RenderFrameHost* frame) {
+ if (!frame->IsRenderFrameLive())
+ return;
+ DCHECK(!root_is_main_frame_ || ShouldIncludeFrame(frame));
+ if (!root_is_main_frame_ && !ShouldIncludeFrame(frame))
+ return;
+ pending_render_frames_.insert(frame);
+ frame->Send(new ExtensionMsg_ExecuteCode(frame->GetRoutingID(), params));
+ }
+
+ // Returns whether a frame is the root frame or a descendant of it.
+ bool ShouldIncludeFrame(content::RenderFrameHost* frame) {
+ while (frame) {
+ if (frame == root_rfh_)
+ return true;
+ frame = frame->GetParent();
+ }
+ return false;
+ }
+
+ // Handles the ExecuteCodeFinished message.
+ void OnExecuteCodeFinished(content::RenderFrameHost* render_frame_host,
+ int request_id,
+ const std::string& error,
+ const GURL& on_url,
+ const base::ListValue& result_list) {
+ DCHECK_EQ(request_id_, request_id);
+ DCHECK(!pending_render_frames_.empty());
+ bool erased = pending_render_frames_.erase(render_frame_host) == 1;
+ DCHECK(erased);
+ bool is_root_frame = root_rfh_ == render_frame_host;
+
+ // Set the result, if there is one.
+ const base::Value* script_value = nullptr;
+ if (result_list.Get(0u, &script_value)) {
+ // If this is the main result, we put it at index 0. Otherwise, we just
+ // append it at the end.
+ if (is_root_frame && !results_.empty())
+ CHECK(results_.Insert(0u, script_value->DeepCopy()));
+ else
+ results_.Append(script_value->DeepCopy());
+ }
+
+ if (is_root_frame) { // Only use the root frame's error and url.
+ root_frame_error_ = error;
+ root_frame_url_ = on_url;
+ }
+
+ // Wait until the final request finishes before reporting back.
+ if (pending_render_frames_.empty())
+ Finish();
+ }
+
+ void Finish() {
+ if (root_frame_url_.is_empty()) {
+ // We never finished the root frame injection.
+ root_frame_error_ =
+ root_is_main_frame_ ? kRendererDestroyed : kFrameRemoved;
+ results_.Clear();
+ }
+
+ if (script_observers_.get() && root_frame_error_.empty() &&
+ host_id_.type() == HostID::EXTENSIONS) {
+ ScriptExecutionObserver::ExecutingScriptsMap id_map;
+ id_map[host_id_.id()] = std::set<std::string>();
+ FOR_EACH_OBSERVER(
+ ScriptExecutionObserver, *script_observers_,
+ OnScriptsExecuted(web_contents(), id_map, root_frame_url_));
+ }
+
+ if (!callback_.is_null())
+ callback_.Run(root_frame_error_, root_frame_url_, results_);
+ delete this;
+ }
+
+ base::WeakPtr<base::ObserverList<ScriptExecutionObserver>> script_observers_;
+
+ // The id of the host (the extension or the webui) doing the injection.
+ HostID host_id_;
+
+ // The request id of the injection.
+ int request_id_;
+
+ // Whether to inject in |root_rfh_| and all of its descendant frames.
+ bool include_sub_frames_;
+
+ // The frame (and optionally its descendant frames) where the injection will
+ // occur.
+ content::RenderFrameHost* root_rfh_;
+
+ // Whether |root_rfh_| is the main frame of a tab.
+ bool root_is_main_frame_;
+
+ // The hosts of the still-running injections.
+ std::set<content::RenderFrameHost*> pending_render_frames_;
+
+ // The results of the injection.
+ base::ListValue results_;
+
+ // The error from injecting into the root frame.
+ std::string root_frame_error_;
+
+ // The url of the root frame.
+ GURL root_frame_url_;
+
+ // The callback to run after all injections complete.
+ ScriptExecutor::ExecuteScriptCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Handler);
+};
+
+} // namespace
+
+ScriptExecutionObserver::~ScriptExecutionObserver() {
+}
+
+ScriptExecutor::ScriptExecutor(
+ content::WebContents* web_contents,
+ base::ObserverList<ScriptExecutionObserver>* script_observers)
+ : next_request_id_(0),
+ web_contents_(web_contents),
+ script_observers_(script_observers) {
+ CHECK(web_contents_);
+}
+
+ScriptExecutor::~ScriptExecutor() {
+}
+
+void ScriptExecutor::ExecuteScript(const HostID& host_id,
+ ScriptExecutor::ScriptType script_type,
+ const std::string& code,
+ ScriptExecutor::FrameScope frame_scope,
+ int frame_id,
+ ScriptExecutor::MatchAboutBlank about_blank,
+ UserScript::RunLocation run_at,
+ ScriptExecutor::WorldType world_type,
+ ScriptExecutor::ProcessType process_type,
+ const GURL& webview_src,
+ const GURL& file_url,
+ bool user_gesture,
+ ScriptExecutor::ResultType result_type,
+ const ExecuteScriptCallback& callback) {
+ if (host_id.type() == HostID::EXTENSIONS) {
+ // Don't execute if the extension has been unloaded.
+ const Extension* extension =
+ ExtensionRegistry::Get(web_contents_->GetBrowserContext())
+ ->enabled_extensions().GetByID(host_id.id());
+ if (!extension)
+ return;
+ } else {
+ CHECK(process_type == WEB_VIEW_PROCESS);
+ }
+
+ ExtensionMsg_ExecuteCode_Params params;
+ params.request_id = next_request_id_++;
+ params.host_id = host_id;
+ params.is_javascript = (script_type == JAVASCRIPT);
+ params.code = code;
+ params.match_about_blank = (about_blank == MATCH_ABOUT_BLANK);
+ params.run_at = static_cast<int>(run_at);
+ params.in_main_world = (world_type == MAIN_WORLD);
+ params.is_web_view = (process_type == WEB_VIEW_PROCESS);
+ params.webview_src = webview_src;
+ params.file_url = file_url;
+ params.wants_result = (result_type == JSON_SERIALIZED_RESULT);
+ params.user_gesture = user_gesture;
+
+ // Handler handles IPCs and deletes itself on completion.
+ new Handler(script_observers_, web_contents_, params, frame_scope, frame_id,
+ callback);
+}
+
+} // namespace extensions