[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-1-9' - 26 commits - loleaflet/Makefile loleaflet/src loolwsd/bundled loolwsd/ChildSession.cpp loolwsd/configure.ac loolwsd/DocumentBroker.cpp loolwsd/LibreOfficeKit.hpp loolwsd/LOOLKit.cpp loolwsd/LOOLStress.cpp loolwsd/MessageQueue.cpp loolwsd/test

Andras Timar andras.timar at collabora.com
Wed Sep 14 20:04:39 UTC 2016


 loleaflet/Makefile                                           |    2 
 loleaflet/src/control/Parts.js                               |    4 
 loleaflet/src/layer/marker/Cursor.js                         |   33 +
 loleaflet/src/layer/tile/TileLayer.js                        |  191 +++++++----
 loleaflet/src/map/Map.js                                     |    4 
 loolwsd/ChildSession.cpp                                     |    1 
 loolwsd/DocumentBroker.cpp                                   |   29 -
 loolwsd/LOOLKit.cpp                                          |   33 -
 loolwsd/LOOLStress.cpp                                       |    5 
 loolwsd/LibreOfficeKit.hpp                                   |   11 
 loolwsd/MessageQueue.cpp                                     |    2 
 loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h      |    4 
 loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h |    2 
 loolwsd/configure.ac                                         |    2 
 loolwsd/test/data/viewcursor.odp                             |binary
 loolwsd/test/helpers.hpp                                     |    1 
 loolwsd/test/httpwstest.cpp                                  |  165 +++++++++
 17 files changed, 372 insertions(+), 117 deletions(-)

New commits:
commit b63c9a6ee45fa24b50e3072f6482d0091bc0e470
Author: Andras Timar <andras.timar at collabora.com>
Date:   Wed Sep 14 22:04:14 2016 +0200

    Bump version to 1.9.1

diff --git a/loleaflet/Makefile b/loleaflet/Makefile
index d249a5a..1e1d7f1 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.0
+VERSION=1.9.1
 
 # Version number of the bundled 'draw' thing
 DRAW_VERSION=0.2.4
diff --git a/loolwsd/configure.ac b/loolwsd/configure.ac
index f301f60..06b511c 100644
--- a/loolwsd/configure.ac
+++ b/loolwsd/configure.ac
@@ -3,7 +3,7 @@
 
 AC_PREREQ([2.69])
 
-AC_INIT([loolwsd], [1.9.0], [libreoffice at lists.freedesktop.org])
+AC_INIT([loolwsd], [1.9.1], [libreoffice at lists.freedesktop.org])
 LT_INIT([shared, disable-static, dlopen])
 
 AM_INIT_AUTOMAKE([1.11 silent-rules subdir-objects])
commit 40c5b26cb2839dbfb3d63ce9c8539767bfe0ce6e
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu Sep 15 00:12:17 2016 +0530

    loleaflet: Handle multiview text selections for part documents
    
    Change-Id: I56425cde49b2b3b319204d7172f4ec4080b5bed1
    (cherry picked from commit 8bd7d83bdcaa2d9654633954f43695a53e07be91)

diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index 55e9dbb..cdde4d1 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -34,6 +34,7 @@ L.Map.include({
 		docLayer.eachView(docLayer._viewCursors, docLayer._onUpdateViewCursor, docLayer);
 		docLayer.eachView(docLayer._cellViewCursors, docLayer._onUpdateCellViewCursor, docLayer);
 		docLayer.eachView(docLayer._graphicViewMarkers, docLayer._onUpdateGraphicViewSelection, docLayer);
+		docLayer.eachView(docLayer._viewSelections, docLayer._onUpdateTextViewSelection, docLayer);
 		docLayer._clearSelections();
 		docLayer._updateOnChangePart();
 		docLayer._pruneTiles();
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 030b4b4..e6e2c7c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -719,8 +719,8 @@ L.TileLayer = L.GridLayer.extend({
 		}
 
 		// Remove selection, if any.
-		if (this._viewSelections[viewId]) {
-			this._viewSelectionsGroup.removeLayer(this._viewSelections[viewId]);
+		if (this._viewSelections[viewId] && this._viewSelections[viewId].selection) {
+			this._viewSelectionsGroup.removeLayer(this._viewSelections[viewId].selection);
 		}
 
 		// Remove the view and update (to refresh as needed).
@@ -914,6 +914,7 @@ L.TileLayer = L.GridLayer.extend({
 		textMsg = textMsg.substring('textviewselection:'.length + 1);
 		var obj = JSON.parse(textMsg);
 		var viewId = parseInt(obj.viewId);
+		var viewPart = parseInt(obj.part);
 
 		// Ignore if viewid is same as ours
 		if (viewId === this._viewId) {
@@ -921,9 +922,7 @@ L.TileLayer = L.GridLayer.extend({
 		}
 
 		var strTwips = obj.selection.match(/\d+/g);
-		if (this._viewSelections[viewId]) {
-			this._viewSelectionsGroup.removeLayer(this._viewSelections[viewId]);
-		}
+		this._viewSelections[viewId] = this._viewSelections[viewId] || {};
 		if (strTwips != null) {
 			var rectangles = [];
 			for (var i = 0; i < strTwips.length; i += 4) {
@@ -935,16 +934,13 @@ L.TileLayer = L.GridLayer.extend({
 				rectangles.push([bottomLeftTwips, bottomRightTwips, topLeftTwips, topRightTwips]);
 			}
 
-			var polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this);
-			var selection = new L.Polygon(polygons, {
-				pointerEvents: 'none',
-				fillColor: L.LOUtil.getViewIdHexColor(viewId),
-				fillOpacity: 0.25,
-				weight: 2,
-				opacity: 0.25});
-			this._viewSelections[viewId] = selection;
-			this._viewSelectionsGroup.addLayer(selection);
+			this._viewSelections[viewId].part = viewPart;
+			this._viewSelections[viewId].polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this);
+		} else {
+			this._viewSelections[viewId].polygons = null;
 		}
+
+		this._onUpdateTextViewSelection(viewId);
 	},
 
 	_onTextSelectionContentMsg: function (textMsg) {
@@ -1177,6 +1173,35 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
+	_onUpdateTextViewSelection: function (viewId) {
+		viewId = parseInt(viewId);
+		var viewPolygons = this._viewSelections[viewId].polygons;
+		var viewSelection = this._viewSelections[viewId].selection;
+		var viewPart = this._viewSelections[viewId].part;
+
+		if (viewPolygons &&
+		    (this._docType === 'text' || this._selectedPart === viewPart)) {
+
+			// Reset previous selections
+			if (viewSelection) {
+				this._viewSelectionsGroup.removeLayer(viewSelection);
+			}
+
+			viewSelection = new L.Polygon(viewPolygons, {
+				pointerEvents: 'none',
+				fillColor: L.LOUtil.getViewIdHexColor(viewId),
+				fillOpacity: 0.25,
+				weight: 2,
+				opacity: 0.25
+			});
+			this._viewSelections[viewId].selection = viewSelection;
+			this._viewSelectionsGroup.addLayer(viewSelection);
+		}
+		else if (viewSelection) {
+			this._viewSelectionsGroup.removeLayer(viewSelection);
+		}
+	},
+
 	_onUpdateGraphicViewSelection: function (viewId) {
 		var viewBounds = this._graphicViewMarkers[viewId].bounds;
 		var viewMarker = this._graphicViewMarkers[viewId].marker;
commit 0b7c5a8f4cf8369dd3677f5ddea469667de6944c
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed Sep 14 20:07:48 2016 +0530

    loleaflet: Remove unused code
    
    Change-Id: Ieb12e03b68eaacb99acb9853d02191c165f5b70c
    (cherry picked from commit 4b18ca3772449d4f8ebd757b01a0efbfcb5c471a)

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index bcb2efe..030b4b4 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -884,7 +884,6 @@ L.TileLayer = L.GridLayer.extend({
 		this._selections.clearLayers();
 		if (strTwips != null) {
 			var rectangles = [];
-			var selectionCenter = new L.Point(0, 0);
 			for (var i = 0; i < strTwips.length; i += 4) {
 				var topLeftTwips = new L.Point(parseInt(strTwips[i]), parseInt(strTwips[i + 1]));
 				var offset = new L.Point(parseInt(strTwips[i + 2]), parseInt(strTwips[i + 3]));
@@ -892,8 +891,6 @@ L.TileLayer = L.GridLayer.extend({
 				var bottomLeftTwips = topLeftTwips.add(new L.Point(0, offset.y));
 				var bottomRightTwips = topLeftTwips.add(offset);
 				rectangles.push([bottomLeftTwips, bottomRightTwips, topLeftTwips, topRightTwips]);
-				selectionCenter = selectionCenter.add(topLeftTwips);
-				selectionCenter = selectionCenter.add(offset.divideBy(2));
 			}
 
 			var polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this);
@@ -929,7 +926,6 @@ L.TileLayer = L.GridLayer.extend({
 		}
 		if (strTwips != null) {
 			var rectangles = [];
-			var selectionCenter = new L.Point(0, 0);
 			for (var i = 0; i < strTwips.length; i += 4) {
 				var topLeftTwips = new L.Point(parseInt(strTwips[i]), parseInt(strTwips[i + 1]));
 				var offset = new L.Point(parseInt(strTwips[i + 2]), parseInt(strTwips[i + 3]));
@@ -937,8 +933,6 @@ L.TileLayer = L.GridLayer.extend({
 				var bottomLeftTwips = topLeftTwips.add(new L.Point(0, offset.y));
 				var bottomRightTwips = topLeftTwips.add(offset);
 				rectangles.push([bottomLeftTwips, bottomRightTwips, topLeftTwips, topRightTwips]);
-				selectionCenter = selectionCenter.add(topLeftTwips);
-				selectionCenter = selectionCenter.add(offset.divideBy(2));
 			}
 
 			var polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this);
commit 77a153adf861cf5c7f1405465c3438577bd5a716
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Sep 14 09:20:35 2016 +0200

    LOOLStress: unused using declaration
    
    Change-Id: If20c38a4575074e7ca30069115002aadd1a1847f
    (cherry picked from commit a652a013aac06a848c9c7b82671b43d7be5cbbc1)

diff --git a/loolwsd/LOOLStress.cpp b/loolwsd/LOOLStress.cpp
index 8290298..a296571 100644
--- a/loolwsd/LOOLStress.cpp
+++ b/loolwsd/LOOLStress.cpp
@@ -66,7 +66,6 @@ protected:
 
 using namespace LOOLProtocol;
 
-using Poco::Net::HTTPClientSession;
 using Poco::Net::HTTPRequest;
 using Poco::Net::HTTPResponse;
 using Poco::Runnable;
@@ -503,12 +502,12 @@ int Stress::main(const std::vector<std::string>& args)
         std::cout << "Tile best: " << renderingStats[0] << " microsecs, rendering 95th percentile: " << percentile(renderingStats, 95) << " microsecs." << std::endl;
         std::cout << "Cached best: " << cachedStats[0] << " microsecs, tile 95th percentile: " << percentile(cachedStats, 95) << " microsecs." << std::endl;
 
-        const auto renderingTime = std::accumulate(renderingStats.begin(), renderingStats.end(), 0);
+        const auto renderingTime = std::accumulate(renderingStats.begin(), renderingStats.end(), 0L);
         const double renderedPixels = 256 * 256 * renderingStats.size();
         const auto pixelsPerSecRendered = renderedPixels / renderingTime;
         std::cout << "Rendering power: " << pixelsPerSecRendered << " MPixels/sec." << std::endl;
 
-        const auto cacheTime = std::accumulate(cachedStats.begin(), cachedStats.end(), 0);
+        const auto cacheTime = std::accumulate(cachedStats.begin(), cachedStats.end(), 0L);
         const double cachePixels = 256 * 256 * cachedStats.size();
         const auto pixelsPerSecCached = cachePixels / cacheTime;
         std::cout << "Cache power: " << pixelsPerSecCached << " MPixels/sec." << std::endl;
commit 33225f2ae5c468f72496a3a3ee2d7e8776a4df00
Author: Henry Castro <hcastro at collabora.com>
Date:   Tue Sep 13 17:26:41 2016 -0400

    loleaflet: simplify iteration view
    
    (cherry picked from commit f85e6cfad9759ec6f4f013e2a0a5ea78f40278fb)

diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index f6c9a18..55e9dbb 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -31,9 +31,9 @@ L.Map.include({
 			docType: docLayer._docType
 		});
 		this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart);
-		docLayer._updateViewCursors();
-		docLayer._updateCellViewCursors();
-		docLayer._updateGraphicViewSelections();
+		docLayer.eachView(docLayer._viewCursors, docLayer._onUpdateViewCursor, docLayer);
+		docLayer.eachView(docLayer._cellViewCursors, docLayer._onUpdateCellViewCursor, docLayer);
+		docLayer.eachView(docLayer._graphicViewMarkers, docLayer._onUpdateGraphicViewSelection, docLayer);
 		docLayer._clearSelections();
 		docLayer._updateOnChangePart();
 		docLayer._pruneTiles();
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index e9594ae..bcb2efe 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1208,21 +1208,9 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
-	_updateViewCursors: function () {
-		for (var key in this._viewCursors) {
-			this._onUpdateViewCursor(key);
-		}
-	},
-
-	_updateCellViewCursors: function () {
-		for (var key in this._cellViewCursors) {
-			this._onUpdateCellViewCursor(key);
-		}
-	},
-
-	_updateGraphicViewSelections: function () {
-		for (var key in this._graphicViewMarkers) {
-			this._onUpdateGraphicViewSelection(key);
+	eachView: function (view, method, context) {
+		for (var key in view) {
+			method.call(context, key);
 		}
 	},
 
commit 4980bdb7a9cec1d803c73f40915b2a786c849dd1
Author: Henry Castro <hcastro at collabora.com>
Date:   Tue Sep 13 16:18:00 2016 -0400

    loleaflet: update graphic markers
    
    LayerGroup handle internal list
    Map handle internal list
    TileLayer handle internal list
    
    Reduce internal list
    
    (cherry picked from commit 663f47b858749688588cc399c16ecfe126618f50)

diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index 45fd68d..f6c9a18 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -33,6 +33,7 @@ L.Map.include({
 		this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart);
 		docLayer._updateViewCursors();
 		docLayer._updateCellViewCursors();
+		docLayer._updateGraphicViewSelections();
 		docLayer._clearSelections();
 		docLayer._updateOnChangePart();
 		docLayer._pruneTiles();
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 8b40da6..e9594ae 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -146,10 +146,6 @@ L.TileLayer = L.GridLayer.extend({
 		map.addLayer(this._viewSelectionsGroup);
 		this._viewSelections = {};
 
-		this._graphicViewMarkersGroup = new L.LayerGroup();
-		map.addLayer(this._graphicViewMarkersGroup);
-		this._graphicViewMarkers = {};
-
 		this._searchResultsLayer = new L.LayerGroup();
 		map.addLayer(this._searchResultsLayer);
 
@@ -500,23 +496,21 @@ L.TileLayer = L.GridLayer.extend({
 		}
 
 		var strTwips = obj.selection.match(/\d+/g);
-		if (this._graphicViewMarkers[viewId]) {
-			this._graphicViewMarkersGroup.removeLayer(this._graphicViewMarkers[viewId]);
-		}
+		this._graphicViewMarkers[viewId] = this._graphicViewMarkers[viewId] || {};
+		this._graphicViewMarkers[viewId].part = parseInt(obj.part);
 		if (strTwips != null) {
 			var topLeftTwips = new L.Point(parseInt(strTwips[0]), parseInt(strTwips[1]));
 			var offset = new L.Point(parseInt(strTwips[2]), parseInt(strTwips[3]));
 			var bottomRightTwips = topLeftTwips.add(offset);
-			var graphicSelection = new L.LatLngBounds(
+			this._graphicViewMarkers[viewId].bounds = new L.LatLngBounds(
 				this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
 				this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
-			this._graphicViewMarkers[viewId] = L.rectangle(graphicSelection, {
-				pointerEvents: 'none',
-				fill: false,
-				color: L.LOUtil.getViewIdHexColor(viewId)
-			});
-			this._graphicViewMarkersGroup.addLayer(this._graphicViewMarkers[viewId]);
 		}
+		else {
+			this._graphicViewMarkers[viewId].bounds = L.LatLngBounds.createDefault();
+		}
+
+		this._onUpdateGraphicViewSelection(viewId);
 	},
 
 	_onCellCursorMsg: function (textMsg) {
@@ -1153,7 +1147,7 @@ L.TileLayer = L.GridLayer.extend({
 	},
 
 	// Update colored non-blinking view cursor
-	_onUpdateViewCursor: function(viewId) {
+	_onUpdateViewCursor: function (viewId) {
 		if (typeof this._viewCursors[viewId] !== 'object' ||
 		    typeof this._viewCursors[viewId].bounds !== 'object') {
 			return;
@@ -1189,6 +1183,31 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
+	_onUpdateGraphicViewSelection: function (viewId) {
+		var viewBounds = this._graphicViewMarkers[viewId].bounds;
+		var viewMarker = this._graphicViewMarkers[viewId].marker;
+		var viewPart = this._graphicViewMarkers[viewId].part;
+
+		if (!this._isEmptyRectangle(viewBounds) &&
+		   (this._docType === 'text' || this._selectedPart === viewPart)) {
+			if (!viewMarker) {
+				viewMarker = L.rectangle(viewBounds, {
+					pointerEvents: 'none',
+					fill: false,
+					color: L.LOUtil.getViewIdHexColor(viewId)
+				});
+				this._graphicViewMarkers[viewId].marker = viewMarker;
+			}
+			else {
+				viewMarker.setBounds(viewBounds);
+			}
+			this._map.addLayer(viewMarker);
+		}
+		else if (viewMarker) {
+			this._map.removeLayer(viewMarker);
+		}
+	},
+
 	_updateViewCursors: function () {
 		for (var key in this._viewCursors) {
 			this._onUpdateViewCursor(key);
@@ -1201,6 +1220,12 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
+	_updateGraphicViewSelections: function () {
+		for (var key in this._graphicViewMarkers) {
+			this._onUpdateGraphicViewSelection(key);
+		}
+	},
+
 	// Update dragged graphics selection resize.
 	_onGraphicEdit: function (e) {
 		if (!e.handle) { return; }
commit 8c9053c457c647ce424522e464758d0c6e13a23b
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Tue Sep 13 08:41:51 2016 +0200

    MessageQueue: avoid allocation of unnecessary temporary strings
    
    Change-Id: I9162407020d84d0c92c9d1c6dfab806c67f3b873
    (cherry picked from commit 74a29c4ee4d4a232e1f2fa8d5c6e6ae835eb05e0)

diff --git a/loolwsd/MessageQueue.cpp b/loolwsd/MessageQueue.cpp
index b4a9c20..027051e 100644
--- a/loolwsd/MessageQueue.cpp
+++ b/loolwsd/MessageQueue.cpp
@@ -120,7 +120,7 @@ void TileQueue::put_impl(const Payload& value)
                 Log::error(std::to_string(i) + ": " + oldMsg);
                 if (newMsg == oldMsg)
                 {
-                    Log::trace("Replacing duplicate tile: " + oldMsg + " -> " + newMsg);
+                    Log::trace() << "Replacing duplicate tile: " << oldMsg << " -> " << newMsg << Log::end;
                     _queue[i] = value;
                     return;
                 }
commit 82ac9f758bca72d9b65a6ea187eea47bb0fd312d
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Sep 12 13:08:29 2016 +0300

    Update to match core master
    
    Just two small comment and whitespace changes.
    
    (cherry picked from commit c56888159a12b8709dbf5d4e987eaac5a70c2eb0)

diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
index 6a8388d..53162a3 100644
--- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
@@ -244,7 +244,6 @@ struct _LibreOfficeKitDocumentClass
                            const int nTileWidth,
                            const int nTileHeight);
 
-
     /// @see lok::Document::setCallbackLatch().
     void (*setCallbackLatch) (LibreOfficeKitDocument* pThis,
                               bool bCallbackLatch);
diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
index 324318c..8aef838 100644
--- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -284,7 +284,7 @@ typedef enum
      * {
      *     "classification": "error" | "warning" | "info"
      *     "kind": "network" etc.
-     *     "code": 403 | 404 | ...
+     *     "code": a structured 32-bit error code, the ErrCode from LibreOffice's <tools/errcode.hxx>
      *     "message": freeform description
      * }
      */
commit 9b4394bc9164378cd795bc025f9e991b1d8864e5
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Mon Sep 12 08:15:33 2016 +0200

    LOOLKit: use std::move() in Document ctor
    
    Change-Id: I25aeb5285465385858180747f441a69b858c4fe6
    (cherry picked from commit 6006e2200151843efa983107e4039f9935e5906b)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 94d02ef..73ade5b 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -405,7 +405,7 @@ public:
         _jailId(jailId),
         _docKey(docKey),
         _url(url),
-        _queue(queue),
+        _queue(std::move(queue)),
         _ws(ws),
         _docPassword(""),
         _haveDocPassword(false),
commit 4e683ac577c6347913ff75abd3c9ce5b727641f7
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date:   Sat Sep 10 21:07:12 2016 +0200

    loolwsd: we use callbacks latch for not missing messages sent very early
    
    - lok::Document::setCallbackLatch: used on doument loading for
    set/unset the latch
    
    - now cell cursors of other views are correctly notified to the new
    view
    
    (cherry picked from commit ed983f17cbf2627ff7b1b1a04af03449bd18ff6b)

diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp
index 93a2312..9c3e3dc 100644
--- a/loolwsd/ChildSession.cpp
+++ b/loolwsd/ChildSession.cpp
@@ -356,6 +356,7 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, StringT
 
     // Inform this view of other views
     _docManager.notifyCurrentViewOfOtherViews(getId());
+    _loKitDocument->setCallbackLatch(false);
 
     Log::info("Loaded session " + getId());
     return true;
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 94c0846..94d02ef 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -1139,6 +1139,7 @@ private:
             _viewIdToCallbackDescr.emplace(viewId,
                                            std::unique_ptr<CallbackDescriptor>(new CallbackDescriptor({ this, viewId })));
             _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get());
+            _loKitDocument->setCallbackLatch(true);
 
             Log::info() << "Document [" << _url << "] view ["
                         << viewId << "] loaded, leaving "
diff --git a/loolwsd/LibreOfficeKit.hpp b/loolwsd/LibreOfficeKit.hpp
index 5881c77..d718edd 100644
--- a/loolwsd/LibreOfficeKit.hpp
+++ b/loolwsd/LibreOfficeKit.hpp
@@ -464,6 +464,17 @@ public:
                                             nTileWidth, nTileHeight);
     }
 
+    /**
+     * Enable/disable callbacks latch. LOK will set the latch when it wants to
+     * queue new callbacks but not flush them.
+     *
+     * @param bCallbackLatch: true enables the latch, false disables it.
+     */
+    inline void setCallbackLatch(bool bCallbackLatch)
+    {
+        _pDoc->pClass->setCallbackLatch(_pDoc, bCallbackLatch);
+    }
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
index 81d65c1..6a8388d 100644
--- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
@@ -244,6 +244,11 @@ struct _LibreOfficeKitDocumentClass
                            const int nTileWidth,
                            const int nTileHeight);
 
+
+    /// @see lok::Document::setCallbackLatch().
+    void (*setCallbackLatch) (LibreOfficeKitDocument* pThis,
+                              bool bCallbackLatch);
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
commit 5569a64aac8cb18e910a45b1f1f353fcac44f620
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date:   Sat Sep 10 20:51:59 2016 +0200

    Revert "loolwsd: we use callbacks latch for not missing messages sent very early"
    
    This reverts commit f2106157f31801d96ac8fdf5e74956455ebbf778.
    
    (cherry picked from commit edc1302f16d707c14995f4342353e35284934091)

diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp
index 93c4cf6..93a2312 100644
--- a/loolwsd/ChildSession.cpp
+++ b/loolwsd/ChildSession.cpp
@@ -357,8 +357,6 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, StringT
     // Inform this view of other views
     _docManager.notifyCurrentViewOfOtherViews(getId());
 
-    _loKitDocument->setCallbackLatch(false);
-
     Log::info("Loaded session " + getId());
     return true;
 }
diff --git a/loolwsd/LOKitClient.cpp b/loolwsd/LOKitClient.cpp
index 13a180e..b882b79 100644
--- a/loolwsd/LOKitClient.cpp
+++ b/loolwsd/LOKitClient.cpp
@@ -112,7 +112,7 @@ protected:
             return Application::EXIT_UNAVAILABLE;
         }
 
-        loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr, /*callback latch*/ false);
+        loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr);
 
         loKitDocument->pClass->initializeForRendering(loKitDocument, nullptr);
 
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 261821b..94c0846 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -1138,7 +1138,7 @@ private:
             viewId = _loKitDocument->getView();
             _viewIdToCallbackDescr.emplace(viewId,
                                            std::unique_ptr<CallbackDescriptor>(new CallbackDescriptor({ this, viewId })));
-            _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get(), /*callback latch*/ true);
+            _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get());
 
             Log::info() << "Document [" << _url << "] view ["
                         << viewId << "] loaded, leaving "
diff --git a/loolwsd/LibreOfficeKit.hpp b/loolwsd/LibreOfficeKit.hpp
index 700db4b..5881c77 100644
--- a/loolwsd/LibreOfficeKit.hpp
+++ b/loolwsd/LibreOfficeKit.hpp
@@ -214,27 +214,15 @@ public:
     }
 
     /**
-     * Enable/disable callbacks latch. LOK will set the latch when it wants to
-     * queue new callbacks but not flush them.
-     *
-     * @param bCallbackLatch: true enables the latch, false disables it.
-     */
-    inline void setCallbackLatch(bool bCallbackLatch)
-    {
-        _pDoc->pClass->setCallbackLatch(_pDoc, bCallbackLatch);
-    }
-
-    /**
      * Registers a callback. LOK will invoke this function when it wants to
      * inform the client about events.
      *
      * @param pCallback the callback to invoke
      * @param pData the user data, will be passed to the callback on invocation
-     * @param bCallbackLatch the event latch status to be set before the callback is registered
      */
-    inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData, bool bCallbackLatch = false)
+    inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData)
     {
-        _pDoc->pClass->registerCallback(_pDoc, pCallback, pData, bCallbackLatch);
+        _pDoc->pClass->registerCallback(_pDoc, pCallback, pData);
     }
 
     /**
diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
index e03163f..81d65c1 100644
--- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
@@ -147,15 +147,10 @@ struct _LibreOfficeKitDocumentClass
     void (*initializeForRendering) (LibreOfficeKitDocument* pThis,
                                     const char* pArguments);
 
-    /// @see lok::Document::setCallbackLatch().
-    void (*setCallbackLatch) (LibreOfficeKitDocument* pThis,
-                              bool bCallbackLatch);
-
     /// @see lok::Document::registerCallback().
     void (*registerCallback) (LibreOfficeKitDocument* pThis,
                               LibreOfficeKitCallback pCallback,
-                              void* pData,
-                              bool bCallbackLatch);
+                              void* pData);
 
     /// @see lok::Document::postKeyEvent
     void (*postKeyEvent) (LibreOfficeKitDocument* pThis,
commit 0a815d0a4b5607b5082aa89b62b5ad7f0cd1149e
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date:   Fri Sep 9 22:23:51 2016 +0200

    loolwsd: we use callbacks latch for not missing messages sent very early
    
    - lok::Document::setCallbackLatch: used by a child session for
    set/unset the latch
    
    - lok::Document::registerCallback has a new boolean parameter used for
    setting the latch state just before the callback is actually
    registered for a (new) view
    
    - now cell cursors of other views are correctly notified to the new
    view
    
    (cherry picked from commit f2106157f31801d96ac8fdf5e74956455ebbf778)

diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp
index 93a2312..93c4cf6 100644
--- a/loolwsd/ChildSession.cpp
+++ b/loolwsd/ChildSession.cpp
@@ -357,6 +357,8 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, StringT
     // Inform this view of other views
     _docManager.notifyCurrentViewOfOtherViews(getId());
 
+    _loKitDocument->setCallbackLatch(false);
+
     Log::info("Loaded session " + getId());
     return true;
 }
diff --git a/loolwsd/LOKitClient.cpp b/loolwsd/LOKitClient.cpp
index b882b79..13a180e 100644
--- a/loolwsd/LOKitClient.cpp
+++ b/loolwsd/LOKitClient.cpp
@@ -112,7 +112,7 @@ protected:
             return Application::EXIT_UNAVAILABLE;
         }
 
-        loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr);
+        loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr, /*callback latch*/ false);
 
         loKitDocument->pClass->initializeForRendering(loKitDocument, nullptr);
 
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 94c0846..261821b 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -1138,7 +1138,7 @@ private:
             viewId = _loKitDocument->getView();
             _viewIdToCallbackDescr.emplace(viewId,
                                            std::unique_ptr<CallbackDescriptor>(new CallbackDescriptor({ this, viewId })));
-            _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get());
+            _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get(), /*callback latch*/ true);
 
             Log::info() << "Document [" << _url << "] view ["
                         << viewId << "] loaded, leaving "
diff --git a/loolwsd/LibreOfficeKit.hpp b/loolwsd/LibreOfficeKit.hpp
index 5881c77..700db4b 100644
--- a/loolwsd/LibreOfficeKit.hpp
+++ b/loolwsd/LibreOfficeKit.hpp
@@ -214,15 +214,27 @@ public:
     }
 
     /**
+     * Enable/disable callbacks latch. LOK will set the latch when it wants to
+     * queue new callbacks but not flush them.
+     *
+     * @param bCallbackLatch: true enables the latch, false disables it.
+     */
+    inline void setCallbackLatch(bool bCallbackLatch)
+    {
+        _pDoc->pClass->setCallbackLatch(_pDoc, bCallbackLatch);
+    }
+
+    /**
      * Registers a callback. LOK will invoke this function when it wants to
      * inform the client about events.
      *
      * @param pCallback the callback to invoke
      * @param pData the user data, will be passed to the callback on invocation
+     * @param bCallbackLatch the event latch status to be set before the callback is registered
      */
-    inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData)
+    inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData, bool bCallbackLatch = false)
     {
-        _pDoc->pClass->registerCallback(_pDoc, pCallback, pData);
+        _pDoc->pClass->registerCallback(_pDoc, pCallback, pData, bCallbackLatch);
     }
 
     /**
diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
index 81d65c1..e03163f 100644
--- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h
@@ -147,10 +147,15 @@ struct _LibreOfficeKitDocumentClass
     void (*initializeForRendering) (LibreOfficeKitDocument* pThis,
                                     const char* pArguments);
 
+    /// @see lok::Document::setCallbackLatch().
+    void (*setCallbackLatch) (LibreOfficeKitDocument* pThis,
+                              bool bCallbackLatch);
+
     /// @see lok::Document::registerCallback().
     void (*registerCallback) (LibreOfficeKitDocument* pThis,
                               LibreOfficeKitCallback pCallback,
-                              void* pData);
+                              void* pData,
+                              bool bCallbackLatch);
 
     /// @see lok::Document::postKeyEvent
     void (*postKeyEvent) (LibreOfficeKitDocument* pThis,
commit 8321ba0369fccd99c43acd4cfbe3ae083c82fc4a
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 8 16:48:56 2016 -0400

    loleaflet: avoid undefined variables when map not loaded
    
    (cherry picked from commit 46a314206b720858127e254d2ae499e12fa53b60)

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 1d01ff7..e2204b4 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -790,6 +790,8 @@ L.Map = L.Evented.extend({
 	},
 
 	_onLostFocus: function () {
+		if (!this._loaded) { return; }
+
 		var doclayer = this._docLayer;
 		if (doclayer._isCursorVisible && doclayer._isCursorOverlayVisible) {
 			doclayer._visibleCursorOnLostFocus = doclayer._visibleCursor;
@@ -802,6 +804,8 @@ L.Map = L.Evented.extend({
 	},
 
 	_onGotFocus: function () {
+		if (!this._loaded) { return; }
+
 		var doclayer = this._docLayer;
 		if (doclayer._isCursorVisibleOnLostFocus && doclayer._isCursorOverlayVisibleOnLostFocus) {
 			// we restore the old cursor position by a small delay, so that if the user clicks
commit 1ca70704f8f4fbb0bb54ebbfeaf3dd308a58f7a8
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 8 12:16:51 2016 -0400

    loolwsd: test: cell view cursor
    
    (cherry picked from commit e9f3fd4e560089aa5402b4b12b48a7483185893a)

diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index 79bb226..a6fe190 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -91,6 +91,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testOptimalResize);
     CPPUNIT_TEST(testInvalidateViewCursor);
     CPPUNIT_TEST(testViewCursorVisible);
+    CPPUNIT_TEST(testCellViewCursor);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -130,6 +131,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     void testOptimalResize();
     void testInvalidateViewCursor();
     void testViewCursorVisible();
+    void testCellViewCursor();
 
     void loadDoc(const std::string& documentURL);
 
@@ -2223,6 +2225,66 @@ void HTTPWSTest::testViewCursorVisible()
     }
 }
 
+void HTTPWSTest::testCellViewCursor()
+{
+    try
+    {
+        int docSheet = -1;
+        int docSheets = 0;
+        int docHeight = 0;
+        int docWidth = 0;
+        int docViewId = -1;
+        int itView = 0;
+
+        // 0..N Views
+        std::vector<std::shared_ptr<Poco::Net::WebSocket>> views;
+
+        // Load a document
+        std::string documentPath, documentURL, response, text;
+        getDocumentPathAndURL("empty.ods", documentPath, documentURL);
+
+        Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL);
+        Poco::Net::WebSocket socket1 = *connectLOKit(_uri, request, _response);
+
+        sendTextFrame(socket1, "load url=" + documentURL);
+        CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket1));
+
+        // Check document size
+        sendTextFrame(socket1, "status");
+        getResponseMessage(socket1, "status:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a status: message as expected", !response.empty());
+        parseDocSize(response, "spreadsheet", docSheet, docSheets, docWidth, docHeight, docViewId);
+
+        // Click to show a cell cursor
+        Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttondown"), docWidth/10, docHeight/10);
+        sendTextFrame(socket1, text); text.clear();
+        Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttonup"), docWidth/10, docHeight/10);
+        sendTextFrame(socket1, text);
+        getResponseMessage(socket1, "cellcursor:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a cellcursor: message as expected", !response.empty());
+
+        // Connect 0..N Views
+        for (itView = 0; itView < 10; ++itView)
+        {
+            views.emplace_back(connectLOKit(_uri, request, _response));
+        }
+
+        // Load 0..N view and expect to receive cellviewcursor
+        for (auto socketView : views)
+        {
+            sendTextFrame(*socketView, "load url=" + documentURL);
+            CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(*socketView, "", true));
+
+            // Expected to receive cellviewcursor second view
+            getResponseMessage(*socketView, "cellviewcursor:", response, false);
+            CPPUNIT_ASSERT_MESSAGE("did not receive a cellviewcursor: message as expected", !response.empty());
+        }
+    }
+    catch (const Poco::Exception& exc)
+    {
+        CPPUNIT_FAIL(exc.displayText());
+    }
+}
 
 CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSTest);
 
commit c5a59d279da33ba7dfc689fed7b82f6fa6bf8052
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 8 12:11:07 2016 -0400

    loleaflet: fix part not taken into account for cell cursors
    
    (cherry picked from commit 878fff5ebb78b58bbcb5c49516eed8f7215111e5)

diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index db63f95..45fd68d 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -32,6 +32,7 @@ L.Map.include({
 		});
 		this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart);
 		docLayer._updateViewCursors();
+		docLayer._updateCellViewCursors();
 		docLayer._clearSelections();
 		docLayer._updateOnChangePart();
 		docLayer._pruneTiles();
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 6fb3c1b..8b40da6 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -653,6 +653,7 @@ L.TileLayer = L.GridLayer.extend({
 				this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
 		}
 
+		this._cellViewCursors[viewId].part = parseInt(obj.part);
 		this._onUpdateCellViewCursor(viewId);
 	},
 
@@ -661,23 +662,19 @@ L.TileLayer = L.GridLayer.extend({
 			return;
 
 		var cellViewCursorsMarker = this._cellViewCursors[viewId].marker;
-		if (!this._isEmptyRectangle(this._cellViewCursors[viewId].bounds)) {
+		var viewPart = this._cellViewCursors[viewId].part;
 
-			if (cellViewCursorsMarker) {
-				this._map.removeLayer(cellViewCursorsMarker);
-			}
-			cellViewCursorsMarker = L.rectangle(this._cellViewCursors[viewId].bounds, {fill: false, color: L.LOUtil.getViewIdHexColor(viewId), weight: 2});
+		if (!this._isEmptyRectangle(this._cellViewCursors[viewId].bounds) && this._selectedPart === viewPart) {
 			if (!cellViewCursorsMarker) {
-				this._map.fire('error', {msg: 'Cell View Cursor marker initialization', cmd: 'cellViewCursor', kind: 'failed', id: 1});
-				return;
+				cellViewCursorsMarker = L.rectangle(this._cellViewCursors[viewId].bounds, {fill: false, color: L.LOUtil.getViewIdHexColor(viewId), weight: 2});
+				this._cellViewCursors[viewId].marker = cellViewCursorsMarker;
 			}
+			cellViewCursorsMarker.setBounds(this._cellViewCursors[viewId].bounds);
 			this._map.addLayer(cellViewCursorsMarker);
 		}
 		else if (cellViewCursorsMarker) {
 			this._map.removeLayer(cellViewCursorsMarker);
 		}
-
-		this._cellViewCursors[viewId].marker = cellViewCursorsMarker;
 	},
 
 	_onViewCursorVisibleMsg: function(textMsg) {
@@ -1198,6 +1195,12 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
+	_updateCellViewCursors: function () {
+		for (var key in this._cellViewCursors) {
+			this._onUpdateCellViewCursor(key);
+		}
+	},
+
 	// Update dragged graphics selection resize.
 	_onGraphicEdit: function (e) {
 		if (!e.handle) { return; }
commit d1af32048d9d3875c14030bb63fddc42320948cd
Author: Henry Castro <hcastro at collabora.com>
Date:   Thu Sep 8 10:26:25 2016 -0400

    loolwsd: test: view cursor visible
    
    (cherry picked from commit f751d36c8f3f84948acd77e5c14b18597578f0fe)

diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index 4af1fea..79bb226 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -90,6 +90,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testColumnRowResize);
     CPPUNIT_TEST(testOptimalResize);
     CPPUNIT_TEST(testInvalidateViewCursor);
+    CPPUNIT_TEST(testViewCursorVisible);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -128,6 +129,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     void testColumnRowResize();
     void testOptimalResize();
     void testInvalidateViewCursor();
+    void testViewCursorVisible();
 
     void loadDoc(const std::string& documentURL);
 
@@ -2172,6 +2174,56 @@ void HTTPWSTest::testInvalidateViewCursor()
     }
 }
 
+void HTTPWSTest::testViewCursorVisible()
+{
+    try
+    {
+        int docSlide = -1;
+        int docSlides = 0;
+        int docHeight = 0;
+        int docWidth = 0;
+        int docViewId = -1;
+
+        // Load a document
+        std::string documentPath, documentURL, response, text;
+        getDocumentPathAndURL("viewcursor.odp", documentPath, documentURL);
+
+        Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL);
+        Poco::Net::WebSocket socket1 = *connectLOKit(_uri, request, _response);
+
+        sendTextFrame(socket1, "load url=" + documentURL);
+        CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket1));
+
+        // Check document size
+        sendTextFrame(socket1, "status");
+        getResponseMessage(socket1, "status:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a status: message as expected", !response.empty());
+        parseDocSize(response, "presentation", docSlide, docSlides, docWidth, docHeight, docViewId);
+
+        // Click to show a cursor
+        Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttondown"), docWidth/2, docHeight/6);
+        sendTextFrame(socket1, text); text.clear();
+        Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttonup"), docWidth/2, docHeight/6);
+        sendTextFrame(socket1, text);
+        getResponseMessage(socket1, "cursorvisible:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a cursorvisible: message as expected", !response.empty());
+
+        // Connect and load second view.
+        Poco::Net::WebSocket socket2 = *connectLOKit(_uri, request, _response);
+        sendTextFrame(socket2, "load url=" + documentURL);
+        CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket2, "", true));
+
+        // Expected to receive viewcursorvisible second view
+        getResponseMessage(socket2, "viewcursorvisible:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a viewcursorvisible: message as expected", !response.empty());
+    }
+    catch (const Poco::Exception& exc)
+    {
+        CPPUNIT_FAIL(exc.displayText());
+    }
+}
+
+
 CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSTest);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit a84d16ece323de3423ebf69ec0b0424c8dc185b9
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed Sep 7 21:46:41 2016 +0200

    loleaflet: Implement object/image multiple view selections
    
    Change-Id: Ifc2d6f95fa48a947fc043bf48d582ab549fa6172
    (cherry picked from commit d2bf039d59d0e4c190535453b2ed8b8faf9b9abd)

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 9f70ac2..6fb3c1b 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -102,6 +102,9 @@ L.TileLayer = L.GridLayer.extend({
 		// View cell cursors with viewId to 'cursor info' mapping.
 		this._cellViewCursors = {};
 
+		// Graphic view selection rectangles
+		this._graphicViewMarkers = {};
+
 		this._lastValidPart = -1;
 		// Cursor marker
 		this._cursorMarker = null;
@@ -143,6 +146,10 @@ L.TileLayer = L.GridLayer.extend({
 		map.addLayer(this._viewSelectionsGroup);
 		this._viewSelections = {};
 
+		this._graphicViewMarkersGroup = new L.LayerGroup();
+		map.addLayer(this._graphicViewMarkersGroup);
+		this._graphicViewMarkers = {};
+
 		this._searchResultsLayer = new L.LayerGroup();
 		map.addLayer(this._searchResultsLayer);
 
@@ -378,6 +385,9 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('textviewselection:')) {
 			this._onTextViewSelectionMsg(textMsg);
 		}
+		else if (textMsg.startsWith('graphicviewselection:')) {
+			this._onGraphicViewSelectionMsg(textMsg);
+		}
 	},
 
 	_onCommandValuesMsg: function (textMsg) {
@@ -479,6 +489,36 @@ L.TileLayer = L.GridLayer.extend({
 		this._onUpdateGraphicSelection();
 	},
 
+	_onGraphicViewSelectionMsg: function (textMsg) {
+		textMsg = textMsg.substring('graphicviewselection:'.length + 1);
+		var obj = JSON.parse(textMsg);
+		var viewId = parseInt(obj.viewId);
+
+		// Ignore yourself
+		if (viewId === this._viewId) {
+			return;
+		}
+
+		var strTwips = obj.selection.match(/\d+/g);
+		if (this._graphicViewMarkers[viewId]) {
+			this._graphicViewMarkersGroup.removeLayer(this._graphicViewMarkers[viewId]);
+		}
+		if (strTwips != null) {
+			var topLeftTwips = new L.Point(parseInt(strTwips[0]), parseInt(strTwips[1]));
+			var offset = new L.Point(parseInt(strTwips[2]), parseInt(strTwips[3]));
+			var bottomRightTwips = topLeftTwips.add(offset);
+			var graphicSelection = new L.LatLngBounds(
+				this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
+				this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
+			this._graphicViewMarkers[viewId] = L.rectangle(graphicSelection, {
+				pointerEvents: 'none',
+				fill: false,
+				color: L.LOUtil.getViewIdHexColor(viewId)
+			});
+			this._graphicViewMarkersGroup.addLayer(this._graphicViewMarkers[viewId]);
+		}
+	},
+
 	_onCellCursorMsg: function (textMsg) {
 		if (!this._cellCursor) {
 			this._cellCursor = L.LatLngBounds.createDefault();
commit fee1819b86b0f8ec7aee6e69506f37bd17c6a422
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed Sep 7 16:52:18 2016 +0200

    Fix comment
    
    Change-Id: Ib43bcc7378590e71b98856c11218fd99ad90d32d
    (cherry picked from commit 33a5c4bc7083154aa8fa93fd663d1e38bc8269e5)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index e3519fb..94c0846 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -936,7 +936,7 @@ private:
         }
     }
 
-    /// Notify all currently active sessions of session with given 'sessionId'
+    /// Notify all currently active sessions about session with given 'sessionId'
     void notifyOtherSessions(const std::string& sessionId, const std::string& message) const override
     {
         std::unique_lock<std::mutex> lock(_mutex);
@@ -954,7 +954,7 @@ private:
         }
     }
 
-    /// Notify session (with given 'sessionId') of other currently active views
+    /// Notify session (with given 'sessionId'), if active, of other existing views
     void notifyCurrentViewOfOtherViews(const std::string& sessionId) const override
     {
         std::unique_lock<std::mutex> lock(_mutex);
commit 5a5f93dc10d34e03f83653a042bc44de805e3d23
Author: Henry Castro <hcastro at collabora.com>
Date:   Wed Sep 7 12:21:34 2016 -0400

    loolwsd: test: invalidate view cursor
    
    (cherry picked from commit d4d764cee580c30ab5cc776118ea6008ca40c7f2)

diff --git a/loolwsd/test/data/viewcursor.odp b/loolwsd/test/data/viewcursor.odp
new file mode 100755
index 0000000..a6a3561
Binary files /dev/null and b/loolwsd/test/data/viewcursor.odp differ
diff --git a/loolwsd/test/helpers.hpp b/loolwsd/test/helpers.hpp
index 0a7d003..2401243 100644
--- a/loolwsd/test/helpers.hpp
+++ b/loolwsd/test/helpers.hpp
@@ -448,7 +448,6 @@ void parseDocSize(const std::string& message, const std::string& type,
                   int& part, int& parts, int& width, int& height, int& viewid)
 {
     Poco::StringTokenizer tokens(message, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
-    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(6), tokens.count());
 
     // Expected format is something like 'type= parts= current= width= height='.
     const std::string text = tokens[0].substr(std::string("type=").size());
diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index f28c5d1..4af1fea 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -89,6 +89,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     CPPUNIT_TEST(testStateUnoCommand);
     CPPUNIT_TEST(testColumnRowResize);
     CPPUNIT_TEST(testOptimalResize);
+    CPPUNIT_TEST(testInvalidateViewCursor);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -126,6 +127,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture
     void testStateUnoCommand();
     void testColumnRowResize();
     void testOptimalResize();
+    void testInvalidateViewCursor();
 
     void loadDoc(const std::string& documentURL);
 
@@ -2121,6 +2123,55 @@ void HTTPWSTest::testOptimalResize()
     }
 }
 
+void HTTPWSTest::testInvalidateViewCursor()
+{
+    try
+    {
+        int docSlide = -1;
+        int docSlides = 0;
+        int docHeight = 0;
+        int docWidth = 0;
+        int docViewId = -1;
+
+        // Load a document
+        std::string documentPath, documentURL, response, text;
+        getDocumentPathAndURL("viewcursor.odp", documentPath, documentURL);
+
+        Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL);
+        Poco::Net::WebSocket socket1 = *connectLOKit(_uri, request, _response);
+
+        sendTextFrame(socket1, "load url=" + documentURL);
+        CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket1));
+
+        // Check document size
+        sendTextFrame(socket1, "status");
+        getResponseMessage(socket1, "status:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a status: message as expected", !response.empty());
+        parseDocSize(response, "presentation", docSlide, docSlides, docWidth, docHeight, docViewId);
+
+        // Click to show a cursor
+        Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttondown"), docWidth/2, docHeight/6);
+        sendTextFrame(socket1, text); text.clear();
+        Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttonup"), docWidth/2, docHeight/6);
+        sendTextFrame(socket1, text);
+        getResponseMessage(socket1, "invalidatecursor:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a invalidatecursor: message as expected", !response.empty());
+
+        // Connect and load second view.
+        Poco::Net::WebSocket socket2 = *connectLOKit(_uri, request, _response);
+        sendTextFrame(socket2, "load url=" + documentURL);
+        CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket2, "", true));
+
+        // Expected to receive invalidateviewcursor second view
+        getResponseMessage(socket2, "invalidateviewcursor:", response, false);
+        CPPUNIT_ASSERT_MESSAGE("did not receive a invalidateviewcursor: message as expected", !response.empty());
+    }
+    catch (const Poco::Exception& exc)
+    {
+        CPPUNIT_FAIL(exc.displayText());
+    }
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSTest);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 8d079bc009d1b7f0aaaadf41977859da6be7706a
Author: Henry Castro <hcastro at collabora.com>
Date:   Wed Sep 7 12:16:23 2016 -0400

    loleaflet: fix part not taken into account for cursors
    
    Also refactor cursor marker
    
    (cherry picked from commit a666c01c03ac3bd680239e22b84ba4fa2f6971e6)

diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index cf90a14..db63f95 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -31,6 +31,7 @@ L.Map.include({
 			docType: docLayer._docType
 		});
 		this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart);
+		docLayer._updateViewCursors();
 		docLayer._clearSelections();
 		docLayer._updateOnChangePart();
 		docLayer._pruneTiles();
diff --git a/loleaflet/src/layer/marker/Cursor.js b/loleaflet/src/layer/marker/Cursor.js
index cebc657..7fade22 100644
--- a/loleaflet/src/layer/marker/Cursor.js
+++ b/loleaflet/src/layer/marker/Cursor.js
@@ -8,18 +8,25 @@ L.Cursor = L.Layer.extend({
 		opacity: 1
 	},
 
-	initialize: function (latlng, options) {
+	initialize: function (latlng, size, options) {
 		L.setOptions(this, options);
 		this._latlng = L.latLng(latlng);
+		this._size = L.point(size);
 	},
 
 	onAdd: function () {
-		this._initLayout();
+		if (!this._container) {
+			this._initLayout();
+		}
+
 		this.update();
+		this.getPane().appendChild(this._container);
 	},
 
 	onRemove: function () {
-		L.DomUtil.remove(this._container);
+		if (this._container) {
+			this.getPane().removeChild(this._container);
+		}
 	},
 
 	getEvents: function () {
@@ -32,16 +39,18 @@ L.Cursor = L.Layer.extend({
 		return this._latlng;
 	},
 
-	setLatLng: function (latlng) {
+	setLatLng: function (latlng, size) {
 		var oldLatLng = this._latlng;
 		this._latlng = L.latLng(latlng);
+		this._size = L.point(size);
 		this.update();
 		return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 	},
 
 	update: function () {
-		if (this._container) {
+		if (this._container && this._map) {
 			var pos = this._map.latLngToLayerPoint(this._latlng).round();
+			this._setSize();
 			this._setPos(pos);
 		}
 		return this;
@@ -71,10 +80,6 @@ L.Cursor = L.Layer.extend({
 		L.DomEvent
 			.disableClickPropagation(this._cursor)
 			.disableScrollPropagation(this._container);
-
-		if (this._container) {
-			this.getPane().appendChild(this._container);
-		}
 	},
 
 	_setPos: function (pos) {
@@ -97,14 +102,14 @@ L.Cursor = L.Layer.extend({
 		L.DomUtil.setOpacity(this._container, opacity);
 	},
 
-	setSize: function (size) {
-		this._cursor.style.height = size.y + 'px';
-		this._container.style.top = '-' + (this._container.clientHeight - size.y - 2) / 2 + 'px';
+	_setSize: function () {
+		this._cursor.style.height = this._size.y + 'px';
+		this._container.style.top = '-' + (this._container.clientHeight - this._size.y - 2) / 2 + 'px';
 	}
 });
 
-L.cursor = function (latlng, options) {
-	return new L.Cursor(latlng, options);
+L.cursor = function (latlng, size, options) {
+	return new L.Cursor(latlng, size, options);
 };
 
 L.Cursor.getCursorURL = function (localPath) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 388582f..9f70ac2 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -576,8 +576,12 @@ L.TileLayer = L.GridLayer.extend({
 		this._viewCursors[viewId].bounds = new L.LatLngBounds(
 			this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
 			this._twipsToLatLng(bottomRightTwips, this._map.getZoom())),
-		this._viewCursors[viewId].part = obj.part;
-		this._viewCursors[viewId].visible = true;
+		this._viewCursors[viewId].part = parseInt(obj.part);
+
+		// FIXME. Server not sending view visible cursor
+		if (typeof this._viewCursors[viewId].visible === 'undefined') {
+			this._viewCursors[viewId].visible = true;
+		}
 
 		this._onUpdateViewCursor(viewId);
 	},
@@ -1098,14 +1102,12 @@ L.TileLayer = L.GridLayer.extend({
 
 		if (this._map._permission === 'edit' && this._isCursorVisible && this._isCursorOverlayVisible
 				&& !this._isEmptyRectangle(this._visibleCursor)) {
-			if (this._cursorMarker) {
-				this._map.removeLayer(this._cursorMarker);
+			if (!this._cursorMarker) {
+				this._cursorMarker = L.cursor(null, null, {blink: true});
 			}
 
-			this._cursorMarker = L.cursor(cursorPos, {blink: true});
+			this._cursorMarker.setLatLng(cursorPos, pixBounds.getSize().multiplyBy(this._map.getZoomScale(this._map.getZoom())));
 			this._map.addLayer(this._cursorMarker);
-			this._cursorMarker.setSize(pixBounds.getSize().multiplyBy(
-						this._map.getZoomScale(this._map.getZoom())));
 		}
 		else if (this._cursorMarker) {
 			this._map.removeLayer(this._cursorMarker);
@@ -1125,27 +1127,35 @@ L.TileLayer = L.GridLayer.extend({
 		var viewCursorPos = this._viewCursors[viewId].bounds.getNorthWest();
 		var viewCursorMarker = this._viewCursors[viewId].marker;
 		var viewCursorVisible = this._viewCursors[viewId].visible;
-
-		if (viewCursorVisible && !this._isEmptyRectangle(this._viewCursors[viewId].bounds)) {
-			if (viewCursorMarker) {
-				this._map.removeLayer(viewCursorMarker);
+		var viewPart = this._viewCursors[viewId].part;
+
+		if (viewCursorVisible && !this._isEmptyRectangle(this._viewCursors[viewId].bounds) &&
+		   (this._docType === 'text' || this._selectedPart === viewPart)) {
+			if (!viewCursorMarker) {
+				var viewCursorOptions = {
+					color: L.LOUtil.getViewIdHexColor(viewId),
+					blink: false,
+					header: true, // we want a 'hat' to our view cursors (which will contain view user names)
+					headerTimeout: 3000, // hide after some interval
+					headerName: this._map.getViewName(viewId)
+				};
+
+				viewCursorMarker = L.cursor(null, null, viewCursorOptions);
+				this._viewCursors[viewId].marker = viewCursorMarker;
 			}
-			var viewCursorOptions = {
-				color: L.LOUtil.getViewIdHexColor(viewId),
-				blink: false,
-				header: true, // we want a 'hat' to our view cursors (which will contain view user names)
-				headerTimeout: 3000, // hide after some interval
-				headerName: this._map.getViewName(viewId)
-			};
-			viewCursorMarker = L.cursor(viewCursorPos, viewCursorOptions);
-			this._map.addLayer(viewCursorMarker);
-			viewCursorMarker.setSize(pixBounds.getSize().multiplyBy(this._map.getZoomScale(this._map.getZoom())));
 
-		} else if (viewCursorMarker) {
-			this._map.removeLayer(this._viewCursors[viewId].marker);
+			viewCursorMarker.setLatLng(viewCursorPos, pixBounds.getSize().multiplyBy(this._map.getZoomScale(this._map.getZoom())));
+			this._map.addLayer(viewCursorMarker);
+		}
+		else if (viewCursorMarker) {
+			this._map.removeLayer(viewCursorMarker);
 		}
+	},
 
-		this._viewCursors[viewId].marker = viewCursorMarker;
+	_updateViewCursors: function () {
+		for (var key in this._viewCursors) {
+			this._onUpdateViewCursor(key);
+		}
 	},
 
 	// Update dragged graphics selection resize.
commit e3d55444f01a26fa95144059cbca7b076c903086
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed Sep 7 11:37:12 2016 +0200

    loolwsd: Comments
    
    Change-Id: Ia4e91f18d695ac2249fa1139afbcb5b3f65f87ba
    (cherry picked from commit 1b0088ab697881f2f54a23366febfe6e18c6bb25)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 3d52244..e3519fb 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -936,6 +936,7 @@ private:
         }
     }
 
+    /// Notify all currently active sessions of session with given 'sessionId'
     void notifyOtherSessions(const std::string& sessionId, const std::string& message) const override
     {
         std::unique_lock<std::mutex> lock(_mutex);
@@ -953,6 +954,7 @@ private:
         }
     }
 
+    /// Notify session (with given 'sessionId') of other currently active views
     void notifyCurrentViewOfOtherViews(const std::string& sessionId) const override
     {
         std::unique_lock<std::mutex> lock(_mutex);
commit 559bae76f11d8b12db6e2c8128aaf1646f9e638a
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Sat Sep 3 02:27:37 2016 +0530

    loolwsd: Code reuse, and don't send 'remview' message to inactive clients
    
    Use the existing function notifyOtherSessions() to broadcast the
    message.
    
    The function also takes care of sending this message only to
    active clients.
    
    Change-Id: I98d40429de17463167a67cb10ba59381a0cbc525
    (cherry picked from commit 3f421be4dab86fcfc2f48c7192cb9a2b79a74c32)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index bd9928f..3d52244 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -419,7 +419,6 @@ public:
         Log::info("Document ctor for url [" + _url + "] on child [" + _jailId +
                   "] LOK_VIEW_CALLBACK=" + std::to_string(_multiView) + ".");
         assert(_loKit && _loKit->get());
-
         _callbackThread.start(*this);
     }
 
@@ -910,22 +909,12 @@ private:
         Log::info("Unloading [" + sessionId + "].");
 
         // Broadcast the demise and removal of session.
-        {
-            std::unique_lock<std::mutex> lock(_mutex);
-
-            // We should be removed by this point, otherwise
-            // our closed connection will throw, if not segfault.
-            for (const auto& pair : _connections)
-            {
-                assert(sessionId != pair.second->getSessionId() && "Unloading connection still lingering.");
-                pair.second->getSession()->sendTextFrame("remview: " + std::to_string(session.getViewId()));
-            }
+        notifyOtherSessions(sessionId, "remview: " + std::to_string(session.getViewId()));
 
-            if (_loKitDocument == nullptr)
-            {
-                Log::error("Unloading session [" + sessionId + "] without loKitDocument.");
-                return;
-            }
+        if (_loKitDocument == nullptr)
+        {
+            Log::error("Unloading session [" + sessionId + "] without loKitDocument.");
+            return;
         }
 
         --_clientViews;
commit b1a0ef733b914cb5cd69f297812216232badfb07
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Sat Sep 3 01:51:04 2016 +0530

    loolwsd: Don't send addview notification to inactive clients
    
    Change-Id: I4771e4bf12057fdc7099e9433d3b7dc2198cb265
    (cherry picked from commit 9edb1c2994b9ab5f6d6d75e1e715deb43a0ae3c8)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index b62754d..bd9928f 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -956,7 +956,7 @@ private:
             if (it.second->isRunning() && it.second->getSessionId() != sessionId)
             {
                 auto session = it.second->getSession();
-                if (session)
+                if (session && session->isActive())
                 {
                     session->sendTextFrame(message);
                 }
@@ -976,6 +976,11 @@ private:
         }
 
         auto currentSession = it->second->getSession();
+        if (!currentSession->isActive())
+        {
+            return;
+        }
+
         for (auto& connectionIt: _connections)
         {
             if (connectionIt.second->isRunning() && connectionIt.second->getSessionId() != sessionId)
commit bf77497f4376837aee91f9e83734a2efe3d5c09a
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Sep 2 15:15:40 2016 +0300

    Revert "Mention --without-system-nss"
    
    That is the default anyway.
    
    This reverts commit 894b0a11b0ab6c6e0e428451cb03c0c9997f6ea6.
    
    (cherry picked from commit c4733fd4d2dccad9db630339fb906c96e8201d90)

diff --git a/loolwsd/README b/loolwsd/README
index eb5cb80..fdd648a 100644
--- a/loolwsd/README
+++ b/loolwsd/README
@@ -22,10 +22,6 @@ On Debian 8 (Linux x86_64) you can use in /etc/apt/sources.list:
 
     deb https://www.collaboraoffice.com/apt-poco/ /
 
-On Fedora 24 at least, you must run against a LibreOffice built with
---without-system-nss. Otherwise opening of password-protected
-documents will fail.
-
 Building
 --------
 
commit ecddb6d479aabdb3d1e5cdf96e29cc0fdb2be8e6
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Sep 2 15:08:46 2016 +0300

    Mention --without-system-nss
    
    (cherry picked from commit 894b0a11b0ab6c6e0e428451cb03c0c9997f6ea6)

diff --git a/loolwsd/README b/loolwsd/README
index fdd648a..eb5cb80 100644
--- a/loolwsd/README
+++ b/loolwsd/README
@@ -22,6 +22,10 @@ On Debian 8 (Linux x86_64) you can use in /etc/apt/sources.list:
 
     deb https://www.collaboraoffice.com/apt-poco/ /
 
+On Fedora 24 at least, you must run against a LibreOffice built with
+--without-system-nss. Otherwise opening of password-protected
+documents will fail.
+
 Building
 --------
 
commit 42521e013e8c95a0f457ff3427e86a1e358da2d2
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Fri Sep 2 07:29:59 2016 -0400

    loolwsd: don't combine tiles by row to allow for better culling
    
    Change-Id: I30c0a9caad51bd77360a97c9f5899742385ccc27
    Reviewed-on: https://gerrit.libreoffice.org/28615
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 99d0ee2ac111e7199626f6c17fb7ce723dac9126)

diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp
index 043218f..fe73d63 100644
--- a/loolwsd/DocumentBroker.cpp
+++ b/loolwsd/DocumentBroker.cpp
@@ -532,8 +532,7 @@ void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
     Log::trace() << "TileCombined request for " << tileCombined.serialize() << Log::end;
 
     // Satisfy as many tiles from the cache.
-    // The rest, group by rows.
-    std::map<int, std::vector<TileDesc>> rows;
+    std::vector<TileDesc> tiles;
     for (auto& tile : tileCombined.getTiles())
     {
         std::unique_ptr<std::fstream> cachedTile = _tileCache->lookupTile(tile);
@@ -587,34 +586,16 @@ void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
             }
         }
 
-        const auto tilePosY = tile.getTilePosY();
-        auto it = rows.lower_bound(tilePosY);
-        if (it != rows.end())
-        {
-            it->second.emplace_back(tile);
-        }
-        else
-        {
-            rows.emplace_hint(it, tilePosY, std::vector<TileDesc>({ tile }));
-        }
+        tiles.push_back(tile);
     }
 
-    if (rows.empty())
+    for (auto& tile : tiles)
     {
-        // Done.
-        return;
-    }
-
-    auto& tiles = tileCombined.getTiles();
-    for (auto& row : rows)
-    {
-        tiles = row.second;
-        const auto tileMsg = tileCombined.serialize();
+        const auto tileMsg = tile.serialize("tile ");
         Log::debug() << "TileCombined residual request for " << tileMsg << Log::end;
 
         // Forward to child to render.
-        const std::string request = "tilecombine " + tileMsg;
-        _childProcess->getWebSocket()->sendFrame(request.data(), request.size());
+        _childProcess->getWebSocket()->sendFrame(tileMsg.data(), tileMsg.size());
     }
 }
 


More information about the Libreoffice-commits mailing list