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