diff options
author | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 17:51:47 +0000 |
---|---|---|
committer | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 17:51:47 +0000 |
commit | 376dea2176504ad886c68110aff882a113807467 (patch) | |
tree | 3d835427a52ee536c72ae6fbe74124dec38c6a9c /remoting | |
parent | 02d08e07d137b394792d15b712d029455713b195 (diff) | |
download | chromium_src-376dea2176504ad886c68110aff882a113807467.zip chromium_src-376dea2176504ad886c68110aff882a113807467.tar.gz chromium_src-376dea2176504ad886c68110aff882a113807467.tar.bz2 |
Hook up communication between Chromoting plugin and JS so that it can notify the JS UI of status changes.
Push SetState handling down ChromotingView so that UI changes are contained in the View.
BUG=54855
TEST=chromoting unittests
Review URL: http://codereview.chromium.org/3446020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61977 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-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. |