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
3// found in the LICENSE file.
4
5/**
6 * @fileoverview
7 * A class for moving clipboard items between the plugin and the OS.
8 */
9
10'use strict';
11
12/** @suppress {duplicate} */
13var remoting = remoting || {};
14
15/**
16 * @constructor
17 */
18remoting.Clipboard = function() {
19};
20
21/**
22 * @private
23 * @enum {string}
24 */
25remoting.Clipboard.prototype.ItemTypes = {
26  TEXT_TYPE: 'text/plain',
27  TEXT_UTF8_TYPE: 'text/plain; charset=UTF-8'
28};
29
30/**
31 * @private
32 * @type {string}
33 */
34remoting.Clipboard.prototype.previousContent = "";
35
36/**
37 * @private
38 * @type {boolean}
39 */
40remoting.Clipboard.prototype.itemFromHostTextPending = false;
41
42/**
43 * @private
44 * @type {boolean}
45 */
46remoting.Clipboard.prototype.blockOneClipboardSend_ = false;
47
48/**
49 * Notifies this object that a session has started.
50 *
51 * @return {void} Nothing.
52 */
53remoting.Clipboard.prototype.startSession = function() {
54  // Clear the store of items sent and received. Those items now relate to a
55  // previous session.
56  this.previousContent = "";
57  this.itemFromHostTextPending = false;
58
59  // Do a paste operation, but make sure the resulting clipboard data isn't sent
60  // to the host. This stops the host seeing items that were placed on the
61  // clipboard before the session began. The user may not have intended such
62  // items to be sent to the host.
63  this.blockOneClipboardSend_ = true;
64  this.initiateToHost();
65};
66
67/**
68 * Accepts a clipboard from the OS, and sends any changed clipboard items to
69 * the host.
70 *
71 * Currently only text items are supported.
72 *
73 * @param {remoting.ClipboardData} clipboardData
74 * @return {void} Nothing.
75 */
76remoting.Clipboard.prototype.toHost = function(clipboardData) {
77  if (!clipboardData || !clipboardData.types || !clipboardData.getData) {
78    console.log('Got invalid clipboardData.');
79    return;
80  }
81  if (!remoting.clientSession) {
82    return;
83  }
84  for (var i = 0; i < clipboardData.types.length; i++) {
85    var type = clipboardData.types[i];
86    var item = clipboardData.getData(type);
87    if (!item) {
88      item = "";
89    }
90    console.log('Got clipboard from OS, type: ' + type +
91                ' length: ' + item.length + ' new: ' +
92                (item != this.previousContent) + ' blocking-send: ' +
93                this.blockOneClipboardSend_);
94    // The browser presents text clipboard items as 'text/plain'.
95    if (type == this.ItemTypes.TEXT_TYPE) {
96      // Don't send the same item more than once. Otherwise the item may be
97      // sent to and fro indefinitely.
98      if (item != this.previousContent) {
99        if (!this.blockOneClipboardSend_) {
100          // The plugin's JSON reader emits UTF-8.
101          console.log('Sending clipboard to host.');
102          remoting.clientSession.sendClipboardItem(
103              this.ItemTypes.TEXT_UTF8_TYPE, item);
104        }
105        this.previousContent = item;
106      }
107    }
108  }
109  this.blockOneClipboardSend_ = false;
110};
111
112/**
113 * Accepts a clipboard item from the host, and stores it so that toOs() will
114 * subsequently send it to the OS clipboard.
115 *
116 * @param {string} mimeType The MIME type of the clipboard item.
117 * @param {string} item The clipboard item.
118 * @return {void} Nothing.
119 */
120remoting.Clipboard.prototype.fromHost = function(mimeType, item) {
121  // The plugin's JSON layer will correctly convert only UTF-8 data sent from
122  // the host.
123  console.log('Got clipboard from host, type: ' + mimeType +
124              ' length: ' + item.length + ' new: ' +
125              (item != this.previousContent));
126  if (mimeType != this.ItemTypes.TEXT_UTF8_TYPE) {
127    return;
128  }
129  if (item == this.previousContent) {
130    return;
131  }
132  this.previousContent = item;
133  this.itemFromHostTextPending = true;
134  this.initiateToOs();
135};
136
137/**
138 * Moves any pending clipboard items to a ClipboardData object.
139 *
140 * @param {remoting.ClipboardData} clipboardData
141 * @return {boolean} Whether any clipboard items were moved to the ClipboardData
142 *     object.
143 */
144remoting.Clipboard.prototype.toOs = function(clipboardData) {
145  if (!this.itemFromHostTextPending) {
146    console.log('Got unexpected clipboard copy event.');
147    return false;
148  }
149  // The JSON layer between the plugin and this webapp converts UTF-8 to the
150  // JS string encoding. The browser will convert JS strings to the correct
151  // encoding, per OS and locale conventions, provided the data type is
152  // 'text/plain'.
153  console.log('Setting OS clipboard, length: ' + this.previousContent.length);
154  clipboardData.setData(this.ItemTypes.TEXT_TYPE, this.previousContent);
155  this.itemFromHostTextPending = false;
156  return true;
157};
158
159/**
160 * Initiates the process of sending any fresh items on the OS clipboard, to the
161 * host.
162 *
163 * This method makes the browser fire a paste event, which provides access to
164 * the OS clipboard. That event will be caught by a handler in the document,
165 * which will call toHost().
166 */
167remoting.Clipboard.prototype.initiateToHost = function() {
168  // It would be cleaner to send a paste command to the plugin element,
169  // but that's not supported.
170  //console.log('Initiating clipboard paste.');
171  document.execCommand("paste");
172};
173
174/**
175 * Initiates the process of sending any items freshly received from the host,
176 * to the OS clipboard.
177 *
178 * This method makes the browser fire a copy event, which provides access to
179 * the OS clipboard. That event will be caught by a handler in the document,
180 * which will call toOs().
181 */
182remoting.Clipboard.prototype.initiateToOs = function() {
183  // It would be cleaner to send a paste command to the plugin element,
184  // but that's not supported.
185  console.log('Initiating clipboard copy.');
186  document.execCommand("copy");
187};
188
189/** @type {remoting.Clipboard} */
190remoting.clipboard = null;
191