diff options
author | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 20:33:12 +0000 |
---|---|---|
committer | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 20:33:12 +0000 |
commit | eadaca8e6f40e19cd9111adaecfbcfb1b8b3ec9e (patch) | |
tree | f934864c759bddd78d09eb9bfc60e29634a9959a /remoting | |
parent | 24950cb67304ad09ee05e32c3ffa5e07f52f0b8c (diff) | |
download | chromium_src-eadaca8e6f40e19cd9111adaecfbcfb1b8b3ec9e.zip chromium_src-eadaca8e6f40e19cd9111adaecfbcfb1b8b3ec9e.tar.gz chromium_src-eadaca8e6f40e19cd9111adaecfbcfb1b8b3ec9e.tar.bz2 |
Move IT2Me functions into their own implementation object.
The script object itself is now used exclusively on the plugin/UI thread.
When the IT2Me host function is used, an internal ref-counted implementation
object is created which runs (mostly) on the network thread. The IT2Me
implementation is passed a weak pointer to the script object through which
to post tasks to it, allowing it to safely out-live the script object, for
instance if there are still pending tasks for it at the moment it is stopped.
BUG=156257
TEST=PyAuto & manual testing of IT2Me host functions.
Review URL: https://chromiumcodereview.appspot.com/11195031
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162777 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/plugin/host_plugin.cc | 4 | ||||
-rw-r--r-- | remoting/host/plugin/host_script_object.cc | 1166 | ||||
-rw-r--r-- | remoting/host/plugin/host_script_object.h | 141 |
3 files changed, 714 insertions, 597 deletions
diff --git a/remoting/host/plugin/host_plugin.cc b/remoting/host/plugin/host_plugin.cc index 2638e79..62af607 100644 --- a/remoting/host/plugin/host_plugin.cc +++ b/remoting/host/plugin/host_plugin.cc @@ -207,10 +207,6 @@ class HostNPPlugin : public remoting::PluginThreadTaskRunner::Delegate { object->_class = aClass; object->referenceCount = 1; object->scriptable_object = new HostNPScriptObject(npp, object, plugin); - if (!object->scriptable_object->Init()) { - Deallocate(object); - object = NULL; - } return object; } diff --git a/remoting/host/plugin/host_script_object.cc b/remoting/host/plugin/host_script_object.cc index c0969d0..cc6a708 100644 --- a/remoting/host/plugin/host_script_object.cc +++ b/remoting/host/plugin/host_script_object.cc @@ -24,6 +24,7 @@ #include "remoting/host/host_event_logger.h" #include "remoting/host/host_key_pair.h" #include "remoting/host/host_secret.h" +#include "remoting/host/host_status_observer.h" #include "remoting/host/it2me_host_user_interface.h" #include "remoting/host/network_settings.h" #include "remoting/host/pin_hash.h" @@ -82,85 +83,614 @@ const int kMaxWorkerPoolThreads = 2; } // namespace +// Internal implementation of the plugin's It2Me host function. +class HostNPScriptObject::It2MeImpl + : public base::RefCountedThreadSafe<It2MeImpl>, + public HostStatusObserver { + public: + It2MeImpl( + scoped_ptr<ChromotingHostContext> context, + scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, + base::WeakPtr<HostNPScriptObject> script_object, + const UiStrings& ui_strings); + + // Methods called by the script object, from the plugin thread. + + // Creates It2Me host structures and starts the host. + void Connect(const std::string& uid, + const std::string& auth_token, + const std::string& auth_service); + + // Disconnects the host, ready for tear-down. + // Also called internally, from the network thread. + void Disconnect(); + + // Request a NAT policy notification. + void RequestNatPolicy(); + + // remoting::HostStatusObserver implementation. + virtual void OnAccessDenied(const std::string& jid) OVERRIDE; + virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE; + virtual void OnClientDisconnected(const std::string& jid) OVERRIDE; + virtual void OnShutdown() OVERRIDE; + + private: + friend class base::RefCountedThreadSafe<It2MeImpl>; + + virtual ~It2MeImpl(); + + // Used to delete and join the ChromotingHostContext on the UI thread. + static void DeleteHostContext(scoped_ptr<ChromotingHostContext> context) {} + + // Updates state of the host. Can be called only on the network thread. + void SetState(State state); + + // Called by Connect() to check for policies and start connection process. + void ReadPolicyAndConnect(const std::string& uid, + const std::string& auth_token, + const std::string& auth_service); + + // Called by ReadPolicyAndConnect once policies have been read. + void FinishConnect(const std::string& uid, + const std::string& auth_token, + const std::string& auth_service); + + // Called when the support host registration completes. + void OnReceivedSupportID(bool success, + const std::string& support_id, + const base::TimeDelta& lifetime); + + // Called when ChromotingHost::Shutdown() has completed. + void OnShutdownFinished(); + + // Called when initial policies are read, and when they change. + void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); + + // Handlers for NAT traversal and host domain policies. + void UpdateNatPolicy(bool nat_traversal_enabled); + void UpdateHostDomainPolicy(const std::string& host_domain); + + scoped_ptr<ChromotingHostContext> host_context_; + scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner_; + base::WeakPtr<HostNPScriptObject> script_object_; + UiStrings ui_strings_; + + State state_; + + HostKeyPair host_key_pair_; + scoped_ptr<SignalStrategy> signal_strategy_; + scoped_ptr<RegisterSupportHostRequest> register_request_; + scoped_ptr<LogToServer> log_to_server_; + scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; + scoped_ptr<It2MeHostUserInterface> it2me_host_user_interface_; + scoped_ptr<HostEventLogger> host_event_logger_; + + scoped_refptr<ChromotingHost> host_; + int failed_login_attempts_; + + scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_; + + // Host the current nat traversal policy setting. + bool nat_traversal_enabled_; + + // The host domain policy setting. + std::string required_host_domain_; + + // Indicates whether or not a policy has ever been read. This is to ensure + // that on startup, we do not accidentally start a connection before we have + // queried our policy restrictions. + bool policy_received_; + + // On startup, it is possible to have Connect() called before the policy read + // is completed. Rather than just failing, we thunk the connection call so + // it can be executed after at least one successful policy read. This + // variable contains the thunk if it is necessary. + base::Closure pending_connect_; + + DISALLOW_COPY_AND_ASSIGN(It2MeImpl); +}; + +HostNPScriptObject::It2MeImpl::It2MeImpl( + scoped_ptr<ChromotingHostContext> host_context, + scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, + base::WeakPtr<HostNPScriptObject> script_object, + const UiStrings& ui_strings) + : host_context_(host_context.Pass()), + plugin_task_runner_(plugin_task_runner), + script_object_(script_object), + ui_strings_(ui_strings), + state_(kDisconnected), + failed_login_attempts_(0), + nat_traversal_enabled_(false), + policy_received_(false) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); +} + +void HostNPScriptObject::It2MeImpl::Connect( + const std::string& uid, + const std::string& auth_token, + const std::string& auth_service) { + if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + host_context_->ui_task_runner()->PostTask( + FROM_HERE, + base::Bind(&It2MeImpl::Connect, this, uid, auth_token, auth_service)); + return; + } + + // Create the desktop environment factory. + desktop_environment_factory_.reset(new DesktopEnvironmentFactory( + host_context_->input_task_runner(), host_context_->ui_task_runner())); + + // Start monitoring configured policies. + policy_watcher_.reset( + policy_hack::PolicyWatcher::Create(host_context_->network_task_runner())); + policy_watcher_->StartWatching( + base::Bind(&It2MeImpl::OnPolicyUpdate, this)); + + // The UserInterface object needs to be created on the UI thread. + it2me_host_user_interface_.reset( + new It2MeHostUserInterface(host_context_->network_task_runner(), + host_context_->ui_task_runner())); + + // Switch to the network thread to start the actual connection. + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind( + &It2MeImpl::ReadPolicyAndConnect, this, + uid, auth_token, auth_service)); +} + +void HostNPScriptObject::It2MeImpl::Disconnect() { + if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::Disconnect, this)); + return; + } + + switch (state_) { + case kDisconnected: + OnShutdownFinished(); + return; + + case kStarting: + SetState(kDisconnecting); + SetState(kDisconnected); + OnShutdownFinished(); + return; + + case kDisconnecting: + return; + + default: + SetState(kDisconnecting); + + if (!host_) { + OnShutdownFinished(); + return; + } + + // ChromotingHost::Shutdown() may destroy SignalStrategy + // synchronously, but SignalStrategy::Listener handlers are not + // allowed to destroy SignalStrategy, so post task to call + // Shutdown() later. + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind( + &ChromotingHost::Shutdown, host_, + base::Bind(&It2MeImpl::OnShutdownFinished, this))); + return; + } +} + +void HostNPScriptObject::It2MeImpl::RequestNatPolicy() { + if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::RequestNatPolicy, this)); + return; + } + + if (policy_received_) + UpdateNatPolicy(nat_traversal_enabled_); +} + +void HostNPScriptObject::It2MeImpl::ReadPolicyAndConnect( + const std::string& uid, + const std::string& auth_token, + const std::string& auth_service) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + SetState(kStarting); + + // Only proceed to FinishConnect() if at least one policy update has been + // received. + if (policy_received_) { + FinishConnect(uid, auth_token, auth_service); + } else { + // Otherwise, create the policy watcher, and thunk the connect. + pending_connect_ = + base::Bind(&It2MeImpl::FinishConnect, this, + uid, auth_token, auth_service); + } +} + +void HostNPScriptObject::It2MeImpl::FinishConnect( + const std::string& uid, + const std::string& auth_token, + const std::string& auth_service) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + if (state_ != kStarting) { + // Host has been stopped while we were fetching policy. + return; + } + + // Check the host domain policy. + if (!required_host_domain_.empty() && + !EndsWith(uid, std::string("@") + required_host_domain_, false)) { + SetState(kInvalidDomainError); + return; + } + + // Generate a key pair for the Host to use. + // TODO(wez): Move this to the worker thread. + host_key_pair_.Generate(); + + // Create XMPP connection. + scoped_ptr<SignalStrategy> signal_strategy( + new XmppSignalStrategy(host_context_->url_request_context_getter(), + uid, auth_token, auth_service)); + + // Request registration of the host for support. + scoped_ptr<RegisterSupportHostRequest> register_request( + new RegisterSupportHostRequest( + signal_strategy.get(), &host_key_pair_, + base::Bind(&It2MeImpl::OnReceivedSupportID, + base::Unretained(this)))); + + // Beyond this point nothing can fail, so save the config and request. + signal_strategy_ = signal_strategy.Pass(); + register_request_ = register_request.Pass(); + + // If NAT traversal is off then limit port range to allow firewall pin-holing. + LOG(INFO) << "NAT state: " << nat_traversal_enabled_; + NetworkSettings network_settings( + nat_traversal_enabled_ ? + NetworkSettings::NAT_TRAVERSAL_ENABLED : + NetworkSettings::NAT_TRAVERSAL_DISABLED); + if (!nat_traversal_enabled_) { + network_settings.min_port = NetworkSettings::kDefaultMinPort; + network_settings.max_port = NetworkSettings::kDefaultMaxPort; + } + + // Create the host. + host_ = new ChromotingHost( + signal_strategy_.get(), + desktop_environment_factory_.get(), + CreateHostSessionManager(network_settings, + host_context_->url_request_context_getter()), + host_context_->capture_task_runner(), + host_context_->encode_task_runner(), + host_context_->network_task_runner()); + host_->AddStatusObserver(this); + log_to_server_.reset( + new LogToServer(host_, ServerLogEntry::IT2ME, signal_strategy_.get())); + + // Disable audio by default. + // TODO(sergeyu): Add UI to enable it. + scoped_ptr<protocol::CandidateSessionConfig> protocol_config = + protocol::CandidateSessionConfig::CreateDefault(); + protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get()); + host_->set_protocol_config(protocol_config.Pass()); + + // Provide localization strings to the host. + host_->SetUiStrings(ui_strings_); + + // Create user interface. + it2me_host_user_interface_->Start(host_.get(), + base::Bind(&It2MeImpl::Disconnect, this)); + + // Create event logger. + host_event_logger_ = HostEventLogger::Create(host_, kApplicationName); + + // Connect signaling and start the host. + signal_strategy_->Connect(); + host_->Start(uid); + + SetState(kRequestedAccessCode); + return; +} + +void HostNPScriptObject::It2MeImpl::OnShutdownFinished() { + if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) { + host_context_->ui_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::OnShutdownFinished, this)); + return; + } + + // Note that OnShutdownFinished() may be called more than once. + + // UI needs to be shut down on the UI thread before we destroy the + // host context (because it depends on the context object), but + // only after the host has been shut down (becase the UI object is + // registered as status observer for the host, and we can't + // unregister it from this thread). + it2me_host_user_interface_.reset(); + + // Destroy the DesktopEnvironmentFactory, to free thread references. + desktop_environment_factory_.reset(); + + // Stop listening for policy updates. + if (policy_watcher_.get()) { + base::WaitableEvent policy_watcher_stopped_(true, false); + policy_watcher_->StopWatching(&policy_watcher_stopped_); + policy_watcher_stopped_.Wait(); + policy_watcher_.reset(); + } +} + +void HostNPScriptObject::It2MeImpl::OnAccessDenied(const std::string& jid) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + ++failed_login_attempts_; + if (failed_login_attempts_ == kMaxLoginAttempts) { + Disconnect(); + } +} + +void HostNPScriptObject::It2MeImpl::OnClientAuthenticated( + const std::string& jid) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + if (state_ == kDisconnecting) { + // Ignore the new connection if we are disconnecting. + return; + } + + std::string client_username = jid; + size_t pos = client_username.find('/'); + if (pos != std::string::npos) + client_username.replace(pos, std::string::npos, ""); + + LOG(INFO) << "Client " << client_username << " connected."; + + // Pass the client user name to the script object before changing state. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&HostNPScriptObject::StoreClientUsername, + script_object_, client_username)); + + SetState(kConnected); +} + +void HostNPScriptObject::It2MeImpl::OnClientDisconnected( + const std::string& jid) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + // Pass the client user name to the script object before changing state. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&HostNPScriptObject::StoreClientUsername, + script_object_, std::string())); + + Disconnect(); +} + +void HostNPScriptObject::It2MeImpl::OnShutdown() { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + register_request_.reset(); + log_to_server_.reset(); + signal_strategy_.reset(); + host_event_logger_.reset(); + host_->RemoveStatusObserver(this); + host_ = NULL; + + if (state_ != kDisconnected) { + SetState(kDisconnected); + } +} + +void HostNPScriptObject::It2MeImpl::OnPolicyUpdate( + scoped_ptr<base::DictionaryValue> policies) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + bool nat_policy; + if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName, + &nat_policy)) { + UpdateNatPolicy(nat_policy); + } + std::string host_domain; + if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName, + &host_domain)) { + UpdateHostDomainPolicy(host_domain); + } + + policy_received_ = true; + + if (!pending_connect_.is_null()) { + pending_connect_.Run(); + pending_connect_.Reset(); + } +} + +void HostNPScriptObject::It2MeImpl::UpdateNatPolicy( + bool nat_traversal_enabled) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled; + + // When transitioning from enabled to disabled, force disconnect any + // existing session. + if (nat_traversal_enabled_ && !nat_traversal_enabled) { + Disconnect(); + } + + nat_traversal_enabled_ = nat_traversal_enabled; + + // Notify the web-app of the policy setting. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&HostNPScriptObject::NotifyNatPolicyChanged, + script_object_, nat_traversal_enabled_)); +} + +void HostNPScriptObject::It2MeImpl::UpdateHostDomainPolicy( + const std::string& host_domain) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + VLOG(2) << "UpdateHostDomainPolicy: " << host_domain; + + // When setting a host domain policy, force disconnect any existing session. + if (!host_domain.empty() && state_ != kStarting) { + Disconnect(); + } + + required_host_domain_ = host_domain; +} + +HostNPScriptObject::It2MeImpl::~It2MeImpl() { + // Check that resources that need to be torn down on the UI thread are gone. + DCHECK(!it2me_host_user_interface_.get()); + DCHECK(!desktop_environment_factory_.get()); + DCHECK(!policy_watcher_.get()); + + // We might be getting deleted on one of the threads the |host_context| owns, + // so we need to post it back to the plugin thread to safely join & delete the + // threads it contains. This will go away when we move to AutoThread. + plugin_task_runner_->PostTask(FROM_HERE, + base::Bind(&It2MeImpl::DeleteHostContext, base::Passed(&host_context_))); +} + +void HostNPScriptObject::It2MeImpl::SetState(State state) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + switch (state_) { + case kDisconnected: + DCHECK(state == kStarting || + state == kError) << state; + break; + case kStarting: + DCHECK(state == kRequestedAccessCode || + state == kDisconnecting || + state == kError || + state == kInvalidDomainError) << state; + break; + case kRequestedAccessCode: + DCHECK(state == kReceivedAccessCode || + state == kDisconnecting || + state == kError) << state; + break; + case kReceivedAccessCode: + DCHECK(state == kConnected || + state == kDisconnecting || + state == kError) << state; + break; + case kConnected: + DCHECK(state == kDisconnecting || + state == kDisconnected || + state == kError) << state; + break; + case kDisconnecting: + DCHECK(state == kDisconnected) << state; + break; + case kError: + DCHECK(state == kDisconnecting) << state; + break; + case kInvalidDomainError: + DCHECK(state == kDisconnecting) << state; + break; + }; + + state_ = state; + + // Post a state-change notification to the web-app. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&HostNPScriptObject::NotifyStateChanged, + script_object_, state)); +} + +void HostNPScriptObject::It2MeImpl::OnReceivedSupportID( + bool success, + const std::string& support_id, + const base::TimeDelta& lifetime) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + if (!success) { + SetState(kError); + Disconnect(); + return; + } + + std::string host_secret = GenerateSupportHostSecret(); + std::string access_code = support_id + host_secret; + + std::string local_certificate = host_key_pair_.GenerateCertificate(); + if (local_certificate.empty()) { + LOG(ERROR) << "Failed to generate host certificate."; + SetState(kError); + Disconnect(); + return; + } + + scoped_ptr<protocol::AuthenticatorFactory> factory( + new protocol::It2MeHostAuthenticatorFactory( + local_certificate, *host_key_pair_.private_key(), access_code)); + host_->SetAuthenticatorFactory(factory.Pass()); + + // Pass the Access Code to the script object before changing state. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&HostNPScriptObject::StoreAccessCode, + script_object_, access_code, lifetime)); + + SetState(kReceivedAccessCode); +} + HostNPScriptObject::HostNPScriptObject( NPP plugin, NPObject* parent, PluginThreadTaskRunner::Delegate* plugin_thread_delegate) : plugin_(plugin), parent_(parent), - am_currently_logging_(false), - state_(kDisconnected), - np_thread_id_(base::PlatformThread::CurrentId()), plugin_task_runner_( new PluginThreadTaskRunner(plugin_thread_delegate)), - failed_login_attempts_(0), - nat_traversal_enabled_(false), - policy_received_(false), + auto_plugin_task_runner_( + new AutoThreadTaskRunner(plugin_task_runner_, + base::Bind(&PluginThreadTaskRunner::Quit, + plugin_task_runner_))), + am_currently_logging_(false), + state_(kDisconnected), daemon_controller_(DaemonController::Create()), - worker_thread_("RemotingHostPlugin") { + worker_thread_("RemotingHostPlugin"), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), + weak_ptr_(weak_factory_.GetWeakPtr()) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + worker_thread_.Start(); } HostNPScriptObject::~HostNPScriptObject() { - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); HostLogHandler::UnregisterLoggingScriptObject(this); - // Stop listening for policy updates. - if (policy_watcher_.get()) { - base::WaitableEvent policy_watcher_stopped_(true, false); - policy_watcher_->StopWatching(&policy_watcher_stopped_); - policy_watcher_stopped_.Wait(); - policy_watcher_.reset(); + // Stop the It2Me host if the caller forgot to. + if (it2me_impl_.get()) { + it2me_impl_->Disconnect(); + it2me_impl_ = NULL; } - if (host_context_.get()) { - DisconnectInternal(); - } else { - plugin_task_runner_->Quit(); - } + // Release the AutoThreadTaskRunner so the plugin thread can quit. + auto_plugin_task_runner_ = NULL; // Stop the message loop and run the remaining tasks. The loop will exit // once the wrapping AutoThreadTaskRunner is destroyed. plugin_task_runner_->DetachAndRunShutdownLoop(); - // Stop all threads. - host_context_.reset(); + // Stop the worker thread. worker_thread_.Stop(); } -bool HostNPScriptObject::Init() { - DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - VLOG(2) << "Init"; - - // Create threads for the Chromoting host & desktop environment to use. - scoped_refptr<AutoThreadTaskRunner> auto_plugin_task_runner = - new AutoThreadTaskRunner(plugin_task_runner_, - base::Bind(&PluginThreadTaskRunner::Quit, - plugin_task_runner_)); - host_context_.reset(new ChromotingHostContext(auto_plugin_task_runner)); - auto_plugin_task_runner = NULL; - if (!host_context_->Start()) { - host_context_.reset(); - return false; - } - - // Create the desktop environment factory. - desktop_environment_factory_.reset(new DesktopEnvironmentFactory( - host_context_->input_task_runner(), host_context_->ui_task_runner())); - - // Start monitoring configured policies. - policy_watcher_.reset( - policy_hack::PolicyWatcher::Create(host_context_->network_task_runner())); - policy_watcher_->StartWatching( - base::Bind(&HostNPScriptObject::OnPolicyUpdate, - base::Unretained(this))); - return true; -} - bool HostNPScriptObject::HasMethod(const std::string& method_name) { VLOG(2) << "HasMethod " << method_name; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); return (method_name == kFuncNameConnect || method_name == kFuncNameDisconnect || method_name == kFuncNameLocalize || @@ -179,7 +709,7 @@ bool HostNPScriptObject::InvokeDefault(const NPVariant* args, uint32_t arg_count, NPVariant* result) { VLOG(2) << "InvokeDefault"; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); SetException("exception during default invocation"); return false; } @@ -189,7 +719,7 @@ bool HostNPScriptObject::Invoke(const std::string& method_name, uint32_t arg_count, NPVariant* result) { VLOG(2) << "Invoke " << method_name; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); if (method_name == kFuncNameConnect) { return Connect(args, arg_count, result); } else if (method_name == kFuncNameDisconnect) { @@ -222,7 +752,7 @@ bool HostNPScriptObject::Invoke(const std::string& method_name, bool HostNPScriptObject::HasProperty(const std::string& property_name) { VLOG(2) << "HasProperty " << property_name; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); return (property_name == kAttrNameAccessCode || property_name == kAttrNameAccessCodeLifetime || property_name == kAttrNameClient || @@ -243,7 +773,7 @@ bool HostNPScriptObject::HasProperty(const std::string& property_name) { bool HostNPScriptObject::GetProperty(const std::string& property_name, NPVariant* result) { VLOG(2) << "GetProperty " << property_name; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); if (!result) { SetException("GetProperty: NULL result"); return false; @@ -262,11 +792,9 @@ bool HostNPScriptObject::GetProperty(const std::string& property_name, INT32_TO_NPVARIANT(state_, *result); return true; } else if (property_name == kAttrNameAccessCode) { - base::AutoLock auto_lock(access_code_lock_); *result = NPVariantFromString(access_code_); return true; } else if (property_name == kAttrNameAccessCodeLifetime) { - base::AutoLock auto_lock(access_code_lock_); INT32_TO_NPVARIANT(access_code_lifetime_.InSeconds(), *result); return true; } else if (property_name == kAttrNameClient) { @@ -308,19 +836,14 @@ bool HostNPScriptObject::GetProperty(const std::string& property_name, bool HostNPScriptObject::SetProperty(const std::string& property_name, const NPVariant* value) { VLOG(2) << "SetProperty " << property_name; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); if (property_name == kAttrNameOnNatTraversalPolicyChanged) { if (NPVARIANT_IS_OBJECT(*value)) { on_nat_traversal_policy_changed_func_ = NPVARIANT_TO_OBJECT(*value); - bool policy_received, nat_traversal_enabled; - { - base::AutoLock lock(nat_policy_lock_); - policy_received = policy_received_; - nat_traversal_enabled = nat_traversal_enabled_; - } - if (policy_received) { - UpdateWebappNatPolicy(nat_traversal_enabled); + if (it2me_impl_) { + // Ask the It2Me implementation to notify the web-app of the policy. + it2me_impl_->RequestNatPolicy(); } return true; } else { @@ -358,13 +881,13 @@ bool HostNPScriptObject::SetProperty(const std::string& property_name, bool HostNPScriptObject::RemoveProperty(const std::string& property_name) { VLOG(2) << "RemoveProperty " << property_name; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); return false; } bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) { VLOG(2) << "Enumerate"; - CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_); + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); const char* entries[] = { kAttrNameAccessCode, kAttrNameState, @@ -396,52 +919,6 @@ bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) { return true; } -void HostNPScriptObject::OnAccessDenied(const std::string& jid) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - ++failed_login_attempts_; - if (failed_login_attempts_ == kMaxLoginAttempts) { - DisconnectInternal(); - } -} - -void HostNPScriptObject::OnClientAuthenticated(const std::string& jid) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - if (state_ == kDisconnecting) { - // Ignore the new connection if we are disconnecting. - return; - } - - client_username_ = jid; - size_t pos = client_username_.find('/'); - if (pos != std::string::npos) - client_username_.replace(pos, std::string::npos, ""); - LOG(INFO) << "Client " << client_username_ << " connected."; - SetState(kConnected); -} - -void HostNPScriptObject::OnClientDisconnected(const std::string& jid) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - client_username_.clear(); - DisconnectInternal(); -} - -void HostNPScriptObject::OnShutdown() { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - register_request_.reset(); - log_to_server_.reset(); - signal_strategy_.reset(); - host_event_logger_.reset(); - host_->RemoveStatusObserver(this); - host_ = NULL; - - if (state_ != kDisconnected) { - SetState(kDisconnected); - } -} - // string uid, string auth_token bool HostNPScriptObject::Connect(const NPVariant* args, uint32_t arg_count, @@ -455,7 +932,7 @@ bool HostNPScriptObject::Connect(const NPVariant* args, return false; } - if (state_ != kDisconnected) { + if (it2me_impl_) { SetException("connect: can be called only when disconnected"); return false; } @@ -476,136 +953,20 @@ bool HostNPScriptObject::Connect(const NPVariant* args, return false; } - // The UserInterface object needs to be created on the UI thread. - it2me_host_user_interface_.reset( - new It2MeHostUserInterface(host_context_->network_task_runner(), - host_context_->ui_task_runner())); - - ReadPolicyAndConnect(uid, auth_token, auth_service); - - return true; -} - -void HostNPScriptObject::ReadPolicyAndConnect(const std::string& uid, - const std::string& auth_token, - const std::string& auth_service) { - if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { - host_context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind( - &HostNPScriptObject::ReadPolicyAndConnect, base::Unretained(this), - uid, auth_token, auth_service)); - return; - } - - SetState(kStarting); - - // Only proceed to FinishConnect() if at least one policy update has been - // received. - if (policy_received_) { - FinishConnect(uid, auth_token, auth_service); - } else { - // Otherwise, create the policy watcher, and thunk the connect. - pending_connect_ = - base::Bind(&HostNPScriptObject::FinishConnect, - base::Unretained(this), uid, auth_token, auth_service); - } -} - -void HostNPScriptObject::FinishConnect( - const std::string& uid, - const std::string& auth_token, - const std::string& auth_service) { - if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { - host_context_->network_task_runner()->PostTask(FROM_HERE, base::Bind( - &HostNPScriptObject::FinishConnect, base::Unretained(this), - uid, auth_token, auth_service)); - return; - } - - if (state_ != kStarting) { - // Host has been stopped while we were fetching policy. - return; - } - - // Check the host domain policy. - if (!required_host_domain_.empty() && - !EndsWith(uid, std::string("@") + required_host_domain_, false)) { - SetState(kInvalidDomainError); - return; - } - - // Generate a key pair for the Host to use. - // TODO(wez): Move this to the worker thread. - host_key_pair_.Generate(); - - // Create XMPP connection. - scoped_ptr<SignalStrategy> signal_strategy( - new XmppSignalStrategy(host_context_->url_request_context_getter(), - uid, auth_token, auth_service)); - - // Request registration of the host for support. - scoped_ptr<RegisterSupportHostRequest> register_request( - new RegisterSupportHostRequest( - signal_strategy.get(), &host_key_pair_, - base::Bind(&HostNPScriptObject::OnReceivedSupportID, - base::Unretained(this)))); - - // Beyond this point nothing can fail, so save the config and request. - signal_strategy_.reset(signal_strategy.release()); - register_request_.reset(register_request.release()); - - // If NAT traversal is off then limit port range to allow firewall pin-holing. - LOG(INFO) << "NAT state: " << nat_traversal_enabled_; - NetworkSettings network_settings( - nat_traversal_enabled_ ? - NetworkSettings::NAT_TRAVERSAL_ENABLED : - NetworkSettings::NAT_TRAVERSAL_DISABLED); - if (!nat_traversal_enabled_) { - network_settings.min_port = NetworkSettings::kDefaultMinPort; - network_settings.max_port = NetworkSettings::kDefaultMaxPort; - } - - // Create the host. - host_ = new ChromotingHost( - signal_strategy_.get(), - desktop_environment_factory_.get(), - CreateHostSessionManager(network_settings, - host_context_->url_request_context_getter()), - host_context_->capture_task_runner(), - host_context_->encode_task_runner(), - host_context_->network_task_runner()); - host_->AddStatusObserver(this); - log_to_server_.reset( - new LogToServer(host_, ServerLogEntry::IT2ME, signal_strategy_.get())); - - // Disable audio by default. - // TODO(sergeyu): Add UI to enable it. - scoped_ptr<protocol::CandidateSessionConfig> protocol_config = - protocol::CandidateSessionConfig::CreateDefault(); - protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get()); - host_->set_protocol_config(protocol_config.Pass()); - - // Provide localization strings to the host. - { - base::AutoLock auto_lock(ui_strings_lock_); - host_->SetUiStrings(ui_strings_); + // Create threads for the Chromoting host & desktop environment to use. + scoped_ptr<ChromotingHostContext> host_context( + new ChromotingHostContext(auto_plugin_task_runner_)); + if (!host_context->Start()) { + SetException("connect: failed to start threads"); + return false; } - // Create user interface. - base::Closure disconnect_callback = base::Bind( - &ChromotingHost::Shutdown, base::Unretained(host_.get()), - base::Closure()); - it2me_host_user_interface_->Start(host_.get(), disconnect_callback); - - // Create event logger. - host_event_logger_ = HostEventLogger::Create(host_, kApplicationName); - - // Connect signaling and start the host. - signal_strategy_->Connect(); - host_->Start(uid); + // Create the It2Me host implementation and start connecting. + it2me_impl_ = new It2MeImpl( + host_context.Pass(), auto_plugin_task_runner_, weak_ptr_, ui_strings_); + it2me_impl_->Connect(uid, auth_token, auth_service); - SetState(kRequestedAccessCode); - return; + return true; } bool HostNPScriptObject::Disconnect(const NPVariant* args, @@ -617,7 +978,10 @@ bool HostNPScriptObject::Disconnect(const NPVariant* args, return false; } - DisconnectInternal(); + if (it2me_impl_) { + it2me_impl_->Disconnect(); + it2me_impl_ = NULL; + } return true; } @@ -692,6 +1056,9 @@ bool HostNPScriptObject::GenerateKeyPair(const NPVariant* args, return false; } + // TODO(wez): HostNPScriptObject needn't be touched on worker + // thread, so make DoGenerateKeyPair static and pass it a callback + // to run (crbug.com/156257). worker_thread_.message_loop_proxy()->PostTask( FROM_HERE, base::Bind(&HostNPScriptObject::DoGenerateKeyPair, base::Unretained(this), callback_obj)); @@ -730,6 +1097,8 @@ bool HostNPScriptObject::UpdateDaemonConfig(const NPVariant* args, return false; } + // TODO(wez): Pass a static method here, that will post the result + // back to us on the right thread (crbug.com/156257). daemon_controller_->UpdateConfig( config_dict.Pass(), base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, @@ -751,8 +1120,8 @@ bool HostNPScriptObject::GetDaemonConfig(const NPVariant* args, return false; } - // We control lifetime of the |daemon_controller_| so it's safe to - // use base::Unretained() here. + // TODO(wez): Pass a static method here, that will post the result + // back to us on the right thread (crbug.com/156257). daemon_controller_->GetConfig( base::Bind(&HostNPScriptObject::InvokeGetDaemonConfigCallback, base::Unretained(this), callback_obj)); @@ -774,8 +1143,8 @@ bool HostNPScriptObject::GetDaemonVersion(const NPVariant* args, return false; } - // We control lifetime of the |daemon_controller_| so it's safe to - // use base::Unretained() here. + // TODO(wez): Pass a static method here, that will post the result + // back to us on the right thread (crbug.com/156257). daemon_controller_->GetVersion( base::Bind(&HostNPScriptObject::InvokeGetDaemonVersionCallback, base::Unretained(this), callback_obj)); @@ -797,8 +1166,8 @@ bool HostNPScriptObject::GetUsageStatsConsent(const NPVariant* args, return false; } - // We control lifetime of the |daemon_controller_| so it's safe to - // use base::Unretained() here. + // TODO(wez): Pass a static method here, that will post the result + // back to us on the right thread (crbug.com/156257). daemon_controller_->GetUsageStatsConsent( base::Bind(&HostNPScriptObject::InvokeGetUsageStatsConsentCallback, base::Unretained(this), callback_obj)); @@ -808,6 +1177,8 @@ bool HostNPScriptObject::GetUsageStatsConsent(const NPVariant* args, bool HostNPScriptObject::StartDaemon(const NPVariant* args, uint32_t arg_count, NPVariant* result) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + if (arg_count != 3) { SetException("startDaemon: bad number of arguments"); return false; @@ -835,6 +1206,8 @@ bool HostNPScriptObject::StartDaemon(const NPVariant* args, return false; } + // TODO(wez): Pass a static method here, that will post the result + // back to us on the right thread (crbug.com/156257). daemon_controller_->SetConfigAndStart( config_dict.Pass(), NPVARIANT_TO_BOOLEAN(args[1]), @@ -846,6 +1219,8 @@ bool HostNPScriptObject::StartDaemon(const NPVariant* args, bool HostNPScriptObject::StopDaemon(const NPVariant* args, uint32_t arg_count, NPVariant* result) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + if (arg_count != 1) { SetException("stopDaemon: bad number of arguments"); return false; @@ -857,240 +1232,54 @@ bool HostNPScriptObject::StopDaemon(const NPVariant* args, return false; } + // TODO(wez): Pass a static method here, that will post the result + // back to us on the right thread (crbug.com/156257). daemon_controller_->Stop( base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, base::Unretained(this), callback_obj)); return true; } -void HostNPScriptObject::DisconnectInternal() { - if (!host_context_->network_task_runner()) - return; - - if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { - host_context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind(&HostNPScriptObject::DisconnectInternal, - base::Unretained(this))); - return; - } - - switch (state_) { - case kDisconnected: - OnShutdownFinished(); - return; - - case kStarting: - SetState(kDisconnecting); - SetState(kDisconnected); - OnShutdownFinished(); - return; - - case kDisconnecting: - return; - - default: - SetState(kDisconnecting); - - if (!host_) { - OnShutdownFinished(); - return; - } - // ChromotingHost::Shutdown() may destroy SignalStrategy - // synchronously, but SignalStrategy::Listener handlers are not - // allowed to destroy SignalStrategy, so post task to call - // Shutdown() later. - host_context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind( - &ChromotingHost::Shutdown, host_, - base::Bind(&HostNPScriptObject::OnShutdownFinished, - base::Unretained(this)))); - return; - } -} - -void HostNPScriptObject::OnShutdownFinished() { - if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) { - host_context_->ui_task_runner()->PostTask( - FROM_HERE, base::Bind(&HostNPScriptObject::OnShutdownFinished, - base::Unretained(this))); - return; - } - - // UI needs to be shut down on the UI thread before we destroy the - // host context (because it depends on the context object), but - // only after the host has been shut down (becase the UI object is - // registered as status observer for the host, and we can't - // unregister it from this thread). - it2me_host_user_interface_.reset(); - - // Destroy the DesktopEnvironmentFactory, to free thread references. - desktop_environment_factory_.reset(); - - // Release the context's TaskRunner references for the threads, so they can - // exit when no objects need them. - host_context_->ReleaseTaskRunners(); -} - -void HostNPScriptObject::OnPolicyUpdate( - scoped_ptr<base::DictionaryValue> policies) { - if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { - host_context_->network_task_runner()->PostTask( - FROM_HERE, - base::Bind(&HostNPScriptObject::OnPolicyUpdate, - base::Unretained(this), base::Passed(&policies))); - return; - } - - bool nat_policy; - if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName, - &nat_policy)) { - UpdateNatPolicy(nat_policy); - } - std::string host_domain; - if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName, - &host_domain)) { - UpdateHostDomainPolicy(host_domain); - } +void HostNPScriptObject::NotifyStateChanged(State state) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - { - base::AutoLock lock(nat_policy_lock_); - policy_received_ = true; - } + state_ = state; - if (!pending_connect_.is_null()) { - pending_connect_.Run(); - pending_connect_.Reset(); + if (on_state_changed_func_.get()) { + NPVariant state_var; + INT32_TO_NPVARIANT(state, state_var); + InvokeAndIgnoreResult(on_state_changed_func_.get(), &state_var, 1); } } -void HostNPScriptObject::UpdateNatPolicy(bool nat_traversal_enabled) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled; - - // When transitioning from enabled to disabled, force disconnect any - // existing session. - if (nat_traversal_enabled_ && !nat_traversal_enabled) { - DisconnectInternal(); - } +void HostNPScriptObject::NotifyNatPolicyChanged(bool nat_traversal_enabled) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - { - base::AutoLock lock(nat_policy_lock_); - nat_traversal_enabled_ = nat_traversal_enabled; + if (on_nat_traversal_policy_changed_func_.get()) { + NPVariant policy; + BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy); + InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_.get(), + &policy, 1); } - - UpdateWebappNatPolicy(nat_traversal_enabled_); } -void HostNPScriptObject::UpdateHostDomainPolicy( - const std::string& host_domain) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - VLOG(2) << "UpdateHostDomainPolicy: " << host_domain; - - // When setting a host domain policy, force disconnect any existing session. - if (!host_domain.empty() && state_ != kStarting) { - DisconnectInternal(); - } +// Stores the Access Code for the web-app to query. +void HostNPScriptObject::StoreAccessCode(const std::string& access_code, + base::TimeDelta access_code_lifetime) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - required_host_domain_ = host_domain; + access_code_ = access_code; + access_code_lifetime_ = access_code_lifetime; } -void HostNPScriptObject::OnReceivedSupportID( - bool success, - const std::string& support_id, - const base::TimeDelta& lifetime) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - if (!success) { - SetState(kError); - DisconnectInternal(); - return; - } - - std::string host_secret = GenerateSupportHostSecret(); - std::string access_code = support_id + host_secret; - - std::string local_certificate = host_key_pair_.GenerateCertificate(); - if (local_certificate.empty()) { - LOG(ERROR) << "Failed to generate host certificate."; - SetState(kError); - DisconnectInternal(); - return; - } - - scoped_ptr<protocol::AuthenticatorFactory> factory( - new protocol::It2MeHostAuthenticatorFactory( - local_certificate, *host_key_pair_.private_key(), access_code)); - host_->SetAuthenticatorFactory(factory.Pass()); - - { - base::AutoLock lock(access_code_lock_); - access_code_ = access_code; - access_code_lifetime_ = lifetime; - } - - SetState(kReceivedAccessCode); -} +// Stores the client user's name for the web-app to query. +void HostNPScriptObject::StoreClientUsername( + const std::string& client_username) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); -void HostNPScriptObject::SetState(State state) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - switch (state_) { - case kDisconnected: - DCHECK(state == kStarting || - state == kError) << state; - break; - case kStarting: - DCHECK(state == kRequestedAccessCode || - state == kDisconnecting || - state == kError || - state == kInvalidDomainError) << state; - break; - case kRequestedAccessCode: - DCHECK(state == kReceivedAccessCode || - state == kDisconnecting || - state == kError) << state; - break; - case kReceivedAccessCode: - DCHECK(state == kConnected || - state == kDisconnecting || - state == kError) << state; - break; - case kConnected: - DCHECK(state == kDisconnecting || - state == kDisconnected || - state == kError) << state; - break; - case kDisconnecting: - DCHECK(state == kDisconnected) << state; - break; - case kError: - DCHECK(state == kDisconnecting) << state; - break; - case kInvalidDomainError: - DCHECK(state == kDisconnecting) << state; - break; - }; - state_ = state; - NotifyStateChanged(state); + client_username_ = client_username; } -void HostNPScriptObject::NotifyStateChanged(State state) { - if (!plugin_task_runner_->BelongsToCurrentThread()) { - plugin_task_runner_->PostTask( - FROM_HERE, base::Bind(&HostNPScriptObject::NotifyStateChanged, - base::Unretained(this), state)); - return; - } - if (on_state_changed_func_.get()) { - VLOG(2) << "Calling state changed " << state; - NPVariant state_var; - INT32_TO_NPVARIANT(state, state_var); - bool is_good = InvokeAndIgnoreResult(on_state_changed_func_.get(), - &state_var, 1); - LOG_IF(ERROR, !is_good) << "OnStateChanged failed"; - } -} void HostNPScriptObject::PostLogDebugInfo(const std::string& message) { if (plugin_task_runner_->BelongsToCurrentThread()) { // Make sure we're not currently processing a log message. @@ -1103,25 +1292,26 @@ void HostNPScriptObject::PostLogDebugInfo(const std::string& message) { // log messages are shown in the correct order. plugin_task_runner_->PostTask( FROM_HERE, base::Bind(&HostNPScriptObject::LogDebugInfo, - base::Unretained(this), message)); + weak_ptr_, message)); } void HostNPScriptObject::SetWindow(NPWindow* np_window) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + daemon_controller_->SetWindow(np_window->window); } void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) { DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - UiStrings ui_strings; string16 direction; LocalizeString(localize_func, "@@bidi_dir", &direction); - ui_strings.direction = UTF16ToUTF8(direction) == "rtl" ? + ui_strings_.direction = UTF16ToUTF8(direction) == "rtl" ? remoting::UiStrings::RTL : remoting::UiStrings::LTR; LocalizeString(localize_func, /*i18n-content*/"PRODUCT_NAME", - &ui_strings.product_name); + &ui_strings_.product_name); LocalizeString(localize_func, /*i18n-content*/"DISCONNECT_OTHER_BUTTON", - &ui_strings.disconnect_button_text); + &ui_strings_.disconnect_button_text); LocalizeString(localize_func, #if defined(OS_WIN) /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_WINDOWS", @@ -1130,19 +1320,16 @@ void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) { #else /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_LINUX", #endif - &ui_strings.disconnect_button_text_plus_shortcut); + &ui_strings_.disconnect_button_text_plus_shortcut); LocalizeString(localize_func, /*i18n-content*/"CONTINUE_PROMPT", - &ui_strings.continue_prompt); + &ui_strings_.continue_prompt); LocalizeString(localize_func, /*i18n-content*/"CONTINUE_BUTTON", - &ui_strings.continue_button_text); + &ui_strings_.continue_button_text); LocalizeString(localize_func, /*i18n-content*/"STOP_SHARING_BUTTON", - &ui_strings.stop_sharing_button_text); + &ui_strings_.stop_sharing_button_text); LocalizeStringWithSubstitution(localize_func, /*i18n-content*/"MESSAGE_SHARED", "$1", - &ui_strings.disconnect_message); - - base::AutoLock auto_lock(ui_strings_lock_); - ui_strings_ = ui_strings; + &ui_strings_.disconnect_message); } bool HostNPScriptObject::LocalizeString(NPObject* localize_func, @@ -1178,21 +1365,6 @@ bool HostNPScriptObject::LocalizeStringWithSubstitution( return true; } -void HostNPScriptObject::UpdateWebappNatPolicy(bool nat_traversal_enabled) { - if (!plugin_task_runner_->BelongsToCurrentThread()) { - plugin_task_runner_->PostTask( - FROM_HERE, base::Bind(&HostNPScriptObject::UpdateWebappNatPolicy, - base::Unretained(this), nat_traversal_enabled)); - return; - } - if (on_nat_traversal_policy_changed_func_.get()) { - NPVariant policy; - BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy); - InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_.get(), - &policy, 1); - } -} - void HostNPScriptObject::DoGenerateKeyPair(const ScopedRefNPObject& callback) { HostKeyPair key_pair; key_pair.Generate(); @@ -1208,7 +1380,7 @@ void HostNPScriptObject::InvokeGenerateKeyPairCallback( plugin_task_runner_->PostTask( FROM_HERE, base::Bind( &HostNPScriptObject::InvokeGenerateKeyPairCallback, - base::Unretained(this), callback, private_key, public_key)); + weak_ptr_, callback, private_key, public_key)); return; } @@ -1227,7 +1399,7 @@ void HostNPScriptObject::InvokeAsyncResultCallback( plugin_task_runner_->PostTask( FROM_HERE, base::Bind( &HostNPScriptObject::InvokeAsyncResultCallback, - base::Unretained(this), callback, result)); + weak_ptr_, callback, result)); return; } @@ -1244,7 +1416,7 @@ void HostNPScriptObject::InvokeGetDaemonConfigCallback( plugin_task_runner_->PostTask( FROM_HERE, base::Bind( &HostNPScriptObject::InvokeGetDaemonConfigCallback, - base::Unretained(this), callback, base::Passed(&config))); + weak_ptr_, callback, base::Passed(&config))); return; } @@ -1265,7 +1437,7 @@ void HostNPScriptObject::InvokeGetDaemonVersionCallback( plugin_task_runner_->PostTask( FROM_HERE, base::Bind( &HostNPScriptObject::InvokeGetDaemonVersionCallback, - base::Unretained(this), callback, version)); + weak_ptr_, callback, version)); return; } @@ -1283,7 +1455,7 @@ void HostNPScriptObject::InvokeGetUsageStatsConsentCallback( plugin_task_runner_->PostTask( FROM_HERE, base::Bind( &HostNPScriptObject::InvokeGetUsageStatsConsentCallback, - base::Unretained(this), callback, supported, allowed, + weak_ptr_, callback, supported, allowed, set_by_policy)); return; } @@ -1300,6 +1472,7 @@ void HostNPScriptObject::InvokeGetUsageStatsConsentCallback( void HostNPScriptObject::LogDebugInfo(const std::string& message) { DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + if (log_debug_info_func_.get()) { am_currently_logging_ = true; NPVariant log_message; @@ -1317,16 +1490,19 @@ bool HostNPScriptObject::InvokeAndIgnoreResult(NPObject* func, const NPVariant* args, uint32_t arg_count) { DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + NPVariant np_result; bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func, args, arg_count, &np_result); if (is_good) g_npnetscape_funcs->releasevariantvalue(&np_result); + return is_good; } void HostNPScriptObject::SetException(const std::string& exception_string) { DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + g_npnetscape_funcs->setexception(parent_, exception_string.c_str()); LOG(INFO) << exception_string; } diff --git a/remoting/host/plugin/host_script_object.h b/remoting/host/plugin/host_script_object.h index cfcb990..5634bab 100644 --- a/remoting/host/plugin/host_script_object.h +++ b/remoting/host/plugin/host_script_object.h @@ -11,6 +11,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/synchronization/cancellation_flag.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" @@ -21,7 +22,6 @@ #include "remoting/base/plugin_thread_task_runner.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/host_key_pair.h" -#include "remoting/host/host_status_observer.h" #include "remoting/host/log_to_server.h" #include "remoting/host/plugin/host_plugin_utils.h" #include "remoting/host/setup/daemon_controller.h" @@ -32,31 +32,17 @@ namespace remoting { -class ChromotingHost; -class DesktopEnvironmentFactory; -class HostEventLogger; -class It2MeHostUserInterface; -class MutableHostConfig; -class RegisterSupportHostRequest; -class SignalStrategy; -class SupportAccessVerifier; - -namespace policy_hack { -class PolicyWatcher; -} // namespace policy_hack - // 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 synchronously shuts down the host and all threads. -class HostNPScriptObject : public HostStatusObserver { +class HostNPScriptObject { public: HostNPScriptObject(NPP plugin, NPObject* parent, PluginThreadTaskRunner::Delegate* plugin_thread_delegate); virtual ~HostNPScriptObject(); - bool Init(); - + // Implementations used to implement the NPObject interface. bool HasMethod(const std::string& method_name); bool InvokeDefault(const NPVariant* args, uint32_t arg_count, @@ -71,12 +57,6 @@ class HostNPScriptObject : public HostStatusObserver { bool RemoveProperty(const std::string& property_name); bool Enumerate(std::vector<std::string>* values); - // remoting::HostStatusObserver implementation. - virtual void OnAccessDenied(const std::string& jid) OVERRIDE; - virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE; - virtual void OnClientDisconnected(const std::string& jid) OVERRIDE; - virtual void OnShutdown() OVERRIDE; - // Post LogDebugInfo to the correct proxy (and thus, on the correct thread). // This should only be called by HostLogHandler. To log to the UI, use the // standard LOG(INFO) and it will be sent to this method. @@ -85,6 +65,11 @@ class HostNPScriptObject : public HostStatusObserver { void SetWindow(NPWindow* np_window); private: + ////////////////////////////////////////////////////////// + // Definitions for It2Me host. + + class It2MeImpl; + // These state values are duplicated in host_session.js. Remember to update // both copies when making changes. enum State { @@ -181,42 +166,23 @@ class HostNPScriptObject : public HostStatusObserver { bool StopDaemon(const NPVariant* args, uint32_t arg_count, NPVariant* result); ////////////////////////////////////////////////////////// - // Helper methods for It2Me host. - - // Updates state of the host. Can be called only on the main thread. - void SetState(State state); + // Helper methods used by the It2Me host implementation. // Notifies OnStateChanged handler of a state change. void NotifyStateChanged(State state); - // Callbacks invoked during session setup. - void OnReceivedSupportID(bool success, - const std::string& support_id, - const base::TimeDelta& lifetime); - - // Helper functions that run on main thread. Can be called on any - // other thread. - void ReadPolicyAndConnect(const std::string& uid, - const std::string& auth_token, - const std::string& auth_service); - void FinishConnect(const std::string& uid, - const std::string& auth_token, - const std::string& auth_service); - - void DisconnectInternal(); - - // Callback for ChromotingHost::Shutdown(). - void OnShutdownFinished(); - - // Called when a policy is updated. - void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); + // If the web-app has registered a callback to be notified of changes to the + // NAT traversal policy, notify it. + void NotifyNatPolicyChanged(bool nat_traversal_enabled); - // Called when the nat traversal policy is updated. - void UpdateNatPolicy(bool nat_traversal_enabled); + // Stores the Access Code for the web-app to query. + void StoreAccessCode(const std::string& access_code, + base::TimeDelta access_code_lifetime); - // Called when the host domain policy is updated. - void UpdateHostDomainPolicy(const std::string& host_domain); + // Stores the client user's name for the web-app to query. + void StoreClientUsername(const std::string& client_username); + // Used to generate localized strings to pass to the It2Me host core. void LocalizeStrings(NPObject* localize_func); // Helper function for executing InvokeDefault on an NPObject that performs @@ -235,10 +201,6 @@ class HostNPScriptObject : public HostStatusObserver { const char* substitution, string16* result); - // If the web-app has registered a callback to be notified of changes to the - // NAT traversal policy, notify it. - void UpdateWebappNatPolicy(bool nat_traversal_enabled); - ////////////////////////////////////////////////////////// // Helper methods for Me2Me host. @@ -287,64 +249,40 @@ class HostNPScriptObject : public HostStatusObserver { ////////////////////////////////////////////////////////// // Plugin state variables shared between It2Me and Me2Me. - // True if we're in the middle of handling a log message. NPP plugin_; NPObject* parent_; + scoped_refptr<PluginThreadTaskRunner> plugin_task_runner_; + + scoped_refptr<AutoThreadTaskRunner> auto_plugin_task_runner_; + + // True if we're in the middle of handling a log message. bool am_currently_logging_; + ScopedRefNPObject log_debug_info_func_; + ////////////////////////////////////////////////////////// // It2Me host state. - State state_; - base::Lock access_code_lock_; + // Internal implementation of the It2Me host function. + scoped_refptr<It2MeImpl> it2me_impl_; + + // Cached, read-only copies of |it2me_impl_| session state. + State state_; std::string access_code_; base::TimeDelta access_code_lifetime_; - std::string client_username_; - ScopedRefNPObject log_debug_info_func_; - ScopedRefNPObject on_nat_traversal_policy_changed_func_; - ScopedRefNPObject on_state_changed_func_; - base::PlatformThreadId np_thread_id_; - scoped_refptr<PluginThreadTaskRunner> plugin_task_runner_; - - scoped_ptr<ChromotingHostContext> host_context_; - HostKeyPair host_key_pair_; - scoped_ptr<SignalStrategy> signal_strategy_; - scoped_ptr<RegisterSupportHostRequest> register_request_; - scoped_ptr<LogToServer> log_to_server_; - scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; - scoped_ptr<It2MeHostUserInterface> it2me_host_user_interface_; - scoped_ptr<HostEventLogger> host_event_logger_; - - scoped_refptr<ChromotingHost> host_; - int failed_login_attempts_; + // Localized strings for use by the |it2me_impl_| UI. UiStrings ui_strings_; - base::Lock ui_strings_lock_; - base::Lock nat_policy_lock_; - - scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_; - - // Host the current nat traversal policy setting. - bool nat_traversal_enabled_; - - // The host domain policy setting. - std::string required_host_domain_; - - // Indicates whether or not a policy has ever been read. This is to ensure - // that on startup, we do not accidentally start a connection before we have - // queried our policy restrictions. - bool policy_received_; - - // On startup, it is possible to have Connect() called before the policy read - // is completed. Rather than just failing, we thunk the connection call so - // it can be executed after at least one successful policy read. This - // variable contains the thunk if it is necessary. - base::Closure pending_connect_; + // Callbacks to notify in response to |it2me_impl_| events. + ScopedRefNPObject on_nat_traversal_policy_changed_func_; + ScopedRefNPObject on_state_changed_func_; ////////////////////////////////////////////////////////// // Me2Me host state. + + // Platform-specific installation & configuration implementation. scoped_ptr<DaemonController> daemon_controller_; // TODO(sergeyu): Replace this thread with @@ -352,6 +290,13 @@ class HostNPScriptObject : public HostStatusObserver { // on MessageLoopProxy::current(). base::Thread worker_thread_; + ////////////////////////////////////////////////////////// + // Plugin state used for both Ir2Me and Me2Me. + + // Used to cancel pending tasks for this object when it is destroyed. + base::WeakPtrFactory<HostNPScriptObject> weak_factory_; + base::WeakPtr<HostNPScriptObject> weak_ptr_; + DISALLOW_COPY_AND_ASSIGN(HostNPScriptObject); }; |