summaryrefslogtreecommitdiffstats
path: root/remoting/host
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-21 00:54:08 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-21 00:54:08 +0000
commite7352bb3ea7b53e06c36cd856d2db4bc6bba5573 (patch)
treef64f6ff5dae92d495322482b9dca886b12fb1dac /remoting/host
parent9efb2dc340ddc55db9cc692949a7318ce6def364 (diff)
downloadchromium_src-e7352bb3ea7b53e06c36cd856d2db4bc6bba5573.zip
chromium_src-e7352bb3ea7b53e06c36cd856d2db4bc6bba5573.tar.gz
chromium_src-e7352bb3ea7b53e06c36cd856d2db4bc6bba5573.tar.bz2
Split host plugin into multiple files.
BUG=None TEST=compiles Review URL: http://codereview.chromium.org/7210025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@89772 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host')
-rw-r--r--remoting/host/host_plugin.cc644
-rw-r--r--remoting/host/host_plugin_utils.cc43
-rw-r--r--remoting/host/host_plugin_utils.h35
-rw-r--r--remoting/host/host_script_object.cc523
-rw-r--r--remoting/host/host_script_object.h148
-rw-r--r--remoting/host/host_status_observer.h2
6 files changed, 756 insertions, 639 deletions
diff --git a/remoting/host/host_plugin.cc b/remoting/host/host_plugin.cc
index 8f8ae28..5c0e387 100644
--- a/remoting/host/host_plugin.cc
+++ b/remoting/host/host_plugin.cc
@@ -9,28 +9,11 @@
#include <vector>
#include "base/at_exit.h"
-#include "base/base_paths.h"
#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/file_path.h"
#include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/path_service.h"
-#include "base/rand_util.h"
#include "base/stringize_macros.h"
-#include "base/task.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread.h"
-#include "base/synchronization/cancellation_flag.h"
-#include "remoting/base/auth_token_util.h"
-#include "remoting/host/chromoting_host.h"
-#include "remoting/host/chromoting_host_context.h"
-#include "remoting/host/host_config.h"
-#include "remoting/host/host_key_pair.h"
-#include "remoting/host/host_status_observer.h"
-#include "remoting/host/in_memory_host_config.h"
-#include "remoting/host/register_support_host_request.h"
-#include "remoting/host/support_access_verifier.h"
+#include "remoting/host/host_plugin_utils.h"
+#include "remoting/host/host_script_object.h"
#include "third_party/npapi/bindings/npapi.h"
#include "third_party/npapi/bindings/npfunctions.h"
#include "third_party/npapi/bindings/npruntime.h"
@@ -58,51 +41,12 @@ uint64_t __cdecl __udivdi3(uint64_t a, uint64_t b) {
}
#endif
-// Supported Javascript interface:
-// readonly attribute string accessCode;
-// readonly attribute int state;
-//
-// state: {
-// DISCONNECTED,
-// REQUESTED_ACCESS_CODE,
-// RECEIVED_ACCESS_CODE,
-// CONNECTED,
-// AFFIRMING_CONNECTION,
-// ERROR,
-// }
-//
-// attribute Function void logDebugInfo(string);
-// attribute Function void onStateChanged();
-//
-// // The |auth_service_with_token| parameter should be in the format
-// // "auth_service:auth_token". An example would be "oauth2:1/2a3912vd".
-// void connect(string uid, string auth_service_with_token);
-// void disconnect();
-
+using remoting::g_npnetscape_funcs;
+using remoting::HostNPScriptObject;
+using remoting::StringFromNPIdentifier;
namespace {
-const char* kAttrNameAccessCode = "accessCode";
-const char* kAttrNameState = "state";
-const char* kAttrNameLogDebugInfo = "logDebugInfo";
-const char* kAttrNameOnStateChanged = "onStateChanged";
-const char* kFuncNameConnect = "connect";
-const char* kFuncNameDisconnect = "disconnect";
-
-// States.
-const char* kAttrNameDisconnected = "DISCONNECTED";
-const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE";
-const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE";
-const char* kAttrNameConnected = "CONNECTED";
-const char* kAttrNameAffirmingConnection = "AFFIRMING_CONNECTION";
-const char* kAttrNameError = "ERROR";
-
-const int kMaxLoginAttempts = 5;
-
-// Global netscape functions initialized in NP_Initialize.
-NPNetscapeFuncs* g_npnetscape_funcs = NULL;
-
-// Global AtExitManager, created in NP_Initialize and destroyed in NP_Shutdown.
base::AtExitManager* g_at_exit_manager = NULL;
// The name and description are returned by GetValue, but are also
@@ -111,582 +55,6 @@ base::AtExitManager* g_at_exit_manager = NULL;
#define HOST_PLUGIN_NAME "Remoting Host Plugin"
#define HOST_PLUGIN_DESCRIPTION "Remoting Host Plugin"
-// Convert an NPIdentifier into a std::string.
-std::string StringFromNPIdentifier(NPIdentifier identifier) {
- if (!g_npnetscape_funcs->identifierisstring(identifier))
- return std::string();
- NPUTF8* np_string = g_npnetscape_funcs->utf8fromidentifier(identifier);
- std::string string(np_string);
- g_npnetscape_funcs->memfree(np_string);
- return string;
-}
-
-// Convert an NPVariant into a std::string.
-std::string StringFromNPVariant(const NPVariant& variant) {
- if (!NPVARIANT_IS_STRING(variant))
- return std::string();
- const NPString& np_string = NPVARIANT_TO_STRING(variant);
- return std::string(np_string.UTF8Characters, np_string.UTF8Length);
-}
-
-// Convert a std::string into an NPVariant.
-// Caller is responsible for making sure that NPN_ReleaseVariantValue is
-// called on returned value.
-NPVariant NPVariantFromString(const std::string& val) {
- size_t len = val.length();
- NPUTF8* chars =
- reinterpret_cast<NPUTF8*>(g_npnetscape_funcs->memalloc(len + 1));
- strcpy(chars, val.c_str());
- NPVariant variant;
- STRINGN_TO_NPVARIANT(chars, len, variant);
- return variant;
-}
-
-// Convert an NPVariant into an NSPObject.
-NPObject* ObjectFromNPVariant(const NPVariant& variant) {
- if (!NPVARIANT_IS_OBJECT(variant))
- return NULL;
- return NPVARIANT_TO_OBJECT(variant);
-}
-
-// NPAPI plugin implementation for remoting host script object.
-// HostNPScriptObject creates threads that are required to run
-// ChromotingHost and starts/stops the host on those threads. When
-// destroyed it sychronously shuts down the host and all threads.
-class HostNPScriptObject : public remoting::HostStatusObserver {
- public:
- HostNPScriptObject(NPP plugin, NPObject* parent)
- : plugin_(plugin),
- parent_(parent),
- state_(kDisconnected),
- log_debug_info_func_(NULL),
- on_state_changed_func_(NULL),
- np_thread_id_(base::PlatformThread::CurrentId()),
- failed_login_attempts_(0),
- disconnected_event_(true, false) {
- VLOG(2) << "HostNPScriptObject";
- host_context_.SetUITaskPostFunction(base::Bind(
- &HostNPScriptObject::PostTaskToNPThread, base::Unretained(this)));
- }
-
- ~HostNPScriptObject() {
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
-
- // Disconnect synchronously. We cannot disconnect asynchronously
- // here because |host_context_| needs to be stopped on the plugin
- // thread, but the plugin thread may not exist after the instance
- // is destroyed.
- destructing_.Set();
- disconnected_event_.Reset();
- DisconnectInternal();
- disconnected_event_.Wait();
-
- host_context_.Stop();
- if (log_debug_info_func_) {
- g_npnetscape_funcs->releaseobject(log_debug_info_func_);
- }
- if (on_state_changed_func_) {
- g_npnetscape_funcs->releaseobject(on_state_changed_func_);
- }
- }
-
- bool Init() {
- VLOG(2) << "Init";
- // TODO(wez): This starts a bunch of threads, which might fail.
- host_context_.Start();
- return true;
- }
-
- bool HasMethod(const std::string& method_name) {
- VLOG(2) << "HasMethod " << method_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- return (method_name == kFuncNameConnect ||
- method_name == kFuncNameDisconnect);
- }
-
- bool InvokeDefault(const NPVariant* args,
- uint32_t argCount,
- NPVariant* result) {
- VLOG(2) << "InvokeDefault";
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- SetException("exception during default invocation");
- return false;
- }
-
- bool Invoke(const std::string& method_name,
- const NPVariant* args,
- uint32_t argCount,
- NPVariant* result) {
- VLOG(2) << "Invoke " << method_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- if (method_name == kFuncNameConnect) {
- return Connect(args, argCount, result);
- } else if (method_name == kFuncNameDisconnect) {
- return Disconnect(args, argCount, result);
- } else {
- SetException("Invoke: unknown method " + method_name);
- return false;
- }
- }
-
- bool HasProperty(const std::string& property_name) {
- VLOG(2) << "HasProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- return (property_name == kAttrNameAccessCode ||
- property_name == kAttrNameState ||
- property_name == kAttrNameLogDebugInfo ||
- property_name == kAttrNameOnStateChanged ||
- property_name == kAttrNameDisconnected ||
- property_name == kAttrNameRequestedAccessCode ||
- property_name == kAttrNameReceivedAccessCode ||
- property_name == kAttrNameConnected ||
- property_name == kAttrNameAffirmingConnection ||
- property_name == kAttrNameError);
- }
-
- bool GetProperty(const std::string& property_name, NPVariant* result) {
- VLOG(2) << "GetProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- if (!result) {
- SetException("GetProperty: NULL result");
- return false;
- }
-
- if (property_name == kAttrNameOnStateChanged) {
- OBJECT_TO_NPVARIANT(on_state_changed_func_, *result);
- return true;
- } else if (property_name == kAttrNameLogDebugInfo) {
- OBJECT_TO_NPVARIANT(log_debug_info_func_, *result);
- return true;
- } else if (property_name == kAttrNameState) {
- INT32_TO_NPVARIANT(state_, *result);
- return true;
- } else if (property_name == kAttrNameAccessCode) {
- *result = NPVariantFromString(access_code_);
- return true;
- } else if (property_name == kAttrNameDisconnected) {
- INT32_TO_NPVARIANT(kDisconnected, *result);
- return true;
- } else if (property_name == kAttrNameRequestedAccessCode) {
- INT32_TO_NPVARIANT(kRequestedAccessCode, *result);
- return true;
- } else if (property_name == kAttrNameReceivedAccessCode) {
- INT32_TO_NPVARIANT(kReceivedAccessCode, *result);
- return true;
- } else if (property_name == kAttrNameConnected) {
- INT32_TO_NPVARIANT(kConnected, *result);
- return true;
- } else if (property_name == kAttrNameAffirmingConnection) {
- INT32_TO_NPVARIANT(kAffirmingConnection, *result);
- return true;
- } else if (property_name == kAttrNameError) {
- INT32_TO_NPVARIANT(kError, *result);
- return true;
- } else {
- SetException("GetProperty: unsupported property " + property_name);
- return false;
- }
- }
-
- bool SetProperty(const std::string& property_name, const NPVariant* value) {
- VLOG(2) << "SetProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
-
- if (property_name == kAttrNameOnStateChanged) {
- if (NPVARIANT_IS_OBJECT(*value)) {
- if (on_state_changed_func_) {
- g_npnetscape_funcs->releaseobject(on_state_changed_func_);
- }
- on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value);
- if (on_state_changed_func_) {
- g_npnetscape_funcs->retainobject(on_state_changed_func_);
- }
- return true;
- } else {
- SetException("SetProperty: unexpected type for property " +
- property_name);
- }
- return false;
- }
-
- if (property_name == kAttrNameLogDebugInfo) {
- if (NPVARIANT_IS_OBJECT(*value)) {
- if (log_debug_info_func_) {
- g_npnetscape_funcs->releaseobject(log_debug_info_func_);
- }
- log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value);
- if (log_debug_info_func_) {
- g_npnetscape_funcs->retainobject(log_debug_info_func_);
- }
- return true;
- } else {
- SetException("SetProperty: unexpected type for property " +
- property_name);
- }
- return false;
- }
-
- return false;
- }
-
- bool RemoveProperty(const std::string& property_name) {
- VLOG(2) << "RemoveProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- return false;
- }
-
- bool Enumerate(std::vector<std::string>* values) {
- VLOG(2) << "Enumerate";
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- const char* entries[] = {
- kAttrNameAccessCode,
- kAttrNameState,
- kAttrNameLogDebugInfo,
- kAttrNameOnStateChanged,
- kFuncNameConnect,
- kFuncNameDisconnect,
- kAttrNameDisconnected,
- kAttrNameRequestedAccessCode,
- kAttrNameReceivedAccessCode,
- kAttrNameConnected,
- kAttrNameAffirmingConnection,
- kAttrNameError
- };
- for (size_t i = 0; i < arraysize(entries); ++i) {
- values->push_back(entries[i]);
- }
- return true;
- }
-
- // remoting::HostStatusObserver implementation.
- virtual void OnSignallingConnected(remoting::SignalStrategy* signal_strategy,
- const std::string& full_jid) OVERRIDE {
- OnStateChanged(kConnected);
- }
-
- virtual void OnSignallingDisconnected() OVERRIDE {
- }
-
- virtual void OnAccessDenied() OVERRIDE {
- DCHECK_EQ(MessageLoop::current(), host_context_.network_message_loop());
-
- ++failed_login_attempts_;
- if (failed_login_attempts_ == kMaxLoginAttempts)
- DisconnectInternal();
- }
-
- virtual void OnShutdown() OVERRIDE {
- DCHECK_EQ(MessageLoop::current(), host_context_.main_message_loop());
-
- OnStateChanged(kDisconnected);
- }
-
- private:
- enum State {
- kDisconnected,
- kRequestedAccessCode,
- kReceivedAccessCode,
- kConnected,
- kAffirmingConnection,
- kError
- };
-
- // Start connection. args are:
- // string uid, string auth_token
- // No result.
- bool Connect(const NPVariant* args, uint32_t argCount, NPVariant* result);
-
- // Disconnect. No arguments or result.
- bool Disconnect(const NPVariant* args, uint32_t argCount, NPVariant* result);
-
- // Call LogDebugInfo handler if there is one.
- void LogDebugInfo(const std::string& message);
-
- // Call OnStateChanged handler if there is one.
- void OnStateChanged(State state);
-
- // Callbacks invoked during session setup.
- void OnReceivedSupportID(remoting::SupportAccessVerifier* access_verifier,
- bool success,
- const std::string& support_id);
-
- // Helper functions that run on main thread. Can be called on any
- // other thread.
- void ConnectInternal(const std::string& uid,
- const std::string& auth_token,
- const std::string& auth_service);
- void DisconnectInternal();
-
- // Callback for ChromotingHost::Shutdown().
- void OnShutdownFinished();
-
- // Call a JavaScript function wrapped as an NPObject.
- // If result is non-null, the result of the call will be stored in it.
- // Caller is responsible for releasing result if they ask for it.
- static bool CallJSFunction(NPObject* func,
- const NPVariant* args,
- uint32_t argCount,
- NPVariant* result);
-
- // Posts a task on the main NP thread.
- void PostTaskToNPThread(const tracked_objects::Location& from_here,
- Task* task);
-
- // Utility function for PostTaskToNPThread.
- static void NPTaskSpringboard(void* task);
-
- // Set an exception for the current call.
- void SetException(const std::string& exception_string);
-
- NPP plugin_;
- NPObject* parent_;
- int state_;
- std::string access_code_;
- NPObject* log_debug_info_func_;
- NPObject* on_state_changed_func_;
- base::PlatformThreadId np_thread_id_;
-
- scoped_ptr<remoting::RegisterSupportHostRequest> register_request_;
- scoped_refptr<remoting::ChromotingHost> host_;
- scoped_refptr<remoting::MutableHostConfig> host_config_;
- remoting::ChromotingHostContext host_context_;
- int failed_login_attempts_;
-
- base::WaitableEvent disconnected_event_;
- base::CancellationFlag destructing_;
-};
-
-// string uid, string auth_token
-bool HostNPScriptObject::Connect(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- LogDebugInfo("Connecting...");
-
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- if (arg_count != 2) {
- SetException("connect: bad number of arguments");
- return false;
- }
-
- std::string uid = StringFromNPVariant(args[0]);
- if (uid.empty()) {
- SetException("connect: bad uid argument");
- return false;
- }
-
- std::string auth_service_with_token = StringFromNPVariant(args[1]);
- std::string auth_token;
- std::string auth_service;
- remoting::ParseAuthTokenWithService(auth_service_with_token, &auth_token,
- &auth_service);
- if (auth_token.empty()) {
- SetException("connect: auth_service_with_token argument has empty token");
- return false;
- }
-
- ConnectInternal(uid, auth_token, auth_service);
-
- return true;
-}
-
-void HostNPScriptObject::ConnectInternal(
- const std::string& uid,
- const std::string& auth_token,
- const std::string& auth_service) {
- if (MessageLoop::current() != host_context_.main_message_loop()) {
- host_context_.main_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &HostNPScriptObject::ConnectInternal,
- uid, auth_token, auth_service));
- return;
- }
- // Store the supplied user ID and token to the Host configuration.
- scoped_refptr<remoting::MutableHostConfig> host_config =
- new remoting::InMemoryHostConfig;
- host_config->SetString(remoting::kXmppLoginConfigPath, uid);
- host_config->SetString(remoting::kXmppAuthTokenConfigPath, auth_token);
- host_config->SetString(remoting::kXmppAuthServiceConfigPath, auth_service);
-
- // Create an access verifier and fetch the host secret.
- scoped_ptr<remoting::SupportAccessVerifier> access_verifier;
- access_verifier.reset(new remoting::SupportAccessVerifier);
-
- // Generate a key pair for the Host to use.
- // TODO(wez): Move this to the worker thread.
- remoting::HostKeyPair host_key_pair;
- host_key_pair.Generate();
- host_key_pair.Save(host_config);
-
- // Request registration of the host for support.
- scoped_ptr<remoting::RegisterSupportHostRequest> register_request(
- new remoting::RegisterSupportHostRequest());
- if (!register_request->Init(
- host_config.get(),
- base::Bind(&HostNPScriptObject::OnReceivedSupportID,
- base::Unretained(this),
- access_verifier.get()))) {
- OnStateChanged(kDisconnected);
- return;
- }
-
- // Create the Host.
- scoped_refptr<remoting::ChromotingHost> host =
- remoting::ChromotingHost::Create(&host_context_, host_config,
- access_verifier.release());
- host->AddStatusObserver(this);
- host->AddStatusObserver(register_request.get());
- host->set_it2me(true);
-
- // Nothing went wrong, so lets save the host, config and request.
- host_ = host;
- host_config_ = host_config;
- register_request_.reset(register_request.release());
-
- // Start the Host.
- host_->Start();
-
- OnStateChanged(kRequestedAccessCode);
- return;
-}
-
-bool HostNPScriptObject::Disconnect(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- if (arg_count != 0) {
- SetException("disconnect: bad number of arguments");
- return false;
- }
-
- DisconnectInternal();
-
- return true;
-}
-
-void HostNPScriptObject::DisconnectInternal() {
- if (MessageLoop::current() != host_context_.main_message_loop()) {
- host_context_.main_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &HostNPScriptObject::DisconnectInternal));
- return;
- }
-
- if (!host_) {
- disconnected_event_.Signal();
- return;
- }
-
- host_->Shutdown(
- NewRunnableMethod(this, &HostNPScriptObject::OnShutdownFinished));
-}
-
-void HostNPScriptObject::OnShutdownFinished() {
- DCHECK_EQ(MessageLoop::current(), host_context_.main_message_loop());
-
- host_ = NULL;
- register_request_.reset();
- host_config_ = NULL;
- disconnected_event_.Signal();
-}
-
-void HostNPScriptObject::OnReceivedSupportID(
- remoting::SupportAccessVerifier* access_verifier,
- bool success,
- const std::string& support_id) {
- CHECK_NE(base::PlatformThread::CurrentId(), np_thread_id_);
-
- if (!success) {
- // TODO(wez): Replace the success/fail flag with full error reporting.
- DisconnectInternal();
- return;
- }
-
- // Inform the AccessVerifier of our Support-Id, for authentication.
- access_verifier->OnIT2MeHostRegistered(success, support_id);
-
- // Combine the Support Id with the Host Id to make the Access Code.
- // TODO(wez): Locking, anyone?
- access_code_ = support_id + "-" + access_verifier->host_secret();
-
- // Let the caller know that life is good.
- OnStateChanged(kReceivedAccessCode);
-}
-
-void HostNPScriptObject::OnStateChanged(State state) {
- if (destructing_.IsSet()) {
- return;
- }
-
- if (!host_context_.IsUIThread()) {
- host_context_.PostToUIThread(
- FROM_HERE,
- NewRunnableMethod(this, &HostNPScriptObject::OnStateChanged, state));
- return;
- }
- state_ = state;
- if (on_state_changed_func_) {
- VLOG(2) << "Calling state changed " << state;
- bool is_good = CallJSFunction(on_state_changed_func_, NULL, 0, NULL);
- LOG_IF(ERROR, !is_good) << "OnStateChangedNP failed";
- }
-}
-
-void HostNPScriptObject::LogDebugInfo(const std::string& message) {
- if (!host_context_.IsUIThread()) {
- host_context_.PostToUIThread(
- FROM_HERE,
- NewRunnableMethod(this, &HostNPScriptObject::LogDebugInfo, message));
- return;
- }
- if (log_debug_info_func_) {
- NPVariant* arg = new NPVariant();
- LOG(INFO) << "Logging: " << message;
- STRINGZ_TO_NPVARIANT(message.c_str(), *arg);
- bool is_good = CallJSFunction(log_debug_info_func_, arg, 1, NULL);
- LOG_IF(ERROR, !is_good) << "LogDebugInfo failed";
- }
-}
-
-void HostNPScriptObject::SetException(const std::string& exception_string) {
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
- LogDebugInfo(exception_string);
-}
-
-bool HostNPScriptObject::CallJSFunction(NPObject* func,
- const NPVariant* args,
- uint32_t argCount,
- NPVariant* result) {
- NPVariant np_result;
- bool is_good = func->_class->invokeDefault(func, args, argCount, &np_result);
- if (is_good) {
- if (result) {
- *result = np_result;
- } else {
- g_npnetscape_funcs->releasevariantvalue(&np_result);
- }
- }
- return is_good;
-}
-
-void HostNPScriptObject::PostTaskToNPThread(
- const tracked_objects::Location& from_here, Task* task) {
- // The NPAPI functions cannot make use of |from_here|, but this method is
- // passed as a callback to ChromotingHostContext, so it needs to have the
- // appropriate signature.
-
- // Can be called from any thread.
- g_npnetscape_funcs->pluginthreadasynccall(plugin_,
- &NPTaskSpringboard,
- task);
-}
-
-void HostNPScriptObject::NPTaskSpringboard(void* task) {
- Task* real_task = reinterpret_cast<Task*>(task);
- real_task->Run();
- delete real_task;
-}
-
// NPAPI plugin implementation for remoting host.
// Documentation for most of the calls in this class can be found here:
// https://developer.mozilla.org/en/Gecko_Plugin_API_Reference/Scripting_plugins
@@ -1004,8 +372,6 @@ NPError SetWindow(NPP instance, NPWindow* pNPWindow) {
} // namespace
-DISABLE_RUNNABLE_METHOD_REFCOUNT(HostNPScriptObject);
-
#if defined(OS_WIN)
HMODULE g_hModule = NULL;
diff --git a/remoting/host/host_plugin_utils.cc b/remoting/host/host_plugin_utils.cc
new file mode 100644
index 0000000..8a52e3f
--- /dev/null
+++ b/remoting/host/host_plugin_utils.cc
@@ -0,0 +1,43 @@
+// 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 "remoting/host/host_plugin_utils.h"
+
+namespace remoting {
+
+NPNetscapeFuncs* g_npnetscape_funcs = NULL;
+
+std::string StringFromNPIdentifier(NPIdentifier identifier) {
+ if (!g_npnetscape_funcs->identifierisstring(identifier))
+ return std::string();
+ NPUTF8* np_string = g_npnetscape_funcs->utf8fromidentifier(identifier);
+ std::string string(np_string);
+ g_npnetscape_funcs->memfree(np_string);
+ return string;
+}
+
+std::string StringFromNPVariant(const NPVariant& variant) {
+ if (!NPVARIANT_IS_STRING(variant))
+ return std::string();
+ const NPString& np_string = NPVARIANT_TO_STRING(variant);
+ return std::string(np_string.UTF8Characters, np_string.UTF8Length);
+}
+
+NPVariant NPVariantFromString(const std::string& val) {
+ size_t len = val.length();
+ NPUTF8* chars =
+ reinterpret_cast<NPUTF8*>(g_npnetscape_funcs->memalloc(len + 1));
+ strcpy(chars, val.c_str());
+ NPVariant variant;
+ STRINGN_TO_NPVARIANT(chars, len, variant);
+ return variant;
+}
+
+NPObject* ObjectFromNPVariant(const NPVariant& variant) {
+ if (!NPVARIANT_IS_OBJECT(variant))
+ return NULL;
+ return NPVARIANT_TO_OBJECT(variant);
+}
+
+} // namespace remoting
diff --git a/remoting/host/host_plugin_utils.h b/remoting/host/host_plugin_utils.h
new file mode 100644
index 0000000..69ac302
--- /dev/null
+++ b/remoting/host/host_plugin_utils.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef REMOTING_HOST_HOST_PLUGIN_UTILS_H_
+#define REMOTING_HOST_HOST_PLUGIN_UTILS_H_
+
+#include <string>
+
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/npfunctions.h"
+#include "third_party/npapi/bindings/npruntime.h"
+
+namespace remoting {
+
+// Global netscape functions initialized in NP_Initialize.
+extern NPNetscapeFuncs* g_npnetscape_funcs;
+
+// Convert an NPIdentifier into a std::string.
+std::string StringFromNPIdentifier(NPIdentifier identifier);
+
+// Convert an NPVariant into a std::string.
+std::string StringFromNPVariant(const NPVariant& variant);
+
+// Convert a std::string into an NPVariant.
+// Caller is responsible for making sure that NPN_ReleaseVariantValue is
+// called on returned value.
+NPVariant NPVariantFromString(const std::string& val);
+
+// Convert an NPVariant into an NSPObject.
+NPObject* ObjectFromNPVariant(const NPVariant& variant);
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_HOST_PLUGIN_UTILS_H_
diff --git a/remoting/host/host_script_object.cc b/remoting/host/host_script_object.cc
new file mode 100644
index 0000000..dab16d1
--- /dev/null
+++ b/remoting/host/host_script_object.cc
@@ -0,0 +1,523 @@
+// 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 "remoting/host/host_script_object.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/threading/platform_thread.h"
+#include "remoting/base/auth_token_util.h"
+#include "remoting/host/chromoting_host.h"
+#include "remoting/host/chromoting_host_context.h"
+#include "remoting/host/host_config.h"
+#include "remoting/host/host_key_pair.h"
+#include "remoting/host/host_plugin_utils.h"
+#include "remoting/host/in_memory_host_config.h"
+#include "remoting/host/register_support_host_request.h"
+#include "remoting/host/support_access_verifier.h"
+
+namespace remoting {
+
+// Supported Javascript interface:
+// readonly attribute string accessCode;
+// readonly attribute int state;
+//
+// state: {
+// DISCONNECTED,
+// REQUESTED_ACCESS_CODE,
+// RECEIVED_ACCESS_CODE,
+// CONNECTED,
+// AFFIRMING_CONNECTION,
+// ERROR,
+// }
+//
+// attribute Function void logDebugInfo(string);
+// attribute Function void onStateChanged();
+//
+// // The |auth_service_with_token| parameter should be in the format
+// // "auth_service:auth_token". An example would be "oauth2:1/2a3912vd".
+// void connect(string uid, string auth_service_with_token);
+// void disconnect();
+
+namespace {
+
+const char* kAttrNameAccessCode = "accessCode";
+const char* kAttrNameState = "state";
+const char* kAttrNameLogDebugInfo = "logDebugInfo";
+const char* kAttrNameOnStateChanged = "onStateChanged";
+const char* kFuncNameConnect = "connect";
+const char* kFuncNameDisconnect = "disconnect";
+
+// States.
+const char* kAttrNameDisconnected = "DISCONNECTED";
+const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE";
+const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE";
+const char* kAttrNameConnected = "CONNECTED";
+const char* kAttrNameAffirmingConnection = "AFFIRMING_CONNECTION";
+const char* kAttrNameError = "ERROR";
+
+const int kMaxLoginAttempts = 5;
+
+} // namespace
+
+HostNPScriptObject::HostNPScriptObject(NPP plugin, NPObject* parent)
+ : plugin_(plugin),
+ parent_(parent),
+ state_(kDisconnected),
+ log_debug_info_func_(NULL),
+ on_state_changed_func_(NULL),
+ np_thread_id_(base::PlatformThread::CurrentId()),
+ failed_login_attempts_(0),
+ disconnected_event_(true, false) {
+ VLOG(2) << "HostNPScriptObject";
+ host_context_.SetUITaskPostFunction(base::Bind(
+ &HostNPScriptObject::PostTaskToNPThread, base::Unretained(this)));
+}
+
+HostNPScriptObject::~HostNPScriptObject() {
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+
+ // Disconnect synchronously. We cannot disconnect asynchronously
+ // here because |host_context_| needs to be stopped on the plugin
+ // thread, but the plugin thread may not exist after the instance
+ // is destroyed.
+ destructing_.Set();
+ disconnected_event_.Reset();
+ DisconnectInternal();
+ disconnected_event_.Wait();
+
+ host_context_.Stop();
+ if (log_debug_info_func_) {
+ g_npnetscape_funcs->releaseobject(log_debug_info_func_);
+ }
+ if (on_state_changed_func_) {
+ g_npnetscape_funcs->releaseobject(on_state_changed_func_);
+ }
+}
+
+bool HostNPScriptObject::Init() {
+ VLOG(2) << "Init";
+ // TODO(wez): This starts a bunch of threads, which might fail.
+ host_context_.Start();
+ return true;
+}
+
+bool HostNPScriptObject::HasMethod(const std::string& method_name) {
+ VLOG(2) << "HasMethod " << method_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ return (method_name == kFuncNameConnect ||
+ method_name == kFuncNameDisconnect);
+}
+
+bool HostNPScriptObject::InvokeDefault(const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result) {
+ VLOG(2) << "InvokeDefault";
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ SetException("exception during default invocation");
+ return false;
+}
+
+bool HostNPScriptObject::Invoke(const std::string& method_name,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result) {
+ VLOG(2) << "Invoke " << method_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ if (method_name == kFuncNameConnect) {
+ return Connect(args, argCount, result);
+ } else if (method_name == kFuncNameDisconnect) {
+ return Disconnect(args, argCount, result);
+ } else {
+ SetException("Invoke: unknown method " + method_name);
+ return false;
+ }
+}
+
+bool HostNPScriptObject::HasProperty(const std::string& property_name) {
+ VLOG(2) << "HasProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ return (property_name == kAttrNameAccessCode ||
+ property_name == kAttrNameState ||
+ property_name == kAttrNameLogDebugInfo ||
+ property_name == kAttrNameOnStateChanged ||
+ property_name == kAttrNameDisconnected ||
+ property_name == kAttrNameRequestedAccessCode ||
+ property_name == kAttrNameReceivedAccessCode ||
+ property_name == kAttrNameConnected ||
+ property_name == kAttrNameAffirmingConnection ||
+ property_name == kAttrNameError);
+}
+
+bool HostNPScriptObject::GetProperty(const std::string& property_name,
+ NPVariant* result) {
+ VLOG(2) << "GetProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ if (!result) {
+ SetException("GetProperty: NULL result");
+ return false;
+ }
+
+ if (property_name == kAttrNameOnStateChanged) {
+ OBJECT_TO_NPVARIANT(on_state_changed_func_, *result);
+ return true;
+ } else if (property_name == kAttrNameLogDebugInfo) {
+ OBJECT_TO_NPVARIANT(log_debug_info_func_, *result);
+ return true;
+ } else if (property_name == kAttrNameState) {
+ INT32_TO_NPVARIANT(state_, *result);
+ return true;
+ } else if (property_name == kAttrNameAccessCode) {
+ *result = NPVariantFromString(access_code_);
+ return true;
+ } else if (property_name == kAttrNameDisconnected) {
+ INT32_TO_NPVARIANT(kDisconnected, *result);
+ return true;
+ } else if (property_name == kAttrNameRequestedAccessCode) {
+ INT32_TO_NPVARIANT(kRequestedAccessCode, *result);
+ return true;
+ } else if (property_name == kAttrNameReceivedAccessCode) {
+ INT32_TO_NPVARIANT(kReceivedAccessCode, *result);
+ return true;
+ } else if (property_name == kAttrNameConnected) {
+ INT32_TO_NPVARIANT(kConnected, *result);
+ return true;
+ } else if (property_name == kAttrNameAffirmingConnection) {
+ INT32_TO_NPVARIANT(kAffirmingConnection, *result);
+ return true;
+ } else if (property_name == kAttrNameError) {
+ INT32_TO_NPVARIANT(kError, *result);
+ return true;
+ } else {
+ SetException("GetProperty: unsupported property " + property_name);
+ return false;
+ }
+}
+
+bool HostNPScriptObject::SetProperty(const std::string& property_name,
+ const NPVariant* value) {
+ VLOG(2) << "SetProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+
+ if (property_name == kAttrNameOnStateChanged) {
+ if (NPVARIANT_IS_OBJECT(*value)) {
+ if (on_state_changed_func_) {
+ g_npnetscape_funcs->releaseobject(on_state_changed_func_);
+ }
+ on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value);
+ if (on_state_changed_func_) {
+ g_npnetscape_funcs->retainobject(on_state_changed_func_);
+ }
+ return true;
+ } else {
+ SetException("SetProperty: unexpected type for property " +
+ property_name);
+ }
+ return false;
+ }
+
+ if (property_name == kAttrNameLogDebugInfo) {
+ if (NPVARIANT_IS_OBJECT(*value)) {
+ if (log_debug_info_func_) {
+ g_npnetscape_funcs->releaseobject(log_debug_info_func_);
+ }
+ log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value);
+ if (log_debug_info_func_) {
+ g_npnetscape_funcs->retainobject(log_debug_info_func_);
+ }
+ return true;
+ } else {
+ SetException("SetProperty: unexpected type for property " +
+ property_name);
+ }
+ return false;
+ }
+
+ return false;
+}
+
+bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
+ VLOG(2) << "RemoveProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ return false;
+}
+
+bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) {
+ VLOG(2) << "Enumerate";
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ const char* entries[] = {
+ kAttrNameAccessCode,
+ kAttrNameState,
+ kAttrNameLogDebugInfo,
+ kAttrNameOnStateChanged,
+ kFuncNameConnect,
+ kFuncNameDisconnect,
+ kAttrNameDisconnected,
+ kAttrNameRequestedAccessCode,
+ kAttrNameReceivedAccessCode,
+ kAttrNameConnected,
+ kAttrNameAffirmingConnection,
+ kAttrNameError
+ };
+ for (size_t i = 0; i < arraysize(entries); ++i) {
+ values->push_back(entries[i]);
+ }
+ return true;
+}
+
+void HostNPScriptObject::OnSignallingConnected(SignalStrategy* signal_strategy,
+ const std::string& full_jid) {
+ OnStateChanged(kConnected);
+}
+
+void HostNPScriptObject::OnSignallingDisconnected() {
+}
+
+void HostNPScriptObject::OnAccessDenied() {
+ DCHECK_EQ(MessageLoop::current(), host_context_.network_message_loop());
+
+ ++failed_login_attempts_;
+ if (failed_login_attempts_ == kMaxLoginAttempts)
+ DisconnectInternal();
+}
+
+void HostNPScriptObject::OnShutdown() {
+ DCHECK_EQ(MessageLoop::current(), host_context_.main_message_loop());
+
+ OnStateChanged(kDisconnected);
+}
+
+// string uid, string auth_token
+bool HostNPScriptObject::Connect(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+
+ LogDebugInfo("Connecting...");
+
+ if (arg_count != 2) {
+ SetException("connect: bad number of arguments");
+ return false;
+ }
+
+ std::string uid = StringFromNPVariant(args[0]);
+ if (uid.empty()) {
+ SetException("connect: bad uid argument");
+ return false;
+ }
+
+ std::string auth_service_with_token = StringFromNPVariant(args[1]);
+ std::string auth_token;
+ std::string auth_service;
+ ParseAuthTokenWithService(auth_service_with_token, &auth_token,
+ &auth_service);
+ if (auth_token.empty()) {
+ SetException("connect: auth_service_with_token argument has empty token");
+ return false;
+ }
+
+ ConnectInternal(uid, auth_token, auth_service);
+
+ return true;
+}
+
+void HostNPScriptObject::ConnectInternal(
+ const std::string& uid,
+ const std::string& auth_token,
+ const std::string& auth_service) {
+ if (MessageLoop::current() != host_context_.main_message_loop()) {
+ host_context_.main_message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &HostNPScriptObject::ConnectInternal,
+ uid, auth_token, auth_service));
+ return;
+ }
+ // Store the supplied user ID and token to the Host configuration.
+ scoped_refptr<MutableHostConfig> host_config = new InMemoryHostConfig();
+ host_config->SetString(kXmppLoginConfigPath, uid);
+ host_config->SetString(kXmppAuthTokenConfigPath, auth_token);
+ host_config->SetString(kXmppAuthServiceConfigPath, auth_service);
+
+ // Create an access verifier and fetch the host secret.
+ scoped_ptr<SupportAccessVerifier> access_verifier;
+ access_verifier.reset(new SupportAccessVerifier());
+
+ // Generate a key pair for the Host to use.
+ // TODO(wez): Move this to the worker thread.
+ HostKeyPair host_key_pair;
+ host_key_pair.Generate();
+ host_key_pair.Save(host_config);
+
+ // Request registration of the host for support.
+ scoped_ptr<RegisterSupportHostRequest> register_request(
+ new RegisterSupportHostRequest());
+ if (!register_request->Init(
+ host_config.get(),
+ base::Bind(&HostNPScriptObject::OnReceivedSupportID,
+ base::Unretained(this),
+ access_verifier.get()))) {
+ OnStateChanged(kDisconnected);
+ return;
+ }
+
+ // Create the Host.
+ scoped_refptr<ChromotingHost> host =
+ ChromotingHost::Create(&host_context_, host_config,
+ access_verifier.release());
+ host->AddStatusObserver(this);
+ host->AddStatusObserver(register_request.get());
+ host->set_it2me(true);
+
+ // Nothing went wrong, so lets save the host, config and request.
+ host_ = host;
+ host_config_ = host_config;
+ register_request_.reset(register_request.release());
+
+ // Start the Host.
+ host_->Start();
+
+ OnStateChanged(kRequestedAccessCode);
+ return;
+}
+
+bool HostNPScriptObject::Disconnect(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ if (arg_count != 0) {
+ SetException("disconnect: bad number of arguments");
+ return false;
+ }
+
+ DisconnectInternal();
+
+ return true;
+}
+
+void HostNPScriptObject::DisconnectInternal() {
+ if (MessageLoop::current() != host_context_.main_message_loop()) {
+ host_context_.main_message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &HostNPScriptObject::DisconnectInternal));
+ return;
+ }
+
+ if (!host_) {
+ disconnected_event_.Signal();
+ return;
+ }
+
+ host_->Shutdown(
+ NewRunnableMethod(this, &HostNPScriptObject::OnShutdownFinished));
+}
+
+void HostNPScriptObject::OnShutdownFinished() {
+ DCHECK_EQ(MessageLoop::current(), host_context_.main_message_loop());
+
+ host_ = NULL;
+ register_request_.reset();
+ host_config_ = NULL;
+ disconnected_event_.Signal();
+}
+
+void HostNPScriptObject::OnReceivedSupportID(
+ SupportAccessVerifier* access_verifier,
+ bool success,
+ const std::string& support_id) {
+ CHECK_NE(base::PlatformThread::CurrentId(), np_thread_id_);
+
+ if (!success) {
+ // TODO(wez): Replace the success/fail flag with full error reporting.
+ DisconnectInternal();
+ return;
+ }
+
+ // Inform the AccessVerifier of our Support-Id, for authentication.
+ access_verifier->OnIT2MeHostRegistered(success, support_id);
+
+ // Combine the Support Id with the Host Id to make the Access Code.
+ // TODO(wez): Locking, anyone?
+ access_code_ = support_id + "-" + access_verifier->host_secret();
+
+ // Let the caller know that life is good.
+ OnStateChanged(kReceivedAccessCode);
+}
+
+void HostNPScriptObject::OnStateChanged(State state) {
+ if (destructing_.IsSet()) {
+ return;
+ }
+
+ if (!host_context_.IsUIThread()) {
+ host_context_.PostToUIThread(
+ FROM_HERE,
+ NewRunnableMethod(this, &HostNPScriptObject::OnStateChanged, state));
+ return;
+ }
+ state_ = state;
+ if (on_state_changed_func_) {
+ VLOG(2) << "Calling state changed " << state;
+ bool is_good = CallJSFunction(on_state_changed_func_, NULL, 0, NULL);
+ LOG_IF(ERROR, !is_good) << "OnStateChanged failed";
+ }
+}
+
+void HostNPScriptObject::LogDebugInfo(const std::string& message) {
+ if (!host_context_.IsUIThread()) {
+ host_context_.PostToUIThread(
+ FROM_HERE,
+ NewRunnableMethod(this, &HostNPScriptObject::LogDebugInfo, message));
+ return;
+ }
+ if (log_debug_info_func_) {
+ NPVariant* arg = new NPVariant();
+ LOG(INFO) << "Logging: " << message;
+ STRINGZ_TO_NPVARIANT(message.c_str(), *arg);
+ bool is_good = CallJSFunction(log_debug_info_func_, arg, 1, NULL);
+ LOG_IF(ERROR, !is_good) << "LogDebugInfo failed";
+ }
+}
+
+void HostNPScriptObject::SetException(const std::string& exception_string) {
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
+ LogDebugInfo(exception_string);
+}
+
+bool HostNPScriptObject::CallJSFunction(NPObject* func,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result) {
+ NPVariant np_result;
+ bool is_good = func->_class->invokeDefault(func, args, argCount, &np_result);
+ if (is_good) {
+ if (result) {
+ *result = np_result;
+ } else {
+ g_npnetscape_funcs->releasevariantvalue(&np_result);
+ }
+ }
+ return is_good;
+}
+
+void HostNPScriptObject::PostTaskToNPThread(
+ const tracked_objects::Location& from_here, Task* task) {
+ // The NPAPI functions cannot make use of |from_here|, but this method is
+ // passed as a callback to ChromotingHostContext, so it needs to have the
+ // appropriate signature.
+
+ // Can be called from any thread.
+ g_npnetscape_funcs->pluginthreadasynccall(plugin_,
+ &NPTaskSpringboard,
+ task);
+}
+
+void HostNPScriptObject::NPTaskSpringboard(void* task) {
+ Task* real_task = reinterpret_cast<Task*>(task);
+ real_task->Run();
+ delete real_task;
+}
+
+} // namespace remoting
diff --git a/remoting/host/host_script_object.h b/remoting/host/host_script_object.h
new file mode 100644
index 0000000..ea953f1
--- /dev/null
+++ b/remoting/host/host_script_object.h
@@ -0,0 +1,148 @@
+// 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.
+
+#ifndef REMOTING_HOST_HOST_SCRIPT_OBJECT_H_
+#define REMOTING_HOST_HOST_SCRIPT_OBJECT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "remoting/host/chromoting_host_context.h"
+#include "remoting/host/host_status_observer.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/npfunctions.h"
+#include "third_party/npapi/bindings/npruntime.h"
+
+class Task;
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace remoting {
+
+class ChromotingHost;
+class MutableHostConfig;
+class RegisterSupportHostRequest;
+class SignalStrategy;
+class SupportAccessVerifier;
+
+// NPAPI plugin implementation for remoting host script object.
+// HostNPScriptObject creates threads that are required to run
+// ChromotingHost and starts/stops the host on those threads. When
+// destroyed it sychronously shuts down the host and all threads.
+class HostNPScriptObject : public HostStatusObserver {
+ public:
+ HostNPScriptObject(NPP plugin, NPObject* parent);
+ virtual ~HostNPScriptObject();
+
+ bool Init();
+
+ bool HasMethod(const std::string& method_name);
+ bool InvokeDefault(const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+ bool Invoke(const std::string& method_name,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+ bool HasProperty(const std::string& property_name);
+ bool GetProperty(const std::string& property_name, NPVariant* result);
+ bool SetProperty(const std::string& property_name, const NPVariant* value);
+ bool RemoveProperty(const std::string& property_name);
+ bool Enumerate(std::vector<std::string>* values);
+
+ // remoting::HostStatusObserver implementation.
+ virtual void OnSignallingConnected(remoting::SignalStrategy* signal_strategy,
+ const std::string& full_jid) OVERRIDE;
+ virtual void OnSignallingDisconnected() OVERRIDE;
+ virtual void OnAccessDenied() OVERRIDE;
+ virtual void OnShutdown() OVERRIDE;
+
+ private:
+ enum State {
+ kDisconnected,
+ kRequestedAccessCode,
+ kReceivedAccessCode,
+ kConnected,
+ kAffirmingConnection,
+ kError
+ };
+
+ // Start connection. args are:
+ // string uid, string auth_token
+ // No result.
+ bool Connect(const NPVariant* args, uint32_t argCount, NPVariant* result);
+
+ // Disconnect. No arguments or result.
+ bool Disconnect(const NPVariant* args, uint32_t argCount, NPVariant* result);
+
+ // Call LogDebugInfo handler if there is one.
+ void LogDebugInfo(const std::string& message);
+
+ // Call OnStateChanged handler if there is one.
+ void OnStateChanged(State state);
+
+ // Callbacks invoked during session setup.
+ void OnReceivedSupportID(remoting::SupportAccessVerifier* access_verifier,
+ bool success,
+ const std::string& support_id);
+
+ // Helper functions that run on main thread. Can be called on any
+ // other thread.
+ void ConnectInternal(const std::string& uid,
+ const std::string& auth_token,
+ const std::string& auth_service);
+ void DisconnectInternal();
+
+ // Callback for ChromotingHost::Shutdown().
+ void OnShutdownFinished();
+
+ // Call a JavaScript function wrapped as an NPObject.
+ // If result is non-null, the result of the call will be stored in it.
+ // Caller is responsible for releasing result if they ask for it.
+ static bool CallJSFunction(NPObject* func,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+
+ // Posts a task on the main NP thread.
+ void PostTaskToNPThread(const tracked_objects::Location& from_here,
+ Task* task);
+
+ // Utility function for PostTaskToNPThread.
+ static void NPTaskSpringboard(void* task);
+
+ // Set an exception for the current call.
+ void SetException(const std::string& exception_string);
+
+ NPP plugin_;
+ NPObject* parent_;
+ int state_;
+ std::string access_code_;
+ NPObject* log_debug_info_func_;
+ NPObject* on_state_changed_func_;
+ base::PlatformThreadId np_thread_id_;
+
+ scoped_ptr<RegisterSupportHostRequest> register_request_;
+ scoped_refptr<ChromotingHost> host_;
+ scoped_refptr<MutableHostConfig> host_config_;
+ ChromotingHostContext host_context_;
+ int failed_login_attempts_;
+
+ base::WaitableEvent disconnected_event_;
+ base::CancellationFlag destructing_;
+};
+
+} // namespace remoting
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::HostNPScriptObject);
+
+#endif // REMOTING_HOST_HOST_SCRIPT_OBJECT_H_
diff --git a/remoting/host/host_status_observer.h b/remoting/host/host_status_observer.h
index f38775a..45bdd22 100644
--- a/remoting/host/host_status_observer.h
+++ b/remoting/host/host_status_observer.h
@@ -5,6 +5,8 @@
#ifndef REMOTING_HOST_STATUS_OBSERVER_H_
#define REMOTING_HOST_STATUS_OBSERVER_H_
+#include <string>
+
namespace remoting {
class SignalStrategy;