diff options
-rw-r--r-- | remoting/client/chromoting_client.cc | 32 | ||||
-rw-r--r-- | remoting/client/chromoting_client.h | 17 | ||||
-rw-r--r-- | remoting/client/chromoting_view.h | 15 | ||||
-rw-r--r-- | remoting/client/chromoting_view_unittest.cc | 1 | ||||
-rw-r--r-- | remoting/client/extension/background.js | 22 | ||||
-rw-r--r-- | remoting/client/extension/chromoting_tab.html | 45 | ||||
-rw-r--r-- | remoting/client/extension/chromoting_tab.js | 143 | ||||
-rw-r--r-- | remoting/client/extension/client.js | 8 | ||||
-rw-r--r-- | remoting/client/extension/main.css | 26 | ||||
-rw-r--r-- | remoting/client/extension/popup.html | 2 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_instance.cc | 28 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_instance.h | 10 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_scriptable_object.cc | 75 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_scriptable_object.h | 63 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view.cc | 31 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_view.h | 1 | ||||
-rw-r--r-- | remoting/client/x11_view.cc | 4 | ||||
-rw-r--r-- | remoting/client/x11_view.h | 1 |
18 files changed, 407 insertions, 117 deletions
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc index 7669e3f..70a5c49 100644 --- a/remoting/client/chromoting_client.cc +++ b/remoting/client/chromoting_client.cc @@ -12,10 +12,6 @@ #include "remoting/client/input_handler.h" #include "remoting/client/rectangle_update_decoder.h" -static const uint32 kCreatedColor = 0xffccccff; -static const uint32 kDisconnectedColor = 0xff00ccff; -static const uint32 kFailedColor = 0xffcc00ff; - namespace remoting { ChromotingClient::ChromotingClient(const ClientConfig& config, @@ -155,51 +151,35 @@ void ChromotingClient::DispatchMessage() { void ChromotingClient::OnConnectionOpened(HostConnection* conn) { LOG(INFO) << "ChromotingClient::OnConnectionOpened"; - SetState(CONNECTED); + SetConnectionState(CONNECTED); } void ChromotingClient::OnConnectionClosed(HostConnection* conn) { LOG(INFO) << "ChromotingClient::OnConnectionClosed"; - SetState(DISCONNECTED); + SetConnectionState(DISCONNECTED); } void ChromotingClient::OnConnectionFailed(HostConnection* conn) { LOG(INFO) << "ChromotingClient::OnConnectionFailed"; - SetState(FAILED); + SetConnectionState(FAILED); } MessageLoop* ChromotingClient::message_loop() { return context_->jingle_thread()->message_loop(); } -void ChromotingClient::SetState(State s) { +void ChromotingClient::SetConnectionState(ConnectionState s) { // TODO(ajwong): We actually may want state to be a shared variable. Think // through later. if (message_loop() != MessageLoop::current()) { message_loop()->PostTask( FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::SetState, s)); + NewRunnableMethod(this, &ChromotingClient::SetConnectionState, s)); return; } 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; - } + view_->SetConnectionState(s); Repaint(); } diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h index f55ec78..74fa35d 100644 --- a/remoting/client/chromoting_client.h +++ b/remoting/client/chromoting_client.h @@ -10,17 +10,17 @@ #include "base/task.h" #include "remoting/client/host_connection.h" #include "remoting/client/client_config.h" +#include "remoting/client/chromoting_view.h" #include "remoting/protocol/messages_decoder.h" class MessageLoop; namespace remoting { -class ChromotingView; -class ClientContext; -class InputHandler; class ChromotingHostMessage; +class ClientContext; class InitClientMessage; +class InputHandler; class RectangleUpdateDecoder; class ChromotingClient : public HostConnection::HostEventCallback { @@ -58,17 +58,10 @@ class ChromotingClient : public HostConnection::HostEventCallback { 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); + void SetConnectionState(ConnectionState s); // If a message is not being processed, dispatches a single message from the // |received_messages_| queue. @@ -90,7 +83,7 @@ class ChromotingClient : public HostConnection::HostEventCallback { // If non-NULL, this is called when the client is done. CancelableTask* client_done_; - State state_; + ConnectionState state_; // Contains all messages that have been received, but have not yet been // processed. diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h index faf4f68..7d2b68a 100644 --- a/remoting/client/chromoting_view.h +++ b/remoting/client/chromoting_view.h @@ -16,6 +16,18 @@ class WaitableEvent; namespace remoting { +static const uint32 kCreatedColor = 0xffccccff; +static const uint32 kDisconnectedColor = 0xff00ccff; +static const uint32 kFailedColor = 0xffcc00ff; + +// TODO(garykac): Move this into a proper class that keeps track of state. +enum ConnectionState { + CREATED, + CONNECTED, + DISCONNECTED, + FAILED, +}; + // ChromotingView defines the behavior of an object that draws a view of the // remote desktop. Its main function is to render the update stream onto the // screen. @@ -46,6 +58,9 @@ class ChromotingView { // does nothing. virtual void UnsetSolidFill() = 0; + // Record the update the state of the connection, updating the UI as needed. + virtual void SetConnectionState(ConnectionState s) = 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; diff --git a/remoting/client/chromoting_view_unittest.cc b/remoting/client/chromoting_view_unittest.cc index 6dcb60c..c376d10 100644 --- a/remoting/client/chromoting_view_unittest.cc +++ b/remoting/client/chromoting_view_unittest.cc @@ -42,6 +42,7 @@ class FakeView : public ChromotingView { void Paint() {} void SetSolidFill(uint32 color) {} void UnsetSolidFill() {} + void SetConnectionState(ConnectionState s) {} void SetViewport(int x, int y, int width, int height) { frame_width_ = width; frame_height_ = height; diff --git a/remoting/client/extension/background.js b/remoting/client/extension/background.js index 9fdf017..0f851a7 100644 --- a/remoting/client/extension/background.js +++ b/remoting/client/extension/background.js @@ -2,24 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -function openChromotingTab(host_jid) { +function openChromotingTab(hostName, hostJid) { var username = getCookie('username'); - var xmpp_auth = getCookie('xmpp_auth'); - var new_tab_url = chrome.extension.getURL("chromoting_tab.html"); + var xmppAuth = getCookie('xmpp_auth'); + var newTabUrl = chrome.extension.getURL("chromoting_tab.html"); var request = { username: getCookie('username'), - xmpp_auth: getCookie('xmpp_auth'), - host_jid: host_jid, + xmppAuth: getCookie('xmpp_auth'), + hostName: hostName, + hostJid: hostJid, }; - var tab_args = { - url: new_tab_url, + var tabArgs = { + url: newTabUrl, }; console.log("Attempt to connect with" + " username='" + request.username + "'" + - " host_jid='" + request.host_jid + "'" + - " auth_token='" + request.xmpp_auth + "'"); - chrome.tabs.create(tab_args, function(tab) { + " hostName='" + request.hostName + "'" + + " hostJid='" + request.hostJid + "'" + + " auth_token='" + request.xmppAuth + "'"); + chrome.tabs.create(tabArgs, function(tab) { console.log("We're trying now to send to " + tab.id); chrome.tabs.sendRequest( tab.id, request, function() { diff --git a/remoting/client/extension/chromoting_tab.html b/remoting/client/extension/chromoting_tab.html index dbe42a56..ef53a8d 100644 --- a/remoting/client/extension/chromoting_tab.html +++ b/remoting/client/extension/chromoting_tab.html @@ -1,39 +1,18 @@ +<!-- +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. +--> + <html> <head> - <title id="title">New Chromoting Session</title> - <script> - function set_listener() { - // This page should only get one request, and it should be - // from the chromoting extension asking for initial connection. - // Later we may need to create a more persistent channel for - // better UI communication. Then, we should probably switch - // to chrome.extension.connect(). - chrome.extension.onRequest.addListener( - function(request, sender, sendResponse) { - console.log(sender.tab ? - "from a content script:" + sender.tab.url : - "from the extension"); - - // Kick off the connection. Hold on to your butts! - var chromoting = document.getElementById('chromoting'); - if (typeof chromoting.connect === 'function') { - chromoting.connect(request.username, request.host_jid, request.xmpp_auth); - } - - var connect_info = "On [" + request.host_jid + "] as [" + request.username + "]"; - document.getElementById("title").innerText = connect_info; - document.getElementById("connectinfo").innerText = connect_info; - - // Send an empty response since we have nothing to say. - sendResponse({}); - }); - } - </script> + <title id="title">Chromoting Session</title> + <link rel="stylesheet" type="text/css" href="main.css" /> + <script type="text/javascript" src="chromoting_tab.js"></script> </head> - <body onload="set_listener();"> - Why hello there! I'm your friendly Chromoting Tab. - <div id="connectinfo"></div> - <div id="plugin_div" style="border: black 1px dashed;"> + <body class="chromoting_body" onload="init();"> + <div id="status_msg" class="status_msg"></div> + <div id="plugin_div" class="plugin"> <embed width="100%" height="100%" name="chromoting" id="chromoting" src="about://none" type="pepper-application/x-chromoting"> </div> diff --git a/remoting/client/extension/chromoting_tab.js b/remoting/client/extension/chromoting_tab.js new file mode 100644 index 0000000..cbdd3a2 --- /dev/null +++ b/remoting/client/extension/chromoting_tab.js @@ -0,0 +1,143 @@ +// 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. + +// Namespace for globals. +var chromoting = {}; + +// Message id so that we can identify (and ignore) message fade operations for +// old messages. This starts at 1 and is incremented for each new message. +chromoting.messageId = 1; + +function init() { + // This page should only get one request, and it should be + // from the chromoting extension asking for initial connection. + // Later we may need to create a more persistent channel for + // better UI communication. Then, we should probably switch + // to chrome.extension.connect(). + chrome.extension.onRequest.addListener(requestListener); +} + +/** + * A listener function to be called when the extension fires a request. + * + * @param request The request sent by the calling script. + * @param sender The MessageSender object with info about the calling script's + * context. + * @param sendResponse Function to call with response. + */ +function requestListener(request, sender, sendResponse) { + console.log(sender.tab ? + 'from a content script: ' + sender.tab.url : + 'from the extension'); + + // Kick off the connection. + var plugin = document.getElementById('chromoting'); + + chromoting.plugin = plugin; + chromoting.username = request.username; + chromoting.hostname = request.hostName; + + // Setup the callback that the plugin will call when the connection status + // has changes and the UI needs to be updated. It needs to be an object with + // a 'callback' property that contains the callback function. + plugin.connectionInfoUpdate = pluginCallback; + + // TODO(garykac): Clean exit if |connect| isn't a funtion. + if (typeof plugin.connect === 'function') { + plugin.connect(request.username, request.hostJid, + request.xmppAuth); + } + + document.getElementById('title').innerText = request.hostName; + + // Send an empty response since we have nothing to say. + sendResponse({}); +} + +/** + * This is that callback that the plugin invokes to indicate that the host/ + * client connection status has changed. + */ +function pluginCallback() { + var status = chromoting.plugin.status; + var quality = chromoting.plugin.quality; + + if (status == chromoting.plugin.STATUS_UNKNOWN) { + showClientStateMessage(''); + } else if (status == chromoting.plugin.STATUS_CONNECTING) { + showClientStateMessage('Connecting to ' + chromoting.hostname + + ' as ' + chromoting.username); + } else if (status == chromoting.plugin.STATUS_INITIALIZING) { + showClientStateMessage('Initializing connection to ' + chromoting.hostname); + } else if (status == chromoting.plugin.STATUS_CONNECTED) { + showClientStateMessage('Connected to ' + chromoting.hostname, 1000); + } else if (status == chromoting.plugin.STATUS_CLOSED) { + showClientStateMessage('Closed'); + } else if (status == chromoting.plugin.STATUS_FAILED) { + showClientStateMessage('Failed'); + } +} + +/** + * Show a client message on the screen. + * If duration is specified, the message fades out after the duration expires. + * Otherwise, the message stays until the state changes. + * + * @param {string} message The message to display. + * @param {number} duration Milliseconds to show message before fading. + */ +function showClientStateMessage(message, duration) { + // Increment message id to ignore any previous fadeout requests. + chromoting.messageId++; + console.log('setting message ' + chromoting.messageId); + + // Update the status message. + var msg = document.getElementById('status_msg'); + msg.innerText = message; + msg.style.opacity = 1; + + if (duration) { + // Set message duration. + window.setTimeout("fade('status_msg', " + chromoting.messageId + ", " + + "100, 10, 200)", + duration); + } +} + +/** + * Fade the specified element. + * For example, to have element 'foo' fade away over 2 seconds, you could use + * either: + * fade('foo', 100, 10, 200) + * - Start at 100%, decrease by 10% each time, wait 200ms between updates. + * fade('foo', 100, 5, 100) + * - Start at 100%, decrease by 5% each time, wait 100ms between updates. + * + * @param {string} name Name of element to fade. + * @param {number} id The id of the message associated with this fade request. + * @param {number} val The new opacity value (0-100) for this element. + * @param {number} delta Amount to adjust the opacity each iteration. + * @param {number} delay Delay (in ms) to wait between each update. + */ +function fade(name, id, val, delta, delay) { + // Ignore the fade call if it does not apply to the current message. + if (id != chromoting.messageId) { + return; + } + + var e = document.getElementById(name); + if (e) { + var newVal = val - delta; + if (newVal > 0) { + // Decrease opacity and set timer for next fade event. + e.style.opacity = newVal / 100; + window.setTimeout("fade('status_msg', " + id + ", " + newVal + ", " + + delta + ", " + delay + ")", + delay); + } else { + // Completely hide the text and stop fading. + e.style.opacity = 0; + } + } +} diff --git a/remoting/client/extension/client.js b/remoting/client/extension/client.js index 138d014..2e636be 100644 --- a/remoting/client/extension/client.js +++ b/remoting/client/extension/client.js @@ -130,9 +130,9 @@ function extractAuthToken(message) { } // Open a chromoting connection in a new tab. -function openChromotingTab(host_jid) { +function openChromotingTab(hostName, hostJid) { var background = chrome.extension.getBackgroundPage(); - background.openChromotingTab(host_jid); + background.openChromotingTab(hostName, hostJid); } // Erase the content of the specified element. @@ -248,8 +248,8 @@ function addHostInfo(host) { var connect = document.createElement('input'); connect.setAttribute('type', 'button'); connect.setAttribute('value', 'Connect'); - connect.setAttribute('onclick', 'openChromotingTab(\'' + host.jabberId + - '\'); return false;'); + connect.setAttribute('onclick', "openChromotingTab('" + host.hostName + + "', '" + host.jabberId + "'); return false;"); span.appendChild(connect); hostEntry.appendChild(span); diff --git a/remoting/client/extension/main.css b/remoting/client/extension/main.css index d375107..42f2761 100644 --- a/remoting/client/extension/main.css +++ b/remoting/client/extension/main.css @@ -1,4 +1,4 @@ -body { +.popup_body { width: 500px; overflow: hidden; scrollbars: no; @@ -18,13 +18,13 @@ h1 { .message { font-family: sans-serif; font-size: 1.2em; - padding: 0px 4px 0px 4px; + padding: 0 4px 0 4px; } .hostlist { width: 100%; height: 400px; - margin: 0px; + margin: 0; overflow: auto; border: black 1px solid; } @@ -96,3 +96,23 @@ a.hostentry { text-decoration: none; } .login_email { font-weight: bold; } + +.chromoting_body { + margin: 0; + padding: 0; + overflow: hidden; + scrollbars: no; +} + +.status_msg { + position: absolute; + left: 20px; + top: 20px; + z-index: 1; + font-family: sans-serif; + font-size: 2em; + font-weight: bold; +} + +.plugin { +} diff --git a/remoting/client/extension/popup.html b/remoting/client/extension/popup.html index d582cba..117248d 100644 --- a/remoting/client/extension/popup.html +++ b/remoting/client/extension/popup.html @@ -11,7 +11,7 @@ found in the LICENSE file. <link rel="stylesheet" type="text/css" href="main.css" /> <title>Remote Access</title> </head> - <body onload="init();"> + <body class="popup_body" onload="init();"> <h1>Remote Access</h1> diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc index 142d9d5..2f61c92 100644 --- a/remoting/client/plugin/chromoting_instance.cc +++ b/remoting/client/plugin/chromoting_instance.cc @@ -93,10 +93,23 @@ void ChromotingInstance::Connect(const ClientConfig& config) { // Kick off the connection. client_->Start(); + + GetScriptableObject()->SetConnectionInfo(STATUS_INITIALIZING, + QUALITY_UNKNOWN); +} + +void ChromotingInstance::Disconnect() { + DCHECK(CurrentlyOnPluginThread()); + + if (client_.get()) { + client_->Stop(); + } + + GetScriptableObject()->SetConnectionInfo(STATUS_CLOSED, QUALITY_UNKNOWN); } void ChromotingInstance::ViewChanged(const pp::Rect& position, - const pp::Rect& clip) { + const pp::Rect& clip) { DCHECK(CurrentlyOnPluginThread()); // TODO(ajwong): This is going to be a race condition when the view changes @@ -154,13 +167,22 @@ bool ChromotingInstance::HandleInputEvent(const PP_InputEvent& event) { return false; } +ChromotingScriptableObject* ChromotingInstance::GetScriptableObject() { + pp::Var object = GetInstanceObject(); + if (!object.is_undefined()) { + pp::deprecated::ScriptableObject* so = object.AsScriptableObject(); + DCHECK(so != NULL); + return static_cast<ChromotingScriptableObject*>(so); + } + LOG(ERROR) << "Unable to get ScriptableObject for Chromoting plugin."; + return NULL; +} + pp::Var ChromotingInstance::GetInstanceObject() { - LOG(ERROR) << "Getting instance object."; if (instance_object_.is_undefined()) { ChromotingScriptableObject* object = new ChromotingScriptableObject(this); object->Init(); - LOG(ERROR) << "Object initted."; // The pp::Var takes ownership of object here. instance_object_ = pp::Var(object); } diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h index 6c9d7c0..df1db9f 100644 --- a/remoting/client/plugin/chromoting_instance.h +++ b/remoting/client/plugin/chromoting_instance.h @@ -14,6 +14,7 @@ #include "base/scoped_ptr.h" #include "remoting/client/client_context.h" #include "remoting/client/host_connection.h" +#include "remoting/client/plugin/chromoting_scriptable_object.h" #include "third_party/ppapi/c/pp_instance.h" #include "third_party/ppapi/c/pp_rect.h" #include "third_party/ppapi/c/pp_resource.h" @@ -52,11 +53,15 @@ class ChromotingInstance : public pp::Instance { virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); virtual void Connect(const ClientConfig& config); virtual bool HandleInputEvent(const PP_InputEvent& event); + virtual void Disconnect(); virtual pp::Var GetInstanceObject(); virtual void ViewChanged(const pp::Rect& position, const pp::Rect& clip); virtual bool CurrentlyOnPluginThread() const; + // Convenience wrapper to get the ChromotingScriptableObject. + ChromotingScriptableObject* GetScriptableObject(); + private: FRIEND_TEST_ALL_PREFIXES(ChromotingInstanceTest, TestCaseSetup); @@ -74,7 +79,10 @@ class ChromotingInstance : public pp::Instance { scoped_ptr<RectangleUpdateDecoder> rectangle_decoder_; scoped_ptr<InputHandler> input_handler_; scoped_ptr<ChromotingClient> client_; - pp::Var instance_object_; // JavaScript interface to control this instance. + + // JavaScript interface to control this instance. + // This wraps a ChromotingScriptableObject in a pp::Var. + pp::Var instance_object_; DISALLOW_COPY_AND_ASSIGN(ChromotingInstance); }; diff --git a/remoting/client/plugin/chromoting_scriptable_object.cc b/remoting/client/plugin/chromoting_scriptable_object.cc index a54b39a..1bc909b 100644 --- a/remoting/client/plugin/chromoting_scriptable_object.cc +++ b/remoting/client/plugin/chromoting_scriptable_object.cc @@ -12,6 +12,10 @@ using pp::Var; namespace remoting { + +const char kStatusAttribute[] = "status"; +const char kQualityAttribute[] = "quality"; + ChromotingScriptableObject::ChromotingScriptableObject( ChromotingInstance* instance) : instance_(instance) { @@ -23,15 +27,27 @@ ChromotingScriptableObject::~ChromotingScriptableObject() { void ChromotingScriptableObject::Init() { // Property addition order should match the interface description at the // top of chromoting_scriptable_object.h. - AddAttribute("onreadystatechange", Var()); - AddAttribute("NOT_CONNECTED", Var(0)); - AddAttribute("CONNECTED", Var(1)); + // Connection status. + AddAttribute(kStatusAttribute, Var(STATUS_UNKNOWN)); + // Connection status values. + AddAttribute("STATUS_UNKNOWN", Var(STATUS_UNKNOWN)); + AddAttribute("STATUS_CONNECTING", Var(STATUS_CONNECTING)); + AddAttribute("STATUS_INITIALIZING", Var(STATUS_INITIALIZING)); + AddAttribute("STATUS_CONNECTED", Var(STATUS_CONNECTED)); + AddAttribute("STATUS_CLOSED", Var(STATUS_CLOSED)); + AddAttribute("STATUS_FAILED", Var(STATUS_FAILED)); - AddMethod("connect", &ChromotingScriptableObject::DoConnect); + // Connection quality. + AddAttribute(kQualityAttribute, Var(QUALITY_UNKNOWN)); + AddAttribute("QUALITY_UNKNOWN", Var(QUALITY_UNKNOWN)); + AddAttribute("QUALITY_GOOD", Var(QUALITY_GOOD)); + AddAttribute("QUALITY_BAD", Var(QUALITY_BAD)); - // TODO(ajwong): Figure out how to implement the status variable. - AddAttribute("status", Var("not_implemented_yet")); + AddAttribute("connectionInfoUpdate", Var()); + + AddMethod("connect", &ChromotingScriptableObject::DoConnect); + AddMethod("disconnect", &ChromotingScriptableObject::DoDisconnect); } bool ChromotingScriptableObject::HasProperty(const Var& name, Var* exception) { @@ -99,7 +115,7 @@ void ChromotingScriptableObject::GetAllPropertyNames( void ChromotingScriptableObject::SetProperty(const Var& name, const Var& value, Var* exception) { - // TODO(ajwong): Check if all these name.is_string() sentinels are required. + // TODO(ajwong): Check if all these name.is_string() sentinels are required. 120 // No externally settable properties for Chromoting. if (!name.is_string()) { *exception = Var("SetProperty expects a string for the name."); return; @@ -108,7 +124,7 @@ void ChromotingScriptableObject::SetProperty(const Var& name, // Only allow writing to onreadystatechange. See top of // chromoting_scriptable_object.h for the object interface definition. std::string property_name = name.AsString(); - if (property_name != "onstatechange") { + if (property_name != "connectionInfoUpdate") { *exception = Var("Cannot set property " + property_name + " on this object."); return; @@ -131,6 +147,22 @@ Var ChromotingScriptableObject::Call(const Var& method_name, return (this->*(properties_[iter->second].method))(args, exception); } +void ChromotingScriptableObject::SetConnectionInfo(ConnectionStatus status, + ConnectionQuality quality) { + int status_index = property_names_[kStatusAttribute]; + int quality_index = property_names_[kQualityAttribute]; + + if (properties_[status_index].attribute.AsInt() != status || + properties_[quality_index].attribute.AsInt() != quality) { + // Update the connection state properties.. + properties_[status_index].attribute = Var(status); + properties_[quality_index].attribute = Var(quality); + + // Signal the Chromoting Tab UI to get the update connection state values. + SignalConnectionInfoChange(); + } +} + void ChromotingScriptableObject::AddAttribute(const std::string& name, Var attribute) { property_names_[name] = properties_.size(); @@ -143,10 +175,27 @@ void ChromotingScriptableObject::AddMethod(const std::string& name, properties_.push_back(PropertyDescriptor(name, handler)); } +void ChromotingScriptableObject::SignalConnectionInfoChange() { + pp::Var exception; + + // The JavaScript callback function is the 'callback' property on the + // 'connectionInfoUpdate' object. + Var cb = GetProperty(Var("connectionInfoUpdate"), &exception); + + // Var() means call the object directly as a function rather than calling + // a method in the object. + cb.Call(Var(), 0, NULL, &exception); + + if (!exception.is_undefined()) { + LOG(WARNING) << "Exception when invoking JS callback" + << exception.AsString(); + } +} + pp::Var ChromotingScriptableObject::DoConnect(const std::vector<Var>& args, - Var* exception) { + Var* exception) { if (args.size() != 3) { - *exception = Var("Usage: connect(username, host_jid, auth_token"); + *exception = Var("Usage: connect(username, host_jid, auth_token)"); return Var(); } @@ -175,4 +224,10 @@ pp::Var ChromotingScriptableObject::DoConnect(const std::vector<Var>& args, return Var(); } +pp::Var ChromotingScriptableObject::DoDisconnect(const std::vector<Var>& args, + Var* exception) { + instance_->Disconnect(); + return Var(); +} + } // namespace remoting diff --git a/remoting/client/plugin/chromoting_scriptable_object.h b/remoting/client/plugin/chromoting_scriptable_object.h index 8c30da0..c317f57 100644 --- a/remoting/client/plugin/chromoting_scriptable_object.h +++ b/remoting/client/plugin/chromoting_scriptable_object.h @@ -6,25 +6,33 @@ // The Javascript API is defined as follows. // // interface ChromotingScriptableObject { -// // Called when the Chromoting instance has had a state change such as -// // connection completed. -// attribute Function onreadystatechange; // -// // Constants for states, etc. -// const unsigned short NOT_CONNECTED = 0; -// const unsigned short CONNECTED = 1; +// // Connection status. +// readonly attribute unsigned short connection_status; + +// // Constants for connection status. +// const unsigned short STATUS_UNKNOWN = 0; +// const unsigned short STATUS_CONNECTING = 1; +// const unsigned short STATUS_INITIALIZING = 2; +// const unsigned short STATUS_CONNECTED = 3; +// const unsigned short STATUS_CLOSED = 4; +// const unsigned short STATUS_FAILED = 5; +// +// // Connection quality. +// readonly attribute unsigned short connection_quality; +// // Constants for connection quality +// const unsigned short QUALITY_UNKNOWN = 0; +// const unsigned short QUALITY_GOOD = 1; +// const unsigned short QUALITY_BAD = 2; +// +// // JS callback function so we can signal the JS UI when the connection +// // status has been updated. +// attribute Function connectionInfoUpdate; // // // Methods on the object. // void connect(string username, string host_jid, string auth_token); -// -// // Attributes. -// readonly attribute unsigned short status; +// void disconnect(); // } -// -// onreadystatechange -// -// Methods: -// Connect(username, auth_token, host_jid, onstatechange); #ifndef REMOTING_CLIENT_PLUGIN_CHROMOTING_SCRIPTABLE_OBJECT_H_ #define REMOTING_CLIENT_PLUGIN_CHROMOTING_SCRIPTABLE_OBJECT_H_ @@ -40,6 +48,25 @@ namespace remoting { class ChromotingInstance; +extern const char kStatusAttribute[]; + +enum ConnectionStatus { + STATUS_UNKNOWN = 0, + STATUS_CONNECTING, + STATUS_INITIALIZING, + STATUS_CONNECTED, + STATUS_CLOSED, + STATUS_FAILED, +}; + +extern const char kQualityAttribute[]; + +enum ConnectionQuality { + QUALITY_UNKNOWN = 0, + QUALITY_GOOD, + QUALITY_BAD, +}; + class ChromotingScriptableObject : public pp::deprecated::ScriptableObject { public: explicit ChromotingScriptableObject(ChromotingInstance* instance); @@ -60,6 +87,8 @@ class ChromotingScriptableObject : public pp::deprecated::ScriptableObject { const std::vector<pp::Var>& args, pp::Var* exception); + void SetConnectionInfo(ConnectionStatus status, ConnectionQuality quality); + private: typedef std::map<std::string, int> PropertyNameMap; typedef pp::Var (ChromotingScriptableObject::*MethodHandler)( @@ -84,10 +113,16 @@ class ChromotingScriptableObject : public pp::deprecated::ScriptableObject { }; + // Routines to add new attribute, method properties. void AddAttribute(const std::string& name, pp::Var attribute); void AddMethod(const std::string& name, MethodHandler handler); + // This should be called to signal the JS code that the connection status has + // changed. + void SignalConnectionInfoChange(); + pp::Var DoConnect(const std::vector<pp::Var>& args, pp::Var* exception); + pp::Var DoDisconnect(const std::vector<pp::Var>& args, pp::Var* exception); PropertyNameMap property_names_; std::vector<PropertyDescriptor> properties_; diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc index eacb37d..9a10d1a 100644 --- a/remoting/client/plugin/pepper_view.cc +++ b/remoting/client/plugin/pepper_view.cc @@ -135,6 +135,37 @@ void PepperView::UnsetSolidFill() { is_static_fill_ = false; } +void PepperView::SetConnectionState(ConnectionState state) { + if (!instance_->CurrentlyOnPluginThread()) { + RunTaskOnPluginThread( + NewRunnableMethod(this, &PepperView::SetConnectionState, state)); + return; + } + + ChromotingScriptableObject* scriptable_obj = instance_->GetScriptableObject(); + switch (state) { + case CREATED: + SetSolidFill(kCreatedColor); + scriptable_obj->SetConnectionInfo(STATUS_CONNECTING, QUALITY_UNKNOWN); + break; + + case CONNECTED: + UnsetSolidFill(); + scriptable_obj->SetConnectionInfo(STATUS_CONNECTED, QUALITY_UNKNOWN); + break; + + case DISCONNECTED: + SetSolidFill(kDisconnectedColor); + scriptable_obj->SetConnectionInfo(STATUS_CLOSED, QUALITY_UNKNOWN); + break; + + case FAILED: + SetSolidFill(kFailedColor); + scriptable_obj->SetConnectionInfo(STATUS_FAILED, QUALITY_UNKNOWN); + break; + } +} + void PepperView::SetViewport(int x, int y, int width, int height) { if (!instance_->CurrentlyOnPluginThread()) { RunTaskOnPluginThread(NewTracedMethod(this, &PepperView::SetViewport, diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h index 95c180d..80ce1ae 100644 --- a/remoting/client/plugin/pepper_view.h +++ b/remoting/client/plugin/pepper_view.h @@ -41,6 +41,7 @@ class PepperView : public ChromotingView, virtual void Paint(); virtual void SetSolidFill(uint32 color); virtual void UnsetSolidFill(); + virtual void SetConnectionState(ConnectionState state); virtual void SetViewport(int x, int y, int width, int height); // FrameConsumer implementation. diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc index 9ccdb88..07f2f87 100644 --- a/remoting/client/x11_view.cc +++ b/remoting/client/x11_view.cc @@ -133,6 +133,10 @@ void X11View::UnsetSolidFill() { // NOTIMPLEMENTED(); } +void X11View::SetConnectionState(ConnectionState s) { + // TODO(garykac): Implement. +} + void X11View::SetViewport(int x, int y, int width, int height) { // TODO(garykac): Implement. // NOTIMPLEMENTED(); diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h index 5da6220..7b62394 100644 --- a/remoting/client/x11_view.h +++ b/remoting/client/x11_view.h @@ -29,6 +29,7 @@ class X11View : public ChromotingView, public FrameConsumer { virtual void Paint(); virtual void SetSolidFill(uint32 color); virtual void UnsetSolidFill(); + virtual void SetConnectionState(ConnectionState s); virtual void SetViewport(int x, int y, int width, int height); // FrameConsumer implementation. |