Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Side by Side Diff: trunk/src/remoting/webapp/client_session.js

Issue 336423003: Revert 277797 "Refactor tool-bar event handlers." (Closed) Base URL: svn://svn.chromium.org/chrome/
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « trunk/src/remoting/webapp/client_screen.js ('k') | trunk/src/remoting/webapp/event_handlers.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview 6 * @fileoverview
7 * Class handling creation and teardown of a remoting client session. 7 * Class handling creation and teardown of a remoting client session.
8 * 8 *
9 * The ClientSession class controls lifetime of the client plugin 9 * The ClientSession class controls lifetime of the client plugin
10 * object and provides the plugin with the functionality it needs to 10 * object and provides the plugin with the functionality it needs to
11 * establish connection. Specifically it: 11 * establish connection. Specifically it:
12 * - Delivers incoming/outgoing signaling messages, 12 * - Delivers incoming/outgoing signaling messages,
13 * - Adjusts plugin size and position when destop resolution changes, 13 * - Adjusts plugin size and position when destop resolution changes,
14 * 14 *
15 * This class should not access the plugin directly, instead it should 15 * This class should not access the plugin directly, instead it should
16 * do it through ClientPlugin class which abstracts plugin version 16 * do it through ClientPlugin class which abstracts plugin version
17 * differences. 17 * differences.
18 */ 18 */
19 19
20 'use strict'; 20 'use strict';
21 21
22 /** @suppress {duplicate} */ 22 /** @suppress {duplicate} */
23 var remoting = remoting || {}; 23 var remoting = remoting || {};
24 24
25 /** 25 /**
26 * @param {string} hostDisplayName A human-readable name for the host.
27 * @param {string} accessCode The IT2Me access code. Blank for Me2Me. 26 * @param {string} accessCode The IT2Me access code. Blank for Me2Me.
28 * @param {function(boolean, function(string): void): void} fetchPin 27 * @param {function(boolean, function(string): void): void} fetchPin
29 * Called by Me2Me connections when a PIN needs to be obtained 28 * Called by Me2Me connections when a PIN needs to be obtained
30 * interactively. 29 * interactively.
31 * @param {function(string, string, string, 30 * @param {function(string, string, string,
32 * function(string, string): void): void} 31 * function(string, string): void): void}
33 * fetchThirdPartyToken Called by Me2Me connections when a third party 32 * fetchThirdPartyToken Called by Me2Me connections when a third party
34 * authentication token must be obtained. 33 * authentication token must be obtained.
35 * @param {string} authenticationMethods Comma-separated list of 34 * @param {string} authenticationMethods Comma-separated list of
36 * authentication methods the client should attempt to use. 35 * authentication methods the client should attempt to use.
37 * @param {string} hostId The host identifier for Me2Me, or empty for IT2Me. 36 * @param {string} hostId The host identifier for Me2Me, or empty for IT2Me.
38 * Mixed into authentication hashes for some authentication methods. 37 * Mixed into authentication hashes for some authentication methods.
39 * @param {string} hostJid The jid of the host to connect to. 38 * @param {string} hostJid The jid of the host to connect to.
40 * @param {string} hostPublicKey The base64 encoded version of the host's 39 * @param {string} hostPublicKey The base64 encoded version of the host's
41 * public key. 40 * public key.
42 * @param {remoting.ClientSession.Mode} mode The mode of this connection. 41 * @param {remoting.ClientSession.Mode} mode The mode of this connection.
43 * @param {string} clientPairingId For paired Me2Me connections, the 42 * @param {string} clientPairingId For paired Me2Me connections, the
44 * pairing id for this client, as issued by the host. 43 * pairing id for this client, as issued by the host.
45 * @param {string} clientPairedSecret For paired Me2Me connections, the 44 * @param {string} clientPairedSecret For paired Me2Me connections, the
46 * paired secret for this client, as issued by the host. 45 * paired secret for this client, as issued by the host.
47 * @constructor 46 * @constructor
48 * @extends {base.EventSource} 47 * @extends {base.EventSource}
49 */ 48 */
50 remoting.ClientSession = function(hostDisplayName, accessCode, fetchPin, 49 remoting.ClientSession = function(accessCode, fetchPin, fetchThirdPartyToken,
51 fetchThirdPartyToken, authenticationMethods, 50 authenticationMethods,
52 hostId, hostJid, hostPublicKey, mode, 51 hostId, hostJid, hostPublicKey, mode,
53 clientPairingId, clientPairedSecret) { 52 clientPairingId, clientPairedSecret) {
54 /** @private */ 53 /** @private */
55 this.state_ = remoting.ClientSession.State.CREATED; 54 this.state_ = remoting.ClientSession.State.CREATED;
56 55
57 /** @private */ 56 /** @private */
58 this.error_ = remoting.Error.NONE; 57 this.error_ = remoting.Error.NONE;
59 58
60 /** @private */ 59 /** @private */
61 this.hostDisplayName_ = hostDisplayName;
62 /** @private */
63 this.hostJid_ = hostJid; 60 this.hostJid_ = hostJid;
64 /** @private */ 61 /** @private */
65 this.hostPublicKey_ = hostPublicKey; 62 this.hostPublicKey_ = hostPublicKey;
66 /** @private */ 63 /** @private */
67 this.accessCode_ = accessCode; 64 this.accessCode_ = accessCode;
68 /** @private */ 65 /** @private */
69 this.fetchPin_ = fetchPin; 66 this.fetchPin_ = fetchPin;
70 /** @private */ 67 /** @private */
71 this.fetchThirdPartyToken_ = fetchThirdPartyToken; 68 this.fetchThirdPartyToken_ = fetchThirdPartyToken;
72 /** @private */ 69 /** @private */
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 * 102 *
106 * @type {boolean} @private 103 * @type {boolean} @private
107 */ 104 */
108 this.logHostOfflineErrors_ = true; 105 this.logHostOfflineErrors_ = true;
109 106
110 /** @private */ 107 /** @private */
111 this.callPluginLostFocus_ = this.pluginLostFocus_.bind(this); 108 this.callPluginLostFocus_ = this.pluginLostFocus_.bind(this);
112 /** @private */ 109 /** @private */
113 this.callPluginGotFocus_ = this.pluginGotFocus_.bind(this); 110 this.callPluginGotFocus_ = this.pluginGotFocus_.bind(this);
114 /** @private */ 111 /** @private */
112 this.callSetScreenMode_ = this.onSetScreenMode_.bind(this);
113 /** @private */
115 this.callToggleFullScreen_ = remoting.fullscreen.toggle.bind( 114 this.callToggleFullScreen_ = remoting.fullscreen.toggle.bind(
116 remoting.fullscreen); 115 remoting.fullscreen);
117 /** @private */ 116 /** @private */
118 this.callOnFullScreenChanged_ = this.onFullScreenChanged_.bind(this) 117 this.callOnFullScreenChanged_ = this.onFullScreenChanged_.bind(this)
119 118
120 /** @private */ 119 /** @private */
121 this.screenOptionsMenu_ = new remoting.MenuButton( 120 this.screenOptionsMenu_ = new remoting.MenuButton(
122 document.getElementById('screen-options-menu'), 121 document.getElementById('screen-options-menu'),
123 this.onShowOptionsMenu_.bind(this)); 122 this.onShowOptionsMenu_.bind(this));
124 /** @private */ 123 /** @private */
(...skipping 13 matching lines...) Expand all
138 this.fullScreenButton_ = document.getElementById('toggle-full-screen'); 137 this.fullScreenButton_ = document.getElementById('toggle-full-screen');
139 138
140 /** @type {remoting.GnubbyAuthHandler} @private */ 139 /** @type {remoting.GnubbyAuthHandler} @private */
141 this.gnubbyAuthHandler_ = null; 140 this.gnubbyAuthHandler_ = null;
142 141
143 if (this.mode_ == remoting.ClientSession.Mode.IT2ME) { 142 if (this.mode_ == remoting.ClientSession.Mode.IT2ME) {
144 // Resize-to-client is not supported for IT2Me hosts. 143 // Resize-to-client is not supported for IT2Me hosts.
145 this.resizeToClientButton_.hidden = true; 144 this.resizeToClientButton_.hidden = true;
146 } else { 145 } else {
147 this.resizeToClientButton_.hidden = false; 146 this.resizeToClientButton_.hidden = false;
147 this.resizeToClientButton_.addEventListener(
148 'click', this.callSetScreenMode_, false);
148 } 149 }
149 150
151 this.shrinkToFitButton_.addEventListener(
152 'click', this.callSetScreenMode_, false);
150 this.fullScreenButton_.addEventListener( 153 this.fullScreenButton_.addEventListener(
151 'click', this.callToggleFullScreen_, false); 154 'click', this.callToggleFullScreen_, false);
152 this.defineEvents(Object.keys(remoting.ClientSession.Events)); 155 this.defineEvents(Object.keys(remoting.ClientSession.Events));
153 }; 156 };
154 157
155 base.extend(remoting.ClientSession, base.EventSource); 158 base.extend(remoting.ClientSession, base.EventSource);
156 159
157 /** @enum {string} */ 160 /** @enum {string} */
158 remoting.ClientSession.Events = { 161 remoting.ClientSession.Events = {
159 stateChanged: 'stateChanged', 162 stateChanged: 'stateChanged',
160 videoChannelStateChanged: 'videoChannelStateChanged' 163 videoChannelStateChanged: 'videoChannelStateChanged'
161 }; 164 };
162 165
163 /** 166 /**
164 * Get host display name.
165 *
166 * @return {string}
167 */
168 remoting.ClientSession.prototype.getHostDisplayName = function() {
169 return this.hostDisplayName_;
170 };
171
172 /**
173 * Called when the window or desktop size or the scaling settings change, 167 * Called when the window or desktop size or the scaling settings change,
174 * to set the scroll-bar visibility. 168 * to set the scroll-bar visibility.
175 * 169 *
176 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is 170 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is
177 * fixed. 171 * fixed.
178 */ 172 */
179 remoting.ClientSession.prototype.updateScrollbarVisibility = function() { 173 remoting.ClientSession.prototype.updateScrollbarVisibility = function() {
180 var needsVerticalScroll = false; 174 var needsVerticalScroll = false;
181 var needsHorizontalScroll = false; 175 var needsHorizontalScroll = false;
182 if (!this.shrinkToFit_) { 176 if (!this.shrinkToFit_) {
(...skipping 18 matching lines...) Expand all
201 } else { 195 } else {
202 scroller.classList.add('no-horizontal-scroll'); 196 scroller.classList.add('no-horizontal-scroll');
203 } 197 }
204 if (needsVerticalScroll) { 198 if (needsVerticalScroll) {
205 scroller.classList.remove('no-vertical-scroll'); 199 scroller.classList.remove('no-vertical-scroll');
206 } else { 200 } else {
207 scroller.classList.add('no-vertical-scroll'); 201 scroller.classList.add('no-vertical-scroll');
208 } 202 }
209 }; 203 };
210 204
211 /**
212 * @return {boolean} True if shrink-to-fit is enabled; false otherwise.
213 */
214 remoting.ClientSession.prototype.getShrinkToFit = function() {
215 return this.shrinkToFit_;
216 };
217
218 /**
219 * @return {boolean} True if resize-to-client is enabled; false otherwise.
220 */
221 remoting.ClientSession.prototype.getResizeToClient = function() {
222 return this.resizeToClient_;
223 };
224
225 // Note that the positive values in both of these enums are copied directly 205 // Note that the positive values in both of these enums are copied directly
226 // from chromoting_scriptable_object.h and must be kept in sync. The negative 206 // from chromoting_scriptable_object.h and must be kept in sync. The negative
227 // values represent state transitions that occur within the web-app that have 207 // values represent state transitions that occur within the web-app that have
228 // no corresponding plugin state transition. 208 // no corresponding plugin state transition.
229 /** @enum {number} */ 209 /** @enum {number} */
230 remoting.ClientSession.State = { 210 remoting.ClientSession.State = {
231 CONNECTION_CANCELED: -3, // Connection closed (gracefully) before connecting. 211 CONNECTION_CANCELED: -3, // Connection closed (gracefully) before connecting.
232 CONNECTION_DROPPED: -2, // Succeeded, but subsequently closed with an error. 212 CONNECTION_DROPPED: -2, // Succeeded, but subsequently closed with an error.
233 CREATED: -1, 213 CREATED: -1,
234 UNKNOWN: 0, 214 UNKNOWN: 0,
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 if (this.plugin_) { 556 if (this.plugin_) {
577 this.plugin_.element().removeEventListener( 557 this.plugin_.element().removeEventListener(
578 'focus', this.callPluginGotFocus_, false); 558 'focus', this.callPluginGotFocus_, false);
579 this.plugin_.element().removeEventListener( 559 this.plugin_.element().removeEventListener(
580 'blur', this.callPluginLostFocus_, false); 560 'blur', this.callPluginLostFocus_, false);
581 this.plugin_.cleanup(); 561 this.plugin_.cleanup();
582 this.plugin_ = null; 562 this.plugin_ = null;
583 } 563 }
584 564
585 // Delete event handlers that aren't relevent when not connected. 565 // Delete event handlers that aren't relevent when not connected.
566 this.resizeToClientButton_.removeEventListener(
567 'click', this.callSetScreenMode_, false);
568 this.shrinkToFitButton_.removeEventListener(
569 'click', this.callSetScreenMode_, false);
586 this.fullScreenButton_.removeEventListener( 570 this.fullScreenButton_.removeEventListener(
587 'click', this.callToggleFullScreen_, false); 571 'click', this.callToggleFullScreen_, false);
588 572
589 // Leave full-screen mode, and stop listening for related events. 573 // Leave full-screen mode, and stop listening for related events.
590 var listener = this.callOnFullScreenChanged_; 574 var listener = this.callOnFullScreenChanged_;
591 remoting.fullscreen.syncWithMaximize(false); 575 remoting.fullscreen.syncWithMaximize(false);
592 remoting.fullscreen.activate( 576 remoting.fullscreen.activate(
593 false, 577 false,
594 function() { 578 function() {
595 remoting.fullscreen.removeListener(listener); 579 remoting.fullscreen.removeListener(listener);
596 }); 580 });
597 if (remoting.windowFrame) { 581 if (remoting.windowFrame) {
598 remoting.windowFrame.setConnected(false); 582 remoting.windowFrame.setConnected(false);
599 } 583 }
600 remoting.toolbar.setClientSession(null);
601 584
602 // Remove mediasource-rendering class from video-contained - this will also 585 // Remove mediasource-rendering class from video-contained - this will also
603 // hide the <video> element. 586 // hide the <video> element.
604 /** @type {HTMLElement} */(document.getElementById('video-container')) 587 /** @type {HTMLElement} */(document.getElementById('video-container'))
605 .classList.remove('mediasource-rendering'); 588 .classList.remove('mediasource-rendering');
606 }; 589 };
607 590
608 /** 591 /**
609 * Disconnect the current session with a particular |error|. The session will 592 * Disconnect the current session with a particular |error|. The session will
610 * raise a |stateChanged| event in response to it. The caller should then call 593 * raise a |stateChanged| event in response to it. The caller should then call
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
686 this.plugin_.injectKeyEvent(keys[i], false); 669 this.plugin_.injectKeyEvent(keys[i], false);
687 } 670 }
688 } 671 }
689 672
690 /** 673 /**
691 * Sends a Ctrl-Alt-Del sequence to the remoting client. 674 * Sends a Ctrl-Alt-Del sequence to the remoting client.
692 * 675 *
693 * @return {void} Nothing. 676 * @return {void} Nothing.
694 */ 677 */
695 remoting.ClientSession.prototype.sendCtrlAltDel = function() { 678 remoting.ClientSession.prototype.sendCtrlAltDel = function() {
696 console.log('Sending Ctrl-Alt-Del.');
697 this.sendKeyCombination_([0x0700e0, 0x0700e2, 0x07004c]); 679 this.sendKeyCombination_([0x0700e0, 0x0700e2, 0x07004c]);
698 } 680 }
699 681
700 /** 682 /**
701 * Sends a Print Screen keypress to the remoting client. 683 * Sends a Print Screen keypress to the remoting client.
702 * 684 *
703 * @return {void} Nothing. 685 * @return {void} Nothing.
704 */ 686 */
705 remoting.ClientSession.prototype.sendPrintScreen = function() { 687 remoting.ClientSession.prototype.sendPrintScreen = function() {
706 console.log('Sending Print Screen.');
707 this.sendKeyCombination_([0x070046]); 688 this.sendKeyCombination_([0x070046]);
708 } 689 }
709 690
710 /** 691 /**
711 * Sets and stores the key remapping setting for the current host. 692 * Sets and stores the key remapping setting for the current host.
712 * 693 *
713 * @param {string} remappings Comma separated list of key remappings. 694 * @param {string} remappings Comma separated list of key remappings.
714 */ 695 */
715 remoting.ClientSession.prototype.setRemapKeys = function(remappings) { 696 remoting.ClientSession.prototype.setRemapKeys = function(remappings) {
716 // Cancel any existing remappings and apply the new ones. 697 // Cancel any existing remappings and apply the new ones.
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
759 '>0x' + toKey.toString(16)); 740 '>0x' + toKey.toString(16));
760 this.plugin_.remapKey(fromKey, toKey); 741 this.plugin_.remapKey(fromKey, toKey);
761 } else { 742 } else {
762 console.log('cancel remapKey 0x' + fromKey.toString(16)); 743 console.log('cancel remapKey 0x' + fromKey.toString(16));
763 this.plugin_.remapKey(fromKey, fromKey); 744 this.plugin_.remapKey(fromKey, fromKey);
764 } 745 }
765 } 746 }
766 } 747 }
767 748
768 /** 749 /**
750 * Callback for the two "screen mode" related menu items: Resize desktop to
751 * fit and Shrink to fit.
752 *
753 * @param {Event} event The click event indicating which mode was selected.
754 * @return {void} Nothing.
755 * @private
756 */
757 remoting.ClientSession.prototype.onSetScreenMode_ = function(event) {
758 var shrinkToFit = this.shrinkToFit_;
759 var resizeToClient = this.resizeToClient_;
760 if (event.target == this.shrinkToFitButton_) {
761 shrinkToFit = !shrinkToFit;
762 }
763 if (event.target == this.resizeToClientButton_) {
764 resizeToClient = !resizeToClient;
765 }
766 this.setScreenMode_(shrinkToFit, resizeToClient);
767 };
768
769 /**
769 * Set the shrink-to-fit and resize-to-client flags and save them if this is 770 * Set the shrink-to-fit and resize-to-client flags and save them if this is
770 * a Me2Me connection. 771 * a Me2Me connection.
771 * 772 *
772 * @param {boolean} shrinkToFit True if the remote desktop should be scaled 773 * @param {boolean} shrinkToFit True if the remote desktop should be scaled
773 * down if it is larger than the client window; false if scroll-bars 774 * down if it is larger than the client window; false if scroll-bars
774 * should be added in this case. 775 * should be added in this case.
775 * @param {boolean} resizeToClient True if window resizes should cause the 776 * @param {boolean} resizeToClient True if window resizes should cause the
776 * host to attempt to resize its desktop to match the client window size; 777 * host to attempt to resize its desktop to match the client window size;
777 * false to disable this behaviour for subsequent window resizes--the 778 * false to disable this behaviour for subsequent window resizes--the
778 * current host desktop size is not restored in this case. 779 * current host desktop size is not restored in this case.
779 * @return {void} Nothing. 780 * @return {void} Nothing.
781 * @private
780 */ 782 */
781 remoting.ClientSession.prototype.setScreenMode = 783 remoting.ClientSession.prototype.setScreenMode_ =
782 function(shrinkToFit, resizeToClient) { 784 function(shrinkToFit, resizeToClient) {
783 if (resizeToClient && !this.resizeToClient_) { 785 if (resizeToClient && !this.resizeToClient_) {
784 var clientArea = this.getClientArea_(); 786 var clientArea = this.getClientArea_();
785 this.plugin_.notifyClientResolution(clientArea.width, 787 this.plugin_.notifyClientResolution(clientArea.width,
786 clientArea.height, 788 clientArea.height,
787 window.devicePixelRatio); 789 window.devicePixelRatio);
788 } 790 }
789 791
790 // If enabling shrink, reset bump-scroll offsets. 792 // If enabling shrink, reset bump-scroll offsets.
791 var needsScrollReset = shrinkToFit && !this.shrinkToFit_; 793 var needsScrollReset = shrinkToFit && !this.shrinkToFit_;
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
973 this.plugin_.notifyClientResolution(clientArea.width, 975 this.plugin_.notifyClientResolution(clientArea.width,
974 clientArea.height, 976 clientArea.height,
975 window.devicePixelRatio); 977 window.devicePixelRatio);
976 } 978 }
977 // Activate full-screen related UX. 979 // Activate full-screen related UX.
978 remoting.fullscreen.addListener(this.callOnFullScreenChanged_); 980 remoting.fullscreen.addListener(this.callOnFullScreenChanged_);
979 remoting.fullscreen.syncWithMaximize(true); 981 remoting.fullscreen.syncWithMaximize(true);
980 if (remoting.windowFrame) { 982 if (remoting.windowFrame) {
981 remoting.windowFrame.setConnected(true); 983 remoting.windowFrame.setConnected(true);
982 } 984 }
983 remoting.toolbar.setClientSession(this);
984 985
985 } else if (status == remoting.ClientSession.State.FAILED) { 986 } else if (status == remoting.ClientSession.State.FAILED) {
986 switch (error) { 987 switch (error) {
987 case remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE: 988 case remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE:
988 this.error_ = remoting.Error.HOST_IS_OFFLINE; 989 this.error_ = remoting.Error.HOST_IS_OFFLINE;
989 break; 990 break;
990 case remoting.ClientSession.ConnectionError.SESSION_REJECTED: 991 case remoting.ClientSession.ConnectionError.SESSION_REJECTED:
991 this.error_ = remoting.Error.INVALID_ACCESS_CODE; 992 this.error_ = remoting.Error.INVALID_ACCESS_CODE;
992 break; 993 break;
993 case remoting.ClientSession.ConnectionError.INCOMPATIBLE_PROTOCOL: 994 case remoting.ClientSession.ConnectionError.INCOMPATIBLE_PROTOCOL:
(...skipping 512 matching lines...) Expand 10 before | Expand all | Expand 10 after
1506 * @return {{width: number, height: number}} The height of the window's client 1507 * @return {{width: number, height: number}} The height of the window's client
1507 * area. This differs between apps v1 and apps v2 due to the custom window 1508 * area. This differs between apps v1 and apps v2 due to the custom window
1508 * borders used by the latter. 1509 * borders used by the latter.
1509 * @private 1510 * @private
1510 */ 1511 */
1511 remoting.ClientSession.prototype.getClientArea_ = function() { 1512 remoting.ClientSession.prototype.getClientArea_ = function() {
1512 return remoting.windowFrame ? 1513 return remoting.windowFrame ?
1513 remoting.windowFrame.getClientArea() : 1514 remoting.windowFrame.getClientArea() :
1514 { 'width': window.innerWidth, 'height': window.innerHeight }; 1515 { 'width': window.innerWidth, 'height': window.innerHeight };
1515 } 1516 }
OLDNEW
« no previous file with comments | « trunk/src/remoting/webapp/client_screen.js ('k') | trunk/src/remoting/webapp/event_handlers.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698