[Libreoffice-commits] online.git: 9 commits - loleaflet/build loleaflet/debug loleaflet/dist loleaflet/package.json loleaflet/README loleaflet/spec loleaflet/src loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/protocol.txt loolwsd/TileCache.cpp loolwsd/TileCache.hpp

Mihai Varga mihai.varga at collabora.com
Thu Aug 20 04:44:09 PDT 2015


 loleaflet/README                                      |   19 +
 loleaflet/build/deps.js                               |   12 +
 loleaflet/debug/document/document_simple_example.html |    1 
 loleaflet/dist/leaflet.css                            |    5 
 loleaflet/package.json                                |    5 
 loleaflet/spec/headlessLoadTest.js                    |  179 ++++++++++++++++++
 loleaflet/spec/loadtest/LoadTestSpec.js               |    7 
 loleaflet/spec/suites/layer/marker/MarkerSpec.js      |    2 
 loleaflet/spec/suites/layer/tile/GridLayerSpec.js     |   74 -------
 loleaflet/spec/suites/layer/vector/CircleSpec.js      |    2 
 loleaflet/spec/suites/map/MapSpec.js                  |    2 
 loleaflet/src/control/Control.Styles.js               |   61 ++++++
 loleaflet/src/control/Styles.js                       |   17 +
 loleaflet/src/layer/Layer.js                          |    3 
 loleaflet/src/layer/tile/GridLayer.js                 |   24 +-
 loleaflet/src/layer/tile/TileLayer.js                 |    5 
 loleaflet/src/map/Map.js                              |   18 +
 loolwsd/LOOLSession.cpp                               |   40 +++-
 loolwsd/LOOLSession.hpp                               |    6 
 loolwsd/TileCache.cpp                                 |   42 ++--
 loolwsd/TileCache.hpp                                 |    6 
 loolwsd/protocol.txt                                  |    4 
 22 files changed, 412 insertions(+), 122 deletions(-)

New commits:
commit 573c6e5b3d3b82029560210878b13498b4f2e5c0
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Thu Aug 20 14:43:40 2015 +0300

    loleaflet: always return the current size

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index c3f091a..ebe67ee 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -333,13 +333,11 @@ L.Map = L.Evented.extend({
 	},
 
 	getSize: function () {
-		if (!this._size || this._sizeChanged) {
-			this._size = new L.Point(
-				this._container.clientWidth,
-				this._container.clientHeight);
+		this._size = new L.Point(
+			this._container.clientWidth,
+			this._container.clientHeight);
 
-			this._sizeChanged = false;
-		}
+		this._sizeChanged = false;
 		return this._size.clone();
 	},
 
commit 7bd000a8fc3caeb36724f94c2dbe77cb7bfc16b0
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Wed Aug 19 18:28:14 2015 +0300

    loleaflet: adapted some of the unit tests

diff --git a/loleaflet/spec/suites/layer/marker/MarkerSpec.js b/loleaflet/spec/suites/layer/marker/MarkerSpec.js
index 2d28fac..d33a8c9 100644
--- a/loleaflet/spec/suites/layer/marker/MarkerSpec.js
+++ b/loleaflet/spec/suites/layer/marker/MarkerSpec.js
@@ -32,7 +32,7 @@ describe("Marker", function () {
 			var afterIcon = marker._icon;
 
 			expect(beforeIcon).to.be(afterIcon);
-			expect(afterIcon.src).to.contain(icon2._getIconUrl('icon'));
+			expect(afterIcon.src.replace(/\.\.\//g, '')).to.be.(icon2._getIconUrl('icon').replace(/\.\.\//g, ''));
 		});
 
 		it("preserves draggability", function () {
diff --git a/loleaflet/spec/suites/layer/tile/GridLayerSpec.js b/loleaflet/spec/suites/layer/tile/GridLayerSpec.js
index 1ca4227..4181f7e 100644
--- a/loleaflet/spec/suites/layer/tile/GridLayerSpec.js
+++ b/loleaflet/spec/suites/layer/tile/GridLayerSpec.js
@@ -32,80 +32,6 @@ describe('GridLayer', function () {
 		});
 	});
 
-	it('positions tiles correctly with wrapping and bounding', function () {
-
-		map.setView([0, 0], 1);
-
-		var tiles = [];
-
-		var grid = L.gridLayer();
-		grid.createTile = function (coords) {
-			var tile = document.createElement('div');
-			tiles.push({coords: coords, tile: tile});
-			return tile;
-		};
-
-		map.addLayer(grid);
-
-		var loaded = {};
-
-		for (var i = 0; i < tiles.length; i++) {
-			var coords = tiles[i].coords,
-				pos = L.DomUtil.getPosition(tiles[i].tile);
-
-			loaded[pos.x + ':' + pos.y] = [coords.x, coords.y];
-		}
-
-		expect(loaded).to.eql({
-			'144:44': [0, 0],
-			'400:44': [1, 0],
-			'144:300': [0, 1],
-			'400:300': [1, 1],
-			'-112:44': [1, 0],
-			'656:44': [0, 0],
-			'-112:300': [1, 1],
-			'656:300': [0, 1]
-		});
-	});
-
-	describe('tile pyramid', function () {
-		var clock;
-
-		beforeEach(function () {
-			clock = sinon.useFakeTimers();
-		});
-
-		afterEach(function () {
-			clock.restore();
-		});
-
-		it('removes tiles for unused zoom levels', function (done) {
-			map.remove();
-			map = L.map(div, {fadeAnimation: false});
-			map.setView([0, 0], 1);
-
-			var grid = L.gridLayer();
-			var tiles = {};
-
-			grid.createTile = function (coords) {
-				tiles[grid._tileCoordsToKey(coords)] = true;
-				return document.createElement('div');
-			};
-
-			grid.on('tileunload', function (e) {
-				delete tiles[grid._tileCoordsToKey(e.coords)];
-				if (Object.keys(tiles).length === 1) {
-					expect(Object.keys(tiles)).to.eql(['0:0:0']);
-					done();
-				}
-			});
-
-			map.addLayer(grid);
-			map.setZoom(0, {animate: false});
-			clock.tick(250);
-		});
-	});
-
 	describe("#onAdd", function () {
 		it('is called after viewreset on first map load', function () {
 			var layer = L.gridLayer().addTo(map);
diff --git a/loleaflet/spec/suites/layer/vector/CircleSpec.js b/loleaflet/spec/suites/layer/vector/CircleSpec.js
index 0b45bc8..d48a2c2 100644
--- a/loleaflet/spec/suites/layer/vector/CircleSpec.js
+++ b/loleaflet/spec/suites/layer/vector/CircleSpec.js
@@ -4,7 +4,7 @@ describe('Circle', function () {
 		var map, circle;
 
 		beforeEach(function () {
-			map = L.map(document.createElement('div')).setView([0, 0], 4);
+			map = L.map(document.createElement('div'), {crs: L.CRS.EPSG3857}).setView([0, 0], 4);
 			circle = L.circle([50, 30], 200).addTo(map);
 		});
 
diff --git a/loleaflet/spec/suites/map/MapSpec.js b/loleaflet/spec/suites/map/MapSpec.js
index e7261ce..2125eae 100644
--- a/loleaflet/spec/suites/map/MapSpec.js
+++ b/loleaflet/spec/suites/map/MapSpec.js
@@ -2,7 +2,7 @@ describe("Map", function () {
 	var map,
 		spy;
 	beforeEach(function () {
-		map = L.map(document.createElement('div'));
+		map = L.map(document.createElement('div'), {crs: L.CRS.EPSG3857});
 	});
 
 	describe("#remove", function () {
commit 8a99c8877acda14e7848a8d5724726b8549820dc
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Wed Aug 19 18:27:33 2015 +0300

    loleaflet: fixed some things reported by failing unit tests

diff --git a/loleaflet/src/layer/Layer.js b/loleaflet/src/layer/Layer.js
index 9d6fb1c..0af279f 100644
--- a/loleaflet/src/layer/Layer.js
+++ b/loleaflet/src/layer/Layer.js
@@ -59,7 +59,8 @@ L.Layer = L.Evented.extend({
 	},
 
 	_initDocument: function () {},
-	_onMessage: function () {}
+	_onMessage: function () {},
+	sendMessage: function () {}
 });
 
 
diff --git a/loleaflet/src/layer/tile/GridLayer.js b/loleaflet/src/layer/tile/GridLayer.js
index 5d5cbfd..9ba1301 100644
--- a/loleaflet/src/layer/tile/GridLayer.js
+++ b/loleaflet/src/layer/tile/GridLayer.js
@@ -40,8 +40,10 @@ L.GridLayer = L.Layer.extend({
 		this._viewReset();
 		this._map._docLayer = this;
 
-		this._map.socket.onopen = L.bind(this._initDocument, this);
-		this._map.socket.onmessage = L.bind(this._onMessage, this);
+		if (this._map.socket) {
+			this._map.socket.onopen = L.bind(this._initDocument, this);
+			this._map.socket.onmessage = L.bind(this._onMessage, this);
+		}
 		if (this._map.socket && this._map.socket.readyState === 1) {
 			// the connection is already open
 			this._initDocument();
@@ -65,18 +67,24 @@ L.GridLayer = L.Layer.extend({
 		this._tileZoom = null;
 		clearTimeout(this._preFetchIdle);
 		clearInterval(this._tilesPreFetcher);
-		this._map.socket.onmessage = function () {};
-		this._map.socket.onclose = function () {};
-		this._map.socket.onerror = function () {};
-		this._map.socket.close();
+		if (this._map.socket) {
+			this._map.socket.onmessage = function () {};
+			this._map.socket.onclose = function () {};
+			this._map.socket.onerror = function () {};
+			this._map.socket.close();
+		}
 		if (this._cursorMarker) {
 			this._cursorMarker.remove();
 		}
 		if (this._graphicMarker) {
 			this._graphicMarker.remove();
 		}
-		this._startMarker.remove();
-		this._endMarker.remove();
+		if (this._startMarker) {
+			this._startMarker.remove();
+		}
+		if (this._endMarker) {
+			this._endMarker.remove();
+		}
 	},
 
 	bringToFront: function () {
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 5799ca7..c3f091a 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -249,8 +249,12 @@ L.Map = L.Evented.extend({
 			this.fire('unload');
 		}
 
-		this.removeLayer(this._docLayer._selections);
-		this.removeLayer(this._docLayer);
+		if (this._docLayer) {
+			if (this._docLayer._selections) {
+				this.removeLayer(this._docLayer._selections);
+			}
+			this.removeLayer(this._docLayer);
+		}
 		this.removeControls();
 		return this;
 	},
commit e46766de1904f51b356aba1cb4e3258b5bb5d180
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Wed Aug 19 10:57:42 2015 +0300

    loleaflet: styles control

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 98b5ee6..5488e50 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -289,6 +289,13 @@ var deps = {
 		desc: 'Handles vex dialogs for displaying alerts'
 	},
 
+	ControlStyles: {
+		src: ['control/Control.js',
+		      'control/Control.Styles.js'],
+		heading: 'Controls',
+		desc: 'Handles styles selection'
+	},
+
 	ControlAttrib: {
 		src: ['control/Control.js',
 		      'control/Control.Attribution.js'],
diff --git a/loleaflet/debug/document/document_simple_example.html b/loleaflet/debug/document/document_simple_example.html
index 9d31f45..eb55c1e 100644
--- a/loleaflet/debug/document/document_simple_example.html
+++ b/loleaflet/debug/document/document_simple_example.html
@@ -67,6 +67,7 @@
             });
 
     ////// Controls /////
+    globalMap.addControl(L.control.styles());
     globalMap.addControl(L.control.buttons());
     globalMap.addControl(L.control.zoom());
     globalMap.addControl(L.control.parts());
diff --git a/loleaflet/dist/leaflet.css b/loleaflet/dist/leaflet.css
index c7c43b7..7c09fb6 100644
--- a/loleaflet/dist/leaflet.css
+++ b/loleaflet/dist/leaflet.css
@@ -762,6 +762,11 @@ input.leaflet-control-search-bar {
     padding: 0.3em;
 }
 
+.leaflet-control-styles {
+	padding: 0.2em 0.4em 0.4em 0em;
+	background-color: white;
+}
+
 .leaflet-control-zoom.leaflet-bar {
     margin-left: 0.5em;
 }
diff --git a/loleaflet/src/control/Control.Styles.js b/loleaflet/src/control/Control.Styles.js
new file mode 100644
index 0000000..7bedc43
--- /dev/null
+++ b/loleaflet/src/control/Control.Styles.js
@@ -0,0 +1,61 @@
+/*
+ * L.Control.Styles is used to display a dropdown list of styles
+ */
+
+L.Control.Styles = L.Control.extend({
+	options: {
+		info: '- Please select a style -'
+	},
+
+	onAdd: function (map) {
+		var stylesName = 'leaflet-control-styles';
+		this._container = L.DomUtil.create('select', stylesName + ' leaflet-bar'),
+
+		map.on('updatepermission', this._onUpdatePermission, this);
+		map.on('updatestyles', this._initList, this);
+		L.DomEvent.on(this._container, 'change', this._onChange, this);
+
+		return this._container;
+	},
+
+	onRemove: function (map) {
+		map.off('updatepermission', this._searchResultFound, this);
+	},
+
+	_initList: function (e) {
+		var container = this._container;
+		var first = L.DomUtil.create('option', '', container);
+		first.innerHTML = this.options.info;
+		if (this._map._docLayer._docType === 'text') {
+			var styles = e.styles.ParagraphStyles.slice(0, 12);
+			styles.forEach(function (style) {
+				var item = L.DomUtil.create('option', '', container);
+				item.value = style;
+				item.innerHTML = style;
+			});
+		}
+	},
+
+	_onUpdatePermission: function (e) {
+		if (e.perm === 'edit') {
+			this._container.disabled = false;
+		}
+		else {
+			this._container.disabled = true;
+		}
+	},
+
+	_onChange: function (e) {
+		var style = e.target.value;
+		if (style === this.options.info) {
+			return;
+		}
+		if (this._map._docLayer._docType === 'text') {
+			this._map.setStyle(style, 'ParagraphStyles');
+		}
+	}
+});
+
+L.control.styles = function (options) {
+	return new L.Control.Styles(options);
+};
commit 438e4008591313c2be7ce2263e9091bf0d5e90e5
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Tue Aug 18 21:24:58 2015 +0300

    loleaflet: document style setter and getter

diff --git a/loleaflet/README b/loleaflet/README
index 78853f9..995f3ed 100644
--- a/loleaflet/README
+++ b/loleaflet/README
@@ -172,6 +172,15 @@ Writer pages:
             + e.pages = number of pages
             + e.docType = document type, should be 'text'
 
+Styles:
+    - API:
+        map.getStyles()
+            + returns a JSON object as a mapping of style families to a list of styles
+        map.setStyle(style, familyName)
+    - events:
+        map.on('updatestyles', function (e) {}) where:
+            e.styles = a JSON object as a mapping of style families to a list of styles
+
 Error:
     - events
         map.on('error', function (e) {}) where
diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index d527e7d..98b5ee6 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -332,6 +332,11 @@ var deps = {
 		desc: 'Scroll handler.'
 	},
 
+	Styles: {
+		src: ['control/Styles.js'],
+		desc: 'Document styles handler.'
+	},
+
 	AnimationPan: {
 		src: [
 			'dom/DomEvent.js',
diff --git a/loleaflet/src/control/Styles.js b/loleaflet/src/control/Styles.js
new file mode 100644
index 0000000..ca233de
--- /dev/null
+++ b/loleaflet/src/control/Styles.js
@@ -0,0 +1,17 @@
+L.Map.include({
+	getStyles: function () {
+		return this._docLayer._docStyles;
+	},
+
+	setStyle: function (style, familyName) {
+		if (!style || !familyName) {
+			this.fire('error', {cmd: 'setStyle', kind: 'incorrectparam'});
+			return;
+		}
+		var msg = 'uno .uno:StyleApply {' +
+				'"Style":{"type":"string", "value": "' + style + '"},' +
+				'"FamilyName":{"type":"string", "value":"' + familyName + '"}' +
+				'}';
+		this._docLayer.sendMessage(msg);
+	}
+});
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 8d7191e..010f7b0 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -105,6 +105,7 @@ L.TileLayer = L.GridLayer.extend({
 			}
 			this.sendMessage(msg);
 			this.sendMessage('status');
+			this.sendMessage('styles');
 		}
 		this._map.on('drag resize zoomend', this._updateScrollOffset, this);
 		this._map.on('clearselection', this._clearSelections, this);
@@ -509,6 +510,10 @@ L.TileLayer = L.GridLayer.extend({
 			var originalPhrase = textMsg.substring(16);
 			this._map.fire('search', {originalPhrase: originalPhrase, count: 0});
 		}
+		else if (textMsg.startsWith('styles:')) {
+			this._docStyles = JSON.parse(textMsg.substring(8));
+			this._map.fire('updatestyles', {styles: this._docStyles});
+		}
 		else if (textMsg.startsWith('error:')) {
 			command = this._parseServerCmd(textMsg);
 			this._map.fire('error', {cmd: command.errorCmd, kind: command.errorKind});
commit 1cfd1352ce70c546f2b11e32eadf8a408b49382d
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Tue Aug 18 21:01:05 2015 +0300

    loolwsd: renamed getStatus/saveStatus to getTextFile/saveTextFile
    
    And added a getStyles method that uses the above methods to cache
    document styles

diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index 1202639..b48af37 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -195,7 +195,11 @@ bool MasterProcessSession::handleInput(const char *buffer, int length)
             }
             else if (tokens[0] == "status:")
             {
-                peer->_tileCache->saveStatus(std::string(buffer, length));
+                peer->_tileCache->saveTextFile(std::string(buffer, length), "status.txt");
+            }
+            else if (tokens[0] == "styles:")
+            {
+                peer->_tileCache->saveTextFile(std::string(buffer, length), "styles.txt");
             }
             else if (tokens[0] == "invalidatetiles:")
             {
@@ -277,6 +281,7 @@ bool MasterProcessSession::handleInput(const char *buffer, int length)
              tokens[0] != "setclientpart" &&
              tokens[0] != "setpage" &&
              tokens[0] != "status" &&
+             tokens[0] != "styles" &&
              tokens[0] != "tile" &&
              tokens[0] != "uno")
     {
@@ -301,6 +306,10 @@ bool MasterProcessSession::handleInput(const char *buffer, int length)
     {
         return getStatus(buffer, length);
     }
+    else if (tokens[0] == "styles")
+    {
+        return getStyles(buffer, length);
+    }
     else if (tokens[0] == "tile")
     {
         sendTile(buffer, length, tokens);
@@ -394,7 +403,7 @@ bool MasterProcessSession::getStatus(const char *buffer, int length)
 {
     std::string status;
 
-    status = _tileCache->getStatus();
+    status = _tileCache->getTextFile("status.txt");
     if (status.size() > 0)
     {
         sendTextFrame(status);
@@ -407,6 +416,23 @@ bool MasterProcessSession::getStatus(const char *buffer, int length)
     return true;
 }
 
+bool MasterProcessSession::getStyles(const char *buffer, int length)
+{
+    std::string styles;
+
+    styles = _tileCache->getTextFile("styles.txt");
+    if (styles.size() > 0)
+    {
+        sendTextFrame(styles);
+        return true;
+    }
+
+    if (_peer.expired())
+        dispatchChild();
+    forwardToPeer(buffer, length);
+    return true;
+}
+
 void MasterProcessSession::sendTile(const char *buffer, int length, StringTokenizer& tokens)
 {
     int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
@@ -608,6 +634,10 @@ bool ChildProcessSession::handleInput(const char *buffer, int length)
     {
         return getStatus(buffer, length);
     }
+    else if (tokens[0] == "styles")
+    {
+        return getStyles(buffer, length);
+    }
     else if (tokens[0] == "tile")
     {
         sendTile(buffer, length, tokens);
@@ -819,6 +849,12 @@ bool ChildProcessSession::getStatus(const char *buffer, int length)
     return true;
 }
 
+bool ChildProcessSession::getStyles(const char *buffer, int length)
+{
+    sendTextFrame("styles: " + std::string(_loKitDocument->pClass->getStyles(_loKitDocument)));
+    return true;
+}
+
 void ChildProcessSession::sendTile(const char *buffer, int length, StringTokenizer& tokens)
 {
     int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 0030f28..a63e40e 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -45,6 +45,8 @@ public:
 
     virtual bool getStatus(const char *buffer, int length) = 0;
 
+    virtual bool getStyles(const char *buffer, int length) = 0;
+
     virtual bool handleInput(const char *buffer, int length) = 0;
 
 protected:
@@ -109,6 +111,8 @@ public:
 
     virtual bool getStatus(const char *buffer, int length);
 
+    virtual bool getStyles(const char *buffer, int length);
+
  protected:
     bool invalidateTiles(const char *buffer, int length, Poco::StringTokenizer& tokens);
 
@@ -154,6 +158,8 @@ public:
 
     virtual bool getStatus(const char *buffer, int length);
 
+    virtual bool getStyles(const char *buffer, int length);
+
     LibreOfficeKitDocument *_loKitDocument;
     std::string _docType;
 
diff --git a/loolwsd/TileCache.cpp b/loolwsd/TileCache.cpp
index 01714a4..76ca730 100644
--- a/loolwsd/TileCache.cpp
+++ b/loolwsd/TileCache.cpp
@@ -106,37 +106,37 @@ void TileCache::saveTile(int part, int width, int height, int tilePosX, int tile
     outStream.close();
 }
 
-std::string TileCache::getStatus()
+std::string TileCache::getTextFile(std::string fileName)
 {
-    const char statusFile[] = "/status.txt";
+    const char *textFile = std::string("/" + fileName).c_str();
 
     std::string dirName = cacheDirName(false);
     if (_hasUnsavedChanges)
     {
-        // try the Editing cache first, and prefer its status.txt if it exists
+        // try the Editing cache first, and prefer it if it exists
         std::string editingDirName = cacheDirName(true);
         File dir(editingDirName);
 
-        File status(editingDirName + statusFile);
-        if (dir.exists() && dir.isDirectory() && status.exists() && !status.isDirectory())
+        File text(editingDirName + textFile);
+        if (dir.exists() && dir.isDirectory() && text.exists() && !text.isDirectory())
             dirName = editingDirName;
     }
 
     if (!File(dirName).exists() || !File(dirName).isDirectory())
         return "";
 
-    std::string fileName = dirName + statusFile;
-    std::fstream statusStream(fileName, std::ios::in);
-    if (!statusStream.is_open())
+    fileName = dirName + textFile;
+    std::fstream textStream(fileName, std::ios::in);
+    if (!textStream.is_open())
         return "";
 
     std::vector<char> result;
-    statusStream.seekg(0, std::ios_base::end);
-    std::streamsize size = statusStream.tellg();
+    textStream.seekg(0, std::ios_base::end);
+    std::streamsize size = textStream.tellg();
     result.resize(size);
-    statusStream.seekg(0, std::ios_base::beg);
-    statusStream.read(result.data(), size);
-    statusStream.close();
+    textStream.seekg(0, std::ios_base::beg);
+    textStream.read(result.data(), size);
+    textStream.close();
 
     if (result[result.size()-1] == '\n')
         result.resize(result.size() - 1);
@@ -170,24 +170,22 @@ void TileCache::setEditing(bool editing)
     _isEditing = editing;
 }
 
-void TileCache::saveStatus(const std::string& status)
+void TileCache::saveTextFile(const std::string& text, std::string fileName)
 {
     std::string dirName = cacheDirName(_hasUnsavedChanges);
 
     File(dirName).createDirectories();
 
-    StringTokenizer tokens(status, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+    StringTokenizer tokens(text, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
 
-    assert(tokens[0] == "status:");
+    fileName = dirName + "/" + fileName;
+    std::fstream textStream(fileName, std::ios::out);
 
-    std::string fileName = dirName + "/status.txt";
-    std::fstream statusStream(fileName, std::ios::out);
-
-    if (!statusStream.is_open())
+    if (!textStream.is_open())
         return;
 
-    statusStream << status << std::endl;
-    statusStream.close();
+    textStream << text << std::endl;
+    textStream.close();
 }
 
 void TileCache::invalidateTiles(int part, int x, int y, int width, int height)
diff --git a/loolwsd/TileCache.hpp b/loolwsd/TileCache.hpp
index 1e01078..8294784 100644
--- a/loolwsd/TileCache.hpp
+++ b/loolwsd/TileCache.hpp
@@ -40,7 +40,7 @@ public:
 
     std::unique_ptr<std::fstream> lookupTile(int part, int width, int height, int tilePosX, int tilePosY, int tileWidth, int tileHeight);
     void saveTile(int part, int width, int height, int tilePosX, int tilePosY, int tileWidth, int tileHeight, const char *data, size_t size);
-    std::string getStatus();
+    std::string getTextFile(std::string fileName);
 
     /// Notify the cache that the document was saved - to copy tiles from the Editing cache to Persistent.
     void documentSaved();
@@ -48,8 +48,8 @@ public:
     /// Notify whether we need to use the Editing cache.
     void setEditing(bool editing = true);
 
-    // The parameter is a status: message
-    void saveStatus(const std::string& status);
+    // The parameter is a message
+    void saveTextFile(const std::string& text, std::string fileName);
 
     // The tiles parameter is an invalidatetiles: message as sent by the child process
     void invalidateTiles(const std::string& tiles);
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 734d317..369bb69 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -65,6 +65,8 @@ selectgraphic type=<type> x=<x> y=<y>
 
 status
 
+styles
+
 tile part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
 
     All parameters are numbers.
@@ -113,6 +115,8 @@ status: type=<typeName> parts=<numberOfParts> current=<currentPartNumber> width=
     <typeName> is 'text, 'spreadsheet', 'presentation', 'drawing' or 'other. Others are numbers.
     if the document has multiple parts and those have names, part names follow separated by '\n'
 
+styles: {"styleFamily": ["styles in family"], etc. }
+
 textselectioncontent: <content>
 
     Current selection's content
commit b5688840adb7eb72340e28d0e40d0e463b0780c4
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Tue Aug 18 14:34:10 2015 +0300

    loleaflet: mention how to run the tests

diff --git a/loleaflet/README b/loleaflet/README
index 0ce0f70..78853f9 100644
--- a/loleaflet/README
+++ b/loleaflet/README
@@ -179,6 +179,16 @@ Error:
             + [e.cmd] = the command that caused the error
             + [e.kind] = the kind of error
 
+Testing
+-------
+    - to simulate an editing session and to get the tile loading times
+        + open spec/tilebench.html in the browser
+    - to simulate a client opening several documents in the browser
+        + open spec/loadtest.html in the browser
+    - to simulate a client opening several documents in the console
+        + run: mocha headlessLoadTest.js
+
+
 Contributing
 ------------
 
commit 7e7e458b1ef8107f211cbdd76c9df453eeca1497
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Tue Aug 18 14:27:17 2015 +0300

    loleaflet: headless load test

diff --git a/loleaflet/package.json b/loleaflet/package.json
index b6e4b12..9c423c4 100644
--- a/loleaflet/package.json
+++ b/loleaflet/package.json
@@ -38,5 +38,8 @@
       "type": "BSD-2-Clause",
       "url": "https://github.com/Leaflet/Leaflet/blob/master/LICENSE"
     }
-  ]
+  ],
+  "dependencies": {
+    "ws": "~0.7.2"
+  }
 }
diff --git a/loleaflet/spec/headlessLoadTest.js b/loleaflet/spec/headlessLoadTest.js
new file mode 100644
index 0000000..fa88982
--- /dev/null
+++ b/loleaflet/spec/headlessLoadTest.js
@@ -0,0 +1,179 @@
+var WebSocket = require('ws');
+var events = require('events');
+
+if (typeof String.prototype.startsWith != 'function') {
+	String.prototype.startsWith = function (str){
+		return this.indexOf(str) === 0;
+	};
+}
+
+describe('LoadTest', function () {
+	// 10s timeout
+	this.timeout(10000);
+	// set the slow time to 5ms knowing each test takes more than that,
+	// so the run time is always printed
+	this.slow(5);
+	var testsRan = 0,
+		tileSize = 256,
+		tileSizeTwips = 3000,
+		host = 'ws://localhost:9980';
+
+	var docPath = 'file:///PATH';
+	var docs = ['eval.odt', 'lorem.odt'];
+
+	var _parseServerCmd = function (msg) {
+		var tokens = msg.split(/[ \n]+/);
+		var command = {};
+		for (var i = 0; i < tokens.length; i++) {
+			if (tokens[i].substring(0, 9) === 'tileposx=') {
+				command.x = parseInt(tokens[i].substring(9));
+			}
+			else if (tokens[i].substring(0, 9) === 'tileposy=') {
+				command.y = parseInt(tokens[i].substring(9));
+			}
+			else if (tokens[i].substring(0, 10) === 'tilewidth=') {
+				command.tileWidth = parseInt(tokens[i].substring(10));
+			}
+			else if (tokens[i].substring(0, 11) === 'tileheight=') {
+				command.tileHeight = parseInt(tokens[i].substring(11));
+			}
+			else if (tokens[i].substring(0, 6) === 'width=') {
+				command.width = parseInt(tokens[i].substring(6));
+			}
+			else if (tokens[i].substring(0, 7) === 'height=') {
+				command.height = parseInt(tokens[i].substring(7));
+			}
+			else if (tokens[i].substring(0, 5) === 'part=') {
+				command.part = parseInt(tokens[i].substring(5));
+			}
+			else if (tokens[i].substring(0, 6) === 'parts=') {
+				command.parts = parseInt(tokens[i].substring(6));
+			}
+			else if (tokens[i].substring(0, 8) === 'current=') {
+				command.currentPart = parseInt(tokens[i].substring(8));
+			}
+		}
+		return command;
+	};
+
+	before(function () {
+		if (docPath === 'file:///PATH') {
+			throw new Error('Document file path not set');
+		}
+		else if (docPath[docPath.length - 1] !== '/') {
+			docPath += '/';
+		}
+	});
+
+	docs.forEach(function (testDoc) {
+		testsRan += 1;
+		describe('Document #' + testsRan + ' (' + testDoc + ')', function () {
+			var ws;
+			var requestedTiles = 0;
+			var docWidthTwips, docHeightTwips, midY, endY;
+			var eventEmitter = new events.EventEmitter();
+
+			var onMessage = function (evt) {
+				var bytes, index, textMsg;
+
+				if (typeof (evt.data) === 'string') {
+					textMsg = evt.data;
+				}
+				else if (typeof (evt.data) === 'object') {
+					bytes = new Uint8Array(evt.data);
+					index = 0;
+					// search for the first newline which marks the end of the message
+					while (index < bytes.length && bytes[index] !== 10) {
+						index++;
+					}
+					textMsg = String.fromCharCode.apply(null, bytes.subarray(0, index));
+				}
+
+				if (textMsg.startsWith('status:')) {
+					command = _parseServerCmd(textMsg);
+					docWidthTwips = command.width;
+					docHeightTwips = command.height;
+					endY = Math.floor(docHeightTwips / tileSizeTwips);
+					midY = Math.floor(endY / 2);
+					eventEmitter.emit('status');
+				}
+				else if (textMsg.startsWith('tile:')) {
+					requestedTiles -= 1;
+					if (requestedTiles <= 0) {
+						eventEmitter.emit('alltilesloaded');
+					}
+				}
+				else if (textMsg.startsWith('error:')) {
+					console.log(textMsg);
+					throw new Error(textMsg);
+				}
+			};
+
+			var requestTiles = function (x, y) {
+				requestedTiles += 1;
+				ws.send('tile ' +
+						'part=0 ' +
+						'width=' + tileSize + ' ' +
+						'height=' + tileSize + ' ' +
+						'tileposx=' + x * tileSizeTwips + ' ' +
+						'tileposy=' + y * tileSizeTwips + ' ' +
+						'tilewidth=' + tileSizeTwips + ' ' +
+						'tileheight=' + tileSizeTwips);
+			};
+
+			var isValidTile = function (x, y) {
+				return x >= 0 && y >= 0 && (x * tileSizeTwips < docWidthTwips) && (y * tileSizeTwips < docHeightTwips);
+			};
+
+			after(function () {
+				ws.onmessage = function () {};
+				ws.close();
+			});
+
+			it('Connect to the server', function (done) {
+				eventEmitter.once('status', done);
+				ws = new WebSocket(host);
+				ws.onmessage = onMessage;
+				ws.onerror = function (e) {console.log(e)};
+				ws.binaryType = 'arraybuffer';
+				ws.onopen = function () {
+					ws.send('load url=' + docPath + testDoc);
+					ws.send('status');
+				};
+			});
+
+			it('Load the document', function (done) {
+				eventEmitter.once('alltilesloaded', done);
+				for (var i = 0; i < 3; i++) {
+					for (j = 0; j < 5; j++) {
+						if (isValidTile(j, i)) {
+							requestTiles(j, i);
+						}
+					}
+				}
+			});
+
+			it('Scroll to the middle', function (done) {
+				eventEmitter.once('alltilesloaded', done);
+				for (var i = midY; i < midY + 3; i++) {
+					for (j = 0; j < 5; j++) {
+						if (isValidTile(j, i)) {
+							requestTiles(j, i);
+						}
+					}
+				}
+			});
+
+			it('Scroll to the end', function (done) {
+				eventEmitter.once('alltilesloaded', done);
+				for (var i = endY; i > endY - 3; i--) {
+					for (j = 0; j < 5; j++) {
+						if (isValidTile(j, i)) {
+							requestTiles(j, i);
+						}
+					}
+				}
+			});
+		});
+	});
+});
commit 6dc45b5825316ba9aeb1389acd9580fab7b4ad66
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Tue Aug 18 13:52:00 2015 +0300

    loleaflet: load test check if doc path is set

diff --git a/loleaflet/spec/loadtest/LoadTestSpec.js b/loleaflet/spec/loadtest/LoadTestSpec.js
index e04350a..c339926 100644
--- a/loleaflet/spec/loadtest/LoadTestSpec.js
+++ b/loleaflet/spec/loadtest/LoadTestSpec.js
@@ -1,6 +1,9 @@
 describe('LoadTest', function () {
 	// 25 s timeout
 	this.timeout(25000);
+	// set the slow time to 5ms knowing each test takes more than that,
+	// so the run time is always printed
+	this.slow(5);
 	var testsRan = 0,
 		checkTimeOut = null,
 		map = null,
@@ -9,6 +12,10 @@ describe('LoadTest', function () {
 		y = 0;
 
 	before(function() {
+		if (docPath === 'file:///PATH') {
+			throw new Error('Document file path not set');
+		}
+
 		map = L.map('map-test', {
 			center: [0, 0],
 			zoom: 10,


More information about the Libreoffice-commits mailing list