1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/browser/script_executor.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/logging.h"
10#include "base/pickle.h"
11#include "content/public/browser/render_view_host.h"
12#include "content/public/browser/web_contents.h"
13#include "content/public/browser/web_contents_observer.h"
14#include "extensions/browser/extension_registry.h"
15#include "extensions/browser/script_execution_observer.h"
16#include "extensions/common/extension_messages.h"
17#include "ipc/ipc_message.h"
18#include "ipc/ipc_message_macros.h"
19
20namespace base {
21class ListValue;
22}  // namespace base
23
24namespace extensions {
25
26namespace {
27
28const char* kRendererDestroyed = "The tab was closed.";
29
30// A handler for a single injection request. On creation this will send the
31// injection request to the renderer, and it will be destroyed after either the
32// corresponding response comes from the renderer, or the renderer is destroyed.
33class Handler : public content::WebContentsObserver {
34 public:
35  Handler(ObserverList<ScriptExecutionObserver>* script_observers,
36          content::WebContents* web_contents,
37          const ExtensionMsg_ExecuteCode_Params& params,
38          const ScriptExecutor::ExecuteScriptCallback& callback)
39      : content::WebContentsObserver(web_contents),
40        script_observers_(AsWeakPtr(script_observers)),
41        extension_id_(params.extension_id),
42        request_id_(params.request_id),
43        callback_(callback) {
44    content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
45    rvh->Send(new ExtensionMsg_ExecuteCode(rvh->GetRoutingID(), params));
46  }
47
48  virtual ~Handler() {}
49
50  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
51    // Unpack by hand to check the request_id, since there may be multiple
52    // requests in flight but only one is for this.
53    if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID)
54      return false;
55
56    int message_request_id;
57    PickleIterator iter(message);
58    CHECK(message.ReadInt(&iter, &message_request_id));
59
60    if (message_request_id != request_id_)
61      return false;
62
63    IPC_BEGIN_MESSAGE_MAP(Handler, message)
64      IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
65                          OnExecuteCodeFinished)
66    IPC_END_MESSAGE_MAP()
67    return true;
68  }
69
70  virtual void WebContentsDestroyed() OVERRIDE {
71    base::ListValue val;
72    callback_.Run(kRendererDestroyed, GURL(std::string()), val);
73    delete this;
74  }
75
76 private:
77  void OnExecuteCodeFinished(int request_id,
78                             const std::string& error,
79                             const GURL& on_url,
80                             const base::ListValue& script_result) {
81    if (script_observers_.get() && error.empty()) {
82      ScriptExecutionObserver::ExecutingScriptsMap id_map;
83      id_map[extension_id_] = std::set<std::string>();
84      FOR_EACH_OBSERVER(ScriptExecutionObserver,
85                        *script_observers_,
86                        OnScriptsExecuted(web_contents(), id_map, on_url));
87    }
88
89    callback_.Run(error, on_url, script_result);
90    delete this;
91  }
92
93  base::WeakPtr<ObserverList<ScriptExecutionObserver> > script_observers_;
94  std::string extension_id_;
95  int request_id_;
96  ScriptExecutor::ExecuteScriptCallback callback_;
97};
98
99}  // namespace
100
101ScriptExecutionObserver::~ScriptExecutionObserver() {
102}
103
104ScriptExecutor::ScriptExecutor(
105    content::WebContents* web_contents,
106    ObserverList<ScriptExecutionObserver>* script_observers)
107    : next_request_id_(0),
108      web_contents_(web_contents),
109      script_observers_(script_observers) {
110  CHECK(web_contents_);
111}
112
113ScriptExecutor::~ScriptExecutor() {
114}
115
116void ScriptExecutor::ExecuteScript(const std::string& extension_id,
117                                   ScriptExecutor::ScriptType script_type,
118                                   const std::string& code,
119                                   ScriptExecutor::FrameScope frame_scope,
120                                   ScriptExecutor::MatchAboutBlank about_blank,
121                                   UserScript::RunLocation run_at,
122                                   ScriptExecutor::WorldType world_type,
123                                   ScriptExecutor::ProcessType process_type,
124                                   const GURL& webview_src,
125                                   const GURL& file_url,
126                                   bool user_gesture,
127                                   ScriptExecutor::ResultType result_type,
128                                   const ExecuteScriptCallback& callback) {
129  // Don't execute if the extension has been unloaded.
130  const Extension* extension =
131      ExtensionRegistry::Get(web_contents_->GetBrowserContext())
132          ->enabled_extensions().GetByID(extension_id);
133  if (!extension)
134    return;
135
136  ExtensionMsg_ExecuteCode_Params params;
137  params.request_id = next_request_id_++;
138  params.extension_id = extension_id;
139  params.is_javascript = (script_type == JAVASCRIPT);
140  params.code = code;
141  params.all_frames = (frame_scope == ALL_FRAMES);
142  params.match_about_blank = (about_blank == MATCH_ABOUT_BLANK);
143  params.run_at = static_cast<int>(run_at);
144  params.in_main_world = (world_type == MAIN_WORLD);
145  params.is_web_view = (process_type == WEB_VIEW_PROCESS);
146  params.webview_src = webview_src;
147  params.file_url = file_url;
148  params.wants_result = (result_type == JSON_SERIALIZED_RESULT);
149  params.user_gesture = user_gesture;
150
151  // Handler handles IPCs and deletes itself on completion.
152  new Handler(script_observers_, web_contents_, params, callback);
153}
154
155}  // namespace extensions
156