Rebased ref, commits from common ancestor:
commit 2d956b4c3719699365630f16efec84e22c9a0cd0
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Wed May 29 13:11:01 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Wed May 29 15:10:47 2019 +0100

    Add pasteresult: message to allow re-try with a different format.
    
    Change-Id: Ia3d7aacdcf2b819437eb2da68a79dd99ed7dca42

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 91a3ad296..f4387d60d 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -997,15 +997,19 @@ bool ChildSession::paste(const char* buffer, int length, 
const std::vector<std::
         return false;
     }
 
-    if (mimeType.find("") == 0)
+    bool fallback = false;
+    if (mimeType.find("application/x-openoffice-embed-source-xml") == 0)
     {
         LOG_TRC("Re-writing garbled mime-type " << mimeType);
         mimeType = 
"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed 
Source (XML)\"";
+        fallback = true;
     }
 
     const std::string firstLine = getFirstLine(buffer, length);
     const char* data = buffer + firstLine.size() + 1;
     const int size = length - firstLine.size() - 1;
+    bool success = false;
+    std::string result = "pasteresult: ";
     if (size > 0)
     {
         std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
@@ -1013,9 +1017,17 @@ bool ChildSession::paste(const char* buffer, int length, 
const std::vector<std::
         getLOKitDocument()->setView(_viewId);
 
         LOG_TRC("Paste data of size " << size << " bytes and hash " << 
SpookyHash::Hash64(data, size, 0));
-        if (!getLOKitDocument()->paste(mimeType.c_str(), data, size))
+        success = getLOKitDocument()->paste(mimeType.c_str(), data, size);
+        if (!success)
             LOG_WRN("Paste failed " << getLOKitLastError());
     }
+    if (success)
+        result += "success";
+    else if (fallback)
+        result += "fallback";
+    else
+        result += "fail";
+    sendTextFrame(result);
 
     return true;
 }
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index b9a23067a..71705148a 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -493,6 +493,9 @@ L.TileLayer = L.GridLayer.extend({
                else if (textMsg.startsWith('editor:')) {
                        this._updateEditor(textMsg);
                }
+               else if (textMsg.startsWith('pasteresult:')) {
+                       this._pasteResult(textMsg);
+               }
                else if (textMsg.startsWith('validitylistbutton:')) {
                        this._onValidityListButtonMsg(textMsg);
                }
@@ -2493,6 +2496,23 @@ L.TileLayer = L.GridLayer.extend({
                return '';
        },
 
+       // Sometimes our smart-paste fails & we need to try again.
+       _pasteResult : function(textMsg)
+       {
+               textMsg = textMsg.substring('pasteresult:'.length + 1);
+               console.log('Paste state: ' + textMsg);
+               if (textMsg == 'fallback') {
+                       if (this._pasteFallback != null) {
+                               console.log('Paste failed- falling back to 
HTML');
+                               
this._map._socket.sendMessage(this._pasteFallback);
+                       } else {
+                               console.log('No paste fallback present.');
+                       }
+               }
+
+               this._pasteFallback = null;
+       },
+
        _dataTransferToDocument: function (dataTransfer, preferInternal) {
 
                // Look for our HTML meta magic.
@@ -2511,6 +2531,9 @@ L.TileLayer = L.GridLayer.extend({
                        return;
                }
 
+               console.log('Resetting paste fallback');
+               this._pasteFallback = null;
+
                // Suck HTML content out of dataTransfer now while it feels 
like working.
                var content = this._readContentSync(dataTransfer);
 
@@ -2541,10 +2564,12 @@ L.TileLayer = L.GridLayer.extend({
                        return;
                }
 
-               // Try Fetch the data directly ourselves instead.
-               if (meta != '') {
+               if (meta != '') { // in Smart server side paste mode ...
                        // FIXME: really should short-circuit on the server.
                        console.log('Doing async paste of data from remote 
origin\n\t"' + meta + '" is not\n\t"' + id + '"');
+
+                       this._pasteFallback = content;
+
                        var tilelayer = this;
                        var oReq = new XMLHttpRequest();
                        oReq.onload = function(e) {
@@ -2556,18 +2581,24 @@ L.TileLayer = L.GridLayer.extend({
                                } else {
                                        console.log('Error code ' + oReq.status 
+ ' fetching from URL "' + meta + '": ' + e + ' falling back to local.');
                                        
tilelayer._map._socket.sendMessage(content);
+                                       this._pasteFallback = null;
                                }
                        }
                        oReq.onerror = function(e) {
                                console.log('Error fetching from URL "' + meta 
+ '": ' + e + ' falling back to local.');
                                tilelayer._map._socket.sendMessage(content);
+                               this._pasteFallback = null;
                        };
                        oReq.open('GET', meta);
                        oReq.responseType = 'arraybuffer';
                        oReq.send();
                        // user abort - if they do stops paste.
+               } else if (content != null) {
+                       console.log('Normal HTML, so smart paste not possible');
+                       tilelayer._map._socket.sendMessage(content);
+                       this._pasteFallback = null;
                } else {
-                       console.log('Received a paste but nothing on the 
clipboard');
+                       console.log('Nothing we can paste on the clipboard');
                }
        },
 
@@ -2575,9 +2606,9 @@ L.TileLayer = L.GridLayer.extend({
                // Try various content mime types
                var mimeTypes;
                if (this._docType === 'spreadsheet') {
-                       // FIXME apparently we cannot paste the text/html or 
text/rtf as
-                       // produced by LibreOffice in Calc from some reason
                        mimeTypes = [
+                               ['text/rtf', 'text/rtf'],
+                               ['text/html', 'text/html'],
                                ['text/plain', 'text/plain;charset=utf-8'],
                                ['Text', 'text/plain;charset=utf-8']
                        ];
commit ce3f672b0675b4dfb00d9f0c4d73d0fd24850ba5
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Wed May 29 13:10:44 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Wed May 29 13:10:44 2019 +0100

    Improve debugging by showing un-masked websocket content.
    
    Change-Id: I0df0b3cc7b13c36ed8afaaec4ed2fe525458a21c

diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 843ca6b37..85d9a2d7f 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -351,7 +351,8 @@ public:
 
         LOG_TRC("#" << socket->getFD() << ": Incoming WebSocket frame code " 
<< static_cast<unsigned>(code) <<
                 ", fin? " << fin << ", mask? " << hasMask << ", payload 
length: " << payloadLen <<
-                ", residual socket data: " << socket->getInBuffer().size() << 
" bytes.");
+                ", residual socket data: " << socket->getInBuffer().size() << 
" bytes, unmasked data: "+
+                Util::stringifyHexLine(_wsPayload, 0, std::min((size_t)32, 
_wsPayload.size())));
 
         if (fin)
         {
commit 47daa9da89d7a4a4f37083bbfb6183a8bcc91ca3
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Tue May 28 11:42:40 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Tue May 28 11:42:40 2019 +0100

    End to end hashing.
    
    Change-Id: Iac4423114111ced3ad45bc8dd58b24b584d0941d

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index a55081396..91a3ad296 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -975,7 +975,8 @@ bool ChildSession::getBinarySelection(const char* 
/*buffer*/, int /*length*/, co
     if (binSelection)
         std::memcpy(output.data() + header.size(), binSelection, binSize);
 
-    LOG_TRC("Sending binaryselectioncontent (" << binSize << " bytes) in: " <<
+    LOG_TRC("Sending binaryselectioncontent (" << binSize << " bytes) and hash 
" <<
+            SpookyHash::Hash64(binSelection, binSize, 0) << " in: " <<
             mimeType << " out: " << (mimeTypeOut ? mimeTypeOut : ""));
     sendBinaryFrame(output.data(), output.size());
     if (binSelection)
@@ -1011,6 +1012,7 @@ bool ChildSession::paste(const char* buffer, int length, 
const std::vector<std::
 
         getLOKitDocument()->setView(_viewId);
 
+        LOG_TRC("Paste data of size " << size << " bytes and hash " << 
SpookyHash::Hash64(data, size, 0));
         if (!getLOKitDocument()->paste(mimeType.c_str(), data, size))
             LOG_WRN("Paste failed " << getLOKitLastError());
     }
commit eac30d86883d4f3d1dab622c1e2fb49b9c773601
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Tue May 28 03:58:22 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Tue May 28 07:20:20 2019 +0100

    Async socket feeding.
    
    Change-Id: Ifbc860593dbcd904c213862c14bc965100de7022

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 11b552e62..a55081396 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -268,6 +268,7 @@ bool ChildSession::_handleInput(const char *buffer, int 
length)
                tokens[0] == "downloadas" ||
                tokens[0] == "getchildid" ||
                tokens[0] == "gettextselection" ||
+               tokens[0] == "getbinaryselection" ||
                tokens[0] == "paste" ||
                tokens[0] == "insertfile" ||
                tokens[0] == "key" ||
@@ -310,10 +311,14 @@ bool ChildSession::_handleInput(const char *buffer, int 
length)
         {
             return getChildId();
         }
-        else if (tokens[0] == "gettextselection")
+        else if (tokens[0] == "gettextselection") // deprecated.
         {
             return getTextSelection(buffer, length, tokens);
         }
+        else if (tokens[0] == "getbinaryselection")
+        {
+            return getBinarySelection(buffer, length, tokens);
+        }
         else if (tokens[0] == "paste")
         {
             return paste(buffer, length, tokens);
@@ -938,6 +943,49 @@ bool ChildSession::getTextSelection(const char* 
/*buffer*/, int /*length*/, cons
     return true;
 }
 
+bool ChildSession::getBinarySelection(const char* /*buffer*/, int /*length*/, 
const std::vector<std::string>& tokens)
+{
+    std::string mimeType;
+
+    if (tokens.size() != 2 ||
+        !getTokenString(tokens[1], "mimetype", mimeType))
+    {
+        sendTextFrame("error: cmd=getbinaryselection kind=syntax");
+        return false;
+    }
+
+    size_t binSize = 0;
+    char* binSelection = nullptr;
+    char *mimeTypeOut = nullptr;
+    {
+        std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
+        getLOKitDocument()->setView(_viewId);
+
+        binSelection = 
getLOKitDocument()->getBinarySelection(mimeType.c_str(), &binSize, 
&mimeTypeOut);
+    }
+
+    std::string header("binaryselectioncontent: mimetype=");
+    if (mimeTypeOut)
+        header += mimeTypeOut;
+    header += "\n";
+
+    std::vector<char> output;
+    output.resize(header.size() + binSize);
+    std::memcpy(output.data(), header.c_str(), header.size());
+    if (binSelection)
+        std::memcpy(output.data() + header.size(), binSelection, binSize);
+
+    LOG_TRC("Sending binaryselectioncontent (" << binSize << " bytes) in: " <<
+            mimeType << " out: " << (mimeTypeOut ? mimeTypeOut : ""));
+    sendBinaryFrame(output.data(), output.size());
+    if (binSelection)
+        free(binSelection);
+    if (mimeTypeOut)
+        free (mimeTypeOut);
+
+    return true;
+}
+
 bool ChildSession::paste(const char* buffer, int length, const 
std::vector<std::string>& tokens)
 {
     std::string mimeType;
@@ -948,6 +996,12 @@ bool ChildSession::paste(const char* buffer, int length, 
const std::vector<std::
         return false;
     }
 
+    if (mimeType.find("") == 0)
+    {
+        LOG_TRC("Re-writing garbled mime-type " << mimeType);
+        mimeType = 
"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed 
Source (XML)\"";
+    }
+
     const std::string firstLine = getFirstLine(buffer, length);
     const char* data = buffer + firstLine.size() + 1;
     const int size = length - firstLine.size() - 1;
@@ -957,7 +1011,8 @@ bool ChildSession::paste(const char* buffer, int length, 
const std::vector<std::
 
         getLOKitDocument()->setView(_viewId);
 
-        getLOKitDocument()->paste(mimeType.c_str(), data, size);
+        if (!getLOKitDocument()->paste(mimeType.c_str(), data, size))
+            LOG_WRN("Paste failed " << getLOKitLastError());
     }
 
     return true;
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 8b1bc7ff1..2f8a8b2ce 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -253,6 +253,7 @@ private:
     bool downloadAs(const char* buffer, int length, const 
std::vector<std::string>& tokens);
     bool getChildId();
     bool getTextSelection(const char* buffer, int length, const 
std::vector<std::string>& tokens);
+    bool getBinarySelection(const char* buffer, int length, const 
std::vector<std::string>& tokens);
     std::string getTextSelectionInternal(const std::string& mimeType);
     bool paste(const char* buffer, int length, const std::vector<std::string>& 
tokens);
     bool insertFile(const char* buffer, int length, const 
std::vector<std::string>& tokens);
@@ -289,6 +290,18 @@ private:
         return _docManager.getLOKitDocument();
     }
 
+    std::string getLOKitLastError()
+    {
+        char *lastErr = _docManager.getLOKit()->getError();
+        std::string ret;
+        if (lastErr)
+        {
+            ret = std::string(lastErr, strlen(lastErr));
+            free (lastErr);
+        }
+        return ret;
+    }
+
 private:
     const std::string _jailId;
     DocumentManagerInterface& _docManager;
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index e3d6cd7b5..b9a23067a 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -2550,10 +2550,9 @@ L.TileLayer = L.GridLayer.extend({
                        oReq.onload = function(e) {
                                var arraybuffer = oReq.response;
                                if (oReq.status == 200) { // OK
-//                                     var blob = new Blob(['paste 
mimetype=application/x-openoffice-embed-source-xml;windows_formatname="Star 
Embed Source (XML)"\n', arraybuffer]);
-                                       var blob = new Blob(['paste 
mimetype=text/plain\n', arraybuffer]);
+                                       var blob = new Blob(['paste 
mimetype=application/x-openoffice-embed-source-xml;windows_formatname="Star 
Embed Source (XML)"\n', arraybuffer]);
                                        
tilelayer._map._socket.sendMessage(blob);
-                                       console.log('Sent paste blob message');
+                                       console.log('Sent paste blob message of 
size ' + arraybuffer.byteLength);
                                } else {
                                        console.log('Error code ' + oReq.status 
+ ' fetching from URL "' + meta + '": ' + e + ' falling back to local.');
                                        
tilelayer._map._socket.sendMessage(content);
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 738521d43..b393e4d0c 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -87,7 +87,8 @@ std::string ClientSession::getURIAndUpdateClipboardHash()
     {
         std::unique_lock<std::mutex> lock(SessionMapMutex);
         _clipboardKey = hash;
-    }
+        LOG_TRC("Clipboard key on [" << getId() << "] set to " << 
_clipboardKey);
+   }
 
     std::string encodedFrom;
     Poco::URI wopiSrc = getDocumentBroker()->getPublicUri();
@@ -117,11 +118,34 @@ std::shared_ptr<ClientSession> 
ClientSession::getByClipboardHash(std::string &ke
     {
         auto session = it.second.lock();
         if (session && session->_clipboardKey == key)
-            return session;
+        {
+            if (session->_wopiFileInfo &&
+                session->_wopiFileInfo->getDisableCopy())
+            {
+                LOG_WRN("Attempting copy from disabled session " << key);
+                break;
+            }
+            else
+                return session;
+        }
     }
     return std::shared_ptr<ClientSession>();
 }
 
+void ClientSession::addClipboardSocket(const std::shared_ptr<StreamSocket> 
&socket)
+{
+    // Move the socket into our DocBroker.
+    auto docBroker = getDocumentBroker();
+    docBroker->addSocketToPoll(socket);
+
+    LOG_TRC("Session [" << getId() << "] sending getbinaryselection");
+    const std::string gettext = "getbinaryselection 
mimetype=application/x-openoffice-embed-source-xml";
+    docBroker->forwardToChild(getId(), gettext);
+
+    // TESTME: onerror / socket cleanup.
+    _clipSockets.push_back(socket);
+}
+
 void ClientSession::handleIncomingMessage(SocketDisposition &disposition)
 {
     // LOG_TRC("***** ClientSession::handleIncomingMessage()");
@@ -1036,6 +1060,7 @@ bool ClientSession::handleKitToClientMessage(const char* 
buffer, const int lengt
             }
         }
     } else if (tokens[0] == "textselectioncontent:") {
+
         // Insert our meta origin if we can
         payload->rewriteDataBody([=](std::vector<char>& data) {
                 size_t pos = Util::findInVector(data, "<meta 
name=\"generator\" content=\"");
@@ -1055,6 +1080,39 @@ bool ClientSession::handleKitToClientMessage(const char* 
buffer, const int lengt
                 }
             });
         return forwardToClient(payload);
+
+    } else if (tokens[0] == "binaryselectioncontent:") {
+
+        // for now just for remote sockets.
+        LOG_TRC("Got binary selection content to send to " << 
_clipSockets.size() << "sockets");
+        size_t header;
+        for (header = 0; header < payload->size();)
+            if (payload->data()[header++] == '\n')
+                break;
+        if (header < payload->size())
+        {
+            for (auto it : _clipSockets)
+            {
+                std::ostringstream oss;
+                oss << "HTTP/1.1 200 OK\r\n"
+                    << "Last-Modified: " << 
Poco::DateTimeFormatter::format(Poco::Timestamp(), 
Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+                    << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                    << "Content-Length: " << (payload->size() - header) << 
"\r\n"
+                    << "Content-Type: application/octet-stream\r\n"
+                    << "X-Content-Type-Options: nosniff\r\n"
+                    << "\r\n";
+                oss.write(&payload->data()[header], payload->size() - header);
+                auto socket = it.lock();
+                socket->setSocketBufferSize(std::min(payload->size() + 256,
+                                                     
size_t(Socket::MaximumSendBufferSize)));
+                socket->send(oss.str());
+                socket->shutdown();
+                LOG_INF("Sent clipboard content.");
+            }
+        }
+        else
+            LOG_DBG("Odd - no binary selection content");
+        _clipSockets.clear();
     }
 
     if (!isDocPasswordProtected())
@@ -1372,7 +1430,8 @@ void ClientSession::dumpState(std::ostream& os)
        << "\n\t\tkit ViewId: " << _kitViewId
        << "\n\t\thost (un-trusted): " << _hostNoTrust
        << "\n\t\tisTextDocument: " << _isTextDocument
-       << "\n\t\tclipboardKey: " << _clipboardKey;
+       << "\n\t\tclipboardKey: " << _clipboardKey
+       << "\n\t\tclip sockets: " << _clipSockets.size();
 
     std::shared_ptr<StreamSocket> socket = getSocket().lock();
     if (socket)
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 61b3c037c..f8993f825 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -140,6 +140,8 @@ public:
     /// Find clipboard for session
     static std::shared_ptr<ClientSession> getByClipboardHash(std::string &key);
 
+    void addClipboardSocket(const std::shared_ptr<StreamSocket> &socket);
+
 private:
 
     /// Create URI for transient clipboard content.
@@ -249,6 +251,9 @@ private:
 
     /// Store wireID's of the sent tiles inside the actual visible area
     std::map<std::string, TileWireId> _oldWireIds;
+
+    /// Sockets to send binary selection content to
+    std::vector<std::weak_ptr<StreamSocket>> _clipSockets;
 };
 
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index c4048b468..599a6f2c0 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2158,7 +2158,7 @@ private:
                 if (request.getMethod() == HTTPRequest::HTTP_GET &&
                     reqPathTokens.count() > 0 && reqPathTokens[0] == 
"clipboard")
                 {
-                    handleClipboardRequest(request);
+                    handleClipboardRequest(request, disposition);
                 }
                 else if (!(request.find("Upgrade") != request.end() && 
Poco::icompare(request["Upgrade"], "websocket") == 0) &&
                     reqPathTokens.count() > 0 && reqPathTokens[0] == "lool")
@@ -2324,7 +2324,7 @@ private:
         LOG_INF("Sent capabilities.json successfully.");
     }
 
-    void handleClipboardRequest(const Poco::Net::HTTPRequest& request)
+    void handleClipboardRequest(const Poco::Net::HTTPRequest& request, 
SocketDisposition &disposition)
     {
         LOG_DBG("Clipboard request: " << request.getURI());
 
@@ -2349,26 +2349,23 @@ private:
         {
             // Do things in the right thread.
             auto docBroker = session->getDocumentBroker();
-            docBroker->addCallback([=](){
-                    ;
-                });
 
-            std::string result = "Hello world !\n";
+            disposition.setMove([docBroker, session]
+                                (const std::shared_ptr<Socket> &moveSocket)
+                {
+                    // Perform all of this after removing the socket
 
-            std::ostringstream oss;
-            oss << "HTTP/1.1 200 OK\r\n"
-                << "Last-Modified: " << 
Poco::DateTimeFormatter::format(Poco::Timestamp(), 
Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
-                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
-                << "Content-Length: " << result.size() << "\r\n"
-                << "Content-Type: application/octet-stream\r\n"
-                << "X-Content-Type-Options: nosniff\r\n"
-                << "\r\n"
-                << result;
+                    // We no longer own this socket.
+                    moveSocket->setThreadOwner(std::thread::id(0));
+
+                    docBroker->addCallback([docBroker, moveSocket, session]()
+                        {
+                            auto streamSocket = 
std::static_pointer_cast<StreamSocket>(moveSocket);
+                            session->addClipboardSocket(streamSocket);
+                        });
+                });
+            LOG_TRC("queued binary clipboard content fetch");
 
-            auto socket = _socket.lock();
-            socket->send(oss.str());
-            socket->shutdown();
-            LOG_INF("Sent clipboard content successfully.");
         } else {
             LOG_ERR("Invalid clipboard request: " << serverId << " with tag " 
<< tag);
 
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index 865d3fd1b..a03969391 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -53,6 +53,10 @@ gettextselection mimetype=<mimeType>
 
     Request selection's content
 
+getbinaryselection mimetype=<mimeType>
+
+    Request selection's content
+
 paste mimetype=<mimeType>
 <binaryPasteData>
 
@@ -390,10 +394,12 @@ statechanged: <key>=<value>
     Notifies client of state changed events of <key>.
     Eg: 'statechanged: .uno:Undo=enabled'
 
-textselectioncontent: <content>
-
+textselectioncontent:
     Current selection's content
 
+binaryselectioncontent mimetype=<type>
+<binary selection content>
+
 tile: part=<partNumber> width=<width> height=<height> tileposx=<xpos> 
tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight> 
[timestamp=<time>] [renderid=<id>] [wid=<wireId>]
 <binaryPngImage>
 
commit c2067a9174748285736549c45276ac741a0fcda6
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Tue May 28 02:00:46 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Tue May 28 02:26:47 2019 +0100

    Start of clipboard server end-point ...
    
    Change-Id: I6da502cc1aacef450d612696abcc38f8d8d8873a

diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 697ab6040..e3d6cd7b5 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -2483,9 +2483,11 @@ L.TileLayer = L.GridLayer.extend({
                var meta = html.substring(start + match.length, end);
 
                // quick sanity checks that it one of ours.
-               if (meta.indexOf('/clipboard/') > 0 &&
-                   meta.indexOf('&amp;tag=') > 0)
-                       return meta;
+               if (meta.indexOf('%2Fclipboard%3FWOPISrc%3D') > 0 &&
+                   meta.indexOf('%26ServerId%3D') > 0 &&
+                   meta.indexOf('%26ViewId%3D') > 0 &&
+                   meta.indexOf('%26Tag%3D') > 0)
+                       return decodeURIComponent(meta);
                else
                        console.log('Mis-understood foreign origin: "' + meta + 
'"');
                return '';
@@ -2498,8 +2500,8 @@ L.TileLayer = L.GridLayer.extend({
                var pasteHtml = dataTransfer.getData('text/html');
                var meta = this._getMetaOrigin(pasteHtml);
                var id = this._map.options.webserver + 
this._map.options.serviceRoot +
-                   '/clipboard/' + this._map._socket.WSDServer.Id + '/' + 
this._viewId +
-                   '?WOPISrc=' + encodeURIComponent(this._map.options.doc);
+                   '/clipboard?WOPISrc='+ 
encodeURIComponent(this._map.options.doc) +
+                   '&ServerId=' + this._map._socket.WSDServer.Id + '&ViewId=' 
+ this._viewId;
 
                // for the paste, we might prefer the internal LOK's copy/paste
                if (meta.startsWith(id) && preferInternal === true) {
@@ -2545,10 +2547,11 @@ L.TileLayer = L.GridLayer.extend({
                        console.log('Doing async paste of data from remote 
origin\n\t"' + meta + '" is not\n\t"' + id + '"');
                        var tilelayer = this;
                        var oReq = new XMLHttpRequest();
-                       oReq.onload = function() {
+                       oReq.onload = function(e) {
                                var arraybuffer = oReq.response;
                                if (oReq.status == 200) { // OK
-                                       var blob = new Blob(['paste 
mimetype=application/x-openoffice-embed-source-xml;windows_formatname="Star 
Embed Source (XML)"\n', arraybuffer]);
+//                                     var blob = new Blob(['paste 
mimetype=application/x-openoffice-embed-source-xml;windows_formatname="Star 
Embed Source (XML)"\n', arraybuffer]);
+                                       var blob = new Blob(['paste 
mimetype=text/plain\n', arraybuffer]);
                                        
tilelayer._map._socket.sendMessage(blob);
                                        console.log('Sent paste blob message');
                                } else {
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 899eae49f..738521d43 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -97,9 +97,16 @@ std::string ClientSession::getURIAndUpdateClipboardHash()
     Poco::URI::encode(wopiSrc.toString(), encodeChars, encodedFrom);
 
     std::string proto = (LOOLWSD::isSSLEnabled() || 
LOOLWSD::isSSLTermination()) ? "https://"; : "http://";;
-    std::string meta = proto + _hostNoTrust + "/clipboard/" + 
LOOLWSD::HostIdentifier +
-        "/" + std::to_string(getKitViewId()) + "?WOPISrc=" + encodedFrom + 
"&amp;tag=" + hash;
-    return meta;
+    std::string meta = proto + _hostNoTrust +
+        "/clipboard?WOPISrc=" + encodedFrom +
+        "&ServerId=" + LOOLWSD::HostIdentifier +
+        "&ViewId=" + std::to_string(getKitViewId()) +
+        "&Tag=" + hash;
+
+    std::string metaEncoded;
+    Poco::URI::encode(meta, encodeChars, metaEncoded);
+
+    return metaEncoded;
 }
 
 // called infrequently
@@ -1357,7 +1364,15 @@ void ClientSession::dumpState(std::ostream& os)
     os << "\t\tisReadOnly: " << isReadOnly()
        << "\n\t\tisDocumentOwner: " << _isDocumentOwner
        << "\n\t\tisAttached: " << _isAttached
-       << "\n\t\tkeyEvents: " << _keyEvents;
+       << "\n\t\tkeyEvents: " << _keyEvents
+//       << "\n\t\tvisibleArea: " << _clientVisibleArea
+       << "\n\t\tclientSelectedPart: " << _clientSelectedPart
+       << "\n\t\ttile size Pixel: " << _tileWidthPixel << "x" << 
_tileHeightPixel
+       << "\n\t\ttile size Twips: " << _tileWidthTwips << "x" << 
_tileHeightTwips
+       << "\n\t\tkit ViewId: " << _kitViewId
+       << "\n\t\thost (un-trusted): " << _hostNoTrust
+       << "\n\t\tisTextDocument: " << _isTextDocument
+       << "\n\t\tclipboardKey: " << _clipboardKey;
 
     std::shared_ptr<StreamSocket> socket = getSocket().lock();
     if (socket)
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index f9dafd373..c4048b468 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2154,7 +2154,13 @@ private:
             else
             {
                 StringTokenizer reqPathTokens(request.getURI(), "/?", 
StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-                if (!(request.find("Upgrade") != request.end() && 
Poco::icompare(request["Upgrade"], "websocket") == 0) &&
+
+                if (request.getMethod() == HTTPRequest::HTTP_GET &&
+                    reqPathTokens.count() > 0 && reqPathTokens[0] == 
"clipboard")
+                {
+                    handleClipboardRequest(request);
+                }
+                else if (!(request.find("Upgrade") != request.end() && 
Poco::icompare(request["Upgrade"], "websocket") == 0) &&
                     reqPathTokens.count() > 0 && reqPathTokens[0] == "lool")
                 {
                     // All post requests have url prefix 'lool'.
@@ -2318,6 +2324,74 @@ private:
         LOG_INF("Sent capabilities.json successfully.");
     }
 
+    void handleClipboardRequest(const Poco::Net::HTTPRequest& request)
+    {
+        LOG_DBG("Clipboard request: " << request.getURI());
+
+        Poco::URI requestUri(request.getURI());
+        Poco::URI::QueryParameters params = requestUri.getQueryParameters();
+        std::string serverId, viewId, tag;
+        for (auto it : params)
+        {
+            if (it.first == "ServerId")
+                serverId = it.second;
+            else if (it.first == "ViewId")
+                viewId = it.second;
+            else if (it.first == "Tag")
+                tag = it.second;
+        }
+        LOG_TRC("Clipboard request for us: " << serverId << " with tag " << 
tag);
+
+        // TODO: check WOPISrc too ...
+        std::shared_ptr<ClientSession> session;
+        if (serverId == LOOLWSD::HostIdentifier &&
+            (session = ClientSession::getByClipboardHash(tag)))
+        {
+            // Do things in the right thread.
+            auto docBroker = session->getDocumentBroker();
+            docBroker->addCallback([=](){
+                    ;
+                });
+
+            std::string result = "Hello world !\n";
+
+            std::ostringstream oss;
+            oss << "HTTP/1.1 200 OK\r\n"
+                << "Last-Modified: " << 
Poco::DateTimeFormatter::format(Poco::Timestamp(), 
Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+                << "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+                << "Content-Length: " << result.size() << "\r\n"
+                << "Content-Type: application/octet-stream\r\n"
+                << "X-Content-Type-Options: nosniff\r\n"
+                << "\r\n"
+                << result;
+
+            auto socket = _socket.lock();
+            socket->send(oss.str());
+            socket->shutdown();
+            LOG_INF("Sent clipboard content successfully.");
+        } else {
+            LOG_ERR("Invalid clipboard request: " << serverId << " with tag " 
<< tag);
+
+            std::string message;
+            if (serverId != LOOLWSD::HostIdentifier)
+                message = "Cluster configuration error: mis-matching serverid 
" + serverId + " vs. " + LOOLWSD::HostIdentifier;
+            else
+                message = "Empty clipboard item / session tag " + tag;
+
+            // Bad request.
+            std::ostringstream oss;
+            oss << "HTTP/1.1 400\r\n"
+                << "Date: " << 
Poco::DateTimeFormatter::format(Poco::Timestamp(), 
Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n"
+                << "User-Agent: LOOLWSD WOPI Agent\r\n"
+                << "Content-Length: 0\r\n"
+                << "\r\n"
+                << message;
+            auto socket = _socket.lock();
+            socket->send(oss.str());
+            socket->shutdown();
+        }
+    }
+
     void handleRobotsTxtRequest(const Poco::Net::HTTPRequest& request)
     {
         LOG_DBG("HTTP request: " << request.getURI());
commit d8195aae3c5008dbfaa6b411ae8f83a92092e02d
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Mon May 27 21:22:06 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Tue May 28 02:26:47 2019 +0100

    First cut of remote clipboard access fun.
    
    Change-Id: I02e40896753e40ad823beafe1f9da739e3023f43

diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 6916ab775..697ab6040 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -2480,46 +2480,97 @@ L.TileLayer = L.GridLayer.extend({
                if (end < 0) {
                        return '';
                }
-               return html.substring(start + match.length, end);
+               var meta = html.substring(start + match.length, end);
+
+               // quick sanity checks that it one of ours.
+               if (meta.indexOf('/clipboard/') > 0 &&
+                   meta.indexOf('&amp;tag=') > 0)
+                       return meta;
+               else
+                       console.log('Mis-understood foreign origin: "' + meta + 
'"');
+               return '';
        },
 
        _dataTransferToDocument: function (dataTransfer, preferInternal) {
+
+               // Look for our HTML meta magic.
+               //   cf. ClientSession.cpp /textselectioncontent:/
+               var pasteHtml = dataTransfer.getData('text/html');
+               var meta = this._getMetaOrigin(pasteHtml);
+               var id = this._map.options.webserver + 
this._map.options.serviceRoot +
+                   '/clipboard/' + this._map._socket.WSDServer.Id + '/' + 
this._viewId +
+                   '?WOPISrc=' + encodeURIComponent(this._map.options.doc);
+
                // for the paste, we might prefer the internal LOK's copy/paste
-               if (preferInternal === true) {
-                       var pasteHtml = dataTransfer.getData('text/html');
-                       var meta = this._getMetaOrigin(pasteHtml);
-                       var id = 'https://transient/' + 
this._map._socket.WSDServer.Id + '/' + this._viewId +
-                           '?WOPISrc=' + 
encodeURIComponent(this._map.options.doc);
-                       // cf. ClientSession.cpp /textselectioncontent:/
-                       if (meta == id) {
-                               // Home from home: short-circuit internally.
-                               this._map._socket.sendMessage('uno .uno:Paste');
-                               return;
-                       } else
-                               console.log('Unusual origin mismatch on paste 
between:\n\t"' +
-                                           meta + '" and\n\t"' + id + '"');
+               if (meta.startsWith(id) && preferInternal === true) {
+                       // Home from home: short-circuit internally.
+                       console.log('short-circuit, internal paste');
+                       this._map._socket.sendMessage('uno .uno:Paste');
+                       return;
                }
 
-               var types = dataTransfer.types;
+               // Suck HTML content out of dataTransfer now while it feels 
like working.
+               var content = this._readContentSync(dataTransfer);
 
-               // first try to transfer images
-               // TODO if we have both Files and a normal mimetype, should we 
handle
-               // both, or prefer one or the other?
-               for (var t = 0; t < types.length; ++t) {
-                       if (types[t] === 'Files') {
-                               var files = dataTransfer.files;
-                               for (var f = 0; f < files.length; ++f) {
-                                       var file = files[f];
-                                       if (file.type.match(/image.*/)) {
-                                               var reader = new FileReader();
-                                               reader.onload = 
this._onFileLoadFunc(file);
-                                               reader.readAsArrayBuffer(file);
+               // Images get a look in only if we have no content and are async
+               if (content == null && pasteHtml == '')
+               {
+                       var types = dataTransfer.types;
+
+                       console.log('Attempting to paste image(s)');
+
+                       // first try to transfer images
+                       // TODO if we have both Files and a normal mimetype, 
should we handle
+                       // both, or prefer one or the other?
+                       for (var t = 0; t < types.length; ++t) {
+                               console.log('\ttype' + types[t]);
+                               if (types[t] === 'Files') {
+                                       var files = dataTransfer.files;
+                                       for (var f = 0; f < files.length; ++f) {
+                                               var file = files[f];
+                                               if (file.type.match(/image.*/)) 
{
+                                                       var reader = new 
FileReader();
+                                                       reader.onload = 
this._onFileLoadFunc(file);
+                                                       
reader.readAsArrayBuffer(file);
+                                               }
                                        }
                                }
                        }
+                       return;
                }
 
-               // now try various mime types
+               // Try Fetch the data directly ourselves instead.
+               if (meta != '') {
+                       // FIXME: really should short-circuit on the server.
+                       console.log('Doing async paste of data from remote 
origin\n\t"' + meta + '" is not\n\t"' + id + '"');
+                       var tilelayer = this;
+                       var oReq = new XMLHttpRequest();
+                       oReq.onload = function() {
+                               var arraybuffer = oReq.response;
+                               if (oReq.status == 200) { // OK
+                                       var blob = new Blob(['paste 
mimetype=application/x-openoffice-embed-source-xml;windows_formatname="Star 
Embed Source (XML)"\n', arraybuffer]);
+                                       
tilelayer._map._socket.sendMessage(blob);
+                                       console.log('Sent paste blob message');
+                               } else {
+                                       console.log('Error code ' + oReq.status 
+ ' fetching from URL "' + meta + '": ' + e + ' falling back to local.');
+                                       
tilelayer._map._socket.sendMessage(content);
+                               }
+                       }
+                       oReq.onerror = function(e) {
+                               console.log('Error fetching from URL "' + meta 
+ '": ' + e + ' falling back to local.');
+                               tilelayer._map._socket.sendMessage(content);
+                       };
+                       oReq.open('GET', meta);
+                       oReq.responseType = 'arraybuffer';
+                       oReq.send();
+                       // user abort - if they do stops paste.
+               } else {
+                       console.log('Received a paste but nothing on the 
clipboard');
+               }
+       },
+
+       _readContentSync: function(dataTransfer) {
+               // Try various content mime types
                var mimeTypes;
                if (this._docType === 'spreadsheet') {
                        // FIXME apparently we cannot paste the text/html or 
text/rtf as
@@ -2547,15 +2598,15 @@ L.TileLayer = L.GridLayer.extend({
                        ];
                }
 
+               var types = dataTransfer.types;
                for (var i = 0; i < mimeTypes.length; ++i) {
-                       for (t = 0; t < types.length; ++t) {
+                       for (var t = 0; t < types.length; ++t) {
                                if (mimeTypes[i][0] === types[t]) {
-                                       var blob = new Blob(['paste mimetype=' 
+ mimeTypes[i][1] + '\n', dataTransfer.getData(types[t])]);
-                                       this._map._socket.sendMessage(blob);
-                                       return;
+                                       return new Blob(['paste mimetype=' + 
mimeTypes[i][1] + '\n', dataTransfer.getData(types[t])]);
                                }
                        }
                }
+               return null;
        },
 
        _onFileLoadFunc: function(file) {
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index f906e690d..899eae49f 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -14,6 +14,7 @@
 #include <fstream>
 #include <sstream>
 #include <memory>
+#include <unordered_map>
 
 #include <Poco/Net/HTTPResponse.h>
 #include <Poco/StreamCopier.h>
@@ -35,10 +36,14 @@ using namespace LOOLProtocol;
 using Poco::Path;
 using Poco::StringTokenizer;
 
+static std::mutex SessionMapMutex;
+static std::unordered_map<std::string, std::weak_ptr<ClientSession>> 
SessionMap;
+
 ClientSession::ClientSession(const std::string& id,
                              const std::shared_ptr<DocumentBroker>& docBroker,
                              const Poco::URI& uriPublic,
-                             const bool readOnly) :
+                             const bool readOnly,
+                             const std::string& hostNoTrust) :
     Session("ToClient-" + id, id, readOnly),
     _docBroker(docBroker),
     _uriPublic(uriPublic),
@@ -53,16 +58,61 @@ ClientSession::ClientSession(const std::string& id,
     _tileWidthTwips(0),
     _tileHeightTwips(0),
     _kitViewId(-1),
+    _hostNoTrust(hostNoTrust),
     _isTextDocument(false)
 {
     const size_t curConnections = ++LOOLWSD::NumConnections;
     LOG_INF("ClientSession ctor [" << getName() << "], current number of 
connections: " << curConnections);
 }
 
+// Can't take a reference in the constructor.
+void ClientSession::construct()
+{
+    std::unique_lock<std::mutex> lock(SessionMapMutex);
+    SessionMap[getId()] = shared_from_this();
+}
+
 ClientSession::~ClientSession()
 {
     const size_t curConnections = --LOOLWSD::NumConnections;
     LOG_INF("~ClientSession dtor [" << getName() << "], current number of 
connections: " << curConnections);
+
+    std::unique_lock<std::mutex> lock(SessionMapMutex);
+    SessionMap.erase(getId());
+}
+
+std::string ClientSession::getURIAndUpdateClipboardHash()
+{
+    std::string hash = Util::rng::getHexString(16);
+    {
+        std::unique_lock<std::mutex> lock(SessionMapMutex);
+        _clipboardKey = hash;
+    }
+
+    std::string encodedFrom;
+    Poco::URI wopiSrc = getDocumentBroker()->getPublicUri();
+    wopiSrc.setQueryParameters(Poco::URI::QueryParameters());
+
+    std::string encodeChars = ",/?:@&=+$#"; // match JS encodeURIComponent
+    Poco::URI::encode(wopiSrc.toString(), encodeChars, encodedFrom);
+
+    std::string proto = (LOOLWSD::isSSLEnabled() || 
LOOLWSD::isSSLTermination()) ? "https://"; : "http://";;
+    std::string meta = proto + _hostNoTrust + "/clipboard/" + 
LOOLWSD::HostIdentifier +
+        "/" + std::to_string(getKitViewId()) + "?WOPISrc=" + encodedFrom + 
"&amp;tag=" + hash;
+    return meta;
+}
+
+// called infrequently
+std::shared_ptr<ClientSession> ClientSession::getByClipboardHash(std::string 
&key)
+{
+    std::unique_lock<std::mutex> lock(SessionMapMutex);
+    for (auto &it : SessionMap)
+    {
+        auto session = it.second.lock();
+        if (session && session->_clipboardKey == key)
+            return session;
+    }
+    return std::shared_ptr<ClientSession>();
 }
 
 void ClientSession::handleIncomingMessage(SocketDisposition &disposition)
@@ -986,14 +1036,7 @@ bool ClientSession::handleKitToClientMessage(const char* 
buffer, const int lengt
                 // cf. TileLayer.js /_dataTransferToDocument/
                 if (pos != std::string::npos) // assume text/html
                 {
-                    // FIXME: expose other content types ? provide an RTF 
back-channel ?
-                    std::string encodedFrom;
-                    Poco::URI wopiSrc = docBroker->getPublicUri();
-                    wopiSrc.setQueryParameters(Poco::URI::QueryParameters());
-                    // matching encodeURIComponent
-                    Poco::URI::encode(wopiSrc.toString(), ",/?:@&=+$#", 
encodedFrom);
-                    std::string meta = "https://transient/"; + 
LOOLWSD::HostIdentifier + "/" +
-                        std::to_string(getKitViewId()) + "?WOPISrc=" + 
encodedFrom;
+                    std::string meta = getURIAndUpdateClipboardHash();
                     std::string origin = "<meta name=\"origin\" content=\"" + 
meta + "\"/>\n";
                     data.insert(data.begin() + pos, origin.begin(), 
origin.end());
                     return true;
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 9562c8f39..61b3c037c 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -33,10 +33,14 @@ public:
     ClientSession(const std::string& id,
                   const std::shared_ptr<DocumentBroker>& docBroker,
                   const Poco::URI& uriPublic,
-                  const bool isReadOnly = false);
-
+                  const bool isReadOnly,
+                  const std::string& hostNoTrust);
+    void construct();
     virtual ~ClientSession();
 
+    /// Lookup any session by id.
+    static std::shared_ptr<ClientSession> getById(const std::string &id);
+
     void handleIncomingMessage(SocketDisposition &) override;
 
     void setReadOnly() override;
@@ -132,8 +136,15 @@ public:
     void resetWireIdMap();
 
     bool isTextDocument() const { return _isTextDocument; }
+
+    /// Find clipboard for session
+    static std::shared_ptr<ClientSession> getByClipboardHash(std::string &key);
+
 private:
 
+    /// Create URI for transient clipboard content.
+    std::string getURIAndUpdateClipboardHash();
+
     /// SocketHandler: disconnection event.
     void onDisconnect() override;
     /// Does SocketHandler: have data or timeouts to setup.
@@ -217,9 +228,15 @@ private:
     /// The integer id of the view in the Kit process
     int _kitViewId;
 
+    /// Un-trusted hostname of our service from the client
+    const std::string _hostNoTrust;
+
     /// Client is using a text document?
     bool _isTextDocument;
 
+    /// Transient clipboard identifier - protected by SessionMapMutex
+    std::string _clipboardKey;
+
     /// TileID's of the sent tiles. Push by sending and pop by tileprocessed 
message from the client.
     std::list<std::pair<std::string, std::chrono::steady_clock::time_point>> 
_tilesOnFly;
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 44652cb34..f9dafd373 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -687,7 +687,7 @@ inline std::string getAdminURI(const 
Poco::Util::LayeredConfiguration &config)
 
 #endif // MOBILEAPP
 
-std::atomic<unsigned> LOOLWSD::NextSessionId;
+std::atomic<uint64_t> LOOLWSD::NextSessionId;
 #ifndef KIT_IN_PROCESS
 std::atomic<int> LOOLWSD::ForKitWritePipe(-1);
 std::atomic<int> LOOLWSD::ForKitProcId(-1);
@@ -1762,7 +1762,8 @@ static std::shared_ptr<ClientSession> 
createNewClientSession(const WebSocketHand
                                                              const 
std::string& id,
                                                              const Poco::URI& 
uriPublic,
                                                              const 
std::shared_ptr<DocumentBroker>& docBroker,
-                                                             const bool 
isReadOnly)
+                                                             const bool 
isReadOnly,
+                                                             const 
std::string& hostNoTrust)
 {
     LOG_CHECK_RET(docBroker && "Null docBroker instance", nullptr);
     try
@@ -1778,7 +1779,10 @@ static std::shared_ptr<ClientSession> 
createNewClientSession(const WebSocketHand
         // In case of WOPI, if this session is not set as readonly, it might 
be set so
         // later after making a call to WOPI host which tells us the 
permission on files
         // (UserCanWrite param).
-        return std::make_shared<ClientSession>(id, docBroker, uriPublic, 
isReadOnly);
+        auto session = std::make_shared<ClientSession>(id, docBroker, 
uriPublic, isReadOnly, hostNoTrust);
+        session->construct();
+
+        return session;
     }
     catch (const std::exception& exc)
     {
@@ -2426,7 +2430,8 @@ private:
                 // Load the document.
                 // TODO: Move to DocumentBroker.
                 const bool isReadOnly = true;
-                std::shared_ptr<ClientSession> clientSession = 
createNewClientSession(nullptr, _id, uriPublic, docBroker, isReadOnly);
+                std::shared_ptr<ClientSession> clientSession = 
createNewClientSession(
+                    nullptr, _id, uriPublic, docBroker, isReadOnly, 
"nocliphost");
                 if (clientSession)
                 {
                     disposition.setMove([docBroker, clientSession, format]
@@ -2475,7 +2480,10 @@ private:
                     sent = true;
                 }
                 else
+                {
                     LOG_WRN("Failed to create Client Session with id [" << _id 
<< "] on docKey [" << docKey << "].");
+                    cleanupDocBrokers();
+                }
             }
 
             if (!sent)
@@ -2673,7 +2681,11 @@ private:
             std::shared_ptr<DocumentBroker> docBroker = 
findOrCreateDocBroker(ws, url, docKey, _id, uriPublic);
             if (docBroker)
             {
-                std::shared_ptr<ClientSession> clientSession = 
createNewClientSession(&ws, _id, uriPublic, docBroker, isReadOnly);
+                // We can send this back to whomever sent it to us though.
+                const std::string hostNoTrust = (LOOLWSD::ServerName.empty() ? 
request.getHost() : LOOLWSD::ServerName);
+
+                std::shared_ptr<ClientSession> clientSession = 
createNewClientSession(&ws, _id, uriPublic,
+                                                                               
       docBroker, isReadOnly, hostNoTrust);
                 if (clientSession)
                 {
                     // Transfer the client socket to the DocumentBroker when 
we get back to the poll:
@@ -2730,7 +2742,6 @@ private:
                 else
                 {
                     LOG_WRN("Failed to create Client Session with id [" << _id 
<< "] on docKey [" << docKey << "].");
-                    cleanupDocBrokers();
                 }
             }
             else
diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp
index 7cb03920f..97cec967f 100644
--- a/wsd/LOOLWSD.hpp
+++ b/wsd/LOOLWSD.hpp
@@ -44,7 +44,7 @@ public:
 
     // An Application is a singleton anyway,
     // so just keep these as statics.
-    static std::atomic<unsigned> NextSessionId;
+    static std::atomic<uint64_t> NextSessionId;
     static unsigned int NumPreSpawnedChildren;
     static bool NoCapsForKit;
     static bool NoSeccomp;
commit df4e17dc26a26cba05155f5102f66b5237dec074
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Sun May 26 00:16:18 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Tue May 28 02:26:47 2019 +0100

    Ensure we have a unique ID for each document and view.
    
    Change-Id: I9f38dd137d1617aa39b1ca505b07ab7740a986b9

diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index a32fa9044..6916ab775 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -2488,14 +2488,16 @@ L.TileLayer = L.GridLayer.extend({
                if (preferInternal === true) {
                        var pasteHtml = dataTransfer.getData('text/html');
                        var meta = this._getMetaOrigin(pasteHtml);
-                       var id = this._map._socket.WSDServer.Id;
+                       var id = 'https://transient/' + 
this._map._socket.WSDServer.Id + '/' + this._viewId +
+                           '?WOPISrc=' + 
encodeURIComponent(this._map.options.doc);
+                       // cf. ClientSession.cpp /textselectioncontent:/
                        if (meta == id) {
                                // Home from home: short-circuit internally.
                                this._map._socket.sendMessage('uno .uno:Paste');
                                return;
                        } else
-                               console.log('Unusual origin mismatch on paste 
between: "' +
-                                           meta + '" and "' + id);
+                               console.log('Unusual origin mismatch on paste 
between:\n\t"' +
+                                           meta + '" and\n\t"' + id + '"');
                }
 
                var types = dataTransfer.types;
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index c6d6def28..f906e690d 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -52,6 +52,7 @@ ClientSession::ClientSession(const std::string& id,
     _tileHeightPixel(0),
     _tileWidthTwips(0),
     _tileHeightTwips(0),
+    _kitViewId(-1),
     _isTextDocument(false)
 {
     const size_t curConnections = ++LOOLWSD::NumConnections;
@@ -979,12 +980,21 @@ bool ClientSession::handleKitToClientMessage(const char* 
buffer, const int lengt
         }
     } else if (tokens[0] == "textselectioncontent:") {
         // Insert our meta origin if we can
-        payload->rewriteDataBody([](std::vector<char>& data) {
+        payload->rewriteDataBody([=](std::vector<char>& data) {
                 size_t pos = Util::findInVector(data, "<meta 
name=\"generator\" content=\"");
+
+                // cf. TileLayer.js /_dataTransferToDocument/
                 if (pos != std::string::npos) // assume text/html
                 {
-                    // FIXME: expose other content types ? provide an RTF 
back-channel ? WOPISRC ?
-                    std::string origin = "<meta name=\"origin\" content=\"" + 
LOOLWSD::HostIdentifier + "\"/>\n";
+                    // FIXME: expose other content types ? provide an RTF 
back-channel ?
+                    std::string encodedFrom;
+                    Poco::URI wopiSrc = docBroker->getPublicUri();
+                    wopiSrc.setQueryParameters(Poco::URI::QueryParameters());
+                    // matching encodeURIComponent
+                    Poco::URI::encode(wopiSrc.toString(), ",/?:@&=+$#", 
encodedFrom);
+                    std::string meta = "https://transient/"; + 
LOOLWSD::HostIdentifier + "/" +
+                        std::to_string(getKitViewId()) + "?WOPISrc=" + 
encodedFrom;
+                    std::string origin = "<meta name=\"origin\" content=\"" + 
meta + "\"/>\n";
                     data.insert(data.begin() + pos, origin.begin(), 
origin.end());
                     return true;
                 }
@@ -1024,6 +1034,11 @@ bool ClientSession::handleKitToClientMessage(const char* 
buffer, const int lengt
                 {
                     _isTextDocument = docType.find("text") != 
std::string::npos;
                 }
+
+                // Store our Kit ViewId
+                int viewId = -1;
+                if(getTokenInteger(token, "viewid", viewId))
+                    _kitViewId = viewId;
             }
 
             // Forward the status response to the client.
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index b701e5568..9562c8f39 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -55,6 +55,9 @@ public:
     /// Handle kit-to-client message.
     bool handleKitToClientMessage(const char* data, const int size);
 
+    /// Integer id of the view in the kit process, or -1 if unknown
+    int getKitViewId() const { return _kitViewId; }
+
     // sendTextFrame that takes std::string and string literal.
     using Session::sendTextFrame;
 
@@ -211,6 +214,9 @@ private:
     int _tileWidthTwips;
     int _tileHeightTwips;
 
+    /// The integer id of the view in the Kit process
+    int _kitViewId;
+
     /// Client is using a text document?
     bool _isTextDocument;
 
commit 4645bbbd36ade52e5b1b3557453c515f665c6824
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Sat May 25 16:13:54 2019 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Tue May 28 02:26:47 2019 +0100

    Initial switch of outbound copy format to HTML.
    
    Use a content-editable instead of an input
    Switch format specifiers left and right.
    Embed a <meta name="origin" content="<Id>"/> to reliably detect
    internal copy/paste not based on content.
    
    Lots of testing.
    
    Change-Id: I36723298e392331515b055b5ebe8132fb2e4fa3c

diff --git a/common/Message.hpp b/common/Message.hpp
index a62cfe375..3fdbc5d36 100644
--- a/common/Message.hpp
+++ b/common/Message.hpp
@@ -13,6 +13,7 @@
 #include <atomic>
 #include <string>
 #include <vector>
+#include <functional>
 
 #include "Protocol.hpp"
 #include "Log.hpp"
@@ -117,6 +118,18 @@ public:
     /// Returns true if and only if the payload is considered Binary.
     bool isBinary() const { return _type == Type::Binary; }
 
+    /// Allows some in-line re-writing of the message
+    void rewriteDataBody(std::function<bool (std::vector<char> &)> func)
+    {
+        if (func(_data))
+        {
+            // Check - just the body.
+            assert(_firstLine == LOOLProtocol::getFirstLine(_data.data(), 
_data.size()));
+            assert(_abbr == _id + ' ' + 
LOOLProtocol::getAbbreviatedMessage(_data.data(), _data.size()));
+            assert(_type == detectType());
+        }
+    }
+
 private:
 
     /// Constructs a unique ID.
diff --git a/common/Util.cpp b/common/Util.cpp
index 6f2322c8f..dcdbd115b 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -739,6 +739,20 @@ namespace Util
 
         return base + Util::anonymize(filename, nAnonymizationSalt) + ext + 
params;
     }
+
+    size_t findInVector(const std::vector<char>& tokens, const char *cstring)
+    {
+        assert(cstring);
+        for (size_t i = 0; i < tokens.size(); ++i)
+        {
+            size_t j;
+            for (j = 0; i + j < tokens.size() && cstring[j] != '\0' && 
tokens[i + j] == cstring[j]; ++j)
+                ;
+            if (cstring[j] == '\0')
+                return i;
+        }
+        return std::string::npos;
+    }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util.hpp b/common/Util.hpp
index 9021b7ed1..488f9d525 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -256,6 +256,8 @@ namespace Util
         return oss.str();
     }
 
+    size_t findInVector(const std::vector<char>& tokens, const char *cstring);
+
     /// Trim spaces from the left. Just spaces.
     inline std::string& ltrim(std::string& s)
     {
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index b48338420..17ea4bb09 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -36,6 +36,7 @@ L.Socket = L.Class.extend({
        ProtocolVersionNumber: '0.1',
        ReconnectCount: 0,
        WasShownLimitDialog: false,
+       WSDServer: {},
 
        getParameterValue: function (s) {
                var i = s.indexOf('=');
@@ -275,25 +276,25 @@ L.Socket = L.Class.extend({
                var command = this.parseServerCmd(textMsg);
                if (textMsg.startsWith('loolserver ')) {
                        // This must be the first message, unless we reconnect.
-                       var loolwsdVersionObj = 
JSON.parse(textMsg.substring(textMsg.indexOf('{')));
-                       var h = loolwsdVersionObj.Hash;
+                       this.WSDServer = 
JSON.parse(textMsg.substring(textMsg.indexOf('{')));
+                       var h = this.WSDServer.Hash;
                        if (parseInt(h,16).toString(16) === 
h.toLowerCase().replace(/^0+/, '')) {
                                if (!window.ThisIsTheiOSApp) {
                                        h = '<a target="_blank" 
href="https://hub.libreoffice.org/git-online/' + h + '">' + h + '</a>';
                                }
-                               
$('#loolwsd-version').html(loolwsdVersionObj.Version + ' (git hash: ' + h + 
')');
+                               
$('#loolwsd-version').html(this.WSDServer.Version + ' (git hash: ' + h + ')');
                        }
                        else {
-                               
$('#loolwsd-version').text(loolwsdVersionObj.Version);
+                               
$('#loolwsd-version').text(this.WSDServer.Version);
                        }
 
                        var idUri = this._map.options.server + 
this._map.options.serviceRoot + '/hosting/discovery';
                        idUri = idUri.replace(/^ws:/, 'http:');
                        idUri = idUri.replace(/^wss:/, 'https:');
-                       $('#loolwsd-id').html('<a href="' + idUri + '">' + 
loolwsdVersionObj.Id + '</a>');
+                       $('#loolwsd-id').html('<a href="' + idUri + '">' + 
this.WSDServer.Id + '</a>');
 
                        // TODO: For now we expect perfect match in protocol 
versions
-                       if (loolwsdVersionObj.Protocol !== 
this.ProtocolVersionNumber) {
+                       if (this.WSDServer.Protocol !== 
this.ProtocolVersionNumber) {
                                this._map.fire('error', {msg: _('Unsupported 
server version.')});
                        }
                }
diff --git a/loleaflet/src/layer/marker/ClipboardContainer.js 
b/loleaflet/src/layer/marker/ClipboardContainer.js
index 41aae051d..f22ed2feb 100644
--- a/loleaflet/src/layer/marker/ClipboardContainer.js
+++ b/loleaflet/src/layer/marker/ClipboardContainer.js
@@ -41,15 +41,25 @@ L.ClipboardContainer = L.Layer.extend({
        },
 
        select: function() {
-               this._textArea.select();
+               window.getSelection().selectAllChildren(this._textArea);
+       },
+
+       resetToSelection: function() {
+               var sel = window.getSelection();
+               if (sel.anchorNode == this._textArea) {
+                       // already selected, don't reset to plain-text from 
toString()
+               } else {
+                       this.setValue(sel.toString());
+               }
        },
 
        getValue: function() {
-               return this._textArea.value;
+               return this._textArea.innerHTML;
        },
 
        setValue: function(val) {
-               this._textArea.value = val;
+               this._textArea.innerHTML = val;
+               this.select();
        },
 
        setLatLng: function (latlng) {
@@ -67,7 +77,8 @@ L.ClipboardContainer = L.Layer.extend({
        _initLayout: function () {
                this._container = L.DomUtil.create('div', 
'clipboard-container');
                this._container.id = 'doc-clipboard-container';
-               this._textArea = L.DomUtil.create('input', 'clipboard', 
this._container);
+               this._textArea = L.DomUtil.create('div', 'clipboard', 
this._container);
+               this._textArea.setAttribute('contenteditable', 'true');
                this._textArea.setAttribute('type', 'text');
                this._textArea.setAttribute('autocorrect', 'off');
                this._textArea.setAttribute('autocapitalize', 'off');
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index a1488669a..a32fa9044 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -26,12 +26,18 @@ function hex2string(inData)
 }
 
 L.Compatibility = {
+       stripHTML: function(html) { // grim.
+               var tmp = document.createElement('div');
+               tmp.innerHTML = html;
+               return tmp.textContent || tmp.innerText || '';
+       },
+
        clipboardSet: function (event, text) {
                if (event.clipboardData) { // Standard
-                       event.clipboardData.setData('text/plain', text);
+                       event.clipboardData.setData('text/html', text);
                }
-               else if (window.clipboardData) { // IE 11
-                       window.clipboardData.setData('Text', text);
+               else if (window.clipboardData) { // IE 11 - poor clipboard API
+                       window.clipboardData.setData('Text', 
this.stripHTML(text));
                }
        }
 };
@@ -1227,7 +1233,7 @@ L.TileLayer = L.GridLayer.extend({
                                clearTimeout(this._selectionContentRequest);
                        }
                        this._selectionContentRequest = 
setTimeout(L.bind(function () {
-                               this._map._socket.sendMessage('gettextselection 
mimetype=text/plain;charset=utf-8');}, this), 100);
+                               this._map._socket.sendMessage('gettextselection 
mimetype=text/html');}, this), 100);
                }
                this._onUpdateTextSelection();
        },
@@ -1267,6 +1273,7 @@ L.TileLayer = L.GridLayer.extend({
 
        _onTextSelectionContentMsg: function (textMsg) {
                this._selectionTextContent = textMsg.substr(22);
+               
this._map._clipboardContainer.setValue(this._selectionTextContent);
        },
 
        _updateScrollOnCellSelection: function (oldSelection, newSelection) {
@@ -2463,18 +2470,32 @@ L.TileLayer = L.GridLayer.extend({
                this._dataTransferToDocument(e.dataTransfer, /* preferInternal 
= */ false);
        },
 
+       _getMetaOrigin: function (html) {
+               var match = '<meta name="origin" content="';
+               var start = html.indexOf(match);
+               if (start < 0) {
+                       return '';
+               }
+               var end = html.indexOf('"', start + match.length);
+               if (end < 0) {
+                       return '';
+               }
+               return html.substring(start + match.length, end);
+       },
+
        _dataTransferToDocument: function (dataTransfer, preferInternal) {
                // for the paste, we might prefer the internal LOK's copy/paste
                if (preferInternal === true) {
-                       var pasteString = dataTransfer.getData('text/plain');
-                       if (!pasteString) {
-                               pasteString = dataTransfer.getData('Text'); // 
IE 11
-                       }
-
-                       if (pasteString && pasteString === 
this._selectionTextHash) {
+                       var pasteHtml = dataTransfer.getData('text/html');
+                       var meta = this._getMetaOrigin(pasteHtml);
+                       var id = this._map._socket.WSDServer.Id;
+                       if (meta == id) {
+                               // Home from home: short-circuit internally.
                                this._map._socket.sendMessage('uno .uno:Paste');
                                return;
-                       }
+                       } else
+                               console.log('Unusual origin mismatch on paste 
between: "' +
+                                           meta + '" and "' + id);
                }
 
                var types = dataTransfer.types;
diff --git a/loleaflet/src/map/handler/Map.Keyboard.js 
b/loleaflet/src/map/handler/Map.Keyboard.js
index 4e3b51354..95a5269b9 100644
--- a/loleaflet/src/map/handler/Map.Keyboard.js
+++ b/loleaflet/src/map/handler/Map.Keyboard.js
@@ -528,7 +528,7 @@ L.Map.Keyboard = L.Handler.extend({
                case 91: // Left Cmd (Safari)
                case 93: // Right Cmd (Safari)
                        // we prepare for a copy or cut event
-                       
this._map._clipboardContainer.setValue(window.getSelection().toString());
+                       this._map._clipboardContainer.resetToSelection();
                        this._map.focus();
                        this._map._clipboardContainer.select();
                        return true;
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 3de6a8651..c6d6def28 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -977,6 +977,24 @@ bool ClientSession::handleKitToClientMessage(const char* 
buffer, const int lengt
                 }
             }
         }
+    } else if (tokens[0] == "textselectioncontent:") {
+        // Insert our meta origin if we can
+        payload->rewriteDataBody([](std::vector<char>& data) {
+                size_t pos = Util::findInVector(data, "<meta 
name=\"generator\" content=\"");
+                if (pos != std::string::npos) // assume text/html
+                {
+                    // FIXME: expose other content types ? provide an RTF 
back-channel ? WOPISRC ?
+                    std::string origin = "<meta name=\"origin\" content=\"" + 
LOOLWSD::HostIdentifier + "\"/>\n";
+                    data.insert(data.begin() + pos, origin.begin(), 
origin.end());
+                    return true;
+                }
+                else
+                {
+                    LOG_DBG("Missing generator in textselectioncontent");
+                    return false;
+                }
+            });
+        return forwardToClient(payload);
     }
 
     if (!isDocPasswordProtected())
commit ce18e7b50a3498dc26b032605529d0f5922ca37e
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Mon May 27 16:00:23 2019 +0200
Commit:     Szymon Kłos <szymon.k...@collabora.com>
CommitDate: Mon May 27 18:27:46 2019 +0200

    Enable the Share and Print buttons even in the View mode.
    
    The user should be able to access these even in the initial View mode on
    Android.
    
    Change-Id: Id0631b7560c1ed0fda5f228f0c621cfa989b4cf7
    Reviewed-on: https://gerrit.libreoffice.org/73039
    Reviewed-by: Szymon Kłos <szymon.k...@collabora.com>
    Tested-by: Szymon Kłos <szymon.k...@collabora.com>

diff --git a/loleaflet/src/control/Control.Menubar.js 
b/loleaflet/src/control/Control.Menubar.js
index 35e0e1d97..02c44ad97 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -437,6 +437,7 @@ L.Control.Menubar = L.Control.extend({
                allowedReadonlyMenus: ['file', 'downloadas', 'view', 'help'],
 
                allowedViewModeActions: [
+                       'shareas', 'print', // file menu
                        'downloadas-pdf', 'downloadas-odt', 'downloadas-doc', 
'downloadas-docx', 'downloadas-rtf', // file menu
                        'downloadas-odp', 'downloadas-ppt', 'downloadas-pptx', 
'print', // file menu
                        'downloadas-ods', 'downloadas-xls', 'downloadas-xlsx', 
'closedocument', // file menu
commit 1288286d453c0df2b82f0a8b7cc16f5b2d66cb13
Author:     merttumer <mert.tu...@collabora.com>
AuthorDate: Mon May 27 17:11:34 2019 +0300
Commit:     merttumer <mert.tu...@collabora.com>
CommitDate: Mon May 27 17:38:15 2019 +0300

    Send postMessage after renaming the document
    
    Change-Id: Iee0854260fc0fab4de3e2a4fae54716009535b37
    Signed-off-by: merttumer <mert.tu...@collabora.com>

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 56402e4d5..b48338420 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -742,13 +742,16 @@ L.Socket = L.Class.extend({
                                this._map.options.wopiSrc = 
encodeURIComponent(docUrl);
                                this._map.loadDocument();
                                this._map.sendInitUNOCommands();
-
-                               this._map.fire('postMessage', {
-                                       msgId: 'File_Rename',
-                                       args: {
-                                               NewName: command.filename
-                                       }
-                               });
+                               
+                               if (textMsg.startsWith('renamefile:')) {
+                                       this._map.fire('postMessage', {
+                                               msgId: 'File_Rename',
+                                               args: {
+                                                       NewName: 
command.filename
+                                               }
+                                       });
+                               }
+                               
                        }
                        // var name = command.name; - ignored, we get the new 
name via the wopi's BaseFileName
                }
commit f426e36f69759d8bded61a62f1fcc3e0f4c0bb8c
Author:     merttumer <mert.tu...@collabora.com>
AuthorDate: Mon May 27 17:11:34 2019 +0300
Commit:     merttumer <mert.tu...@collabora.com>
CommitDate: Mon May 27 17:11:34 2019 +0300

    Send postMessage after renaming the document
    
    Change-Id: Iee0854260fc0fab4de3e2a4fae54716009535b37
    Signed-off-by: merttumer <mert.tu...@collabora.com>

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 06fa5eb47..56402e4d5 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -742,6 +742,13 @@ L.Socket = L.Class.extend({
                                this._map.options.wopiSrc = 
encodeURIComponent(docUrl);
                                this._map.loadDocument();
                                this._map.sendInitUNOCommands();
+
+                               this._map.fire('postMessage', {
+                                       msgId: 'File_Rename',
+                                       args: {
+                                               NewName: command.filename
+                                       }
+                               });
                        }
                        // var name = command.name; - ignored, we get the new 
name via the wopi's BaseFileName
                }
@@ -988,6 +995,9 @@ L.Socket = L.Class.extend({
                        else if (tokens[i].substring(0, 5) === 'name=') {
                                command.name = tokens[i].substring(5);
                        }
+                       else if (tokens[i].substring(0, 9) === 'filename=') {
+                               command.filename = tokens[i].substring(9);
+                       }
                        else if (tokens[i].substring(0, 5) === 'port=') {
                                command.port = tokens[i].substring(5);
                        }
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to