summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/client/chromoting_client.cc32
-rw-r--r--remoting/client/chromoting_client.h17
-rw-r--r--remoting/client/chromoting_view.h15
-rw-r--r--remoting/client/chromoting_view_unittest.cc1
-rw-r--r--remoting/client/extension/background.js22
-rw-r--r--remoting/client/extension/chromoting_tab.html45
-rw-r--r--remoting/client/extension/chromoting_tab.js143
-rw-r--r--remoting/client/extension/client.js8
-rw-r--r--remoting/client/extension/main.css26
-rw-r--r--remoting/client/extension/popup.html2
-rw-r--r--remoting/client/plugin/chromoting_instance.cc28
-rw-r--r--remoting/client/plugin/chromoting_instance.h10
-rw-r--r--remoting/client/plugin/chromoting_scriptable_object.cc75
-rw-r--r--remoting/client/plugin/chromoting_scriptable_object.h63
-rw-r--r--remoting/client/plugin/pepper_view.cc31
-rw-r--r--remoting/client/plugin/pepper_view.h1
-rw-r--r--remoting/client/x11_view.cc4
-rw-r--r--remoting/client/x11_view.h1
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.