1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * A class for moving clipboard items between the plugin and the OS.
10 /** @suppress {duplicate} */
11 var remoting
= remoting
|| {};
22 TEXT_TYPE
: 'text/plain',
23 TEXT_UTF8_TYPE
: 'text/plain; charset=UTF-8'
28 * @param {remoting.ClientPlugin} plugin
29 * @implements {base.Disposable}
31 remoting
.Clipboard = function(plugin
) {
32 /** @private {string} */
33 this.previousContent_
= '';
35 /** @private {boolean} */
36 this.itemFromHostTextPending_
= false;
38 /** @private {boolean} */
39 this.blockOneClipboardSend_
= true;
42 this.plugin_
= plugin
;
45 this.eventHooks_
= new base
.Disposables(
46 new base
.DomEventHook(plugin
.element(), 'focus',
47 this.initiateToHost_
.bind(this), false),
48 new base
.DomEventHook(window
, 'paste', this.onPaste_
.bind(this), false),
49 new base
.DomEventHook(window
, 'copy', this.onCopy_
.bind(this), false));
51 // Do a paste operation, but make sure the resulting clipboard data isn't sent
52 // to the host. This stops the host seeing items that were placed on the
53 // clipboard before the session began. The user may not have intended such
54 // items to be sent to the host.
55 this.initiateToHost_();
56 this.plugin_
.setClipboardHandler(this.fromHost_
.bind(this));
59 remoting
.Clipboard
.prototype.dispose = function() {
60 this.plugin_
.setClipboardHandler(base
.doNothing
);
62 base
.dispose(this.eventHooks_
);
63 this.eventHooks_
= null;
67 * Accepts a clipboard from the OS, and sends any changed clipboard items to
70 * Currently only text items are supported.
72 * @param {ClipboardData} clipboardData
73 * @return {void} Nothing.
76 remoting
.Clipboard
.prototype.toHost_ = function(clipboardData
) {
77 if (!clipboardData
|| !clipboardData
.types
|| !clipboardData
.getData
) {
78 console
.log('Got invalid clipboardData.');
81 for (var i
= 0; i
< clipboardData
.types
.length
; i
++) {
82 var type
= clipboardData
.types
[i
];
83 var item
= clipboardData
.getData(type
);
87 console
.log('Got clipboard from OS, type: ' + type
+
88 ' length: ' + item
.length
+ ' new: ' +
89 (item
!= this.previousContent_
) + ' blocking-send: ' +
90 this.blockOneClipboardSend_
);
91 // The browser presents text clipboard items as 'text/plain'.
92 if (type
== ItemTypes
.TEXT_TYPE
) {
93 // Don't send the same item more than once. Otherwise the item may be
94 // sent to and fro indefinitely.
95 if (item
!= this.previousContent_
) {
96 if (!this.blockOneClipboardSend_
) {
97 // The plugin's JSON reader emits UTF-8.
98 console
.log('Sending clipboard to host.');
99 this.plugin_
.sendClipboardItem(ItemTypes
.TEXT_UTF8_TYPE
, item
);
101 this.previousContent_
= item
;
105 this.blockOneClipboardSend_
= false;
109 * Accepts a clipboard item from the host, and stores it so that toOs() will
110 * subsequently send it to the OS clipboard.
112 * @param {string} mimeType The MIME type of the clipboard item.
113 * @param {string} item The clipboard item.
114 * @return {void} Nothing.
116 remoting
.Clipboard
.prototype.fromHost_ = function(mimeType
, item
) {
117 // The plugin's JSON layer will correctly convert only UTF-8 data sent from
119 console
.log('Got clipboard from host, type: ' + mimeType
+
120 ' length: ' + item
.length
+ ' new: ' +
121 (item
!= this.previousContent_
));
122 if (mimeType
!= ItemTypes
.TEXT_UTF8_TYPE
) {
125 if (item
== this.previousContent_
) {
128 this.previousContent_
= item
;
129 this.itemFromHostTextPending_
= true;
130 this.initiateToOs_();
134 * Moves any pending clipboard items to a ClipboardData object.
136 * @param {ClipboardData} clipboardData
137 * @return {boolean} Whether any clipboard items were moved to the ClipboardData
141 remoting
.Clipboard
.prototype.toOs_ = function(clipboardData
) {
142 if (!this.itemFromHostTextPending_
) {
143 console
.log('Got unexpected clipboard copy event.');
146 // The JSON layer between the plugin and this webapp converts UTF-8 to the
147 // JS string encoding. The browser will convert JS strings to the correct
148 // encoding, per OS and locale conventions, provided the data type is
150 console
.log('Setting OS clipboard, length: ' + this.previousContent_
.length
);
151 clipboardData
.setData(ItemTypes
.TEXT_TYPE
, this.previousContent_
);
152 this.itemFromHostTextPending_
= false;
157 * Initiates the process of sending any fresh items on the OS clipboard, to the
160 * This method makes the browser fire a paste event, which provides access to
161 * the OS clipboard. That event will be caught by a handler in the document,
162 * which will call toHost().
165 remoting
.Clipboard
.prototype.initiateToHost_ = function() {
166 // It would be cleaner to send a paste command to the plugin element,
167 // but that's not supported.
168 //console.log('Initiating clipboard paste.');
169 document
.execCommand('paste');
173 * Initiates the process of sending any items freshly received from the host,
174 * to the OS clipboard.
176 * This method makes the browser fire a copy event, which provides access to
177 * the OS clipboard. That event will be caught by a handler in the document,
178 * which will call toOs().
181 remoting
.Clipboard
.prototype.initiateToOs_ = function() {
182 // It would be cleaner to send a paste command to the plugin element,
183 // but that's not supported.
184 console
.log('Initiating clipboard copy.');
185 document
.execCommand('copy');
189 * Callback function called when the browser window gets a paste operation.
191 * @param {Event} event
192 * @return {void} Nothing.
195 remoting
.Clipboard
.prototype.onPaste_ = function(event
) {
196 if (event
&& event
.clipboardData
) {
197 this.toHost_(event
.clipboardData
);
202 * Callback function called when the browser window gets a copy operation.
204 * @param {Event} event
205 * @return {void} Nothing.
208 remoting
.Clipboard
.prototype.onCopy_ = function(event
) {
209 if (event
&& event
.clipboardData
&& this.toOs_(event
.clipboardData
)) {
210 // The default action may overwrite items that we added to clipboardData.
211 event
.preventDefault();