1 // Copyright (c) 2013 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/execute_code_function.h"
6
7 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
8 #include "chrome/browser/extensions/script_executor.h"
9 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
10 #include "extensions/browser/component_extension_resource_manager.h"
11 #include "extensions/browser/extensions_browser_client.h"
12 #include "extensions/browser/file_reader.h"
13 #include "extensions/common/error_utils.h"
14 #include "extensions/common/extension_messages.h"
15 #include "extensions/common/file_util.h"
16 #include "extensions/common/message_bundle.h"
17 #include "net/base/filename_util.h"
18 #include "ui/base/resource/resource_bundle.h"
19
20 namespace extensions {
21
22 namespace keys = tabs_constants;
23 using api::tabs::InjectDetails;
24
ExecuteCodeFunction()25 ExecuteCodeFunction::ExecuteCodeFunction() {
26 }
27
~ExecuteCodeFunction()28 ExecuteCodeFunction::~ExecuteCodeFunction() {
29 }
30
DidLoadFile(bool success,const std::string & data)31 void ExecuteCodeFunction::DidLoadFile(bool success,
32 const std::string& data) {
33
34 if (!success || !details_->file) {
35 DidLoadAndLocalizeFile(success, data);
36 return;
37 }
38
39 ScriptExecutor::ScriptType script_type =
40 ShouldInsertCSS() ? ScriptExecutor::CSS : ScriptExecutor::JAVASCRIPT;
41
42 std::string extension_id;
43 base::FilePath extension_path;
44 std::string extension_default_locale;
45 const Extension* extension = GetExtension();
46 if (extension) {
47 extension_id = extension->id();
48 extension_path = extension->path();
49 extension_default_locale = LocaleInfo::GetDefaultLocale(extension);
50 }
51
52 content::BrowserThread::PostTask(
53 content::BrowserThread::FILE, FROM_HERE,
54 base::Bind(&ExecuteCodeFunction::GetFileURLAndLocalizeCSS, this,
55 script_type,
56 data,
57 extension_id,
58 extension_path,
59 extension_default_locale));
60 }
61
GetFileURLAndLocalizeCSS(ScriptExecutor::ScriptType script_type,const std::string & data,const std::string & extension_id,const base::FilePath & extension_path,const std::string & extension_default_locale)62 void ExecuteCodeFunction::GetFileURLAndLocalizeCSS(
63 ScriptExecutor::ScriptType script_type,
64 const std::string& data,
65 const std::string& extension_id,
66 const base::FilePath& extension_path,
67 const std::string& extension_default_locale) {
68
69 std::string localized_data = data;
70 // Check if the file is CSS and needs localization.
71 if ((script_type == ScriptExecutor::CSS) &&
72 !extension_id.empty() &&
73 (data.find(MessageBundle::kMessageBegin) != std::string::npos)) {
74 scoped_ptr<SubstitutionMap> localization_messages(
75 file_util::LoadMessageBundleSubstitutionMap(
76 extension_path, extension_id, extension_default_locale));
77
78 // We need to do message replacement on the data, so it has to be mutable.
79 std::string error;
80 MessageBundle::ReplaceMessagesWithExternalDictionary(*localization_messages,
81 &localized_data,
82 &error);
83 }
84
85 file_url_ = net::FilePathToFileURL(resource_.GetFilePath());
86
87 // Call back DidLoadAndLocalizeFile on the UI thread. The success parameter
88 // is always true, because if loading had failed, we wouldn't have had
89 // anything to localize.
90 content::BrowserThread::PostTask(
91 content::BrowserThread::UI, FROM_HERE,
92 base::Bind(&ExecuteCodeFunction::DidLoadAndLocalizeFile, this,
93 true, localized_data));
94 }
95
DidLoadAndLocalizeFile(bool success,const std::string & data)96 void ExecuteCodeFunction::DidLoadAndLocalizeFile(bool success,
97 const std::string& data) {
98 if (success) {
99 if (!Execute(data))
100 SendResponse(false);
101 } else {
102 // TODO(viettrungluu): bug: there's no particular reason the path should be
103 // UTF-8, in which case this may fail.
104 error_ = ErrorUtils::FormatErrorMessage(keys::kLoadFileError,
105 resource_.relative_path().AsUTF8Unsafe());
106 SendResponse(false);
107 }
108 }
109
Execute(const std::string & code_string)110 bool ExecuteCodeFunction::Execute(const std::string& code_string) {
111 ScriptExecutor* executor = GetScriptExecutor();
112 if (!executor)
113 return false;
114
115 const Extension* extension = GetExtension();
116 if (!extension)
117 return false;
118
119 ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT;
120 if (ShouldInsertCSS())
121 script_type = ScriptExecutor::CSS;
122
123 ScriptExecutor::FrameScope frame_scope =
124 details_->all_frames.get() && *details_->all_frames ?
125 ScriptExecutor::ALL_FRAMES :
126 ScriptExecutor::TOP_FRAME;
127
128 ScriptExecutor::MatchAboutBlank match_about_blank =
129 details_->match_about_blank.get() && *details_->match_about_blank ?
130 ScriptExecutor::MATCH_ABOUT_BLANK :
131 ScriptExecutor::DONT_MATCH_ABOUT_BLANK;
132
133 UserScript::RunLocation run_at =
134 UserScript::UNDEFINED;
135 switch (details_->run_at) {
136 case InjectDetails::RUN_AT_NONE:
137 case InjectDetails::RUN_AT_DOCUMENT_IDLE:
138 run_at = UserScript::DOCUMENT_IDLE;
139 break;
140 case InjectDetails::RUN_AT_DOCUMENT_START:
141 run_at = UserScript::DOCUMENT_START;
142 break;
143 case InjectDetails::RUN_AT_DOCUMENT_END:
144 run_at = UserScript::DOCUMENT_END;
145 break;
146 }
147 CHECK_NE(UserScript::UNDEFINED, run_at);
148
149 executor->ExecuteScript(
150 extension->id(),
151 script_type,
152 code_string,
153 frame_scope,
154 match_about_blank,
155 run_at,
156 ScriptExecutor::ISOLATED_WORLD,
157 IsWebView() ? ScriptExecutor::WEB_VIEW_PROCESS
158 : ScriptExecutor::DEFAULT_PROCESS,
159 GetWebViewSrc(),
160 file_url_,
161 user_gesture_,
162 has_callback() ? ScriptExecutor::JSON_SERIALIZED_RESULT
163 : ScriptExecutor::NO_RESULT,
164 base::Bind(&ExecuteCodeFunction::OnExecuteCodeFinished, this));
165 return true;
166 }
167
HasPermission()168 bool ExecuteCodeFunction::HasPermission() {
169 return true;
170 }
171
RunAsync()172 bool ExecuteCodeFunction::RunAsync() {
173 EXTENSION_FUNCTION_VALIDATE(Init());
174
175 if (!details_->code.get() && !details_->file.get()) {
176 error_ = keys::kNoCodeOrFileToExecuteError;
177 return false;
178 }
179 if (details_->code.get() && details_->file.get()) {
180 error_ = keys::kMoreThanOneValuesError;
181 return false;
182 }
183
184 if (!CanExecuteScriptOnPage())
185 return false;
186
187 if (details_->code.get())
188 return Execute(*details_->code);
189
190 if (!details_->file.get())
191 return false;
192 resource_ = GetExtension()->GetResource(*details_->file);
193
194 if (resource_.extension_root().empty() || resource_.relative_path().empty()) {
195 error_ = keys::kNoCodeOrFileToExecuteError;
196 return false;
197 }
198
199 int resource_id;
200 if (ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager()->
201 IsComponentExtensionResource(
202 resource_.extension_root(), resource_.relative_path(),
203 &resource_id)) {
204 const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
205 DidLoadFile(true, rb.GetRawDataResource(resource_id).as_string());
206 } else {
207 scoped_refptr<FileReader> file_reader(new FileReader(
208 resource_, base::Bind(&ExecuteCodeFunction::DidLoadFile, this)));
209 file_reader->Start();
210 }
211
212 return true;
213 }
214
OnExecuteCodeFinished(const std::string & error,int32 on_page_id,const GURL & on_url,const base::ListValue & result)215 void ExecuteCodeFunction::OnExecuteCodeFinished(
216 const std::string& error,
217 int32 on_page_id,
218 const GURL& on_url,
219 const base::ListValue& result) {
220 if (!error.empty())
221 SetError(error);
222
223 SendResponse(error.empty());
224 }
225
226 } // namespace extensions
227