1// Copyright (c) 2012 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 "chrome/browser/extensions/script_executor.h"
6
7#include "base/callback.h"
8#include "base/logging.h"
9#include "base/pickle.h"
10#include "chrome/common/extensions/extension_messages.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 "ipc/ipc_message.h"
15#include "ipc/ipc_message_macros.h"
16
17namespace base {
18class ListValue;
19}  // namespace base
20
21namespace extensions {
22
23namespace {
24
25const char* kRendererDestroyed = "The tab was closed.";
26
27// A handler for a single injection request. On creation this will send the
28// injection request to the renderer, and it will be destroyed after either the
29// corresponding response comes from the renderer, or the renderer is destroyed.
30class Handler : public content::WebContentsObserver {
31 public:
32  Handler(ObserverList<TabHelper::ScriptExecutionObserver>* script_observers,
33          content::WebContents* web_contents,
34          const ExtensionMsg_ExecuteCode_Params& params,
35          const ScriptExecutor::ExecuteScriptCallback& callback)
36          : content::WebContentsObserver(web_contents),
37            script_observers_(AsWeakPtr(script_observers)),
38            extension_id_(params.extension_id),
39            request_id_(params.request_id),
40            callback_(callback) {
41    content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
42    rvh->Send(new ExtensionMsg_ExecuteCode(rvh->GetRoutingID(), params));
43  }
44
45  virtual ~Handler() {}
46
47  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
48    // Unpack by hand to check the request_id, since there may be multiple
49    // requests in flight but only one is for this.
50    if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID)
51      return false;
52
53    int message_request_id;
54    PickleIterator iter(message);
55    CHECK(message.ReadInt(&iter, &message_request_id));
56
57    if (message_request_id != request_id_)
58      return false;
59
60    IPC_BEGIN_MESSAGE_MAP(Handler, message)
61      IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
62                          OnExecuteCodeFinished)
63    IPC_END_MESSAGE_MAP()
64    return true;
65  }
66
67  virtual void WebContentsDestroyed(content::WebContents* tab) OVERRIDE {
68    base::ListValue val;
69    callback_.Run(kRendererDestroyed, -1, GURL(std::string()), val);
70    delete this;
71  }
72
73 private:
74  void OnExecuteCodeFinished(int request_id,
75                             const std::string& error,
76                             int32 on_page_id,
77                             const GURL& on_url,
78                             const base::ListValue& script_result) {
79    if (script_observers_.get() && error.empty()) {
80      TabHelper::ScriptExecutionObserver::ExecutingScriptsMap id_map;
81      id_map[extension_id_] = std::set<std::string>();
82      FOR_EACH_OBSERVER(TabHelper::ScriptExecutionObserver, *script_observers_,
83                        OnScriptsExecuted(web_contents(),
84                                          id_map,
85                                          on_page_id,
86                                          on_url));
87    }
88
89    callback_.Run(error, on_page_id, on_url, script_result);
90    delete this;
91  }
92
93  base::WeakPtr<ObserverList<TabHelper::ScriptExecutionObserver> >
94      script_observers_;
95  std::string extension_id_;
96  int request_id_;
97  ScriptExecutor::ExecuteScriptCallback callback_;
98};
99
100}  // namespace
101
102ScriptExecutor::ScriptExecutor(
103    content::WebContents* web_contents,
104    ObserverList<TabHelper::ScriptExecutionObserver>* script_observers)
105    : next_request_id_(0),
106      web_contents_(web_contents),
107      script_observers_(script_observers) {}
108
109ScriptExecutor::~ScriptExecutor() {}
110
111void ScriptExecutor::ExecuteScript(
112    const std::string& extension_id,
113    ScriptExecutor::ScriptType script_type,
114    const std::string& code,
115    ScriptExecutor::FrameScope frame_scope,
116    UserScript::RunLocation run_at,
117    ScriptExecutor::WorldType world_type,
118    bool is_web_view,
119    const ExecuteScriptCallback& callback) {
120  ExtensionMsg_ExecuteCode_Params params;
121  params.request_id = next_request_id_++;
122  params.extension_id = extension_id;
123  params.is_javascript = (script_type == JAVASCRIPT);
124  params.code = code;
125  params.all_frames = (frame_scope == ALL_FRAMES);
126  params.run_at = static_cast<int>(run_at);
127  params.in_main_world = (world_type == MAIN_WORLD);
128  params.is_web_view = is_web_view;
129
130  // Handler handles IPCs and deletes itself on completion.
131  new Handler(script_observers_, web_contents_, params, callback);
132}
133
134}  // namespace extensions
135