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