1 // Copyright (c) 2011 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/execute_code_in_tab_function.h"
6
7 #include "base/callback.h"
8 #include "base/string_util.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_tabs_module.h"
12 #include "chrome/browser/extensions/extension_tabs_module_constants.h"
13 #include "chrome/browser/extensions/file_reader.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "chrome/common/extensions/extension_error_utils.h"
20 #include "chrome/common/extensions/extension_messages.h"
21 #include "content/browser/renderer_host/render_view_host.h"
22 #include "content/browser/tab_contents/tab_contents.h"
23 #include "content/browser/renderer_host/render_view_host.h"
24 #include "content/common/notification_service.h"
25
26 namespace keys = extension_tabs_module_constants;
27
ExecuteCodeInTabFunction()28 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
29 : ALLOW_THIS_IN_INITIALIZER_LIST(registrar_(this)),
30 execute_tab_id_(-1),
31 all_frames_(false) {
32 }
33
~ExecuteCodeInTabFunction()34 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {
35 }
36
RunImpl()37 bool ExecuteCodeInTabFunction::RunImpl() {
38 DictionaryValue* script_info;
39 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &script_info));
40 size_t number_of_value = script_info->size();
41 if (number_of_value == 0) {
42 error_ = keys::kNoCodeOrFileToExecuteError;
43 return false;
44 } else {
45 bool has_code = script_info->HasKey(keys::kCodeKey);
46 bool has_file = script_info->HasKey(keys::kFileKey);
47 if (has_code && has_file) {
48 error_ = keys::kMoreThanOneValuesError;
49 return false;
50 } else if (!has_code && !has_file) {
51 error_ = keys::kNoCodeOrFileToExecuteError;
52 return false;
53 }
54 }
55
56 execute_tab_id_ = -1;
57 Browser* browser = NULL;
58 TabContentsWrapper* contents = NULL;
59
60 // If |tab_id| is specified, look for it. Otherwise default to selected tab
61 // in the current window.
62 Value* tab_value = NULL;
63 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
64 if (tab_value->IsType(Value::TYPE_NULL)) {
65 browser = GetCurrentBrowser();
66 if (!browser) {
67 error_ = keys::kNoCurrentWindowError;
68 return false;
69 }
70 if (!ExtensionTabUtil::GetDefaultTab(browser, &contents, &execute_tab_id_))
71 return false;
72 } else {
73 EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&execute_tab_id_));
74 if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(),
75 include_incognito(),
76 &browser, NULL, &contents, NULL)) {
77 return false;
78 }
79 }
80
81 // NOTE: This can give the wrong answer due to race conditions, but it is OK,
82 // we check again in the renderer.
83 CHECK(browser);
84 CHECK(contents);
85 if (!GetExtension()->CanExecuteScriptOnPage(
86 contents->tab_contents()->GetURL(), NULL, &error_)) {
87 return false;
88 }
89
90 if (script_info->HasKey(keys::kAllFramesKey)) {
91 if (!script_info->GetBoolean(keys::kAllFramesKey, &all_frames_))
92 return false;
93 }
94
95 std::string code_string;
96 if (script_info->HasKey(keys::kCodeKey)) {
97 if (!script_info->GetString(keys::kCodeKey, &code_string))
98 return false;
99 }
100
101 if (!code_string.empty()) {
102 if (!Execute(code_string))
103 return false;
104 return true;
105 }
106
107 std::string relative_path;
108 if (script_info->HasKey(keys::kFileKey)) {
109 if (!script_info->GetString(keys::kFileKey, &relative_path))
110 return false;
111 resource_ = GetExtension()->GetResource(relative_path);
112 }
113 if (resource_.extension_root().empty() || resource_.relative_path().empty()) {
114 error_ = keys::kNoCodeOrFileToExecuteError;
115 return false;
116 }
117
118 scoped_refptr<FileReader> file_reader(new FileReader(
119 resource_, NewCallback(this, &ExecuteCodeInTabFunction::DidLoadFile)));
120 file_reader->Start();
121 AddRef(); // Keep us alive until DidLoadFile is called.
122
123 return true;
124 }
125
DidLoadFile(bool success,const std::string & data)126 void ExecuteCodeInTabFunction::DidLoadFile(bool success,
127 const std::string& data) {
128 if (success) {
129 Execute(data);
130 } else {
131 #if defined(OS_POSIX)
132 // TODO(viettrungluu): bug: there's no particular reason the path should be
133 // UTF-8, in which case this may fail.
134 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kLoadFileError,
135 resource_.relative_path().value());
136 #elif defined(OS_WIN)
137 error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kLoadFileError,
138 WideToUTF8(resource_.relative_path().value()));
139 #endif // OS_WIN
140 SendResponse(false);
141 }
142 Release(); // Balance the AddRef taken in RunImpl
143 }
144
Execute(const std::string & code_string)145 bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) {
146 TabContentsWrapper* contents = NULL;
147 Browser* browser = NULL;
148
149 bool success = ExtensionTabUtil::GetTabById(
150 execute_tab_id_, profile(), include_incognito(), &browser, NULL,
151 &contents, NULL) && contents && browser;
152
153 if (!success) {
154 SendResponse(false);
155 return false;
156 }
157
158 const Extension* extension = GetExtension();
159 if (!extension) {
160 SendResponse(false);
161 return false;
162 }
163
164 bool is_js_code = true;
165 std::string function_name = name();
166 if (function_name == TabsInsertCSSFunction::function_name()) {
167 is_js_code = false;
168 } else if (function_name != TabsExecuteScriptFunction::function_name()) {
169 DCHECK(false);
170 }
171
172 ExtensionMsg_ExecuteCode_Params params;
173 params.request_id = request_id();
174 params.extension_id = extension->id();
175 params.is_javascript = is_js_code;
176 params.code = code_string;
177 params.all_frames = all_frames_;
178 params.in_main_world = false;
179 contents->render_view_host()->Send(new ExtensionMsg_ExecuteCode(
180 contents->render_view_host()->routing_id(), params));
181
182 registrar_.Observe(contents->tab_contents());
183 AddRef(); // balanced in OnExecuteCodeFinished()
184 return true;
185 }
186
OnMessageReceived(const IPC::Message & message)187 bool ExecuteCodeInTabFunction::OnMessageReceived(const IPC::Message& message) {
188 if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID)
189 return false;
190
191 int message_request_id;
192 void* iter = NULL;
193 if (!message.ReadInt(&iter, &message_request_id)) {
194 NOTREACHED() << "malformed extension message";
195 return true;
196 }
197
198 if (message_request_id != request_id())
199 return false;
200
201 IPC_BEGIN_MESSAGE_MAP(ExecuteCodeInTabFunction, message)
202 IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
203 OnExecuteCodeFinished)
204 IPC_END_MESSAGE_MAP()
205 return true;
206 }
207
OnExecuteCodeFinished(int request_id,bool success,const std::string & error)208 void ExecuteCodeInTabFunction::OnExecuteCodeFinished(int request_id,
209 bool success,
210 const std::string& error) {
211 if (!error.empty()) {
212 CHECK(!success);
213 error_ = error;
214 }
215
216 SendResponse(success);
217
218 registrar_.Observe(NULL);
219 Release(); // balanced in Execute()
220 }
221