// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/execute_code_in_tab_function.h" #include "base/callback.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/extensions/extension_tabs_module_constants.h" #include "chrome/browser/extensions/file_reader.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/extensions/extension_messages.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/common/notification_service.h" namespace keys = extension_tabs_module_constants; ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() : ALLOW_THIS_IN_INITIALIZER_LIST(registrar_(this)), execute_tab_id_(-1), all_frames_(false) { } ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() { } bool ExecuteCodeInTabFunction::RunImpl() { DictionaryValue* script_info; EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &script_info)); size_t number_of_value = script_info->size(); if (number_of_value == 0) { error_ = keys::kNoCodeOrFileToExecuteError; return false; } else { bool has_code = script_info->HasKey(keys::kCodeKey); bool has_file = script_info->HasKey(keys::kFileKey); if (has_code && has_file) { error_ = keys::kMoreThanOneValuesError; return false; } else if (!has_code && !has_file) { error_ = keys::kNoCodeOrFileToExecuteError; return false; } } execute_tab_id_ = -1; Browser* browser = NULL; TabContentsWrapper* contents = NULL; // If |tab_id| is specified, look for it. Otherwise default to selected tab // in the current window. Value* tab_value = NULL; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value)); if (tab_value->IsType(Value::TYPE_NULL)) { browser = GetCurrentBrowser(); if (!browser) { error_ = keys::kNoCurrentWindowError; return false; } if (!ExtensionTabUtil::GetDefaultTab(browser, &contents, &execute_tab_id_)) return false; } else { EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&execute_tab_id_)); if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(), include_incognito(), &browser, NULL, &contents, NULL)) { return false; } } // NOTE: This can give the wrong answer due to race conditions, but it is OK, // we check again in the renderer. CHECK(browser); CHECK(contents); if (!GetExtension()->CanExecuteScriptOnPage( contents->tab_contents()->GetURL(), NULL, &error_)) { return false; } if (script_info->HasKey(keys::kAllFramesKey)) { if (!script_info->GetBoolean(keys::kAllFramesKey, &all_frames_)) return false; } std::string code_string; if (script_info->HasKey(keys::kCodeKey)) { if (!script_info->GetString(keys::kCodeKey, &code_string)) return false; } if (!code_string.empty()) { if (!Execute(code_string)) return false; return true; } std::string relative_path; if (script_info->HasKey(keys::kFileKey)) { if (!script_info->GetString(keys::kFileKey, &relative_path)) return false; resource_ = GetExtension()->GetResource(relative_path); } if (resource_.extension_root().empty() || resource_.relative_path().empty()) { error_ = keys::kNoCodeOrFileToExecuteError; return false; } scoped_refptr<FileReader> file_reader(new FileReader( resource_, NewCallback(this, &ExecuteCodeInTabFunction::DidLoadFile))); file_reader->Start(); AddRef(); // Keep us alive until DidLoadFile is called. return true; } void ExecuteCodeInTabFunction::DidLoadFile(bool success, const std::string& data) { if (success) { Execute(data); } else { #if defined(OS_POSIX) // TODO(viettrungluu): bug: there's no particular reason the path should be // UTF-8, in which case this may fail. error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kLoadFileError, resource_.relative_path().value()); #elif defined(OS_WIN) error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kLoadFileError, WideToUTF8(resource_.relative_path().value())); #endif // OS_WIN SendResponse(false); } Release(); // Balance the AddRef taken in RunImpl } bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) { TabContentsWrapper* contents = NULL; Browser* browser = NULL; bool success = ExtensionTabUtil::GetTabById( execute_tab_id_, profile(), include_incognito(), &browser, NULL, &contents, NULL) && contents && browser; if (!success) { SendResponse(false); return false; } const Extension* extension = GetExtension(); if (!extension) { SendResponse(false); return false; } bool is_js_code = true; std::string function_name = name(); if (function_name == TabsInsertCSSFunction::function_name()) { is_js_code = false; } else if (function_name != TabsExecuteScriptFunction::function_name()) { DCHECK(false); } ExtensionMsg_ExecuteCode_Params params; params.request_id = request_id(); params.extension_id = extension->id(); params.is_javascript = is_js_code; params.code = code_string; params.all_frames = all_frames_; params.in_main_world = false; contents->render_view_host()->Send(new ExtensionMsg_ExecuteCode( contents->render_view_host()->routing_id(), params)); registrar_.Observe(contents->tab_contents()); AddRef(); // balanced in OnExecuteCodeFinished() return true; } bool ExecuteCodeInTabFunction::OnMessageReceived(const IPC::Message& message) { if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID) return false; int message_request_id; void* iter = NULL; if (!message.ReadInt(&iter, &message_request_id)) { NOTREACHED() << "malformed extension message"; return true; } if (message_request_id != request_id()) return false; IPC_BEGIN_MESSAGE_MAP(ExecuteCodeInTabFunction, message) IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished, OnExecuteCodeFinished) IPC_END_MESSAGE_MAP() return true; } void ExecuteCodeInTabFunction::OnExecuteCodeFinished(int request_id, bool success, const std::string& error) { if (!error.empty()) { CHECK(!success); error_ = error; } SendResponse(success); registrar_.Observe(NULL); Release(); // balanced in Execute() }