common/Common.hpp                                |    2 
 debian/changelog                                 |    6 
 kit/Kit.cpp                                      |    4 
 loleaflet/build/deps.js                          |    7 
 loleaflet/dist/leaflet.css                       |    8 
 loleaflet/dist/loleaflet.css                     |   15 +
 loleaflet/dist/menubar.css                       |   22 ++
 loleaflet/src/control/Control.Menubar.js         |    1 
 loleaflet/src/control/Control.MobileInput.js     |  195 ++++++++++++++++++++++
 loleaflet/src/core/Socket.js                     |    3 
 loleaflet/src/layer/marker/ClipboardContainer.js |    4 
 loleaflet/src/layer/tile/GridLayer.js            |   13 +
 loleaflet/src/layer/tile/TileLayer.js            |   25 +-
 loleaflet/src/map/Map.js                         |   19 +-
 loolwsd.spec.in                                  |    2 
 loolwsd.xml.in                                   |    1 
 net/Socket.cpp                                   |    4 
 test/TileCacheTests.cpp                          |  198 +++++++++++++++++++++++
 wsd/Admin.cpp                                    |    4 
 wsd/Auth.cpp                                     |    2 
 wsd/Auth.hpp                                     |    5 
 wsd/ClientSession.cpp                            |   66 ++++++-
 wsd/ClientSession.hpp                            |   25 --
 wsd/DocumentBroker.cpp                           |   67 +++++--
 wsd/FileServer.cpp                               |    9 -
 wsd/LOOLWSD.cpp                                  |   16 +
 wsd/TestStubs.cpp                                |    2 
 wsd/TileCache.cpp                                |   48 ++++-
 wsd/TileCache.hpp                                |    8 
 29 files changed, 685 insertions(+), 96 deletions(-)

New commits:
commit fa40d73f1466d34a9562d9a1a49f99c3cdbdc9f2
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Fri Oct 5 20:39:00 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:39:00 2018 +0200

    Bump package version to 3.4.0-5
    
    Change-Id: I6ea9ddf09e0eaa6a7a088e679a07193df818b8ef

diff --git a/debian/changelog b/debian/changelog
index 8bbcd69f1..32f7f0044 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+loolwsd (3.4.0-5) unstable; urgency=medium
+
+  * see the git log: http://col.la/coolcd34
+
+ -- Andras Timar <andras.ti...@collabora.com>  Fri, 05 Oct 2018 20:30:00 +0200
+
 loolwsd (3.4.0-4) unstable; urgency=medium
 
   * see the git log: http://col.la/coolcd34
diff --git a/loolwsd.spec.in b/loolwsd.spec.in
index bf9dee572..c54d376a7 100644
--- a/loolwsd.spec.in
+++ b/loolwsd.spec.in
@@ -12,7 +12,7 @@ Name:           loolwsd%{name_suffix}
 Name:           loolwsd
 %endif
 Version:        @PACKAGE_VERSION@
-Release:        4%{?dist}
+Release:        5%{?dist}
 %if 0%{?suse_version} == 1110
 Group:          Productivity/Office/Suite
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
commit 587bc7bdb4b541ec4676974cf53066a61937cca3
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Mon Oct 1 20:17:35 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:37:18 2018 +0200

    tdf#115163 allow bind to loopback interface
    
    Change-Id: I4808fb0fd685dfe990efd5fb739ee86f1276ffad
    Reviewed-on: https://gerrit.libreoffice.org/61412
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit 1d087b3545be712073ab52ed11352a6b686f7a63)

diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index 587452ba4..26e5db7a8 100644
--- a/loolwsd.xml.in
+++ b/loolwsd.xml.in
@@ -68,6 +68,7 @@
 
     <net desc="Network settings">
       <proto type="string" default="all" desc="Protocol to use IPv4, IPv6 or 
all for both">all</proto>
+      <listen type="string" default="any" desc="Listen address that loolwsd 
binds to. Can be 'any' or 'loopback'.">any</listen>
       <service_root type="path" default="" desc="Prefix all the pages, 
websockets, etc. with this path."></service_root>
       <post_allow desc="Allow/deny client IP address for POST(REST)." 
allow="true">
         <host desc="The IPv4 private 192.168 block as plain IPv4 dotted 
decimal addresses.">192\.168\.[0-9]{1,3}\.[0-9]{1,3}</host>
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index eca0644f6..b55ec5617 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -168,6 +168,9 @@ int ClientPortNumber = DEFAULT_CLIENT_PORT_NUMBER;
 /// Protocols to listen on
 Socket::Type ClientPortProto = Socket::Type::All;
 
+/// INET address to listen on
+ServerSocket::Type ClientListenAddr = ServerSocket::Type::Public;
+
 /// Port for prisoners to connect to
 int MasterPortNumber = DEFAULT_MASTER_PORT_NUMBER;
 
@@ -698,6 +701,7 @@ void LOOLWSD::initialize(Application& self)
             { "loleaflet_html", "loleaflet.html" },
             { "loleaflet_logging", "false" },
             { "net.proto", "all" },
+            { "net.listen", "any" },
             { "net.service_root", "" },
             { "num_prespawn_children", "1" },
             { "per_document.autosave_duration_secs", "300" },
@@ -874,6 +878,16 @@ void LOOLWSD::initialize(Application& self)
             LOG_WRN("Invalid protocol: " << proto);
     }
 
+    {
+        std::string listen = getConfigValue<std::string>(conf, "net.listen", 
"");
+        if (!Poco::icompare(listen, "any"))
+            ClientListenAddr = ServerSocket::Type::Public;
+        else if (!Poco::icompare(listen, "loopback"))
+            ClientListenAddr = ServerSocket::Type::Local;
+        else
+            LOG_WRN("Invalid listen address: " << listen << ". Falling back to 
default: 'any'" );
+    }
+
     // Prefix for the loolwsd pages; should not end with a '/'
     ServiceRoot = getPathFromConfig("net.service_root");
     while (ServiceRoot.length() > 0 && ServiceRoot[ServiceRoot.length() - 1] 
== '/')
@@ -2734,7 +2748,7 @@ private:
             factory = std::make_shared<PlainSocketFactory>();
 
         std::shared_ptr<ServerSocket> socket = getServerSocket(
-            ServerSocket::Type::Public, port, WebServerPoll, factory);
+            ClientListenAddr, port, WebServerPoll, factory);
         while (!socket)
         {
             ++port;
commit 30e5f4fff318803457da8ca8d053a48d8118cba6
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Fri Sep 28 11:54:20 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:37:12 2018 +0200

    don't use ssl key file for admin console auth, use a generated key instead
    
    Change-Id: I424afe0184a64b7f069d896bde6941e42b7b5531
    rational: setup is easier in case, when user does not use ssl in loolwsd 
config
    Reviewed-on: https://gerrit.libreoffice.org/61411
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit 86f50208829772934ce310be103ec9a36c862d7f)

diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index 9c24d2007..f93cf75b7 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -76,11 +76,9 @@ void AdminSocketHandler::handleMessage(bool /* fin */, 
WSOpCode /* code */,
         }
         std::string jwtToken;
         LOOLProtocol::getTokenString(tokens[1], "jwt", jwtToken);
-        const auto& config = Application::instance().config();
-        const auto sslKeyPath = config.getString("ssl.key_file_path", "");
 
         LOG_INF("Verifying JWT token: " << jwtToken);
-        JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin");
+        JWTAuth authAgent("admin", "admin", "admin");
         if (authAgent.verify(jwtToken))
         {
             LOG_TRC("JWT token is valid");
diff --git a/wsd/Auth.cpp b/wsd/Auth.cpp
index 088719d78..b1f76ae60 100644
--- a/wsd/Auth.cpp
+++ b/wsd/Auth.cpp
@@ -37,6 +37,8 @@ using Poco::Base64Decoder;
 using Poco::Base64Encoder;
 using Poco::OutputLineEndingConverter;
 
+const Poco::Crypto::RSAKey 
JWTAuth::_key(Poco::Crypto::RSAKey(Poco::Crypto::RSAKey::KL_2048, 
Poco::Crypto::RSAKey::EXP_LARGE));
+
 void Authorization::authorizeURI(Poco::URI& uri) const
 {
     if (_type == Authorization::Type::Token)
diff --git a/wsd/Auth.hpp b/wsd/Auth.hpp
index 96bcb86b6..fa9029bba 100644
--- a/wsd/Auth.hpp
+++ b/wsd/Auth.hpp
@@ -69,11 +69,10 @@ public:
 class JWTAuth : public AuthBase
 {
 public:
-    JWTAuth(const std::string& keyPath, const std::string& name, const 
std::string& sub, const std::string& aud)
+    JWTAuth(const std::string& name, const std::string& sub, const 
std::string& aud)
         : _name(name),
           _sub(sub),
           _aud(aud),
-          _key(Poco::Crypto::RSAKey("", keyPath)),
           _digestEngine(_key, "SHA256")
     {
     }
@@ -96,7 +95,7 @@ private:
     const std::string _sub;
     const std::string _aud;
 
-    const Poco::Crypto::RSAKey _key;
+    static const Poco::Crypto::RSAKey _key;
     Poco::Crypto::RSADigestEngine _digestEngine;
 };
 
diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index 35b43c27e..714dac28b 100644
--- a/wsd/FileServer.cpp
+++ b/wsd/FileServer.cpp
@@ -196,7 +196,6 @@ bool FileServerRequestHandler::isAdminLoggedIn(const 
HTTPRequest& request,
     assert(LOOLWSD::AdminEnabled);
 
     const auto& config = Application::instance().config();
-    const auto sslKeyPath = config.getString("ssl.key_file_path", "");
 
     NameValueCollection cookies;
     request.getCookies(cookies);
@@ -204,7 +203,7 @@ bool FileServerRequestHandler::isAdminLoggedIn(const 
HTTPRequest& request,
     {
         const std::string jwtToken = cookies.get("jwt");
         LOG_INF("Verifying JWT token: " << jwtToken);
-        JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin");
+        JWTAuth authAgent("admin", "admin", "admin");
         if (authAgent.verify(jwtToken))
         {
             LOG_TRC("JWT token is valid");
@@ -247,7 +246,7 @@ bool FileServerRequestHandler::isAdminLoggedIn(const 
HTTPRequest& request,
     }
 
     // authentication passed, generate and set the cookie
-    JWTAuth authAgent(sslKeyPath, "admin", "admin", "admin");
+    JWTAuth authAgent("admin", "admin", "admin");
     const std::string jwtToken = authAgent.getAccessToken();
 
     Poco::Net::HTTPCookie cookie("jwt", jwtToken);
commit 70ca7e3bc3f7ab4652613c99b7778182c493ad4b
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Wed Oct 3 13:25:36 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:37:07 2018 +0200

    fix that internal port 9981 was opened on all interfaces
    
    Change-Id: I04cd12b7fa2f0be9b08a3d325f08b36ca2ce240e
    Reviewed-on: https://gerrit.libreoffice.org/61410
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit f32b75eefe5ac2b4ac5b54039e3b4bb665b994d6)

diff --git a/net/Socket.cpp b/net/Socket.cpp
index b78900276..35b713632 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -312,9 +312,9 @@ bool ServerSocket::bind(Type type, int port)
         addrv4.sin_family = AF_INET;
         addrv4.sin_port = htons(port);
         if (type == Type::Public)
-            addrv4.sin_addr.s_addr = type == htonl(INADDR_ANY);
+            addrv4.sin_addr.s_addr = htonl(INADDR_ANY);
         else
-            addrv4.sin_addr.s_addr = type == htonl(INADDR_LOOPBACK);
+            addrv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
         rc = ::bind(getFD(), (const sockaddr *)&addrv4, sizeof(addrv4));
     }
commit ca2d2b4a33c9b54f9644944373fcc295a4f83cfd
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Wed Oct 3 21:49:31 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:37:03 2018 +0200

    loleaflet: mobile: use max-device-height media query
    
    max-height it is used for window size, max-device-height
    it is the device screen dimensions, so when a tablet
    rotate to landscape we keep the mobile layout
    
    Change-Id: I921007014a63374114ec7563144f3532a53fd021
    Reviewed-on: https://gerrit.libreoffice.org/61339
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 63491fe0f11749f04cddd0599f7b99253c7d0479)

diff --git a/loleaflet/dist/leaflet.css b/loleaflet/dist/leaflet.css
index 332490ed5..3dde4a01a 100644
--- a/loleaflet/dist/leaflet.css
+++ b/loleaflet/dist/leaflet.css
@@ -788,7 +788,7 @@ input.clipboard {
        }
 }
 
-@media (max-width: 768px),(max-height: 768px) {
+@media (max-width: 768px),(max-device-height: 768px) {
        .loleaflet-ruler {
                height: 0px;
                display: none;
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index e7d38b5eb..3ce760548 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -81,7 +81,7 @@ body {
     width: 0;
 }
 
-@media (max-width: 768px),(max-height: 768px) {
+@media (max-width: 768px), (max-device-height: 768px) {
     /* Show slidesorter beyond 768px only */
     #presentation-controls-wrapper {
        display: none;
diff --git a/loleaflet/dist/menubar.css b/loleaflet/dist/menubar.css
index 7af9d31b6..f248ec4e0 100644
--- a/loleaflet/dist/menubar.css
+++ b/loleaflet/dist/menubar.css
@@ -191,7 +191,7 @@
 }
 
 /* desktop mode */
-@media (min-width: 769px) and (orientation: portrait),(min-height: 769px) and 
(orientation: landscape) {
+@media (min-width: 769px) {
     /* hide the button in desktop view */
     .main-menu-btn {
        position: absolute;
@@ -220,6 +220,26 @@
     }
 }
 
+@media (max-device-height: 768px) {
+    .main-menu-btn {
+       position: relative;
+       top: 5px;
+    }
+
+    #main-menu-state:not(:checked) ~ #main-menu {
+       display: none;
+    }
+
+    #main-menu {
+       top: 39px !important;
+    }
+
+    .main-nav {
+       position: absolute;
+       width: 100%;
+    }
+}
+
 /* Some more lo-menu specific customizations */
 
 /* The smartmenus plugin doesn't seem to have support for icons, so implement 
our own pseudo-elements */
commit ad4e98c2341a8fbc69ae119433a766136bf2da42
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Thu Oct 4 00:53:06 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:59 2018 +0200

    Cleanup the debugging logging.
    
    Change-Id: I52ba4be4017a1b4d7a2464c078ec2144511f11f7
    Reviewed-on: https://gerrit.libreoffice.org/61338
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit 6baa59678c34ec1e618ba0a2ac2b74983fbbd858)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 9439f0221..0530cab21 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -89,9 +89,6 @@ L.Control.MobileInput = L.Control.extend({
                    docLayer = this._map._docLayer,
                    unoKeyCode = handler._toUNOKeyCode(keyCode);
 
-               console.log('onKeyEvent: e.type === ' + e.type);
-               console.log('onKeyEvent: e.keyCode === "' + e.keyCode + '"');
-               console.log('onKeyEvent: e.charCode === "' + e.charCode + '"');
                this._keyHandled = this._keyHandled || false;
                if (this._isComposing) {
                        if (keyCode === 229 && charCode === 0) {
@@ -138,8 +135,7 @@ L.Control.MobileInput = L.Control.extend({
 
        onCompEvents: function (e) {
                var map = this._map;
-               console.log('onCompEvents: e.type === ' + e.type);
-               console.log('onCompEvents: e.data === "' + e.data + '"');
+
                if (e.type === 'compositionstart' || e.type === 
'compositionupdate') {
                        this._isComposing = true; // we are starting composing 
with IME
                        this._composingData = e.data; // cache what we have 
composed so far
@@ -154,9 +150,6 @@ L.Control.MobileInput = L.Control.extend({
        },
 
        onTextInput: function (e) {
-               console.log('onTextInput: e.type === ' + e.type);
-               console.log('onTextInput: e.data === "' + e.data + '"');
-
                if (!this._keyHandled) {
                        this._textData = e.data;
                        this._textArea.value = '';
@@ -166,9 +159,8 @@ L.Control.MobileInput = L.Control.extend({
        },
 
        onInput: function (e) {
-               console.log('onInput: e.inputType === ' + e.inputType);
-
                var backSpace = this._map.keyboard._toUNOKeyCode(8);
+
                // deferred processing of composition text or normal text; we 
can get
                // both in some cases, and based on the input event we need to 
decide
                // which one we actually need to use
commit 0219af868e2eb63f3736c110292bd229ae4960c7
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Thu Oct 4 00:41:34 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:54 2018 +0200

    android chrome: Make the insertion of . , ! $ and similar chars work.
    
    The trick is that actually the 'onInput' (the 'input') event decides
    what data we should actually use - if those from compose event, or from
    the text input event.
    
    With this, things finally seem to work reasonably well - I am able to
    insert even emoji :-)
    
    Change-Id: I9f19d1e56e4e638cf88b8497abb8eefd24e82cee
    Reviewed-on: https://gerrit.libreoffice.org/61337
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit 10bfc4b449577590c4de82cb15d73204be053a3c)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 45920f63a..9439f0221 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -143,9 +143,6 @@ L.Control.MobileInput = L.Control.extend({
                if (e.type === 'compositionstart' || e.type === 
'compositionupdate') {
                        this._isComposing = true; // we are starting composing 
with IME
                        this._composingData = e.data; // cache what we have 
composed so far
-                       if (e.data) {
-                               map._docLayer._postCompositionEvent(0, 'input', 
e.data);
-                       }
                }
 
                if (e.type === 'compositionend') {
@@ -161,17 +158,7 @@ L.Control.MobileInput = L.Control.extend({
                console.log('onTextInput: e.data === "' + e.data + '"');
 
                if (!this._keyHandled) {
-                       // Hack for making space in combination with 
autocompletion text
-                       // input work in Chrome on Andorid.
-                       //
-                       // Chrome (Android) IME triggers keyup/keydown input 
with
-                       // code 229 when hitting space (as with all composiiton 
events)
-                       // with addition to 'textinput' event, in which we only 
see that
-                       // space was entered.
-                       var data = e.data;
-                       if (data.length == 1 && data[0] === ' ') {
-                               map._docLayer._postKeyboardEvent('input', 
data[0].charCodeAt(), 0);
-                       }
+                       this._textData = e.data;
                        this._textArea.value = '';
                }
 
@@ -179,8 +166,25 @@ L.Control.MobileInput = L.Control.extend({
        },
 
        onInput: function (e) {
+               console.log('onInput: e.inputType === ' + e.inputType);
+
                var backSpace = this._map.keyboard._toUNOKeyCode(8);
-               if (e.inputType && e.inputType === 'deleteContentBackward' && 
this._lastInput !== backSpace) {
+               // deferred processing of composition text or normal text; we 
can get
+               // both in some cases, and based on the input event we need to 
decide
+               // which one we actually need to use
+               if (e.inputType === 'insertText') {
+                       if (this._textData) {
+                               for (var i = 0; i < this._textData.length; ++i) 
{
+                                       
map._docLayer._postKeyboardEvent('input', this._textData[i].charCodeAt(), 0);
+                               }
+                       }
+               }
+               else if (e.inputType === 'insertCompositionText') {
+                       if (this._composingData) {
+                               map._docLayer._postCompositionEvent(0, 'input', 
this._composingData);
+                       }
+               }
+               else if (e.inputType === 'deleteContentBackward' && 
this._lastInput !== backSpace) {
                        // this means we need to delete the entire interim 
composition;
                        // let's issue backspace that many times
                        if (this._composingData) {
@@ -189,6 +193,7 @@ L.Control.MobileInput = L.Control.extend({
                                }
                        }
                }
+
                L.DomEvent.stopPropagation(e);
        }
 });
commit 678e151e28d8069c6552e6e7b27b0744e080ccec
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Wed Oct 3 23:49:45 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:50 2018 +0200

    android chrome: Move the onTextInput handling to a separate method.
    
    Change-Id: I1cf091ed9bf82b719ff7bd5170b9e75ecb999d01
    Reviewed-on: https://gerrit.libreoffice.org/61336
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit ea5165794338a05837cc14257ebfcf0fa437c2ab)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 94084371b..45920f63a 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -75,7 +75,8 @@ L.Control.MobileInput = L.Control.extend({
                this._textArea.setAttribute('spellcheck', 'false');
                L.DomEvent.on(this._textArea, stopEvents, 
L.DomEvent.stopPropagation)
                        .on(this._textArea, 'keydown keypress keyup', 
this.onKeyEvents, this)
-                       .on(this._textArea, 'compositionstart compositionupdate 
compositionend textInput', this.onCompEvents, this)
+                       .on(this._textArea, 'compositionstart compositionupdate 
compositionend', this.onCompEvents, this)
+                       .on(this._textArea, 'textInput', this.onTextInput, this)
                        .on(this._textArea, 'input', this.onInput, this)
                        .on(this._textArea, 'focus', this.onGotFocus, this)
                        .on(this._textArea, 'blur', this.onLostFocus, this);
@@ -152,7 +153,14 @@ L.Control.MobileInput = L.Control.extend({
                        map._docLayer._postCompositionEvent(0, 'end', '');
                }
 
-               if (e.type === 'textInput' && !this._keyHandled) {
+               L.DomEvent.stopPropagation(e);
+       },
+
+       onTextInput: function (e) {
+               console.log('onTextInput: e.type === ' + e.type);
+               console.log('onTextInput: e.data === "' + e.data + '"');
+
+               if (!this._keyHandled) {
                        // Hack for making space in combination with 
autocompletion text
                        // input work in Chrome on Andorid.
                        //
@@ -166,6 +174,7 @@ L.Control.MobileInput = L.Control.extend({
                        }
                        this._textArea.value = '';
                }
+
                L.DomEvent.stopPropagation(e);
        },
 
commit 96ca59dba4b2c795d5b8878597314acee1422dc9
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Wed Oct 3 23:42:30 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:46 2018 +0200

    debugging: Set noCache when LOOL_SERVE_FROM_FS is set.
    
    Makes debugging on Android browser so much easier, as it is otherwise
    hard to force reload.
    
    Change-Id: Iede4fec1c2a110f0ae4b73095ed19f0ee29fa736
    Reviewed-on: https://gerrit.libreoffice.org/61335
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit fde08ea55af98b0374fe872870ebfcedc098c515)

diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index 5b3aed55b..35b43c27e 100644
--- a/wsd/FileServer.cpp
+++ b/wsd/FileServer.cpp
@@ -266,6 +266,10 @@ void FileServerRequestHandler::handleRequest(const 
HTTPRequest& request, Poco::M
     try
     {
         bool noCache = false;
+#if ENABLE_DEBUG
+        if (std::getenv("LOOL_SERVE_FROM_FS"))
+            noCache = true;
+#endif
         Poco::Net::HTTPResponse response;
         Poco::URI requestUri(request.getURI());
         LOG_TRC("Fileserver request: " << requestUri.toString());
commit d784a59fb5096385ab30cbd24269c79c8bc76e29
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Wed Oct 3 21:52:59 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:42 2018 +0200

    android chrome: Stop composing when we get an actual key event.
    
    So that it is possible to press Enter while still in composition.
    It is necessary to stop the composing itself, because otherwise the word
    gets duplicated on the new line as soon as the user presses a key.
    
    Change-Id: I78951a423715e71533f1a73d5bbe3b0f0f05e6cd
    Reviewed-on: https://gerrit.libreoffice.org/61334
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit f42e0bb4d1c99c1d229a638e59061ef7985d01e8)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 3fab3bf56..94084371b 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -88,10 +88,19 @@ L.Control.MobileInput = L.Control.extend({
                    docLayer = this._map._docLayer,
                    unoKeyCode = handler._toUNOKeyCode(keyCode);
 
+               console.log('onKeyEvent: e.type === ' + e.type);
+               console.log('onKeyEvent: e.keyCode === "' + e.keyCode + '"');
+               console.log('onKeyEvent: e.charCode === "' + e.charCode + '"');
                this._keyHandled = this._keyHandled || false;
                if (this._isComposing) {
+                       if (keyCode === 229 && charCode === 0) {
+                               return;
+                       }
+                       // stop the composing - so that we handle eg. Enter 
even during
+                       // composition
+                       this._isComposing = false;
                        this._lastInput = null;
-                       return;
+                       this._textArea.value = '';
                }
 
                docLayer._resetPreFetching();
commit 6db4171f694779b0ba0cf199a576df0cab88ac77
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Wed Oct 3 19:46:07 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:38 2018 +0200

    android chrome: We have to delete the interim results sometimes.
    
    When we are autocorrecting a word, we get a deleteContentBackward event;
    in that case we have to delete everything we have composed so far.
    
    Change-Id: I36f3d1afcb9b74ac75dee7d64832cc5a3d346045
    Reviewed-on: https://gerrit.libreoffice.org/61333
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit f176bb1e81b5f3769cb56752c7102f93f2eca024)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 25b0addff..3fab3bf56 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -128,14 +128,13 @@ L.Control.MobileInput = L.Control.extend({
 
        onCompEvents: function (e) {
                var map = this._map;
+               console.log('onCompEvents: e.type === ' + e.type);
+               console.log('onCompEvents: e.data === "' + e.data + '"');
                if (e.type === 'compositionstart' || e.type === 
'compositionupdate') {
                        this._isComposing = true; // we are starting composing 
with IME
-                       var txt = '';
-                       for (var i = 0; i < e.data.length; i++) {
-                               txt += e.data[i];
-                       }
-                       if (txt) {
-                               map._docLayer._postCompositionEvent(0, 'input', 
txt);
+                       this._composingData = e.data; // cache what we have 
composed so far
+                       if (e.data) {
+                               map._docLayer._postCompositionEvent(0, 'input', 
e.data);
                        }
                }
 
@@ -152,11 +151,6 @@ L.Control.MobileInput = L.Control.extend({
                        // code 229 when hitting space (as with all composiiton 
events)
                        // with addition to 'textinput' event, in which we only 
see that
                        // space was entered.
-                       //
-                       // TODO: Maybe make sure this is only triggered when 
keydown has
-                       // 229 code. Also we need to detect that composition 
was overriden
-                       // (part or whole word deleted) with the spell-checked 
word. (for
-                       // example: enter 'tar' and with spell-check correct 
that to 'rat')
                        var data = e.data;
                        if (data.length == 1 && data[0] === ' ') {
                                map._docLayer._postKeyboardEvent('input', 
data[0].charCodeAt(), 0);
@@ -169,7 +163,13 @@ L.Control.MobileInput = L.Control.extend({
        onInput: function (e) {
                var backSpace = this._map.keyboard._toUNOKeyCode(8);
                if (e.inputType && e.inputType === 'deleteContentBackward' && 
this._lastInput !== backSpace) {
-                       this._map._docLayer._postKeyboardEvent('input', 0, 
backSpace);
+                       // this means we need to delete the entire interim 
composition;
+                       // let's issue backspace that many times
+                       if (this._composingData) {
+                               for (var i = 0; i < this._composingData.length; 
++i) {
+                                       
this._map._docLayer._postKeyboardEvent('input', 0, backSpace);
+                               }
+                       }
                }
                L.DomEvent.stopPropagation(e);
        }
commit 51ec3547a6695888b64c32ac7155efadbfa9df34
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Wed Oct 3 17:39:38 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:34 2018 +0200

    android chrome: When autocompleting a word, don't enter it twice.
    
    Most of the text input on Android in Chrome works via the composition;
    only the space has to be entered via textInput.
    
    Change-Id: Icd6cea54a962f324215bb6438265e6500f28421d
    Reviewed-on: https://gerrit.libreoffice.org/61332
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit 04b858d90fb1de00f312efcbf131bacda5479e7c)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index f03b7ddc9..25b0addff 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -145,24 +145,21 @@ L.Control.MobileInput = L.Control.extend({
                }
 
                if (e.type === 'textInput' && !this._keyHandled) {
-                       // Hack for making space and spell-check text insert 
work
-                       // in Chrome (on Andorid) or Chrome with IME.
+                       // Hack for making space in combination with 
autocompletion text
+                       // input work in Chrome on Andorid.
                        //
                        // Chrome (Android) IME triggers keyup/keydown input 
with
                        // code 229 when hitting space (as with all composiiton 
events)
                        // with addition to 'textinput' event, in which we only 
see that
-                       // space was entered. Similar situation is also when 
inserting
-                       // a soft-keyboard spell-check item - it is visible 
only with
-                       // 'textinput' event (no composition event is fired).
-                       // To make this work we need to insert textinput.data 
here..
+                       // space was entered.
                        //
                        // TODO: Maybe make sure this is only triggered when 
keydown has
                        // 229 code. Also we need to detect that composition 
was overriden
                        // (part or whole word deleted) with the spell-checked 
word. (for
                        // example: enter 'tar' and with spell-check correct 
that to 'rat')
                        var data = e.data;
-                       for (var idx = 0; idx < data.length; idx++) {
-                               map._docLayer._postKeyboardEvent('input', 
data[idx].charCodeAt(), 0);
+                       if (data.length == 1 && data[0] === ' ') {
+                               map._docLayer._postKeyboardEvent('input', 
data[0].charCodeAt(), 0);
                        }
                        this._textArea.value = '';
                }
commit 0e293cc488cc2637b6361a3caaa49328f2a674e4
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue Oct 2 23:47:32 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:29 2018 +0200

    loleaflet: mobile: ensure to not create the ruler
    
    Change-Id: I7f985f93d8282533aa6cb32bff455b08011d7dfc
    Reviewed-on: https://gerrit.libreoffice.org/61281
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit acf5ebd540d322077030ed240e7dbba3e14411ea)

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 769b2969d..7a078ebd2 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -122,7 +122,7 @@ L.Map = L.Evented.extend({
                        if (!this.initComplete) {
                                this._fireInitComplete('doclayerinit');
                        }
-                       if (this._docLayer._docType == 'text') {
+                       if (!L.Browser.mobile && this._docLayer._docType == 
'text') {
                                var interactiveRuler = this._permission === 
'edit' ? true : false;
                                L.control.ruler({position:'topleft', 
interactive:interactiveRuler}).addTo(this);
                        }
commit 99e7b9f4b24500492a6d4824c7c368dc5d574265
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue Oct 2 22:34:01 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:24 2018 +0200

    loleafet: mobile: fix max-height screen size tablet
    
    Change-Id: I1c869089aee6991de34e06daf0a38d8b07f82a22
    Reviewed-on: https://gerrit.libreoffice.org/61280
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit edb617dc94643db2aa18e4762ac306e3e2bf9906)

diff --git a/loleaflet/dist/leaflet.css b/loleaflet/dist/leaflet.css
index 28de9a21b..332490ed5 100644
--- a/loleaflet/dist/leaflet.css
+++ b/loleaflet/dist/leaflet.css
@@ -788,7 +788,7 @@ input.clipboard {
        }
 }
 
-@media (max-width: 768px) {
+@media (max-width: 768px),(max-height: 768px) {
        .loleaflet-ruler {
                height: 0px;
                display: none;
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index 07565ff29..e7d38b5eb 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -81,7 +81,7 @@ body {
     width: 0;
 }
 
-@media (max-width: 768px) {
+@media (max-width: 768px),(max-height: 768px) {
     /* Show slidesorter beyond 768px only */
     #presentation-controls-wrapper {
        display: none;
diff --git a/loleaflet/dist/menubar.css b/loleaflet/dist/menubar.css
index 9649cadd8..7af9d31b6 100644
--- a/loleaflet/dist/menubar.css
+++ b/loleaflet/dist/menubar.css
@@ -191,7 +191,7 @@
 }
 
 /* desktop mode */
-@media (min-width: 769px) {
+@media (min-width: 769px) and (orientation: portrait),(min-height: 769px) and 
(orientation: landscape) {
     /* hide the button in desktop view */
     .main-menu-btn {
        position: absolute;
commit 3cc023490e8fa9a07029c6cd40c672b5057a772f
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue Oct 2 17:16:56 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:19 2018 +0200

    loleaflet: mobile fix after hitting back, on-screen keyboard...
    
    "permanently" disappears
    
    This forces to blur and focus the text area even it is focused
    because there is no way to determine when the soft on-screen keyboard
    disappears
    
    Change-Id: Ib89ecc42fb795e34032564a62715463dd944c588
    Reviewed-on: https://gerrit.libreoffice.org/61277
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 8c11f8c85ed6404a0e3972b4b905945dc4e210ba)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 8d5f42664..f03b7ddc9 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -34,9 +34,8 @@ L.Control.MobileInput = L.Control.extend({
                        return;
                }
 
-               if (focus === false) {
-                       this._textArea.blur();
-               } else {
+               this._textArea.blur();
+               if (focus !== false) {
                        this._textArea.focus();
                }
        },
commit 3735faabc81cf080d7c10dde45156fe822a157c4
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Mon Oct 1 18:11:25 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:15 2018 +0200

    wsd: Avoid parsing tile messages twice
    
    Change-Id: I049e7ce645999a4d0366ab34ffa75ab0d351947b
    (cherry picked from commit 8a318cc44d0a96bced7c565544c4772cfb936f93)
    Reviewed-on: https://gerrit.libreoffice.org/61211
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>
    (cherry picked from commit 8f2ea2eba7fbc58b2e2ab5c154e3c9b7a240f59e)

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 428455f0f..4753db07c 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -13,6 +13,7 @@
 
 #include <fstream>
 #include <sstream>
+#include <memory>
 
 #include <Poco/Net/HTTPResponse.h>
 #include <Poco/StringTokenizer.h>
@@ -1027,15 +1028,16 @@ void ClientSession::enqueueSendMessage(const 
std::shared_ptr<Message>& data)
         docBroker->assertCorrectThread();
 
     const std::string command = data->firstToken();
+    std::unique_ptr<TileDesc> tile;
     if (command == "tile:")
     {
         // Avoid sending tile if it has the same wireID as the previously sent 
tile
-        const TileDesc tile = TileDesc::parse(data->firstLine());
-        const std::string tileID = generateTileID(tile);
+        tile.reset(new TileDesc(TileDesc::parse(data->firstLine())));
+        const std::string tileID = generateTileID(*tile);
         auto iter = _oldWireIds.find(tileID);
-        if(iter != _oldWireIds.end() && tile.getWireId() != 0 && 
tile.getWireId() == iter->second)
+        if(iter != _oldWireIds.end() && tile->getWireId() != 0 && 
tile->getWireId() == iter->second)
         {
-            LOG_INF("WSD filters out a tile with the same wireID: " <<  
tile.serialize("tile:"));
+            LOG_INF("WSD filters out a tile with the same wireID: " <<  
tile->serialize("tile:"));
             return;
         }
     }
@@ -1045,10 +1047,9 @@ void ClientSession::enqueueSendMessage(const 
std::shared_ptr<Message>& data)
     size_t newSize = _senderQueue.enqueue(data);
 
     // Track sent tile
-    if (command == "tile:")
+    if (tile)
     {
-        const TileDesc tile = TileDesc::parse(data->firstLine());
-        traceTileBySend(tile, sizeBefore == newSize);
+        traceTileBySend(*tile, sizeBefore == newSize);
     }
 }
 
commit 0438cc7b665eb63231588656828962c395ad116f
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Mon Oct 1 18:04:47 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:10 2018 +0200

    Remove accidentally pushed log line
    
    Change-Id: Icd7fcb96725b3cf7fded199a5eae13ec2c109a58
    (cherry picked from commit 835959a617f388541edaf11b210897566b65b754)
    Reviewed-on: https://gerrit.libreoffice.org/61210
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>
    (cherry picked from commit e4766c150ffb1a7a71b93c089bbba0e17c75542f)

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index d28915ec8..428455f0f 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -1049,8 +1049,6 @@ void ClientSession::enqueueSendMessage(const 
std::shared_ptr<Message>& data)
     {
         const TileDesc tile = TileDesc::parse(data->firstLine());
         traceTileBySend(tile, sizeBefore == newSize);
-        if (sizeBefore != newSize)
-            LOG_INF("Sending new tile to client: " <<  
tile.serialize("tile:"));
     }
 }
 
commit a9760131bc969eaee9fc6b4024e5f0a750fc7e12
Author:     Jan Holesovsky <ke...@collabora.com>
AuthorDate: Tue Oct 2 17:14:33 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:36:02 2018 +0200

    calc: We currently cannot paste text/rtf or text/html via LOK...
    
    Change-Id: Ic699dddb2cbeaab4c13d030c2f8f3c71eeddac4f
    Reviewed-on: https://gerrit.libreoffice.org/61256
    Reviewed-by: Aron Budea <aron.bu...@collabora.com>
    Tested-by: Aron Budea <aron.bu...@collabora.com>
    (cherry picked from commit c648be85f51654302b16fd345471c98421da2cb4)

diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 33b8632c9..b3f45581d 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -2013,12 +2013,22 @@ L.TileLayer = L.GridLayer.extend({
                }
 
                // now try various mime types
-               var mimeTypes = [
-                       ['text/rtf', 'text/rtf'],
-                       ['text/html', 'text/html'],
-                       ['text/plain', 'text/plain;charset=utf-8'],
-                       ['Text', 'text/plain;charset=utf-8']
-               ];
+               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/plain', 'text/plain;charset=utf-8'],
+                               ['Text', 'text/plain;charset=utf-8']
+                       ];
+               } else {
+                       mimeTypes = [
+                               ['text/rtf', 'text/rtf'],
+                               ['text/html', 'text/html'],
+                               ['text/plain', 'text/plain;charset=utf-8'],
+                               ['Text', 'text/plain;charset=utf-8']
+                       ];
+               }
 
                for (var i = 0; i < mimeTypes.length; ++i) {
                        for (t = 0; t < types.length; ++t) {
commit f8fbb2b0b0ca1137b146218491721cf68d58f7a7
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Fri Sep 21 15:49:30 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:35:10 2018 +0200

    Fix warning: in case of a text document part number has no meaning
    
    In this case the part number is undefined, somewhere it is set to 0 or 1,
    but has no meaning at all. Just avoid using it with text ducments.
    
    Change-Id: Ic98217bf3ea6c86d37c34e42302bf456f7274975
    (cherry picked from commit 4b13430f6d63d950c07cb9ceda4e1bca7bec6422)
    Reviewed-on: https://gerrit.libreoffice.org/60877
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit f707ba7c753ab53ef454fa7c2a6418c4d0b17f77)

diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index eb0440046..33c7ca7c2 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -133,6 +133,8 @@ public:
 
     /// Clear wireId map anytime when client visible area changes (visible 
area, zoom, part number)
     void resetWireIdMap();
+
+    bool isTextDocument() const { return _isTextDocument; }
 private:
 
     /// SocketHandler: disconnection event.
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 02b112a95..32e20e8f5 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1357,13 +1357,14 @@ void 
DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
         for (const auto& newTile : tileCombined.getTiles())
         {
             const TileDesc& firstOldTile = *(requestedTiles.begin());
-            if(newTile.getPart() != firstOldTile.getPart() ||
-               newTile.getWidth() != firstOldTile.getWidth() ||
-               newTile.getHeight() != firstOldTile.getHeight() ||
-               newTile.getTileWidth() != firstOldTile.getTileWidth() ||
-               newTile.getTileHeight() != firstOldTile.getTileHeight() )
+            if(!session->isTextDocument() && newTile.getPart() != 
firstOldTile.getPart())
             {
-                LOG_WRN("Different visible area information in tile requests");
+                LOG_WRN("Different part numbers in tile requests");
+            }
+            else if (newTile.getTileWidth() != firstOldTile.getTileWidth() ||
+                     newTile.getTileHeight() != firstOldTile.getTileHeight() )
+            {
+                LOG_WRN("Different tile sizes in tile requests");
             }
 
             bool tileFound = false;
commit fa8e499729658245c23621d246067a3b8b7d78ff
Author:     Aron Budea <aron.bu...@collabora.com>
AuthorDate: Wed Sep 26 04:43:38 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:35:04 2018 +0200

    Menu: Add Hyperlink to Insert menu in Calc
    
    Change-Id: I1401315e0a679adfe074d68a8e3ac2ee45df5e80
    Reviewed-on: https://gerrit.libreoffice.org/61181
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit b649919cfaf12377f74fa1958ff3497a8987bf60)

diff --git a/loleaflet/src/control/Control.Menubar.js 
b/loleaflet/src/control/Control.Menubar.js
index 5fe17ada1..2b33c4e4f 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -333,6 +333,7 @@ L.Control.Menubar = L.Control.extend({
                                {uno: '.uno:InsertObjectChart'},
                                {name: _UNO('.uno:InsertAnnotation', 
'spreadsheet'), id: 'insertcomment', type: 'action'},
                                {type: 'separator'},
+                               {uno: '.uno:HyperlinkDialog'},
                                {uno: '.uno:InsertSymbol'}
                        ]},
                        {name: _UNO('.uno:FormatMenu', 'spreadsheet'), type: 
'menu', menu: [
commit 7cc0034190db40fa555a9442545d64493075728f
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue Sep 25 09:51:51 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:35:00 2018 +0200

    loleaflet: mobile: fix max-width screen size tablet
    
    Change-Id: I3337e669521f0f0f1dd749884f38dd35663491d3
    Reviewed-on: https://gerrit.libreoffice.org/60977
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit d1dc66ae5f33e68136a734d5687ab6903a30e442)

diff --git a/loleaflet/dist/leaflet.css b/loleaflet/dist/leaflet.css
index 2fb0a1925..28de9a21b 100644
--- a/loleaflet/dist/leaflet.css
+++ b/loleaflet/dist/leaflet.css
@@ -788,7 +788,7 @@ input.clipboard {
        }
 }
 
-@media (max-width: 767px) {
+@media (max-width: 768px) {
        .loleaflet-ruler {
                height: 0px;
                display: none;
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index 68349f2fb..07565ff29 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -81,7 +81,7 @@ body {
     width: 0;
 }
 
-@media (max-width: 767px) {
+@media (max-width: 768px) {
     /* Show slidesorter beyond 768px only */
     #presentation-controls-wrapper {
        display: none;
diff --git a/loleaflet/dist/menubar.css b/loleaflet/dist/menubar.css
index f192eacbf..9649cadd8 100644
--- a/loleaflet/dist/menubar.css
+++ b/loleaflet/dist/menubar.css
@@ -191,7 +191,7 @@
 }
 
 /* desktop mode */
-@media (min-width: 768px) {
+@media (min-width: 769px) {
     /* hide the button in desktop view */
     .main-menu-btn {
        position: absolute;
commit 7db188e4b0bfa4b638b5dc508d352f11138c9279
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Fri Sep 28 19:30:57 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:54 2018 +0200

    Upper limit of sent out versions of the same tile
    
    We try to decrease the network usage with avoiding sending out
    to much tiles to the client. When we already sent out two versions
    of the same tile without having the tileprocessed message from the
    client we delay sending out the next version to avoid spamming tiles
    on the network.
    
    Change-Id: Ia47cd7c0d3fb829f6777f0c3265970433591df19
    (cherry picked from commit ac2cd92d25815cd476008c6cd8035e989d0c8826)
    Reviewed-on: https://gerrit.libreoffice.org/61129
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit ea871566685f64949080735d430c7f767c63f582)

diff --git a/common/Common.hpp b/common/Common.hpp
index 978ef2c18..29008fd24 100644
--- a/common/Common.hpp
+++ b/common/Common.hpp
@@ -20,6 +20,8 @@ constexpr int CHILD_REBALANCE_INTERVAL_MS = CHILD_TIMEOUT_MS 
/ 10;
 constexpr int POLL_TIMEOUT_MS = COMMAND_TIMEOUT_MS / 10;
 constexpr int WS_SEND_TIMEOUT_MS = 1000;
 
+constexpr int TILE_ROUNDTRIP_TIMEOUT_MS = 5000;
+
 /// Pipe and Socket read buffer size.
 /// Should be large enough for ethernet packets
 /// which can be 1500 bytes long.
diff --git a/test/TileCacheTests.cpp b/test/TileCacheTests.cpp
index 535bafc30..f8eefa278 100644
--- a/test/TileCacheTests.cpp
+++ b/test/TileCacheTests.cpp
@@ -81,6 +81,7 @@ class TileCacheTests : public CPPUNIT_NS::TestFixture
     //CPPUNIT_TEST(testTileInvalidatePartImpress);
     CPPUNIT_TEST(testTileBeingRenderedHandling);
     CPPUNIT_TEST(testWireIDFilteringOnWSDSide);
+    CPPUNIT_TEST(testLimitTileVersionsOnFly);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -106,6 +107,7 @@ class TileCacheTests : public CPPUNIT_NS::TestFixture
     void testTileInvalidatePartImpress();
     void testTileBeingRenderedHandling();
     void testWireIDFilteringOnWSDSide();
+    void testLimitTileVersionsOnFly();
 
     void checkTiles(std::shared_ptr<LOOLWebSocket>& socket,
                     const std::string& type,
@@ -1282,6 +1284,66 @@ void TileCacheTests::testWireIDFilteringOnWSDSide()
     CPPUNIT_ASSERT_MESSAGE("Not expected tile message arrived!", tile.empty());
 }
 
+void TileCacheTests::testLimitTileVersionsOnFly()
+{
+    // We have an upper limit (2) for the versions of the same tile wsd send 
out
+    // without getting the tileprocessed message for the first tile message.
+    const char* testname = "testLimitTileVersionsOnFly ";
+
+    std::string documentPath, documentURL;
+    getDocumentPathAndURL("empty.odt", documentPath, documentURL, testname);
+    std::shared_ptr<LOOLWebSocket> socket = loadDocAndGetSocket(_uri, 
documentURL, testname);
+
+    // Set the client visible area
+    sendTextFrame(socket, "clientvisiblearea x=-2662 y=0 width=16000 
height=9875");
+    sendTextFrame(socket, "clientzoom tilepixelwidth=256 tilepixelheight=256 
tiletwipwidth=3200 tiletwipheight=3200");
+
+    // Type one character to trigger sending tiles
+    sendChar(socket, 'x', skNone, testname);
+
+    // Handle all tiles send by wsd
+    bool getTileResp = false;
+    do
+    {
+        std::string tile = getResponseString(socket, "tile:", testname, 1000);
+        getTileResp = !tile.empty();
+    } while(getTileResp);
+
+    // Type an other character to trigger sending tiles
+    sendChar(socket, 'x', skNone, testname);
+
+    // Handle all tiles sent by wsd
+    getTileResp = false;
+    do
+    {
+        std::string tile = getResponseString(socket, "tile:", testname, 1000);
+        getTileResp = !tile.empty();
+    } while(getTileResp);
+
+    // For the third invalidation wsd does not send the new tile since
+    // two versions of the same tile were already sent.
+    sendChar(socket, 'x', skNone, testname);
+
+    std::vector<char> tile1 = getResponseMessage(socket, "tile:", testname, 
1000);
+    CPPUNIT_ASSERT_MESSAGE("Not expected tile message arrived!", 
tile1.empty());
+
+    // When the next tileprocessed message arrive with correct tileID
+    // wsd sends the delayed tile
+    sendTextFrame(socket, "tileprocessed tile=0:0:0:3200:3200");
+
+    int arrivedTiles = 0;
+    bool gotTile = false;
+    do
+    {
+        std::vector<char> tile = getResponseMessage(socket, "tile:", testname, 
1000);
+        gotTile = !tile.empty();
+        if(gotTile)
+            ++arrivedTiles;
+    } while(gotTile);
+
+    CPPUNIT_ASSERT_EQUAL(1, arrivedTiles);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(TileCacheTests);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 8a4bb18cd..d28915ec8 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -1100,7 +1100,7 @@ void ClientSession::removeOutdatedTilesOnFly()
     {
         auto tileIter = _tilesOnFly.begin();
         double elapsedTimeMs = 
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()
 - tileIter->second).count();
-        if(elapsedTimeMs > 5000.0)
+        if(elapsedTimeMs > TILE_ROUNDTRIP_TIMEOUT_MS)
         {
             LOG_WRN("Tracker tileID was dropped because of time out. 
Tileprocessed message did not arrive");
             _tilesOnFly.erase(tileIter);
@@ -1110,6 +1110,18 @@ void ClientSession::removeOutdatedTilesOnFly()
     }
 }
 
+size_t ClientSession::countIdenticalTilesOnFly(const TileDesc& tile) const
+{
+    size_t count = 0;
+    std::string tileID = generateTileID(tile);
+    for(auto& tileItem : _tilesOnFly)
+    {
+        if(tileItem.first == tileID)
+            ++count;
+    }
+    return count;
+}
+
 Util::Rectangle ClientSession::getNormalizedVisibleArea() const
 {
     Util::Rectangle normalizedVisArea;
@@ -1242,7 +1254,7 @@ void ClientSession::handleTileInvalidation(const 
std::string& message,
                 Util::Rectangle tileRect (j * _tileWidthTwips, i * 
_tileHeightTwips, _tileWidthTwips, _tileHeightTwips);
                 if(invalidateRect.intersects(tileRect))
                 {
-                    invalidTiles.emplace_back(TileDesc(part, _tileWidthPixel, 
_tileHeightPixel, j * _tileWidthTwips, i * _tileHeightTwips, _tileWidthTwips, 
_tileHeightTwips, -1, 0, -1, false));
+                    invalidTiles.emplace_back(part, _tileWidthPixel, 
_tileHeightPixel, j * _tileWidthTwips, i * _tileHeightTwips, _tileWidthTwips, 
_tileHeightTwips, -1, 0, -1, false);
 
                     TileWireId oldWireId = 0;
                     auto iter = 
_oldWireIds.find(generateTileID(invalidTiles.back()));
@@ -1329,7 +1341,7 @@ void ClientSession::clearTileSubscription()
     _tilesBeingRendered.clear();
 }
 
-std::string ClientSession::generateTileID(const TileDesc& tile)
+std::string ClientSession::generateTileID(const TileDesc& tile) const
 {
     std::ostringstream tileID;
     tileID << tile.getPart() << ":" << tile.getTilePosX() << ":" << 
tile.getTilePosY() << ":"
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 38da74801..eb0440046 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -110,6 +110,7 @@ public:
     void clearTilesOnFly();
     size_t getTilesOnFlyCount() const { return _tilesOnFly.size(); }
     void removeOutdatedTilesOnFly();
+    size_t countIdenticalTilesOnFly(const TileDesc& tile) const;
 
     Util::Rectangle getVisibleArea() const { return _clientVisibleArea; }
     /// Visible area can have negative value as position, but we have tiles 
only in the positive range
@@ -174,7 +175,7 @@ private:
                                 const std::shared_ptr<DocumentBroker>& 
docBroker);
 
     /// Generate a unique id for a tile
-    std::string generateTileID(const TileDesc& tile);
+    std::string generateTileID(const TileDesc& tile) const;
 
 private:
     std::weak_ptr<DocumentBroker> _docBroker;
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index c6b589899..02b112a95 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1424,12 +1424,26 @@ void DocumentBroker::sendRequestedTiles(const 
std::shared_ptr<ClientSession>& se
     std::deque<TileDesc>& requestedTiles = session->getRequestedTiles();
     if (!requestedTiles.empty())
     {
+        size_t delayedTiles = 0;
         std::vector<TileDesc> tilesNeedsRendering;
         while(session->getTilesOnFlyCount() + 
session->getTilesBeingRenderedCount() < tilesOnFlyUpperLimit &&
-              !requestedTiles.empty())
+              !requestedTiles.empty() &&
+              // If we delayed all tiles we don't send any tile (we will when 
next tileprocessed message arrives)
+              delayedTiles < requestedTiles.size())
         {
             TileDesc& tile = *(requestedTiles.begin());
 
+            // We already sent out two versions of the same tile, let's not 
send the third one
+            // until we get a tileprocessed message for this specific tile.
+            if (session->countIdenticalTilesOnFly(tile) >= 2)
+            {
+                requestedTiles.push_back(requestedTiles.front());
+                requestedTiles.pop_front();
+                delayedTiles += 1;
+                LOG_INF("Requested tile was delayed!");
+                continue;
+            }
+
             // Satisfy as many tiles from the cache.
             std::unique_ptr<std::fstream> cachedTile = 
_tileCache->lookupTile(tile);
             if (cachedTile)
commit 4d4fb3a2d6f7443bc6653b556e71ee166ed621bc
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Wed Sep 26 22:15:41 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:49 2018 +0200

    Better to have a smaller tiles-on-fly limit
    
    It's good if this limit big enough to send all the tiles of the
    current visible area at once, so the tiles arrive at the same
    time to the client by zoom or scroll. Now this value is set to a bit
    bigger so if we have a small amount of tiles before zoom / scroll we'll
    still see the same on the client side.
    
    (cherry picked from commit a1e2acd5effb1c805f06c78040b71d30fb821be4)
    
    Change-Id: I8e28dbf6197fe2f683fe9528e9a32c15a191b20e
    Reviewed-on: https://gerrit.libreoffice.org/61128
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 6cbd1445cdc6451aa0a93cb8700fe967194228df)

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 764054d1b..c6b589899 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1406,7 +1406,7 @@ void DocumentBroker::sendRequestedTiles(const 
std::shared_ptr<ClientSession>& se
                                      std::ceil(normalizedVisArea._y1 / 
session->getTileHeightInTwips()) + 1;
         const int tilesInVisArea = tilesFitOnWidth * tilesFitOnHeight;
 
-        tilesOnFlyUpperLimit = std::max(TILES_ON_FLY_MIN_UPPER_LIMIT, 
tilesInVisArea * 1.5f);
+        tilesOnFlyUpperLimit = std::max(TILES_ON_FLY_MIN_UPPER_LIMIT, 
tilesInVisArea * 1.1f);
     }
     else
     {
commit 2bef97a4773579ae44c0d72b8db1fc71808fd5b8
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Wed Sep 26 22:15:37 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:44 2018 +0200

    Filter out tiles by wired on wsd side too
    
    Some times tiles with the same wireID survives the wireID
    filtering in kit, so we should do that in wsd too.
    The issue is with the tilesBeingRendered construction. First when
    one tile is filtered out on kit side the client remains subcribed
    to the tile, since wsd does not know filtering happened.
    Second via the tilesBeingRendered object more clients can be subcribed
    to the same tile and so when one client request a new version of this
    tile (with an old wireID) the rendered tile is sent to all subscribed
    clients even if the other clients has up-to-date tiles.
    
    (cherry picked from commit 9683f53a9c81dfcb9624705cbeeed48ec426ca3b)
    
    Change-Id: I4ca6b7a83a5d6979a9f924d766a71aba5e5362c7
    Reviewed-on: https://gerrit.libreoffice.org/61127
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 76aa5e07958dc297001a36b26b3d0069f5da4089)

diff --git a/test/TileCacheTests.cpp b/test/TileCacheTests.cpp
index 2c8f241d8..535bafc30 100644
--- a/test/TileCacheTests.cpp
+++ b/test/TileCacheTests.cpp
@@ -80,6 +80,7 @@ class TileCacheTests : public CPPUNIT_NS::TestFixture
     //CPPUNIT_TEST(testTileInvalidatePartCalc);
     //CPPUNIT_TEST(testTileInvalidatePartImpress);
     CPPUNIT_TEST(testTileBeingRenderedHandling);
+    CPPUNIT_TEST(testWireIDFilteringOnWSDSide);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -104,6 +105,7 @@ class TileCacheTests : public CPPUNIT_NS::TestFixture
     void testTileInvalidatePartCalc();
     void testTileInvalidatePartImpress();
     void testTileBeingRenderedHandling();
+    void testWireIDFilteringOnWSDSide();
 
     void checkTiles(std::shared_ptr<LOOLWebSocket>& socket,
                     const std::string& type,
@@ -1204,6 +1206,82 @@ void TileCacheTests::testTileBeingRenderedHandling()
     }
 }
 
+void TileCacheTests::testWireIDFilteringOnWSDSide()
+{
+    const char* testname = "testWireIDFilteringOnWSDSide ";
+
+    std::string documentPath, documentURL;
+    getDocumentPathAndURL("empty.odt", documentPath, documentURL, testname);
+    std::shared_ptr<LOOLWebSocket> socket1 = loadDocAndGetSocket(_uri, 
documentURL, testname);
+    // Set the client visible area
+    sendTextFrame(socket1, "clientvisiblearea x=-4005 y=0 width=50490 
height=72300");
+    sendTextFrame(socket1, "clientzoom tilepixelwidth=256 tilepixelheight=256 
tiletwipwidth=3840 tiletwipheight=3840");
+
+    std::shared_ptr<LOOLWebSocket> socket2 = loadDocAndGetSocket(_uri, 
documentURL, testname, true);
+    // Set the client visible area
+    sendTextFrame(socket1, "clientvisiblearea x=-4005 y=0 width=50490 
height=72300");
+    sendTextFrame(socket1, "clientzoom tilepixelwidth=256 tilepixelheight=256 
tiletwipwidth=3840 tiletwipheight=3840");
+
+    //1. First make the first client to trigger the kit to filter out tiles 
based on identical wireIDs
+
+    // Type one character to trigger invalidation
+    sendChar(socket1, 'x', skNone, testname);
+
+    // First wsd forwards the invalidation
+    assertResponseString(socket1, "invalidatetiles:", testname);
+
+    // For the first input wsd will send all invalidated tiles
+    int arrivedTiles = 0;
+    bool gotTile = false;
+    do
+    {
+        std::vector<char> tile = getResponseMessage(socket1, "tile:", 
testname);
+        gotTile = !tile.empty();
+        if(gotTile)
+            ++arrivedTiles;
+    } while(gotTile);
+
+    CPPUNIT_ASSERT_MESSAGE("We expect two tiles at least!", arrivedTiles > 1);
+
+    // Type an other character
+    sendChar(socket1, 'x', skNone, testname);
+    assertResponseString(socket1, "invalidatetiles:", testname);
+
+    // For the second input wsd will send one tile, since other tiles are 
indentical
+    arrivedTiles = 0;
+    gotTile = false;
+    do
+    {
+        std::vector<char> tile = getResponseMessage(socket1, "tile:", 
testname);
+        gotTile = !tile.empty();
+        if(gotTile)
+            ++arrivedTiles;
+    } while(gotTile);
+
+    CPPUNIT_ASSERT_EQUAL(1, arrivedTiles);
+
+    //2. Now request the same tiles by the other client (e.g. scroll to the 
same view)
+
+    sendTextFrame(socket2, "tilecombine part=0 width=256 height=256 
tileposx=0,3840,7680 tileposy=0,0,0 tilewidth=3840 tileheight=3840");
+
+    // We expect three tiles sent to the second client
+    arrivedTiles = 0;
+    gotTile = false;
+    do
+    {
+        std::vector<char> tile = getResponseMessage(socket2, "tile:", 
testname);
+        gotTile = !tile.empty();
+        if(gotTile)
+            ++arrivedTiles;
+    } while(gotTile);
+
+    CPPUNIT_ASSERT_EQUAL(3, arrivedTiles);
+
+    // wsd should not send tiles messages for the first client
+    std::vector<char> tile = getResponseMessage(socket1, "tile:", testname);
+    CPPUNIT_ASSERT_MESSAGE("Not expected tile message arrived!", tile.empty());
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(TileCacheTests);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index eb8414ff5..8a4bb18cd 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -1019,6 +1019,41 @@ bool ClientSession::forwardToClient(const 
std::shared_ptr<Message>& payload)
     return true;
 }
 
+void ClientSession::enqueueSendMessage(const std::shared_ptr<Message>& data)
+{
+    const std::shared_ptr<DocumentBroker> docBroker = _docBroker.lock();
+    // If in the correct thread - no need for wakeups.
+    if (docBroker)
+        docBroker->assertCorrectThread();
+
+    const std::string command = data->firstToken();
+    if (command == "tile:")
+    {
+        // Avoid sending tile if it has the same wireID as the previously sent 
tile
+        const TileDesc tile = TileDesc::parse(data->firstLine());
+        const std::string tileID = generateTileID(tile);
+        auto iter = _oldWireIds.find(tileID);
+        if(iter != _oldWireIds.end() && tile.getWireId() != 0 && 
tile.getWireId() == iter->second)
+        {
+            LOG_INF("WSD filters out a tile with the same wireID: " <<  
tile.serialize("tile:"));
+            return;
+        }
+    }
+
+    LOG_TRC(getName() << " enqueueing client message " << data->id());
+    size_t sizeBefore = _senderQueue.size();
+    size_t newSize = _senderQueue.enqueue(data);
+
+    // Track sent tile
+    if (command == "tile:")
+    {
+        const TileDesc tile = TileDesc::parse(data->firstLine());
+        traceTileBySend(tile, sizeBefore == newSize);
+        if (sizeBefore != newSize)
+            LOG_INF("Sending new tile to client: " <<  
tile.serialize("tile:"));
+    }
+}
+
 Authorization ClientSession::getAuthorization() const
 {
     Poco::URI::QueryParameters queryParams = _uriPublic.getQueryParameters();
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 67664ee35..38da74801 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -78,25 +78,7 @@ public:
         return true;
     }
 
-    void enqueueSendMessage(const std::shared_ptr<Message>& data)
-    {
-        const auto docBroker = _docBroker.lock();
-        // If in the correct thread - no need for wakeups.
-        if (docBroker)
-            docBroker->assertCorrectThread();
-
-        LOG_TRC(getName() << " enqueueing client message " << data->id());
-        size_t sizeBefore = _senderQueue.size();
-        size_t newSize = _senderQueue.enqueue(data);
-
-        // Track sent tile
-        const std::string command = data->firstToken();
-        if (command == "tile:")
-        {
-            const TileDesc tile = TileDesc::parse(data->firstLine());
-            traceTileBySend(tile, sizeBefore == newSize);
-        }
-    }
+    void enqueueSendMessage(const std::shared_ptr<Message>& data);
 
     /// Set the save-as socket which is used to send convert-to results.
     void setSaveAsSocket(const std::shared_ptr<StreamSocket>& socket)
diff --git a/wsd/TestStubs.cpp b/wsd/TestStubs.cpp
index 9a56e0a67..659d8476d 100644
--- a/wsd/TestStubs.cpp
+++ b/wsd/TestStubs.cpp
@@ -28,4 +28,6 @@ void ClientSession::traceUnSubscribeToTile(const std::string& 
/*tileCacheName*/)
 
 void ClientSession::clearTileSubscription() {};
 
+void ClientSession::enqueueSendMessage(const std::shared_ptr<Message>& 
/*data*/) {};
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 01c11d240ccbb02356efc4a59ceba32b07f16a8b
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Wed Sep 26 22:16:11 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:38 2018 +0200

    Don't send tiles which was changed outside of the visible area
    
    Since this commit:
    9473908d45a884827356b504c5f768e2f29cc46b
    We can avoid that, because the tiles will be invalidated
    on the client side and when the client visible area changes
    the invalidated tiles are requested anyway.
    
    (cherry picked from commit a1a0bf3718f5dff3996a3ace98a6a9cb0f10f55b)
    
    Change-Id: I272e3b4b87380ae574c16a2b480dbc8caabf4b32
    Reviewed-on: https://gerrit.libreoffice.org/61126
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 0726bdf1df4982febd6b0a7cba1c0736be0b488e)

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index b097d5dd3..eb8414ff5 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -1185,9 +1185,14 @@ void ClientSession::handleTileInvalidation(const 
std::string& message,
     int part = result.first;
     Util::Rectangle& invalidateRect = result.second;
 
+    // We can ignore the invalidation if it's outside of the visible area
+    if(!normalizedVisArea.intersects(invalidateRect))
+        return;
+
     if( part == -1 ) // If no part is specifed we use the part used by the 
client
         part = _clientSelectedPart;
 
+
     std::vector<TileDesc> invalidTiles;
     if(part == _clientSelectedPart || _isTextDocument)
     {
commit 9330ed38d70dca1bc41bd86f09f72abe2a8c367e
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Wed Sep 26 22:15:30 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:32 2018 +0200

    Fix tilesBeingRendered tracking by client
    
    I changed the code in this commit:
    c2a5f6acb0f1e93f19104b761661c852d930fb9e
    
    To make kit send a tilecombine message even if it does not
    send actual tile data so we can track that the rendering was
    done and so we can update the clients' _tilesBeingRendered
    list. The issue is that tileBeingRendered object
    belongs to not only one client, but more and so we don't
    know which client gets the actual empty tile response.
    
    So revert this method and rather use a smaller timeout for "waiting" the
    arrival of the rendered tile.
    
    (cherry picked from commit 07e99ad336c87f3d7e211abaaf18639be27c2929)
    
    Change-Id: I2dbbab1a62b81cbbb5314f2f37fdbc3415c69130
    Reviewed-on: https://gerrit.libreoffice.org/61125
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 97a5856c5b94c826d0ef987b5b29204fd9d70af3)

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 6d8bae5f8..50ed15435 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -1034,9 +1034,7 @@ public:
                 // The tile content is identical to what the client already 
has, so skip it
                 LOG_TRC("Match for tile #" << tileIndex << " at (" << 
positionX << "," <<
                         positionY << ") oldhash==hash (" << hash << "), 
wireId: " << wireId << " skipping");
-                tiles[tileIndex].setWireId(wireId);
-                tiles[tileIndex].setImgSize(0);
-                tileIndex++;
+                tiles.erase(tiles.begin() + tileIndex);
                 continue;
             }
 
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index ca43bc426..b097d5dd3 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -50,8 +50,7 @@ ClientSession::ClientSession(const std::string& id,
     _tileHeightPixel(0),
     _tileWidthTwips(0),
     _tileHeightTwips(0),
-    _isTextDocument(false),
-    _tilesBeingRendered(0)
+    _isTextDocument(false)
 {
     const size_t curConnections = ++LOOLWSD::NumConnections;
     LOG_INF("ClientSession ctor [" << getName() << "], current number of 
connections: " << curConnections);
@@ -1275,9 +1274,9 @@ void ClientSession::removeOutdatedTileSubscriptions()
     while(iterator != _tilesBeingRendered.end())
     {
         double elapsedTime = 
docBroker->tileCache().getTileBeingRenderedElapsedTimeMs(*iterator);
-        if(elapsedTime < 0.0 && elapsedTime > 5000.0)
+        if(elapsedTime < 0.0 && elapsedTime > 200.0)
         {
-            LOG_WRN("Tracked TileBeingRendered was dropped because of time 
out.");
+            LOG_INF("Tracked TileBeingRendered was dropped because of time 
out.");
             _tilesBeingRendered.erase(iterator);
         }
         else
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 1d1adf746..764054d1b 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1543,16 +1543,25 @@ void DocumentBroker::handleTileCombinedResponse(const 
std::vector<char>& payload
 
     try
     {
-        const auto tileCombined = TileCombined::parse(firstLine);
-        const auto buffer = payload.data();
-        auto offset = firstLine.size() + 1;
+        const size_t length = payload.size();
+        if (firstLine.size() < static_cast<std::string::size_type>(length) - 1)
+        {
+            const auto tileCombined = TileCombined::parse(firstLine);
+            const auto buffer = payload.data();
+            size_t offset = firstLine.size() + 1;
 
-        std::unique_lock<std::mutex> lock(_mutex);
+            std::unique_lock<std::mutex> lock(_mutex);
 
-        for (const auto& tile : tileCombined.getTiles())
+            for (const auto& tile : tileCombined.getTiles())
+            {
+                tileCache().saveTileAndNotify(tile, buffer + offset, 
tile.getImgSize());
+                offset += tile.getImgSize();
+            }
+        }
+        else
         {
-            tileCache().saveTileAndNotify(tile, buffer + offset, 
tile.getImgSize());
-            offset += tile.getImgSize();
+            LOG_WRN("Dropping empty tilecombine response: " << firstLine);
+            // They will get re-issued if we don't forget them.
         }
     }
     catch (const std::exception& exc)
diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp
index a00987f3b..250d070a1 100644
--- a/wsd/TileCache.cpp
+++ b/wsd/TileCache.cpp
@@ -192,17 +192,6 @@ void TileCache::saveTileAndNotify(const TileDesc& tile, 
const char *data, const
 
     std::shared_ptr<TileBeingRendered> tileBeingRendered = 
findTileBeingRendered(tile);
 
-    // Kit did not send image data, because tile has the same wireID as the 
previously sent tile
-    // We need to remove only the subscriptions
-    if(size == 0)
-    {
-        if(tileBeingRendered && tileBeingRendered->getVersion() <= 
tile.getVersion())
-        {
-            forgetTileBeingRendered(tileBeingRendered, tile);
-        }
-        return;
-    }
-
     // Save to disk.
     const auto cachedName = (tileBeingRendered ? 
tileBeingRendered->getCacheName()
                                                : cacheFileName(tile));
commit 21e1c0efd578ab6219d4280f17ec5beff680148f
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Wed Sep 26 22:15:26 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:25 2018 +0200

    Avoid rendering / sending the same tile twice
    
    When we're doing the prerendering we also need to register
    a tileBeingRendered object, so we know that the given tile
    will arrive soon. In earlier version of the code, rendering
    and sending of the tile was not separated in time, but now it is,
    so we need create a tileBeingRendered object even if we don't
    subscribe the client to it yet.
    
    (cherry picked from commit 5260eae05f89056d4ffc1bdfad1927c3c5f9706d)
    
    Change-Id: I1374c22e5ffebe1d6fcc6e37261ddcedeb9066ec
    Reviewed-on: https://gerrit.libreoffice.org/61124
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit c2d800bd345a14c006697e4fd8a75fcadf36557c)

diff --git a/test/TileCacheTests.cpp b/test/TileCacheTests.cpp
index ac670792c..2c8f241d8 100644
--- a/test/TileCacheTests.cpp
+++ b/test/TileCacheTests.cpp
@@ -79,6 +79,7 @@ class TileCacheTests : public CPPUNIT_NS::TestFixture
     // temporarily disable
     //CPPUNIT_TEST(testTileInvalidatePartCalc);
     //CPPUNIT_TEST(testTileInvalidatePartImpress);
+    CPPUNIT_TEST(testTileBeingRenderedHandling);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -102,6 +103,7 @@ class TileCacheTests : public CPPUNIT_NS::TestFixture
     void testTileInvalidateCalc();
     void testTileInvalidatePartCalc();
     void testTileInvalidatePartImpress();
+    void testTileBeingRenderedHandling();
 
     void checkTiles(std::shared_ptr<LOOLWebSocket>& socket,
                     const std::string& type,
@@ -1146,6 +1148,62 @@ void 
TileCacheTests::requestTiles(std::shared_ptr<LOOLWebSocket>& socket, const
     }
 }
 
+void TileCacheTests::testTileBeingRenderedHandling()
+{
+    // The issue here was that we requested the tile of the same tile twice
+    // and so sometimes we got the same tile message twice from wsd.
+    const char* testname = "testTileBeingRenderedHandling ";
+
+    std::string documentPath, documentURL;
+    getDocumentPathAndURL("empty.odt", documentPath, documentURL, testname);
+    std::shared_ptr<LOOLWebSocket> socket = loadDocAndGetSocket(_uri, 
documentURL, testname);
+
+    // Set the client visible area
+    sendTextFrame(socket, "clientvisiblearea x=-2662 y=0 width=16000 
height=9875");
+    sendTextFrame(socket, "clientzoom tilepixelwidth=256 tilepixelheight=256 
tiletwipwidth=3200 tiletwipheight=3200");
+
+    // Type one character to trigger invalidation
+    sendChar(socket, 'x', skNone, testname);
+
+    // First wsd forwards the invalidation
+    assertResponseString(socket, "invalidatetiles:", testname);
+
+    // For the first input wsd will send all invalidated tiles
+    int arrivedTiles = 0;
+    bool gotTile = false;
+    do
+    {
+        std::vector<char> tile = getResponseMessage(socket, "tile:", testname);
+        gotTile = !tile.empty();
+        if(gotTile)
+            ++arrivedTiles;
+    } while(gotTile);
+
+    CPPUNIT_ASSERT_MESSAGE("We expect two tiles at least!", arrivedTiles > 1);
+
+    // For the later inputs wsd will send one tile, since other ones are 
indentical
+    for(int i = 0; i < 10; ++i)
+    {
+        // Type an other character
+        sendChar(socket, 'x', skNone, testname);
+        assertResponseString(socket, "invalidatetiles:", testname);
+
+        arrivedTiles = 0;
+        gotTile = false;
+        do
+        {
+            std::vector<char> tile = getResponseMessage(socket, "tile:", 
testname, 1000);
+            gotTile = !tile.empty();
+            if(gotTile)
+                ++arrivedTiles;
+        } while(gotTile);
+
+        CPPUNIT_ASSERT_EQUAL(1, arrivedTiles);
+
+        sendTextFrame(socket, "tileprocessed tile=0:0:0:3200:3200");
+    }
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(TileCacheTests);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index e01af9069..1d1adf746 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1321,15 +1321,16 @@ void 
DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
     std::vector<TileDesc> tilesNeedsRendering;
     for (auto& tile : tileCombined.getTiles())
     {
+        tile.setVersion(++_tileVersion);
         std::unique_ptr<std::fstream> cachedTile = 
_tileCache->lookupTile(tile);
         if(cachedTile)
             cachedTile->close();
         else
         {
             // Not cached, needs rendering.
-            tile.setVersion(++_tileVersion);
             tilesNeedsRendering.push_back(tile);
             _debugRenderedTileCount++;
+            tileCache().registerTileBeingRendered(tile);
         }
     }
 
@@ -1459,9 +1460,13 @@ void DocumentBroker::sendRequestedTiles(const 
std::shared_ptr<ClientSession>& se
             else
             {
                 // Not cached, needs rendering.
-                tile.setVersion(++_tileVersion);
-                tilesNeedsRendering.push_back(tile);
-                _debugRenderedTileCount++;
+                if (!tileCache().hasTileBeingRendered(tile) || // There is no 
in progress rendering of the given tile
+                    tileCache().getTileBeingRenderedVersion(tile) < 
tile.getVersion()) // We need a newer version
+                {
+                    tile.setVersion(++_tileVersion);
+                    tilesNeedsRendering.push_back(tile);
+                    _debugRenderedTileCount++;
+                }
                 tileCache().subscribeToTileRendering(tile, session);
             }
             requestedTiles.pop_front();
diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp
index 8e0da8cfe..a00987f3b 100644
--- a/wsd/TileCache.cpp
+++ b/wsd/TileCache.cpp
@@ -151,6 +151,20 @@ double TileCache::getTileBeingRenderedElapsedTimeMs(const 
std::string& tileCache
     return iterator->second->getElapsedTimeMs();
 }
 
+bool TileCache::hasTileBeingRendered(const TileDesc& tile)
+{
+    return (findTileBeingRendered(tile) != nullptr);
+}
+
+int TileCache::getTileBeingRenderedVersion(const TileDesc& tile)
+{
+    std::shared_ptr<TileBeingRendered> tileBeingRendered = 
findTileBeingRendered(tile);
+    if (tileBeingRendered)
+        return tileBeingRendered->getVersion();
+    else
+        return 0;
+}
+
 std::unique_ptr<std::fstream> TileCache::lookupTile(const TileDesc& tile)
 {
     if (!_tileCachePersistent)
@@ -535,6 +549,29 @@ void TileCache::subscribeToTileRendering(const TileDesc& 
tile, const std::shared
     }
 }
 
+void TileCache::registerTileBeingRendered(const TileDesc& tile)
+{
+    std::shared_ptr<TileBeingRendered> tileBeingRendered = 
findTileBeingRendered(tile);
+    if (tileBeingRendered)
+    {
+        const auto duration = (std::chrono::steady_clock::now() - 
tileBeingRendered->getStartTime());
+        if 
(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() > 
COMMAND_TIMEOUT_MS)
+        {
+            // Tile painting has stalled. Reissue.
+            tileBeingRendered->setVersion(tile.getVersion());
+        }
+    }
+    else
+    {
+        const std::string cachedName = cacheFileName(tile);
+
+        assert(_tilesBeingRendered.find(cachedName) == 
_tilesBeingRendered.end());
+
+        tileBeingRendered = std::make_shared<TileBeingRendered>(cachedName, 
tile);
+        _tilesBeingRendered[cachedName] = tileBeingRendered;
+    }
+}
+
 std::string TileCache::cancelTiles(const std::shared_ptr<ClientSession> 
&subscriber)
 {
     assert(subscriber && "cancelTiles expects valid subscriber");
diff --git a/wsd/TileCache.hpp b/wsd/TileCache.hpp
index 146e505ae..3d756fee0 100644
--- a/wsd/TileCache.hpp
+++ b/wsd/TileCache.hpp
@@ -46,6 +46,11 @@ public:
     /// Otherwise returns 0 to signify a subscription exists.
     void subscribeToTileRendering(const TileDesc& tile, const 
std::shared_ptr<ClientSession>& subscriber);
 
+    /// Create the TileBeingRendered object for the given tile indicating that 
the tile was sent to
+    /// the kit for rendering. Note: subscribeToTileRendering calls this 
internally, so you don't need
+    /// to call this method if you need also to subcribe for the rendered tile.
+    void registerTileBeingRendered(const TileDesc& tile);
+
     /// Cancels all tile requests by the given subscriber.
     std::string cancelTiles(const std::shared_ptr<ClientSession>& subscriber);
 
@@ -82,6 +87,9 @@ public:
     void forgetTileBeingRendered(std::shared_ptr<TileCache::TileBeingRendered> 
tileBeingRendered, const TileDesc& tile);
     double getTileBeingRenderedElapsedTimeMs(const std::string& tileCacheName) 
const;
 
+    bool hasTileBeingRendered(const TileDesc& tile);
+    int getTileBeingRenderedVersion(const TileDesc& tile);
+
     void setThreadOwner(const std::thread::id &id) { _owner = id; }
     void assertCorrectThread();
 
commit 4de804fad8438449a6544297ce0258b8916aeac9
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Wed Sep 26 22:15:18 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:17 2018 +0200

    loleaflet: Reset invalid counter when request new tiles by view change
    
    This invalidCount does not work perfectly, since we don't always
    send tile after every invalidation message (e.g. tile deduplication
    on severs side, wireID handling). So this invalidCount usage should
    be reconsidered. Now just avoid to have a tile still invalid after
    we requested (and get) the newest tile when we are changing the view.
    
    Change-Id: I249be63611767af02b04e142bb1c2afcb3a8eebb
    (cherry picked from commit b7f76d24fb49433c1d6d3b540a33530a482e6df3)
    Reviewed-on: https://gerrit.libreoffice.org/61123
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit c302c79eab8aa089a58ec2ba411a3580355cda79)

diff --git a/loleaflet/src/layer/tile/GridLayer.js 
b/loleaflet/src/layer/tile/GridLayer.js
index a5860a9c0..067509d22 100644
--- a/loleaflet/src/layer/tile/GridLayer.js
+++ b/loleaflet/src/layer/tile/GridLayer.js
@@ -542,6 +542,9 @@ L.GridLayer = L.Layer.extend({
                                if (tile && tile.loaded && !invalid) {
                                        tile.current = true;
                                        newView = false;
+                               } else if (invalid) {
+                                       tile._invalidCount = 1;
+                                       queue.push(coords);
                                } else {
                                        queue.push(coords);
                                }
commit 18e00fd07f8de1521e83c826a7e20298d56e2324
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Fri Sep 21 16:04:53 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:13 2018 +0200

    Use a bigger timeout for waiting tileprocessed message
    
    The main purpose of this time-out is to avoid waiting forever for lost
    tile messages, but since it rare to loose them we can use bigger value,
    so we can avoid to send new tiles to a slow network.
    
    Note that the used time stamp does not mean the time when the tile
    actually send to the client, but the time when it gets to the sender
    queue.
    
    Change-Id: I230d85c38b3a5dafd195851d0cf4caac23149e3e
    (cherry picked from commit b623aca57ea47c785ab70ff6ccec58acbff63d75)
    Reviewed-on: https://gerrit.libreoffice.org/60879
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 7253dd1499db54550a4e4e887820027c9c8a4c31)

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 9ef6cf07f..ca43bc426 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -1066,7 +1066,7 @@ void ClientSession::removeOutdatedTilesOnFly()
     {
         auto tileIter = _tilesOnFly.begin();
         double elapsedTimeMs = 
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()
 - tileIter->second).count();
-        if(elapsedTimeMs > 3000)
+        if(elapsedTimeMs > 5000.0)
         {
             LOG_WRN("Tracker tileID was dropped because of time out. 
Tileprocessed message did not arrive");
             _tilesOnFly.erase(tileIter);
commit ec026416aef31041c7623a0a4cd203721aa05068
Author:     Tamás Zolnai <tamas.zol...@collabora.com>
AuthorDate: Fri Sep 21 15:52:08 2018 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:09 2018 +0200

    Convert this warning to info
    
    There valid cases when we get unknown tileID. For example we
    sent some tiles, but in the meantime canceltiles arrives which
    resets tiles-on-fly list, but the client still send tileprocessed
    messages for earlier requested tiles.
    
    Change-Id: If2ec015106a0e58d66ae4517b64a9552eb8c38fc
    (cherry picked from commit 0807d04934476a5c27ada9b1938fd7b94147e012)
    Reviewed-on: https://gerrit.libreoffice.org/60878
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 0e7fbcb26f7231088d402186500c24992ffa7288)

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 6461851d9..9ef6cf07f 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -351,7 +351,7 @@ bool ClientSession::_handleInput(const char *buffer, int 
length)
         if(iter != _tilesOnFly.end())
             _tilesOnFly.erase(iter);
         else
-            LOG_WRN("Tileprocessed message with an unknown tile ID");
+            LOG_INF("Tileprocessed message with an unknown tile ID");
 
         docBroker->sendRequestedTiles(shared_from_this());
         return true;
commit b5533d34c2ab75060536caf3ae40889e175f8365
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Thu Sep 20 11:17:38 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:05 2018 +0200

    loleaflet: remove docLayer precondition when the map is focused
    
    Change-Id: Id7fb2e9d7823a788cb40cd3a953b3f024dd22ee5
    Reviewed-on: https://gerrit.libreoffice.org/60826
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 38fd910bee95c63a6b3f2e82c20f82346d4a526a)

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 80460dc59..769b2969d 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -661,11 +661,7 @@ L.Map = L.Evented.extend({
        },
 
        focus: function () {
-               console.debug('focus:');
-               if (this._docLayer) {
-                       console.debug('focus: focussing');
-                       this._clipboardContainer.focus();
-               }
+               this._clipboardContainer.focus();
        },
 
        _fireInitComplete: function (condition) {
commit 30924be3f36c7940f9c1a85e40111f1f00c387f8
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Wed Sep 19 14:30:32 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:34:01 2018 +0200

    loleaflet: mobile: fix non composition input
    
    Change-Id: I6f72f888314baaef00549bb08bcfa78854a0b610
    Reviewed-on: https://gerrit.libreoffice.org/60779
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit b8d7dda8c493f135ec62358f0c07579238d10ef9)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 312c99845..8d5f42664 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -91,17 +91,20 @@ L.Control.MobileInput = L.Control.extend({
 
                this._keyHandled = this._keyHandled || false;
                if (this._isComposing) {
+                       this._lastInput = null;
                        return;
                }
 
                docLayer._resetPreFetching();
                if (handler._ignoreKeyEvent({originalEvent: e})) {
+                       this._lastInput = null;
                        // key ignored
                }
                else if (e.type === 'keydown') {
                        this._keyHandled = false;
                        if (handler.handleOnKeyDownKeys[keyCode] && charCode 
=== 0) {
                                docLayer._postKeyboardEvent('input', charCode, 
unoKeyCode);
+                               this._lastInput = unoKeyCode;
                        }
                }
                else if ((e.type === 'keypress') && 
(!handler.handleOnKeyDownKeys[keyCode] || charCode !== 0)) {
@@ -113,10 +116,12 @@ L.Control.MobileInput = L.Control.extend({
                        }
 
                        docLayer._postKeyboardEvent('input', charCode, 
unoKeyCode);
+                       this._lastInput = unoKeyCode;
                        this._keyHandled = true;
                }
                else if (e.type === 'keyup') {
                        docLayer._postKeyboardEvent('up', charCode, unoKeyCode);
+                       this._lastInput = null;
                        this._keyHandled = true;
                }
                L.DomEvent.stopPropagation(e);
@@ -166,8 +171,9 @@ L.Control.MobileInput = L.Control.extend({
        },
 
        onInput: function (e) {
-               if (e.inputType && e.inputType === 'deleteContentBackward' && 
!this._keyHandled) {
-                       this._map._docLayer._postKeyboardEvent('input', 0, 
this._map.keyboard._toUNOKeyCode(8));
+               var backSpace = this._map.keyboard._toUNOKeyCode(8);
+               if (e.inputType && e.inputType === 'deleteContentBackward' && 
this._lastInput !== backSpace) {
+                       this._map._docLayer._postKeyboardEvent('input', 0, 
backSpace);
                }
                L.DomEvent.stopPropagation(e);
        }
commit b6087919b5252e68e59d96d68c15f388f716e802
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Wed Sep 19 09:25:29 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:33:58 2018 +0200

    loleaflet: mobile: hide the ruler
    
    Change-Id: I87335f3a1116553f1ea548ee759680ffa6533aca
    Reviewed-on: https://gerrit.libreoffice.org/60767
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit b28dd845004b8931ebfa73525eb3c76e6ed83756)

diff --git a/loleaflet/dist/leaflet.css b/loleaflet/dist/leaflet.css
index d42802bef..2fb0a1925 100644
--- a/loleaflet/dist/leaflet.css
+++ b/loleaflet/dist/leaflet.css
@@ -784,6 +784,14 @@ input.clipboard {
 @media screen and (max-height: 400px) {
        .loleaflet-ruler {
                height: 0px;
+               display: none;
+       }
+}
+
+@media (max-width: 767px) {
+       .loleaflet-ruler {
+               height: 0px;
+               display: none;
        }
 }
 
commit c32b354df29046edffde8f7ed6ba67b03431c774
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue Sep 18 21:33:11 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:33:51 2018 +0200

    loleaflet: mobile: fix input text composition events
    
    Change-Id: I04446f8c29c4901ebe67e49ae8dffcfac286f300
    Reviewed-on: https://gerrit.libreoffice.org/60733
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>
    (cherry picked from commit 5b33ec605f9a26d7f7070b2b030f8a7c953f96bd)

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 156cb4294..312c99845 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -17,12 +17,16 @@ L.Control.MobileInput = L.Control.extend({
        },
 
        onGotFocus: function () {
-               this._map.addLayer(this._map._docLayer._cursorMarker);
+               if (this._map._docLayer._cursorMarker) {
+                       this._map.addLayer(this._map._docLayer._cursorMarker);
+               }
        },
 
        onLostFocus: function () {
-               this._textArea.value = '';
-               this._map.removeLayer(this._map._docLayer._cursorMarker);
+               if (this._map._docLayer._cursorMarker) {
+                       this._textArea.value = '';
+                       
this._map.removeLayer(this._map._docLayer._cursorMarker);
+               }
        },
 
        focus: function(focus) {
@@ -73,6 +77,7 @@ L.Control.MobileInput = L.Control.extend({
                L.DomEvent.on(this._textArea, stopEvents, 
L.DomEvent.stopPropagation)
                        .on(this._textArea, 'keydown keypress keyup', 
this.onKeyEvents, this)
                        .on(this._textArea, 'compositionstart compositionupdate 
compositionend textInput', this.onCompEvents, this)
+                       .on(this._textArea, 'input', this.onInput, this)
                        .on(this._textArea, 'focus', this.onGotFocus, this)
                        .on(this._textArea, 'blur', this.onLostFocus, this);
        },
@@ -85,6 +90,10 @@ L.Control.MobileInput = L.Control.extend({
                    unoKeyCode = handler._toUNOKeyCode(keyCode);
 
                this._keyHandled = this._keyHandled || false;
+               if (this._isComposing) {
+                       return;
+               }
+
                docLayer._resetPreFetching();
                if (handler._ignoreKeyEvent({originalEvent: e})) {
                        // key ignored
@@ -128,11 +137,6 @@ L.Control.MobileInput = L.Control.extend({
 
                if (e.type === 'compositionend') {
                        this._isComposing = false; // stop of composing with IME
-                       // get the composited char codes
-                       // clear the input now - best to do this ASAP so the 
input
-                       // is clear for the next word
-                       //map._clipboardContainer.setValue('');
-                       // Set all keycodes to zero
                        map._docLayer._postCompositionEvent(0, 'end', '');
                }
 
@@ -156,6 +160,14 @@ L.Control.MobileInput = L.Control.extend({
                        for (var idx = 0; idx < data.length; idx++) {
                                map._docLayer._postKeyboardEvent('input', 
data[idx].charCodeAt(), 0);
                        }
+                       this._textArea.value = '';
+               }
+               L.DomEvent.stopPropagation(e);
+       },
+
+       onInput: function (e) {
+               if (e.inputType && e.inputType === 'deleteContentBackward' && 
!this._keyHandled) {
+                       this._map._docLayer._postKeyboardEvent('input', 0, 
this._map.keyboard._toUNOKeyCode(8));
                }
                L.DomEvent.stopPropagation(e);
        }
commit ea81916dabfe03c4871afae693839d8a178971c4
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Sun Aug 26 11:55:35 2018 -0400
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Fri Oct 5 20:33:47 2018 +0200

    loleaflet: mobile: add control to handle keyboard events
    
    Change-Id: I978e388eb1066374bd0174e35211bd3bd5a6743b
    Reviewed-on: https://gerrit.libreoffice.org/60651
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit d7a2a4c257accfae655ede2046da3aa1225c95fd)

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index bac393583..2f95c54d4 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -290,6 +290,13 @@ var deps = {
                desc: 'Column Header bar'
        },
 
+       ControlMobileInput: {
+               src: ['control/Control.js',
+                     'control/Control.MobileInput.js'],
+               heading: 'Controls',
+               desc: 'Mobile Input'
+       },
+
        ControlRowHeader: {
                src: ['control/Control.js',
                      'control/Control.RowHeader.js'],
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index e220f5126..68349f2fb 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -140,6 +140,19 @@ body {
        overflow: auto;
 }
 
+.loleaflet-mobile-container {
+       top: 30px;
+       margin: 0;
+       width: 1px;
+       opacity: 0;
+}
+
+.loleaflet-mobile-input {
+       width: 1px;
+       padding: 0px;
+       border: 0px;
+}
+
 /* Important to override context-menu-icon's font-family here otherwise, 
jquery-contextmenu.css
  * will try to load its own font file which is not available in dist/ */
 .context-menu-icon::before {
diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
new file mode 100644
index 000000000..156cb4294
--- /dev/null
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -0,0 +1,166 @@
+/* -*- js-indent-level: 8 -*- */
+/*
+ * L.Control.MobileInput.
+ */
+L.Control.MobileInput = L.Control.extend({
+       options: {
+               position: 'topleft'
+       },
+
+       initialize: function (options) {
+               L.setOptions(this, options);
+       },
+
+       onAdd: function () {
+               this._initLayout();
+               return this._container;
+       },
+
+       onGotFocus: function () {
+               this._map.addLayer(this._map._docLayer._cursorMarker);
+       },
+
+       onLostFocus: function () {
+               this._textArea.value = '';
+               this._map.removeLayer(this._map._docLayer._cursorMarker);
+       },
+
+       focus: function(focus) {
+               if (this._map._permission !== 'edit') {
+                       return;
+               }
+
+               if (focus === false) {
+                       this._textArea.blur();
+               } else {
+                       this._textArea.focus();
+               }
+       },
+
+       select: function() {
+               this._textArea.select();
+       },
+
+       getValue: function() {
+               return this._textArea.value;
+       },
+
+       setValue: function(val) {
+               this._textArea.value = val;
+       },
+
+       activeElement: function () {
+               return this._textArea;
+       },
+
+       showCursor: function () {
+               if (this._textArea === document.activeElement) {
+                       this.onGotFocus();
+               }
+       },
+
+       _initLayout: function () {
+               var constOff = 'off',
+               stopEvents = 'touchstart touchmove touchend mousedown mousemove 
mouseout mouseover mouseup mousewheel click scroll',
+               container = this._container = L.DomUtil.create('div', 
'loleaflet-mobile-container');
+
+               this._textArea = L.DomUtil.create('input', 
'loleaflet-mobile-input', container);
+               this._textArea.setAttribute('type', 'text');
+               this._textArea.setAttribute('autocorrect', constOff);
+               this._textArea.setAttribute('autocapitalize', constOff);
+               this._textArea.setAttribute('autocomplete', constOff);
+               this._textArea.setAttribute('spellcheck', 'false');
+               L.DomEvent.on(this._textArea, stopEvents, 
L.DomEvent.stopPropagation)
+                       .on(this._textArea, 'keydown keypress keyup', 
this.onKeyEvents, this)
+                       .on(this._textArea, 'compositionstart compositionupdate 
compositionend textInput', this.onCompEvents, this)
+                       .on(this._textArea, 'focus', this.onGotFocus, this)
+                       .on(this._textArea, 'blur', this.onLostFocus, this);
+       },
+
+       onKeyEvents: function (e) {
+               var keyCode = e.keyCode,
+                   charCode = e.charCode,
+                   handler = this._map.keyboard,
+                   docLayer = this._map._docLayer,
+                   unoKeyCode = handler._toUNOKeyCode(keyCode);
+
+               this._keyHandled = this._keyHandled || false;
+               docLayer._resetPreFetching();
+               if (handler._ignoreKeyEvent({originalEvent: e})) {
+                       // key ignored
+               }
+               else if (e.type === 'keydown') {
+                       this._keyHandled = false;
+                       if (handler.handleOnKeyDownKeys[keyCode] && charCode 
=== 0) {
+                               docLayer._postKeyboardEvent('input', charCode, 
unoKeyCode);
+                       }
+               }
+               else if ((e.type === 'keypress') && 
(!handler.handleOnKeyDownKeys[keyCode] || charCode !== 0)) {
+                       if (charCode === keyCode && charCode !== 13) {
+                               // Chrome sets keyCode = charCode for printable 
keys
+                               // while LO requires it to be 0
+                               keyCode = 0;
+                               unoKeyCode = handler._toUNOKeyCode(keyCode);
+                       }
+
+                       docLayer._postKeyboardEvent('input', charCode, 
unoKeyCode);
+                       this._keyHandled = true;
+               }
+               else if (e.type === 'keyup') {
+                       docLayer._postKeyboardEvent('up', charCode, unoKeyCode);
+                       this._keyHandled = true;
+               }
+               L.DomEvent.stopPropagation(e);
+       },
+
+       onCompEvents: function (e) {
+               var map = this._map;
+               if (e.type === 'compositionstart' || e.type === 
'compositionupdate') {
+                       this._isComposing = true; // we are starting composing 
with IME
+                       var txt = '';
+                       for (var i = 0; i < e.data.length; i++) {
+                               txt += e.data[i];
+                       }
+                       if (txt) {
+                               map._docLayer._postCompositionEvent(0, 'input', 
txt);
+                       }
+               }
+
+               if (e.type === 'compositionend') {
+                       this._isComposing = false; // stop of composing with IME
+                       // get the composited char codes
+                       // clear the input now - best to do this ASAP so the 
input
+                       // is clear for the next word
+                       //map._clipboardContainer.setValue('');
+                       // Set all keycodes to zero
+                       map._docLayer._postCompositionEvent(0, 'end', '');
+               }
+
+               if (e.type === 'textInput' && !this._keyHandled) {
+                       // Hack for making space and spell-check text insert 
work
+                       // in Chrome (on Andorid) or Chrome with IME.
+                       //
+                       // Chrome (Android) IME triggers keyup/keydown input 
with
+                       // code 229 when hitting space (as with all composiiton 
events)
+                       // with addition to 'textinput' event, in which we only 
see that
+                       // space was entered. Similar situation is also when 
inserting
+                       // a soft-keyboard spell-check item - it is visible 
only with
+                       // 'textinput' event (no composition event is fired).
+                       // To make this work we need to insert textinput.data 
here..
+                       //
+                       // TODO: Maybe make sure this is only triggered when 
keydown has
+                       // 229 code. Also we need to detect that composition 
was overriden
+                       // (part or whole word deleted) with the spell-checked 
word. (for
+                       // example: enter 'tar' and with spell-check correct 
that to 'rat')
+                       var data = e.data;
+                       for (var idx = 0; idx < data.length; idx++) {
+                               map._docLayer._postKeyboardEvent('input', 
data[idx].charCodeAt(), 0);
+                       }
+               }
+               L.DomEvent.stopPropagation(e);
+       }
+});
+
+L.control.mobileInput = function (options) {
+       return new L.Control.MobileInput(options);
+};
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 8cc34c7c6..80460dc59 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -87,8 +87,12 @@ L.Map = L.Evented.extend({
                this._socket = L.socket(this);
                this._progressBar = L.progressOverlay(this.getCenter(), 
L.point(150, 25));
 
-               this._clipboardContainer = L.clipboardContainer();
-               this.addLayer(this._clipboardContainer);
+               if (L.Browser.mobile) {
+                       this._clipboardContainer = 
L.control.mobileInput().addTo(this);
+               } else {
+                       this._clipboardContainer = L.clipboardContainer();
+                       this.addLayer(this._clipboardContainer);
+               }
 
                // When all these conditions are met, fire 
statusindicator:initializationcomplete
                this.initConditions = {
commit 64eb99ebf681c71102ebf258dd3bf358501d8628
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Mon Sep 17 23:28:13 2018 -0400

... etc. - the rest is truncated
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to