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
26namespace keys = extension_tabs_module_constants;
27
28ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
29    : ALLOW_THIS_IN_INITIALIZER_LIST(registrar_(this)),
30      execute_tab_id_(-1),
31      all_frames_(false) {
32}
33
34ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {
35}
36
37bool 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
126void 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
145bool 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
187bool 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
208void 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