[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-1-9' - 93 commits - loleaflet/dist loleaflet/Makefile loleaflet/po loleaflet/README loleaflet/reference.html loleaflet/src loolwsd/ChildSession.cpp loolwsd/ChildSession.hpp loolwsd/ClientSession.cpp loolwsd/ClientSession.hpp loolwsd/configure.ac loolwsd/discovery.xml loolwsd/DocumentBroker.cpp loolwsd/DocumentBroker.hpp loolwsd/.gitignore loolwsd/lint-discovery.py loolwsd/LOOLForKit.cpp loolwsd/LOOLKit.cpp loolwsd/LOOLKit.hpp loolwsd/LOOLProtocol.cpp loolwsd/LOOLProtocol.hpp loolwsd/LOOLSession.hpp loolwsd/LOOLStress.cpp loolwsd/LOOLWSD.cpp loolwsd/MessageQueue.cpp loolwsd/MessageQueue.hpp loolwsd/PrisonerSession.cpp loolwsd/protocol.txt loolwsd/Storage.cpp loolwsd/test loolwsd/TileCache.cpp loolwsd/TileDesc.hpp loolwsd/Unit.cpp loolwsd/UserMessages.hpp loolwsd/Util.cpp loolwsd/Util.hpp

Andras Timar andras.timar at collabora.com
Tue Oct 11 11:55:20 UTC 2016


 loleaflet/Makefile                            |    2 
 loleaflet/README                              |    4 
 loleaflet/dist/toolbar/toolbar.js             |    6 
 loleaflet/po/help-da.po                       |    6 
 loleaflet/po/help-gd.po                       | 1023 ++++++++++++--------------
 loleaflet/po/help-gl.po                       |  787 +++++++++-----------
 loleaflet/po/help-kab.po                      |   14 
 loleaflet/po/help-lt.po                       |    6 
 loleaflet/po/help-oc.po                       |  675 ++++++++---------
 loleaflet/po/help-ro.po                       |   10 
 loleaflet/po/help-ta.po                       |  833 ++++++++++-----------
 loleaflet/po/help-uz.po                       |  114 +-
 loleaflet/po/help-vec.po                      |    8 
 loleaflet/po/templates/loleaflet-help.pot     |    2 
 loleaflet/po/templates/loleaflet-ui.pot       |  666 ++++++++++------
 loleaflet/po/ui-ca.po                         |   76 -
 loleaflet/po/ui-da.po                         |   64 -
 loleaflet/po/ui-fi.po                         |   68 -
 loleaflet/po/ui-gd.po                         |  149 +--
 loleaflet/po/ui-gl.po                         |  195 +---
 loleaflet/po/ui-lt.po                         |    6 
 loleaflet/po/ui-nl.po                         |   16 
 loleaflet/po/ui-nn.po                         |   43 -
 loleaflet/po/ui-oc.po                         |  211 +----
 loleaflet/po/ui-sk.po                         |   64 -
 loleaflet/po/ui-ta.po                         |  505 +++++-------
 loleaflet/po/ui-th.po                         |  263 ++----
 loleaflet/po/ui-vec.po                        |    8 
 loleaflet/reference.html                      |   21 
 loleaflet/src/control/Control.ColumnHeader.js |    4 
 loleaflet/src/control/Control.Menubar.js      |    8 
 loleaflet/src/control/Control.RowHeader.js    |    4 
 loleaflet/src/core/LOUtil.js                  |    8 
 loleaflet/src/core/Socket.js                  |    7 
 loleaflet/src/layer/tile/TileLayer.js         |  109 +-
 loleaflet/src/map/Map.js                      |   19 
 loolwsd/.gitignore                            |    1 
 loolwsd/ChildSession.cpp                      |   48 -
 loolwsd/ChildSession.hpp                      |   19 
 loolwsd/ClientSession.cpp                     |   20 
 loolwsd/ClientSession.hpp                     |   16 
 loolwsd/DocumentBroker.cpp                    |  114 ++
 loolwsd/DocumentBroker.hpp                    |   23 
 loolwsd/LOOLForKit.cpp                        |   12 
 loolwsd/LOOLKit.cpp                           |  411 ++++------
 loolwsd/LOOLKit.hpp                           |    3 
 loolwsd/LOOLProtocol.cpp                      |   43 -
 loolwsd/LOOLProtocol.hpp                      |   56 +
 loolwsd/LOOLSession.hpp                       |    2 
 loolwsd/LOOLStress.cpp                        |    5 
 loolwsd/LOOLWSD.cpp                           |  165 ++--
 loolwsd/MessageQueue.cpp                      |   52 +
 loolwsd/MessageQueue.hpp                      |    7 
 loolwsd/PrisonerSession.cpp                   |   33 
 loolwsd/Storage.cpp                           |   26 
 loolwsd/TileCache.cpp                         |   19 
 loolwsd/TileDesc.hpp                          |    1 
 loolwsd/Unit.cpp                              |    1 
 loolwsd/UserMessages.hpp                      |    2 
 loolwsd/Util.cpp                              |    2 
 loolwsd/Util.hpp                              |   38 
 loolwsd/configure.ac                          |    3 
 loolwsd/discovery.xml                         |  130 ++-
 loolwsd/lint-discovery.py                     |  246 ++++++
 loolwsd/protocol.txt                          |   51 -
 loolwsd/test/Makefile.am                      |   16 
 loolwsd/test/TileCacheTests.cpp               |  150 ++-
 loolwsd/test/TileQueueTests.cpp               |   83 +-
 loolwsd/test/UnitFonts.cpp                    |    4 
 loolwsd/test/UnitPrefork.cpp                  |   86 +-
 loolwsd/test/UnitTileCache.cpp                |   13 
 loolwsd/test/WhiteBoxTests.cpp                |    7 
 loolwsd/test/countloolkits.hpp                |   15 
 loolwsd/test/helpers.hpp                      |  187 ----
 loolwsd/test/httpcrashtest.cpp                |    2 
 loolwsd/test/httpwserror.cpp                  |    2 
 loolwsd/test/httpwstest.cpp                   |  720 ++++++++----------
 loolwsd/test/integration-http-server.cpp      |    1 
 loolwsd/test/test.cpp                         |    1 
 loolwsd/test/testlokit.cpp                    |  107 ++
 80 files changed, 4645 insertions(+), 4302 deletions(-)

New commits:
commit a66db5303ee4df8481705be03ebfb5e36d1c9363
Author: Andras Timar <andras.timar at collabora.com>
Date:   Tue Oct 11 13:54:54 2016 +0200

    Bump version to 1.9.4

diff --git a/loleaflet/Makefile b/loleaflet/Makefile
index 487fcb0..0a8cb52 100644
--- a/loleaflet/Makefile
+++ b/loleaflet/Makefile
@@ -3,7 +3,7 @@
 # ("micro") part: Between releases odd, even for releases (no other
 # changes inbetween).
 
-VERSION=1.9.3
+VERSION=1.9.4
 
 # Version number of the bundled 'draw' thing
 DRAW_VERSION=0.2.4
diff --git a/loolwsd/configure.ac b/loolwsd/configure.ac
index ec27a97..adf548a 100644
--- a/loolwsd/configure.ac
+++ b/loolwsd/configure.ac
@@ -3,7 +3,7 @@
 
 AC_PREREQ([2.69])
 
-AC_INIT([loolwsd], [1.9.3], [libreoffice at lists.freedesktop.org])
+AC_INIT([loolwsd], [1.9.4], [libreoffice at lists.freedesktop.org])
 LT_INIT([shared, disable-static, dlopen])
 
 AM_INIT_AUTOMAKE([1.11 silent-rules subdir-objects])
commit 4676f609dfdc77d1a28edbc261d5ed900caa4014
Author: Andras Timar <andras.timar at collabora.com>
Date:   Tue Oct 11 12:13:23 2016 +0200

    update pot files
    
    (cherry picked from commit 7ecf78a18e8ea9a7b809a89e2dec748aa64715b5)

diff --git a/loleaflet/po/templates/loleaflet-help.pot b/loleaflet/po/templates/loleaflet-help.pot
index 4226c27..b05eab5 100644
--- a/loleaflet/po/templates/loleaflet-help.pot
+++ b/loleaflet/po/templates/loleaflet-help.pot
@@ -3,7 +3,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-09 07:00+0000\n"
+"POT-Creation-Date: 2016-10-11 12:11+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
diff --git a/loleaflet/po/templates/loleaflet-ui.pot b/loleaflet/po/templates/loleaflet-ui.pot
index a5bb598..307e66f 100644
--- a/loleaflet/po/templates/loleaflet-ui.pot
+++ b/loleaflet/po/templates/loleaflet-ui.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-09 07:00+0000\n"
+"POT-Creation-Date: 2016-10-11 12:11+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -17,6 +17,92 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+#: admin.strings.js:5
+msgid "Admin console"
+msgstr ""
+
+#: admin.strings.js:6
+msgid "Toggle navigation"
+msgstr ""
+
+#: admin.strings.js:7
+msgid "Settings"
+msgstr ""
+
+#: admin.strings.js:8
+msgid "Overview"
+msgstr ""
+
+#: admin.strings.js:9
+msgid "(current)"
+msgstr ""
+
+#: admin.strings.js:10
+msgid "Analytics"
+msgstr ""
+
+#: admin.strings.js:11
+msgid "Dashboard"
+msgstr ""
+
+#: admin.strings.js:12
+msgid "Users online"
+msgstr ""
+
+#: admin.strings.js:13
+msgid "Documents opened"
+msgstr ""
+
+#: admin.strings.js:14
+msgid "Memory consumed"
+msgstr ""
+
+#: admin.strings.js:15
+msgid "PID"
+msgstr ""
+
+#: admin.strings.js:16
+msgid "Document"
+msgstr ""
+
+#: admin.strings.js:17
+msgid "Number of views"
+msgstr ""
+
+#: admin.strings.js:18
+msgid "Elapsed time"
+msgstr ""
+
+#: admin.strings.js:19
+msgid "Kill"
+msgstr ""
+
+#: admin.strings.js:20
+msgid "Graphs"
+msgstr ""
+
+#: admin.strings.js:21 dist/toolbar/toolbar.js:337
+#: src/control/Control.Menubar.js:10 src/control/Control.Menubar.js:63
+#: src/control/Control.Menubar.js:109
+msgid "Save"
+msgstr ""
+
+#: admin.strings.js:22
+msgid "Cache size of memory statistics"
+msgstr ""
+
+#: admin.strings.js:23
+msgid "Time interval of memory statistics (in ms)"
+msgstr ""
+
+#: admin.strings.js:24
+msgid "Cache size of CPU statistics"
+msgstr ""
+
+#: admin.strings.js:25
+msgid "Time interval of CPU statistics (in ms)"
+msgstr ""
+
 #: evol.colorpicker.strings.js:2
 msgid "Theme Colors"
 msgstr ""
@@ -85,6 +171,30 @@ msgstr ""
 msgid "Unformatted Text"
 msgstr ""
 
+#: unocommands.js:12
+msgid "No Wrap"
+msgstr ""
+
+#: unocommands.js:13
+msgid "Wrap Before"
+msgstr ""
+
+#: unocommands.js:14
+msgid "Wrap After"
+msgstr ""
+
+#: unocommands.js:15
+msgid "Enable Contour"
+msgstr ""
+
+#: unocommands.js:16
+msgid "Update Index or Table of Contents"
+msgstr ""
+
+#: unocommands.js:17
+msgid "Delete Index or Table of Contents"
+msgstr ""
+
 #: dist/errormessages.js:1
 msgid ""
 "Wrong WOPISrc, usage: WOPISrc=valid encoded URI, or file_path, usage: "
@@ -97,347 +207,387 @@ msgid ""
 "contact the administrator."
 msgstr ""
 
-#: dist/toolbar/toolbar.js:154
+#: dist/errormessages.js:3
+msgid ""
+"No disk space left on server, please contact the server administrator to "
+"continue."
+msgstr ""
+
+#: dist/errormessages.js:4
+msgid ""
+"This development build is limited to %0 documents, and %1 connections - to "
+"avoid the impression that it is suitable for deployment in large "
+"enterprises. To find out more about deploying and scaling %2 checkout: <br/"
+"><a href=\"%3\">%3</a>."
+msgstr ""
+
+#: dist/toolbar/toolbar.js:150
 msgid "Are you sure you want to delete this page?"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:329 src/admin/AdminStrings.js:20
-#: src/control/Control.Menubar.js:10 src/control/Control.Menubar.js:56
-#: src/control/Control.Menubar.js:102
-msgid "Save"
+#: dist/toolbar/toolbar.js:328
+msgid "Textwrap"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:331 src/control/Control.Menubar.js:18
-#: src/control/Control.Menubar.js:64 src/control/Control.Menubar.js:110
+#: dist/toolbar/toolbar.js:329
+msgid "No wrap"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:330
+msgid "Page wrap"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:331
+msgid "Wrap anchor only"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:332
+msgid "Ideal wrap"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:333
+msgid "Left wrap"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:334
+msgid "Right wrap"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:335
+msgid "Wrap through"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:339 src/control/Control.Menubar.js:19
+#: src/control/Control.Menubar.js:71 src/control/Control.Menubar.js:117
 msgid "Undo"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:332 src/control/Control.Menubar.js:19
-#: src/control/Control.Menubar.js:65 src/control/Control.Menubar.js:111
+#: dist/toolbar/toolbar.js:340 src/control/Control.Menubar.js:20
+#: src/control/Control.Menubar.js:72 src/control/Control.Menubar.js:118
 msgid "Redo"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:338
+#: dist/toolbar/toolbar.js:341
+msgid "Document repair"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:347
 msgid "Bold"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:339
+#: dist/toolbar/toolbar.js:348
 msgid "Italic"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:340
+#: dist/toolbar/toolbar.js:349
 msgid "Underline"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:341
+#: dist/toolbar/toolbar.js:350
 msgid "Strikeout"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:344
+#: dist/toolbar/toolbar.js:352
+msgid "Insert Footnote"
+msgstr ""
+
+#: dist/toolbar/toolbar.js:355
 msgid "Font color"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:346
+#: dist/toolbar/toolbar.js:357
 msgid "Highlighting"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:348
+#: dist/toolbar/toolbar.js:359
 msgid "Align left"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:349
+#: dist/toolbar/toolbar.js:360
 msgid "Center horizontally"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:350
+#: dist/toolbar/toolbar.js:361
 msgid "Align right"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:351
+#: dist/toolbar/toolbar.js:362
 msgid "Justified"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:353
+#: dist/toolbar/toolbar.js:364
 msgid "Bullets on/off"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:354
+#: dist/toolbar/toolbar.js:365
 msgid "Numbering on/off"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:356
+#: dist/toolbar/toolbar.js:367
 msgid "Increase indent"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:357
+#: dist/toolbar/toolbar.js:368
 msgid "Decrease indent"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:360
+#: dist/toolbar/toolbar.js:371
 msgid "Insert table"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:361
+#: dist/toolbar/toolbar.js:372
 msgid "Insert comment"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:362
+#: dist/toolbar/toolbar.js:373
 msgid "Insert graphic"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:364
+#: dist/toolbar/toolbar.js:375
 msgid "More"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:365
+#: dist/toolbar/toolbar.js:376
 msgid "Close document"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:395 dist/toolbar/toolbar.js:758
+#: dist/toolbar/toolbar.js:406 dist/toolbar/toolbar.js:776
 msgid "Sum"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:396
+#: dist/toolbar/toolbar.js:407
 msgid "Function"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:397
+#: dist/toolbar/toolbar.js:408
 msgid "Cancel"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:398
+#: dist/toolbar/toolbar.js:409
 msgid "Accept"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:409
+#: dist/toolbar/toolbar.js:420
 msgid "First sheet"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:410
+#: dist/toolbar/toolbar.js:421
 msgid "Previous sheet"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:411
+#: dist/toolbar/toolbar.js:422
 msgid "Next sheet"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:412
+#: dist/toolbar/toolbar.js:423
 msgid "Last sheet"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:422 src/control/Control.Menubar.js:94
+#: dist/toolbar/toolbar.js:433 src/control/Control.Menubar.js:101
 msgid "Fullscreen presentation"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:424
+#: dist/toolbar/toolbar.js:435
 msgid "Insert slide"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:425 src/control/Control.Menubar.js:91
+#: dist/toolbar/toolbar.js:436 src/control/Control.Menubar.js:98
 msgid "Duplicate slide"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:426 src/control/Control.Menubar.js:92
+#: dist/toolbar/toolbar.js:437 src/control/Control.Menubar.js:99
 msgid "Delete slide"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:438
+#: dist/toolbar/toolbar.js:450
 msgid "Search:"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:443
+#: dist/toolbar/toolbar.js:455
 msgid "Search backwards"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:444
+#: dist/toolbar/toolbar.js:456
 msgid "Search forward"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:445
+#: dist/toolbar/toolbar.js:457
 msgid "Cancel the search"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:450 dist/toolbar/toolbar.js:1196
-msgid "Take edit lock (others can only view)"
-msgstr ""
-
-#: dist/toolbar/toolbar.js:450 dist/toolbar/toolbar.js:1196
-msgid "VIEWING"
+#: dist/toolbar/toolbar.js:462 dist/toolbar/toolbar.js:1328
+msgid "No users"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:452
+#: dist/toolbar/toolbar.js:464
 msgid "Previous page"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:453
+#: dist/toolbar/toolbar.js:465
 msgid "Next page"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:455 src/control/Control.Menubar.js:34
-#: src/control/Control.Menubar.js:79 src/control/Control.Menubar.js:128
+#: dist/toolbar/toolbar.js:467 src/control/Control.Menubar.js:41
+#: src/control/Control.Menubar.js:86 src/control/Control.Menubar.js:135
 msgid "Reset zoom"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:456 src/control/Control.Menubar.js:33
-#: src/control/Control.Menubar.js:78 src/control/Control.Menubar.js:127
+#: dist/toolbar/toolbar.js:468 src/control/Control.Menubar.js:40
+#: src/control/Control.Menubar.js:85 src/control/Control.Menubar.js:134
 msgid "Zoom out"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:458 src/control/Control.Menubar.js:32
-#: src/control/Control.Menubar.js:77 src/control/Control.Menubar.js:126
+#: dist/toolbar/toolbar.js:470 src/control/Control.Menubar.js:39
+#: src/control/Control.Menubar.js:84 src/control/Control.Menubar.js:133
 msgid "Zoom in"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:476
-msgid "You are viewing now."
+#: dist/toolbar/toolbar.js:494
+msgid "%user has joined"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:476
-msgid "Click here to take edit."
+#: dist/toolbar/toolbar.js:495
+msgid "%user has left"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:607 dist/toolbar/toolbar.js:1094
+#: dist/toolbar/toolbar.js:625 dist/toolbar/toolbar.js:1117
 msgid "Size"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:739
+#: dist/toolbar/toolbar.js:757
 msgid "Number of Sheets"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:742
+#: dist/toolbar/toolbar.js:760
 msgid "Selected range of cells"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:745 dist/toolbar/toolbar.js:791
+#: dist/toolbar/toolbar.js:763 dist/toolbar/toolbar.js:809
 msgid "Entering text mode"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:748 dist/toolbar/toolbar.js:794
+#: dist/toolbar/toolbar.js:766 dist/toolbar/toolbar.js:812
 msgid "Selection Mode"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:751
+#: dist/toolbar/toolbar.js:769
 msgid "Choice of functions"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:753
+#: dist/toolbar/toolbar.js:771
 msgid "Average"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:754
+#: dist/toolbar/toolbar.js:772
 msgid "CountA"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:755
+#: dist/toolbar/toolbar.js:773
 msgid "Count"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:756
+#: dist/toolbar/toolbar.js:774
 msgid "Maximum"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:757
+#: dist/toolbar/toolbar.js:775
 msgid "Minimum"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:759
+#: dist/toolbar/toolbar.js:777
 msgid "Selection count"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:760
+#: dist/toolbar/toolbar.js:778
 msgid "None"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:765
+#: dist/toolbar/toolbar.js:783
 msgid "Wrap Text"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:766
+#: dist/toolbar/toolbar.js:784
 msgid "Merge and Center Cells"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:768
+#: dist/toolbar/toolbar.js:786
 msgid "Format as Currency"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:769
+#: dist/toolbar/toolbar.js:787
 msgid "Format as Percent"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:770
+#: dist/toolbar/toolbar.js:788
 msgid "Format as Number"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:771
+#: dist/toolbar/toolbar.js:789
 msgid "Format as Date"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:772
+#: dist/toolbar/toolbar.js:790
 msgid "Add Decimal Place"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:773
+#: dist/toolbar/toolbar.js:791
 msgid "Delete Decimal Place"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:775
+#: dist/toolbar/toolbar.js:793
 msgid "Sort Ascending"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:776
+#: dist/toolbar/toolbar.js:794
 msgid "Sort Descending"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:785
+#: dist/toolbar/toolbar.js:803
 msgid "Number of Pages"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:788
+#: dist/toolbar/toolbar.js:806
 msgid "Word Counter"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:802
+#: dist/toolbar/toolbar.js:820
 msgid "Number of Slides"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:919
+#: dist/toolbar/toolbar.js:941
 msgid "Document saved"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1077
+#: dist/toolbar/toolbar.js:1100
 msgid "Style"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1089
+#: dist/toolbar/toolbar.js:1112
 msgid "Font"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1113
+#: dist/toolbar/toolbar.js:1136
 msgid "Previous slide"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1114
+#: dist/toolbar/toolbar.js:1137
 msgid "Next slide"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1192
-msgid "You are editing (others can only view)"
-msgstr ""
-
-#: dist/toolbar/toolbar.js:1192
-msgid "EDITING"
+#: dist/toolbar/toolbar.js:1305
+msgid "Layout"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1212
-msgid "You are locked in readonly mode"
+#: dist/toolbar/toolbar.js:1319
+msgid "%n users"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1212
-msgid "READONLY"
+#: dist/toolbar/toolbar.js:1326
+msgid "1 user"
 msgstr ""
 
-#: dist/toolbar/toolbar.js:1324
-msgid "Layout"
+#: dist/toolbar/toolbar.js:1351 src/control/Control.DocumentRepair.js:87
+msgid "You"
 msgstr ""
 
 #: src/admin/AdminSocketBase.js:45
@@ -448,144 +598,100 @@ msgstr ""
 msgid "Are you sure you want to terminate this session?"
 msgstr ""
 
-#: src/admin/AdminStrings.js:4
-msgid "Admin console"
-msgstr ""
-
-#: src/admin/AdminStrings.js:5
-msgid "Toggle navigation"
-msgstr ""
-
-#: src/admin/AdminStrings.js:6
-msgid "Settings"
-msgstr ""
-
-#: src/admin/AdminStrings.js:7
-msgid "Overview"
-msgstr ""
-
-#: src/admin/AdminStrings.js:8
-msgid "(current)"
-msgstr ""
-
-#: src/admin/AdminStrings.js:9
-msgid "Analytics"
-msgstr ""
-
-#: src/admin/AdminStrings.js:10
-msgid "Dashboard"
-msgstr ""
-
-#: src/admin/AdminStrings.js:11
-msgid "Users online"
-msgstr ""
-
-#: src/admin/AdminStrings.js:12
-msgid "Documents opened"
-msgstr ""
-
-#: src/admin/AdminStrings.js:13
-msgid "Memory consumed"
-msgstr ""
-
-#: src/admin/AdminStrings.js:14
-msgid "PID"
-msgstr ""
-
-#: src/admin/AdminStrings.js:15
-msgid "Document"
+#: src/admin/Util.js:13
+msgid "kB"
 msgstr ""
 
-#: src/admin/AdminStrings.js:16
-msgid "Number of views"
+#: src/admin/Util.js:13
+msgid "MB"
 msgstr ""
 
-#: src/admin/AdminStrings.js:17
-msgid "Elapsed time"
+#: src/admin/Util.js:13
+msgid "GB"
 msgstr ""
 
-#: src/admin/AdminStrings.js:18
-msgid "Kill"
+#: src/admin/Util.js:13
+msgid "TB"
 msgstr ""
 
-#: src/admin/AdminStrings.js:19
-msgid "Graphs"
+#: src/admin/Util.js:41
+msgid " hrs"
 msgstr ""
 
-#: src/admin/AdminStrings.js:21
-msgid "Cache size of memory statistics"
+#: src/admin/Util.js:43
+msgid " mins"
 msgstr ""
 
-#: src/admin/AdminStrings.js:22
-msgid "Time interval of memory statistics (in ms)"
+#: src/admin/Util.js:45
+msgid " s"
 msgstr ""
 
-#: src/admin/AdminStrings.js:23
-msgid "Cache size of CPU statistics"
+#: src/control/Control.ColumnHeader.js:38
+msgid "Insert column before"
 msgstr ""
 
-#: src/admin/AdminStrings.js:24
-msgid "Time interval of CPU statistics (in ms)"
+#: src/control/Control.ColumnHeader.js:45 src/control/Control.Menubar.js:141
+msgid "Delete column"
 msgstr ""
 
-#: src/admin/Util.js:13
-msgid "kB"
+#: src/control/Control.ColumnHeader.js:52
+msgid "Optimal Width"
 msgstr ""
 
-#: src/admin/Util.js:13
-msgid "MB"
+#: src/control/Control.ColumnHeader.js:67
+msgid "Optimal Column Width"
 msgstr ""
 
-#: src/admin/Util.js:13
-msgid "GB"
+#: src/control/Control.DocumentRepair.js:32
+msgid "Repair Document"
 msgstr ""
 
-#: src/admin/Util.js:13
-msgid "TB"
+#: src/control/Control.DocumentRepair.js:40
+msgid "Type"
 msgstr ""
 
-#: src/admin/Util.js:41
-msgid " hrs"
+#: src/control/Control.DocumentRepair.js:42
+msgid "Index"
 msgstr ""
 
-#: src/admin/Util.js:43
-msgid " mins"
+#: src/control/Control.DocumentRepair.js:44 src/control/Control.Menubar.js:29
+msgid "Comment"
 msgstr ""
 
-#: src/admin/Util.js:45
-msgid " s"
+#: src/control/Control.DocumentRepair.js:46
+msgid "User name"
 msgstr ""
 
-#: src/control/Control.ColumnHeader.js:38
-msgid "Insert column before"
+#: src/control/Control.DocumentRepair.js:48
+msgid "Timestamp"
 msgstr ""
 
-#: src/control/Control.ColumnHeader.js:45 src/control/Control.Menubar.js:134
-msgid "Delete column"
+#: src/control/Control.DocumentRepair.js:52
+msgid "Jump to state"
 msgstr ""
 
-#: src/control/Control.Menubar.js:10 src/control/Control.Menubar.js:56
-#: src/control/Control.Menubar.js:102
+#: src/control/Control.Menubar.js:10 src/control/Control.Menubar.js:63
+#: src/control/Control.Menubar.js:109
 msgid "File"
 msgstr ""
 
-#: src/control/Control.Menubar.js:11 src/control/Control.Menubar.js:57
-#: src/control/Control.Menubar.js:103
+#: src/control/Control.Menubar.js:11 src/control/Control.Menubar.js:64
+#: src/control/Control.Menubar.js:110
 msgid "Print"
 msgstr ""
 
-#: src/control/Control.Menubar.js:12 src/control/Control.Menubar.js:58
-#: src/control/Control.Menubar.js:104
+#: src/control/Control.Menubar.js:12 src/control/Control.Menubar.js:65
+#: src/control/Control.Menubar.js:111
 msgid "See revision history"
 msgstr ""
 
-#: src/control/Control.Menubar.js:13 src/control/Control.Menubar.js:59
-#: src/control/Control.Menubar.js:105
+#: src/control/Control.Menubar.js:13 src/control/Control.Menubar.js:66
+#: src/control/Control.Menubar.js:112
 msgid "Download as"
 msgstr ""
 
-#: src/control/Control.Menubar.js:13 src/control/Control.Menubar.js:59
-#: src/control/Control.Menubar.js:105
+#: src/control/Control.Menubar.js:13 src/control/Control.Menubar.js:66
+#: src/control/Control.Menubar.js:112
 msgid "PDF Document (.pdf)"
 msgstr ""
 
@@ -601,176 +707,192 @@ msgstr ""
 msgid "Microsoft Word (.docx)"
 msgstr ""
 
-#: src/control/Control.Menubar.js:18 src/control/Control.Menubar.js:64
-#: src/control/Control.Menubar.js:110
+#: src/control/Control.Menubar.js:18 src/control/Control.Menubar.js:71
+#: src/control/Control.Menubar.js:117
 msgid "Edit"
 msgstr ""
 
-#: src/control/Control.Menubar.js:21 src/control/Control.Menubar.js:67
-#: src/control/Control.Menubar.js:113
+#: src/control/Control.Menubar.js:18
+msgid "Repair"
+msgstr ""
+
+#: src/control/Control.Menubar.js:22 src/control/Control.Menubar.js:74
+#: src/control/Control.Menubar.js:120
 msgid "Cut"
 msgstr ""
 
-#: src/control/Control.Menubar.js:22 src/control/Control.Menubar.js:68
-#: src/control/Control.Menubar.js:114
+#: src/control/Control.Menubar.js:23 src/control/Control.Menubar.js:75
+#: src/control/Control.Menubar.js:121
 msgid "Copy"
 msgstr ""
 
-#: src/control/Control.Menubar.js:23 src/control/Control.Menubar.js:69
-#: src/control/Control.Menubar.js:115
+#: src/control/Control.Menubar.js:24 src/control/Control.Menubar.js:76
+#: src/control/Control.Menubar.js:122
 msgid "Paste"
 msgstr ""
 
-#: src/control/Control.Menubar.js:25 src/control/Control.Menubar.js:71
-#: src/control/Control.Menubar.js:117
+#: src/control/Control.Menubar.js:26 src/control/Control.Menubar.js:78
+#: src/control/Control.Menubar.js:124
 msgid "Select all"
 msgstr ""
 
-#: src/control/Control.Menubar.js:27 src/control/Control.Menubar.js:36
-#: src/control/Control.Menubar.js:73 src/control/Control.Menubar.js:81
-#: src/control/Control.Menubar.js:119
+#: src/control/Control.Menubar.js:28 src/control/Control.Menubar.js:43
+#: src/control/Control.Menubar.js:80 src/control/Control.Menubar.js:88
+#: src/control/Control.Menubar.js:126
 msgid "Insert"
 msgstr ""
 
-#: src/control/Control.Menubar.js:27 src/control/Control.Menubar.js:73
-#: src/control/Control.Menubar.js:119
+#: src/control/Control.Menubar.js:28 src/control/Control.Menubar.js:80
+#: src/control/Control.Menubar.js:126
 msgid "Image"
 msgstr ""
 
-#: src/control/Control.Menubar.js:28
-msgid "Comment"
+#: src/control/Control.Menubar.js:31
+msgid "Footnote"
 msgstr ""
 
-#: src/control/Control.Menubar.js:30 src/control/Control.Menubar.js:75
-#: src/control/Control.Menubar.js:124
+#: src/control/Control.Menubar.js:32
+msgid "Endnote"
+msgstr ""
+
+#: src/control/Control.Menubar.js:34
+msgid "Page break"
+msgstr ""
+
+#: src/control/Control.Menubar.js:35
+msgid "Column break"
+msgstr ""
+
+#: src/control/Control.Menubar.js:37 src/control/Control.Menubar.js:82
+#: src/control/Control.Menubar.js:131
 msgid "View"
 msgstr ""
 
-#: src/control/Control.Menubar.js:30 src/control/Control.Menubar.js:75
-#: src/control/Control.Menubar.js:124
+#: src/control/Control.Menubar.js:37 src/control/Control.Menubar.js:82
+#: src/control/Control.Menubar.js:131
 msgid "Full screen"
 msgstr ""
 
-#: src/control/Control.Menubar.js:36 src/control/Control.Menubar.js:81
+#: src/control/Control.Menubar.js:43 src/control/Control.Menubar.js:88
 msgid "Tables"
 msgstr ""
 
-#: src/control/Control.Menubar.js:36 src/control/Control.Menubar.js:81
+#: src/control/Control.Menubar.js:43 src/control/Control.Menubar.js:88
 msgid "Rows before"
 msgstr ""
 
-#: src/control/Control.Menubar.js:37 src/control/Control.Menubar.js:82
+#: src/control/Control.Menubar.js:44 src/control/Control.Menubar.js:89
 msgid "Rows after"
 msgstr ""
 
-#: src/control/Control.Menubar.js:39 src/control/Control.Menubar.js:84
+#: src/control/Control.Menubar.js:46 src/control/Control.Menubar.js:91
 msgid "Columns left"
 msgstr ""
 
-#: src/control/Control.Menubar.js:40 src/control/Control.Menubar.js:85
+#: src/control/Control.Menubar.js:47 src/control/Control.Menubar.js:92
 msgid "Columns right"
 msgstr ""
 
-#: src/control/Control.Menubar.js:41 src/control/Control.Menubar.js:86
+#: src/control/Control.Menubar.js:48 src/control/Control.Menubar.js:93
 msgid "Delete"
 msgstr ""
 
-#: src/control/Control.Menubar.js:41 src/control/Control.Menubar.js:86
+#: src/control/Control.Menubar.js:48 src/control/Control.Menubar.js:93
 msgid "Rows"
 msgstr ""
 
-#: src/control/Control.Menubar.js:42 src/control/Control.Menubar.js:87
+#: src/control/Control.Menubar.js:49 src/control/Control.Menubar.js:94
 msgid "Columns"
 msgstr ""
 
-#: src/control/Control.Menubar.js:43 src/control/Control.Menubar.js:44
+#: src/control/Control.Menubar.js:50 src/control/Control.Menubar.js:51
 msgid "Table"
 msgstr ""
 
-#: src/control/Control.Menubar.js:44
+#: src/control/Control.Menubar.js:51
 msgid "Select"
 msgstr ""
 
-#: src/control/Control.Menubar.js:45 src/control/Control.Menubar.js:121
+#: src/control/Control.Menubar.js:52 src/control/Control.Menubar.js:128
 msgid "Row"
 msgstr ""
 
-#: src/control/Control.Menubar.js:46 src/control/Control.Menubar.js:122
+#: src/control/Control.Menubar.js:53 src/control/Control.Menubar.js:129
 msgid "Column"
 msgstr ""
 
-#: src/control/Control.Menubar.js:47
+#: src/control/Control.Menubar.js:54
 msgid "Cell"
 msgstr ""
 
-#: src/control/Control.Menubar.js:48 src/control/Control.Menubar.js:88
+#: src/control/Control.Menubar.js:55 src/control/Control.Menubar.js:95
 msgid "Merge cells"
 msgstr ""
 
-#: src/control/Control.Menubar.js:50 src/control/Control.Menubar.js:96
-#: src/control/Control.Menubar.js:136
+#: src/control/Control.Menubar.js:57 src/control/Control.Menubar.js:103
+#: src/control/Control.Menubar.js:143
 msgid "Help"
 msgstr ""
 
-#: src/control/Control.Menubar.js:50 src/control/Control.Menubar.js:96
-#: src/control/Control.Menubar.js:136
+#: src/control/Control.Menubar.js:57 src/control/Control.Menubar.js:103
+#: src/control/Control.Menubar.js:143
 msgid "Keyboard shortcuts"
 msgstr ""
 
-#: src/control/Control.Menubar.js:51 src/control/Control.Menubar.js:97
-#: src/control/Control.Menubar.js:137
+#: src/control/Control.Menubar.js:58 src/control/Control.Menubar.js:104
+#: src/control/Control.Menubar.js:144
 msgid "About"
 msgstr ""
 
-#: src/control/Control.Menubar.js:60
+#: src/control/Control.Menubar.js:67
 msgid "ODF presentation (.odp)"
 msgstr ""
 
-#: src/control/Control.Menubar.js:61
+#: src/control/Control.Menubar.js:68
 msgid "Microsoft Powerpoint 2003 (.ppt)"
 msgstr ""
 
-#: src/control/Control.Menubar.js:62
+#: src/control/Control.Menubar.js:69
 msgid "Microsoft Powerpoint (.pptx)"
 msgstr ""
 
-#: src/control/Control.Menubar.js:90
+#: src/control/Control.Menubar.js:97
 msgid "Slide"
 msgstr ""
 
-#: src/control/Control.Menubar.js:90
+#: src/control/Control.Menubar.js:97
 msgid "New slide"
 msgstr ""
 
-#: src/control/Control.Menubar.js:106
+#: src/control/Control.Menubar.js:113
 msgid "ODF spreadsheet (.ods)"
 msgstr ""
 
-#: src/control/Control.Menubar.js:107
+#: src/control/Control.Menubar.js:114
 msgid "Microsoft Excel 2003 (.xls)"
 msgstr ""
 
-#: src/control/Control.Menubar.js:108
+#: src/control/Control.Menubar.js:115
 msgid "Microsoft Excel (.xlsx)"
 msgstr ""
 
-#: src/control/Control.Menubar.js:130
+#: src/control/Control.Menubar.js:137
 msgid "Cells"
 msgstr ""
 
-#: src/control/Control.Menubar.js:130
+#: src/control/Control.Menubar.js:137
 msgid "Insert row"
 msgstr ""
 
-#: src/control/Control.Menubar.js:131
+#: src/control/Control.Menubar.js:138
 msgid "Insert column"
 msgstr ""
 
-#: src/control/Control.Menubar.js:133 src/control/Control.RowHeader.js:43
+#: src/control/Control.Menubar.js:140 src/control/Control.RowHeader.js:43
 msgid "Delete row"
 msgstr ""
 
-#: src/control/Control.Menubar.js:306
+#: src/control/Control.Menubar.js:313
 msgid "Are you sure you want to delete this slide?"
 msgstr ""
 
@@ -778,6 +900,14 @@ msgstr ""
 msgid "Insert row above"
 msgstr ""
 
+#: src/control/Control.RowHeader.js:50
+msgid "Optimal Height"
+msgstr ""
+
+#: src/control/Control.RowHeader.js:63
+msgid "Optimal Row Height"
+msgstr ""
+
 #: src/control/Control.Tabs.js:46
 msgid "Insert sheet before this"
 msgstr ""
@@ -802,56 +932,60 @@ msgstr ""
 msgid "Enter new sheet name"
 msgstr ""
 
-#: src/control/Toolbar.js:59 src/control/Toolbar.js:68
+#: src/control/Toolbar.js:63 src/control/Toolbar.js:72
 msgid "Downloading..."
 msgstr ""
 
-#: src/control/Toolbar.js:80 src/map/Map.js:806
+#: src/control/Toolbar.js:84 src/map/Map.js:856
 msgid "Saving..."
 msgstr ""
 
-#: src/control/Toolbar.js:212
+#: src/control/Toolbar.js:216
 msgid "This version of %productName is powered by"
 msgstr ""
 
-#: src/core/Socket.js:17
+#: src/core/Socket.js:22
 msgid "Oops, there is a problem connecting to LibreOffice Online : "
 msgstr ""
 
-#: src/core/Socket.js:139
+#: src/core/Socket.js:138
 msgid "Unsupported server version."
 msgstr ""
 
-#: src/core/Socket.js:158
+#: src/core/Socket.js:170
 msgid "Document requires password to view."
 msgstr ""
 
-#: src/core/Socket.js:161
+#: src/core/Socket.js:173
 msgid "Document requires password to modify."
 msgstr ""
 
-#: src/core/Socket.js:163
+#: src/core/Socket.js:175
 msgid "Hit Cancel to open in view-only mode."
 msgstr ""
 
-#: src/core/Socket.js:167
+#: src/core/Socket.js:179
 msgid "Wrong password provided. Please try again."
 msgstr ""
 
-#: src/core/Socket.js:304
+#: src/core/Socket.js:221
+msgid "Connecting..."
+msgstr ""
+
+#: src/core/Socket.js:339
 msgid ""
 "Well, this is embarrassing, we cannot connect to your document. Please try "
 "again."
 msgstr ""
 
-#: src/core/Socket.js:307
-msgid "We are sorry, this is an unexpected connection error. Please try again."
-msgstr ""
-
-#: src/map/Map.js:116
+#: src/map/Map.js:118
 msgid "Initializing..."
 msgstr ""
 
-#: src/map/Map.js:809
+#: src/map/Map.js:859
 msgid "Loading..."
 msgstr ""
+
+#: src/map/handler/Map.FileInserter.js:54
+msgid "Uploading..."
+msgstr ""
commit 89f29e30fc7f797cab07bb2dc12a055d1e58b71f
Author: Tor Lillqvist <tml at collabora.com>
Date:   Tue Oct 11 13:05:13 2016 +0300

    It's 'error:', in lower case
    
    Clearly this if branch has never been reached.
    
    (cherry picked from commit 9ff9452e4c427fe66ee8fea3bf0eb44a00d3d53e)

diff --git a/loolwsd/test/UnitPrefork.cpp b/loolwsd/test/UnitPrefork.cpp
index 393d449..b4213bd 100644
--- a/loolwsd/test/UnitPrefork.cpp
+++ b/loolwsd/test/UnitPrefork.cpp
@@ -62,7 +62,7 @@ public:
     virtual bool filterChildMessage(const std::vector<char>& payload) override
     {
         const std::string memory = LOOLProtocol::getFirstLine(payload);
-        if (!memory.compare(0,6,"Error:"))
+        if (!memory.compare(0,6,"error:"))
         {
             _failure = memory;
         }
commit 98435cf718163fbf0ff14aebd3cb48ab8ff8dead
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Mon Oct 10 09:12:01 2016 +0200

    Fix WOPI request header: X-WOPIOverride -> X-WOPI-Override
    
    Change-Id: Ibd288beeffecea415c8524dfc03e77e71ba3c10e
    Reviewed-on: https://gerrit.libreoffice.org/29662
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>
    (cherry picked from commit 799b783968f86f1c943e17008220bb4741f41b5f)

diff --git a/loolwsd/Storage.cpp b/loolwsd/Storage.cpp
index 57e3c5f..7cf5693 100644
--- a/loolwsd/Storage.cpp
+++ b/loolwsd/Storage.cpp
@@ -364,7 +364,7 @@ bool WopiStorage::saveLocalFileToStorage()
     std::unique_ptr<Poco::Net::HTTPClientSession> psession(getHTTPClientSession(uriObject));
 
     Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uriObject.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1);
-    request.set("X-WOPIOverride", "PUT");
+    request.set("X-WOPI-Override", "PUT");
     request.setContentType("application/octet-stream");
     request.setContentLength(size);
 
commit 7939ef25695bcc7cba2ac8467b1617b19a5d6f6b
Author: Henry Castro <hcastro at collabora.com>
Date:   Mon Oct 10 22:28:56 2016 -0400

    loolwsd: websocket shutdown cleanup
    
    (cherry picked from commit c62344db814fe351787316accf0faf53ee811db5)

diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 2b3b205..79d1986 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -182,7 +182,6 @@ static inline
 void shutdownLimitReached(WebSocket& ws)
 {
     const std::string error = Poco::format(PAYLOAD_UNAVALABLE_LIMIT_REACHED, MAX_DOCUMENTS, MAX_CONNECTIONS);
-    const std::string close = Poco::format(SERVICE_UNAVALABLE_LIMIT_REACHED, static_cast<int>(WebSocket::WS_POLICY_VIOLATION));
 
     /* loleaflet sends loolclient, load and partrectangles message immediately
        after web socket handshake, so closing web socket fails loading page in
@@ -204,7 +203,7 @@ void shutdownLimitReached(WebSocket& ws)
             if (--retries == 4)
             {
                 ws.sendFrame(error.data(), error.size());
-                ws.shutdown(WebSocket::WS_POLICY_VIOLATION, close);
+                ws.shutdown(WebSocket::WS_POLICY_VIOLATION, "");
             }
         }
         while (retries > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
@@ -212,7 +211,7 @@ void shutdownLimitReached(WebSocket& ws)
     catch (Exception&)
     {
         ws.sendFrame(error.data(), error.size());
-        ws.shutdown(WebSocket::WS_POLICY_VIOLATION, close);
+        ws.shutdown(WebSocket::WS_POLICY_VIOLATION, "");
     }
 }
 
@@ -897,8 +896,8 @@ private:
             // something wrong, with internal exceptions
             Log::trace("Abnormal close handshake.");
             session->closeFrame();
-            ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, SERVICE_UNAVALABLE_INTERNAL_ERROR);
-            session->shutdownPeer(WebSocket::WS_ENDPOINT_GOING_AWAY, SERVICE_UNAVALABLE_INTERNAL_ERROR);
+            ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, "");
+            session->shutdownPeer(WebSocket::WS_ENDPOINT_GOING_AWAY, "");
         }
     }
 
@@ -1050,7 +1049,7 @@ public:
                         const std::string msg = std::string("error: ") + exc.what();
                         ws->sendFrame(msg.data(), msg.size());
                         // abnormal close frame handshake
-                        ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, msg);
+                        ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, "");
                     }
                     catch (const std::exception& exc2)
                     {
@@ -1275,8 +1274,8 @@ public:
                 // something wrong, with internal exceptions
                 Log::trace("Abnormal close handshake.");
                 session->closeFrame();
-                ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, SERVICE_UNAVALABLE_INTERNAL_ERROR);
-                session->shutdownPeer(WebSocket::WS_ENDPOINT_GOING_AWAY, SERVICE_UNAVALABLE_INTERNAL_ERROR);
+                ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, "");
+                session->shutdownPeer(WebSocket::WS_ENDPOINT_GOING_AWAY, "");
             }
         }
         catch (const Exception& exc)
diff --git a/loolwsd/UserMessages.hpp b/loolwsd/UserMessages.hpp
index 36e6ad3..7883fc0 100644
--- a/loolwsd/UserMessages.hpp
+++ b/loolwsd/UserMessages.hpp
@@ -13,9 +13,7 @@
 #define INCLUDED_USERMESSAGES_HPP
 
 //NOTE: For whatever reason Poco seems to trim the first character.
-
 constexpr auto SERVICE_UNAVALABLE_INTERNAL_ERROR = " Service is unavailable. Please try again later and report to your administrator if the issue persists.";
-constexpr auto SERVICE_UNAVALABLE_LIMIT_REACHED = "error: cmd=socket kind=close code=%d";
 constexpr auto PAYLOAD_UNAVALABLE_LIMIT_REACHED = "error: cmd=socket kind=limitreached params=%d,%d";
 
 #endif
diff --git a/loolwsd/test/httpwserror.cpp b/loolwsd/test/httpwserror.cpp
index 8e7769d..509046c 100644
--- a/loolwsd/test/httpwserror.cpp
+++ b/loolwsd/test/httpwserror.cpp
@@ -103,7 +103,6 @@ void HTTPWSError::testMaxDocuments()
         sendTextFrame(socket, "partpagerectangles ");
         statusCode = getErrorCode(socket, message);
         CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_POLICY_VIOLATION), statusCode);
-        CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("error: cmd=socket kind=close") != std::string::npos);
     }
     catch (const Poco::Exception& exc)
     {
@@ -144,7 +143,6 @@ void HTTPWSError::testMaxConnections()
         sendTextFrame(socketN, "partpagerectangles ");
         statusCode = getErrorCode(*socketN, message);
         CPPUNIT_ASSERT_EQUAL(static_cast<Poco::UInt16>(Poco::Net::WebSocket::WS_POLICY_VIOLATION), statusCode);
-        CPPUNIT_ASSERT_MESSAGE("Wrong error message ", message.find("error: cmd=socket kind=close") != std::string::npos);
     }
     catch (const Poco::Exception& exc)
     {
commit 8babf00a3523c1e6808a200999f9e643eb082482
Author: Henry Castro <hcastro at collabora.com>
Date:   Mon Oct 10 16:03:30 2016 -0400

    loleaflet: do not show internal errors
    
    Internal error message, it was intended for debugging purposes,
    so it is not necessary to show them to final users
    
    (cherry picked from commit 03f912115a93e94840c8cedb23ab7d5698c1257a)

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index c4bb43f..d26277d 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -336,12 +336,7 @@ L.Socket = L.Class.extend({
 			this._map._docLayer.removeAllViews();
 		}
 
-		if (e.code && e.reason) {
-			this._map.fire('error', {msg: e.reason});
-		}
-		else {
-			this._map.fire('error', {msg: _('Well, this is embarrassing, we cannot connect to your document. Please try again.'), cmd: 'socket', kind: 'closed', id: 4});
-		}
+		this._map.fire('error', {msg: _('Well, this is embarrassing, we cannot connect to your document. Please try again.'), cmd: 'socket', kind: 'closed', id: 4});
 	},
 
 	parseServerCmd: function (msg) {
commit bedcf4c16d8ebb87e5196b48ba4065ff1499c4c1
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Oct 10 18:16:13 2016 +0300

    There doesn't seem to be any 'invalidate:' message any longer
    
    There is only 'invalidatetiles:'. Try to document that properly then
    instead.
    
    (cherry picked from commit d5bbe8b52c27744a3e6e06c84e9e8a0cd2615b51)

diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index d617354..ba91ae0 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -224,20 +224,13 @@ getchildid: id=<id>
 
     Returns the child id
 
-invalidate: part=<partNumber> x=<x> y=<y> width=<width> height=<height>
+invalidatetiles: part=<partNumber> x=<x> y=<y> width=<width> height=<height>
 
     All parameters are numbers. Tells the client to invalidate any
     cached tiles for the document area specified (in twips), at any
     zoom level.
 
-    The client should handle either this message or the
-    invalidatetiles: message, which has a different syntax, with
-    payload directly from the LOK_CALLBACK_INVALIDATE_TILES
-    callback. (The latter does not contain a part number, and as the
-    protocol is asynchronous, it is unclear whether a client can be
-    sure, or find out with certainty, for what part the
-    invalidatetiles: message is. The invalidatetiles: message will be
-    dropped soon.)
+invalidatetiles: EMPTY
 
 nextmessage: size=<byteSize>
 
@@ -282,17 +275,17 @@ tile: part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<
     render a tile, or the string 'cached' if the tile was found in the
     cache.
 
-Each LOK_CALLBACK_FOO_BAR callback causes a corresponding message to
-the client, consisting of the FOO_BAR part in lowercase, without
-underscore, followed by a colon, space and the callback payload. For
-instance:
+Each LOK_CALLBACK_FOO_BAR callback except
+LOK_CALLBACK_INVALIDATE_TILES causes a corresponding message to the
+client, consisting of the FOO_BAR part in lowercase, without
+underscore, followed by a colon, space and the callback
+payload. (LOK_CALLBACK_INVALIDATE_TILES causes an invalidatetiles:
+message as documented above.) For instance:
 
 invalidatecursor: <payload>
 
 The payload contains a rectangle describing the cursor position.
 
-invalidatetiles: <payload>
-
 The communication between the parent process (the one keeping open the
 Websocket connections to the clients) and a child process (handling
 one document through LibreOfficeKit) uses the same protocol, with
commit b3105d99d5b2fe74c55a56fb0cc7dfc8b25d6375
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Oct 10 18:00:38 2016 +0530

    loleaflet: Restore old coloring algorithm for non-text documents
    
    .uno:TrackedChangeAuthors doesn't give correct colors for
    documents other than writer, lets use our old algorithm for color
    assignment for these documents.
    
    Change-Id: If865788154a80da2637aad84183a0e947bb4b7e8
    (cherry picked from commit a01d2fc91dfd6ea4ce365d30641fa251d45c8b04)

diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 3c51cb4..8b06ee2 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -1330,7 +1330,7 @@ map.on('addview', function(e) {
 	}, 3000);
 
 	var username = e.username;
-	var color = L.LOUtil.rgbToHex(e.color);
+	var color = L.LOUtil.rgbToHex(map.getViewColor(e.viewId));
 	if (e.viewId === map._docLayer._viewId) {
 		username = _('You');
 		color = '#000';
diff --git a/loleaflet/src/core/LOUtil.js b/loleaflet/src/core/LOUtil.js
index 4ebb9d1..ed6b718 100644
--- a/loleaflet/src/core/LOUtil.js
+++ b/loleaflet/src/core/LOUtil.js
@@ -3,6 +3,21 @@
  */
 
 L.LOUtil = {
+	// Based on core.git's colordata.hxx: COL_AUTHOR1_DARK...COL_AUTHOR9_DARK
+	// consisting of arrays of RGB values
+	// Maybe move the color logic to separate file when it becomes complex
+	darkColors: [
+		[198, 146, 0],
+		[6,  70, 162],
+		[87, 157,  28],
+		[105,  43, 157],
+		[197,   0,  11],
+		[0, 128, 128],
+		[140, 132,  0],
+		[53,  85, 107],
+		[209, 118,   0]
+	],
+
 	startSpinner: function (spinnerCanvas, spinnerSpeed) {
 		var spinnerInterval;
 		spinnerCanvas.width = 50;
@@ -28,6 +43,11 @@ L.LOUtil = {
 		return spinnerInterval;
 	},
 
+	getViewIdHexColor: function(viewId) {
+		var color = this.darkColors[(viewId + 1) % this.darkColors.length];
+		return (color[2] | (color[1] << 8) | (color[0] << 16));
+	},
+
 	rgbToHex: function(color) {
 		return '#' + ('000000' + color.toString(16)).slice(-6);
 	}
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index c6d2f71..70fc83b 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -130,7 +130,7 @@ L.Map = L.Evented.extend({
 
 	addView: function(viewid, username, color) {
 		this._viewInfo[viewid] = {'username': username, 'color': color};
-		this.fire('addview', {viewId: viewid, username: username, color: color});
+		this.fire('addview', {viewId: viewid, username: username});
 	},
 
 	removeView: function(viewid) {
@@ -144,6 +144,10 @@ L.Map = L.Evented.extend({
 	},
 
 	getViewColor: function(viewid) {
+		if (this._docLayer._docType !== 'text') {
+			return L.LOUtil.getViewIdHexColor(viewid);
+		}
+
 		return this._viewInfo[viewid].color;
 	},
 
commit 0f3363b6ad310f70081ac5d1e4514b99758ff09a
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Oct 10 17:13:02 2016 +0530

    loleaflet: Fix incorrect reference to username
    
    Change-Id: Ibd6433c862eaf5f5fe57244180691ef8b08e3fbb
    (cherry picked from commit 176367b40844038adabc8e82e076c011d6000f98)

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 38619f6..c6d2f71 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -134,7 +134,7 @@ L.Map = L.Evented.extend({
 	},
 
 	removeView: function(viewid) {
-		var username = this._viewInfo[viewid];
+		var username = this._viewInfo[viewid].username;
 		delete this._viewInfo[viewid];
 		this.fire('removeview', {viewId: viewid, username: username});
 	},
commit ac6edaf3954054ccfcc78c67bf6bc6b6c9519cfb
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Oct 10 13:15:15 2016 +0300

    Bin dead code
    
    ClientSession::isLoadFailed() was not used anywhere. As a fallout, the
    _loadFailed field and setLoadFailed() member function became useless,
    too.
    
    (cherry picked from commit 44d7f26c89a29b0c03137edb6b1b4bf49518ea78)

diff --git a/loolwsd/ClientSession.cpp b/loolwsd/ClientSession.cpp
index e4623ff..f3acf98 100644
--- a/loolwsd/ClientSession.cpp
+++ b/loolwsd/ClientSession.cpp
@@ -41,7 +41,6 @@ ClientSession::ClientSession(const std::string& id,
     LOOLSession(id, Kind::ToClient, ws),
     _docBroker(std::move(docBroker)),
     _isReadOnly(readOnly),
-    _loadFailed(false),
     _loadPart(-1)
 {
     Log::info("ClientSession ctor [" + getName() + "].");
diff --git a/loolwsd/ClientSession.hpp b/loolwsd/ClientSession.hpp
index 3be6277..926d250 100644
--- a/loolwsd/ClientSession.hpp
+++ b/loolwsd/ClientSession.hpp
@@ -50,13 +50,6 @@ public:
         _saveAsQueue.put(url);
     }
 
-    bool isLoadFailed() const { return _loadFailed; }
-    void setLoadFailed(const std::string& reason)
-    {
-        Log::warn("Document load failed: " + reason);
-        _loadFailed = true;
-    }
-
     std::shared_ptr<DocumentBroker> getDocumentBroker() const { return _docBroker; }
 
 private:
@@ -88,8 +81,6 @@ private:
     /// Store URLs of completed 'save as' documents.
     MessageQueue _saveAsQueue;
 
-    /// Marks if document loading failed.
-    bool _loadFailed;
     int _loadPart;
 };
 
diff --git a/loolwsd/PrisonerSession.cpp b/loolwsd/PrisonerSession.cpp
index c40e4da..ccedaec 100644
--- a/loolwsd/PrisonerSession.cpp
+++ b/loolwsd/PrisonerSession.cpp
@@ -105,7 +105,7 @@ bool PrisonerSession::_handleInput(const char *buffer, int length)
                     errorKind == "wrongpassword")
                 {
                     forwardToPeer(_peer, buffer, length, isBinary);
-                    peer->setLoadFailed(errorKind);
+                    Log::warn("Document load failed: " + errorKind);
                     return false;
                 }
             }
commit d2070e834946fdd6fccb5bf0e140b576d8f2d55d
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu Oct 6 20:07:53 2016 +0530

    loleaflet: Use view colors directly from core
    
    Change-Id: I2fdffd6dd0823a77ff52e40150a81db4b261ec81
    (cherry picked from commit 0ad39593d02e139e5f0bba0b8262d7c12563663a)

diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 49407af..3c51cb4 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -1330,7 +1330,7 @@ map.on('addview', function(e) {
 	}, 3000);
 
 	var username = e.username;
-	var color = L.LOUtil.getViewIdHexColor(e.viewId);
+	var color = L.LOUtil.rgbToHex(e.color);
 	if (e.viewId === map._docLayer._viewId) {
 		username = _('You');
 		color = '#000';
diff --git a/loleaflet/src/core/LOUtil.js b/loleaflet/src/core/LOUtil.js
index 24c516b..4ebb9d1 100644
--- a/loleaflet/src/core/LOUtil.js
+++ b/loleaflet/src/core/LOUtil.js
@@ -3,20 +3,6 @@
  */
 
 L.LOUtil = {
-	// Based on core.git's colordata.hxx: COL_AUTHOR1_DARK...COL_AUTHOR9_DARK
-	// consisting of arrays of RGB values
-	// Maybe move the color logic to separate file when it becomes complex
-	darkColors: [
-		[198, 146, 0],
-		[87, 157,  28],
-		[105,  43, 157],
-		[197,   0,  11],
-		[0, 128, 128],
-		[140, 132,  0],
-		[53,  85, 107],
-		[209, 118,   0]
-	],
-
 	startSpinner: function (spinnerCanvas, spinnerSpeed) {
 		var spinnerInterval;
 		spinnerCanvas.width = 50;
@@ -42,9 +28,7 @@ L.LOUtil = {
 		return spinnerInterval;
 	},
 
-	getViewIdHexColor: function(viewId) {
-		var color = this.darkColors[(viewId + 1) % this.darkColors.length];
-		var hex = color[2] | (color[1] << 8) | (color[0] << 16);
-		return '#' + ('000000' + hex.toString(16)).slice(-6);
+	rgbToHex: function(color) {
+		return '#' + ('000000' + color.toString(16)).slice(-6);
 	}
 };
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index a5aad09..452a93c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -682,7 +682,7 @@ L.TileLayer = L.GridLayer.extend({
 
 		if (!this._isEmptyRectangle(this._cellViewCursors[viewId].bounds) && this._selectedPart === viewPart) {
 			if (!cellViewCursorMarker) {
-				var borderColor = L.LOUtil.getViewIdHexColor(viewId);
+				var borderColor = L.LOUtil.rgbToHex(this._map.getViewColor(viewId));
 				cellViewCursorMarker = L.rectangle(this._cellViewCursors[viewId].bounds, {fill: false, color: borderColor, weight: 2});
 				this._cellViewCursors[viewId].marker = cellViewCursorMarker;
 				cellViewCursorMarker.bindPopup(this._map.getViewName(viewId), {autoClose: false, autoPan: false, borderColor: borderColor});
@@ -714,8 +714,8 @@ L.TileLayer = L.GridLayer.extend({
 		this._onUpdateViewCursor(viewId);
 	},
 
-	_addView: function(viewId, username) {
-		this._map.addView(viewId, username);
+	_addView: function(viewId, username, color) {
+		this._map.addView(viewId, username, color);
 
 		//TODO: We can initialize color and other properties here.
 		if (typeof this._viewCursors[viewId] !== 'undefined') {
@@ -755,7 +755,7 @@ L.TileLayer = L.GridLayer.extend({
 		var viewIds = [];
 		for (var viewInfoIdx in viewInfo) {
 			if (!(parseInt(viewInfo[viewInfoIdx].id) in this._map._viewInfo)) {
-				this._addView(viewInfo[viewInfoIdx].id, viewInfo[viewInfoIdx].username);
+				this._addView(viewInfo[viewInfoIdx].id, viewInfo[viewInfoIdx].username, viewInfo[viewInfoIdx].color);
 			}
 			viewIds.push(viewInfo[viewInfoIdx].id);
 		}
@@ -1235,7 +1235,7 @@ L.TileLayer = L.GridLayer.extend({
 		   (this._docType === 'text' || this._selectedPart === viewPart)) {
 			if (!viewCursorMarker) {
 				var viewCursorOptions = {
-					color: L.LOUtil.getViewIdHexColor(viewId),
+					color: L.LOUtil.rgbToHex(this._map.getViewColor(viewId)),
 					blink: false,
 					header: true, // we want a 'hat' to our view cursors (which will contain view user names)
 					headerTimeout: 3000, // hide after some interval
@@ -1272,7 +1272,7 @@ L.TileLayer = L.GridLayer.extend({
 
 			viewSelection = new L.Polygon(viewPolygons, {
 				pointerEvents: 'none',
-				fillColor: L.LOUtil.getViewIdHexColor(viewId),
+				fillColor: L.LOUtil.rgbToHex(this._map.getViewColor(viewId)),
 				fillOpacity: 0.25,
 				weight: 2,
 				opacity: 0.25
@@ -1293,7 +1293,7 @@ L.TileLayer = L.GridLayer.extend({
 		if (!this._isEmptyRectangle(viewBounds) &&
 		   (this._docType === 'text' || this._selectedPart === viewPart)) {
 			if (!viewMarker) {
-				var color = L.LOUtil.getViewIdHexColor(viewId);
+				var color = L.LOUtil.rgbToHex(this._map.getViewColor(viewId));
 				viewMarker = L.rectangle(viewBounds, {
 					pointerEvents: 'auto',
 					fill: false,
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 7d2dfac..38619f6 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -120,14 +120,17 @@ L.Map = L.Evented.extend({
 
 		// View info (user names and view ids)
 		this._viewInfo = {};
+
+		// View color map
+		this._viewColors = {};
 	},
 
 
 	// public methods that modify map state
 
-	addView: function(viewid, username) {
-		this._viewInfo[viewid] = username;
-		this.fire('addview', {viewId: viewid, username: username});
+	addView: function(viewid, username, color) {
+		this._viewInfo[viewid] = {'username': username, 'color': color};
+		this.fire('addview', {viewId: viewid, username: username, color: color});
 	},
 
 	removeView: function(viewid) {
@@ -137,7 +140,11 @@ L.Map = L.Evented.extend({
 	},
 
 	getViewName: function(viewid) {
-		return this._viewInfo[viewid];
+		return this._viewInfo[viewid].username;
+	},
+
+	getViewColor: function(viewid) {
+		return this._viewInfo[viewid].color;
 	},
 
 	// replaced by animation-powered implementation in Map.PanAnimation.js
commit 1979d96ec2549490b59faa9c2205ba84a0913a09
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu Oct 6 20:08:24 2016 +0530

    loolwsd: Forward 'color' property in 'viewinfo' message to client
    
    Change-Id: Id7d28a46cacd662aeb371805509e5a81b90b9c90
    (cherry picked from commit 587c0e5222def90659ec5959352c56eed0df27b5)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 852be7a..089b695 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -874,7 +874,7 @@ private:
         }
 
         return viewInfo;
-    };
+    }
 
     std::mutex& getMutex() override
     {
@@ -891,6 +891,7 @@ private:
     {
         // Store the list of viewid, username mapping in a map
         std::map<int, std::string> viewInfoMap = getViewInfo();
+        std::map<std::string, int> viewColorsMap = getViewColors();
         std::unique_lock<std::mutex> lock(_mutex);
 
         // Double check if list of viewids from core and our list matches,
@@ -901,7 +902,7 @@ private:
         {
             Object::Ptr viewInfoObj = new Object();
             viewInfoObj->set("id", viewId);
-
+            int color = 0;
             if (viewInfoMap.find(viewId) == viewInfoMap.end())
             {
                 Log::error("No username found for viewId [" + std::to_string(viewId) + "].");
@@ -910,7 +911,12 @@ private:
             else
             {
                 viewInfoObj->set("username", viewInfoMap[viewId]);
+                if (viewColorsMap.find(viewInfoMap[viewId]) != viewColorsMap.end())
+                {
+                    color = viewColorsMap[viewInfoMap[viewId]];
+                }
             }
+            viewInfoObj->set("color", color);
 
             viewInfoArray->set(arrayIndex++, viewInfoObj);
         }
@@ -931,6 +937,49 @@ private:
 
 private:
 
+    // Get the color value for all author names from the core
+    std::map<std::string, int> getViewColors()
+    {
+        std::string colorValues;
+        std::map<std::string, int> viewColors;
+
+        {
+            auto lock(_loKitDocument->getLock());
+
+            char* pValues = _loKitDocument->getCommandValues(".uno:TrackedChangeAuthors");
+            colorValues = std::string(pValues == nullptr ? "" : pValues);
+            std::free(pValues);
+        }
+
+        try
+        {
+            if (!colorValues.empty())
+            {
+                Poco::JSON::Parser parser;
+                auto root = parser.parse(colorValues).extract<Poco::JSON::Object::Ptr>();
+                if (root->get("authors").type() == typeid(Poco::JSON::Array::Ptr))
+                {
+                    auto authorsArray = root->get("authors").extract<Poco::JSON::Array::Ptr>();
+                    for (auto& authorVar: *authorsArray)
+                    {
+                        auto authorObj = authorVar.extract<Poco::JSON::Object::Ptr>();
+                        auto authorName = authorObj->get("name").convert<std::string>();
+                        auto colorValue = authorObj->get("color").convert<int>();
+                        viewColors[authorName] = colorValue;
+                    }
+                }
+            }
+        }
+        catch(const Exception& exc)
+        {
+            Log::error() << "Poco Exception: " << exc.displayText()
+                         << (exc.nested() ? " (" + exc.nested()->displayText() + ")" : "")
+                         << Log::end;
+        }
+
+        return viewColors;
+    }
+
     std::shared_ptr<lok::Document> load(const std::string& sessionId,
                                         const std::string& uri,
                                         const std::string& userName,
commit b9ca4dc7365642a271a8aaba693f86eb688cf0cb
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Oct 9 17:18:29 2016 -0400

    loolwsd: kill Connection object in child
    
    Change-Id: Ic4d0d3e7286272a0765b299824dfa3556fe56f4b
    Reviewed-on: https://gerrit.libreoffice.org/29652
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit e0ff6eef6b636789b0f7e849df77a6047fec5bae)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 95d3f8c..852be7a 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -258,123 +258,6 @@ namespace
 #endif
 }
 
-/// Connection thread with a client (via WSD).
-class Connection: public Runnable
-{
-public:
-    Connection(std::shared_ptr<ChildSession> session,
-               std::shared_ptr<WebSocket> ws) :
-        _sessionId(session->getId()),
-        _session(std::move(session)),
-        _ws(std::move(ws)),
-        _threadMutex(),
-        _joined(false)
-    {
-        Log::info("Connection ctor in child for " + _sessionId);
-    }
-
-    ~Connection()
-    {
-        Log::info("~Connection dtor in child for " + _sessionId);
-        stop();
-        join();
-    }
-
-    const std::string& getSessionId() const { return _sessionId; };
-    std::shared_ptr<WebSocket> getWebSocket() const { return _ws; }
-    std::shared_ptr<ChildSession> getSession() { return _session; }
-
-    void start()
-    {
-        _thread.start(*this);
-
-        // Busy-wait until we run.
-        // This is important to make sure we can process
-        // callbacks, which if we're late to start will
-        // be dropped. No need for async notification here.
-        constexpr auto delay = COMMAND_TIMEOUT_MS / 20;
-        for (auto i = 0; i < 20 && !isRunning(); ++i)
-        {
-            std::this_thread::sleep_for(std::chrono::milliseconds(delay));
-        }
-    }
-
-    bool isRunning()
-    {
-        return _thread.isRunning();
-    }
-
-    void stop()
-    {
-        // What should we do here?
-    }
-
-    void join()
-    {
-        // The thread is joinable only once.
-        std::unique_lock<std::mutex> lock(_threadMutex);
-        if (!_joined)
-        {
-            _thread.join();
-            _joined = true;
-        }
-    }
-
-    void run() override
-    {
-        Util::setThreadName("kit_ws_" + _sessionId);
-
-        Log::debug("Thread started.");
-        try
-        {
-            IoUtil::SocketProcessor(_ws,
-                [this](const std::vector<char>& payload)
-                {
-                    if (!_session->handleInput(payload.data(), payload.size()))
-                    {
-                        Log::info("Socket handler flagged for finishing.");
-                        return false;
-                    }
-
-                    return true;
-                },
-                [this]() { _session->closeFrame(); },
-                []() { return !!TerminationFlag; });
-
-            if (_session->isCloseFrame())
-            {
-                Log::trace("Normal close handshake.");
-                _ws->shutdown();
-            }
-            else
-            {
-                Log::trace("Abnormal close handshake.");
-               _ws->shutdown(WebSocket::WS_ENDPOINT_GOING_AWAY, SERVICE_UNAVALABLE_INTERNAL_ERROR);
-            }
-        }
-        catch (const Exception& exc)
-        {
-            Log::error() << "Connection::run: Exception: " << exc.displayText()
-                         << (exc.nested() ? " (" + exc.nested()->displayText() + ")" : "")
-                         << Log::end;
-        }
-        catch (const std::exception& exc)
-        {
-            Log::error(std::string("Connection::run: Exception: ") + exc.what());
-        }
-
-        Log::debug("Thread finished.");
-    }
-
-private:
-    const std::string _sessionId;
-    Thread _thread;
-    std::shared_ptr<ChildSession> _session;
-    std::shared_ptr<WebSocket> _ws;
-    std::mutex _threadMutex;
-    std::atomic<bool> _joined;
-};
-
 /// A document container.
 /// Owns LOKitDocument instance and connections.
 /// Manages the lifetime of a document.
@@ -464,10 +347,10 @@ public:
 
             if (!_sessions.emplace(sessionId, session).second)
             {
-                Log::error("Connection already exists for child: " + _jailId + ", session: " + sessionId);
+                Log::error("Session already exists for child: " + _jailId + ", session: " + sessionId);
             }
 
-            Log::debug("Connections: " + std::to_string(_sessions.size()));
+            Log::debug("Sessions: " + std::to_string(_sessions.size()));
             return true;
         }
         catch (const std::exception& ex)
@@ -539,7 +422,7 @@ public:
 
     /// Returns true if at least one *live* connection exists.
     /// Does not consider user activity, just socket status.
-    bool hasConnections()
+    bool hasSessions()
     {
         // -ve values for failure.
         return purgeSessions() != 0;
@@ -550,7 +433,7 @@ public:
     bool canDiscard()
     {
         //TODO: Implement proper time-out on inactivity.
-        return !hasConnections();
+        return !hasSessions();
     }
 
     /// Set Document password for given URL
@@ -1296,7 +1179,7 @@ private:
                             }
                             else
                             {
-                                Log::error() << "Connection thread for session " << session->getId() << " for view "
+                                Log::error() << "Session thread for session " << session->getId() << " for view "
                                              << viewId << " is not running. Dropping [" << LOKitHelper::kitCallbackTypeToString(type)
                                              << "] payload [" << payload << "]." << Log::end;
                             }
commit 9ebdc07e5d7e9aaebfa02f4e254b1b7eeec88382
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Oct 9 17:05:24 2016 -0400

    loolwsd: cleanup of Connection in ChildSession
    
    Change-Id: I07636163df7b2973dada55b9704abf7105ad285f
    Reviewed-on: https://gerrit.libreoffice.org/29651
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit e33eff4abdc4a3145b5bf7a9b6def2fce63d64a6)

diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 17f7f3f..95d3f8c 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -428,62 +428,21 @@ public:
 
         _tileQueue->put("eof");
         _callbackThread.join();
-
-        // Flag all connections to stop.
-        for (auto aIterator : _connections)
-        {
-            aIterator.second->stop();
-        }
-
-        // Destroy all connections and views.
-        for (auto aIterator : _connections)
-        {
-            try
-            {
-                // stop all websockets
-                if (aIterator.second->isRunning())
-                {
-                    std::shared_ptr<WebSocket> ws = aIterator.second->getWebSocket();
-                    if (ws)
-                    {
-                        ws->shutdownReceive();
-                        aIterator.second->join();
-                    }
-                }
-            }
-            catch(NetException& exc)
-            {
-                Log::error() << "Document::~Document: " << exc.displayText()
-                             << (exc.nested() ? " (" + exc.nested()->displayText() + ")" : "")
-                             << Log::end;
-            }
-        }
-
-        // Destroy all connections and views.
-        _connections.clear();
     }
 
     const std::string& getUrl() const { return _url; }
 
-    bool createSession(const std::string& sessionId, const unsigned intSessionId)
+    bool createSession(const std::string& sessionId)
     {
         std::unique_lock<std::mutex> lock(_mutex);
 
         try
         {
-            const auto& it = _connections.find(intSessionId);
-            if (it != _connections.end())
+            const auto& it = _sessions.find(sessionId);
+            if (it != _sessions.end())
             {
-                // found item, check if still running
-                if (it->second->isRunning())
-                {
-                    Log::warn("Session [" + sessionId + "] is already running.");
-                    return true;
-                }
-
-                // Restore thread. TODO: Review this logic.
-                Log::warn("Session [" + sessionId + "] is not running. Restoring.");
-                _connections.erase(intSessionId);
+                Log::warn("Session [" + sessionId + "] is already running.");
+                return true;
             }
 
             Log::info() << "Creating " << (_clientViews ? "new" : "first")
@@ -503,18 +462,12 @@ public:
 
             auto session = std::make_shared<ChildSession>(sessionId, ws, _jailId, *this);
 
-            auto thread = std::make_shared<Connection>(session, ws);
-            const auto aInserted = _connections.emplace(intSessionId, thread);
-            if (aInserted.second)
-            {
-                thread->start();
-            }
-            else
+            if (!_sessions.emplace(sessionId, session).second)
             {
                 Log::error("Connection already exists for child: " + _jailId + ", session: " + sessionId);
             }
 
-            Log::debug("Connections: " + std::to_string(_connections.size()));
+            Log::debug("Connections: " + std::to_string(_sessions.size()));
             return true;
         }
         catch (const std::exception& ex)
@@ -531,7 +484,7 @@ public:
     {
         std::vector<std::shared_ptr<ChildSession>> deadSessions;
         size_t numRunning = 0;
-        size_t num_connections = 0;
+        size_t num_sessions = 0;
         {
             std::unique_lock<std::mutex> lock(_mutex, std::defer_lock);
             if (!lock.try_lock())
@@ -544,20 +497,20 @@ public:
             // bluntly exit, no need to clean up our own data structures. Also, there is a bug that
             // causes the deadSessions.clear() call below to crash in some situations when the last
             // session is being removed.
-            for (auto it = _connections.cbegin(); it != _connections.cend(); ++it)
+            for (auto it = _sessions.cbegin(); it != _sessions.cend(); ++it)
             {
-                if (it->second->isRunning())
+                if (!it->second->isCloseFrame())
                     numRunning++;
             }
 
             if (numRunning > 0)
             {
-                for (auto it = _connections.cbegin(); it != _connections.cend(); )
+                for (auto it = _sessions.cbegin(); it != _sessions.cend(); )
                 {
-                    if (!it->second->isRunning())
+                    if (it->second->isCloseFrame())
                     {
-                        deadSessions.push_back(it->second->getSession());
-                        it = _connections.erase(it);
+                        deadSessions.push_back(it->second);
+                        it = _sessions.erase(it);
                     }
                     else
                     {
@@ -566,7 +519,7 @@ public:
                 }
             }
 
-            num_connections = _connections.size();
+            num_sessions = _sessions.size();
         }
 
         if (numRunning == 0)
@@ -581,7 +534,7 @@ public:
         // and the dtor tries to take its lock (which is taken).
         deadSessions.clear();
 
-        return num_connections;
+        return num_sessions;
     }
 
     /// Returns true if at least one *live* connection exists.
@@ -1027,11 +980,11 @@ private:
         std::unique_lock<std::mutex> lock(_mutex);
         std::map<int, std::string> viewInfo;
 
-        for (auto& connection : _connections)
+        for (auto& pair : _sessions)
         {
-            if (connection.second->isRunning())
+            const auto session = pair.second;
+            if (!session->isCloseFrame())
             {
-                const auto session = connection.second->getSession();
                 const auto viewId = session->getViewId();
                 viewInfo[viewId] = session->getViewUserName();
             }
@@ -1083,15 +1036,12 @@ private:
         viewInfoArray->stringify(ossViewInfo);
 
         // Broadcast updated viewinfo to all _active_ connections
-        for (auto& connectionIt: _connections)
+        for (auto& pair : _sessions)
         {
-            if (connectionIt.second->isRunning())
+            const auto session = pair.second;
+            if (!session->isCloseFrame() && session->isActive())
             {
-                auto session = connectionIt.second->getSession();
-                if (session->isActive())
-                {
-                    session->sendTextFrame("viewinfo: " + ossViewInfo.str());
-                }
+                session->sendTextFrame("viewinfo: " + ossViewInfo.str());
             }
         }
     }
@@ -1105,15 +1055,14 @@ private:
                                         const std::string& renderOpts,
                                         const bool haveDocPassword)
     {
-        const unsigned intSessionId = Util::decodeId(sessionId);
-        const auto it = _connections.find(intSessionId);
-        if (it == _connections.end() || !it->second)
+        const auto it = _sessions.find(sessionId);
+        if (it == _sessions.end() || !it->second)
         {
             Log::error("Cannot find session [" + sessionId + "].");
             return nullptr;
         }
 
-        auto session = it->second->getSession();
+        auto session = it->second;
         int viewId = 0;
         std::unique_lock<std::mutex> lockLokDoc;
 
@@ -1257,21 +1206,20 @@ private:
         Log::trace("Forwarding payload to " + prefix + ' ' + message);
 
         std::string name;
-        std::string value;
-        if (LOOLProtocol::parseNameValuePair(prefix, name, value, '-') && name == "child")
+        std::string viewId;
+        if (LOOLProtocol::parseNameValuePair(prefix, name, viewId, '-') && name == "child")
         {
-            const unsigned viewId = Util::decodeId(value);
-            const auto it = _connections.find(viewId);
-            if (it != _connections.end())
+            const auto it = _sessions.find(viewId);
+            if (it != _sessions.end())
             {
                 if (message == "disconnect")
                 {
-                    Log::debug("Removing ChildSession " + value);
-                    _connections.erase(it);
+                    Log::debug("Removing ChildSession " + viewId);
+                    _sessions.erase(it);
                     return true;
                 }
 
-                auto session = it->second->getSession();
+                auto session = it->second;
                 if (session)
                 {
                     return session->handleInput(message.data(), message.size());
@@ -1336,19 +1284,19 @@ private:
                     // Forward the callback to the same view, demultiplexing is done by the LibreOffice core.
                     // TODO: replace with a map to be faster.
                     bool isFound = false;
-                    for (auto& it : _connections)
+                    for (auto& it : _sessions)
                     {
-                        auto session = it.second->getSession();
+                        auto session = it.second;
                         if (session && ((session->getViewId() == viewId) || (viewId == -1)))
                         {
-                            if (it.second->isRunning())
+                            if (!it.second->isCloseFrame())
                             {
                                 isFound = true;
                                 session->loKitCallback(type, payload);
                             }
                             else
                             {
-                                Log::error() << "Connection thread for session " << it.second->getSessionId() << " for view "
+                                Log::error() << "Connection thread for session " << session->getId() << " for view "
                                              << viewId << " is not running. Dropping [" << LOKitHelper::kitCallbackTypeToString(type)
                                              << "] payload [" << payload << "]." << Log::end;
                             }
@@ -1404,7 +1352,7 @@ private:
     std::condition_variable _cvLoading;
     std::atomic_size_t _isLoading;
     std::map<int, std::unique_ptr<CallbackDescriptor>> _viewIdToCallbackDescr;
-    std::map<unsigned, std::shared_ptr<Connection>> _connections;
+    std::map<std::string, std::shared_ptr<ChildSession>> _sessions;
     Poco::Thread _callbackThread;
     std::atomic_size_t _clientViews;
 };
@@ -1628,7 +1576,6 @@ void lokit_main(const std::string& childRoot,
                     else if (tokens[0] == "session")
                     {
                         const std::string& sessionId = tokens[1];
-                        const unsigned intSessionId = Util::decodeId(sessionId);
                         const std::string& docKey = tokens[2];
 
                         std::string url;
@@ -1642,7 +1589,7 @@ void lokit_main(const std::string& childRoot,
 
                         // Validate and create session.
                         if (!(url == document->getUrl() &&
-                            document->createSession(sessionId, intSessionId)))
+                            document->createSession(sessionId)))
                         {
                             Log::debug("CreateSession failed.");
                         }
commit 5a4644e881453f2d68b0d8bf24192636e17c43fa
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Oct 9 16:37:13 2016 -0400

    loolwsd: unload child view when client disconnects
    
    Using a new internal command, when a client disconnects
    an internal 'disconnect' message is dispatched so
    the child process cleans up the ChildSession in question.
    
    Change-Id: I34166ad59e84ae389a3913bd2430fe537225bb4b
    Reviewed-on: https://gerrit.libreoffice.org/29650
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 4fa7e53eaeb43233ac039525b58eb8c588968019)

diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp
index 02686fa..4e7e618 100644
--- a/loolwsd/DocumentBroker.cpp
+++ b/loolwsd/DocumentBroker.cpp
@@ -441,6 +441,10 @@ size_t DocumentBroker::removeSession(const std::string& id)
     if (it != _sessions.end())
     {
         _sessions.erase(it);
+
+        // Let the child know the client has disconnected.
+        const std::string msg("child-" + id + " disconnect");
+        _childProcess->getWebSocket()->sendFrame(msg.data(), msg.size());
     }
 
     return _sessions.size();
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 3bb689d..17f7f3f 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -1264,6 +1264,13 @@ private:
             const auto it = _connections.find(viewId);
             if (it != _connections.end())
             {
+                if (message == "disconnect")
+                {
+                    Log::debug("Removing ChildSession " + value);
+                    _connections.erase(it);
+                    return true;
+                }
+
                 auto session = it->second->getSession();
                 if (session)
                 {
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 418986d..d617354 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -373,6 +373,24 @@ saveas: url=<url>
     <url> is a URL of the destination, encoded. Sent from the child to the
     parent after a saveAs() completed.
 
+client-<sessionId> <Payload Message>
+
+    Forwarding message between a child and its parent session.
+    The payload message is forwarded to the ClientSession.
+
+parent -> child
+===============
+
+child-<sessionId> <Payload Message>
+
+    Forwarding message between a parent and its child session.
+    The payload message is forwarded to the ChildSession.
+
+disconnect
+
+    Signals to the child that the client for the respective connection
+    has disconnected.
+
 Admin console
 ===============
 
commit f63d137d5348d7f5782a805ce9ec02f799f0ae3e
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Oct 9 13:42:30 2016 -0400

    loolwsd: fix convert-to handling
    
    Change-Id: I24b8c0b7129bee2692696809613ee49a38024c98
    Reviewed-on: https://gerrit.libreoffice.org/29649
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 1edd37ea2dbe1e3d140115396b85bc09be90af4b)

diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 2cbfd88..2b3b205 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -432,7 +432,9 @@ private:
 
                     auto uriPublic = DocumentBroker::sanitizeURI(fromPath);
                     const auto docKey = DocumentBroker::getDocKey(uriPublic);
+                    Log::debug("New DocumentBroker for docKey [" + docKey + "].");
                     auto docBroker = std::make_shared<DocumentBroker>(uriPublic, docKey, LOOLWSD::ChildRoot, child);
+                    child->setDocumentBroker(docBroker);
 
                     // This lock could become a bottleneck.
                     // In that case, we can use a pool and index by publicPath.
@@ -474,14 +476,24 @@ private:
                     session->handleInput(saveas.data(), saveas.size());
 
                     // Send it back to the client.
-                    Poco::URI resultURL(session->getSaveAsUrl(COMMAND_TIMEOUT_MS));
-                    if (!resultURL.getPath().empty())
+                    try
+                    {
+                        Poco::URI resultURL(session->getSaveAsUrl(COMMAND_TIMEOUT_MS));
+                        Log::trace("Save-as URL: " + resultURL.toString());
+
+                        if (!resultURL.getPath().empty())
+                        {
+                            const std::string mimeType = "application/octet-stream";
+                            std::string encodedFilePath;
+                            URI::encode(resultURL.getPath(), "", encodedFilePath);
+                            Log::trace("Sending file: " + encodedFilePath);
+                            response.sendFile(encodedFilePath, mimeType);
+                            sent = true;
+                        }
+                    }
+                    catch (const std::exception& ex)
                     {
-                        const std::string mimeType = "application/octet-stream";
-                        std::string encodedFilePath;
-                        URI::encode(resultURL.getPath(), "", encodedFilePath);
-                        response.sendFile(encodedFilePath, mimeType);
-                        sent = true;
+                        Log::error(std::string("Failed to get save-as url: ") + ex.what());
                     }
 
                     lock.lock();
@@ -495,6 +507,8 @@ private:
                     {
                         Log::error("Multiple sessions during conversion. " + std::to_string(sessionsCount) + " sessions remain.");
                     }
+
+                    session->shutdownPeer(WebSocket::WS_NORMAL_CLOSE, "");
                 }
 
                 // Clean up the temporary directory the HTMLForm ctor created.
commit 3ef7835ddbc0de042496d2c0221f794cd10da0a0
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Oct 9 11:59:00 2016 -0400

    loolwsd: support timeout on MessageQueue get
    
    Change-Id: Iaad39aaa06c59cdacdd4a864599ef6a4a12976f8
    Reviewed-on: https://gerrit.libreoffice.org/29648
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 366e0e21d5733808fda7080a013bbb6c4096b669)

diff --git a/loolwsd/ClientSession.hpp b/loolwsd/ClientSession.hpp
index e9bc176..3be6277 100644
--- a/loolwsd/ClientSession.hpp
+++ b/loolwsd/ClientSession.hpp
@@ -39,9 +39,9 @@ public:
      * Return the URL of the saved-as document when it's ready. If called
      * before it's ready, the call blocks till then.
      */
-    std::string getSaveAsUrl()
+    std::string getSaveAsUrl(const unsigned timeoutMs)
     {
-        const auto payload = _saveAsQueue.get();
+        const auto payload = _saveAsQueue.get(timeoutMs);
         return std::string(payload.data(), payload.size());
     }
 
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 1889987..2cbfd88 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -474,8 +474,7 @@ private:
                     session->handleInput(saveas.data(), saveas.size());
 
                     // Send it back to the client.
-                    //TODO: Should have timeout to avoid waiting forever.
-                    Poco::URI resultURL(session->getSaveAsUrl());
+                    Poco::URI resultURL(session->getSaveAsUrl(COMMAND_TIMEOUT_MS));
                     if (!resultURL.getPath().empty())
                     {
                         const std::string mimeType = "application/octet-stream";
diff --git a/loolwsd/MessageQueue.cpp b/loolwsd/MessageQueue.cpp
index ffa5f79..3729018 100644
--- a/loolwsd/MessageQueue.cpp
+++ b/loolwsd/MessageQueue.cpp
@@ -32,10 +32,23 @@ void MessageQueue::put(const Payload& value)
     _cv.notify_one();
 }
 
-MessageQueue::Payload MessageQueue::get()
+MessageQueue::Payload MessageQueue::get(const unsigned timeoutMs)
 {
     std::unique_lock<std::mutex> lock(_mutex);
-    _cv.wait(lock, [this] { return wait_impl(); });
+
+    if (timeoutMs > 0)
+    {
+        if (!_cv.wait_for(lock, std::chrono::milliseconds(timeoutMs),
+                          [this] { return wait_impl(); }))
+        {
+            throw std::runtime_error("Timed out waiting to get queue item.");
+        }
+    }
+    else
+    {
+        _cv.wait(lock, [this] { return wait_impl(); });
+    }
+
     return get_impl();
 }
 
diff --git a/loolwsd/MessageQueue.hpp b/loolwsd/MessageQueue.hpp
index 8ce2287..95a5654 100644
--- a/loolwsd/MessageQueue.hpp
+++ b/loolwsd/MessageQueue.hpp
@@ -43,7 +43,8 @@ public:
     }
 
     /// Thread safe obtaining of the message.
-    Payload get();
+    /// timeoutMs can be 0 to signify infinity.
+    Payload get(const unsigned timeoutMs = 0);
 
     /// Thread safe removal of all the pending messages.
     void clear();
diff --git a/loolwsd/test/integration-http-server.cpp b/loolwsd/test/integration-http-server.cpp
index 6899476..25aea88 100644
--- a/loolwsd/test/integration-http-server.cpp
+++ b/loolwsd/test/integration-http-server.cpp
@@ -243,6 +243,7 @@ void HTTPServerTest::testConvertTo()
 {
     const auto srcPath = Util::getTempFilePath(TDOC, "hello.odt");
     std::unique_ptr<Poco::Net::HTTPClientSession> session(helpers::createSession(_uri));
+    session->setTimeout(Poco::Timespan(2, 0)); // 2 seconds.
 
     Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, "/lool/convert-to");
     Poco::Net::HTMLForm form;
commit e29de15cef68a8d7956b412b3ea5005a1cb63734
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Oct 9 11:28:22 2016 -0400

    loolwsd: send child messages to client via unified wsd-kit WS
    
    Change-Id: I237120e5a81a2e6d8772a2b6f1e98b1ba567f97e
    Reviewed-on: https://gerrit.libreoffice.org/29647
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 128cd73154894cc63cb16cc1d47c5b905cb1f7a6)

diff --git a/loolwsd/ChildSession.hpp b/loolwsd/ChildSession.hpp
index e8e23ac..d8d68bd 100644
--- a/loolwsd/ChildSession.hpp
+++ b/loolwsd/ChildSession.hpp
@@ -50,6 +50,9 @@ public:
     std::mutex& getMutex() = 0;
     virtual
     std::shared_ptr<TileQueue>& getTileQueue() = 0;
+
+    virtual
+    bool sendTextFrame(const std::string& message) = 0;
 };
 
 /// Represents a session to the WSD process, in a Kit process. Note that this is not a singleton.
@@ -77,6 +80,20 @@ public:
 
     void loKitCallback(const int nType, const std::string& rPayload);
 
+    bool sendTextFrame(const char* buffer, const int length) override
+    {
+        const auto msg = "client-" + getId() + ' ' + std::string(buffer, length);
+
+        const auto lock = getLock();
+
+        return _docManager.sendTextFrame(msg);
+    }
+
+    bool sendTextFrame(const std::string& text)
+    {
+        return sendTextFrame(text.data(), text.size());
+    }
+
 private:
     bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens);
 
diff --git a/loolwsd/ClientSession.hpp b/loolwsd/ClientSession.hpp
index 7508802..e9bc176 100644
--- a/loolwsd/ClientSession.hpp
+++ b/loolwsd/ClientSession.hpp
@@ -30,6 +30,7 @@ public:
     bool isReadOnly() const { return _isReadOnly; }
 
     void setPeer(const std::shared_ptr<PrisonerSession>& peer) { _peer = peer; }
+    std::shared_ptr<PrisonerSession> getPeer() const { return _peer.lock(); }
     bool shutdownPeer(Poco::UInt16 statusCode, const std::string& message);
 
     void setUserName(const std::string& userName) { _userName = userName; }
diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp
index 3201170..02686fa 100644
--- a/loolwsd/DocumentBroker.cpp
+++ b/loolwsd/DocumentBroker.cpp
@@ -756,7 +756,15 @@ bool DocumentBroker::forwardToClient(const std::string& prefix, const std::vecto
         const auto it = _sessions.find(sid);
         if (it != _sessions.end())
         {
-            return it->second->sendTextFrame(message);
+            const auto peer = it->second->getPeer();
+            if (peer)
+            {
+                return peer->handleInput(message.data(), message.size());
+            }
+            else
+            {
+                Log::warn() << "Client session [" << sid << "] has no peer to forward message: " << message << Log::end;
+            }
         }
         else
         {
diff --git a/loolwsd/DocumentBroker.hpp b/loolwsd/DocumentBroker.hpp
index 66ec29c..d8ccee4 100644
--- a/loolwsd/DocumentBroker.hpp
+++ b/loolwsd/DocumentBroker.hpp
@@ -64,6 +64,7 @@ public:
 
     void setDocumentBroker(const std::shared_ptr<DocumentBroker>& docBroker)
     {
+        assert(docBroker && "Invalid DocumentBroker instance.");
         _docBroker = docBroker;
     }
 
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 65f8fa2..3bb689d 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -40,6 +40,7 @@
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/Net/HTTPResponse.h>
 #include <Poco/Net/NetException.h>
+#include <Poco/Net/Socket.h>
 #include <Poco/Net/WebSocket.h>
 #include <Poco/NotificationQueue.h>
 #include <Poco/Process.h>
@@ -78,6 +79,7 @@ using Poco::Net::HTTPClientSession;
 using Poco::Net::HTTPRequest;
 using Poco::Net::HTTPResponse;
 using Poco::Net::NetException;
+using Poco::Net::Socket;
 using Poco::Net::WebSocket;
 using Poco::Path;
 using Poco::Process;
@@ -799,11 +801,34 @@ public:
         ws->sendFrame(response.data(), length, WebSocket::FRAME_BINARY);
     }
 
-    void sendTextFrame(const std::string& message)
+    bool sendTextFrame(const std::string& message) override
     {
-        std::lock_guard<std::mutex> lock(_mutex);
+        try
+        {
+            if (!_ws || _ws->poll(Poco::Timespan(0), Socket::SelectMode::SELECT_ERROR))
+            {
+                Log::error("Child Doc: Bad socket while sending [" + getAbbreviatedMessage(message) + "].");
+                return false;
+            }
+
+            const auto length = message.size();
+            if (length > SMALL_MESSAGE_SIZE)
+            {
+                const std::string nextmessage = "nextmessage: size=" + std::to_string(length);
+                _ws->sendFrame(nextmessage.data(), nextmessage.size());
+            }
 
-        _ws->sendFrame(message.data(), message.size());
+            _ws->sendFrame(message.data(), length);
+            return true;
+        }
+        catch (const Exception& exc)
+        {
+            Log::error() << "Document::sendTextFrame: "
+                         << "Exception: " << exc.displayText()
+                         << (exc.nested() ? "( " + exc.nested()->displayText() + ")" : "");
+        }
+
+        return false;
     }
 
     static void GlobalCallback(const int nType, const char* pPayload, void* pData)
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 0606f4e..b07b2c6 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -42,7 +42,9 @@ public:
     const std::string& getName() const { return _name; }
     bool isDisconnected() const { return _disconnected; }
 
+    virtual
     bool sendBinaryFrame(const char *buffer, int length);
+    virtual
     bool sendTextFrame(const char* buffer, const int length);
     bool sendTextFrame(const std::string& text)
     {
diff --git a/loolwsd/test/WhiteBoxTests.cpp b/loolwsd/test/WhiteBoxTests.cpp
index d17cc22..45db538 100644
--- a/loolwsd/test/WhiteBoxTests.cpp
+++ b/loolwsd/test/WhiteBoxTests.cpp
@@ -192,6 +192,11 @@ public:
     {
         return _tileQueue;
     }
+
+    bool sendTextFrame(const std::string& /*message*/) override
+    {
+        return true;
+    }
 };
 
 void WhiteBoxTests::testEmptyCellCursor()
commit 50926103cd6aeff2cd97f50cabadb4fba6f8694a
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Oct 9 11:27:01 2016 -0400

    loolwsd: trim forwarded messages to avoid leading whitespace
    
    Change-Id: I932baf3ec41789d89bf897fcbf25a1ee1d27f89d
    Reviewed-on: https://gerrit.libreoffice.org/29646
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 22a5db2fc09a123f9c00135f7fc44c4d257c7a9d)

diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp
index 6c6cbb8..3201170 100644
--- a/loolwsd/DocumentBroker.cpp
+++ b/loolwsd/DocumentBroker.cpp
@@ -725,7 +725,7 @@ void DocumentBroker::setModified(const bool value)
 bool DocumentBroker::forwardToChild(const std::string& viewId, const char *buffer, int length)
 {
     const auto message = std::string(buffer, length);
-    Log::warn() << "Forwarding payload to child [" << viewId << "]: " << message << Log::end;
+    Log::trace() << "Forwarding payload to child [" << viewId << "]: " << message << Log::end;
 
     const auto it = _sessions.find(viewId);
     if (it != _sessions.end())
@@ -745,8 +745,9 @@ bool DocumentBroker::forwardToChild(const std::string& viewId, const char *buffe
 
 bool DocumentBroker::forwardToClient(const std::string& prefix, const std::vector<char>& payload)
 {
-    const std::string message(payload.data() + prefix.size(), payload.size() - prefix.size());
-    Log::warn("Forwarding payload to client: " + message);
+    std::string message(payload.data() + prefix.size(), payload.size() - prefix.size());
+    Util::ltrim(message);
+    Log::trace("Forwarding payload to " + prefix + ' ' + message);
 
     std::string name;
     std::string sid;
diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp
index 41d39fc..65f8fa2 100644
--- a/loolwsd/LOOLKit.cpp
+++ b/loolwsd/LOOLKit.cpp
@@ -1227,8 +1227,9 @@ private:
 
     bool forwardToChild(const std::string& prefix, const std::vector<char>& payload)
     {
-        const std::string message(payload.data() + prefix.size(), payload.size() - prefix.size());
-        Log::trace("Forwarding payload to client: " + message);
+        std::string message(payload.data() + prefix.size(), payload.size() - prefix.size());
+        Util::ltrim(message);
+        Log::trace("Forwarding payload to " + prefix + ' ' + message);
 
         std::string name;
         std::string value;
diff --git a/loolwsd/Util.hpp b/loolwsd/Util.hpp
index bd1a5a3..df4f60c 100644
--- a/loolwsd/Util.hpp
+++ b/loolwsd/Util.hpp
@@ -153,6 +153,30 @@ namespace Util
     /// Return a string that is unique across processes and calls.
     std::string UniqueId();
 
+    /// Trim spaces from the left. Just spaces.
+    inline
+    void ltrim(std::string& s)
+    {
+        const auto pos = s.find_first_not_of(" ");
+        if (pos != std::string::npos)
+        {
+            s = s.substr(pos);
+        }
+    }
+
+    /// Trim spaces from the left and copy. Just spaces.
+    inline
+    std::string ltrimmed(const std::string& s)
+    {
+        const auto pos = s.find_first_not_of(" ");
+        if (pos != std::string::npos)
+        {
+            return s.substr(pos);
+        }
+
+        return s;
+    }
+
     /// Given one or more patterns to allow, and one or more to deny,
     /// the match member will return true if, and only if, the subject
     /// matches the allowed list, but not the deny.
commit b8a61afe9254874d725645c17f553d0601287f69
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sat Oct 8 23:51:07 2016 -0400

    loolwsd: unittest cleanup
    
    Change-Id: I90e4fa7d5377b7ecf427c87ce068efdb4bffe933
    Reviewed-on: https://gerrit.libreoffice.org/29645
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 0f2f49823ef02c9cebb32f32d4bcd4dd44e9351c)

diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp
index a6f0654..6876218 100644
--- a/loolwsd/test/httpwstest.cpp
+++ b/loolwsd/test/httpwstest.cpp
@@ -338,21 +338,9 @@ void HTTPWSTest::loadDoc(const std::string& documentURL, const std::string& test
         // Don't replace with helpers, so we catch status.
         Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL);
         auto socket = connectLOKit(_uri, request, _response, testname);
-        sendTextFrame(*socket, "load url=" + documentURL, testname);
+        sendTextFrame(socket, "load url=" + documentURL, testname);
 
-        SocketProcessor(testname, socket, [&](const std::string& msg)
-                {
-                    const std::string prefix = "status: ";
-                    if (msg.find(prefix) == 0)
-                    {
-                        const auto status = msg.substr(prefix.length());
-                        // Might be too strict, consider something flexible instread.
-                        CPPUNIT_ASSERT_EQUAL(std::string("type=text parts=1 current=0 width=12808 height=16408 viewid=0"), status);
-                        return false;
-                    }
-
-                    return true;
-                });
+        assertResponseString(socket, "status:", testname);
     }
     catch (const Poco::Exception& exc)
     {
commit b79a5fb056ca86d225ca53486c7a5a0fcfd1ac46
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sat Oct 8 21:48:41 2016 -0400

    loolwsd: cleanup of isDocumentLoaded
    
    Change-Id: I28321bda3000b443aff4603ad438183fa6cfb2f9
    Reviewed-on: https://gerrit.libreoffice.org/29644
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 46aec10c3c71a69f6308a404aa8bbac08af42ff3)

diff --git a/loolwsd/test/helpers.hpp b/loolwsd/test/helpers.hpp
index 651e247..bed5788 100644
--- a/loolwsd/test/helpers.hpp
+++ b/loolwsd/test/helpers.hpp
@@ -106,64 +106,6 @@ void sendTextFrame(const std::shared_ptr<Poco::Net::WebSocket>& socket, const st
 }
 
 inline
-bool isDocumentLoaded(Poco::Net::WebSocket& ws, const std::string& name = "", bool isView = true)
-{
-    bool isLoaded = false;
-    try
-    {
-        int flags = 0;
-        int retries = 30;
-        const Poco::Timespan waitTime(1000000);
-
-        ws.setReceiveTimeout(0);
-        do
-        {
-            char buffer[READ_BUFFER_SIZE];
-            if (ws.poll(waitTime, Poco::Net::Socket::SELECT_READ))
-            {
-                int bytes = ws.receiveFrame(buffer, sizeof(buffer), flags);
-                if (bytes > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE)
-                {
-                    std::cerr << name << "Got " << bytes << " bytes: " << LOOLProtocol::getAbbreviatedMessage(buffer, bytes) << std::endl;
-                    const std::string line = LOOLProtocol::getFirstLine(buffer, bytes);
-                    const std::string prefix = isView ? "status:" : "statusindicatorfinish:";
-                    if (line.find(prefix) == 0)
-                    {
-                        isLoaded = true;
-                        break;
-                    }
-                }
-                else
-                {
-                    std::cerr << name << "Got " << bytes << " bytes, flags: " << std::hex << flags << std::dec << std::endl;
-                    break;
-                }
-
-                retries = 10;
-            }
-            else
-            {
-                std::cerr << "Timeout\n";
-                --retries;
-            }
-        }
-        while (retries > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE);
-    }
-    catch (const Poco::Net::WebSocketException& exc)
-    {
-        std::cerr << exc.message();
-    }
-
-    return isLoaded;
-}
-
-inline
-bool isDocumentLoaded(std::shared_ptr<Poco::Net::WebSocket>& ws, const std::string& name = "", bool isView = true)
-{
-    return isDocumentLoaded(*ws, name, isView);
-}
-
-inline
 Poco::Net::HTTPClientSession* createSession(const Poco::URI& uri)
 {
 #if ENABLE_SSL
@@ -290,7 +232,7 @@ std::vector<char> getResponseMessage(Poco::Net::WebSocket& ws, const std::string
             {
                 if (!timedout)
                 {
-                    std::cerr << name << "Timeout " ;
+                    std::cerr << name << "Timeout ";
                 }
                 else
                 {
@@ -341,6 +283,20 @@ std::string assertNotInResponse(T& ws, const std::string& prefix, const std::str
     return res;
 }
 
+inline
+bool isDocumentLoaded(Poco::Net::WebSocket& ws, const std::string& name = "", bool isView = true)
+{
+    const std::string prefix = isView ? "status:" : "statusindicatorfinish:";
+    const auto message = getResponseString(ws, prefix, name);
+    return LOOLProtocol::getFirstToken(message) == prefix;
+}
+
+inline
+bool isDocumentLoaded(std::shared_ptr<Poco::Net::WebSocket>& ws, const std::string& name = "", bool isView = true)
+{
+    return isDocumentLoaded(*ws, name, isView);
+}
+
 // Connecting to a Kit process is managed by document broker, that it does several
 // jobs to establish the bridge connection between the Client and Kit process,
 // The result, it is mostly time outs to get messages in the unit test and it could fail.
commit 50c289a43425b5bf418fed2c38993a404da85dc8
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sat Oct 8 21:40:52 2016 -0400

    loolwsd: cleanup of LoolKit process counter
    
    Change-Id: I7d8bec2634b1c838cd10a8bef928ea22c2d2f549
    Reviewed-on: https://gerrit.libreoffice.org/29643
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit d7799d21a888fe0c0325531e43feb9d613739f31)

diff --git a/loolwsd/test/countloolkits.hpp b/loolwsd/test/countloolkits.hpp
index d0c479c..b96e173 100644
--- a/loolwsd/test/countloolkits.hpp
+++ b/loolwsd/test/countloolkits.hpp
@@ -61,30 +61,33 @@ int getLoolKitProcessCount()
         }
     }
 
-    std::cerr << "Number of loolkit processes: " << result << std::endl;
     return result;
 }
 
 static
 int countLoolKitProcesses(const int expected)
 {
-    // Fairly random number, I don't think there is any actual reason for using exactly this repeat
-    // count.
-    const size_t repeat = 21;
+    std::cerr << "Waiting to have " << expected << " loolkit processes. Loolkits: ";
+
+    // Retry for about 3 seconds.
+    const auto sleepMs = static_cast<int>(POLL_TIMEOUT_MS / 3);
+    const size_t repeat = (3000 / sleepMs) + 1;
     auto count = getLoolKitProcessCount();
     for (size_t i = 0; i < repeat; ++i)
     {

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list