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