[Libreoffice-commits] online.git: Branch 'feature/jsdialogs' - 109 commits - android/app android/lib configure.ac cypress_test/data cypress_test/integration_tests cypress_test/Makefile.am docker/Debian docker/Dockerfile docker/l10n-docker-nightly.sh docker/Ubuntu fuzzer/ClientSession.cpp fuzzer/README INSTALL ios/IPAExportOptions.plist ios/Mobile.xcodeproj kit/ChildSession.cpp kit/ChildSession.hpp loleaflet/css loleaflet/images loleaflet/js loleaflet/Makefile.am loleaflet/po loleaflet/src loolwsd.xml.in Makefile.am net/Socket.cpp net/Socket.hpp test/helpers.hpp wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/FileServer.cpp wsd/FileServer.hpp wsd/LOOLWSD.cpp wsd/LOOLWSD.hpp wsd/ProofKey.cpp wsd/ProxyProtocol.cpp wsd/ProxyProtocol.hpp wsd/RequestDetails.cpp wsd/RequestDetails.hpp wsd/ServerURL.hpp

Szymon KÅ‚os (via logerrit) logerrit at kemper.freedesktop.org
Mon May 18 12:23:09 UTC 2020


Rebased ref, commits from common ancestor:
commit 9eb64fd441a50b55b77f252fc7e2aedfb3b0d385
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed May 6 10:49:47 2020 +0200
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:22:09 2020 +0200

    jsdialog: use edit instead of combobox in find & replace
    
    Change-Id: I1d365ef8ac3860ffa581f830c1b989842532723a

diff --git a/loleaflet/src/control/Control.JSDialogBuilder.js b/loleaflet/src/control/Control.JSDialogBuilder.js
index 492bbe3dd..0de7e0aba 100644
--- a/loleaflet/src/control/Control.JSDialogBuilder.js
+++ b/loleaflet/src/control/Control.JSDialogBuilder.js
@@ -1393,8 +1393,12 @@ L.Control.JSDialogBuilder = L.Control.extend({
 		if (data.id === 'applystyle' ||
 			data.id === 'fontnamecombobox' ||
 			data.id === 'fontsizecombobox' ||
-			data.id === 'FontBox')
+			data.id === 'FontBox') {
 			builder._listboxControl(parentContainer, data, builder);
+		} else if (data.id === 'searchterm' ||
+			data.id === 'replaceterm') {
+			builder._editControl(parentContainer, data, builder);
+		}
 		else
 			builder._explorableEditControl(parentContainer, data, builder);
 	},
commit 0e90c7a99fc1a1eff4c6347f370e96e6a4371a16
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed May 6 10:20:37 2020 +0200
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:22:09 2020 +0200

    Add spell check dialog in mobile menu
    
    Change-Id: I926025cdda2658865dcdb417ea4f030b325ff685

diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 1531e7142..323d40917 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -505,6 +505,7 @@ L.Control.Menubar = L.Control.extend({
 				{uno: '.uno:SelectAll'}
 			]},
 			{name: _('Search'), id: 'searchdialog', uno: '.uno:SearchDialog'},
+			{uno: '.uno:SpellingAndGrammarDialog'},
 			{uno: '.uno:WordCountDialog'},
 			{name: _UNO('.uno:ChangesMenu', 'text'), id: 'changesmenu', type: 'menu', menu: [
 				{uno: '.uno:TrackChanges'},
commit 048af68f2f57b3776a24f38072a3fda1dd64c622
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed May 6 10:18:30 2020 +0200
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:22:09 2020 +0200

    Add data validation dialog in menu
    
    Change-Id: I8887eac359f87fa2fe59b29f10a5fe9804bf33b9

diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 86e175841..1531e7142 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -617,6 +617,8 @@ L.Control.Menubar = L.Control.extend({
 					{uno: '.uno:DeleteColumnbreak'}]}
 			]},
 			{name: _UNO('.uno:DataMenu', 'spreadsheet'), id: 'datamenu', type: 'menu', menu: [
+				{uno: '.uno:Validation'},
+				{type: 'separator'},
 				{uno: '.uno:DataSort'},
 				{uno: '.uno:SortAscending'},
 				{uno: '.uno:SortDescending'},
commit eeceb5e82f9cf4ba134c4766d72a9180b52931a8
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed May 6 10:15:23 2020 +0200
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:22:07 2020 +0200

    Add watermark dialog in menu
    
    Change-Id: I76054a101c8d45f46761bbc7b70d4939a48acd30

diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 481cfa2ac..86e175841 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -516,6 +516,7 @@ L.Control.Menubar = L.Control.extend({
 				{uno: '.uno:NextTrackedChange'}
 			]},
 			{name: _('Page Setup'), id: 'pagesetup', type: 'action'},
+			{uno: '.uno:Watermark'},
 			{name: _UNO('.uno:ViewMenu', 'text'), id: 'view', type: 'menu', menu: [
 				{uno: '.uno:ControlCodes'},
 				{uno: '.uno:SpellOnline'},
commit 7b95ee1908626f260852f64f2e7f2ca3b7e74529
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed May 6 10:13:40 2020 +0200
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:21:31 2020 +0200

    Add word count dialog in menu
    
    Change-Id: I1417ac3b261bbd9ce7411c5f4bb37e3093ee88bf

diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 184e6bdc7..481cfa2ac 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -505,6 +505,7 @@ L.Control.Menubar = L.Control.extend({
 				{uno: '.uno:SelectAll'}
 			]},
 			{name: _('Search'), id: 'searchdialog', uno: '.uno:SearchDialog'},
+			{uno: '.uno:WordCountDialog'},
 			{name: _UNO('.uno:ChangesMenu', 'text'), id: 'changesmenu', type: 'menu', menu: [
 				{uno: '.uno:TrackChanges'},
 				{uno: '.uno:ShowTrackedChanges'},
commit 69f5f8182011175a21a7231d12f60c2228e8fbee
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed May 6 10:12:52 2020 +0200
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:21:31 2020 +0200

    Add find & replace dialog in menu
    
    Change-Id: I4cf7f773e46d39fac521f607df4db6922a3c70bf

diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index d2ef6b36d..184e6bdc7 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -504,7 +504,7 @@ L.Control.Menubar = L.Control.extend({
 				{uno: '.uno:Paste'},
 				{uno: '.uno:SelectAll'}
 			]},
-			{name: _('Search'), id: 'searchdialog', type: 'action'},
+			{name: _('Search'), id: 'searchdialog', uno: '.uno:SearchDialog'},
 			{name: _UNO('.uno:ChangesMenu', 'text'), id: 'changesmenu', type: 'menu', menu: [
 				{uno: '.uno:TrackChanges'},
 				{uno: '.uno:ShowTrackedChanges'},
@@ -549,7 +549,7 @@ L.Control.Menubar = L.Control.extend({
 				{uno: '.uno:Paste'},
 				{uno: '.uno:SelectAll'}
 			]},
-			{name: _('Search'), id: 'searchdialog', type: 'action'},
+			{name: _('Search'), id: 'searchdialog', uno: '.uno:SearchDialog'},
 			{name: _UNO('.uno:TableMenu', 'text'/*HACK should be 'presentation', but not in xcu*/), id: 'tablemenu', type: 'menu', menu: [
 				{uno: '.uno:InsertRowsBefore'},
 				{uno: '.uno:InsertRowsAfter'},
@@ -596,7 +596,7 @@ L.Control.Menubar = L.Control.extend({
 				{uno: '.uno:Paste'},
 				{uno: '.uno:SelectAll'}
 			]},
-			{name: _('Search'), id: 'searchdialog', type: 'action'},
+			{name: _('Search'), id: 'searchdialog', uno: '.uno:SearchDialog'},
 			{name: _UNO('.uno:SheetMenu', 'spreadsheet'), id: 'sheetmenu', type: 'menu', menu: [
 				{name: _UNO('.uno:InsertRowsMenu', 'spreadsheet'), id: 'insertrowsmenu', type: 'menu', menu: [
 					{uno: '.uno:InsertRowsBefore'},
@@ -1218,11 +1218,6 @@ L.Control.Menubar = L.Control.extend({
 			}
 		} else if (id === 'repair') {
 			this._map._socket.sendMessage('commandvalues command=.uno:DocumentRepair');
-		} else if (id === 'searchdialog') {
-			$('#toolbar-down').hide();
-			$('#toolbar-search').show();
-			$('#mobile-edit-button').hide();
-			L.DomUtil.get('search-input').focus();
 		} else if (id === 'inserttextbox') {
 			this._map.sendUnoCommand('.uno:Text?CreateDirectly:bool=true');
 		} else if (id === 'insertslidefield') {
commit 82790574e51c4e561af7a9def8f970daf3f37724
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed Mar 11 13:17:07 2020 +0100
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:21:31 2020 +0200

    jsdialog: fix inifinite regenetate JSON loop
    
    Change-Id: I52667e115a0cf70f09f570dc6ea1ec0bcac49460

diff --git a/loleaflet/src/control/Control.MobileWizard.js b/loleaflet/src/control/Control.MobileWizard.js
index 958eeb985..b9623eab5 100644
--- a/loleaflet/src/control/Control.MobileWizard.js
+++ b/loleaflet/src/control/Control.MobileWizard.js
@@ -273,7 +273,8 @@ L.Control.MobileWizard = L.Control.extend({
 	},
 
 	_goToPath: function(path) {
-		if (this._tabs && path && path.length)
+		// when dialog has tabs, tab selection triggers the callback, causes infinite regenetate loop
+		if (this._tabs && path && path.length && !this.map.dialog.hasDialogInMobilePanelOpened())
 			this._selectTab(path[0]);
 
 		var _path = [];
commit b7ddaffad3fc5735ae91305c2bd5a0038a9e0c67
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Thu Mar 5 14:40:54 2020 +0100
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:21:31 2020 +0200

    jsdialog: send control type with event
    
    Change-Id: I6cd6f1d26b5c78715f9e0adc992a1869e0f19a97

diff --git a/loleaflet/src/control/Control.JSDialogBuilder.js b/loleaflet/src/control/Control.JSDialogBuilder.js
index ab0996c22..492bbe3dd 100644
--- a/loleaflet/src/control/Control.JSDialogBuilder.js
+++ b/loleaflet/src/control/Control.JSDialogBuilder.js
@@ -230,8 +230,11 @@ L.Control.JSDialogBuilder = L.Control.extend({
 			data = typeof data === 'string' ? data.replace('"', '\\"') : data;
 			var windowId = window.mobileDialogId !== undefined ? window.mobileDialogId :
 								(window.sidebarId !== undefined ? window.sidebarId : -1);
-			var message = 'dialogevent ' + windowId +
-			    ' {\"id\":\"' + object.id + '\", \"cmd\": \"' + eventType + '\", \"data\":\"' + data + '\"}';
+			var message = 'dialogevent ' + windowId
+			    + ' {\"id\":\"' + object.id
+				+ '\", \"cmd\": \"' + eventType
+				+ '\", \"data\": \"' + data
+				+ '\", \"type\": \"' + objectType + '\"}';
 			builder.map._socket.sendMessage(message);
 		}
 	},
commit 83cfbef802e71926da85b1d235ce6e4b1fcd9dfa
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Fri Feb 21 09:54:33 2020 +0100
Commit:     Szymon Kłos <szymon.klos at collabora.com>
CommitDate: Mon May 18 14:21:31 2020 +0200

    jsdialog: handle events for dialogs in mobilewizard
    
    - remember window id
    - close mobile wizard on dialog close
    - close dialog on mobile wizard close
    - don't show dialogs on mobile devices
    
    Change-Id: I585c5b92192655684eedd62d88817a92fc1fc0a8

diff --git a/loleaflet/src/control/Control.JSDialogBuilder.js b/loleaflet/src/control/Control.JSDialogBuilder.js
index 4ecbc0ad9..ab0996c22 100644
--- a/loleaflet/src/control/Control.JSDialogBuilder.js
+++ b/loleaflet/src/control/Control.JSDialogBuilder.js
@@ -228,7 +228,9 @@ L.Control.JSDialogBuilder = L.Control.extend({
 			builder.map.sendUnoCommand(encodedCommand);
 		} else if (object) {
 			data = typeof data === 'string' ? data.replace('"', '\\"') : data;
-			var message = 'dialogevent ' + (window.sidebarId !== undefined ? window.sidebarId : -1) +
+			var windowId = window.mobileDialogId !== undefined ? window.mobileDialogId :
+								(window.sidebarId !== undefined ? window.sidebarId : -1);
+			var message = 'dialogevent ' + windowId +
 			    ' {\"id\":\"' + object.id + '\", \"cmd\": \"' + eventType + '\", \"data\":\"' + data + '\"}';
 			builder.map._socket.sendMessage(message);
 		}
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 43a8ee7f4..a96ae309f 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -59,6 +59,10 @@ L.Control.LokDialog = L.Control.extend({
 
 	dialogIdPrefix: 'lokdialog-',
 
+	hasDialogInMobilePanelOpened: function() {
+		return window.mobileDialogId !== undefined;
+	},
+
 	onPan: function (ev) {
 		if (!draggedObject)
 			return;
@@ -289,7 +293,7 @@ L.Control.LokDialog = L.Control.extend({
 		}
 
 		if (e.action === 'created') {
-			if (e.winType === 'dialog') {
+			if (e.winType === 'dialog' && !window.mode.isMobile()) {
 				// When left/top are invalid, the dialog shows in the center.
 				this._launchDialog(e.id, left, top, width, height, e.title);
 			} else if (e.winType === 'calc-input-win') {
@@ -335,7 +339,17 @@ L.Control.LokDialog = L.Control.extend({
 		}
 
 		// All other callbacks doen't make sense without an active dialog.
-		if (!(this._isOpen(e.id) || this._getParentId(e.id)))
+		if (!(this._isOpen(e.id) || this._getParentId(e.id))) {
+			if (e.action == 'close' && window.mobileDialogId == e.id) {
+				window.mobileDialogId = undefined;
+				this._map.fire('closemobilewizard');
+			}
+
+			return;
+		}
+
+		// We don't want dialogs and sidebar on smartphones, only calc input window is allowed
+		if (window.mode.isMobile() && e.winType !== 'calc-input-win' && !this.isCalcInputBar(e.id))
 			return;
 
 		if (e.action === 'invalidate') {
@@ -1401,6 +1415,9 @@ L.Control.LokDialog = L.Control.extend({
 				this._onDialogClose(dialogId, true);
 			}
 		}
+		if (this.hasDialogInMobilePanelOpened()) {
+			this._onDialogClose(window.mobileDialogId, true);
+		}
 	},
 
 	onCloseCurrentPopUp: function() {
diff --git a/loleaflet/src/control/Control.MobileWizard.js b/loleaflet/src/control/Control.MobileWizard.js
index 926f9f707..958eeb985 100644
--- a/loleaflet/src/control/Control.MobileWizard.js
+++ b/loleaflet/src/control/Control.MobileWizard.js
@@ -93,6 +93,12 @@ L.Control.MobileWizard = L.Control.extend({
 	},
 
 	_hideWizard: function() {
+		// dialog
+		if (this.map.dialog.hasDialogInMobilePanelOpened()) {
+			this.map.dialog._onDialogClose(window.mobileDialogId, true);
+			window.mobileDialogId = undefined;
+		}
+
 		$('#mobile-wizard').hide();
 		$('#mobile-wizard-content').empty();
 		if (this.map._permission === 'edit') {
@@ -320,6 +326,11 @@ L.Control.MobileWizard = L.Control.extend({
 				return;
 			}
 
+			if (data.id && !isNaN(data.id) && !isSidebar) {
+				// id is a number - remember window id for interaction
+				window.mobileDialogId = data.id;
+			}
+
 			// Sometimes it happens that we get the same sidebar
 			// structure twice. This makes hard to test mobile wizard.
 			if (isSidebar && L.Browser.cypressTest) {
commit f1262f7b3180e984bff53b5958aa059562966f79
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Mon May 18 14:15:26 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Mon May 18 14:15:26 2020 +0200

    cypress: this is unstable.
    
    Change-Id: Ifb492aa60594a98f7274e09682e3042797431ce8

diff --git a/cypress_test/integration_tests/desktop/writer/form_field_spec.js b/cypress_test/integration_tests/desktop/writer/form_field_spec.js
index 9ae817ec7..a1554fa3b 100644
--- a/cypress_test/integration_tests/desktop/writer/form_field_spec.js
+++ b/cypress_test/integration_tests/desktop/writer/form_field_spec.js
@@ -263,7 +263,7 @@ describe('Form field button tests.', function() {
 			.should('not.exist');
 	});
 
-	it('Test field button after zoom.', function() {
+	it.skip('Test field button after zoom.', function() {
 		helper.loadTestDoc('form_field.odt', 'writer');
 
 		// Move the cursor next to the form field
commit 82f96b40f8b8ae714f9c86ce47cde1652f8db8d5
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Mon May 18 11:50:24 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Mon May 18 12:45:05 2020 +0200

    cypress: add zooming test for form fields.
    
    Make sure the text cursor is inside the form field
    button's frame.
    
    Change-Id: Ib8a6fe50c14d01b5d3864f04e9668b9b1844b50e
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94408
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/cypress_test/integration_tests/desktop/writer/form_field_spec.js b/cypress_test/integration_tests/desktop/writer/form_field_spec.js
index def301989..9ae817ec7 100644
--- a/cypress_test/integration_tests/desktop/writer/form_field_spec.js
+++ b/cypress_test/integration_tests/desktop/writer/form_field_spec.js
@@ -28,6 +28,24 @@ describe('Form field button tests.', function() {
 
 		cy.get('.drop-down-field-list')
 			.should('exist');
+
+		// Check also the position relative to the blinking cursor
+		cy.get('.blinking-cursor')
+			.then(function(cursors) {
+				// TODO: why we have two blinking cursors here?
+				//expect(cursors).to.have.lengthOf(1);
+
+				var cursorRect = cursors[0].getBoundingClientRect();
+				cy.get('.form-field-frame')
+					.should(function(frames) {
+						expect(frames).to.have.lengthOf(1);
+						var frameRect = frames[0].getBoundingClientRect();
+						expect(frameRect.top).to.be.lessThan(cursorRect.top);
+						expect(frameRect.bottom).to.be.greaterThan(cursorRect.bottom);
+						expect(frameRect.left).to.be.lessThan(cursorRect.left);
+						expect(frameRect.right).to.be.greaterThan(cursorRect.right);
+					});
+			});
 	}
 
 	it('Activate and deactivate form field button.', function() {
@@ -244,5 +262,39 @@ describe('Form field button tests.', function() {
 		cy.get('.drop-down-field-list-item.selected')
 			.should('not.exist');
 	});
+
+	it('Test field button after zoom.', function() {
+		helper.loadTestDoc('form_field.odt', 'writer');
+
+		// Move the cursor next to the form field
+		cy.get('textarea.clipboard')
+			.type('{rightArrow}');
+
+		buttonShouldExist();
+
+		// Do a zoom in
+		cy.get('#tb_actionbar_item_zoom')
+			.click();
+
+		cy.contains('.menu-text', '120')
+			.click();
+
+		cy.contains('#tb_actionbar_item_zoom', '120')
+			.should('exist');
+
+		buttonShouldExist();
+
+		// Do a zoom out
+		cy.get('#tb_actionbar_item_zoom')
+			.click();
+
+		cy.contains('.menu-text', '85')
+			.click();
+
+		cy.contains('#tb_actionbar_item_zoom', '85')
+			.should('exist');
+
+		buttonShouldExist();
+	});
 });
 
commit 13f1f0182edd159034489caed910b5e1608d07f3
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Mon May 18 11:27:54 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Mon May 18 12:41:48 2020 +0200

    MSForms: rerender form field button on zoom.
    
    Hide the button on zoom start and display it again with
    the new size on zoom end.
    
    Change-Id: If507009923e85225c8594252bd76033c8b84783b
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94407
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/loleaflet/src/layer/FormFieldButtonLayer.js b/loleaflet/src/layer/FormFieldButtonLayer.js
index fcd826c28..a8c268872 100644
--- a/loleaflet/src/layer/FormFieldButtonLayer.js
+++ b/loleaflet/src/layer/FormFieldButtonLayer.js
@@ -41,6 +41,9 @@ L.FormFieldButton = L.Layer.extend({
 
 		// Build list of items opened by clicking on the drop down button
 		this._buildDropDownList(framePos, frameWidth, frameHeight);
+
+		map.on('zoomstart', this._onZoomStart, this);
+		map.on('zoomend', this._onZoomEnd, this);
 	},
 
 	_calculateButtonArea: function(map) {
@@ -161,6 +164,16 @@ L.FormFieldButton = L.Layer.extend({
 		this.map._socket.sendMessage(message);
 	},
 
+	_onZoomStart: function() {
+		$('.drop-down-field-list').hide();
+		this._clearButton();
+	},
+
+	_onZoomEnd: function() {
+		// rebuild button on zoom
+		this.onAdd(this.map);
+	},
+
 	_clearButton: function() {
 		this.getPane('formfieldPane').innerHTML = '';
 	}
commit 29b08b31d7460b6dbdeb3dcb5fca193abcabdc2e
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Mon May 18 10:25:44 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Mon May 18 10:36:36 2020 +0200

    Revert "cypress: enable some more tests."
    
    This reverts commit 53e887d193c5361c4152dcee55a1a43271c518f8.
    
    Change-Id: I184e9e44975596e3b05a7beb45a16e79ea22f282
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94280
    Tested-by: Tamás Zolnai <tamas.zolnai at collabora.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
index 6d854372d..a3b5c0165 100644
--- a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
+++ b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
@@ -219,7 +219,8 @@ describe('Change alignment settings.', function() {
 			});
 	});
 
-	it('Change text indent via input field.', function() {
+	it.skip('Change text indent via input field.', function() {
+		// TODO: this fails, because the input field always becomes disabled.
 		helper.initAliasToNegative('originalTextPos');
 
 		getTextPosForFirstCell();
@@ -232,6 +233,11 @@ describe('Change alignment settings.', function() {
 		openAlignmentPaneForFirstCell();
 
 		// TODO: First we need to increase indent to make the input enabled
+		cy.get('#IncrementIndent')
+			.click();
+
+		cy.wait(300);
+
 		cy.get('#IncrementIndent')
 			.click();
 
diff --git a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
index 5ed397d1d..d54869074 100644
--- a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
+++ b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
@@ -113,7 +113,7 @@ describe('Change shape properties via mobile wizard.', function() {
 			.should('have.attr', 'd', 'M 1965,4863 L 7957,18073 1965,18073 1965,4863 1965,4863 Z');
 	});
 
-	it('Change size with keep ratio enabled.', function() {
+	it.skip('Change size with keep ratio enabled.', function() {
 		// TODO: Entering a value inside the spinbutton has no effect on the shape.
 		if (Cypress.env('LO_CORE_VERSION') === 'master')
 			return;
commit df205d63609aeaa381549166a26a6d64dcefc5e0
Author:     Aron Budea <aron.budea at collabora.com>
AuthorDate: Fri May 15 23:07:43 2020 +0200
Commit:     Aron Budea <aron.budea at collabora.com>
CommitDate: Sat May 16 00:30:55 2020 +0200

    loleaflet: Empty user list flashes up at start on mobile
    
    Regression from 610e2dbd583da4537e8cdef7f10318dc43e8c269
    
    Change-Id: I35809de56d9533381f0c0d2bdfcd5587e065ce9f
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94336
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Aron Budea <aron.budea at collabora.com>

diff --git a/loleaflet/src/control/Control.MobileTopBar.js b/loleaflet/src/control/Control.MobileTopBar.js
index ba727279f..acfa82c7d 100644
--- a/loleaflet/src/control/Control.MobileTopBar.js
+++ b/loleaflet/src/control/Control.MobileTopBar.js
@@ -33,7 +33,7 @@ L.Control.MobileTopBar = L.Control.extend({
 				{type: 'button',  id: 'insertion_mobile_wizard', img: 'insertion_mobile_wizard', disabled: true},
 				{type: 'button',  id: 'insertcomment', img: 'insertcomment', disabled: true},
 				{type: 'button',  id: 'fullscreen', img: 'fullscreen', hint: _UNO('.uno:FullScreen', 'text')},
-				{type: 'drop', id: 'userlist', img: 'users', html: L.control.createUserListWidget()},
+				{type: 'drop', id: 'userlist', img: 'users', hidden: true, html: L.control.createUserListWidget()},
 			];
 		} else if (docType == 'spreadsheet') {
 			return [
@@ -47,7 +47,7 @@ L.Control.MobileTopBar = L.Control.extend({
 				{type: 'button',  id: 'insertion_mobile_wizard', img: 'insertion_mobile_wizard', disabled: true},
 //				{type: 'button',  id: 'insertcomment', img: 'insertcomment', disabled: true},
 				{type: 'button',  id: 'fullscreen', img: 'fullscreen', hint: _UNO('.uno:FullScreen', 'text')},
-				{type: 'drop', id: 'userlist', img: 'users', html: L.control.createUserListWidget()},
+				{type: 'drop', id: 'userlist', img: 'users', hidden: true, html: L.control.createUserListWidget()},
 			];
 		} else if (docType == 'presentation') {
 			return [
commit 56afddaba085c07d2a4407e52dc7d0c4c85e907b
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Fri May 15 23:33:31 2020 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Fri May 15 23:34:01 2020 +0300

    Add RequestDetails.cpp and hpp
    
    Change-Id: I084ce2e731073ab69a806d5053c27987b2a8244c

diff --git a/ios/Mobile.xcodeproj/project.pbxproj b/ios/Mobile.xcodeproj/project.pbxproj
index a7ab38573..bedc02c3b 100644
--- a/ios/Mobile.xcodeproj/project.pbxproj
+++ b/ios/Mobile.xcodeproj/project.pbxproj
@@ -63,6 +63,7 @@
 		BEA28360214ACA8500848631 /* FakeSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA2835F214ACA8500848631 /* FakeSocket.cpp */; };
 		BEA28377214FFD8C00848631 /* Unit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEA28376214FFD8C00848631 /* Unit.cpp */; };
 		BEB0E5D921C7CA800085A0CF /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BEB0E5D821C7CA800085A0CF /* Settings.bundle */; };
+		BEBF3EB0246EB1C800415E87 /* RequestDetails.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEBF3EAF246EB1C800415E87 /* RequestDetails.cpp */; };
 		BECD984024336DD400016117 /* device-mobile.css in Resources */ = {isa = PBXBuildFile; fileRef = BECD983E24336DD400016117 /* device-mobile.css */; };
 		BECD984124336DD400016117 /* device-tablet.css in Resources */ = {isa = PBXBuildFile; fileRef = BECD983F24336DD400016117 /* device-tablet.css */; };
 		BEFB1EE121C29CC70081D757 /* L10n.mm in Sources */ = {isa = PBXBuildFile; fileRef = BEFB1EE021C29CC70081D757 /* L10n.mm */; };
@@ -1054,6 +1055,8 @@
 		BEB6524E216FD0CA00B8C09A /* stream.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = stream.cxx; path = "../../ios-device/tools/source/stream/stream.cxx"; sourceTree = "<group>"; };
 		BEB6524F216FD0CA00B8C09A /* strmwnt.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = strmwnt.cxx; path = "../../ios-device/tools/source/stream/strmwnt.cxx"; sourceTree = "<group>"; };
 		BEB65250216FD0CA00B8C09A /* strmunx.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = strmunx.cxx; path = "../../ios-device/tools/source/stream/strmunx.cxx"; sourceTree = "<group>"; };
+		BEBF3EAE246EB1C800415E87 /* RequestDetails.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RequestDetails.hpp; sourceTree = "<group>"; };
+		BEBF3EAF246EB1C800415E87 /* RequestDetails.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RequestDetails.cpp; sourceTree = "<group>"; };
 		BECBD30323D7152900DA5582 /* SlsFramePainter.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SlsFramePainter.cxx; path = "../../ios-device/sd/source/ui/slidesorter/view/SlsFramePainter.cxx"; sourceTree = "<group>"; };
 		BECBD30423D7152900DA5582 /* SlideSorterView.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SlideSorterView.cxx; path = "../../ios-device/sd/source/ui/slidesorter/view/SlideSorterView.cxx"; sourceTree = "<group>"; };
 		BECBD30523D7152900DA5582 /* SlsFramePainter.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = SlsFramePainter.hxx; path = "../../ios-device/sd/source/ui/slidesorter/view/SlsFramePainter.hxx"; sourceTree = "<group>"; };
@@ -1903,6 +1906,8 @@
 		BE5EB5B7213FE21600E0826C /* wsd */ = {
 			isa = PBXGroup;
 			children = (
+				BEBF3EAF246EB1C800415E87 /* RequestDetails.cpp */,
+				BEBF3EAE246EB1C800415E87 /* RequestDetails.hpp */,
 				BE5EB5CC213FE2D000E0826C /* ClientSession.cpp */,
 				BE5EB5D321400DC100E0826C /* DocumentBroker.cpp */,
 				BE484B71228D8622001EE76C /* DocumentBroker.hpp */,
@@ -3033,6 +3038,7 @@
 				BE5EB5D421400DC100E0826C /* DocumentBroker.cpp in Sources */,
 				BEA28377214FFD8C00848631 /* Unit.cpp in Sources */,
 				BE5EB5CF213FE2D000E0826C /* ClientSession.cpp in Sources */,
+				BEBF3EB0246EB1C800415E87 /* RequestDetails.cpp in Sources */,
 				BE5EB5C2213FE29900E0826C /* SpookyV2.cpp in Sources */,
 				BE8D77402136762600AC58EA /* main.m in Sources */,
 				BE5EB5C8213FE29900E0826C /* FileUtil.cpp in Sources */,
commit d6b8a37eb103fe8457e173458cdce670fda9ec45
Author:     Christian Lohmaier <lohmaier+github at googlemail.com>
AuthorDate: Fri May 15 12:50:33 2020 +0200
Commit:     Thorsten Behrens <Thorsten.Behrens at CIB.de>
CommitDate: Fri May 15 22:30:14 2020 +0200

    set INSTDIR again in and check for dockerfile in proper location
    
    f'up fix to 607ddbff466d145d0dcf87b2759c7e4d02b28f1d
    
    Change-Id: Idbb5191afcb9baab5c58e43e1b2511493491f029
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94291
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/docker/l10n-docker-nightly.sh b/docker/l10n-docker-nightly.sh
index e921a4283..dcd72b0ee 100755
--- a/docker/l10n-docker-nightly.sh
+++ b/docker/l10n-docker-nightly.sh
@@ -53,6 +53,7 @@ echo "LibreOffice build target: '$LIBREOFFICE_BUILD_TARGET'"
 
 
 SRCDIR=$(realpath `dirname $0`)
+INSTDIR="$SRCDIR/instdir"
 
 if [ -z "$(lsb_release -si)" ]; then
 	echo "WARNING: Unable to determine your distribution"
@@ -62,7 +63,7 @@ if [ -z "$(lsb_release -si)" ]; then
 else
 	HOST_OS=$(lsb_release -si)
 fi
-if ! [ -e "$HOST_OS" ]; then
+if ! [ -e "$SRCDIR/$HOST_OS" ]; then
   echo "There is no suitable Dockerfile for your host system."
   echo "Please fix this problem and re-run $0"
   exit 1
commit 53e887d193c5361c4152dcee55a1a43271c518f8
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Fri May 15 15:41:50 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Fri May 15 16:13:15 2020 +0200

    cypress: enable some more tests.
    
    Change-Id: I9e6e03e8fb082cfbb942b5af9a33e5b0a0037770
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94311
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
index a3b5c0165..6d854372d 100644
--- a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
+++ b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
@@ -219,8 +219,7 @@ describe('Change alignment settings.', function() {
 			});
 	});
 
-	it.skip('Change text indent via input field.', function() {
-		// TODO: this fails, because the input field always becomes disabled.
+	it('Change text indent via input field.', function() {
 		helper.initAliasToNegative('originalTextPos');
 
 		getTextPosForFirstCell();
@@ -233,11 +232,6 @@ describe('Change alignment settings.', function() {
 		openAlignmentPaneForFirstCell();
 
 		// TODO: First we need to increase indent to make the input enabled
-		cy.get('#IncrementIndent')
-			.click();
-
-		cy.wait(300);
-
 		cy.get('#IncrementIndent')
 			.click();
 
diff --git a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
index d54869074..5ed397d1d 100644
--- a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
+++ b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
@@ -113,7 +113,7 @@ describe('Change shape properties via mobile wizard.', function() {
 			.should('have.attr', 'd', 'M 1965,4863 L 7957,18073 1965,18073 1965,4863 1965,4863 Z');
 	});
 
-	it.skip('Change size with keep ratio enabled.', function() {
+	it('Change size with keep ratio enabled.', function() {
 		// TODO: Entering a value inside the spinbutton has no effect on the shape.
 		if (Cypress.env('LO_CORE_VERSION') === 'master')
 			return;
commit 262e9f94753994f31c22bfbc98c8b25a8ce1b043
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Fri May 15 11:56:03 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Fri May 15 15:27:58 2020 +0200

    cypress: simplify clickFormulaBar() method.
    
    It's always called without arguments.
    
    Change-Id: I3f825b23c4e8b0079019dc916a8357ba94cda4f1
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94289
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/cypress_test/integration_tests/common/calc.js b/cypress_test/integration_tests/common/calc.js
index 1ed2ef289..1e148d2fa 100644
--- a/cypress_test/integration_tests/common/calc.js
+++ b/cypress_test/integration_tests/common/calc.js
@@ -1,9 +1,9 @@
 /* global cy expect */
 
 // Click on the formula bar.
-// moveMouse is set to avoid leaving the mouse on the Formula-Bar,
+// mouseover is triggered to avoid leaving the mouse on the Formula-Bar,
 // which shows the tooltip and messes up tests.
-function clickFormulaBar(XPos = -1, moveMouse = true) {
+function clickFormulaBar() {
 
 	// The inputbar_container is 100% width, which
 	// can extend behind the sidebar. So we can't
@@ -14,15 +14,13 @@ function clickFormulaBar(XPos = -1, moveMouse = true) {
 	cy.get('.inputbar_canvas')
 		.then(function(items) {
 			expect(items).to.have.lengthOf(1);
-			if (XPos < 0) // Click in the center if undefined.
-				XPos = items[0].getBoundingClientRect().width / 2;
+			var XPos = items[0].getBoundingClientRect().width / 2;
 			var YPos = items[0].getBoundingClientRect().height / 2;
 			cy.get('.inputbar_container')
 				.click(XPos, YPos);
 		});
 
-	if (moveMouse)
-		cy.get('body').trigger('mouseover');
+	cy.get('body').trigger('mouseover');
 }
 
 // Click on the first cell.
diff --git a/cypress_test/integration_tests/mobile/calc/focus_spec.js b/cypress_test/integration_tests/mobile/calc/focus_spec.js
index 8ed94512c..e13bf6155 100644
--- a/cypress_test/integration_tests/mobile/calc/focus_spec.js
+++ b/cypress_test/integration_tests/mobile/calc/focus_spec.js
@@ -95,7 +95,6 @@ describe('Calc focus tests', function() {
 
 		// Click in the formula-bar.
 		calc.clickFormulaBar();
-		cy.get('body').trigger('mouseover');
 		helper.assertCursorAndFocus();
 
 		// Type some text.
@@ -110,7 +109,6 @@ describe('Calc focus tests', function() {
 
 		// Check the text we typed.
 		calc.clickFormulaBar();
-		cy.get('body').trigger('mouseover');
 		helper.assertCursorAndFocus();
 		cy.get('textarea.clipboard').type('{ctrl}a');
 		helper.expectTextForClipboard(text1);
@@ -123,7 +121,6 @@ describe('Calc focus tests', function() {
 		cy.log('Appending text at the end.');
 		calc.clickOnFirstCell();
 		calc.clickFormulaBar();
-		cy.get('body').trigger('mouseover');
 		helper.assertCursorAndFocus();
 		var text2 = ', this is a test.';
 		cy.get('textarea.clipboard').type(text2);
@@ -138,7 +135,6 @@ describe('Calc focus tests', function() {
 		cy.log('Inserting text in the middle.');
 		calc.clickOnFirstCell();
 		calc.clickFormulaBar();
-		cy.get('body').trigger('mouseover');
 		helper.assertCursorAndFocus();
 
 		// Move cursor before text2
commit 2598417df711f8647cfe2c1932fcb3154836d443
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Fri May 15 11:57:29 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Fri May 15 15:27:47 2020 +0200

    cypress: reenable checkboxes related tests.
    
    This reverts commit e07a94abc1ed844334040acf21c1085a074e418a.
    
    Change-Id: I9b1cd05f8ad6acc065c941cd17d886ead75f12be
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94290
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
index 077b1be3b..a3b5c0165 100644
--- a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
+++ b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
@@ -268,7 +268,7 @@ describe('Change alignment settings.', function() {
 			});
 	});
 
-	it.skip('Enable text wrapping.', function() {
+	it('Enable text wrapping.', function() {
 		helper.initAliasToNegative('originalTextPos');
 
 		getTextPosForFirstCell();
@@ -322,7 +322,7 @@ describe('Change alignment settings.', function() {
 		// neither the text position nor the clipboard container helps here.
 	});
 
-	it.skip('Merge cells.', function() {
+	it('Merge cells.', function() {
 		// Select the full row
 		cy.get('.spreadsheet-header-rows')
 			.then(function(items) {
diff --git a/cypress_test/integration_tests/mobile/calc/number_format_spec.js b/cypress_test/integration_tests/mobile/calc/number_format_spec.js
index 39d3b2f91..efd7576d7 100644
--- a/cypress_test/integration_tests/mobile/calc/number_format_spec.js
+++ b/cypress_test/integration_tests/mobile/calc/number_format_spec.js
@@ -385,7 +385,7 @@ describe('Apply number formatting.', function() {
 			.should('have.text', '001000');
 	});
 
-	it.skip('Apply red color for negative numbers.', function() {
+	it('Apply red color for negative numbers.', function() {
 		// Check default value
 		cy.get('#negativenumbersred input')
 			.should('not.have.prop', 'checked', true);
@@ -406,7 +406,7 @@ describe('Apply number formatting.', function() {
 			.should('have.text', '1000');
 	});
 
-	it.skip('Add thousands separator.', function() {
+	it('Add thousands separator.', function() {
 		// Check default value
 		cy.get('#thousandseparator input')
 			.should('not.have.prop', 'checked', true);
commit ee9942338740d1431e578149591641ea25042d27
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Fri May 15 13:56:04 2020 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Fri May 15 14:00:56 2020 +0300

    Add an "export options" plist for providing .ipa archives
    
    Change-Id: If8309bf2548290ef9e4edd15eea2287253ffde17

diff --git a/ios/IPAExportOptions.plist b/ios/IPAExportOptions.plist
new file mode 100644
index 000000000..72710c767
--- /dev/null
+++ b/ios/IPAExportOptions.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?> <!-- -*- Mode: nXML; tab-width: 8; indent-tabs-mode: nil; nxml-child-indent:2; fill-column: 100 -*- -->
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+    <key>teamID</key>
+    <string>J4FQ687VJK</string>
+  </dict>
+</plist>
commit ef2304b8e003890aa0a47f8090489c826b840ada
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Thu May 14 19:21:51 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Fri May 15 11:05:01 2020 +0200

    cypress: reenable fixed test cases.
    
    Change-Id: I5820c8ea314b1c9292e69bab3c147fe2e9e77945
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94252
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
index d111f59b1..d54869074 100644
--- a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
+++ b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
@@ -211,7 +211,7 @@ describe('Change shape properties via mobile wizard.', function() {
 			.should('have.attr', 'stroke', 'rgb(152,0,0)');
 	});
 
-	it.skip('Change line style', function() {
+	it('Change line style', function() {
 		// TODO: Layout of the line properties panel is completely broken.
 		if (Cypress.env('LO_CORE_VERSION') === 'master')
 			return;
diff --git a/cypress_test/integration_tests/mobile/writer/table_properties_spec.js b/cypress_test/integration_tests/mobile/writer/table_properties_spec.js
index e979b226f..28a792330 100644
--- a/cypress_test/integration_tests/mobile/writer/table_properties_spec.js
+++ b/cypress_test/integration_tests/mobile/writer/table_properties_spec.js
@@ -284,8 +284,7 @@ describe('Change table properties / layout via mobile wizard.', function() {
 		helper.afterAll('table_properties.odt');
 	});
 
-	// Regression here: we can't enter value directly into the spinfield
-	it.skip('Change row height.', function() {
+	it('Change row height.', function() {
 		// TODO: Select all does not work with core/master
 		// Table panel layout is also broken
 		if (Cypress.env('LO_CORE_VERSION') === 'master')
@@ -315,8 +314,7 @@ describe('Change table properties / layout via mobile wizard.', function() {
 		helper.afterAll('table_properties.odt');
 	});
 
-	// Regression here: we can't enter value directly into the spinfield
-	it.skip('Change column width.', function() {
+	it('Change column width.', function() {
 		// TODO: Select all does not work with core/master
 		// Table panel layout is also broken
 		if (Cypress.env('LO_CORE_VERSION') === 'master')
commit eacf089363dbbecfb804c4c68ca27c1682b61b35
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri May 15 10:05:19 2020 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri May 15 10:06:58 2020 +0200

    libfuzzer: add initial documentation
    
    So that one does not have to search back the commit messages to get the
    commandlines to run these.
    
    Change-Id: I3acfc0fa5b38577f22f6248a8ae0705e6af68940

diff --git a/fuzzer/README b/fuzzer/README
new file mode 100644
index 000000000..de16da5cb
--- /dev/null
+++ b/fuzzer/README
@@ -0,0 +1,20 @@
+These fuzzers are meant to be built and executed inside lode.git's sanitizers
+environment (currently enables both asan and ubsan).
+
+online.git can be built the usual way, just the additional `--enable-fuzzers`
+flag is needed to build the fuzzers. It is useful to do this in a separate
+build tree, since the fuzzers config doesn't produce a `loolwsd` binary.
+
+Run the fuzzers like this:
+
+- Admin:
+
+----
+./admin_fuzzer -max_len=16384 fuzzer/admin-data/
+----
+
+- ClientSession:
+
+----
+./clientsession_fuzzer -max_len=16384 fuzzer/data/
+----
commit 3bb8754ef1b8208f7594aaa35364980662ee3dce
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu May 14 14:33:46 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu May 14 18:12:22 2020 +0200

    Welcome: include the host + service-root.
    
    Also fixes proxy usage.
    
    Change-Id: I118101077b6294c3720af47f0188326ace9bbc09
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94212
    Tested-by: Jenkins
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index a1bc47d8d..8736ed0af 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -414,7 +414,7 @@ L.Map.include({
 
 	showWelcomeDialog: function(calledFromMenu) {
 		console.log('showWelcomeDialog, calledFromMenu: ' + calledFromMenu);
-		var welcomeLocation = 'welcome/welcome-' + String.locale + '.html';
+		var welcomeLocation = window.host + window.serviceRoot + 'welcome/welcome-' + String.locale + '.html';
 
 		var map = this;
 
commit e07a94abc1ed844334040acf21c1085a074e418a
Author:     Tamás Zolnai <tamas.zolnai at collabora.com>
AuthorDate: Thu May 14 17:43:32 2020 +0200
Commit:     Tamás Zolnai <tamas.zolnai at collabora.com>
CommitDate: Thu May 14 18:04:55 2020 +0200

    cypress: it seems checkboxes on mobile wizard does not work anymore.
    
    Change-Id: I08f8ba69e37b0c396d0a995d633bcf58de7096f5
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94238
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tamás Zolnai <tamas.zolnai at collabora.com>

diff --git a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
index a3b5c0165..077b1be3b 100644
--- a/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
+++ b/cypress_test/integration_tests/mobile/calc/alignment_options_spec.js
@@ -268,7 +268,7 @@ describe('Change alignment settings.', function() {
 			});
 	});
 
-	it('Enable text wrapping.', function() {
+	it.skip('Enable text wrapping.', function() {
 		helper.initAliasToNegative('originalTextPos');
 
 		getTextPosForFirstCell();
@@ -322,7 +322,7 @@ describe('Change alignment settings.', function() {
 		// neither the text position nor the clipboard container helps here.
 	});
 
-	it('Merge cells.', function() {
+	it.skip('Merge cells.', function() {
 		// Select the full row
 		cy.get('.spreadsheet-header-rows')
 			.then(function(items) {
diff --git a/cypress_test/integration_tests/mobile/calc/number_format_spec.js b/cypress_test/integration_tests/mobile/calc/number_format_spec.js
index efd7576d7..39d3b2f91 100644
--- a/cypress_test/integration_tests/mobile/calc/number_format_spec.js
+++ b/cypress_test/integration_tests/mobile/calc/number_format_spec.js
@@ -385,7 +385,7 @@ describe('Apply number formatting.', function() {
 			.should('have.text', '001000');
 	});
 
-	it('Apply red color for negative numbers.', function() {
+	it.skip('Apply red color for negative numbers.', function() {
 		// Check default value
 		cy.get('#negativenumbersred input')
 			.should('not.have.prop', 'checked', true);
@@ -406,7 +406,7 @@ describe('Apply number formatting.', function() {
 			.should('have.text', '1000');
 	});
 
-	it('Add thousands separator.', function() {
+	it.skip('Add thousands separator.', function() {
 		// Check default value
 		cy.get('#thousandseparator input')
 			.should('not.have.prop', 'checked', true);
commit 6063cfc2266c512067abf2c9aec2e89dc35f70d1
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu May 14 14:50:29 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu May 14 16:51:11 2020 +0200

    Mend error substitution, jsstring.replace() doesn't replace in-place.
    
    Change-Id: I9286779c6f25499aeb390f1c8346fc15574b8c8c
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94217
    Tested-by: Michael Meeks <michael.meeks at collabora.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/loleaflet/src/map/handler/Map.FileInserter.js b/loleaflet/src/map/handler/Map.FileInserter.js
index 94e28e2aa..be1f4e922 100644
--- a/loleaflet/src/map/handler/Map.FileInserter.js
+++ b/loleaflet/src/map/handler/Map.FileInserter.js
@@ -96,7 +96,7 @@ L.Map.FileInserter = L.Handler.extend({
 			var errMsg =  _('The file of type: %0 cannot be uploaded to server since the file has no name');
 			if (file.size === 0)
 				errMsg = _('The file of type: %0 cannot be uploaded to server since the file is empty');
-			errMsg.replace('%0', file.type);
+			errMsg = errMsg.replace('%0', file.type);
 			map.fire('error', {msg: errMsg});
 			return;
 		}
@@ -141,7 +141,7 @@ L.Map.FileInserter = L.Handler.extend({
 					}
 					else {
 						var msg = _('Uploading file to server failed with status: %0');
-						msg.replace('%0', xmlHttp.status);
+						msg = msg.replace('%0', xmlHttp.status);
 						map.fire('error', {msg: msg});
 					}
 				}
commit f65c8ef71fdabd41add558c706154334cfd3ed00
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu May 14 12:50:17 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu May 14 16:49:48 2020 +0200

    Avoid exception during racy startup.
    
    TypeError: Cannot read property '_updateCursorAndOverlay' of undefined
    
    Change-Id: Id6d5ed3df44bb04f9ed456974ee800ed564f1b4a
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94195
    Tested-by: Jenkins
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 3eb011500..670e625c7 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -1387,7 +1387,8 @@ L.Map = L.Evented.extend({
 		if (this.editorHasFocus()) {
 			// The document has the focus.
 			var doclayer = this._docLayer;
-			doclayer._updateCursorAndOverlay();
+			if (doclayer)
+				doclayer._updateCursorAndOverlay();
 		} else if (acceptInput !== undefined) {
 			// A dialog has the focus.
 			this.focus(acceptInput);
commit cd0ae0df7001ab9d33cf51bfbc5d5cf054d8367d
Author:     Pedro Pinto Silva <pedro.silva at collabora.com>
AuthorDate: Thu May 14 14:39:00 2020 +0200
Commit:     Pedro Pinto da Silva <pedro.silva at collabora.com>
CommitDate: Thu May 14 15:40:07 2020 +0200

    Fix indentation markers:
    
    - SVG markup: Convert polygon to path
    - SVG markup: remove xml transformations
    - SVG markup: optimize svg
    - increase border so it's visible at 100%
    - modify border color (so it's not 100% black)
    
    Change-Id: Ie0237793ea59b6877013c8f0bca37b73053c89d3
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94198
    Tested-by: Pedro Pinto da Silva <pedro.silva at collabora.com>
    Reviewed-by: Pedro Pinto da Silva <pedro.silva at collabora.com>

diff --git a/loleaflet/images/indentation_marker_down.svg b/loleaflet/images/indentation_marker_down.svg
index 9dd7fa373..10980cd82 100644
--- a/loleaflet/images/indentation_marker_down.svg
+++ b/loleaflet/images/indentation_marker_down.svg
@@ -1,13 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="14" height="10" version="1.1" viewBox="0 0 14 10" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata>
-  <rdf:RDF>
-   <cc:Work rdf:about="">
-    <dc:format>image/svg+xml</dc:format>
-    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
-    <dc:title/>
-   </cc:Work>
-  </rdf:RDF>
- </metadata>
- <polygon transform="matrix(.10796 0 0 .098871 -.55736 -.50999)" points="130 50 70 100 10 50 10 10 130 10" fill="#fff" stroke="#000" stroke-width="9.6788"/>
+<svg width="14" height="10" version="1.1" viewBox="0 0 14 10" xmlns="http://www.w3.org/2000/svg">
+ <path d="m0.67518 0.6752v3.7701l6.3248 4.7127 6.3248-4.7127v-3.7701z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fff" image-rendering="auto" shape-rendering="auto" solid-color="#000000" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;paint-order:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
+ <path d="m7 10-7-5.2168v-4.7832h14v4.7832zm0-1.6836 5.8-4.209v-2.9074h-11.6v2.9074z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#4d4d4d" image-rendering="auto" shape-rendering="auto" solid-color="#000000" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;paint-order:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
 </svg>
diff --git a/loleaflet/images/indentation_marker_up.svg b/loleaflet/images/indentation_marker_up.svg
index 2bb0a9baf..bbb67b2ae 100644
--- a/loleaflet/images/indentation_marker_up.svg
+++ b/loleaflet/images/indentation_marker_up.svg
@@ -1,13 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="14" height="10" version="1.1" viewBox="0 0 14 10" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata>
-  <rdf:RDF>
-   <cc:Work rdf:about="">
-    <dc:format>image/svg+xml</dc:format>
-    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
-    <dc:title/>
-   </cc:Work>
-  </rdf:RDF>
- </metadata>
- <polygon transform="matrix(-.10796 0 0 -.098871 14.557 10.51)" points="130 10 130 50 70 100 10 50 10 10" fill="#fff" stroke="#000" stroke-width="9.6788"/>
+<svg width="14" height="10" version="1.1" viewBox="0 0 14 10" xmlns="http://www.w3.org/2000/svg">
+ <path d="m0.67518 9.3248v-3.7701l6.3248-4.7127 6.3248 4.7127v3.7701z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fff" image-rendering="auto" shape-rendering="auto" solid-color="#000000" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;paint-order:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
+ <path d="m7 0-7 5.2168v4.7832h14v-4.7832zm0 1.6836 5.8 4.209v2.9074h-11.6v-2.9074z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#4d4d4d" image-rendering="auto" shape-rendering="auto" solid-color="#000000" stop-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;paint-order:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
 </svg>
commit 3ed7cceeb73248d2f610b24c3c454793b33e96b5
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Thu May 14 13:09:32 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Thu May 14 15:31:14 2020 +0200

    Show the welcome message at maximum once, unless requested.
    
    Change-Id: I2a8a7b53876e402102d5fce2b56da78edd709ad9
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94196
    Tested-by: Michael Meeks <michael.meeks at collabora.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index c30abc500..a1bc47d8d 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -416,8 +416,15 @@ L.Map.include({
 		console.log('showWelcomeDialog, calledFromMenu: ' + calledFromMenu);
 		var welcomeLocation = 'welcome/welcome-' + String.locale + '.html';
 
-		// try to load the welcome message
 		var map = this;
+
+		// if the user doesn't accept cookies, or we get several triggers,
+		// ensure we only ever do this once.
+		if (!calledFromMenu && map._alreadyShownWelcomeDialog)
+			return;
+		map._alreadyShownWelcomeDialog = true;
+
+		// try to load the welcome message
 		$.get(welcomeLocation)
 			.done(function(data) {
 				map._showWelcomeDialogVex(data, calledFromMenu);
commit 607ddbff466d145d0dcf87b2759c7e4d02b28f1d
Author:     Marco Marinello <marinello at libreoffice.org>
AuthorDate: Fri May 8 12:34:48 2020 +0200
Commit:     Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
CommitDate: Thu May 14 10:08:56 2020 +0200

    Enhance build system: support multiple OS as host and pull the image before build
    
    Change-Id: Ic9875dd1aa49335df83cc6a8492144450f725f31
    Signed-off-by: Marco Marinello <marinello at libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/92665
    Tested-by: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
    Reviewed-by: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>

diff --git a/docker/Debian b/docker/Debian
new file mode 100644
index 000000000..b43bc4a32
--- /dev/null
+++ b/docker/Debian
@@ -0,0 +1,46 @@
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FROM debian:stable
+
+# get the latest fixes
+RUN apt-get update
+
+# install LibreOffice run-time dependencies
+# install adduser, findutils, openssl and cpio that we need later
+# install an editor
+RUN apt-get -y install locales-all libpng16-16 fontconfig adduser cpio findutils nano libpoco-dev libcap2-bin openssl inotify-tools procps libubsan0 libubsan1 openssh-client
+
+# tdf#117557 - Add CJK Fonts to LibreOffice Online Docker Image
+RUN apt-get -y install fonts-wqy-zenhei fonts-wqy-microhei fonts-droid-fallback fonts-noto-cjk
+
+# copy freshly built LibreOffice master and LibreOffice Online master with latest translations
+COPY /instdir /
+
+# copy the shell script which can start LibreOffice Online (loolwsd)
+COPY /scripts/run-lool.sh /
+
+# set up LibreOffice Online (normally done by postinstall script of package)
+RUN setcap cap_fowner,cap_mknod,cap_sys_chroot=ep /usr/bin/loolforkit
+RUN adduser --quiet --system --group --home /opt/lool lool
+RUN mkdir -p /var/cache/loolwsd && chown lool: /var/cache/loolwsd
+RUN rm -rf /var/cache/loolwsd/*
+RUN rm -rf /opt/lool
+RUN mkdir -p /opt/lool/child-roots
+RUN loolwsd-systemplate-setup /opt/lool/systemplate /opt/libreoffice >/dev/null 2>&1
+RUN touch /var/log/loolwsd.log
+# Fix permissions
+RUN chown lool:lool /var/log/loolwsd.log
+RUN chown -R lool:lool /opt/
+RUN chown -R lool:lool /etc/loolwsd
+
+EXPOSE 9980
+
+# switch to lool user (use numeric user id to be compatible with Kubernetes Pod Security Policies)
+USER 101
+
+CMD bash /run-lool.sh
+
diff --git a/docker/Dockerfile b/docker/Ubuntu
similarity index 100%
rename from docker/Dockerfile
rename to docker/Ubuntu
diff --git a/docker/l10n-docker-nightly.sh b/docker/l10n-docker-nightly.sh
index 9bbb4ad4a..e921a4283 100755
--- a/docker/l10n-docker-nightly.sh
+++ b/docker/l10n-docker-nightly.sh
@@ -51,9 +51,22 @@ if [ -z "$LIBREOFFICE_BUILD_TARGET" ]; then
 fi;
 echo "LibreOffice build target: '$LIBREOFFICE_BUILD_TARGET'"
 
-# do everything in the builddir
+
 SRCDIR=$(realpath `dirname $0`)
-INSTDIR="$SRCDIR/instdir"
+
+if [ -z "$(lsb_release -si)" ]; then
+	echo "WARNING: Unable to determine your distribution"
+	echo "(Is lsb_release installed?)"
+	echo "Using Ubuntu Dockerfile."
+	HOST_OS="Ubuntu"
+else
+	HOST_OS=$(lsb_release -si)
+fi
+if ! [ -e "$HOST_OS" ]; then
+  echo "There is no suitable Dockerfile for your host system."
+  echo "Please fix this problem and re-run $0"
+  exit 1
+fi
 BUILDDIR="$SRCDIR/builddir"
 
 mkdir -p "$BUILDDIR"
@@ -104,7 +117,11 @@ cp -a libreoffice/instdir "$INSTDIR"/opt/libreoffice
 # Create new docker image
 if [ -z "$NO_DOCKER_IMAGE" ]; then
   cd "$SRCDIR"
-  docker build --no-cache -t $DOCKER_HUB_REPO:$DOCKER_HUB_TAG . || exit 1
+
+  # Pull the image first to be sure we're using the latest available
+  docker pull $(grep "FROM " $HOST_OS | cut -d ' ' -f 2)
+
+  docker build --no-cache -t $DOCKER_HUB_REPO:$DOCKER_HUB_TAG -f $HOST_OS . || exit 1
   if [ -z "$NO_DOCKER_PUSH" ]; then
     docker push $DOCKER_HUB_REPO:$DOCKER_HUB_TAG || exit 1
   fi;
commit 6ba706662739cc9ef029c13aef86d894ff371a37
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed May 13 17:21:07 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed May 13 19:48:31 2020 +0200

    Proxy: significantly simplify proxy socket.
    
    This avoids needing long-term wait sockets which consume server-side
    resources & can block other requests.
    
    Change-Id: I0909f49e16c5ce2315b9980cdf34fa4e370e3abc
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94150
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js
index f0556a5fb..279f2b7c9 100644
--- a/loleaflet/js/global.js
+++ b/loleaflet/js/global.js
@@ -220,7 +220,7 @@
 		this.sessionId = 'open';
 		this.id = window.proxySocketCounter++;
 		this.sendCounter = 0;
-		this.readWaiting = 0;
+		this.msgInflight = 0;
 		this.inSerial = 0;
 		this.outSerial = 0;
 		this.onclose = function() {
@@ -295,26 +295,34 @@
 			}
 		};
 		this.sendQueue = '';
-		this.sendTimeout = undefined;
 		this.doSend = function () {
-			console.debug('send msg "' + that.sendQueue + '"');
-			var req = new XMLHttpRequest();
-			req.open('POST', that.getEndPoint('write'));
 			if (that.sessionId === 'open')
+			{
 				console.debug('new session not completed');
-			else
+				return;
+			}
+			if (that.msgInflight >= 4) // something went badly wrong.
 			{
-				req.responseType = 'arraybuffer';
-				req.addEventListener('load', function() {
-					if (this.status == 200)
-						that.parseIncomingArray(new Uint8Array(this.response));
-					else
-						console.debug('proxy: error on incoming response');
-				});
+				console.debug('High latency connection - too much in-flight, pausing');
+				return;
 			}
+			console.debug('send msg - ' + that.msgInflight + ' on session ' +
+				      that.sessionId + '  queue: "' + that.sendQueue + '"');
+			var req = new XMLHttpRequest();
+			req.open('POST', that.getEndPoint('write'));
+			req.responseType = 'arraybuffer';
+			req.addEventListener('load', function() {
+				if (this.status == 200)
+					that.parseIncomingArray(new Uint8Array(this.response));
+				else
+					console.debug('proxy: error on incoming response');
+			});
+			req.addEventListener('loadend', function() {
+				that.msgInflight--;
+			});
 			req.send(that.sendQueue);
 			that.sendQueue = '';
-			that.sendTimeout = undefined;
+			that.msgInflight++;
 		};
 		this.getSessionId = function() {
 			var req = new XMLHttpRequest();
@@ -343,8 +351,6 @@
 				'B0x' + this.outSerial.toString(16) + '\n' +
 				'0x' + msg.length.toString(16) + '\n' + msg + '\n');
 			this.outSerial++;
-			if (this.sessionId !== 'open' && this.sendTimeout === undefined)
-				this.sendTimeout = setTimeout(this.doSend, 2 /* ms */);
 		};
 		this.sendCloseMsg = function(beacon) {
 			var url = that.getEndPoint('close');
@@ -378,32 +384,11 @@
 		// queue fetch of session id.
 		this.getSessionId();
 
-		// horrors ...
-		this.waitConnect = function() {
-			console.debug('proxy: waiting - ' + that.readWaiting + ' on session ' + that.sessionId);
-			if (that.readWaiting >= 4) // max 4 waiting connections concurrently.
-				return;
-			if (that.sessionId == 'open')
-				return; // waiting for our session id.
-			var req = new XMLHttpRequest();
-			// fetch session id:
-			req.addEventListener('load', function() {
-				if (this.status == 200)
-					that.parseIncomingArray(new Uint8Array(this.response));
-				else
-					console.debug('Handle error ' + this.status);
-			});
-			req.addEventListener('loadend', function() {
-				that.readWaiting--;
-				console.debug('proxy: wait ended, re-issue');
-				that.waitConnect();
-			});
-			req.open('GET', that.getEndPoint('wait'));
-			req.responseType = 'arraybuffer';
-			req.send('');
-			that.readWaiting++;
-		};
-		this.waitInterval = setInterval(this.waitConnect, 250);
+		// For those who think that long-running sockets are a
+		// better way to wait: you're so right. However, each
+		// consumes a scarce server worker thread while it waits,
+		// so ... back in the real world:
+		this.pollInterval = setInterval(this.doSend, 25);
 	};
 
 	if (global.socketProxy)
commit 42f833675744d8ddcd9e059de78546a278f7f6a0
Author:     Henry Castro <hcastro at collabora.com>
AuthorDate: Wed May 13 09:12:32 2020 -0400
Commit:     Henry Castro <hcastro at collabora.com>
CommitDate: Wed May 13 17:44:22 2020 +0200

    cypress: enable skipped unit test for "writer/shape_properties_spec.js"
    
    Enable unit test related to Spin button controls changes
    
    Change-Id: Ib2f0098d5d0b9f8b6eae28a4cce8680c6378e623
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94124
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Henry Castro <hcastro at collabora.com>

diff --git a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
index feb8582f3..d111f59b1 100644
--- a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
+++ b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js
@@ -81,11 +81,7 @@ describe('Change shape properties via mobile wizard.', function() {
 			.should('have.attr', 'fill', 'rgb(114,159,207)');
 	});
 
-	// Regression here: we can't enter value directly into the spinfield
-	it.skip('Change shape width.', function() {
-		// TODO: Entering a value inside the spinbutton has no effect on the shape.
-		if (Cypress.env('LO_CORE_VERSION') === 'master')
-			return;
+	it('Change shape width.', function() {
 
 		openPosSizePanel();
 
@@ -101,11 +97,7 @@ describe('Change shape properties via mobile wizard.', function() {
 			.should('have.attr', 'd', 'M 1965,4863 L 12635,10855 1965,10855 1965,4863 1965,4863 Z');
 	});
 
-	// Regression here: we can't enter value directly into the spinfield
-	it.skip('Change shape height.', function() {
-		// TODO: Entering a value inside the spinbutton has no effect on the shape.
-		if (Cypress.env('LO_CORE_VERSION') === 'master')
-			return;
+	it('Change shape height.', function() {
 
 		openPosSizePanel();
 
@@ -267,8 +259,7 @@ describe('Change shape properties via mobile wizard.', function() {
 			.should('have.attr', 'stroke-width', '88');
 	});
 
-	// Regression here: we can't enter value directly into the spinfield
-	it.skip('Change line transparency', function() {
+	it('Change line transparency', function() {
 		// TODO: Layout of the line properties panel is completely broken.
 		if (Cypress.env('LO_CORE_VERSION') === 'master')
 			return;
commit 580f06a21e045e8a732862542c5d729f02d18607
Author:     Henry Castro <hcastro at collabora.com>
AuthorDate: Tue May 12 15:31:10 2020 -0400
Commit:     Henry Castro <hcastro at collabora.com>
CommitDate: Wed May 13 17:24:38 2020 +0200

    cypress: logging the command state changes
    
    So when unit test fails, we can inspect the response
    from server some UNO command states
    
    Change-Id: Ic652918a8e57cadfd1cd58a89d7a1c9905abcfc9
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94081
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Henry Castro <hcastro at collabora.com>

diff --git a/cypress_test/integration_tests/common/helper.js b/cypress_test/integration_tests/common/helper.js
index 7a8c6d38c..8d68cfb38 100644
--- a/cypress_test/integration_tests/common/helper.js
+++ b/cypress_test/integration_tests/common/helper.js
@@ -1,5 +1,9 @@
 /* global cy Cypress*/
 
+function onCommandStateChanged(e) {
+	Cypress.log({ displayName: 'onCommandState', message: e.commandName + '=' + JSON.stringify(e.state)});
+}
+
 function loadTestDoc(fileName, subFolder, mobile) {
 	cy.log('Loading test document - start.');
 	cy.log('Param - fileName: ' + fileName);
@@ -47,7 +51,11 @@ function loadTestDoc(fileName, subFolder, mobile) {
 	cy.visit(URI, {
 		onLoad: function(win) {
 			win.onerror = cy.onUncaughtException;
-		}});
+		}})
+		.then(function(win) {
+			var map = win.L.Map.THIS;
+			map.on('commandstatechanged', onCommandStateChanged);
+		});
 	// Wait for the document to fully load
 	cy.get('.leaflet-tile-loaded', {timeout : 10000});
 
commit e6f5917812ea078bb8a2e279057648175e631b70
Author:     Henry Castro <hcastro at collabora.com>
AuthorDate: Tue May 12 15:29:08 2020 -0400
Commit:     Henry Castro <hcastro at collabora.com>
CommitDate: Wed May 13 16:51:29 2020 +0200

    cypress: fix error running single unit test
    
    builddir != srcdir
    
    Change-Id: I2521c3e11f05f0c91bf0e8f5667a5466493acac0
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94080
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Henry Castro <hcastro at collabora.com>

diff --git a/configure.ac b/configure.ac
index ae72b9d13..f3a13a3ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -999,6 +999,8 @@ AC_CONFIG_LINKS([cypress_test/package.json:cypress_test/package.json])
 AC_CONFIG_LINKS([cypress_test/cypress.json:cypress_test/cypress.json])
 AC_LINK_FILES([cypress_test/plugins], [cypress_test/plugins])
 AC_LINK_FILES([loleaflet/archived-packages], [loleaflet/archived-packages])
+AC_LINK_FILES([cypress_test/eslint_plugin], [cypress_test/eslint_plugin])
+AC_LINK_FILES([cypress_test/support], [cypress_test/support])
 
 APP_BRANDING_DIR=
 APP_IC_LAUNCHER="ic_launcher"
diff --git a/cypress_test/Makefile.am b/cypress_test/Makefile.am
index 50b2ae4c3..158016d5f 100644
--- a/cypress_test/Makefile.am
+++ b/cypress_test/Makefile.am
@@ -1,5 +1,7 @@
 if ENABLE_CYPRESS
 
+export NODE_PATH=$(abs_builddir)/node_modules
+
 abs_dir = $(if $(filter $(abs_builddir),$(abs_srcdir)),.,$(abs_srcdir))
 CYPRESS_BINARY = $(abs_builddir)/node_modules/cypress/bin/cypress
 ESLINT_BINARY = $(abs_builddir)/node_modules/eslint/bin/eslint.js
@@ -116,7 +118,7 @@ run-mobile: @JAILS_PATH@ $(NODE_BINS)
 define run_JS_error_check
 	@echo "Checking for JS errors in test code..."
 	@echo
-	@NODE_PATH=$(abs_dir)/node_modules $(NODE) $(ESLINT_BINARY) $(abs_srcdir) \
+	@$(NODE) $(ESLINT_BINARY) $(abs_srcdir) \
 		--ignore-path $(abs_srcdir)/.eslintignore --config $(abs_srcdir)/.eslintrc
 	@echo
 endef
commit d77cecee810542e743abd2efacb8df287c0729c5
Author:     Henry Castro <hcastro at collabora.com>
AuthorDate: Tue May 12 15:24:04 2020 -0400
Commit:     Henry Castro <hcastro at collabora.com>
CommitDate: Wed May 13 16:08:01 2020 +0200

    loleaflet: assign a static singleton map object
    
    So cypress can access the object and listen some events
    when unit test fails
    
    Change-Id: I8b05383a2655865b540306f83ebb5c5d42f1929c
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94079
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Henry Castro <hcastro at collabora.com>

diff --git a/loleaflet/src/main.js b/loleaflet/src/main.js
index 6b9390058..86826522b 100644
--- a/loleaflet/src/main.js
+++ b/loleaflet/src/main.js
@@ -87,5 +87,6 @@ window.addEventListener('beforeunload', function () {
 
 window.docPermission = permission;
 window.bundlejsLoaded = true;
+L.Map.THIS = map;
 
 }(window));
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 18b168a1e..3eb011500 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -14,6 +14,10 @@ function isAnyVexDialogActive() {
 /* global vex $ _ */
 L.Map = L.Evented.extend({
 
+	statics: {
+		THIS : undefined
+	},
+
 	options: {
 		crs: L.CRS.Simple,
 		center: [0, 0],
commit 5299ec381ce9e7df049bc16609f15af11d1c36b3
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Wed May 13 09:12:20 2020 +0200
Commit:     Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Wed May 13 10:05:11 2020 +0200

    make indent markers nicer, put update after _TSContainer change
    
    Make markers look more like in LO - smaller, more slick.
    Move the _updateParagraphIndentations after _TSContainer position
    is recalculated as we depend on that.
    
    Change-Id: I08034f6b5402a1532b6f74203a465164327f4593
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94095
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>

diff --git a/loleaflet/css/leaflet.css b/loleaflet/css/leaflet.css
index ec6a3d3de..ccf881de2 100644
--- a/loleaflet/css/leaflet.css
+++ b/loleaflet/css/leaflet.css
@@ -843,8 +843,8 @@ input.clipboard {
 	}
 
 .loleaflet-ruler-indentation-marker-down {
-	width: 17px;
-	height: 11px;
+	width: 14px;
+	height: 10px;
 	background: url(images/indentation_marker_down.svg);
 	position: absolute;
 	left: 0;
@@ -854,8 +854,8 @@ input.clipboard {
 }
 
 .loleaflet-ruler-indentation-marker-up {
-	width: 17px;
-	height: 11px;
+	width: 14px;
+	height: 10px;
 	background: url(images/indentation_marker_up.svg);
 	position: absolute;
 	left: 0;
diff --git a/loleaflet/images/indentation_marker_down.svg b/loleaflet/images/indentation_marker_down.svg
index 41e73bbeb..9dd7fa373 100644
--- a/loleaflet/images/indentation_marker_down.svg
+++ b/loleaflet/images/indentation_marker_down.svg
@@ -1,3 +1,13 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 140 110" >
-    <polygon points="10,10 130,10 130,50 70,100 10,50" />
-</svg>
\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="14" height="10" version="1.1" viewBox="0 0 14 10" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+    <dc:title/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <polygon transform="matrix(.10796 0 0 .098871 -.55736 -.50999)" points="130 50 70 100 10 50 10 10 130 10" fill="#fff" stroke="#000" stroke-width="9.6788"/>
+</svg>
diff --git a/loleaflet/images/indentation_marker_up.svg b/loleaflet/images/indentation_marker_up.svg
index 88f90c049..2bb0a9baf 100644
--- a/loleaflet/images/indentation_marker_up.svg
+++ b/loleaflet/images/indentation_marker_up.svg
@@ -1,3 +1,13 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 140 110" >
-    <polygon points="10,100 130,100 130,60 70,10 10,60" />
-</svg>
\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="14" height="10" version="1.1" viewBox="0 0 14 10" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+    <dc:title/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <polygon transform="matrix(-.10796 0 0 -.098871 14.557 10.51)" points="130 10 130 50 70 100 10 50 10 10" fill="#fff" stroke="#000" stroke-width="9.6788"/>
+</svg>
diff --git a/loleaflet/src/control/Ruler.js b/loleaflet/src/control/Ruler.js
index 98b65370e..a5537055e 100644
--- a/loleaflet/src/control/Ruler.js
+++ b/loleaflet/src/control/Ruler.js
@@ -220,9 +220,9 @@ L.Control.Ruler = L.Control.extend({
 
 		// Conversion to mm100.
 		if (this.options.indentUnit === 'inch') {
-			this.options.firstLineIndent = (this.options.firstLineIndent) * 2540;
-			this.options.leftParagraphIndent = (this.options.leftParagraphIndent) * 2540;
-			this.options.rightParagraphIndent = (this.options.rightParagraphIndent) * 2540;
+			this.options.firstLineIndent = this.options.firstLineIndent * 2540;
+			this.options.leftParagraphIndent = this.options.leftParagraphIndent * 2540;
+			this.options.rightParagraphIndent = this.options.rightParagraphIndent * 2540;
 		}
 
 		this.options.firstLineIndent *= pxPerMm100;
@@ -235,10 +235,9 @@ L.Control.Ruler = L.Control.extend({
 		var pEndPosition = this._rTSContainer.getBoundingClientRect().right - this.options.rightParagraphIndent;
 
 		// We calculated the positions. Now we should move them to left in order to make their sharp edge point to the right direction..
-		var halfWidth = (this._firstLineMarker.getBoundingClientRect().right - this._firstLineMarker.getBoundingClientRect().left) * 0.5;
-		this._firstLineMarker.style.left = (fLinePosition - halfWidth) + 'px';
-		this._pStartMarker.style.left = (pStartPosition - halfWidth) + 'px';
-		this._pEndMarker.style.left = (pEndPosition - halfWidth) + 'px';
+		this._firstLineMarker.style.left = (fLinePosition - (this._firstLineMarker.getBoundingClientRect().width / 2.0)) + 'px';
+		this._pStartMarker.style.left = (pStartPosition - (this._pStartMarker.getBoundingClientRect().width / 2.0)) + 'px';
+		this._pEndMarker.style.left = (pEndPosition - (this._pEndMarker.getBoundingClientRect().width / 2.0)) + 'px';
 
 		this._markerVerticalLine.style.top = this._rTSContainer.getBoundingClientRect().bottom + 'px';
 	},
@@ -263,8 +262,6 @@ L.Control.Ruler = L.Control.extend({
 		if (this.options.margin1 == null || this.options.margin2 == null)
 			return;
 
-		this._updateParagraphIndentations();
-
 		if (this._map._docLayer._annotations._items.length === 0
 		|| this._map._docLayer._annotations._items.length
 		=== this._map._docLayer._annotations._hiddenItems
@@ -388,6 +385,8 @@ L.Control.Ruler = L.Control.extend({
 		this._rTSContainer.style.left = (this.options.DraggableConvertRatio * lMargin) + 'px';
 		this._rTSContainer.style.right = (this.options.DraggableConvertRatio * rMargin) + 'px';
 
+		this._updateParagraphIndentations();
+
 		if (this.options.interactive) {
 			this._changeInteractions({perm:'edit'});
 		}
commit e600721abee7eb6aba7fab58fbcbe2e7910da1b4
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Tue May 12 23:52:25 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed May 13 02:01:59 2020 +0200

    Proxy: use much more obscure session IDs.
    
    Change-Id: I1220216b88aaa3c9a0bc58ed5bf4b20b4214d997
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94090
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 3991c3d92..369bed08d 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -35,6 +35,11 @@ using namespace LOOLProtocol;
 
 using Poco::Path;
 
+// rotates regularly
+const int ClipboardTokenLengthBytes = 16;
+// home-use, disabled by default.
+const int ProxyAccessTokenLengthBytes = 32;
+
 static std::mutex GlobalSessionMapMutex;
 static std::unordered_map<std::string, std::weak_ptr<ClientSession>> GlobalSessionMap;
 
@@ -188,7 +193,8 @@ void ClientSession::rotateClipboardKey(bool notifyClient)
         return;
 
     _clipboardKeys[1] = _clipboardKeys[0];
-    _clipboardKeys[0] = Util::rng::getHardRandomHexString(16);
+    _clipboardKeys[0] = Util::rng::getHardRandomHexString(
+        ClipboardTokenLengthBytes);
     LOG_TRC("Clipboard key on [" << getId() << "] set to " << _clipboardKeys[0] <<
             " last was " << _clipboardKeys[1]);
     if (notifyClient)
@@ -1719,7 +1725,8 @@ void ClientSession::dumpState(std::ostream& os)
        << "\n\t\tisTextDocument: " << _isTextDocument
        << "\n\t\tclipboardKeys[0]: " << _clipboardKeys[0]
        << "\n\t\tclipboardKeys[1]: " << _clipboardKeys[1]
-       << "\n\t\tclip sockets: " << _clipSockets.size();
+       << "\n\t\tclip sockets: " << _clipSockets.size()
+       << "\n\t\tproxy access:: " << _proxyAccess;
 
     if (_protocol)
     {
@@ -1733,6 +1740,14 @@ void ClientSession::dumpState(std::ostream& os)
 
 }
 
+const std::string &ClientSession::getOrCreateProxyAccess()
+{
+    if (_proxyAccess.size() <= 0)
+        _proxyAccess = Util::rng::getHardRandomHexString(
+            ProxyAccessTokenLengthBytes);
+    return _proxyAccess;
+}
+
 void ClientSession::handleTileInvalidation(const std::string& message,
     const std::shared_ptr<DocumentBroker>& docBroker)
 {
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 865649c30..c7d4e66ed 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -171,6 +171,9 @@ public:
     /// Generate and rotate a new clipboard hash, sending it if appropriate
     void rotateClipboardKey(bool notifyClient);
 
+    /// Generate an access token for this session via proxy protocol.
+    const std::string &getOrCreateProxyAccess();
+
 private:
     std::shared_ptr<ClientSession> client_from_this()
     {
@@ -282,8 +285,11 @@ private:
     /// Sockets to send binary selection content to
     std::vector<std::weak_ptr<StreamSocket>> _clipSockets;
 
-    ///Time when loading of view started
+    /// Time when loading of view started
     std::chrono::steady_clock::time_point _viewLoadStart;
+
+    /// Secure session id token for proxyprotocol authentication
+    std::string _proxyAccess;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp
index 7a5ef1b4c..9f25a47b4 100644
--- a/wsd/ProxyProtocol.cpp
+++ b/wsd/ProxyProtocol.cpp
@@ -55,17 +55,17 @@ void DocumentBroker::handleProxyRequest(
         LOOLWSD::checkDiskSpaceAndWarnClients(true);
         LOOLWSD::checkSessionLimitsAndWarnClients();
 
-        LOG_TRC("proxy: Returning sessionId " << clientSession->getId());
+        const std::string &sessionId = clientSession->getOrCreateProxyAccess();
+        LOG_TRC("proxy: Returning sessionId " << sessionId);
 
         std::ostringstream oss;
         oss << "HTTP/1.1 200 OK\r\n"
             "Last-Modified: " << Util::getHttpTimeNow() << "\r\n"
             "User-Agent: " WOPI_AGENT_STRING "\r\n"
-            "Content-Length: " << clientSession->getId().size() << "\r\n"
+            "Content-Length: " << sessionId.size() << "\r\n"
             "Content-Type: application/json\r\n"
             "X-Content-Type-Options: nosniff\r\n"
-            "\r\n"
-            << clientSession->getId();
+            "\r\n" << sessionId;
 
         socket->send(oss.str());
         socket->shutdown();
@@ -77,7 +77,7 @@ void DocumentBroker::handleProxyRequest(
         LOG_TRC("proxy: find session for " << _docKey << " with id " << sessionId);
         for (const auto &it : _sessions)
         {
-            if (it.second->getId() == sessionId)
+            if (it.second->getOrCreateProxyAccess() == sessionId)
             {
                 clientSession = it.second;
                 break;
commit 99d6282dde64b9528eec54bcd960ea547823571a
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Sat May 9 19:41:40 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed May 13 00:53:36 2020 +0200

    Proxy: re-arrange URL structure & document it better.
    
    Also implement 'close' during browser unload.
    
    Change-Id: Ie2072c14cf863876c633b3371b86016367ad4992
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94089
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js
index c5546f7c2..f0556a5fb 100644
--- a/loleaflet/js/global.js
+++ b/loleaflet/js/global.js
@@ -213,10 +213,11 @@
 		this.binaryType = 'arraybuffer';
 		this.bufferedAmount = 0;
 		this.extensions = '';
+		this.unloading = false;
 		this.protocol = '';
 		this.connected = true;
 		this.readyState = 0; // connecting
-		this.sessionId = 'fetchsession';
+		this.sessionId = 'open';
 		this.id = window.proxySocketCounter++;
 		this.sendCounter = 0;
 		this.readWaiting = 0;
@@ -299,9 +300,8 @@
 			console.debug('send msg "' + that.sendQueue + '"');
 			var req = new XMLHttpRequest();
 			req.open('POST', that.getEndPoint('write'));
-			req.setRequestHeader('SessionId', that.sessionId);
-			if (that.sessionId === 'fetchsession')
-				console.debug('session fetch not completed');
+			if (that.sessionId === 'open')
+				console.debug('new session not completed');
 			else
 			{
 				req.responseType = 'arraybuffer';
@@ -318,8 +318,7 @@
 		};
 		this.getSessionId = function() {
 			var req = new XMLHttpRequest();
-			req.open('POST', that.getEndPoint('write'));
-			req.setRequestHeader('SessionId', that.sessionId);
+			req.open('POST', that.getEndPoint('open'));
 			req.responseType = 'text';
 			req.addEventListener('load', function() {
 				console.debug('got session: ' + this.responseText);
@@ -344,19 +343,35 @@
 				'B0x' + this.outSerial.toString(16) + '\n' +
 				'0x' + msg.length.toString(16) + '\n' + msg + '\n');
 			this.outSerial++;
-			if (this.sessionId !== 'fetchsession' && this.sendTimeout === undefined)
+			if (this.sessionId !== 'open' && this.sendTimeout === undefined)
 				this.sendTimeout = setTimeout(this.doSend, 2 /* ms */);
 		};
+		this.sendCloseMsg = function(beacon) {
+			var url = that.getEndPoint('close');
+			if (!beacon)
+			{
+				var req = new XMLHttpRequest();
+				req.open('POST', url);
+				req.send('');
+			}
+			else
+				navigator.sendBeacon(url, '');
+		};
 		this.close = function() {
 			console.debug('proxy: close socket');
 			this.readyState = 3;
 			this.onclose();
 			clearInterval(this.waitInterval);
 			this.waitInterval = undefined;
+			this.sendCloseMsg(this.unloading);
+			this.sessionId = 'open';
+		};
+		this.setUnloading = function() {
+			this.unloading = true;
 		};
-		this.getEndPoint = function(type) {
+		this.getEndPoint = function(command) {
 			var base = this.uri;
-			return base + '/' + type + '/' + this.outSerial;
+			return base + '/' + this.sessionId + '/' + command + '/' + this.outSerial;
 		};
 		console.debug('proxy: new socket ' + this.id + ' ' + this.uri);
 
@@ -368,7 +383,7 @@
 			console.debug('proxy: waiting - ' + that.readWaiting + ' on session ' + that.sessionId);
 			if (that.readWaiting >= 4) // max 4 waiting connections concurrently.
 				return;
-			if (that.sessionId == 'fetchsession')
+			if (that.sessionId == 'open')
 				return; // waiting for our session id.
 			var req = new XMLHttpRequest();
 			// fetch session id:
@@ -384,7 +399,6 @@
 				that.waitConnect();
 			});
 			req.open('GET', that.getEndPoint('wait'));
-			req.setRequestHeader('SessionId', that.sessionId);
 			req.responseType = 'arraybuffer';
 			req.send('');
 			that.readWaiting++;
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index f6ba2a5c1..a5e394c67 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -104,6 +104,11 @@ L.Socket = L.Class.extend({
 		                                            120 * 1000);
 	},
 
+	setUnloading: function() {
+		if (this.socket.setUnloading)
+			this.socket.setUnloading();
+	},
+
 	close: function () {
 		this.socket.onerror = function () {};
 		this.socket.onclose = function () {};
diff --git a/loleaflet/src/main.js b/loleaflet/src/main.js
index 7f8fbb267..6b9390058 100644
--- a/loleaflet/src/main.js
+++ b/loleaflet/src/main.js
@@ -79,6 +79,8 @@ map.loadDocument(global.socket);
 global.socket = map._socket;
 window.addEventListener('beforeunload', function () {
 	if (map && map._socket) {
+		if (map._socket.setUnloading)
+			map._socket.setUnloading();
 		map._socket.close();
 	}
 });
diff --git a/net/Socket.hpp b/net/Socket.hpp
index c004a1748..967e7b8c6 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -385,7 +385,7 @@ public:
     /// Do some of the queued writing.
     virtual void performWrites() = 0;
 
-    /// Called when the is disconnected and will be destroyed.
+    /// Called when the socket is disconnected and will be destroyed.
     /// Will be called exactly once.
     virtual void onDisconnect() {}
 
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 939743fe4..17300d0cb 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -158,13 +158,11 @@ public:
 
     /// Find or create a new client session for the PHP proxy
     void handleProxyRequest(
-        const std::string& sessionId,
         const std::string& id,
         const Poco::URI& uriPublic,
         const bool isReadOnly,
         const RequestDetails &requestDetails,
-        const std::shared_ptr<StreamSocket> &socket,
-        bool isWaiting);
+        const std::shared_ptr<StreamSocket> &socket);
 
     /// Thread safe termination of this broker if it has a lingering thread
     void joinThread();
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 600c1e369..b03574724 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2909,11 +2909,7 @@ private:
                                   Poco::MemoryInputStream& message,
                                   SocketDisposition &disposition)
     {
-        if (!request.has("SessionId"))
-            throw BadRequestException("No session id header on proxied request");
-
         std::string url = requestDetails.getDocumentURI();
-        std::string sessionId = request.get("SessionId");
 
         LOG_INF("URL [" << url << "].");
         const auto uriPublic = DocumentBroker::sanitizeURI(url);
@@ -2943,15 +2939,12 @@ private:
         // Request a kit process for this doc.
         std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker(
             none, DocumentBroker::ChildType::Interactive, url, docKey, _id, uriPublic);
-
-        std::string fullURL = request.getURI();
-        std::string ending = "/ws/wait";
-        bool isWaiting = fullURL.find(ending) != std::string::npos;
         if (docBroker)
         {
             // need to move into the DocumentBroker context before doing session lookup / creation etc.
             std::string id = _id;
-            disposition.setMove([docBroker, id, uriPublic, isReadOnly, requestDetails, sessionId, isWaiting]
+            disposition.setMove([docBroker, id, uriPublic,
+                                 isReadOnly, requestDetails]
                                 (const std::shared_ptr<Socket> &moveSocket)
                 {
                     LOG_TRC("Setting up docbroker thread for " << docBroker->getDocKey());
@@ -2961,8 +2954,8 @@ private:
                     // We no longer own this socket.
                     moveSocket->setThreadOwner(std::thread::id());
 
-                    docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, requestDetails,
-                                            sessionId, moveSocket, isWaiting]()
+                    docBroker->addCallback([docBroker, id, uriPublic, isReadOnly,
+                                            requestDetails, moveSocket]()
                         {
                             // Now inside the document broker thread ...
                             LOG_TRC("In the docbroker thread for " << docBroker->getDocKey());
@@ -2971,8 +2964,8 @@ private:
                             try
                             {
                                 docBroker->handleProxyRequest(
-                                    sessionId, id, uriPublic, isReadOnly,
-                                    requestDetails, streamSocket, isWaiting);
+                                    id, uriPublic, isReadOnly,
+                                    requestDetails, streamSocket);
                                 return;
                             }
                             catch (const UnauthorizedRequestException& exc)
diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp
index 8784c39b1..7a5ef1b4c 100644
--- a/wsd/ProxyProtocol.cpp
+++ b/wsd/ProxyProtocol.cpp
@@ -7,6 +7,15 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+/*
+ * The ProxyProtocol creates a web-socket like connection over HTTP
+ * requests. URLs are formed like this:
+ *      0              1           2      3          4         5
+ *   /lool/<encoded-document-url>/ws/<session-id>/<command>/<serial>
+ * <session-id> can be 'unknown'
+ * <command> can be 'open', 'write', 'wait', or 'close'
+ */
+
 #include <config.h>
 
 #include "DocumentBroker.hpp"
@@ -20,18 +29,18 @@
 #include <cassert>
 
 void DocumentBroker::handleProxyRequest(
-    const std::string& sessionId,
     const std::string& id,
     const Poco::URI& uriPublic,
     const bool isReadOnly,
     const RequestDetails &requestDetails,
-    const std::shared_ptr<StreamSocket> &socket,
-    bool isWaiting)
+    const std::shared_ptr<StreamSocket> &socket)
 {
-    std::shared_ptr<ClientSession> clientSession;
-
+    const size_t session = 3;
+    const size_t command = 4;
+    const size_t serial = 5;
 
-    if (sessionId == "fetchsession")
+    std::shared_ptr<ClientSession> clientSession;
+    if (requestDetails.equals(command, "open"))
     {
         bool isLocal = socket->isLocal();
         LOG_TRC("proxy: validate that socket is from localhost: " << isLocal);
@@ -64,6 +73,7 @@ void DocumentBroker::handleProxyRequest(
     }
     else
     {
+        std::string sessionId = requestDetails[session];
         LOG_TRC("proxy: find session for " << _docKey << " with id " << sessionId);
         for (const auto &it : _sessions)
         {
@@ -87,9 +97,16 @@ void DocumentBroker::handleProxyRequest(
     // this DocumentBroker's poll handles reading & writing
     addSocketToPoll(socket);
 
-    auto proxy = std::static_pointer_cast<ProxyProtocolHandler>(
-        protocol);
+    auto proxy = std::static_pointer_cast<ProxyProtocolHandler>(protocol);
+    if (requestDetails.equals(command, "close"))
+    {
+        LOG_TRC("Close session");
+        proxy->notifyDisconnected();
+        return;
+    }
 
+    (void)serial; // in URL for logging, debugging, and uniqueness.
+    bool isWaiting = requestDetails.equals(command, "wait");
     proxy->handleRequest(isWaiting, socket);
 }
 
@@ -98,9 +115,11 @@ bool ProxyProtocolHandler::parseEmitIncoming(
 {
     std::vector<char> &in = socket->getInBuffer();
 
+#if 0 // protocol debugging.
     std::stringstream oss;
     socket->dumpState(oss);
     LOG_TRC("Parse message:\n" << oss.str());
+#endif
 
     while (in.size() > 0)
     {
@@ -219,6 +238,12 @@ void ProxyProtocolHandler::handleIncomingMessage(SocketDisposition &disposition)
     LOG_ERR("If you got here, it means we failed to parse this properly in handleRequest: " << oss.str());
 }
 
+void ProxyProtocolHandler::notifyDisconnected()
+{
+    if (_msgHandler)
+        _msgHandler->onDisconnect();
+}
+
 int ProxyProtocolHandler::sendMessage(const char *msg, const size_t len, bool text, bool flush)
 {
     _writeQueue.push_back(std::make_shared<Message>(msg, len, text, _outSerial++));
diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp
index 7bca66600..4a890d8f6 100644
--- a/wsd/ProxyProtocol.hpp
+++ b/wsd/ProxyProtocol.hpp
@@ -30,9 +30,7 @@ public:
     virtual ~ProxyProtocolHandler() { }
 
     /// Will be called exactly once by setHandler
-    void onConnect(const std::shared_ptr<StreamSocket>& /* socket */) override
-    {
-    }
+    void onConnect(const std::shared_ptr<StreamSocket>& /* socket */) override {}
 
     /// Called after successful socket reads.
     void handleIncomingMessage(SocketDisposition &/* disposition */) override;
@@ -61,10 +59,15 @@ public:
     void getIOStats(uint64_t &sent, uint64_t &recv) override;
     void dumpState(std::ostream& os) override;
     bool parseEmitIncoming(const std::shared_ptr<StreamSocket> &socket);
+
     void handleRequest(bool isWaiting, const std::shared_ptr<Socket> &socket);
 
+    /// tell our handler we've received a close.
+    void notifyDisconnected();
+
 private:
     std::shared_ptr<StreamSocket> popOutSocket();
+    /// can we find anything to send back if we try ?
     bool slurpHasMessages();
     int sendMessage(const char *msg, const size_t len, bool text, bool flush);
     bool flushQueueTo(const std::shared_ptr<StreamSocket> &socket);
commit 34fc7fb6b726dcdf0c40f19ed402c3728581d1a2
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Tue May 12 21:10:56 2020 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed May 13 00:53:24 2020 +0200

    Proxy: move requestDetails closer to ProxyProtocol.
    
    Change-Id: I07c00ea1dad15fd70b658a04f722cbd516fd5c18
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94088
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/fuzzer/ClientSession.cpp b/fuzzer/ClientSession.cpp
index edd4053d1..e24647315 100644
--- a/fuzzer/ClientSession.cpp
+++ b/fuzzer/ClientSession.cpp
@@ -22,9 +22,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
     std::shared_ptr<ProtocolHandlerInterface> ws;
     std::string id;
     bool isReadOnly = false;
-    const ServerURL serverURL;
+    const RequestDetails requestDetails("fuzzer");
     auto session
-        = std::make_shared<ClientSession>(ws, id, docBroker, uriPublic, isReadOnly, serverURL);
+        = std::make_shared<ClientSession>(ws, id, docBroker, uriPublic, isReadOnly, requestDetails);
 
     std::string input(reinterpret_cast<const char*>(data), size);
     std::stringstream ss(input);
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 41d9bbcd0..3991c3d92 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -44,7 +44,7 @@ ClientSession::ClientSession(
     const std::shared_ptr<DocumentBroker>& docBroker,
     const Poco::URI& uriPublic,
     const bool readOnly,
-    const ServerURL &serverURL) :
+    const RequestDetails &requestDetails) :
     Session(ws, "ToClient-" + id, id, readOnly),
     _docBroker(docBroker),
     _uriPublic(uriPublic),
@@ -58,7 +58,7 @@ ClientSession::ClientSession(
     _tileWidthTwips(0),
     _tileHeightTwips(0),
     _kitViewId(-1),
-    _serverURL(serverURL),
+    _serverURL(requestDetails),
     _isTextDocument(false)
 {
     const size_t curConnections = ++LOOLWSD::NumConnections;
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index a99832560..865649c30 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -34,7 +34,7 @@ public:
                   const std::shared_ptr<DocumentBroker>& docBroker,
                   const Poco::URI& uriPublic,
                   const bool isReadOnly,
-                  const ServerURL &serverURL);
+                  const RequestDetails &requestDetails);
     void construct();
     virtual ~ClientSession();
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 1cbfcac13..3293dea94 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1526,7 +1526,7 @@ std::shared_ptr<ClientSession> DocumentBroker::createNewClientSession(
     const std::string& id,
     const Poco::URI& uriPublic,
     const bool isReadOnly,
-    const ServerURL &serverURL)
+    const RequestDetails &requestDetails)
 {
     try
     {
@@ -1541,7 +1541,7 @@ std::shared_ptr<ClientSession> DocumentBroker::createNewClientSession(
         // In case of WOPI, if this session is not set as readonly, it might be set so
         // later after making a call to WOPI host which tells us the permission on files
         // (UserCanWrite param).
-        auto session = std::make_shared<ClientSession>(ws, id, shared_from_this(), uriPublic, isReadOnly, serverURL);
+        auto session = std::make_shared<ClientSession>(ws, id, shared_from_this(), uriPublic, isReadOnly, requestDetails);
         session->construct();
 
         return session;
@@ -2269,8 +2269,8 @@ bool ConvertToBroker::startConversion(SocketDisposition &disposition, const std:
     const bool isReadOnly = true;
     // FIXME: associate this with moveSocket (?)
     std::shared_ptr<ProtocolHandlerInterface> nullPtr;
-    ServerURL serverURL;
-    _clientSession = std::make_shared<ClientSession>(nullPtr, id, docBroker, getPublicUri(), isReadOnly, serverURL);
+    RequestDetails requestDetails("convert-to");
+    _clientSession = std::make_shared<ClientSession>(nullPtr, id, docBroker, getPublicUri(), isReadOnly, requestDetails);
     _clientSession->construct();
 
     if (!_clientSession)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index f086ddea6..939743fe4 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -97,7 +97,7 @@ private:
     int _smapsFD;
 };
 
-class ServerURL;
+class RequestDetails;
 class ClientSession;
 
 /// DocumentBroker is responsible for setting up a document in jail and brokering loading it from
@@ -154,7 +154,7 @@ public:
         const std::string& id,
         const Poco::URI& uriPublic,
         const bool isReadOnly,

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list