leaflet: Improve handling of paste without source document

Improve on handling paste without download and
handles onerror properly, which invokes the error
handler callback (where provided) where previously
it was ignored.

Change-Id: I3b527516dc4f90484fd1cfba411b45ff948ffc53
This commit is contained in:
Ashod Nakashian 2019-07-04 23:35:57 -04:00 committed by Michael Meeks
parent 9a0bb531eb
commit b9674d51cd
3 changed files with 127 additions and 57 deletions

View file

@ -1038,6 +1038,8 @@ bool ChildSession::setClipboard(const char* buffer, int length, const std::vecto
LOG_ERR("set clipboard returned failure");
else
LOG_TRC("set clipboard succeeded");
} catch (const std::exception& ex) {
LOG_ERR("set clipboard failed with exception: " << ex.what());
} catch (...) {
LOG_ERR("set clipboard failed with exception");
}

View file

@ -2685,7 +2685,10 @@ L.TileLayer = L.GridLayer.extend({
e = e.originalEvent;
e.preventDefault();
this._map._clip.dataTransferToDocument(e.dataTransfer, /* preferInternal = */ false);
// Always capture the html content separate as we may lose it when we
// pass the clipboard data to a different context (async calls, f.e.).
var htmlText = e.dataTransfer.getData('text/html');
this._map._clip.dataTransferToDocument(e.dataTransfer, /* preferInternal = */ false, htmlText);
},
_onDragStart: function () {

View file

@ -63,12 +63,23 @@ L.Clipboard = L.Class.extend({
'&Tag=' + this._accessKey[idx];
},
getStubHtml: function() {
// Returns the marker used to identify stub messages.
_getHtmlStubMarker: function() {
return '<title>Stub HTML Message</title>';
},
// Returns true if the argument is a stub html.
_isStubHtml: function(text) {
return text.indexOf(this._getHtmlStubMarker()) > 0;
},
_getStubHtml: function() {
var lang = 'en_US'; // FIXME: l10n
var encodedOrigin = encodeURIComponent(this.getMetaPath());
var stub = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\n' +
'<html>\n' +
' <head>\n' +
' ' + this._getHtmlStubMarker() + '\n' +
' <meta http-equiv="content-type" content="text/html; charset=utf-8"/>\n' +
' <meta name="origin" content="' + encodedOrigin + '"/>\n' +
' </head>\n' +
@ -138,7 +149,8 @@ L.Clipboard = L.Class.extend({
// optionalFormData: used for POST for form data
// completeFn: called on completion - with response.
// progressFn: allows splitting the progress bar up.
_doAsyncDownload: function(type,url,optionalFormData,completeFn,progressFn) {
_doAsyncDownload: function(type,url,optionalFormData,completeFn,progressFn,onErrorFn) {
try {
var that = this;
var request = new XMLHttpRequest();
@ -152,9 +164,20 @@ L.Clipboard = L.Class.extend({
if (type === 'POST') {
that._downloadProgress._onClose();
}
// For some reason 400 error from the server doesn't
// invoke onerror callback, but we do get here with
// size==0, which signifies no response from the server.
// So we check the status code instead.
if (this.status == 200) {
completeFn(this.response);
} else if (onErrorFn) {
onErrorFn(this.response);
}
};
request.onerror = function() {
if (onErrorFn)
onErrorFn();
that._downloadProgress._onComplete();
that._downloadProgress._onClose();
};
@ -173,10 +196,14 @@ L.Clipboard = L.Class.extend({
request.send(optionalFormData);
else
request.send();
} catch (error) {
if (onErrorFn)
onErrorFn();
}
},
// Suck the data from one server to another asynchronously ...
_dataTransferDownloadAndPasteAsync: function(src, dest) {
_dataTransferDownloadAndPasteAsync: function(src, dest, fallbackHtml) {
var that = this;
// FIXME: add a timestamp in the links (?) ignroe old / un-responsive servers (?)
that._doAsyncDownload(
@ -194,7 +221,37 @@ L.Clipboard = L.Class.extend({
function(progress) { return 50 + progress/2; }
);
},
function(progress) { return progress/2; }
function(progress) { return progress/2; },
function() {
console.log('failed to download clipboard using fallback html');
// If it's the stub, avoid pasting.
if (that._isStubHtml(fallbackHtml))
{
// Let the user know they haven't really copied document content.
vex.dialog.alert({
message: _('Failed to download clipboard, please re-copy'),
callback: function () {
that._map.focus();
}
});
return;
}
var formData = new FormData();
formData.append('data', new Blob([fallbackHtml]), 'clipboard');
that._doAsyncDownload(
'POST', dest, formData,
function() {
console.log('up-load of fallback done, now paste');
that._map._socket.sendMessage('uno .uno:Paste')
},
function(progress) { return 50 + progress/2; },
function() {
that.dataTransferToDocumentFallback(null, fallbackHtml);
}
);
}
);
},
@ -220,13 +277,7 @@ L.Clipboard = L.Class.extend({
// Look for our HTML meta magic.
// cf. ClientSession.cpp /textselectioncontent:/
var pasteHtml = null;
if (dataTransfer == null) { // IE
pasteHtml = htmlText;
} else {
pasteHtml = dataTransfer.getData('text/html');
}
var meta = this._getMetaOrigin(pasteHtml);
var meta = this._getMetaOrigin(htmlText);
var id = this.getMetaPath(0);
var idOld = this.getMetaPath(1);
@ -240,26 +291,36 @@ L.Clipboard = L.Class.extend({
return;
}
var destination = this.getMetaBase() + this.getMetaPath();
// Do we have a remote Online we can suck rich data from ?
if (meta !== '')
{
console.log('Transfer between servers\n\t"' + meta + '" vs. \n\t"' + id + '"');
this._dataTransferDownloadAndPasteAsync(meta, destination);
var destination = this.getMetaBase() + this.getMetaPath();
this._dataTransferDownloadAndPasteAsync(meta, destination, htmlText);
return;
}
// Fallback.
this.dataTransferToDocumentFallback(dataTransfer, htmlText, usePasteKeyEvent);
},
dataTransferToDocumentFallback: function(dataTransfer, htmlText, usePasteKeyEvent) {
var content;
if (dataTransfer == null)
content = this._encodeHtmlToBlob(htmlText);
else // Suck HTML content out of dataTransfer now while it feels like working.
if (dataTransfer) {
// Suck HTML content out of dataTransfer now while it feels like working.
content = this._readContentSyncToBlob(dataTransfer);
}
// Fallback on the html.
if (!content) {
content = this._encodeHtmlToBlob(htmlText);
}
// FIXME: do we want this section ?
// Images get a look in only if we have no content and are async
if (content == null && pasteHtml === '' && dataTransfer != null)
if (content == null && htmlText === '' && dataTransfer != null)
{
var types = dataTransfer.types;
@ -291,6 +352,7 @@ L.Clipboard = L.Class.extend({
formData.append('file', content);
var that = this;
var destination = this.getMetaBase() + this.getMetaPath();
this._doAsyncDownload('POST', destination, formData,
function() {
console.log('Posted ' + content.size + ' bytes successfully');
@ -326,14 +388,14 @@ L.Clipboard = L.Class.extend({
else
{
console.log('Downloaded that selection.');
text = this.getStubHtml();
text = this._getStubHtml();
this._onDownloadOnLargeCopyPaste();
this._downloadProgress.setURI( // richer, bigger HTML ...
this.getMetaBase() + this.getMetaPath() + '&MimeType=text/html');
}
} else if (this._selectionType === null) {
console.log('Copy/Cut with no selection!');
text = this.getStubHtml();
text = this._getStubHtml();
} else {
console.log('Copy/Cut with simple text selection');
text = this._selectionContent;
@ -400,7 +462,7 @@ L.Clipboard = L.Class.extend({
// Can't get HTML until it is pasted ... so quick timeout
setTimeout(function() {
var tmpDiv = document.getElementById(that._dummyDivName);
that.dataTransferToDocument(null, false, tmpDiv.innerHTML);
that.dataTransferToDocument(null, /* preferInternal = */ false, tmpDiv.innerHTML);
that.compatRemoveNode(tmpDiv);
// attempt to restore focus.
if (active == null)
@ -553,8 +615,11 @@ L.Clipboard = L.Class.extend({
if (ev.clipboardData) { // Standard
ev.preventDefault();
var usePasteKeyEvent = ev.usePasteKeyEvent;
this.dataTransferToDocument(ev.clipboardData, /* preferInternal = */ true, null, usePasteKeyEvent);
this._map._clipboardContainer._abortComposition();
// Always capture the html content separate as we may lose it when we
// pass the clipboard data to a different context (async calls, f.e.).
var htmlText = ev.clipboardData.getData('text/html');
this.dataTransferToDocument(ev.clipboardData, /* preferInternal = */ true, htmlText, usePasteKeyEvent);
this._map._clipboardContainer._abortComposition(ev);
this._clipboardSerial++;
this._stopHideDownload();
}