OLD | NEW |
| (Empty) |
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/api/tabs/execute_code_in_tab_function.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/string_util.h" | |
9 #include "base/utf_string_conversions.h" | |
10 #include "chrome/browser/extensions/api/tabs/tabs.h" | |
11 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" | |
12 #include "chrome/browser/extensions/extension_service.h" | |
13 #include "chrome/browser/extensions/extension_tab_util.h" | |
14 #include "chrome/browser/extensions/file_reader.h" | |
15 #include "chrome/browser/extensions/script_executor.h" | |
16 #include "chrome/browser/extensions/tab_helper.h" | |
17 #include "chrome/browser/profiles/profile.h" | |
18 #include "chrome/browser/ui/browser.h" | |
19 #include "chrome/common/extensions/api/tabs.h" | |
20 #include "chrome/common/extensions/extension.h" | |
21 #include "chrome/common/extensions/extension_constants.h" | |
22 #include "chrome/common/extensions/extension_file_util.h" | |
23 #include "chrome/common/extensions/extension_l10n_util.h" | |
24 #include "chrome/common/extensions/extension_manifest_constants.h" | |
25 #include "chrome/common/extensions/extension_messages.h" | |
26 #include "chrome/common/extensions/message_bundle.h" | |
27 #include "content/public/browser/render_view_host.h" | |
28 #include "content/public/browser/web_contents.h" | |
29 #include "extensions/common/error_utils.h" | |
30 | |
31 using content::BrowserThread; | |
32 using extensions::api::tabs::InjectDetails; | |
33 using extensions::ErrorUtils; | |
34 using extensions::ScriptExecutor; | |
35 using extensions::UserScript; | |
36 | |
37 namespace keys = extensions::tabs_constants; | |
38 | |
39 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() | |
40 : execute_tab_id_(-1) { | |
41 } | |
42 | |
43 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} | |
44 | |
45 bool ExecuteCodeInTabFunction::HasPermission() { | |
46 if (Init() && | |
47 extension_->HasAPIPermissionForTab(execute_tab_id_, | |
48 extensions::APIPermission::kTab)) { | |
49 return true; | |
50 } | |
51 return ExtensionFunction::HasPermission(); | |
52 } | |
53 | |
54 bool ExecuteCodeInTabFunction::RunImpl() { | |
55 EXTENSION_FUNCTION_VALIDATE(Init()); | |
56 | |
57 if (!details_->code.get() && !details_->file.get()) { | |
58 error_ = keys::kNoCodeOrFileToExecuteError; | |
59 return false; | |
60 } | |
61 if (details_->code.get() && details_->file.get()) { | |
62 error_ = keys::kMoreThanOneValuesError; | |
63 return false; | |
64 } | |
65 | |
66 content::WebContents* contents = NULL; | |
67 | |
68 // If |tab_id| is specified, look for the tab. Otherwise default to selected | |
69 // tab in the current window. | |
70 CHECK_GE(execute_tab_id_, 0); | |
71 if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(), | |
72 include_incognito(), | |
73 NULL, NULL, &contents, NULL)) { | |
74 return false; | |
75 } | |
76 | |
77 // NOTE: This can give the wrong answer due to race conditions, but it is OK, | |
78 // we check again in the renderer. | |
79 CHECK(contents); | |
80 if (!GetExtension()->CanExecuteScriptOnPage(contents->GetURL(), | |
81 contents->GetURL(), | |
82 execute_tab_id_, | |
83 NULL, | |
84 &error_)) { | |
85 return false; | |
86 } | |
87 | |
88 if (details_->code.get()) | |
89 return Execute(*details_->code); | |
90 | |
91 CHECK(details_->file.get()); | |
92 resource_ = GetExtension()->GetResource(*details_->file); | |
93 | |
94 if (resource_.extension_root().empty() || resource_.relative_path().empty()) { | |
95 error_ = keys::kNoCodeOrFileToExecuteError; | |
96 return false; | |
97 } | |
98 | |
99 scoped_refptr<FileReader> file_reader(new FileReader( | |
100 resource_, base::Bind(&ExecuteCodeInTabFunction::DidLoadFile, this))); | |
101 file_reader->Start(); | |
102 | |
103 return true; | |
104 } | |
105 | |
106 void ExecuteCodeInTabFunction::OnExecuteCodeFinished(const std::string& error, | |
107 int32 on_page_id, | |
108 const GURL& on_url, | |
109 const ListValue& result) { | |
110 if (!error.empty()) | |
111 SetError(error); | |
112 | |
113 SendResponse(error.empty()); | |
114 } | |
115 | |
116 void TabsExecuteScriptFunction::OnExecuteCodeFinished(const std::string& error, | |
117 int32 on_page_id, | |
118 const GURL& on_url, | |
119 const ListValue& result) { | |
120 if (error.empty()) | |
121 SetResult(result.DeepCopy()); | |
122 ExecuteCodeInTabFunction::OnExecuteCodeFinished(error, on_page_id, on_url, | |
123 result); | |
124 } | |
125 | |
126 bool ExecuteCodeInTabFunction::Init() { | |
127 if (details_.get()) | |
128 return true; | |
129 | |
130 // |tab_id| is optional so it's ok if it's not there. | |
131 int tab_id = -1; | |
132 args_->GetInteger(0, &tab_id); | |
133 | |
134 // |details| are not optional. | |
135 DictionaryValue* details_value = NULL; | |
136 if (!args_->GetDictionary(1, &details_value)) | |
137 return false; | |
138 scoped_ptr<InjectDetails> details(new InjectDetails()); | |
139 if (!InjectDetails::Populate(*details_value, details.get())) | |
140 return false; | |
141 | |
142 // If the tab ID is -1 then it needs to be converted to the currently active | |
143 // tab's ID. | |
144 if (tab_id == -1) { | |
145 Browser* browser = GetCurrentBrowser(); | |
146 if (!browser) | |
147 return false; | |
148 content::WebContents* web_contents = NULL; | |
149 if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, &tab_id)) | |
150 return false; | |
151 } | |
152 | |
153 execute_tab_id_ = tab_id; | |
154 details_ = details.Pass(); | |
155 return true; | |
156 } | |
157 | |
158 void ExecuteCodeInTabFunction::DidLoadFile(bool success, | |
159 const std::string& data) { | |
160 std::string function_name = name(); | |
161 const extensions::Extension* extension = GetExtension(); | |
162 | |
163 // Check if the file is CSS and needs localization. | |
164 if (success && | |
165 function_name == TabsInsertCSSFunction::function_name() && | |
166 extension != NULL && | |
167 data.find( | |
168 extensions::MessageBundle::kMessageBegin) != std::string::npos) { | |
169 BrowserThread::PostTask( | |
170 BrowserThread::FILE, FROM_HERE, | |
171 base::Bind(&ExecuteCodeInTabFunction::LocalizeCSS, this, | |
172 data, | |
173 extension->id(), | |
174 extension->path(), | |
175 extension->default_locale())); | |
176 } else { | |
177 DidLoadAndLocalizeFile(success, data); | |
178 } | |
179 } | |
180 | |
181 void ExecuteCodeInTabFunction::LocalizeCSS( | |
182 const std::string& data, | |
183 const std::string& extension_id, | |
184 const FilePath& extension_path, | |
185 const std::string& extension_default_locale) { | |
186 scoped_ptr<SubstitutionMap> localization_messages( | |
187 extension_file_util::LoadMessageBundleSubstitutionMap( | |
188 extension_path, extension_id, extension_default_locale)); | |
189 | |
190 // We need to do message replacement on the data, so it has to be mutable. | |
191 std::string css_data = data; | |
192 std::string error; | |
193 extensions::MessageBundle::ReplaceMessagesWithExternalDictionary( | |
194 *localization_messages, &css_data, &error); | |
195 | |
196 // Call back DidLoadAndLocalizeFile on the UI thread. The success parameter | |
197 // is always true, because if loading had failed, we wouldn't have had | |
198 // anything to localize. | |
199 BrowserThread::PostTask( | |
200 BrowserThread::UI, FROM_HERE, | |
201 base::Bind(&ExecuteCodeInTabFunction::DidLoadAndLocalizeFile, this, | |
202 true, css_data)); | |
203 } | |
204 | |
205 void ExecuteCodeInTabFunction::DidLoadAndLocalizeFile(bool success, | |
206 const std::string& data) { | |
207 if (success) { | |
208 if (!Execute(data)) | |
209 SendResponse(false); | |
210 } else { | |
211 #if defined(OS_POSIX) | |
212 // TODO(viettrungluu): bug: there's no particular reason the path should be | |
213 // UTF-8, in which case this may fail. | |
214 error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError, | |
215 resource_.relative_path().value()); | |
216 #elif defined(OS_WIN) | |
217 error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError, | |
218 WideToUTF8(resource_.relative_path().value())); | |
219 #endif // OS_WIN | |
220 SendResponse(false); | |
221 } | |
222 } | |
223 | |
224 bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) { | |
225 content::WebContents* contents = NULL; | |
226 Browser* browser = NULL; | |
227 | |
228 bool success = ExtensionTabUtil::GetTabById( | |
229 execute_tab_id_, profile(), include_incognito(), &browser, NULL, | |
230 &contents, NULL) && contents && browser; | |
231 | |
232 if (!success) | |
233 return false; | |
234 | |
235 const extensions::Extension* extension = GetExtension(); | |
236 if (!extension) | |
237 return false; | |
238 | |
239 ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT; | |
240 std::string function_name = name(); | |
241 if (function_name == TabsInsertCSSFunction::function_name()) { | |
242 script_type = ScriptExecutor::CSS; | |
243 } else if (function_name != TabsExecuteScriptFunction::function_name()) { | |
244 NOTREACHED(); | |
245 } | |
246 | |
247 ScriptExecutor::FrameScope frame_scope = | |
248 details_->all_frames.get() && *details_->all_frames ? | |
249 ScriptExecutor::ALL_FRAMES : | |
250 ScriptExecutor::TOP_FRAME; | |
251 | |
252 UserScript::RunLocation run_at = UserScript::UNDEFINED; | |
253 switch (details_->run_at) { | |
254 case InjectDetails::RUN_AT_NONE: | |
255 case InjectDetails::RUN_AT_DOCUMENT_IDLE: | |
256 run_at = UserScript::DOCUMENT_IDLE; | |
257 break; | |
258 case InjectDetails::RUN_AT_DOCUMENT_START: | |
259 run_at = UserScript::DOCUMENT_START; | |
260 break; | |
261 case InjectDetails::RUN_AT_DOCUMENT_END: | |
262 run_at = UserScript::DOCUMENT_END; | |
263 break; | |
264 } | |
265 CHECK_NE(UserScript::UNDEFINED, run_at); | |
266 | |
267 extensions::TabHelper::FromWebContents(contents)-> | |
268 script_executor()->ExecuteScript( | |
269 extension->id(), | |
270 script_type, | |
271 code_string, | |
272 frame_scope, | |
273 run_at, | |
274 ScriptExecutor::ISOLATED_WORLD, | |
275 base::Bind(&ExecuteCodeInTabFunction::OnExecuteCodeFinished, this)); | |
276 return true; | |
277 } | |
OLD | NEW |