diff options
author | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-17 17:06:32 +0000 |
---|---|---|
committer | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-17 17:06:32 +0000 |
commit | fc232b598c601e066f6a17b5c647b1e30667f38e (patch) | |
tree | bc7bb8d459caaed60ddc882d9aa66caa4bedacd0 /remoting | |
parent | c1e0696a17d2683b191ebc9b3b5233260c12c3db (diff) | |
download | chromium_src-fc232b598c601e066f6a17b5c647b1e30667f38e.zip chromium_src-fc232b598c601e066f6a17b5c647b1e30667f38e.tar.gz chromium_src-fc232b598c601e066f6a17b5c647b1e30667f38e.tar.bz2 |
Hookup the ChromotingPlugin into the refactored implementation
of HostConnection and ChromotingView. Also implement a bare-bones
PepperView.
No unittests yet. Still too in flux to be worth it.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/2852003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50116 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/client/chromoting_client.cc | 170 | ||||
-rw-r--r-- | remoting/client/chromoting_client.h | 78 | ||||
-rw-r--r-- | remoting/client/chromoting_view.h | 21 | ||||
-rw-r--r-- | remoting/client/host_connection.h | 3 | ||||
-rw-r--r-- | remoting/client/jingle_host_connection.cc | 20 | ||||
-rw-r--r-- | remoting/client/jingle_host_connection.h | 20 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_main.cc | 2 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_plugin.cc | 116 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_plugin.h | 69 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_plugin_unittest.cc | 51 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view.cc | 154 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view.h | 77 | ||||
-rw-r--r-- | remoting/client/simple_client.cc | 7 | ||||
-rw-r--r-- | remoting/client/x11_client.cc | 23 | ||||
-rw-r--r-- | remoting/client/x11_view.cc | 22 | ||||
-rw-r--r-- | remoting/client/x11_view.h | 6 | ||||
-rw-r--r-- | remoting/remoting.gyp | 59 | ||||
-rw-r--r-- | remoting/tools/client_webserver/main.c | 5 |
18 files changed, 785 insertions, 118 deletions
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc new file mode 100644 index 0000000..cad1993 --- /dev/null +++ b/remoting/client/chromoting_client.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2010 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/client/chromoting_client.h" + +#include "base/message_loop.h" +#include "remoting/client/chromoting_view.h" +#include "remoting/client/host_connection.h" + +static const uint32 kCreatedColor = 0xff0000ff; +static const uint32 kDisconnectedColor = 0xff00ff00; +static const uint32 kFailedColor = 0xffff0000; + +namespace remoting { + +ChromotingClient::ChromotingClient(MessageLoop* message_loop, + HostConnection* connection, + ChromotingView* view) + : message_loop_(message_loop), + state_(CREATED), + host_connection_(connection), + view_(view) { +} + +ChromotingClient::~ChromotingClient() { +} + +void ChromotingClient::Repaint() { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::DoRepaint)); +} + +void ChromotingClient::SetViewport(int x, int y, int width, int height) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::DoSetViewport, + x, y, width, height)); +} + +void ChromotingClient::HandleMessages(HostConnection* conn, + HostMessageList* messages) { + for (size_t i = 0; i < messages->size(); ++i) { + HostMessage* msg = (*messages)[i]; + // TODO(ajwong): Consider creating a macro similar to the IPC message + // mappings. Also reconsider the lifetime of the message object. + if (msg->has_init_client()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::DoInitClient, msg)); + } else if (msg->has_begin_update_stream()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::DoBeginUpdate, msg)); + } else if (msg->has_update_stream_packet()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::DoHandleUpdate, msg)); + } else if (msg->has_end_update_stream()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::DoEndUpdate, msg)); + } else { + NOTREACHED() << "Unknown message received"; + } + } + // Assume we have processed all the messages. + messages->clear(); +} + +void ChromotingClient::OnConnectionOpened(HostConnection* conn) { + LOG(INFO) << "ChromotingClient::OnConnectionOpened"; + SetState(CONNECTED); +} + +void ChromotingClient::OnConnectionClosed(HostConnection* conn) { + LOG(INFO) << "ChromotingClient::OnConnectionClosed"; + SetState(DISCONNECTED); +} + +void ChromotingClient::OnConnectionFailed(HostConnection* conn) { + LOG(INFO) << "ChromotingClient::OnConnectionFailed"; + SetState(FAILED); +} + +MessageLoop* ChromotingClient::message_loop() { + return message_loop_; +} + +void ChromotingClient::SetState(State s) { + // TODO(ajwong): We actually may want state to be a shared variable. Think + // through later. + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::DoSetState, s)); +} + +void ChromotingClient::DoSetState(State s) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + + state_ = s; + switch (state_) { + case CREATED: + view_->SetSolidFill(kCreatedColor); + break; + + case CONNECTED: + view_->UnsetSolidFill(); + break; + + case DISCONNECTED: + view_->SetSolidFill(kDisconnectedColor); + break; + + case FAILED: + view_->SetSolidFill(kFailedColor); + break; + } + + DoRepaint(); +} + +void ChromotingClient::DoRepaint() { + DCHECK_EQ(message_loop(), MessageLoop::current()); + + view_->Paint(); +} + +void ChromotingClient::DoSetViewport(int x, int y, int width, int height) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + + view_->SetViewport(x, y, width, height); +} + +void ChromotingClient::DoInitClient(HostMessage* msg) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + DCHECK(msg->has_init_client()); + scoped_ptr<HostMessage> deleter(msg); + + // Saves the dimension and resize the window. + int width = msg->init_client().width(); + int height = msg->init_client().height(); + LOG(INFO) << "Init client received geometry: " << width << "x" << height; + + view_->SetBackingStoreSize(width, height); +} + +void ChromotingClient::DoBeginUpdate(HostMessage* msg) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + DCHECK(msg->has_begin_update_stream()); + + view_->HandleBeginUpdateStream(msg); +} + +void ChromotingClient::DoHandleUpdate(HostMessage* msg) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + DCHECK(msg->has_update_stream_packet()); + + view_->HandleUpdateStreamPacket(msg); +} + +void ChromotingClient::DoEndUpdate(HostMessage* msg) { + DCHECK_EQ(message_loop(), MessageLoop::current()); + DCHECK(msg->has_update_stream_packet()); + + view_->HandleUpdateStreamPacket(msg); +} + +} // namespace remoting diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h new file mode 100644 index 0000000..587f1fb --- /dev/null +++ b/remoting/client/chromoting_client.h @@ -0,0 +1,78 @@ +// Copyright (c) 2010 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. + +// ChromotingClient is the controller for the Client implementation. + +#ifndef REMOTING_CLIENT_CHROMOTING_CLIENT_H +#define REMOTING_CLIENT_CHROMOTING_CLIENT_H + +#include "base/task.h" +#include "remoting/client/host_connection.h" + +class MessageLoop; + +namespace remoting { + +class ChromotingView; + +class ChromotingClient : public HostConnection::HostEventCallback { + public: + ChromotingClient(MessageLoop* message_loop, + HostConnection* connection, + ChromotingView* view); + virtual ~ChromotingClient(); + + // Signals that the associated view may need updating. + virtual void Repaint(); + + // Sets the viewport to do display. The viewport may be larger and/or + // smaller than the actual image background being displayed. + virtual void SetViewport(int x, int y, int width, int height); + + // HostConnection::HostEventCallback implementation. + virtual void HandleMessages(HostConnection* conn, HostMessageList* messages); + virtual void OnConnectionOpened(HostConnection* conn); + virtual void OnConnectionClosed(HostConnection* conn); + virtual void OnConnectionFailed(HostConnection* conn); + + private: + enum State { + CREATED, + CONNECTED, + DISCONNECTED, + FAILED, + }; + + MessageLoop* message_loop(); + + // Convenience method for modifying the state on this object's message loop. + void SetState(State s); + + // TODO(ajwong): Do all of these methods need to run on the client's thread? + void DoSetState(State s); + void DoRepaint(); + void DoSetViewport(int x, int y, int width, int height); + + // Handles for chromotocol messages. + void DoInitClient(HostMessage* msg); + void DoBeginUpdate(HostMessage* msg); + void DoHandleUpdate(HostMessage* msg); + void DoEndUpdate(HostMessage* msg); + + MessageLoop* message_loop_; + + State state_; + + // Connection to views and hosts. Not owned. + HostConnection* host_connection_; + ChromotingView* view_; + + DISALLOW_COPY_AND_ASSIGN(ChromotingClient); +}; + +} // namespace remoting + +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::ChromotingClient); + +#endif // REMOTING_CLIENT_CHROMOTING_CLIENT_H diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h index cf70b99..503a903 100644 --- a/remoting/client/chromoting_view.h +++ b/remoting/client/chromoting_view.h @@ -14,7 +14,7 @@ class HostMessage; // ChromotingView defines the behavior of an object that draws a view of the // remote desktop. Its main function is to choose the right decoder and render // the update stream onto the screen. -class ChromotingView : public base::RefCountedThreadSafe<ChromotingView> { +class ChromotingView { public: virtual ~ChromotingView() {} @@ -22,6 +22,25 @@ class ChromotingView : public base::RefCountedThreadSafe<ChromotingView> { // TODO(hclam): Add rects as parameter if needed. virtual void Paint() = 0; + // Fill the screen with one single static color, and ignore updates. + // Useful for debugging. + virtual void SetSolidFill(uint32 color) = 0; + + // Removes a previously set solid fill. If no fill was previous set, this + // does nothing. + virtual void UnsetSolidFill() = 0; + + // Reposition and resize the viewport into the backing store. If the viewport + // extends past the end of the backing store, it is filled with black. + virtual void SetViewport(int x, int y, int width, int height) = 0; + + // Resize the underlying image that is displayed. This should match the size + // of the output from the decoder. + // + // TODO(ajwong): We need a better name. Look at how Java represents this + // stuff? + virtual void SetBackingStoreSize(int width, int height) = 0; + // Handle the BeginUpdateStream message. virtual void HandleBeginUpdateStream(HostMessage* msg) = 0; diff --git a/remoting/client/host_connection.h b/remoting/client/host_connection.h index adbb876..0667cc9 100644 --- a/remoting/client/host_connection.h +++ b/remoting/client/host_connection.h @@ -39,7 +39,8 @@ class HostConnection { // TODO(ajwong): We need to generalize this API. virtual void Connect(const std::string& username, const std::string& auth_token, - const std::string& host_jid) = 0; + const std::string& host_jid, + HostEventCallback* event_callback) = 0; virtual void Disconnect() = 0; protected: diff --git a/remoting/client/jingle_host_connection.cc b/remoting/client/jingle_host_connection.cc index 7b9241c..9bf547f 100644 --- a/remoting/client/jingle_host_connection.cc +++ b/remoting/client/jingle_host_connection.cc @@ -9,9 +9,9 @@ namespace remoting { -JingleHostConnection::JingleHostConnection(JingleThread* network_thread, - HostEventCallback* event_callback) - : network_thread_(network_thread), event_callback_(event_callback) { +JingleHostConnection::JingleHostConnection(JingleThread* network_thread) + : network_thread_(network_thread), + event_callback_(NULL) { } JingleHostConnection::~JingleHostConnection() { @@ -19,12 +19,13 @@ JingleHostConnection::~JingleHostConnection() { void JingleHostConnection::Connect(const std::string& username, const std::string& password, - const std::string& host_jid) { + const std::string& host_jid, + HostEventCallback* event_callback) { message_loop()->PostTask( FROM_HERE, - NewRunnableMethod(this, - &JingleHostConnection::DoConnect, - username, password, host_jid)); + NewRunnableMethod(this, &JingleHostConnection::DoConnect, + username, password, host_jid, + event_callback)); } void JingleHostConnection::Disconnect() { @@ -109,9 +110,12 @@ MessageLoop* JingleHostConnection::message_loop() { void JingleHostConnection::DoConnect(const std::string& username, const std::string& auth_token, - const std::string& host_jid) { + const std::string& host_jid, + HostEventCallback* event_callback) { DCHECK_EQ(message_loop(), MessageLoop::current()); + event_callback_ = event_callback; + jingle_client_ = new JingleClient(network_thread_); jingle_client_->Init(username, auth_token, kChromotingTokenServiceName, this); jingle_channel_ = jingle_client_->Connect(host_jid, this); diff --git a/remoting/client/jingle_host_connection.h b/remoting/client/jingle_host_connection.h index 00faaa1..96178aa 100644 --- a/remoting/client/jingle_host_connection.h +++ b/remoting/client/jingle_host_connection.h @@ -20,6 +20,7 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "base/task.h" #include "remoting/base/protocol_decoder.h" #include "remoting/client/host_connection.h" #include "remoting/jingle_glue/jingle_channel.h" @@ -31,19 +32,17 @@ namespace remoting { class JingleThread; -class JingleHostConnection : - public base::RefCountedThreadSafe<JingleHostConnection>, - public HostConnection, - public JingleChannel::Callback, - public JingleClient::Callback { +class JingleHostConnection : public HostConnection, + public JingleChannel::Callback, + public JingleClient::Callback { public: - JingleHostConnection(JingleThread* network_thread, - HostEventCallback* event_callback); + explicit JingleHostConnection(JingleThread* network_thread); virtual ~JingleHostConnection(); virtual void Connect(const std::string& username, const std::string& auth_token, - const std::string& host_jid); + const std::string& host_jid, + HostEventCallback* event_callback); virtual void Disconnect(); // JingleChannel::Callback interface. @@ -64,7 +63,8 @@ class JingleHostConnection : void DoConnect(const std::string& username, const std::string& auth_token, - const std::string& host_jid); + const std::string& host_jid, + HostEventCallback* event_callback); void DoDisconnect(); JingleThread* network_thread_; @@ -80,4 +80,6 @@ class JingleHostConnection : } // namespace remoting +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::JingleHostConnection); + #endif // REMOTING_CLIENT_JINGLE_HOST_CONNECTION_H_ diff --git a/remoting/client/plugin/chromoting_main.cc b/remoting/client/plugin/chromoting_main.cc index e2974bd..9d02936 100644 --- a/remoting/client/plugin/chromoting_main.cc +++ b/remoting/client/plugin/chromoting_main.cc @@ -8,7 +8,7 @@ // This information needs to live outside of the PepperPlugin since it can // be requested by the browser before the PepperPlugin has been instantiated. void InitializePluginInfo(pepper::PepperPlugin::Info* plugin_info) { - plugin_info->mime_description = remoting::kMimeType; + plugin_info->mime_description = remoting::ChromotingPlugin::kMimeType; plugin_info->plugin_name = "Chromoting"; plugin_info->plugin_description = "Remote access for Chrome"; } diff --git a/remoting/client/plugin/chromoting_plugin.cc b/remoting/client/plugin/chromoting_plugin.cc index 6ed867a..55c72afe 100644 --- a/remoting/client/plugin/chromoting_plugin.cc +++ b/remoting/client/plugin/chromoting_plugin.cc @@ -4,15 +4,28 @@ #include "remoting/client/plugin/chromoting_plugin.h" -#include "remoting/client/plugin/client.h" +#include <string> +#include <vector> + +#include "base/string_util.h" +#include "base/thread.h" +#include "remoting/client/chromoting_client.h" +#include "remoting/client/host_connection.h" +#include "remoting/client/jingle_host_connection.h" +#include "remoting/client/plugin/pepper_view.h" +#include "remoting/jingle_glue/jingle_thread.h" + +using std::string; +using std::vector; namespace remoting { +const char* ChromotingPlugin::kMimeType = + "pepper-application/x-chromoting-plugin::Chromoting"; + ChromotingPlugin::ChromotingPlugin(NPNetscapeFuncs* browser_funcs, NPP instance) : PepperPlugin(browser_funcs, instance) { - device_ = extensions()->acquireDevice(instance, NPPepper2DDevice); - client_ = new ChromotingClient(this); } ChromotingPlugin::~ChromotingPlugin() { @@ -20,6 +33,8 @@ ChromotingPlugin::~ChromotingPlugin() { NPError ChromotingPlugin::New(NPMIMEType pluginType, int16 argc, char* argn[], char* argv[]) { + LOG(INFO) << "Started ChromotingPlugin::New"; + // Verify the mime type and subtype std::string mime(kMimeType); std::string::size_type type_end = mime.find("/"); @@ -41,30 +56,60 @@ NPError ChromotingPlugin::New(NPMIMEType pluginType, return NPERR_GENERIC_ERROR; } - return NPERR_NO_ERROR; -} + string user_id; + string auth_token; + string host_jid; + if (!ParseUrl(url, &user_id, &auth_token, &host_jid)) { + LOG(WARNING) << "Could not parse URL: " << url; + return NPERR_GENERIC_ERROR; + } + + // Setup pepper context. + device_ = extensions()->acquireDevice(instance(), NPPepper2DDevice); + + // Start the threads. + main_thread_.reset(new base::Thread("ChromoClientMain")); + if (!main_thread_->Start()) { + LOG(ERROR) << "Main thread failed to start."; + return NPERR_GENERIC_ERROR; + } + network_thread_.reset(new JingleThread()); + network_thread_->Start(); + + // Create the chromting objects. + host_connection_.reset(new JingleHostConnection(network_thread_.get())); + view_.reset(new PepperView(main_thread_->message_loop(), device_, + instance())); + client_.reset(new ChromotingClient(main_thread_->message_loop(), + host_connection_.get(), view_.get())); + + // Kick off the connection. + host_connection_->Connect(user_id, auth_token, host_jid, client_.get()); -NPError ChromotingPlugin::Destroy(NPSavedData** save) { return NPERR_NO_ERROR; } -void ChromotingPlugin::draw() { - NPDeviceContext2D context; - NPDeviceContext2DConfig config; - device_->initializeContext(instance(), &config, &context); +NPError ChromotingPlugin::Destroy(NPSavedData** save) { + host_connection_->Disconnect(); - client_->draw(width_, height_, &context); + // TODO(ajwong): We need to ensure all objects have actually stopped posting + // to the message loop before this point. Right now, we don't have a well + // defined stop for the plugin process, and the thread shutdown is likely a + // race condition. + network_thread_->Stop(); + main_thread_->Stop(); - device_->flushContext(instance(), &context, NULL, NULL); + main_thread_.reset(); + network_thread_.reset(); + return NPERR_NO_ERROR; } NPError ChromotingPlugin::SetWindow(NPWindow* window) { width_ = window->width; height_ = window->height; - client_->set_window(); - - draw(); + client_->SetViewport(0, 0, window->width, window->height); + client_->Repaint(); return NPERR_NO_ERROR; } @@ -82,7 +127,7 @@ int16 ChromotingPlugin::HandleEvent(void* event) { case NPEventType_MouseEnter: // Fall through case NPEventType_MouseLeave: - client_->handle_mouse_event(npevent); + //client_->handle_mouse_event(npevent); break; case NPEventType_MouseWheel: case NPEventType_RawKeyDown: @@ -91,7 +136,7 @@ int16 ChromotingPlugin::HandleEvent(void* event) { case NPEventType_KeyUp: break; case NPEventType_Char: - client_->handle_char_event(npevent); + //client_->handle_char_event(npevent); break; case NPEventType_Minimize: case NPEventType_Focus: @@ -110,4 +155,41 @@ NPError ChromotingPlugin::SetValue(NPNVariable variable, void* value) { return NPERR_NO_ERROR; } +bool ChromotingPlugin::ParseUrl(const std::string& url, + string* user_id, + string* auth_token, + string* host_jid) { + // TODO(ajwong): We should use GURL or something. Don't parse this by hand! + + // The Url should be of the form: + // + // chromotocol://<hostid>?user=<userid>&auth=<authtoken>&jid=<hostjid> + // + vector<string> parts; + SplitString(url, '&', &parts); + if (parts.size() != 3) { + return false; + } + + size_t pos = parts[0].rfind('='); + if (pos == string::npos && (pos + 1) != string::npos) { + return false; + } + user_id->assign(parts[0].substr(pos + 1)); + + pos = parts[1].rfind('='); + if (pos == string::npos && (pos + 1) != string::npos) { + return false; + } + auth_token->assign(parts[1].substr(pos + 1)); + + pos = parts[2].rfind('='); + if (pos == string::npos && (pos + 1) != string::npos) { + return false; + } + host_jid->assign(parts[2].substr(pos + 1)); + + return true; +} + } // namespace remoting diff --git a/remoting/client/plugin/chromoting_plugin.h b/remoting/client/plugin/chromoting_plugin.h index e2bc78f..16e6f1d 100644 --- a/remoting/client/plugin/chromoting_plugin.h +++ b/remoting/client/plugin/chromoting_plugin.h @@ -7,43 +7,80 @@ #include <string> +#include "base/at_exit.h" +#include "base/scoped_ptr.h" +#include "remoting/client/host_connection.h" #include "remoting/client/pepper/pepper_plugin.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +namespace base { +class Thread; +} // namespace base namespace remoting { -static const char kMimeType[] - = "pepper-application/x-chromoting-plugin::Chromoting"; +class ChromotingClient; +class HostConnection; +class JingleThread; +class PepperView; class ChromotingClient; class ChromotingPlugin : public pepper::PepperPlugin { public: + // The mimetype for which this plugin is registered. + // + // TODO(ajwong): Mimetype doesn't really make sense for us as the trigger + // point. I think we should handle a special protocol (eg., chromotocol://) + static const char *kMimeType; + ChromotingPlugin(NPNetscapeFuncs* browser_funcs, NPP instance); virtual ~ChromotingPlugin(); - int width() { return width_; } - int height() { return height_; } - NPDevice* device() { return device_; } + // PepperPlugin implementation. + virtual NPError New(NPMIMEType pluginType, int16 argc, char* argn[], + char* argv[]); + virtual NPError Destroy(NPSavedData** save); + virtual NPError SetWindow(NPWindow* window); + virtual int16 HandleEvent(void* event); + virtual NPError GetValue(NPPVariable variable, void* value); + virtual NPError SetValue(NPNVariable variable, void* value); - NPError New(NPMIMEType pluginType, int16 argc, char* argn[], char* argv[]); - NPError Destroy(NPSavedData** save); - NPError SetWindow(NPWindow* window); - int16 HandleEvent(void* event); - NPError GetValue(NPPVariable variable, void* value); - NPError SetValue(NPNVariable variable, void* value); + private: + FRIEND_TEST(ChromotingPluginTest, ParseUrl); + FRIEND_TEST(ChromotingPluginTest, TestCaseSetup); + + NPDevice* device() { return device_; } - // Set up drawing context and update display. - void draw(); + static bool ParseUrl(const std::string& url, + std::string* user_id, + std::string* auth_token, + std::string* host_jid); - private: // Size of the plugin window. int width_, height_; // Rendering device provided by browser. NPDevice* device_; - // Chromoting client manager. - ChromotingClient* client_; + // Since the ChromotingPlugin's lifetime is conceptually the lifetime of the + // object, use it to control running of atexit() calls. + // + // TODO(ajwong): Should this be moved into PepperPlugin? Or maybe even + // higher? + // + // TODO(ajwong): This causes unittests to fail so commenting out for now. We + // need to understand process instantiation better. This may also be a + // non-issue when we are no longer being loaded as a DSO. + // + // base::AtExitManager at_exit_manager_; + + scoped_ptr<base::Thread> main_thread_; + scoped_ptr<JingleThread> network_thread_; + + scoped_ptr<HostConnection> host_connection_; + scoped_ptr<PepperView> view_; + scoped_ptr<ChromotingClient> client_; DISALLOW_COPY_AND_ASSIGN(ChromotingPlugin); }; diff --git a/remoting/client/plugin/chromoting_plugin_unittest.cc b/remoting/client/plugin/chromoting_plugin_unittest.cc index 12f9193..fedf252 100644 --- a/remoting/client/plugin/chromoting_plugin_unittest.cc +++ b/remoting/client/plugin/chromoting_plugin_unittest.cc @@ -13,6 +13,7 @@ pepper::PepperPlugin* CreatePlugin(NPNetscapeFuncs* browser_funcs, NPP instance); +namespace remoting { class ChromotingPluginTest : public testing::Test { protected: @@ -24,10 +25,10 @@ class ChromotingPluginTest : public testing::Test { instance_.reset(new NPP_t()); // Create the ChromotingPlugin for testing. - pepper::PepperPlugin* pepper_plugin; - pepper_plugin = CreatePlugin(browser_funcs_, instance_.get()); + pepper::PepperPlugin* pepper_plugin = + CreatePlugin(browser_funcs_, instance_.get()); plugin_.reset( - reinterpret_cast<remoting::ChromotingPlugin*>(pepper_plugin)); + static_cast<ChromotingPlugin*>(pepper_plugin)); } virtual void TearDown() { @@ -35,48 +36,44 @@ class ChromotingPluginTest : public testing::Test { FakeBrowser* fake_browser_; scoped_ptr<NPP_t> instance_; - scoped_ptr<remoting::ChromotingPlugin> plugin_; + scoped_ptr<ChromotingPlugin> plugin_; }; -TEST_F(ChromotingPluginTest, TestSetup) { +TEST_F(ChromotingPluginTest, TestCaseSetup) { ASSERT_TRUE(plugin_->browser() != NULL); ASSERT_TRUE(plugin_->extensions() != NULL); ASSERT_TRUE(plugin_->instance() != NULL); - ASSERT_TRUE(plugin_->device() != NULL); + // Device is not set until New() is called. + ASSERT_TRUE(plugin_->device() == NULL); } +#if 0 +TODO(ajwong): reenable once we have the threading sorted out. TEST_F(ChromotingPluginTest, TestNew) { NPMIMEType mimetype = const_cast<NPMIMEType>("pepper-application/x-chromoting-plugin"); int16 argc; char* argn[4]; char* argv[4]; - NPError result; - - // Test 0 arguments (NULL arrays). - argc = 0; - result = plugin_->New(mimetype, argc, NULL, NULL); - ASSERT_EQ(NPERR_GENERIC_ERROR, result); // Test 0 arguments. argc = 0; - result = plugin_->New(mimetype, argc, argn, argv); - ASSERT_EQ(NPERR_GENERIC_ERROR, result); + ASSERT_EQ(NPERR_GENERIC_ERROR, plugin_->New(mimetype, argc, argn, argv)); // Test 1 argument (missing "src"). argc = 1; argn[0] = const_cast<char*>("noturl"); argv[0] = const_cast<char*>("random.value"); - result = plugin_->New(mimetype, argc, argn, argv); - ASSERT_EQ(NPERR_GENERIC_ERROR, result); + ASSERT_EQ(NPERR_GENERIC_ERROR, plugin_->New(mimetype, argc, argn, argv)); // Test "src" argument. argc = 1; argn[0] = const_cast<char*>("src"); - argv[0] = const_cast<char*>("chromotocol:name@chromoting.org"); - result = plugin_->New(mimetype, argc, argn, argv); - ASSERT_EQ(NPERR_NO_ERROR, result); + argv[0] = const_cast<char*>("chromotocol://name?user=u&auth=a&jid=j"); + ASSERT_EQ(NPERR_NO_ERROR, plugin_->New(mimetype, argc, argn, argv)); + + ASSERT_EQ(NPERR_NO_ERROR, plugin_->Destroy(NULL)); } TEST_F(ChromotingPluginTest, TestSetWindow) { @@ -86,3 +83,19 @@ TEST_F(ChromotingPluginTest, TestSetWindow) { result = plugin_->SetWindow(window); ASSERT_EQ(NPERR_NO_ERROR, result); } +#endif + +TEST_F(ChromotingPluginTest, ParseUrl) { + const char url[] = "chromotocol://hostid?user=auser&auth=someauth&jid=ajid"; + std::string user_id; + std::string auth_token; + std::string host_jid; + ASSERT_TRUE( + ChromotingPlugin::ParseUrl(url, &user_id, &auth_token, &host_jid)); + + EXPECT_EQ("auser", user_id); + EXPECT_EQ("someauth", auth_token); + EXPECT_EQ("ajid", host_jid); +} + +} // namespace remoting diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc new file mode 100644 index 0000000..d66624b5 --- /dev/null +++ b/remoting/client/plugin/pepper_view.cc @@ -0,0 +1,154 @@ +// Copyright (c) 2010 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/client/plugin/pepper_view.h" + +#include "base/message_loop.h" +#include "remoting/client/decoder_verbatim.h" + +namespace remoting { + +PepperView::PepperView(MessageLoop* message_loop, NPDevice* rendering_device, + NPP plugin_instance) + : message_loop_(message_loop), + rendering_device_(rendering_device), + plugin_instance_(plugin_instance), + backing_store_width_(0), + backing_store_height_(0), + viewport_x_(0), + viewport_y_(0), + viewport_width_(0), + viewport_height_(0), + is_static_fill_(false), + static_fill_color_(0) { +} + +PepperView::~PepperView() { +} + +void PepperView::Paint() { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoPaint)); +} + +void PepperView::SetSolidFill(uint32 color) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoSetSolidFill, color)); +} + +void PepperView::UnsetSolidFill() { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoUnsetSolidFill)); +} + +void PepperView::SetViewport(int x, int y, int width, int height) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoSetViewport, + x, y, width, height)); +} + +void PepperView::SetBackingStoreSize(int width, int height) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoSetBackingStoreSize, + width, height)); +} + +void PepperView::HandleBeginUpdateStream(HostMessage* msg) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoHandleBeginUpdateStream, msg)); +} + +void PepperView::HandleUpdateStreamPacket(HostMessage* msg) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoHandleUpdateStreamPacket, msg)); +} + +void PepperView::HandleEndUpdateStream(HostMessage* msg) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &PepperView::DoHandleEndUpdateStream, msg)); +} + +void PepperView::DoPaint() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + LOG(INFO) << "Starting PepperView::DoPaint"; + + NPDeviceContext2D context; + NPDeviceContext2DConfig config; + rendering_device_->initializeContext(plugin_instance_, &config, &context); + + uint32* output_bitmap = static_cast<uint32*>(context.region); + + // TODO(ajwong): Remove debugging code and actually hook up real painting + // logic from the decoder. + LOG(INFO) << "Painting top: " << context.dirty.top + << " bottom: " << context.dirty.bottom + << " left: " << context.dirty.left + << " right: " << context.dirty.right; + for (int i = context.dirty.top; i < context.dirty.bottom; ++i) { + for (int j = context.dirty.left; j < context.dirty.right; ++j) { + *output_bitmap++ = static_fill_color_; + } + } + + rendering_device_->flushContext(plugin_instance_, &context, NULL, NULL); + LOG(INFO) << "Finishing PepperView::DoPaint"; +} + +void PepperView::DoSetSolidFill(uint32 color) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + is_static_fill_ = true; + static_fill_color_ = color; +} + +void PepperView::DoUnsetSolidFill() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + is_static_fill_ = false; +} + +void PepperView::DoSetViewport(int x, int y, int width, int height) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + viewport_x_ = x; + viewport_y_ = y; + viewport_width_ = width; + viewport_height_ = height; +} + +void PepperView::DoSetBackingStoreSize(int width, int height) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + backing_store_width_ = width; + backing_store_height_ = height; +} + +void PepperView::DoHandleBeginUpdateStream(HostMessage* msg) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + NOTIMPLEMENTED(); +} + +void PepperView::DoHandleUpdateStreamPacket(HostMessage* msg) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + NOTIMPLEMENTED(); +} + +void PepperView::DoHandleEndUpdateStream(HostMessage* msg) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + NOTIMPLEMENTED(); +} + +} // namespace remoting diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h new file mode 100644 index 0000000..286a15a --- /dev/null +++ b/remoting/client/plugin/pepper_view.h @@ -0,0 +1,77 @@ +// Copyright (c) 2010 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_CLIENT_PLUGIN_PEPPER_VIEW_H_ +#define REMOTING_CLIENT_PLUGIN_PEPPER_VIEW_H_ + +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "remoting/client/chromoting_view.h" +#include "remoting/client/pepper/pepper_plugin.h" + +class MessageLoop; + +namespace remoting { + +class Decoder; + +class PepperView : public ChromotingView { + public: + // Constructs a PepperView that draw to the |rendering_device|. The + // |rendering_device| instance must outlive this class. + // + // TODO(ajwong): This probably needs to synchronize with the pepper thread + // to be safe. + PepperView(MessageLoop* message_loop, NPDevice* rendering_device, + NPP plugin_instance); + virtual ~PepperView(); + + // ChromotingView implementation. + virtual void Paint(); + virtual void SetSolidFill(uint32 color); + virtual void UnsetSolidFill(); + virtual void SetViewport(int x, int y, int width, int height); + virtual void SetBackingStoreSize(int width, int height); + virtual void HandleBeginUpdateStream(HostMessage* msg); + virtual void HandleUpdateStreamPacket(HostMessage* msg); + virtual void HandleEndUpdateStream(HostMessage* msg); + + private: + void DoPaint(); + void DoSetSolidFill(uint32 color); + void DoUnsetSolidFill(); + void DoSetViewport(int x, int y, int width, int height); + void DoSetBackingStoreSize(int width, int height); + void DoHandleBeginUpdateStream(HostMessage* msg); + void DoHandleUpdateStreamPacket(HostMessage* msg); + void DoHandleEndUpdateStream(HostMessage* msg); + + // Synchronization and thread handling objects. + MessageLoop* message_loop_; + + // Handles to Pepper objects needed for drawing to the screen. + NPDevice* rendering_device_; + NPP plugin_instance_; + + int backing_store_width_; + int backing_store_height_; + + int viewport_x_; + int viewport_y_; + int viewport_width_; + int viewport_height_; + + bool is_static_fill_; + uint32 static_fill_color_; + + scoped_ptr<Decoder> decoder_; + + DISALLOW_COPY_AND_ASSIGN(PepperView); +}; + +} // namespace remoting + +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::PepperView); + +#endif // REMOTING_CLIENT_PLUGIN_PEPPER_VIEW_H_ diff --git a/remoting/client/simple_client.cc b/remoting/client/simple_client.cc index b2e9ce8..98074bf 100644 --- a/remoting/client/simple_client.cc +++ b/remoting/client/simple_client.cc @@ -129,14 +129,13 @@ int main(int argc, char** argv) { base::WaitableEvent client_done(false, false); SimpleHostEventCallback handler(network_thread.message_loop(), &client_done); - scoped_refptr<JingleHostConnection> connection = - new JingleHostConnection(&network_thread, &handler); - connection->Connect(username, auth_token, host_jid); + JingleHostConnection connection(&network_thread); + connection.Connect(username, auth_token, host_jid, &handler); // Wait until the mainloop has been signaled to exit. client_done.Wait(); - connection->Disconnect(); + connection.Disconnect(); network_thread.message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); network_thread.Stop(); diff --git a/remoting/client/x11_client.cc b/remoting/client/x11_client.cc index 11b0ebf..4b111e9 100644 --- a/remoting/client/x11_client.cc +++ b/remoting/client/x11_client.cc @@ -28,8 +28,7 @@ using remoting::JingleThread; namespace remoting { -class X11Client : public base::RefCountedThreadSafe<X11Client>, - public HostConnection::HostEventCallback { +class X11Client : public HostConnection::HostEventCallback { public: X11Client(MessageLoop* loop, base::WaitableEvent* client_done) : message_loop_(loop), @@ -145,7 +144,7 @@ class X11Client : public base::RefCountedThreadSafe<X11Client>, XResizeWindow(display_, window_, width_, height_); // Now construct the X11View that renders the remote desktop. - view_ = new X11View(display_, window_, width_, height_); + view_.reset(new X11View(display_, window_, width_, height_)); // Schedule the event handler to process the event queue of X11. ScheduleX11EventHandler(); @@ -215,13 +214,15 @@ class X11Client : public base::RefCountedThreadSafe<X11Client>, int width_; int height_; - scoped_refptr<ChromotingView> view_; + scoped_ptr<ChromotingView> view_; DISALLOW_COPY_AND_ASSIGN(X11Client); }; } // namespace remoting +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::X11Client); + int main(int argc, char** argv) { base::AtExitManager at_exit; std::string host_jid; @@ -237,20 +238,18 @@ int main(int argc, char** argv) { network_thread.Start(); base::WaitableEvent client_done(false, false); - scoped_refptr<remoting::X11Client> client = - new remoting::X11Client(network_thread.message_loop(), &client_done); - scoped_refptr<JingleHostConnection> connection = - new JingleHostConnection(&network_thread, client); - connection->Connect(username, auth_token, host_jid); + remoting::X11Client client(network_thread.message_loop(), &client_done); + JingleHostConnection connection(&network_thread); + connection.Connect(username, auth_token, host_jid, &client); - client->InitX11(); + client.InitX11(); // Wait until the main loop is done. client_done.Wait(); - connection->Disconnect(); + connection.Disconnect(); - client->DestroyX11(); + client.DestroyX11(); // Quit the current message loop. network_thread.message_loop()->PostTask(FROM_HERE, diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc index 599fa31..eb16acc 100644 --- a/remoting/client/x11_view.cc +++ b/remoting/client/x11_view.cc @@ -8,6 +8,8 @@ #include <X11/Xutil.h> #include <X11/extensions/Xrender.h> #include <X11/extensions/Xcomposite.h> + +#include "base/logging.h" #include "remoting/client/decoder_verbatim.h" namespace remoting { @@ -74,6 +76,26 @@ void X11View::Paint() { XFreePixmap(display_, pixmap); } +void X11View::SetSolidFill(uint32 color) { + // TODO(ajwong): Implement. + NOTIMPLEMENTED(); +} + +void X11View::UnsetSolidFill() { + // TODO(ajwong): Implement. + NOTIMPLEMENTED(); +} + +void X11View::SetViewport(int x, int y, int width, int height) { + // TODO(ajwong): Implement. + NOTIMPLEMENTED(); +} + +void X11View::SetBackingStoreSize(int width, int height) { + // TODO(ajwong): Implement. + NOTIMPLEMENTED(); +} + void X11View::InitPaintTarget() { // Testing XRender support. int dummy; diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h index f6f8a3d..1a76cb3 100644 --- a/remoting/client/x11_view.h +++ b/remoting/client/x11_view.h @@ -25,6 +25,10 @@ class X11View : public ChromotingView { // ChromotingView implementations. virtual void Paint(); + virtual void SetSolidFill(uint32 color); + virtual void UnsetSolidFill(); + virtual void SetViewport(int x, int y, int width, int height); + virtual void SetBackingStoreSize(int width, int height); virtual void HandleBeginUpdateStream(HostMessage* msg); virtual void HandleUpdateStreamPacket(HostMessage* msg); virtual void HandleEndUpdateStream(HostMessage* msg) ; @@ -54,4 +58,6 @@ class X11View : public ChromotingView { } // namespace remoting +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::X11View); + #endif // REMOTING_CLIENT_X11_VIEW_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 101680f..9156951 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -26,19 +26,17 @@ 'HAVE_STDINT_H', # Required by on2_integer.h ], 'dependencies': [ + 'chromoting_base', + 'chromoting_client', + 'chromoting_jingle_glue', '../third_party/zlib/zlib.gyp:zlib', ], 'sources': [ 'client/plugin/chromoting_main.cc', 'client/plugin/chromoting_plugin.cc', 'client/plugin/chromoting_plugin.h', - 'client/plugin/client.cc', - 'client/plugin/client.h', - 'client/plugin/compression.cc', - 'client/plugin/compression.h', - 'client/plugin/decoder.h', - 'client/plugin/host_connection.cc', - 'client/plugin/host_connection.h', + 'client/plugin/pepper_view.cc', + 'client/plugin/pepper_view.h', 'client/pepper/pepper_plugin.cc', 'client/pepper/pepper_plugin.h', '../media/base/yuv_convert.cc', @@ -60,28 +58,29 @@ ], # end of 'conditions' }, # end of target 'chromoting_client_plugin_lib' +# TODO(ajwong): reenable once we figure out the -fPIC issues. # Client plugin: libchromoting_plugin.so. - { - 'target_name': 'chromoting_client_plugin', - 'type': 'shared_library', - 'product_name': 'chromoting_plugin', - 'dependencies': [ - 'chromoting_client_plugin_lib', - ], - 'sources': [ - # Required here (rather than in lib) so that functions are - # exported properly. - 'client/pepper/pepper_main.cc', - ], - 'conditions': [ - ['OS=="linux" and target_arch=="x64" and linux_fpic!=1', { - # Shared libraries need -fPIC on x86-64 - 'cflags': [ - '-fPIC' - ], - }], - ], # end of 'conditions' - }, # end of target 'chromoting_client_plugin' +# { +# 'target_name': 'chromoting_client_plugin', +# 'type': 'shared_library', +# 'product_name': 'chromoting_plugin', +# 'dependencies': [ +# 'chromoting_client_plugin_lib', +# ], +# 'sources': [ +# # Required here (rather than in lib) so that functions are +# # exported properly. +# 'client/pepper/pepper_main.cc', +# ], +# 'conditions': [ +# ['OS=="linux" and target_arch=="x64" and linux_fpic!=1', { +# # Shared libraries need -fPIC on x86-64 +# 'cflags': [ +# '-fPIC' +# ], +# }], +# ], # end of 'conditions' +# }, # end of target 'chromoting_client_plugin' # Simple webserver for testing chromoting client plugin. { @@ -226,9 +225,11 @@ 'chromoting_jingle_glue', ], 'sources': [ + 'client/chromoting_client.cc', + 'client/chromoting_client.h', + 'client/chromoting_view.h', 'client/client_util.cc', 'client/client_util.h', - 'client/chromoting_view.h', 'client/decoder.h', 'client/decoder_verbatim.cc', 'client/decoder_verbatim.h', diff --git a/remoting/tools/client_webserver/main.c b/remoting/tools/client_webserver/main.c index 6f7baba..f9494b4 100644 --- a/remoting/tools/client_webserver/main.c +++ b/remoting/tools/client_webserver/main.c @@ -106,7 +106,10 @@ void handle_request(int connection) { // However, we return empty content, but with the requested plugin mimetype. write_data(connection, "HTTP/1.0 200 OK\r\n"); write_data(connection, "Content-Type: "); - write_data(connection, mime_type); + // TODO(ajwong): Currently hardcoding the mimetype rather than parsing out of + // URL to make it easier to invoke the plugin. Change this back to writing + // |mime_type| out after we get things cleaned up. + write_data(connection, "pepper-application/x-chromoting-plugin"); write_data(connection, "\r\n\r\n"); // This dummy data is unused, but must be present or else the reader may hang. |