[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-1-9' - 142 commits - loleaflet/dist loleaflet/main.js loleaflet/Makefile loleaflet/node_shrinkwrap loleaflet/npm-shrinkwrap.json loleaflet/package.json loleaflet/src loleaflet/unocommands.js loolwsd/Admin.cpp loolwsd/bundled loolwsd/ChildSession.cpp loolwsd/ChildSession.hpp loolwsd/ClientSession.cpp loolwsd/configure.ac loolwsd/Connect.cpp loolwsd/debian loolwsd/DocumentBroker.cpp loolwsd/DocumentBroker.hpp loolwsd/Exceptions.hpp loolwsd/FileServer.cpp loolwsd/.gitignore loolwsd/LibreOfficeKit.hpp loolwsd/Log.cpp loolwsd/LOKitClient.cpp loolwsd/LOOLForKit.cpp loolwsd/LOOLKit.cpp loolwsd/LOOLKit.hpp loolwsd/LOOLProtocol.cpp loolwsd/LOOLProtocol.hpp loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/LOOLStress.cpp loolwsd/LOOLWSD.cpp loolwsd/loolwsd.spec.in loolwsd/loolwsd.xml.in loolwsd/Makefile.am loolwsd/MessageQueue.cpp loolwsd/MessageQueue.hpp loolwsd/protocol.txt loolwsd/README.vars loolwsd/Storage.cpp loolwsd/St orage.hpp loolwsd/test loolwsd/TileCache.cpp loolwsd/TileCache.hpp loolwsd/TileDesc.hpp loolwsd/TraceFile.hpp loolwsd/Unit.cpp loolwsd/UserMessages.hpp loolwsd/Util.cpp loolwsd/Util.hpp

Andras Timar andras.timar at collabora.com
Sun Oct 2 15:05:05 UTC 2016


 loleaflet/Makefile                                           |    2 
 loleaflet/dist/errormessages.js                              |    2 
 loleaflet/dist/l10n/help-localizations.json                  |  232 +++----
 loleaflet/dist/l10n/localizations.json                       |  232 +++----
 loleaflet/dist/l10n/styles-localizations.json                |  238 +++----
 loleaflet/dist/l10n/uno-localizations.json                   |  238 +++----
 loleaflet/dist/leaflet.css                                   |   30 
 loleaflet/dist/selectionMarkers.css                          |    4 
 loleaflet/dist/toolbar.css                                   |  126 ++--
 loleaflet/dist/toolbar/toolbar.js                            |   60 +
 loleaflet/main.js                                            |    7 
 loleaflet/node_shrinkwrap/timeago-1.5.3.tgz                  |binary
 loleaflet/npm-shrinkwrap.json                                |    5 
 loleaflet/package.json                                       |    1 
 loleaflet/src/control/Control.Attribution.js                 |   56 -
 loleaflet/src/control/Control.ContextMenu.js                 |    3 
 loleaflet/src/control/Control.Dialog.js                      |    3 
 loleaflet/src/control/Control.DocumentRepair.js              |   18 
 loleaflet/src/control/Permission.js                          |    1 
 loleaflet/src/control/Toolbar.js                             |    4 
 loleaflet/src/core/Socket.js                                 |   90 +-
 loleaflet/src/layer/Layer.js                                 |    8 
 loleaflet/src/layer/marker/Cursor.js                         |   27 
 loleaflet/src/layer/marker/ProgressOverlay.js                |    3 
 loleaflet/src/layer/tile/CalcTileLayer.js                    |   10 
 loleaflet/src/layer/tile/GridLayer.js                        |   11 
 loleaflet/src/layer/tile/ImpressTileLayer.js                 |   12 
 loleaflet/src/layer/tile/TileLayer.js                        |  273 ++++++--
 loleaflet/src/layer/tile/WriterTileLayer.js                  |   13 
 loleaflet/src/map/Map.js                                     |   42 -
 loleaflet/src/map/handler/Map.FileInserter.js                |    3 
 loleaflet/src/map/handler/Map.Keyboard.js                    |   10 
 loleaflet/unocommands.js                                     |    2 
 loolwsd/.gitignore                                           |    3 
 loolwsd/Admin.cpp                                            |    7 
 loolwsd/ChildSession.cpp                                     |  221 ++++---
 loolwsd/ChildSession.hpp                                     |    9 
 loolwsd/ClientSession.cpp                                    |    6 
 loolwsd/Connect.cpp                                          |   11 
 loolwsd/DocumentBroker.cpp                                   |   58 +
 loolwsd/DocumentBroker.hpp                                   |    2 
 loolwsd/Exceptions.hpp                                       |    6 
 loolwsd/FileServer.cpp                                       |    9 
 loolwsd/LOKitClient.cpp                                      |   11 
 loolwsd/LOOLForKit.cpp                                       |   10 
 loolwsd/LOOLKit.cpp                                          |  334 +++++------
 loolwsd/LOOLKit.hpp                                          |   11 
 loolwsd/LOOLProtocol.cpp                                     |   18 
 loolwsd/LOOLProtocol.hpp                                     |   13 
 loolwsd/LOOLSession.cpp                                      |   33 -
 loolwsd/LOOLSession.hpp                                      |   10 
 loolwsd/LOOLStress.cpp                                       |   49 -
 loolwsd/LOOLWSD.cpp                                          |  171 ++++-
 loolwsd/LibreOfficeKit.hpp                                   |    6 
 loolwsd/Log.cpp                                              |   21 
 loolwsd/Makefile.am                                          |   17 
 loolwsd/MessageQueue.cpp                                     |  236 +++----
 loolwsd/MessageQueue.hpp                                     |   57 +
 loolwsd/README.vars                                          |    5 
 loolwsd/Storage.cpp                                          |   10 
 loolwsd/Storage.hpp                                          |   22 
 loolwsd/TileCache.cpp                                        |   49 -
 loolwsd/TileCache.hpp                                        |    8 
 loolwsd/TileDesc.hpp                                         |   15 
 loolwsd/TraceFile.hpp                                        |   34 -
 loolwsd/Unit.cpp                                             |    3 
 loolwsd/UserMessages.hpp                                     |    3 
 loolwsd/Util.cpp                                             |  160 +++++
 loolwsd/Util.hpp                                             |   42 +
 loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h |    8 
 loolwsd/configure.ac                                         |   20 
 loolwsd/debian/control                                       |    4 
 loolwsd/debian/loolwsd.postinst                              |    8 
 loolwsd/debian/loolwsd.preinst                               |    2 
 loolwsd/debian/loolwsd.service                               |   13 
 loolwsd/debian/rules                                         |    5 
 loolwsd/loolwsd.spec.in                                      |    6 
 loolwsd/loolwsd.xml.in                                       |    6 
 loolwsd/protocol.txt                                         |   20 
 loolwsd/test/Makefile.am                                     |   33 -
 loolwsd/test/TileCacheTests.cpp                              |   83 --
 loolwsd/test/TileQueueTests.cpp                              |  193 ++++++
 loolwsd/test/UnitAdmin.cpp                                   |   18 
 loolwsd/test/WhiteBoxTests.cpp                               |  100 +++
 loolwsd/test/data/Example.odt                                |binary
 loolwsd/test/data/shape.ods                                  |binary
 loolwsd/test/helpers.hpp                                     |   69 ++
 loolwsd/test/httpwserror.cpp                                 |  158 +++++
 loolwsd/test/httpwstest.cpp                                  |  187 +++++-
 loolwsd/test/integration-http-server.cpp                     |    4 
 loolwsd/test/run_unit.sh.in                                  |    5 
 loolwsd/test/test.cpp                                        |    2 
 92 files changed, 2896 insertions(+), 1494 deletions(-)

New commits:
commit 60834bfcec39755eebe73fe6a2d272e3a5ae59aa
Author: Andras Timar <andras.timar at collabora.com>
Date:   Sun Oct 2 16:01:46 2016 +0200

    loolwsd: do not install 'unittest' binary

diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index 232872c..0dfe9b7 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -4,9 +4,7 @@ AUTOMAKE_OPTION = serial-tests
 
 # unittest: tests that do not need loolwsd running, and that are run during a
 # normal build
-bin_PROGRAMS = unittest
-
-# unittest: tests that need loolwsd running, and that are run via 'make check'
+# test: tests that need loolwsd running, and that are run via 'make check'
 check_PROGRAMS = test
 
 noinst_PROGRAMS = test unittest
commit 48c5b552df5a00cc46a1c2663de4f941313ce6af
Author: Andras Timar <andras.timar at collabora.com>
Date:   Sun Oct 2 14:16:08 2016 +0200

    Bump version to 1.9.3

diff --git a/loleaflet/Makefile b/loleaflet/Makefile
index 135b8bf..487fcb0 100644
--- a/loleaflet/Makefile
+++ b/loleaflet/Makefile
@@ -3,7 +3,7 @@
 # ("micro") part: Between releases odd, even for releases (no other
 # changes inbetween).
 
-VERSION=1.9.2
+VERSION=1.9.3
 
 # Version number of the bundled 'draw' thing
 DRAW_VERSION=0.2.4
diff --git a/loolwsd/configure.ac b/loolwsd/configure.ac
index e262e00..e66b0ee 100644
--- a/loolwsd/configure.ac
+++ b/loolwsd/configure.ac
@@ -3,7 +3,7 @@
 
 AC_PREREQ([2.69])
 
-AC_INIT([loolwsd], [1.9.2], [libreoffice at lists.freedesktop.org])
+AC_INIT([loolwsd], [1.9.3], [libreoffice at lists.freedesktop.org])
 LT_INIT([shared, disable-static, dlopen])
 
 AM_INIT_AUTOMAKE([1.11 silent-rules subdir-objects])
commit 6e94e22cda329f3f45075b9cdd638d4be1555f1b
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date:   Sat Oct 1 20:07:39 2016 +0200

    loolwsd: Calc: core and client could be on 2 different tabs
    
    The check for the current part was done before set view on core to the
    one of the sender message
    
    Change-Id: If4b9264c981ecc4e858d23366045e42be603c7c4
    (cherry picked from commit 5edb4cb8c4c51ee5575faed25cc687ec69149096)

diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp
index 355a5d4..1d39b17 100644
--- a/loolwsd/ChildSession.cpp
+++ b/loolwsd/ChildSession.cpp
@@ -895,10 +895,10 @@ bool ChildSession::setClientPart(const char* /*buffer*/, int /*length*/, StringT
 
     auto lock(_loKitDocument->getLock());
 
+    _loKitDocument->setView(_viewId);
+
     if (part != _loKitDocument->getPart())
     {
-        _loKitDocument->setView(_viewId);
-
         _loKitDocument->setPart(part);
     }
 
commit 6cb8be8fa4b3b2a221f43367122fcf32bf2a51dd
Author: Henry Castro <hcastro at collabora.com>
Date:   Sat Oct 1 11:12:47 2016 -0400

    loleaflet: fix error message localization
    
    (cherry picked from commit 97fa8e45f9f5648866fe03dfafac57e4986c9bfd)

diff --git a/loleaflet/dist/errormessages.js b/loleaflet/dist/errormessages.js
index dc99714..625f017 100644
--- a/loleaflet/dist/errormessages.js
+++ b/loleaflet/dist/errormessages.js
@@ -1,3 +1,4 @@
 exports.wrongwopisrc = _('Wrong WOPISrc, usage: WOPISrc=valid encoded URI, or file_path, usage: file_path=/path/to/doc/');
 exports.emptyhosturl = _('The host URL is empty. The loolwsd server is probably misconfigured, please contact the administrator.');
 exports.diskfull = _('No disk space left on server, please contact the server administrator to continue.');
+exports.limitreached = _('This development build is limited to %0 documents, and %1 connections - to avoid the impression that it is suitable for deployment in large enterprises. To find out more about deploying and scaling %2 checkout: <br/><a href=\"%3\">%3</a>.');
diff --git a/loleaflet/src/control/Control.Dialog.js b/loleaflet/src/control/Control.Dialog.js
index 9417a8b..41836ae 100644
--- a/loleaflet/src/control/Control.Dialog.js
+++ b/loleaflet/src/control/Control.Dialog.js
@@ -10,7 +10,8 @@ L.Control.Dialog = L.Control.extend({
 	},
 
 	_onError: function (e) {
-		if (vex.dialogID > 0) {
+		if (vex.dialogID > 0 && !this._map._fatal) {
+			// TODO. queue message errors and pop-up dialogs
 			// Close other dialogs before presenting a new one.
 			vex.close(vex.dialogID);
 		}
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 0ce132c..c4bb43f 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -201,6 +201,14 @@ L.Socket = L.Class.extend({
 		}
 		else if (textMsg.startsWith('error:') && !this._map._docLayer) {
 			textMsg = textMsg.substring(6);
+			if (command.errorKind === 'limitreached') {
+				this._map._fatal = true;
+				textMsg = errorMessages.limitreached;
+				textMsg = textMsg.replace(/%0/g, command.params[0]);
+				textMsg = textMsg.replace(/%1/g, command.params[1]);
+				textMsg = textMsg.replace(/%2/g, (typeof brandProductName !== 'undefined' ? brandProductName : 'LibreOffice Online'));
+				textMsg = textMsg.replace(/%3/g, (typeof brandProductURL !== 'undefined' ? brandProductURL : 'https://wiki.documentfoundation.org/Development/LibreOffice_Online'));
+			}
 			this._map.fire('error', {msg: textMsg});
 		}
 		else if (textMsg === 'pong' && this._map._docLayer && this._map._docLayer._debug) {
@@ -411,6 +419,9 @@ L.Socket = L.Class.extend({
 			else if (tokens[i].substring(0, 7) === 'viewid=') {
 				command.viewid = tokens[i].substring(7);
 			}
+			else if (tokens[i].substring(0, 7) === 'params=') {
+				command.params = tokens[i].substring(7).split(',');
+			}
 		}
 		if (command.tileWidth && command.tileHeight && this._map._docLayer) {
 			var defaultZoom = this._map.options.zoom;
commit 152fa64a10b8d456a31de66d69f82d0117081c72
Author: Henry Castro <hcastro at collabora.com>
Date:   Sat Oct 1 08:45:08 2016 -0400

    loolwsd: error message is localizable
    
    (cherry picked from commit 2a6a5eab2ea0fddb1eb2bd5c51cf453457e91b83)

diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 1d0d519..a54378a 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -19,12 +19,6 @@
 /* Default document used in the start test URI */
 #define LOOLWSD_TEST_DOCUMENT_RELATIVE_PATH "test/data/hello-world.odt"
 
-/* PRODUCT */
-#define LOOLWSD_PRODUCT "LibreOffice Online"
-
-/* PRODUCT URL */
-#define LOOLWSD_URL "https://wiki.documentfoundation.org/Development/LibreOffice_Online"
-
 // This is the main source for the loolwsd program. LOOL uses several loolwsd processes: one main
 // parent process that listens on the TCP port and accepts connections from LOOL clients, and a
 // number of child processes, each which handles a viewing (editing) session for one document.
@@ -185,10 +179,8 @@ namespace {
 static inline
 void lcl_shutdownLimitReached(WebSocket& ws)
 {
-    const std::string msg = std::string("error: ") + Poco::format(PAYLOAD_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS,
-        std::string(LOOLWSD_PRODUCT),
-        std::string(LOOLWSD_URL),
-        std::string(LOOLWSD_URL));
+    const std::string error = Poco::format(PAYLOAD_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS);
+    const std::string close = Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, static_cast<int>(WebSocket::WS_POLICY_VIOLATION));
 
     /* loleaflet sends loolclient, load and partrectangles message immediately
        after web socket handshake, so closing web socket fails loading page in
@@ -209,16 +201,16 @@ void lcl_shutdownLimitReached(WebSocket& ws)
             ws.receiveFrame(buffer.data(), buffer.capacity(), flags);
             if (--handshake == 0)
             {
-                ws.sendFrame(msg.data(), msg.size());
-                ws.shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS));
+                ws.sendFrame(error.data(), error.size());
+                ws.shutdown(WebSocket::WS_POLICY_VIOLATION, close);
             }
         }
         while ((flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
     }
     catch (Exception& e)
     {
-        ws.sendFrame(msg.data(), msg.size());
-        ws.shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS));
+        ws.sendFrame(error.data(), error.size());
+        ws.shutdown(WebSocket::WS_POLICY_VIOLATION, close);
     }
 }
 
diff --git a/loolwsd/UserMessages.hpp b/loolwsd/UserMessages.hpp
index 7aa7aa4..36e6ad3 100644
--- a/loolwsd/UserMessages.hpp
+++ b/loolwsd/UserMessages.hpp
@@ -15,8 +15,8 @@
 //NOTE: For whatever reason Poco seems to trim the first character.
 
 constexpr auto SERVICE_UNAVALABLE_INTERNAL_ERROR = " Service is unavailable. Please try again later and report to your administrator if the issue persists.";
-constexpr auto SERVICE_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections";
-constexpr auto PAYLOAD_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections - to avoid the impression that it is suitable for deployment in large enterprises. To find out more about deploying and scaling %s checkout: <br/><a href=\"%s\">%s</a>.";
+constexpr auto SERVICE_UNAVALABLE_LIMIT_REACHED = "error: cmd=socket kind=close code=%d";
+constexpr auto PAYLOAD_UNAVALABLE_LIMIT_REACHED = "error: cmd=socket kind=limitreached params=%d,%d";
 
 #endif
 
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index c22babf..65ad0a0 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -207,7 +207,7 @@ downloadas: jail=<jail directory> dir=<a tmp dir> name=<name> port=<port>
     The client should then request http://server:port/jail/dir/name in order to download
     the document
 
-error: cmd=<command> kind=<kind> [code=<error_code>]
+error: cmd=<command> kind=<kind> [code=<error_code>] [params=1,2,3,...,N]
 <freeErrorText>
 
     <command> is the command part of the corresponding client->server
diff --git a/loolwsd/test/httpwserror.cpp b/loolwsd/test/httpwserror.cpp
index a7f8385..8e7769d 100644
--- a/loolwsd/test/httpwserror.cpp
+++ b/loolwsd/test/httpwserror.cpp
@@ -102,8 +102,8 @@ void HTTPWSError::testMaxDocuments()
         sendTextFrame(socket, "load ");
         sendTextFrame(socket, "partpagerectangles ");
         statusCode = getErrorCode(socket, message);
-        CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY), statusCode);
-        CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("This development build") != std::string::npos);
+        CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_POLICY_VIOLATION), statusCode);
+        CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("error: cmd=socket kind=close") != std::string::npos);
     }
     catch (const Poco::Exception& exc)
     {
@@ -143,8 +143,8 @@ void HTTPWSError::testMaxConnections()
         sendTextFrame(socketN, "load ");
         sendTextFrame(socketN, "partpagerectangles ");
         statusCode = getErrorCode(*socketN, message);
-        CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY), statusCode);
-        CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("This development build") != std::string::npos);
+        CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_POLICY_VIOLATION), statusCode);
+        CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("error: cmd=socket kind=close") != std::string::npos);
     }
     catch (const Poco::Exception& exc)
     {
commit ede235912162c6303ea20a3b99902b46fb669e7e
Author: Henry Castro <hcastro at collabora.com>
Date:   Sat Oct 1 11:19:20 2016 -0400

    loolwsd: .gitignore unittest
    
    (cherry picked from commit ef2a8ee99fe632274d00550bcc5fa47a6e32db9d)

diff --git a/loolwsd/.gitignore b/loolwsd/.gitignore
index 4cdd66b..d98297b 100644
--- a/loolwsd/.gitignore
+++ b/loolwsd/.gitignore
@@ -55,3 +55,4 @@ looltool
 loolstress
 loolforkit-nocaps
 loadtest
+unittest
commit c443c57d32d7fea8f0e1ffc227352df52c79c05e
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Sep 30 23:53:48 2016 +0200

    Add a callgrind target too.
    
    Change-Id: Ib04e3ccd136766f9d8eb4f99e711390cb09eeb31
    (cherry picked from commit 2e3f297c8ae28b0d481a7600e81a4dbf5c080146)

diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am
index 295d120..2bd5701 100644
--- a/loolwsd/Makefile.am
+++ b/loolwsd/Makefile.am
@@ -154,12 +154,19 @@ run: all @JAILS_PATH@ @SYSTEMPLATE_PATH@/system_stamp
 			  --o:child_root_path="@JAILS_PATH@" --o:storage.filesystem[@allow]=true \
 			  --o:admin_console.username=admin --o:admin_console.password=admin
 
-run_valgrind: all @JAILS_PATH@ @SYSTEMPLATE_PATH@/system_stamp
+run-valgrind: all @JAILS_PATH@ @SYSTEMPLATE_PATH@/system_stamp
 	@echo "Launching loolwsd under valgrind (but not forkit/loolkit, yet)"
 	valgrind --tool=memcheck --trace-children=no -v --read-var-info=yes \
 		./loolwsd --o:sys_template_path="@SYSTEMPLATE_PATH@" --o:lo_template_path="@LO_PATH@" \
 			  --o:child_root_path="@JAILS_PATH@" --o:storage.filesystem[@allow]=true \
 			  --o:admin_console.username=admin --o:admin_console.password=admin
+
+run-callgrind: all @JAILS_PATH@ @SYSTEMPLATE_PATH@/system_stamp
+	@echo "Launching loolwsd under valgrind (but not forkit/loolkit, yet)"
+	valgrind --tool=callgrind --simulate-cache=yes --dump-instr=yes --num-callers=50 --error-limit=no \
+		./loolwsd --o:sys_template_path="@SYSTEMPLATE_PATH@" --o:lo_template_path="@LO_PATH@" \
+			  --o:child_root_path="@JAILS_PATH@" --o:storage.filesystem[@allow]=true \
+			  --o:admin_console.username=admin --o:admin_console.password=admin
 else
 
 SYSTEM_STAMP =
commit 7f365663446cbc36e4b9ecbede3397372ae2a07c
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Sep 30 23:06:26 2016 +0200

    Early break when we have found the highest priority tile.
    
    Change-Id: I47f00dfb67c1a713b1c7fa9fef7a9cc8c59bde00
    (cherry picked from commit 9c5967a432888ce2c4519552746055938e9f5782)

diff --git a/loolwsd/MessageQueue.cpp b/loolwsd/MessageQueue.cpp
index 0a49051..f06f670 100644
--- a/loolwsd/MessageQueue.cpp
+++ b/loolwsd/MessageQueue.cpp
@@ -211,6 +211,10 @@ MessageQueue::Payload TileQueue::get_impl()
             prioritySoFar = p;
             prioritized = i;
             msg = prio;
+
+            // found the highest priority already?
+            if (prioritySoFar == static_cast<int>(_viewOrder.size()) - 1)
+                break;
         }
     }
 
commit 14f35559044bca2609f064349efc4ea6908bb262
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Sep 30 22:38:20 2016 +0200

    Simplify the code around priorities and view ordering in TileQueue.
    
    No functional change.
    
    Change-Id: If34747a069032b13d7d9eb232b1bf10cef967afa
    (cherry picked from commit cbf9556b0a9fc7117471da8b43882fe186f75981)

diff --git a/loolwsd/MessageQueue.cpp b/loolwsd/MessageQueue.cpp
index 8a37e80..0a49051 100644
--- a/loolwsd/MessageQueue.cpp
+++ b/loolwsd/MessageQueue.cpp
@@ -163,7 +163,7 @@ int TileQueue::priority(const std::string& tileMsg)
 {
     auto tile = TileDesc::parse(tileMsg); //FIXME: Expensive, avoid.
 
-    for (size_t i = 0; i < _viewOrder.size(); ++i)
+    for (int i = static_cast<int>(_viewOrder.size()) - 1; i >= 0; --i)
     {
         auto& cursor = _cursorPositions[_viewOrder[i]];
         if (tile.intersectsWithRect(cursor.X, cursor.Y, cursor.Width, cursor.Height))
@@ -205,10 +205,8 @@ MessageQueue::Payload TileQueue::get_impl()
             break;
 
         int p = priority(prio);
-        if (p == -1)
-            continue;
 
-        if (prioritySoFar == -1 || p < prioritySoFar)
+        if (p > prioritySoFar)
         {
             prioritySoFar = p;
             prioritized = i;
diff --git a/loolwsd/MessageQueue.hpp b/loolwsd/MessageQueue.hpp
index 3bf778c..2a13484 100644
--- a/loolwsd/MessageQueue.hpp
+++ b/loolwsd/MessageQueue.hpp
@@ -108,7 +108,7 @@ public:
             _viewOrder.erase(view);
         }
 
-        _viewOrder.push_front(viewId);
+        _viewOrder.push_back(viewId);
     }
 
     void removeCursorPosition(int viewId)
@@ -132,16 +132,16 @@ private:
     void removeDuplicate(const std::string& tileMsg);
 
     /// Priority of the given tile message.
-    /// 0 is the highest prio, the bigger is the value, the lower is the prio.
-    /// -1 means the lowest prio (the tile does not intersect any of the cursors).
+    /// -1 means the lowest prio (the tile does not intersect any of the cursors),
+    /// the higher the number, the bigger is priority [up to _viewOrder.size()-1].
     int priority(const std::string& tileMsg);
 
 private:
     std::map<int, CursorPosition> _cursorPositions;
 
     /// Check the views in the order of how the editing (cursor movement) has
-    /// been happening.
-    std::deque<int> _viewOrder;
+    /// been happening (0 == oldest, size() - 1 == newest).
+    std::vector<int> _viewOrder;
 };
 
 #endif
commit 7748a7707de1aa3f9a972b666f900c67ee7e758f
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Sep 30 22:02:22 2016 +0200

    Check we get views prioritized according to when the cursor was updated.
    
    Fails with any of e5adf272b876b0d6afd8aa989c005d8a4bed5f96,
    d9c90e30fd9f02a7287183d2b58195f547fb92da or
    4e7ba53a2b0c91c35d93f11e114a9e56dc467555 reverted.
    
    Change-Id: I5a9384f20ae1f64e29509ec568670e29c3c4eb94
    (cherry picked from commit f101b2b7cbbec2392109b6e8d5a095b2a0214216)

diff --git a/loolwsd/MessageQueue.hpp b/loolwsd/MessageQueue.hpp
index 33edeeb..3bf778c 100644
--- a/loolwsd/MessageQueue.hpp
+++ b/loolwsd/MessageQueue.hpp
@@ -21,8 +21,6 @@
 */
 class MessageQueue
 {
-    friend class TileQueueTests;
-
 public:
 
     typedef std::vector<char> Payload;
@@ -73,6 +71,8 @@ protected:
 */
 class TileQueue : public MessageQueue
 {
+    friend class TileQueueTests;
+
 private:
 
     class CursorPosition
diff --git a/loolwsd/test/TileQueueTests.cpp b/loolwsd/test/TileQueueTests.cpp
index 49a45c2..efe13cf 100644
--- a/loolwsd/test/TileQueueTests.cpp
+++ b/loolwsd/test/TileQueueTests.cpp
@@ -42,12 +42,14 @@ class TileQueueTests : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testTileQueuePriority);
     CPPUNIT_TEST(testTileCombinedRendering);
     CPPUNIT_TEST(testTileRecombining);
+    CPPUNIT_TEST(testViewOrder);
 
     CPPUNIT_TEST_SUITE_END();
 
     void testTileQueuePriority();
     void testTileCombinedRendering();
     void testTileRecombining();
+    void testViewOrder();
 };
 
 void TileQueueTests::testTileQueuePriority()
@@ -149,6 +151,43 @@ void TileQueueTests::testTileRecombining()
     CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(queue._queue.size()));
 }
 
+void TileQueueTests::testViewOrder()
+{
+    TileQueue queue;
+
+    // should result in the 3, 2, 1, 0 order of the views
+    queue.updateCursorPosition(0, 0, 0, 0, 10, 100);
+    queue.updateCursorPosition(2, 0, 0, 0, 10, 100);
+    queue.updateCursorPosition(1, 0, 0, 7680, 10, 100);
+    queue.updateCursorPosition(3, 0, 0, 0, 10, 100);
+    queue.updateCursorPosition(2, 0, 0, 15360, 10, 100);
+    queue.updateCursorPosition(3, 0, 0, 23040, 10, 100);
+
+    const std::vector<std::string> tiles =
+    {
+        "tile part=0 width=256 height=256 tileposx=0 tileposy=0 tilewidth=3840 tileheight=3840 ver=-1",
+        "tile part=0 width=256 height=256 tileposx=0 tileposy=7680 tilewidth=3840 tileheight=3840 ver=-1",
+        "tile part=0 width=256 height=256 tileposx=0 tileposy=15360 tilewidth=3840 tileheight=3840 ver=-1",
+        "tile part=0 width=256 height=256 tileposx=0 tileposy=23040 tilewidth=3840 tileheight=3840 ver=-1"
+    };
+
+    for (auto &tile : tiles)
+        queue.put(tile);
+
+    CPPUNIT_ASSERT_EQUAL(4, static_cast<int>(tiles.size()));
+
+    // should result in the 3, 2, 1, 0 order of the tiles thanks to the cursor
+    // positions
+    for (size_t i = 0; i < tiles.size(); ++i)
+    {
+        MessageQueue::Payload payload(queue.get());
+        std::string message(payload.data(), payload.size());
+
+        CPPUNIT_ASSERT_EQUAL(tiles[3 - i], message);
+    }
+
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(TileQueueTests);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit d1ed10c8267779b89efd6f6c038ef91bad5b1c37
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Sep 30 22:00:20 2016 +0200

    Search harder for the highest priority tile.
    
    Without this, any cursor intersecting with the tile was treated as the
    priority.
    
    Change-Id: I6bc4629620d368ce383900386a00c535255ee133
    (cherry picked from commit e5adf272b876b0d6afd8aa989c005d8a4bed5f96)

diff --git a/loolwsd/MessageQueue.cpp b/loolwsd/MessageQueue.cpp
index b9059cb..8a37e80 100644
--- a/loolwsd/MessageQueue.cpp
+++ b/loolwsd/MessageQueue.cpp
@@ -159,18 +159,18 @@ void TileQueue::removeDuplicate(const std::string& tileMsg)
     }
 }
 
-bool TileQueue::priority(const std::string& tileMsg)
+int TileQueue::priority(const std::string& tileMsg)
 {
     auto tile = TileDesc::parse(tileMsg); //FIXME: Expensive, avoid.
 
-    for (int view : _viewOrder)
+    for (size_t i = 0; i < _viewOrder.size(); ++i)
     {
-        auto& cursor = _cursorPositions[view];
+        auto& cursor = _cursorPositions[_viewOrder[i]];
         if (tile.intersectsWithRect(cursor.X, cursor.Y, cursor.Width, cursor.Height))
-            return true;
+            return i;
     }
 
-    return false;
+    return -1;
 }
 
 MessageQueue::Payload TileQueue::get_impl()
@@ -191,7 +191,8 @@ MessageQueue::Payload TileQueue::get_impl()
 
     // We are handling a tile; first try to find one that is at the cursor's
     // position, otherwise handle the one that is at the front
-    bool foundPrioritized = false;
+    int prioritized = 0;
+    int prioritySoFar = -1;
     for (size_t i = 0; i < _queue.size(); ++i)
     {
         auto& it = _queue[i];
@@ -203,18 +204,19 @@ MessageQueue::Payload TileQueue::get_impl()
         if (LOOLProtocol::getFirstToken(prio) != "tile")
             break;
 
-        if (priority(prio))
+        int p = priority(prio);
+        if (p == -1)
+            continue;
+
+        if (prioritySoFar == -1 || p < prioritySoFar)
         {
-            Log::debug() << "Handling a priority message: " << prio << Log::end;
-            _queue.erase(_queue.begin() + i);
+            prioritySoFar = p;
+            prioritized = i;
             msg = prio;
-            foundPrioritized = true;
-            break;
         }
     }
 
-    if (!foundPrioritized)
-        _queue.pop_front();
+    _queue.erase(_queue.begin() + prioritized);
 
     tiles.emplace_back(TileDesc::parse(msg));
 
diff --git a/loolwsd/MessageQueue.hpp b/loolwsd/MessageQueue.hpp
index 284f847..33edeeb 100644
--- a/loolwsd/MessageQueue.hpp
+++ b/loolwsd/MessageQueue.hpp
@@ -131,8 +131,10 @@ private:
     /// Search the queue for a duplicate tile and remove it (if present).
     void removeDuplicate(const std::string& tileMsg);
 
-    /// Check if the given tile msg underlies a cursor.
-    bool priority(const std::string& tileMsg);
+    /// Priority of the given tile message.
+    /// 0 is the highest prio, the bigger is the value, the lower is the prio.
+    /// -1 means the lowest prio (the tile does not intersect any of the cursors).
+    int priority(const std::string& tileMsg);
 
 private:
     std::map<int, CursorPosition> _cursorPositions;
commit 35b0014075be5368b731fe4b93a82e3a604b7578
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 17:44:15 2016 +0200

    DocumentRepair: still show the absolute time as a tooltip
    
    Change-Id: I64cbf519e53c2a2ad697deb9932fb7a32e702d25
    (cherry picked from commit 81a114c47d7df9271c67dd4acd4d7c164ebef9be)

diff --git a/loleaflet/src/control/Control.DocumentRepair.js b/loleaflet/src/control/Control.DocumentRepair.js
index 91d01a0..8ff56b6 100644
--- a/loleaflet/src/control/Control.DocumentRepair.js
+++ b/loleaflet/src/control/Control.DocumentRepair.js
@@ -66,16 +66,23 @@ L.Control.DocumentRepair = L.Control.extend({
 		td.appendChild(document.createTextNode(comment));
 		td = L.DomUtil.create('td', '', row);
 		td.appendChild(document.createTextNode(viewId));
+
+		// Show relative date by default, absolute one as tooltip.
 		td = L.DomUtil.create('td', '', row);
 		var relativeDateTime = jQuery.timeago(dateTime.replace(/,.*/, 'Z'));
-		td.appendChild(document.createTextNode(relativeDateTime));
+		var span = document.createElement('span');
+		span.title = dateTime;
+		span.appendChild(document.createTextNode(relativeDateTime));
+		td.appendChild(span);
+
 		L.DomEvent.on(row, 'click', this._onRowClick, this);
 		L.DomEvent.on(row, 'dblclick', this._onJumpClick, this);
 	},
 
 	fillAction: function (actions, type) {
 		for (var iterator = 0; iterator < actions.length; ++iterator) {
-			var userName = actions[iterator].userName;
+			// No user name if the user in question is already disconnected.
+			var userName = actions[iterator].userName ? actions[iterator].userName : '';
 			if (parseInt(actions[iterator].viewId) === this._map._docLayer._viewId) {
 				userName = _('You');
 			}
commit 7aa1decfb3f30fce1f64becb60c9dd240282beeb
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 16:52:03 2016 +0200

    leaflet: bundle timeago JS module
    
    And start using it in DocumentRepair.
    
    Change-Id: I8d2b8af4d2e0dcc7cf8082c9d7adbc633df1c387
    (cherry picked from commit 655915da052ee45d7d8b2d961ee01afc72d605af)

diff --git a/loleaflet/main.js b/loleaflet/main.js
index c2851d6..3074bdf 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -7,6 +7,7 @@ global.$ = global.jQuery = $;
 require('smartmenus');
 require('jquery-ui');
 require('jquery-contextmenu');
+require('timeago');
 // FIXME: would be good to remove w2ui script tags and require
 // like other modules. problem is that w2ui doesn't export
 // its global variables for a module, so following doesn't work
diff --git a/loleaflet/node_shrinkwrap/timeago-1.5.3.tgz b/loleaflet/node_shrinkwrap/timeago-1.5.3.tgz
new file mode 100644
index 0000000..eefea77
Binary files /dev/null and b/loleaflet/node_shrinkwrap/timeago-1.5.3.tgz differ
diff --git a/loleaflet/npm-shrinkwrap.json b/loleaflet/npm-shrinkwrap.json
index b7a7733..2e889cf 100644
--- a/loleaflet/npm-shrinkwrap.json
+++ b/loleaflet/npm-shrinkwrap.json
@@ -2385,6 +2385,11 @@
       "from": "smartmenus at 1.0.0",
       "resolved": "./node_shrinkwrap/smartmenus-1.0.0.tgz"
     },
+    "timeago": {
+      "version": "1.5.3",
+      "from": "timeago at 1.5.3",
+      "resolved": "./node_shrinkwrap/timeago-1.5.3.tgz"
+    },
     "uglify-js": {
       "version": "2.4.24",
       "from": "uglify-js@>=2.4.16 <2.5.0",
diff --git a/loleaflet/package.json b/loleaflet/package.json
index 91fdffa..c0b144d 100644
--- a/loleaflet/package.json
+++ b/loleaflet/package.json
@@ -32,6 +32,7 @@
     "select2": "4.0.1",
     "shrinkpack": "^0.13.1",
     "smartmenus": "1.0.0",
+    "timeago": "1.5.3",
     "uglify-js": "~2.4.16",
     "uglifyify": "3.0.2",
     "vex-js": "2.3.2",
diff --git a/loleaflet/src/control/Control.DocumentRepair.js b/loleaflet/src/control/Control.DocumentRepair.js
index 5176ea2..91d01a0 100644
--- a/loleaflet/src/control/Control.DocumentRepair.js
+++ b/loleaflet/src/control/Control.DocumentRepair.js
@@ -67,7 +67,8 @@ L.Control.DocumentRepair = L.Control.extend({
 		td = L.DomUtil.create('td', '', row);
 		td.appendChild(document.createTextNode(viewId));
 		td = L.DomUtil.create('td', '', row);
-		td.appendChild(document.createTextNode(dateTime));
+		var relativeDateTime = jQuery.timeago(dateTime.replace(/,.*/, 'Z'));
+		td.appendChild(document.createTextNode(relativeDateTime));
 		L.DomEvent.on(row, 'click', this._onRowClick, this);
 		L.DomEvent.on(row, 'dblclick', this._onJumpClick, this);
 	},
commit 3b9ed884912094dfbaf1a90ba05128bc0197461c
Author: Andras Timar <andras.timar at collabora.com>
Date:   Fri Sep 30 16:33:38 2016 +0200

    loleaflet: enable l10n of Update/Delete TOC commands
    
    (cherry picked from commit d22cdad721ede50af16daed87815058fd84517bd)

diff --git a/loleaflet/unocommands.js b/loleaflet/unocommands.js
index 3861ab5..1f2177c 100644
--- a/loleaflet/unocommands.js
+++ b/loleaflet/unocommands.js
@@ -13,3 +13,5 @@ var NoWrap = _('No Wrap');
 var WrapBefore = _('Wrap Before');
 var WrapAfter = _('Wrap After');
 var EnableContour = _('Enable Contour');
+var UpdateTOC = _('Update Index or Table of Contents');
+var DeleteTOC = _('Delete Index or Table of Contents');
commit e061d2055c76962b63db13503858262afefbf853
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 15:46:28 2016 +0200

    Move HTTPWSTest::testEmptyCellCursor() to WhiteBoxTests
    
    Which is a unit test, so should be faster.
    
    Change-Id: Id5c2ed705576a1467756ac8dba6e5e434b56f725
    (cherry picked from commit d0abdee6d5bef7ca24a8042445f4ea2a071d0575)

diff --git a/loolwsd/test/WhiteBoxTests.cpp b/loolwsd/test/WhiteBoxTests.cpp
index 3e43437..af4e34f 100644
--- a/loolwsd/test/WhiteBoxTests.cpp
+++ b/loolwsd/test/WhiteBoxTests.cpp
@@ -11,7 +11,9 @@
 
 #include <cppunit/extensions/HelperMacros.h>
 
+#include <ChildSession.hpp>
 #include <Common.hpp>
+#include <LOOLKit.hpp>
 #include <LOOLProtocol.hpp>
 #include <MessageQueue.hpp>
 #include <Util.hpp>
@@ -24,12 +26,14 @@ class WhiteBoxTests : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testLOOLProtocolFunctions);
     CPPUNIT_TEST(testRegexListMatcher);
     CPPUNIT_TEST(testRegexListMatcher_Init);
+    CPPUNIT_TEST(testEmptyCellCursor);
 
     CPPUNIT_TEST_SUITE_END();
 
     void testLOOLProtocolFunctions();
     void testRegexListMatcher();
     void testRegexListMatcher_Init();
+    void testEmptyCellCursor();
 };
 
 void WhiteBoxTests::testLOOLProtocolFunctions()
@@ -145,6 +149,59 @@ void WhiteBoxTests::testRegexListMatcher_Init()
     CPPUNIT_ASSERT(matcher.match("192.168.."));
 }
 
+/// A stub IDocumentManager implementation for unit test purposes.
+class DummyDocument : public IDocumentManager
+{
+    std::shared_ptr<TileQueue> _tileQueue;
+    std::mutex _mutex;
+public:
+    DummyDocument()
+        : _tileQueue(new TileQueue()),
+        _mutex()
+    {
+    }
+    std::shared_ptr<lok::Document> onLoad(const std::string& /*sessionId*/,
+                                          const std::string& /*jailedFilePath*/,
+                                          const std::string& /*userName*/,
+                                          const std::string& /*docPassword*/,
+                                          const std::string& /*renderOpts*/,
+                                          const bool /*haveDocPassword*/) override
+    {
+        return nullptr;
+    }
+
+    void onUnload(const ChildSession& /*session*/) override
+    {
+    }
+
+    void notifyViewInfo() override
+    {
+    }
+
+    std::map<int, std::string> getViewInfo() override
+    {
+        return {};
+    }
+
+    std::mutex& getMutex() override
+    {
+        return _mutex;
+    }
+
+    std::shared_ptr<TileQueue>& getTileQueue() override
+    {
+        return _tileQueue;
+    }
+};
+
+void WhiteBoxTests::testEmptyCellCursor()
+{
+    DummyDocument document;
+    CallbackDescriptor callbackDescriptor{&document, 0};
+    // This failed as stoi raised an std::invalid_argument exception.
+    documentViewCallback(LOK_CALLBACK_CELL_CURSOR, "EMPTY", &callbackDescriptor);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(WhiteBoxTests);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index 5e8f50c..91dc79a 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -82,7 +82,6 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testInactiveClient);
     CPPUNIT_TEST(testMaxColumn);
     CPPUNIT_TEST(testMaxRow);
-    CPPUNIT_TEST(testEmptyCellCursor);
     CPPUNIT_TEST(testInsertAnnotationWriter);
     CPPUNIT_TEST(testEditAnnotationWriter);  // Broken with multiview.
     CPPUNIT_TEST(testInsertAnnotationCalc);
@@ -126,7 +125,6 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     void testInactiveClient();
     void testMaxColumn();
     void testMaxRow();
-    void testEmptyCellCursor();
     void testInsertAnnotationWriter();
     void testEditAnnotationWriter();
     void testInsertAnnotationCalc();
@@ -1127,27 +1125,6 @@ void HTTPWSTest::testMaxRow()
     }
 }
 
-void HTTPWSTest::testEmptyCellCursor()
-{
-    // Load a document, and make sure a cell cursor shows up.
-    std::string docPath;
-    std::string docURL;
-    getDocumentPathAndURL("setclientpart.ods", docPath, docURL);
-    Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, docURL);
-    std::shared_ptr<Poco::Net::WebSocket> socket = loadDocAndGetSocket(_uri, docURL);
-    std::string response;
-    getResponseMessage(socket, "cellcursor:", response, false);
-
-    // Begin text edit of a cell.
-    sendTextFrame(socket, "key type=input char=115 key=97");
-
-    // Make sure the cell cursor is now hidden.
-    getResponseMessage(socket, "cellcursor: ", response, false);
-    // This failed as response was "", not "EMPTY", because code in
-    // Document::ViewCallback() crashed.
-    CPPUNIT_ASSERT_EQUAL(std::string("EMPTY"), response);
-}
-
 void HTTPWSTest::testNoExtraLoolKitsLeft()
 {
     const auto countNow = countLoolKitProcesses(InitialLoolKitCount);
commit 1954f3b1b3013f2f0dd119ba0c877ec2e350a986
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 15:42:27 2016 +0200

    Refactor to be able to unit-test Document::ViewCallback()
    
    Change-Id: If07eb89e5d2f45737a8af10803511ab2ee3a07b3
    (cherry picked from commit 38577b74fd25d0f1f5b775d8ed815124c4514bea)

diff --git a/loolwsd/ChildSession.hpp b/loolwsd/ChildSession.hpp
index dfc5bf2..1ee6ecd 100644
--- a/loolwsd/ChildSession.hpp
+++ b/loolwsd/ChildSession.hpp
@@ -46,6 +46,10 @@ public:
     /// Get a view ID <-> user name map.
     virtual
     std::map<int, std::string> getViewInfo() = 0;
+    virtual
+    std::mutex& getMutex() = 0;
+    virtual
+    std::shared_ptr<TileQueue>& getTileQueue() = 0;
 };
 
 /// Represents a session to the WSD process, in a Kit process. Note that this is not a singleton.
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index d34fe47..f73de13 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -53,6 +53,7 @@
 #include "Common.hpp"
 #include "IoUtil.hpp"
 #include "LOKitHelper.hpp"
+#include "LOOLKit.hpp"
 #include "LOOLProtocol.hpp"
 #include "LibreOfficeKit.hpp"
 #include "Log.hpp"
@@ -95,6 +96,7 @@ static std::shared_ptr<Document> document;
 
 namespace
 {
+#ifndef BUILDING_TESTS
     typedef enum { COPY_ALL, COPY_LO, COPY_NO_USR } LinkOrCopyType;
     LinkOrCopyType linkOrCopyType;
     std::string sourceForLinkOrCopy;
@@ -251,6 +253,7 @@ namespace
             throw Exception("symlink() failed");
         }
     }
+#endif
 }
 
 /// Connection thread with a client (via WSD).
@@ -385,14 +388,6 @@ public:
     /// 2) Document which require password to modify
     enum class PasswordType { ToView, ToModify };
 
-    /// Descriptor class used to link a LOK
-    /// callback to a specific view.
-    struct CallbackDescriptor
-    {
-        Document* const Doc;
-        const int ViewId;
-    };
-
 public:
     Document(const std::shared_ptr<lok::Office>& loKit,
              const std::string& jailId,
@@ -811,8 +806,6 @@ public:
         _ws->sendFrame(message.data(), message.size());
     }
 
-private:
-
     static void GlobalCallback(const int nType, const char* pPayload, void* pData)
     {
         if (TerminationFlag)
@@ -853,7 +846,7 @@ private:
                      << "] [" << LOKitHelper::kitCallbackTypeToString(nType)
                      << "] [" << payload << "]." << Log::end;
 
-        std::unique_lock<std::mutex> lock(pDescr->Doc->_mutex);
+        std::unique_lock<std::mutex> lock(pDescr->Doc->getMutex());
 
         if (nType == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR ||
             nType == LOK_CALLBACK_CELL_CURSOR)
@@ -867,7 +860,7 @@ private:
                 auto cursorWidth = std::stoi(tokens[2]);
                 auto cursorHeight = std::stoi(tokens[3]);
 
-                pDescr->Doc->_tileQueue->updateCursorPosition(0, 0, cursorX, cursorY, cursorWidth, cursorHeight);
+                pDescr->Doc->getTileQueue()->updateCursorPosition(0, 0, cursorX, cursorY, cursorWidth, cursorHeight);
             }
         }
         else if (nType == LOK_CALLBACK_INVALIDATE_VIEW_CURSOR ||
@@ -888,13 +881,15 @@ private:
                 auto cursorWidth = std::stoi(tokens[2]);
                 auto cursorHeight = std::stoi(tokens[3]);
 
-                pDescr->Doc->_tileQueue->updateCursorPosition(std::stoi(viewId), std::stoi(part), cursorX, cursorY, cursorWidth, cursorHeight);
+                pDescr->Doc->getTileQueue()->updateCursorPosition(std::stoi(viewId), std::stoi(part), cursorX, cursorY, cursorWidth, cursorHeight);
             }
         }
 
-        pDescr->Doc->_tileQueue->put("callback " + std::to_string(pDescr->ViewId) + " " + std::to_string(nType) + " " + payload);
+        pDescr->Doc->getTileQueue()->put("callback " + std::to_string(pDescr->ViewId) + " " + std::to_string(nType) + " " + payload);
     }
 
+private:
+
     static void DocumentCallback(const int nType, const char* pPayload, void* pData)
     {
         if (TerminationFlag)
@@ -1014,6 +1009,16 @@ private:
         return viewInfo;
     };
 
+    std::mutex& getMutex() override
+    {
+        return _mutex;
+    }
+
+    std::shared_ptr<TileQueue>& getTileQueue() override
+    {
+        return _tileQueue;
+    }
+
     /// Notify all views of viewId and their associated usernames
     void notifyViewInfo() override
     {
@@ -1335,6 +1340,12 @@ private:
     std::atomic_size_t _clientViews;
 };
 
+void documentViewCallback(const int nType, const char* pPayload, void* pData)
+{
+    Document::ViewCallback(nType, pPayload, pData);
+}
+
+#ifndef BUILDING_TESTS
 void lokit_main(const std::string& childRoot,
                 const std::string& sysTemplate,
                 const std::string& loTemplate,
@@ -1636,6 +1647,7 @@ void lokit_main(const std::string& childRoot,
     Log::info("Process finished.");
     std::_Exit(Application::EXIT_OK);
 }
+#endif
 
 /// Initializes LibreOfficeKit for cross-fork re-use.
 bool globalPreinit(const std::string &loTemplate)
@@ -1696,10 +1708,12 @@ bool globalPreinit(const std::string &loTemplate)
 namespace Util
 {
 
+#ifndef BUILDING_TESTS
 void alertAllUsers(const std::string& cmd, const std::string& kind)
 {
     document->sendTextFrame("errortoall: cmd=" + cmd + " kind=" + kind);
 }
+#endif
 
 }
 
diff --git a/loolwsd/LOOLKit.hpp b/loolwsd/LOOLKit.hpp
index 4206133..a7e0dab 100644
--- a/loolwsd/LOOLKit.hpp
+++ b/loolwsd/LOOLKit.hpp
@@ -17,7 +17,18 @@ void lokit_main(const std::string& childRoot,
                 bool queryVersionInfo);
 
 bool globalPreinit(const std::string &loTemplate);
+/// Wrapper around private Document::ViewCallback().
+void documentViewCallback(const int nType, const char* pPayload, void* pData);
 
+class IDocumentManager;
+
+/// Descriptor class used to link a LOK
+/// callback to a specific view.
+struct CallbackDescriptor
+{
+    IDocumentManager* const Doc;
+    const int ViewId;
+};
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index cbe1135..232872c 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -31,6 +31,7 @@ AM_CPPFLAGS = -pthread -I$(top_srcdir) -DBUILDING_TESTS
 wsd_sources = \
             ../IoUtil.cpp \
             ../Log.cpp \
+            ../LOOLKit.cpp \
             ../LOOLProtocol.cpp \
             ../LOOLSession.cpp \
             ../TileCache.cpp \
commit 5f66a4fb5f2c859c3a918d695623d0fd5d4ccafd
Author: Andras Timar <andras.timar at collabora.com>
Date:   Fri Sep 30 15:36:29 2016 +0200

    expose Update TOC and Remove TOC context menu items in Writer
    
    (cherry picked from commit b4d3bfdcae8ed5e212ddc9f0b41459edf2e21ae3)

diff --git a/loleaflet/src/control/Control.ContextMenu.js b/loleaflet/src/control/Control.ContextMenu.js
index e67e76b..c0a5722 100644
--- a/loleaflet/src/control/Control.ContextMenu.js
+++ b/loleaflet/src/control/Control.ContextMenu.js
@@ -31,7 +31,8 @@ L.Control.ContextMenu = L.Control.extend({
 				   'InsertRowsBefore', 'InsertRowsAfter', 'InsertColumnsBefore', 'InsertColumnsAfter',
 				   'TableDeleteMenu',
 				   'DeleteRows', 'DeleteColumns', 'DeleteTable',
-				   'MergeCells', 'SetOptimalColumnWidth', 'SetOptimalRowWidth'],
+				   'MergeCells', 'SetOptimalColumnWidth', 'SetOptimalRowWidth',
+				   'UpdateCurIndex','RemoveTableOf'],
 
 			spreadsheet: ['MergeCells', 'SplitCells',
 						  'InsertAnnotation', 'EditAnnotation', 'DeleteNote', 'ShowNote', 'HideNote'],
commit 343eb86a40b140649fea1d7e6b5beb139d1625be
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Sep 30 16:09:53 2016 +0300

    Add handling of dangerously low storage space for local files
    
    Sends the same 'error: cmd=internal kind=diskfull' message as when
    disk space for LOOL's own needs is getting full, but only to the
    user(s) of that document. We can't in general know whether one
    document in the Storage abstraction is located even close to another.
    
    (cherry picked from commit 5b08fe6477dc18ff9055ad59a28499b9c4264d0a)

diff --git a/loolwsd/Exceptions.hpp b/loolwsd/Exceptions.hpp
index eb2b4a6..b9839c8 100644
--- a/loolwsd/Exceptions.hpp
+++ b/loolwsd/Exceptions.hpp
@@ -21,6 +21,12 @@ protected:
     using std::runtime_error::runtime_error;
 };
 
+class StorageSpaceLowException : public LoolException
+{
+public:
+    using LoolException::LoolException;
+};
+
 /// A bad-request exception that is meant to signify,
 /// and translate into, an HTTP bad request.
 class BadRequestException : public LoolException
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 7e36a4b..1d0d519 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -1181,8 +1181,6 @@ public:
                 }
             }
 
-            docBroker->load(jailId);
-
             auto ws = std::make_shared<WebSocket>(request, response);
             auto session = std::make_shared<PrisonerSession>(sessionId, ws, docBroker);
 
@@ -1192,6 +1190,20 @@ public:
                 Log::warn("Failed to connect " + session->getName() + " to its peer.");
             }
 
+            try
+            {
+                docBroker->load(jailId);
+            }
+            catch (const StorageSpaceLowException&)
+            {
+                // We use the same message as is sent when some of lool's own locations are full,
+                // even if in this case it might be a totally different location (file system, or
+                // some other type of storage somewhere). This message is not sent to all clients,
+                // though, just to all sessions of this document.
+                docBroker->alertAllUsersOfDocument("internal", "diskfull");
+                throw;
+            }
+
             std::unique_lock<std::mutex> lock(AvailableChildSessionMutex);
             AvailableChildSessions.emplace(sessionId, session);
 
diff --git a/loolwsd/Storage.cpp b/loolwsd/Storage.cpp
index 89efd79..f320e2e 100644
--- a/loolwsd/Storage.cpp
+++ b/loolwsd/Storage.cpp
@@ -187,7 +187,13 @@ std::string LocalStorage::loadStorageFileToLocal()
     Log::info("Public URI [" + _uri +
               "] jailed to [" + _jailedFilePath + "].");
 
+    // Despite the talk about URIs it seems that _uri is actually just a pathname here
+
     const auto publicFilePath = _uri;
+
+    if (!Util::checkDiskSpace(publicFilePath))
+        throw StorageSpaceLowException("Low disk space for " + publicFilePath);
+
     Log::info("Linking " + publicFilePath + " to " + _jailedFilePath);
     if (!Poco::File(_jailedFilePath).exists() && link(publicFilePath.c_str(), _jailedFilePath.c_str()) == -1)
     {
commit c6ab0200fb7f1f810aa9d22f6b0fe0398683c21f
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 14:21:14 2016 +0200

    Document repair: expose user names, not only view IDs
    
    Also in leaflet replace the current user with "You" to be consistent
    with the statusbar.
    
    Change-Id: If2d76f078eeae3038f8ae17506ae7679f7b23023
    (cherry picked from commit 3090981c8a306c47e1c1f031bafe77fdd3dd7bde)

diff --git a/loleaflet/src/control/Control.DocumentRepair.js b/loleaflet/src/control/Control.DocumentRepair.js
index 365d21d..5176ea2 100644
--- a/loleaflet/src/control/Control.DocumentRepair.js
+++ b/loleaflet/src/control/Control.DocumentRepair.js
@@ -43,7 +43,7 @@ L.Control.DocumentRepair = L.Control.extend({
 		th = L.DomUtil.create('th', '', tr);
 		th.appendChild(document.createTextNode(_('Comment')));
 		th = L.DomUtil.create('th', '', tr);
-		th.appendChild(document.createTextNode(_('View ID')));
+		th.appendChild(document.createTextNode(_('User name')));
 		th = L.DomUtil.create('th', '', tr);
 		th.appendChild(document.createTextNode(_('Timestamp')));
 
@@ -74,7 +74,11 @@ L.Control.DocumentRepair = L.Control.extend({
 
 	fillAction: function (actions, type) {
 		for (var iterator = 0; iterator < actions.length; ++iterator) {
-			this.createAction(type, actions[iterator].index, actions[iterator].comment, actions[iterator].viewId, actions[iterator].dateTime);
+			var userName = actions[iterator].userName;
+			if (parseInt(actions[iterator].viewId) === this._map._docLayer._viewId) {
+				userName = _('You');
+			}
+			this.createAction(type, actions[iterator].index, actions[iterator].comment, userName, actions[iterator].dateTime);
 		}
 	},
 
diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp
index 7ae8b7e..355a5d4 100644
--- a/loolwsd/ChildSession.cpp
+++ b/loolwsd/ChildSession.cpp
@@ -397,6 +397,38 @@ bool ChildSession::getStatus(const char* /*buffer*/, int /*length*/)
     return sendTextFrame("status: " + status);
 }
 
+namespace
+{
+
+/// Given a view ID <-> user name map and a .uno:DocumentRepair result, annotate with user names.
+void insertUserNames(const std::map<int, std::string>& viewInfo, std::string& json)
+{
+    Poco::JSON::Parser parser;
+    auto root = parser.parse(json).extract<Poco::JSON::Object::Ptr>();
+    std::vector<std::string> directions { "Undo", "Redo" };
+    for (auto& directionName : directions)
+    {
+        auto direction = root->get(directionName).extract<Poco::JSON::Object::Ptr>();
+        if (direction->get("actions").type() == typeid(Poco::JSON::Array::Ptr))
+        {
+            auto actions = direction->get("actions").extract<Poco::JSON::Array::Ptr>();
+            for (auto& actionVar : *actions)
+            {
+                auto action = actionVar.extract<Poco::JSON::Object::Ptr>();
+                int viewId = action->getValue<int>("viewId");
+                auto it = viewInfo.find(viewId);
+                if (it != viewInfo.end())
+                    action->set("userName", Poco::Dynamic::Var(it->second));
+            }
+        }
+    }
+    std::stringstream ss;
+    root->stringify(ss);
+    json = ss.str();
+}
+
+}
+
 bool ChildSession::getCommandValues(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
 {
     bool success;
@@ -415,12 +447,16 @@ bool ChildSession::getCommandValues(const char* /*buffer*/, int /*length*/, Stri
     if (command == ".uno:DocumentRepair")
     {
         char* pUndo;
-        const std::string json("{\"commandName\":\".uno:DocumentRepair\",\"Redo\":%s,\"Undo\":%s}");
+        const std::string jsonTemplate("{\"commandName\":\".uno:DocumentRepair\",\"Redo\":%s,\"Undo\":%s}");
         pValues = _loKitDocument->getCommandValues(".uno:Redo");
         pUndo = _loKitDocument->getCommandValues(".uno:Undo");
-        success = sendTextFrame("commandvalues: " + Poco::format(json,
-                                                                 std::string(pValues == nullptr ? "" : pValues),
-                                                                 std::string(pUndo == nullptr ? "" : pUndo)));
+        std::string json = Poco::format(jsonTemplate,
+                                        std::string(pValues == nullptr ? "" : pValues),
+                                        std::string(pUndo == nullptr ? "" : pUndo));
+        // json only contains view IDs, insert matching user names.
+        std::map<int, std::string> viewInfo =_docManager.getViewInfo();
+        insertUserNames(viewInfo, json);
+        success = sendTextFrame("commandvalues: " + json);
         std::free(pValues);
         std::free(pUndo);
     }
diff --git a/loolwsd/ChildSession.hpp b/loolwsd/ChildSession.hpp
index 87a754f..dfc5bf2 100644
--- a/loolwsd/ChildSession.hpp
+++ b/loolwsd/ChildSession.hpp
@@ -43,6 +43,9 @@ public:
     /// Send updated view info to all active sessions
     virtual
     void notifyViewInfo() = 0;
+    /// Get a view ID <-> user name map.
+    virtual
+    std::map<int, std::string> getViewInfo() = 0;
 };
 
 /// Represents a session to the WSD process, in a Kit process. Note that this is not a singleton.
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index c37f2c6..d34fe47 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -996,6 +996,24 @@ private:
         notifyViewInfo();
     }
 
+    std::map<int, std::string> getViewInfo() override
+    {
+        std::unique_lock<std::mutex> lock(_mutex);
+        std::map<int, std::string> viewInfo;
+
+        for (auto& connection : _connections)
+        {
+            if (connection.second->isRunning())
+            {
+                const auto session = connection.second->getSession();
+                const auto viewId = session->getViewId();
+                viewInfo[viewId] = session->getViewUserName();
+            }
+        }
+
+        return viewInfo;
+    };
+
     /// Notify all views of viewId and their associated usernames
     void notifyViewInfo() override
     {
@@ -1007,18 +1025,9 @@ private:
         _loKitDocument->getViewIds(viewIds.data(), viewCount);
         lockLokDoc.unlock();
 
-        std::unique_lock<std::mutex> lock(_mutex);
         // Store the list of viewid, username mapping in a map
-        std::map<int, std::string> viewInfoMap;
-        for (auto& connectionIt : _connections)
-        {
-            if (connectionIt.second->isRunning())
-            {
-                const auto session = connectionIt.second->getSession();
-                const auto viewId = session->getViewId();
-                viewInfoMap[viewId] = session->getViewUserName();
-            }
-        }
+        std::map<int, std::string> viewInfoMap = getViewInfo();
+        std::unique_lock<std::mutex> lock(_mutex);
 
         // Double check if list of viewids from core and our list matches,
         // and create an array of JSON objects containing id and username
commit 0ecae4f709050a85758f5a562f53fee4c9b8e483
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Sep 30 12:36:41 2016 +0300

    testCalcEditRendering is not working so bypass it for now
    
    (cherry picked from commit 3cabf7c9c121180f0ce75ae018ed7b90b136ff8b)

diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index 37f803d..5e8f50c 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -1489,7 +1489,8 @@ void HTTPWSTest::testCalcEditRendering()
     assert(stream.get() == '.');
     int minor = 0;
     stream >> minor;
-    if (major > 5 || (major == 5 && minor >= 2))
+
+    if (true /* major > 5 || (major == 5 && minor >= 2) */)
         return;
 
     const std::string firstLine = LOOLProtocol::getFirstLine(tile);
commit 49d7cc67e141704192665eb91abecac09e0450bd
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Sep 30 12:05:36 2016 +0300

    Re-factor Util::checkDiskSpace() into separate parts
    
    We will later want to just to a disk space check for the file system a
    file is on, without registering that file system for periodic checks.
    
    Adapt callers to keep working like before.
    
    (cherry picked from commit 9511bbda71f7a6ca95c457e7547a9468f61eace4)

diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 146449c..7e36a4b 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -230,7 +230,7 @@ static void forkChildren(const int number)
 
     if (number > 0)
     {
-        Util::checkDiskSpace("");
+        Util::checkDiskSpaceOnRegisteredFileSystems();
         const std::string aMessage = "spawn " + std::to_string(number) + "\n";
         Log::debug("MasterToForKit: " + aMessage.substr(0, aMessage.length() - 1));
         IoUtil::writeFIFO(LOOLWSD::ForKitWritePipe, aMessage);
@@ -779,7 +779,7 @@ private:
             Log::trace("Sending to Client [" + status + "].");
             ws->sendFrame(status.data(), (int) status.size());
 
-            Util::checkDiskSpace("");
+            Util::checkDiskSpaceOnRegisteredFileSystems();
 
             // Let messages flow
             IoUtil::SocketProcessor(ws,
@@ -1832,8 +1832,8 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
     else if (ChildRoot[ChildRoot.size() - 1] != '/')
         ChildRoot += '/';
 
-    Util::checkDiskSpace(ChildRoot);
-    Util::checkDiskSpace(Cache + "/.");
+    Util::registerFileSystemForDiskSpaceChecks(ChildRoot);
+    Util::registerFileSystemForDiskSpaceChecks(Cache + "/.");
 
     if (FileServerRoot.empty())
         FileServerRoot = Poco::Path(Application::instance().commandPath()).parent().parent().toString();
diff --git a/loolwsd/Util.cpp b/loolwsd/Util.cpp
index 2ff8532..add0fdd 100644
--- a/loolwsd/Util.cpp
+++ b/loolwsd/Util.cpp
@@ -212,36 +212,44 @@ namespace Util
         }
     }
 
-    void checkDiskSpace(const std::string& path)
-    {
-        static std::mutex mutex;
-        std::lock_guard<std::mutex> lock(mutex);
+} // namespace Util
+
+namespace
+{
 
-        struct fs
+    struct fs
+    {
+        fs(const std::string& p, dev_t d)
+            : path(p), dev(d)
         {
-            fs(const std::string& p, dev_t d)
-                : path(p), dev(d)
-            {
-            }
+        }
 
-            fs(dev_t d)
-                : fs("", d)
-            {
-            }
+        fs(dev_t d)
+            : fs("", d)
+        {
+        }
 
-            std::string path;
-            dev_t dev;
-        };
+        std::string path;
+        dev_t dev;
+    };
 
-        struct fsComparator
+    struct fsComparator
+    {
+        bool operator() (const fs& lhs, const fs& rhs) const
         {
-            bool operator() (const fs& lhs, const fs& rhs) const
-            {
-                return (lhs.dev < rhs.dev);
-            }
-        };
+            return (lhs.dev < rhs.dev);
+        }
+    };
+
+    static std::mutex fsmutex;
+    static std::set<fs, fsComparator> filesystems;
+} // unnamed namespace
 
-        static std::set<fs, fsComparator> filesystems;
+namespace Util
+{
+    void registerFileSystemForDiskSpaceChecks(const std::string& path)
+    {
+        std::lock_guard<std::mutex> lock(fsmutex);
 
         if (path != "")
         {
@@ -255,11 +263,16 @@ namespace Util
                 return;
             filesystems.insert(fs(dirPath, s.st_dev));
         }
+    }
+
+    void checkDiskSpaceOnRegisteredFileSystems()
+    {
+        std::lock_guard<std::mutex> lock(fsmutex);
 
         static std::chrono::steady_clock::time_point lastCheck;
         std::chrono::steady_clock::time_point now(std::chrono::steady_clock::now());
 
-        // Don't check disk space more often that once a minute
+        // Don't check more often that once a minute
         if (std::chrono::duration_cast<std::chrono::seconds>(now - lastCheck).count() < 60)
             return;
 
@@ -267,13 +280,7 @@ namespace Util
 
         for (auto& i: filesystems)
         {
-            struct stat s;
-            struct statfs sfs;
-            if (stat(i.path.c_str(), &s) == -1 ||
-                statfs(i.path.c_str(), &sfs) == -1)
-                continue;
-
-            if (static_cast<double>(sfs.f_bavail) / sfs.f_blocks <= 0.05)
+            if (!checkDiskSpace(i.path))
             {
                 alertAllUsersAndLog("File system of " + i.path + " dangerously low on disk space", "internal", "diskfull");
                 break;
@@ -281,6 +288,18 @@ namespace Util
         }
     }
 
+    bool checkDiskSpace(const std::string& path)
+    {
+        assert(path != "");
+        struct statfs sfs;
+        if (statfs(path.c_str(), &sfs) == -1)
+            return true;
+
+        if (static_cast<double>(sfs.f_bavail) / sfs.f_blocks <= 0.05)
+            return false;
+        return true;
+    }
+
     const char *signalName(const int signo)
     {
         switch (signo)
diff --git a/loolwsd/Util.hpp b/loolwsd/Util.hpp
index 04673d0..935264e 100644
--- a/loolwsd/Util.hpp
+++ b/loolwsd/Util.hpp
@@ -73,12 +73,20 @@ namespace Util
     }
 #endif
 
-    // Check disk space on a list of file systems. The list is initially empty, and each call to the
-    // function with a non-empty 'path' adds the file system that path is located on to the list, if
-    // not already there. If the free space on any of the file systems in question is below 5%, call
+    // Add the file system that 'path' is located on to a list of file systems that are periodically
+    // checked for available space. The list is initially empty.
+    void registerFileSystemForDiskSpaceChecks(const std::string& path);
+
+    // Perform the check. If the free space on any of the registered file systems is below 5%, call
     // 'alertAllUsers("internal", "diskfull")'. The check will be made no more often than once a
     // minute.
-    void checkDiskSpace(const std::string& path);
+    void checkDiskSpaceOnRegisteredFileSystems();
+
+    // Check disk space on a specific file system, the one where 'path' is located. This does not
+    // add that file system to the list used by 'registerFileSystemForDiskSpaceChecks'. If the free
+    // space on the file system is below 5%, return false, otherwise true. Note that this function
+    // does not call 'alertAllUsers'.
+    bool checkDiskSpace(const std::string& path);
 
     /// Assert that a lock is already taken.
     template <typename T>
commit 9bdc2d4a9a7368c367a89484fe3519c623a49c42
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri Sep 30 15:49:24 2016 +0530

    loleaflet: loleaflet-font for userlist items too
    
    Change-Id: If2c96d9d70a84bac1e75816f07bb7da8ff2642f5
    (cherry picked from commit ec88a530728c5f6f16fd0e6e3e1d66759b3bc575)

diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 5266d0d..85356c7 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -460,6 +460,9 @@ $(function () {
 				return;
 			}
 			onClick(e.target, e.item, e.subItem);
+		},
+		onRefresh: function(e) {
+			$('#tb_toolbar-down_item_userlist .w2ui-tb-caption').addClass('loleaflet-font');
 		}
 	});
 });
@@ -1295,7 +1298,7 @@ map.on('statusindicator', function (e) {
 function getUserItem(viewId, userName, color) {
 	var html = '<tr class="useritem" id="user-' + viewId + '">' +
 	             '<td class=usercolor style="background-color: ' + color  +';"></td>' +
-	             '<td class="username">' + userName + '</td>' +
+	             '<td class="username loleaflet-font">' + userName + '</td>' +
 	           '</tr>';
 	return html;
 }
commit 5bdde8262dfe25a0167f8d528548fb3e14dbba36
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri Sep 30 15:20:06 2016 +0530

    loleaflet: Show error message dialog on diskfull
    
    Set the mode to viewonly after the error so that user can still
    see the tiles downloaded from the server, but cannot interact
    anymore before server administrator fixes the disk space.
    
    Change-Id: If03f4fabe93458672a631c020d09dcebecbb955c
    (cherry picked from commit 7b60a9e3f6d5b65dabfaecc097b848a7184f29da)

diff --git a/loleaflet/dist/errormessages.js b/loleaflet/dist/errormessages.js
index 0a2003c..dc99714 100644
--- a/loleaflet/dist/errormessages.js
+++ b/loleaflet/dist/errormessages.js
@@ -1,2 +1,3 @@
 exports.wrongwopisrc = _('Wrong WOPISrc, usage: WOPISrc=valid encoded URI, or file_path, usage: file_path=/path/to/doc/');
 exports.emptyhosturl = _('The host URL is empty. The loolwsd server is probably misconfigured, please contact the administrator.');
+exports.diskfull = _('No disk space left on server, please contact the server administrator to continue.');
diff --git a/loleaflet/main.js b/loleaflet/main.js
index cb53fb7..c2851d6 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -75,6 +75,7 @@ if (host === '') {
 global.closebutton = closebutton;
 global.revHistoryEnabled = revHistoryEnabled;
 global.title = title;
+global.errorMessages = errorMessages;
 var docURL, docParams;
 if (wopiSrc != '') {
     docURL = wopiSrc;
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index 54a1d95..19eeaa9 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -48,6 +48,10 @@ L.Map.include({
 	},
 
 	downloadAs: function (name, format, options, id) {
+		if (this._fatal) {
+			return;
+		}
+
 		if (format === undefined || format === null) {
 			format = '';
 		}
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 53598fc..0ce132c 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -2,7 +2,7 @@
  * L.Socket contains methods for the communication with the server
  */
 
-/* global _ vex $ */
+/* global _ vex $ errorMessages */
 L.Socket = L.Class.extend({
 	ProtocolVersionNumber: '0.1',
 
@@ -37,7 +37,8 @@ L.Socket = L.Class.extend({
 	},
 
 	sendMessage: function (msg, coords) {
-		if (!msg.startsWith('useractive') && !msg.startsWith('userinactive') && !this._map._active) {
+		if ((!msg.startsWith('useractive') && !msg.startsWith('userinactive') && !this._map._active) ||
+		    this._map._fatal) {
 			// Avoid communicating when we're inactive.
 			return;
 		}
@@ -143,6 +144,19 @@ L.Socket = L.Class.extend({
 			                         lokitVersionObj.ProductVersion + lokitVersionObj.ProductExtension.replace('.10.','-') +
 			                         ' (git hash: ' + lokitVersionObj.BuildId.substring(0, 7) + ')');
 		}
+		else if (textMsg.startsWith('error:') && command.errorCmd === 'internal') {
+			this._map._fatal = true;
+			if (command.errorKind === 'diskfull') {
+				this._map.fire('error', {msg: errorMessages.diskfull});
+			}
+
+			if (this._map._docLayer) {
+				this._map._docLayer.removeAllViews();
+			}
+			this.close();
+
+			return;
+		}
 		else if (textMsg.startsWith('error:') && command.errorCmd === 'load') {
 			this.close();
 
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 5110c60..7d2dfac 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -65,6 +65,7 @@ L.Map = L.Evented.extend({
 		this._sizeChanged = true;
 		this._bDisableKeyboard = false;
 		this._active = true;
+		this._fatal = false;
 		this._enabled = true;
 
 		vex.dialogID = -1;
commit d040ed30869cd4e0c73f31481c96ed34badff57e
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri Sep 30 13:44:11 2016 +0530

    loleaflet: Use 'error' event to open the alert dialog
    
    Change-Id: I8f1ca3d896027755fe1291cb227ded102e57e112
    (cherry picked from commit 42fbfa936ce2007686b5f9931a20b6a31a66d1fe)

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 9a6579e..53598fc 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -187,10 +187,7 @@ L.Socket = L.Class.extend({
 		}
 		else if (textMsg.startsWith('error:') && !this._map._docLayer) {
 			textMsg = textMsg.substring(6);
-			vex.dialog.alert({
-				message: textMsg,
-				css: {'font-size': 'small'}
-			});
+			this._map.fire('error', {msg: textMsg});
 		}
 		else if (textMsg === 'pong' && this._map._docLayer && this._map._docLayer._debug) {
 			var times = this._map._docLayer._debugTimePING;
commit 621ad1cfe4737b757e515b63b40eb2ac7ee033ce
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 11:02:09 2016 +0200

    LocalStorage: differentiate between multiple localhost users
    
    Hopefully useful in general, but currently I need this to see if view
    ids are mapped to the correct user names even with file:/// URLs.
    
    Change-Id: Iec7d552ee0b0d53738040d868e49ecb7e9c10843
    (cherry picked from commit c9346e14b1013a12db81d7d2f8f287d841976f2a)

diff --git a/loolwsd/Storage.cpp b/loolwsd/Storage.cpp
index c0e1c4c..89efd79 100644
--- a/loolwsd/Storage.cpp
+++ b/loolwsd/Storage.cpp
@@ -163,6 +163,8 @@ std::unique_ptr<StorageBase> StorageBase::create(const std::string& jailRoot, co
     throw BadRequestException("No Storage configured or invalid URI.");
 }
 
+unsigned LocalStorage::LastLocalStorageId = 0;
+
 StorageBase::FileInfo LocalStorage::getFileInfo(const Poco::URI& uri)
 {
     const auto path = Poco::Path(uri.getPath());
@@ -171,7 +173,7 @@ StorageBase::FileInfo LocalStorage::getFileInfo(const Poco::URI& uri)
     const auto file = Poco::File(path);
     const auto lastModified = file.getLastModified();
     const auto size = file.getSize();
-    return FileInfo({filename, lastModified, size, "localhost", "Local Host"});
+    return FileInfo({filename, lastModified, size, "localhost", std::string("Local Host #") + std::to_string(_localStorageId)});
 }
 
 std::string LocalStorage::loadStorageFileToLocal()
diff --git a/loolwsd/Storage.hpp b/loolwsd/Storage.hpp
index 6ab3e93..23bc7cd 100644
--- a/loolwsd/Storage.hpp
+++ b/loolwsd/Storage.hpp
@@ -118,7 +118,8 @@ public:
                  const std::string& jailPath,
                  const std::string& uri) :
         StorageBase(localStorePath, jailPath, uri),
-        _isCopy(false)
+        _isCopy(false),
+        _localStorageId(LocalStorage::LastLocalStorageId++)
     {
         Log::info("LocalStorage ctor with localStorePath: [" + localStorePath +
                   "], jailPath: [" + jailPath + "], uri: [" + uri + "].");
@@ -133,6 +134,9 @@ public:
 private:
     /// True if the jailed file is not linked but copied.
     bool _isCopy;
+    static unsigned LastLocalStorageId;
+    /// Used to differentiate between multiple localhost users.
+    const unsigned _localStorageId;
 };
 
 /// WOPI protocol backed storage.
commit 9e6bd6425ad889b2e9cfb288f25dd1ff45e325c5
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Sep 30 10:53:22 2016 +0200

    Let the make fail when the unittest fails.
    
    Change-Id: I66a7e82b864f1c13c1c145c27f019b832c38bb62
    (cherry picked from commit bf80260a28ee61801da2c7d22457df5e730ce065)

diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index 5cc993e..cbe1135 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -87,4 +87,4 @@ all-local: unittest
 	@echo
 	@echo "Running build-time unit tests.  For more thorough testing, please run 'make check'."
 	@echo
-	@${top_builddir}/test/unittest 2> unittest.log || cat unittest.log
+	@${top_builddir}/test/unittest 2> unittest.log || { cat unittest.log ; exit 1 ; }
commit f60544df469580d835611cd2d31b9d65442baa70
Author: Jan Holesovsky <kendy at collabora.com>
Date:   Fri Sep 30 10:43:04 2016 +0200

    Run unit tests (that don't require loolwsd) during the build time.
    
    Change-Id: I2b1ea4fb5e6dcd8e4eb5f3890507f541c1457b39
    (cherry picked from commit 0b5fa973da715bd5e5539ce953628ca15e45c8cf)

diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am
index fa6c3a0..295d120 100644
--- a/loolwsd/Makefile.am
+++ b/loolwsd/Makefile.am
@@ -180,10 +180,13 @@ clang-tidy:
 # installing the RPM or Debian package.
 
 all-local: loolforkit @JAILS_PATH@ $(SYSTEM_STAMP)
-	if test "$$BUILDING_FROM_RPMBUILD" != yes; then \
+	@if test "$$BUILDING_FROM_RPMBUILD" != yes; then \
 	    sudo @SETCAP@ cap_fowner,cap_mknod,cap_sys_chroot=ep loolforkit; \
 	    sudo @SETCAP@ cap_sys_admin=ep loolmount; \
 	    echo "Set required capabilities"; \
 	else \
 	    echo "Skipping capability setting"; \
 	fi
+
+# just run the build without any tests
+build-nocheck: all-am
diff --git a/loolwsd/test/Makefile.am b/loolwsd/test/Makefile.am
index cf255d9..5cc993e 100644
--- a/loolwsd/test/Makefile.am
+++ b/loolwsd/test/Makefile.am
@@ -2,8 +2,14 @@
 export MAX_CONCURRENCY=4
 AUTOMAKE_OPTION = serial-tests
 
+# unittest: tests that do not need loolwsd running, and that are run during a
+# normal build
+bin_PROGRAMS = unittest
+
+# unittest: tests that need loolwsd running, and that are run via 'make check'
 check_PROGRAMS = test
-noinst_PROGRAMS = test
+
+noinst_PROGRAMS = test unittest
 
 AM_CXXFLAGS = $(CPPUNIT_CFLAGS)
 
@@ -33,10 +39,14 @@ wsd_sources = \
             ../Util.cpp
 
 test_CPPFLAGS = -DTDOC=\"$(abs_top_srcdir)/test/data\" -I$(top_srcdir) -DBUILDING_TESTS
-test_SOURCES = TileCacheTests.cpp TileQueueTests.cpp WhiteBoxTests.cpp integration-http-server.cpp \
+test_SOURCES = TileCacheTests.cpp integration-http-server.cpp \
                httpwstest.cpp httpcrashtest.cpp httpwserror.cpp test.cpp $(wsd_sources)
 test_LDADD = $(CPPUNIT_LIBS)
 
+unittest_CPPFLAGS = -I$(top_srcdir) -DBUILDING_TESTS
+unittest_SOURCES = TileQueueTests.cpp WhiteBoxTests.cpp test.cpp $(wsd_sources)
+unittest_LDADD = $(CPPUNIT_LIBS)
+
 # unit test modules:
 unit_fuzz_la_SOURCES = UnitFuzz.cpp
 unit_admin_la_SOURCES = UnitAdmin.cpp
@@ -67,8 +77,14 @@ endif
 TEST_EXTENSIONS = .la
 LA_LOG_DRIVER = ${top_srcdir}/test/run_unit.sh
 
-EXTRA_DIST = data/hello.odt data/hello.txt $(test_SOURCES) run_unit.sh
+EXTRA_DIST = data/hello.odt data/hello.txt $(test_SOURCES) $(unittest_SOURCES) run_unit.sh
 
 check_valgrind: all
 	./run_unit.sh --log-file test.log --trs-file test.trs --valgrind
 
+# run unittest during the normal build
+all-local: unittest
+	@echo
+	@echo "Running build-time unit tests.  For more thorough testing, please run 'make check'."
+	@echo
+	@${top_builddir}/test/unittest 2> unittest.log || cat unittest.log
commit 5935210f1b810eb98ade4698c457942691fddfc1
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 09:42:12 2016 +0200

    Revert "httpwstest: add tolerance, so that testCursorPosition actually passes"
    
    This reverts commit 3629113e5978ea26f8222e79a372aef807fab8fb. This
    workaround is no longer needed, now that core.git
    b32b6c09d190effbe29389a87a80df36007d2e99 (sw lok: disable pixel
    alignment of cursor logic values, 2016-09-29) fixed the root cause.
    
    Change-Id: I208e18517158cf55b143d1018ad0fd933db3accf
    (cherry picked from commit 8c2446d15bc60594b35c51155198d1b9621b2376)

diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index dcb9b42..37f803d 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -15,7 +15,6 @@
 #include <thread>
 #include <regex>
 #include <vector>
-#include <string>
 
 #include <Poco/Dynamic/Var.h>
 #include <Poco/FileStream.h>
@@ -2200,12 +2199,11 @@ void HTTPWSTest::testCursorPosition()
         Poco::StringTokenizer viewTokens(command->get("rectangle").toString(), ",", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
         CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), viewTokens.count());
 
-        // check both cursor should be equal (but tolerate at most 2px -> 30 twips differences)
-        const int tolerance = 30;
-        CPPUNIT_ASSERT(std::abs(std::stoi(cursorTokens[0]) - std::stoi(viewTokens[0])) < tolerance);
-        CPPUNIT_ASSERT(std::abs(std::stoi(cursorTokens[1]) - std::stoi(viewTokens[1])) < tolerance);
-        CPPUNIT_ASSERT(std::abs(std::stoi(cursorTokens[2]) - std::stoi(viewTokens[2])) < tolerance);
-        CPPUNIT_ASSERT(std::abs(std::stoi(cursorTokens[3]) - std::stoi(viewTokens[3])) < tolerance);
+        // check both cursor should be equal
+        CPPUNIT_ASSERT_EQUAL(cursorTokens[0], viewTokens[0]);
+        CPPUNIT_ASSERT_EQUAL(cursorTokens[1], viewTokens[1]);
+        CPPUNIT_ASSERT_EQUAL(cursorTokens[2], viewTokens[2]);
+        CPPUNIT_ASSERT_EQUAL(cursorTokens[3], viewTokens[3]);
     }
     catch (const Poco::Exception& exc)
     {
commit a37eb621e40683a83422ee022bbe74c4a1773f5b
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Sep 30 09:29:08 2016 +0200

    DocumentBroker: it's enough to build that temporary string once
    
    Change-Id: I253874fefdb2dfe9bb22a8b3bef641499a49dcc9
    (cherry picked from commit 827d5ed9c825c3636d32071de2611029436e1f87)

diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp
index ba2dd04..aeafaa3 100644
--- a/loolwsd/DocumentBroker.cpp
+++ b/loolwsd/DocumentBroker.cpp
@@ -450,9 +450,11 @@ void DocumentBroker::alertAllUsersOfDocument(const std::string& cmd, const std::
 {
     std::lock_guard<std::mutex> lock(_mutex);
 
+    std::stringstream ss;
+    ss << "error: cmd=" << cmd << " kind=" << kind;
     for (auto& it: _sessions)
     {
-        it.second->sendTextFrame("error: cmd=" + cmd + " kind=" + kind);
+        it.second->sendTextFrame(ss.str());
     }
 }
 
diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am
index b9c935c..fa6c3a0 100644
--- a/loolwsd/Makefile.am
+++ b/loolwsd/Makefile.am
@@ -170,6 +170,9 @@ clean-cache cache-clean:
 # Intentionally don't use "*" below... Avoid risk of accidentally running rm -rf /*
 	test -n "@LOOLWSD_CACHEDIR@" && rm -rf "@LOOLWSD_CACHEDIR@"/[0-9a-f]
 
+clang-tidy:
+	for i in *.cpp; do echo $$i; clang-tidy -header-filter=^$(PWD).* $$i || break; done
+
 # After building loolforkit, set its capabilities as required. Do it
 # already after a plain 'make' to allow for testing without
 # installing. When building for packaging, no need for this, as the
commit 80d0a7af3851529bbcecf0ebcbd11a6ea2607b40
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 29 17:57:17 2016 -0400

    loleaflet: fix mege mistakes
    
    (cherry picked from commit b4ba3c9c553ac836007b2f1c3cb4a45280b61673)

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 937af32..9a6579e 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -12,7 +12,7 @@ L.Socket = L.Class.extend({
 			if (map.options.permission) {
 				map.options.docParams['permission'] = map.options.permission;
 			}
-			this.socket = new WebSocket(map.options.server + '/lool/ws/' + map.options.doc + '?' + $.param(map.options.docParams));
+			this.socket = new WebSocket(map.options.server + '/lool/' + encodeURIComponent(map.options.doc + '?' + $.param(map.options.docParams)) + '/ws');
 			this.socket.onerror = L.bind(this._onSocketError, this);
 			this.socket.onclose = L.bind(this._onSocketClose, this);
 			this.socket.onopen = L.bind(this._onSocketOpen, this);
commit 7c41703c4c5bb3e7f18dd2408cb6b07e1bc4c82d
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 29 13:48:08 2016 -0400

    loleaflet: fix undefined variables
    
    (cherry picked from commit e766e82f3d118ef0c5f2d8dac9a14255232b2dd8)

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index f456ce6..5110c60 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -754,7 +754,7 @@ L.Map = L.Evented.extend({
 			// A dialog is already dimming the screen and probably
 			// shows an error message. Leave it alone.
 			this._active = false;
-			this._docLayer._onMessage('textselection:', null);
+			this._docLayer && this._docLayer._onMessage('textselection:', null);
 			if (this._socket.connected()) {
 				this._socket.sendMessage('userinactive');
 			}
@@ -798,7 +798,7 @@ L.Map = L.Evented.extend({
 			$(options.appendLocation).append(options.$vex);
 			vex.setupBodyClassName(options.$vex);
 
-			map._docLayer._onMessage('textselection:', null);
+			map._doclayer && map._docLayer._onMessage('textselection:', null);
 			map._socket.sendMessage('userinactive');
 
 		}, 30 * 1000); // Dim in 30 seconds.
@@ -808,7 +808,7 @@ L.Map = L.Evented.extend({
 		if (!this._loaded) { return; }
 
 		var doclayer = this._docLayer;
-		if (doclayer._isCursorVisible && doclayer._isCursorOverlayVisible) {
+		if (doclayer && doclayer._isCursorVisible && doclayer._isCursorOverlayVisible) {
 			doclayer._visibleCursorOnLostFocus = doclayer._visibleCursor;
 			doclayer._isCursorOverlayVisibleOnLostFocus = doclayer._isCursorVisibleOnLostFocus = true;
 			doclayer._isCursorOverlayVisible = false;
@@ -822,7 +822,7 @@ L.Map = L.Evented.extend({
 		if (!this._loaded) { return; }
 
 		var doclayer = this._docLayer;
-		if (doclayer._isCursorVisibleOnLostFocus && doclayer._isCursorOverlayVisibleOnLostFocus) {
+		if (doclayer && doclayer._isCursorVisibleOnLostFocus && doclayer._isCursorOverlayVisibleOnLostFocus) {
 			// we restore the old cursor position by a small delay, so that if the user clicks
 			// inside the document we skip to restore it, so that the user does not see the cursor
 			// jumping from the old position to the new one
commit 85dcba82b5ae26005012334f1fa1164ee43a6983
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 29 09:50:00 2016 -0400

    loleaflet: fix show dialog error
    
    Conflicts:
    	loleaflet/src/core/Socket.js
    
    (cherry picked from commit 3fcde8be836da5b54a110f43f888030fd749b093)

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index d66c578..937af32 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -12,17 +12,17 @@ L.Socket = L.Class.extend({
 			if (map.options.permission) {
 				map.options.docParams['permission'] = map.options.permission;
 			}
-			this.socket = new WebSocket(map.options.server + '/lool/' + encodeURIComponent(map.options.doc + '?' + $.param(map.options.docParams)) + '/ws');
+			this.socket = new WebSocket(map.options.server + '/lool/ws/' + map.options.doc + '?' + $.param(map.options.docParams));
+			this.socket.onerror = L.bind(this._onSocketError, this);
+			this.socket.onclose = L.bind(this._onSocketClose, this);
+			this.socket.onopen = L.bind(this._onSocketOpen, this);
+			this.socket.onmessage = L.bind(this._onMessage, this);
+			this.socket.binaryType = 'arraybuffer';
 		} catch (e) {
-			this.fire('error', {msg: _('Oops, there is a problem connecting to LibreOffice Online : ' + e), cmd: 'socket', kind: 'failed', id: 3});
+			this._map.fire('error', {msg: _('Oops, there is a problem connecting to LibreOffice Online : ' + e), cmd: 'socket', kind: 'failed', id: 3});
 			return null;
 		}
 		this._msgQueue = [];
-		this.socket.onerror = L.bind(this._onSocketError, map);
-		this.socket.onclose = L.bind(this._onSocketClose, map);
-		this.socket.onopen = L.bind(this._onOpen, this);
-		this.socket.onmessage = L.bind(this._onMessage, this);
-		this.socket.binaryType = 'arraybuffer';
 	},
 
 	close: function () {
@@ -69,7 +69,7 @@ L.Socket = L.Class.extend({
 		this.socket.send(msg);
 	},
 
-	_onOpen: function () {
+	_onSocketOpen: function () {
 		// Always send the protocol version number.
 		// TODO: Move the version number somewhere sensible.
 		this._doSend('loolclient ' + this.ProtocolVersionNumber);
@@ -134,7 +134,7 @@ L.Socket = L.Class.extend({
 
 			// TODO: For now we expect perfect match in protocol versions
 			if (loolwsdVersionObj.Protocol !== this.ProtocolVersionNumber) {
-				this.fire('error', {msg: _('Unsupported server version.')});
+				this._map.fire('error', {msg: _('Unsupported server version.')});
 			}
 		}
 		else if (textMsg.startsWith('lokitversion ')) {
@@ -186,7 +186,11 @@ L.Socket = L.Class.extend({
 			}
 		}
 		else if (textMsg.startsWith('error:') && !this._map._docLayer) {
-			this.fail = true;
+			textMsg = textMsg.substring(6);
+			vex.dialog.alert({
+				message: textMsg,
+				css: {'font-size': 'small'}
+			});
 		}
 		else if (textMsg === 'pong' && this._map._docLayer && this._map._docLayer._debug) {
 			var times = this._map._docLayer._debugTimePING;
@@ -301,26 +305,24 @@ L.Socket = L.Class.extend({
 	},
 
 	_onSocketError: function () {
-		this.hideBusy();
+		this._map.hideBusy();
 		// Let onclose (_onSocketClose) report errors.
 	},
 
-	_onSocketClose: function () {
-		this.hideBusy();
-		if (this) {
-			this._active = false;
-		}
+	_onSocketClose: function (e) {
+		this._map.hideBusy();
+		this._map._active = false;
 
-		if (this._docLayer) {
-			this._docLayer.removeAllViews();
+		if (this._map._docLayer) {
+			this._map._docLayer.removeAllViews();
 		}
-		if (this.fail) {
-			this.fire('error', {msg: _('Well, this is embarrassing, we cannot connect to your document. Please try again.'), cmd: 'socket', kind: 'closed', id: 4});
+
+		if (e.code && e.reason) {
+			this._map.fire('error', {msg: e.reason});
 		}
 		else {
-			this.fire('error', {msg: _('We are sorry, this is an unexpected connection error. Please try again.'), cmd: 'socket', kind: 'closed', id: 4});
+			this._map.fire('error', {msg: _('Well, this is embarrassing, we cannot connect to your document. Please try again.'), cmd: 'socket', kind: 'closed', id: 4});
 		}
-		this.fail = false;
 	},
 
 	parseServerCmd: function (msg) {
commit 87117880e1d7902ab7e72050c95beca9f3f11cae
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 29 09:48:09 2016 -0400

    loolwsd: control frames must have a payload of 125 bytes
    
    (cherry picked from commit 6f7d67a20472f16a78c58e0dd8017195b0d4bb5d)

diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 460a6c9..146449c 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -180,6 +180,50 @@ static std::map<std::string, std::shared_ptr<PrisonerSession>> AvailableChildSes
 static int careerSpanSeconds = 0;
 #endif
 
+namespace {
+
+static inline
+void lcl_shutdownLimitReached(WebSocket& ws)
+{
+    const std::string msg = std::string("error: ") + Poco::format(PAYLOAD_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS,
+        std::string(LOOLWSD_PRODUCT),
+        std::string(LOOLWSD_URL),
+        std::string(LOOLWSD_URL));
+
+    /* loleaflet sends loolclient, load and partrectangles message immediately
+       after web socket handshake, so closing web socket fails loading page in
+       some sensible browsers. Ignore handshake messages and gracefully
+       close in order to send error messages.
+    */
+    try
+    {
+        int flags = 0;
+        int handshake = 3;
+        std::vector<char> buffer(READ_BUFFER_SIZE * 100);
+
+        // 5 seconds timeout
+        ws.setReceiveTimeout(5000000);
+        do
+        {
+            // ignore loolclient, load and partpagerectangles
+            ws.receiveFrame(buffer.data(), buffer.capacity(), flags);
+            if (--handshake == 0)
+            {
+                ws.sendFrame(msg.data(), msg.size());
+                ws.shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS));
+            }
+        }
+        while ((flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
+    }
+    catch (Exception& e)
+    {
+        ws.sendFrame(msg.data(), msg.size());
+        ws.shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS));
+    }
+}
+
+}
+
 static void forkChildren(const int number)
 {
     Util::assertIsLocked(newChildrenMutex);
@@ -657,7 +701,8 @@ private:
             {
                 --LOOLWSD::NumDocBrokers;
                 Log::error("Maximum number of open documents reached.");
-                throw WebSocketErrorMessageException(Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS, std::string(LOOLWSD_PRODUCT), std::string(LOOLWSD_URL), std::string(LOOLWSD_URL)));
+                lcl_shutdownLimitReached(*ws);
+                return;
             }
 #endif
 
@@ -882,9 +927,9 @@ public:
         {
             --LOOLWSD::NumConnections;
             Log::error("Maximum number of connections reached.");
-            response.setStatusAndReason(HTTPResponse::HTTP_NOT_ACCEPTABLE, Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS, std::string(LOOLWSD_PRODUCT), std::string(LOOLWSD_URL), std::string(LOOLWSD_URL)));
-            response.setContentLength(0);
-            response.send();
+            // accept hand shake
+            WebSocket ws(request, response);
+            lcl_shutdownLimitReached(ws);
             return;
         }
 #endif
diff --git a/loolwsd/UserMessages.hpp b/loolwsd/UserMessages.hpp
index 8d1dee7..7aa7aa4 100644
--- a/loolwsd/UserMessages.hpp
+++ b/loolwsd/UserMessages.hpp
@@ -15,7 +15,8 @@
 //NOTE: For whatever reason Poco seems to trim the first character.
 
 constexpr auto SERVICE_UNAVALABLE_INTERNAL_ERROR = " Service is unavailable. Please try again later and report to your administrator if the issue persists.";
-constexpr auto SERVICE_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections - to avoid the impression that it is suitable for deployment in large enterprises. To find out more about deploying and scaling %s checkout - <a href=\"%s\">%s</a>.";
+constexpr auto SERVICE_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections";
+constexpr auto PAYLOAD_UNAVALABLE_LIMIT_REACHED = "This development build is limited to %d documents, and %d connections - to avoid the impression that it is suitable for deployment in large enterprises. To find out more about deploying and scaling %s checkout: <br/><a href=\"%s\">%s</a>.";
 
 #endif
 
diff --git a/loolwsd/test/helpers.hpp b/loolwsd/test/helpers.hpp
index fd9c572..01922e3 100644
--- a/loolwsd/test/helpers.hpp
+++ b/loolwsd/test/helpers.hpp
@@ -198,7 +198,7 @@ int getErrorCode(Poco::Net::WebSocket& ws, std::string& message)
     Poco::Buffer<char> buffer(READ_BUFFER_SIZE);
 
     message.clear();
-    Poco::Timespan timeout(250000);
+    Poco::Timespan timeout(5000000);
     ws.setReceiveTimeout(timeout);
     do
     {
diff --git a/loolwsd/test/httpwserror.cpp b/loolwsd/test/httpwserror.cpp
index b8b8afb..a7f8385 100644
--- a/loolwsd/test/httpwserror.cpp
+++ b/loolwsd/test/httpwserror.cpp
@@ -97,6 +97,10 @@ void HTTPWSError::testMaxDocuments()
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, docURL);
         std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri));
         Poco::Net::WebSocket socket(*session, request, _response);
+        // send loolclient, load and partpagerectangles
+        sendTextFrame(socket, "loolclient ");
+        sendTextFrame(socket, "load ");
+        sendTextFrame(socket, "partpagerectangles ");
         statusCode = getErrorCode(socket, message);
         CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY), statusCode);
         CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("This development build") != std::string::npos);
@@ -117,6 +121,7 @@ void HTTPWSError::testMaxConnections()
         std::string docPath;
         std::string docURL;
         std::string message;
+        Poco::UInt16 statusCode;
         std::vector<std::shared_ptr<Poco::Net::WebSocket>> views;
 
         getDocumentPathAndURL("empty.odt", docPath, docURL);
@@ -125,31 +130,21 @@ void HTTPWSError::testMaxConnections()
 
         for(int it = 1; it < MAX_CONNECTIONS; it++)
         {
-            std::cerr << it << std::endl;
             std::unique_ptr<Poco::Net::HTTPClientSession> session(createSession(_uri));
             auto ws = std::make_shared<Poco::Net::WebSocket>(*session, request, _response);
             views.emplace_back(ws);
         }
 
         // try to connect MAX_CONNECTIONS + 1
-        {
-            // Load a document and get its status.
-            const std::string documentURL = "lool/ws/file:///fake.doc";
-
-            Poco::Net::HTTPResponse response;
-            Poco::Net::HTTPRequest requestN(Poco::Net::HTTPRequest::HTTP_GET, documentURL);
-            std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri));
-            requestN.set("Connection", "Upgrade");
-            requestN.set("Upgrade", "websocket");
-            requestN.set("Sec-WebSocket-Version", "13");
-            requestN.set("Sec-WebSocket-Key", "");
-            requestN.setChunkedTransferEncoding(false);
-            session->setKeepAlive(true);
-            session->sendRequest(requestN);
-            session->receiveResponse(response);
-            CPPUNIT_ASSERT_EQUAL(Poco::Net::HTTPResponse::HTTPResponse::HTTP_NOT_ACCEPTABLE, response.getStatus());
-            CPPUNIT_ASSERT_MESSAGE("Wrong error message ", response.getReason().find("This development build") != std::string::npos);
-        }
+        std::unique_ptr<Poco::Net::HTTPClientSession> session(createSession(_uri));
+        auto socketN = std::make_shared<Poco::Net::WebSocket>(*session, request, _response);
+        // send loolclient, load and partpagerectangles
+        sendTextFrame(socketN, "loolclient ");
+        sendTextFrame(socketN, "load ");
+        sendTextFrame(socketN, "partpagerectangles ");
+        statusCode = getErrorCode(*socketN, message);
+        CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY), statusCode);
+        CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("This development build") != std::string::npos);
     }
     catch (const Poco::Exception& exc)
     {
commit 60e033e119dc67fbcb18a0dc0058759ba78c05d2
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu Sep 29 22:50:52 2016 +0530

    Change main websocket url for consistency with other urls
    
    From
    /lool/ws/docurl
    to
    /lool/docurl/ws
    
    The main purpose behind changing this and other related urls is
    to help in load-balancing. The first two parts of the URL can now
    be used in a hash-based load-balancing algorithm which decides which
    slave gets to serve the requests. Requests such as insertfile,
    downloadas need to end up in the same slave in which document was opened.
    /lool/docurl/ i.e first two parts of the URL are constant for all
    the requests that should be served by a single slave.
    
    Change-Id: Ibc605ca1251957b8b32b376cf19b78f6178fadfa
    (cherry picked from commit d5f8c5b2e62f279d52673f297515fadcd1a6915c)

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 73c54fb..d66c578 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -12,7 +12,7 @@ L.Socket = L.Class.extend({
 			if (map.options.permission) {
 				map.options.docParams['permission'] = map.options.permission;
 			}
-			this.socket = new WebSocket(map.options.server + '/lool/ws/' + map.options.doc + '?' + $.param(map.options.docParams));
+			this.socket = new WebSocket(map.options.server + '/lool/' + encodeURIComponent(map.options.doc + '?' + $.param(map.options.docParams)) + '/ws');
 		} catch (e) {
 			this.fire('error', {msg: _('Oops, there is a problem connecting to LibreOffice Online : ' + e), cmd: 'socket', kind: 'failed', id: 3});
 			return null;
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 0b3051c..460a6c9 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -561,7 +561,7 @@ private:
     }
 
     /// Handle GET requests.
-    static void handleGetRequest(HTTPServerRequest& request, std::shared_ptr<WebSocket>& ws, const std::string& id)
+    static void handleGetRequest(const std::string& uri, std::shared_ptr<WebSocket>& ws, const std::string& id)
     {
         Log::info("Starting GET request handler for session [" + id + "].");
 
@@ -570,15 +570,6 @@ private:
         Log::trace("Sending to Client [" + status + "].");
         ws->sendFrame(status.data(), (int) status.size());
 
-        // Remove the leading '/' in the GET URL.
-        std::string uri = request.getURI();
-        if (uri.size() > 0 && uri[0] == '/')
-            uri.erase(0, 1);
-
-        // Remove leading 'lool/ws/' from GET URL
-        if (uri.size() > 0 && uri.compare(0, 8, "lool/ws/") == 0)
-            uri.erase(0, 8);
-
         const auto uriPublic = DocumentBroker::sanitizeURI(uri);
         const auto docKey = DocumentBroker::getDocKey(uriPublic);
         std::shared_ptr<DocumentBroker> docBroker;
@@ -912,8 +903,7 @@ public:
         Poco::URI requestUri(request.getURI());
         Log::debug("Handling GET: " + request.getURI());
 
-        std::vector<std::string> reqPathSegs;
-        requestUri.getPathSegments(reqPathSegs);
+        StringTokenizer reqPathTokens(request.getURI(), "/?", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
 
         Util::setThreadName("client_ws_" + id);
 
@@ -954,12 +944,12 @@ public:
                 responded = handleGetWOPIDiscovery(request, response);
             }
             else if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) &&
-                     reqPathSegs.size() > 0 && reqPathSegs[0] == "lool")
+                     reqPathTokens.count() > 0 && reqPathTokens[0] == "lool")
             {
                 // All post requests have url prefix 'lool'.
                 responded = handlePostRequest(request, response, id);
             }
-            else if (reqPathSegs.size() > 2 && reqPathSegs[0] == "lool" && reqPathSegs[1] == "ws")
+            else if (reqPathTokens.count() > 2 && reqPathTokens[0] == "lool" && reqPathTokens[2] == "ws")
             {
                 auto ws = std::make_shared<WebSocket>(request, response);
                 responded = true; // After upgrading to WS we should not set HTTP response.
@@ -968,7 +958,9 @@ public:
                     // First, setup WS options.
                     ws->setBlocking(false);
                     ws->setSendTimeout(WS_SEND_TIMEOUT_MICROSECS);
-                    handleGetRequest(request, ws, id);
+                    std::string decodedUri;
+                    URI::decode(reqPathTokens[1], decodedUri);
+                    handleGetRequest(decodedUri, ws, id);
                 }
                 catch (const WebSocketErrorMessageException& exc)
                 {
diff --git a/loolwsd/test/UnitAdmin.cpp b/loolwsd/test/UnitAdmin.cpp
index 36bebfc..24fe35f 100644
--- a/loolwsd/test/UnitAdmin.cpp
+++ b/loolwsd/test/UnitAdmin.cpp
@@ -204,8 +204,9 @@ private:
         const std::string subscribeMessage = "subscribe adddoc";
         _adminWs->sendFrame(subscribeMessage.data(), subscribeMessage.size());
 
-        const std::string documentPath1 = Util::getTempFilePath(TDOC, "hello.odt");
-        const std::string documentURL1 = std::string("lool/ws/") + "file://" + Poco::Path(documentPath1).makeAbsolute().toString();
+
+        std::string documentPath1, documentURL1;
+        helpers::getDocumentPathAndURL("hello.odt", documentPath1, documentURL1);
         HTTPRequest request1(HTTPRequest::HTTP_GET, documentURL1);
         HTTPResponse response1;
         const Poco::URI docUri1(helpers::getTestServerURI());
@@ -268,8 +269,8 @@ private:
         }
 
         // Open another document (different)
-        const std::string documentPath2 = Util::getTempFilePath(TDOC, "insert-delete.odp");
-        const std::string documentURL2 = std::string("lool/ws/") + "file://" + Poco::Path(documentPath2).makeAbsolute().toString();
+        std::string documentPath2, documentURL2;
+        helpers::getDocumentPathAndURL("insert-delete.odp", documentPath2, documentURL2);
         HTTPRequest request2(HTTPRequest::HTTP_GET, documentURL2);
         HTTPResponse response2;
         const Poco::URI docUri2(helpers::getTestServerURI());
diff --git a/loolwsd/test/helpers.hpp b/loolwsd/test/helpers.hpp
index f73db3c..fd9c572 100644
--- a/loolwsd/test/helpers.hpp
+++ b/loolwsd/test/helpers.hpp
@@ -86,8 +86,9 @@ inline
 void getDocumentPathAndURL(const std::string& docFilename, std::string& documentPath, std::string& documentURL)
 {
     documentPath = Util::getTempFilePath(TDOC, docFilename);
-    documentURL = "lool/ws/file://" + Poco::Path(documentPath).makeAbsolute().toString();
-
+    std::string encodedUri;
+    Poco::URI::encode("file://" + Poco::Path(documentPath).makeAbsolute().toString(), ":/?", encodedUri);
+    documentURL = "lool/" + encodedUri + "/ws";
     std::cerr << "Test file: " << documentPath << std::endl;
 }
 
diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index 03f4396..dcb9b42 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -212,8 +212,8 @@ void HTTPWSTest::testBadRequest()
 {
     try
     {
-        // Load a document and get its status.
-        const std::string documentURL = "lool/ws/file:///fake.doc";
+        // Try to load a fake document and get its status.
+        const std::string documentURL = "/lool/file%3A%2F%2F%2Ffake.doc/ws";
 
         Poco::Net::HTTPResponse response;
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL);
@@ -827,8 +827,8 @@ void HTTPWSTest::testPasswordProtectedDocumentWithCorrectPassword()
 {
     try
     {
-        const std::string documentPath = Util::getTempFilePath(TDOC, "password-protected.ods");
-        const std::string documentURL = "lool/ws/file://" + Poco::Path(documentPath).makeAbsolute().toString();
+        std::string documentPath, documentURL;
+        getDocumentPathAndURL("password-protected.ods", documentPath, documentURL);
 
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL);
         Poco::Net::WebSocket socket = *connectLOKit(_uri, request, _response);
commit 40d8d1abb286a0225d064e14d83ccf23eaf5ac34
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu Sep 29 22:44:08 2016 +0530

    loleaflet: Ignore view messages before 'viewinfo' message
    
    Change-Id: If0f759976f13a76c54c068a18bab5e07818cb86e
    (cherry picked from commit 085a07b3e223c48e103ba3fae07728088d68af65)

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 3307450..b2b1790 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -500,8 +500,8 @@ L.TileLayer = L.GridLayer.extend({
 		var obj = JSON.parse(textMsg);
 		var viewId = parseInt(obj.viewId);
 
-		// Ignore yourself
-		if (viewId === this._viewId) {
+		// Ignore if viewid is ours or not in our db
+		if (viewId === this._viewId || !this._map._viewInfo[viewId]) {
 			return;
 		}
 
@@ -606,8 +606,8 @@ L.TileLayer = L.GridLayer.extend({
 		var obj = JSON.parse(textMsg);
 		var viewId = parseInt(obj.viewId);
 
-		// Ignore if viewid is same as ours
-		if (viewId === this._viewId) {
+		// Ignore if viewid is same as ours or not in our db
+		if (viewId === this._viewId || !this._map._viewInfo[viewId]) {
 			return;
 		}
 
@@ -686,8 +686,8 @@ L.TileLayer = L.GridLayer.extend({
 		var obj = JSON.parse(textMsg);
 		var viewId = parseInt(obj.viewId);
 
-		// Ignore if viewid is same as ours
-		if (viewId === this._viewId) {
+		// Ignore if viewid is same as ours or not in our db
+		if (viewId === this._viewId || !this._map._viewInfo[viewId]) {
 			return;
 		}
 
@@ -929,8 +929,8 @@ L.TileLayer = L.GridLayer.extend({
 		var viewId = parseInt(obj.viewId);
 		var viewPart = parseInt(obj.part);
 
-		// Ignore if viewid is same as ours
-		if (viewId === this._viewId) {
+		// Ignore if viewid is same as ours or not in our db
+		if (viewId === this._viewId || !this._map._viewInfo[viewId]) {
 			return;
 		}
 
commit 920c340f82683117a13c71207bfa8e048747bdb5
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu Sep 29 21:52:45 2016 +0530

    Include docurl in document download requests
    
    From
    /lool/jailid/randomdir/filename
    to
    /lool/docurl/jailid/randomdir/filename
    
    Change-Id: I4d58e733768c62f2951ecfdcfcb604d99772862d

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list