.gitreview                                           |    2 
 bundled/include/LibreOfficeKit/LibreOfficeKit.h      |   29 -
 bundled/include/LibreOfficeKit/LibreOfficeKit.hxx    |   63 +--
 bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h |   11 
 bundled/include/LibreOfficeKit/LibreOfficeKitInit.h  |   15 
 common/Util.hpp                                      |   23 +
 configure.ac                                         |    2 
 discovery.xml                                        |    4 
 kit/ChildSession.cpp                                 |   18 
 kit/ForKit.cpp                                       |    4 
 kit/Kit.cpp                                          |    4 
 kit/KitHelper.hpp                                    |    2 
 loleaflet/js/jquery.mCustomScrollbar.js              |   12 
 loleaflet/js/toolbar.js                              |   19 
 loleaflet/src/control/Control.LokDialog.js           |   24 +
 loleaflet/src/control/Control.Menubar.js             |   13 
 loleaflet/src/control/Control.MobileInput.js         |   75 +++
 loleaflet/src/control/Control.Scroll.js              |    6 
 loleaflet/src/dom/DomEvent.js                        |    8 
 loleaflet/src/layer/AnnotationManager.js             |    8 
 loleaflet/src/layer/tile/TileLayer.js                |   18 
 loleaflet/src/layer/vector/SVGGroup.js               |   25 +
 loleaflet/src/map/Map.js                             |    8 
 loleaflet/src/map/handler/Map.Keyboard.js            |    3 
 loleaflet/src/map/handler/Map.Mouse.js               |    1 
 loolwsd.xml.in                                       |    5 
 net/Socket.cpp                                       |  135 ++++++
 net/Socket.hpp                                       |   32 +
 net/WebSocketHandler.hpp                             |  275 ++++++++-----
 scripts/perftrace.pl                                 |  394 +++++++++++++++++++
 test/Makefile.am                                     |    8 
 test/UnitHTTP.cpp                                    |  224 ++++++++++
 test/UnitWOPILoadEncoded.cpp                         |   71 +++
 test/WopiTestServer.hpp                              |   13 
 test/data/non_shape_writer_image.svg                 |    2 
 test/data/shapes_impress.svg                         |   16 
 test/data/shapes_writer.svg                          |   26 -
 test/helpers.hpp                                     |   47 ++
 test/httpcrashtest.cpp                               |    3 
 test/httpwstest.cpp                                  |   43 +-
 tools/KitClient.cpp                                  |    1 
 wsd/ClientSession.cpp                                |   11 
 wsd/DocumentBroker.cpp                               |   58 ++
 wsd/DocumentBroker.hpp                               |   11 
 wsd/LOOLWSD.cpp                                      |   42 +-
 45 files changed, 1566 insertions(+), 248 deletions(-)

New commits:
commit a24c633591520a3799406fd532ef652d2ae577c6
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Thu May 2 17:03:18 2019 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    SVG: update for current core.
    
    Change-Id: Ibb5da2d71ebd68a13b8ccc2a23f891320508f0a5
    Reviewed-on: https://gerrit.libreoffice.org/71684
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>

diff --git a/test/data/non_shape_writer_image.svg 
b/test/data/non_shape_writer_image.svg
index 771ce3c31..2f40e47c3 100644
--- a/test/data/non_shape_writer_image.svg
+++ b/test/data/non_shape_writer_image.svg
@@ -36,7 +36,7 @@
  <g class="Page">
   <g class="Graphic">
    <g>
-    <rect class="BoundingBox" stroke="none" fill="none" x="0" y="0" 
width="9523" height="6031"/><desc>120</desc>
+    <rect class="BoundingBox" stroke="none" fill="none" x="0" y="0" 
width="9523" height="6031"/>
     <image x="0" y="0" width="9523" height="6031" preserveAspectRatio="none" 
xlink:href="data:image/png;base64,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"/>
    </g>
   </g>
diff --git a/test/data/shape_writer.svg b/test/data/shapes_writer.svg
similarity index 67%
rename from test/data/shape_writer.svg
rename to test/data/shapes_writer.svg
index 8937cb663..ed77bdade 100644
--- a/test/data/shape_writer.svg
+++ b/test/data/shapes_writer.svg
@@ -36,19 +36,19 @@
  <g class="Page">
   <g class="com.sun.star.drawing.CustomShape">
    <g id="id1">
-    <rect class="BoundingBox" stroke="none" fill="none" x="7310" y="6216" 
width="3367" 
height="8262"/><desc>150</desc><desc>139</desc><desc>133</desc><desc>132</desc><desc>111</desc>
-    <path fill="rgb(114,159,207)" stroke="none" d="M 7311,14474 L 7311,7057 
8151,6218 10673,6218 10673,13633 9832,14474 7311,14474 Z M 7311,6218 L 
7311,6218 Z M 10673,14474 L 10673,14474 
Z"/><desc>140</desc><desc>139</desc><desc>132</desc><desc>512: 
XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,14474 L 7311,7057 
8151,6218 10673,6218 10673,13633 9832,14474 7311,14474 Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,6218 L 7311,6218 
Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 10673,14474 L 10673,14474 
Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>133</desc><desc>132</desc><desc>111</desc>
-    <path fill="rgb(139,176,217)" stroke="none" d="M 7311,7057 L 8151,6218 
10673,6218 9832,7057 7311,7057 Z M 7311,6218 L 7311,6218 Z M 10673,14474 L 
10673,14474 Z"/><desc>140</desc><desc>139</desc><desc>132</desc><desc>512: 
XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,7057 L 8151,6218 
10673,6218 9832,7057 7311,7057 Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,6218 L 7311,6218 
Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 10673,14474 L 10673,14474 
Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>133</desc><desc>132</desc><desc>111</desc>
-    <path fill="rgb(91,127,166)" stroke="none" d="M 9832,14474 L 9832,7057 
10673,6218 10673,13633 9832,14474 Z M 7311,6218 L 7311,6218 Z M 10673,14474 L 
10673,14474 Z"/><desc>140</desc><desc>139</desc><desc>132</desc><desc>512: 
XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 9832,14474 L 9832,7057 
10673,6218 10673,13633 9832,14474 Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,6218 L 7311,6218 
Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 10673,14474 L 10673,14474 
Z"/><desc>512: XPATHSTROKE_SEQ_END</desc><desc>140</desc>
+    <rect class="BoundingBox" stroke="none" fill="none" x="7310" y="6216" 
width="3367" height="8262"/>
+    <path fill="rgb(114,159,207)" stroke="none" d="M 7311,14474 L 7311,7057 
8151,6218 10673,6218 10673,13633 9832,14474 7311,14474 Z M 7311,6218 L 
7311,6218 Z M 10673,14474 L 10673,14474 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,14474 L 7311,7057 
8151,6218 10673,6218 10673,13633 9832,14474 7311,14474 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,6218 L 7311,6218 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 10673,14474 L 10673,14474 
Z"/>
+    <path fill="rgb(139,176,217)" stroke="none" d="M 7311,7057 L 8151,6218 
10673,6218 9832,7057 7311,7057 Z M 7311,6218 L 7311,6218 Z M 10673,14474 L 
10673,14474 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,7057 L 8151,6218 
10673,6218 9832,7057 7311,7057 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,6218 L 7311,6218 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 10673,14474 L 10673,14474 
Z"/>
+    <path fill="rgb(91,127,166)" stroke="none" d="M 9832,14474 L 9832,7057 
10673,6218 10673,13633 9832,14474 Z M 7311,6218 L 7311,6218 Z M 10673,14474 L 
10673,14474 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 9832,14474 L 9832,7057 
10673,6218 10673,13633 9832,14474 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 7311,6218 L 7311,6218 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 10673,14474 L 10673,14474 
Z"/>
    </g>
   </g>
  </g>
commit 34e95d379c7d8921154f4a19bf362223ee1d34c1
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Sat May 4 22:50:00 2019 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    test: close testCrashKit race.
    
    LOOLWSD is conservative and leaves forkit to start processes for a
    while. If we kill a kit before it has started and registered we can
    end up with LOOLWSD holding off to wait for the (now dead) kit, and
    the tearDown assertions that we have 1 kit failing.
    
    Change-Id: Id25e48bf55d1757d2223816293500fde6ff9df1b
    Reviewed-on: https://gerrit.libreoffice.org/71808
    Reviewed-by: Ashod Nakashian <ashnak...@gmail.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>

diff --git a/test/httpcrashtest.cpp b/test/httpcrashtest.cpp
index a2b1a9d2a..6fd8ee938 100644
--- a/test/httpcrashtest.cpp
+++ b/test/httpcrashtest.cpp
@@ -136,6 +136,9 @@ void HTTPCrashTest::testCrashKit()
     {
         std::shared_ptr<LOOLWebSocket> socket = 
loadDocAndGetSocket("empty.odt", _uri, testname);
 
+        TST_LOG("Allowing time for kits to spawn and connect to wsd to get 
cleanly killed");
+        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
         TST_LOG("Killing loolkit instances.");
 
         killLoKitProcesses();
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 89171b339..75ab01c48 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -360,6 +360,7 @@ void cleanupDocBrokers()
 /// -1 for error.
 static int forkChildren(const int number)
 {
+    LOG_TRC("Request forkit to spawn " << number << " new child(ren)");
     Util::assertIsLocked(NewChildrenMutex);
 
     if (number > 0)
@@ -1507,6 +1508,9 @@ void LOOLWSD::autoSave(const std::string& docKey)
 void PrisonerPoll::wakeupHook()
 {
 #ifndef MOBILEAPP
+    LOG_TRC("PrisonerPoll - wakes up with " << NewChildren.size() <<
+            " new children and " << DocBrokers.size() << " brokers and " <<
+            OutstandingForks << " kits forking");
     if (!LOOLWSD::checkAndRestoreForKit())
     {
         // No children have died.
commit 8306480f6b53ce2920a5763548dd1d78be747f2a
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu May 9 14:25:21 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    Fix HTTPWSTest::testRenderShapeSelectionImpress() against product core
    
    This test only passed when core.git was built in a way so that '#if
    OSL_DEBUG_LEVEL > 0' was true (debug, dbgutil builds). Re-generate this
    against a product build, since 2 other tests already have references
    like that.
    
    We already have infrastructure for stripping <desc> XML elements from
    the actual result.
    
    (cherry picked from commit 1d32dc1742a2a51343af993939f0f51538de54aa)
    
    Change-Id: I9cdd53bf9625266d94313a90946270bfc266bd47
    Reviewed-on: https://gerrit.libreoffice.org/72042
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>
    Tested-by: Michael Meeks <michael.me...@collabora.com>

diff --git a/test/data/shapes_impress.svg b/test/data/shapes_impress.svg
index 0cfac22b7..8087f20a7 100644
--- a/test/data/shapes_impress.svg
+++ b/test/data/shapes_impress.svg
@@ -36,18 +36,18 @@
  <g ooo:name="page1" class="Page">
   <g class="com.sun.star.drawing.CustomShape">
    <g id="id3">
-    <rect class="BoundingBox" stroke="none" fill="none" x="3301" y="5333" 
width="8385" 
height="6607"/><desc>150</desc><desc>139</desc><desc>133</desc><desc>132</desc><desc>111</desc>
-    <path fill="rgb(114,159,207)" stroke="none" d="M 7493,11938 L 3302,11938 
3302,5334 11684,5334 11684,11938 7493,11938 
Z"/><desc>140</desc><desc>139</desc><desc>132</desc><desc>512: 
XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 7493,11938 L 3302,11938 
3302,5334 11684,5334 11684,11938 7493,11938 Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc>
+    <rect class="BoundingBox" stroke="none" fill="none" x="3301" y="5333" 
width="8385" height="6607"/>
+    <path fill="rgb(114,159,207)" stroke="none" d="M 7493,11938 L 3302,11938 
3302,5334 11684,5334 11684,11938 7493,11938 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 7493,11938 L 3302,11938 
3302,5334 11684,5334 11684,11938 7493,11938 Z"/>
    </g>
   </g>
   <g class="com.sun.star.drawing.CustomShape">
    <g id="id4">
-    <rect class="BoundingBox" stroke="none" fill="none" x="14477" y="4571" 
width="9656" 
height="8386"/><desc>150</desc><desc>139</desc><desc>133</desc><desc>132</desc><desc>111</desc>
-    <path fill="rgb(114,159,207)" stroke="none" d="M 19304,4572 C 22040,4572 
24130,6387 24130,8763 24130,11139 22040,12954 19304,12954 16568,12954 
14478,11139 14478,8763 14478,6387 16568,4572 19304,4572 Z M 14478,4572 L 
14478,4572 Z M 24131,12955 L 24131,12955 
Z"/><desc>140</desc><desc>139</desc><desc>132</desc><desc>512: 
XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 19304,4572 C 22040,4572 
24130,6387 24130,8763 24130,11139 22040,12954 19304,12954 16568,12954 
14478,11139 14478,8763 14478,6387 16568,4572 19304,4572 Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 14478,4572 L 14478,4572 
Z"/><desc>512: 
XPATHSTROKE_SEQ_END</desc><desc>140</desc><desc>139</desc><desc>132</desc><desc>512:
 XPATHSTROKE_SEQ_BEGIN</desc><desc>132</desc><desc>133</desc><desc>109</desc>
-    <path fill="none" stroke="rgb(52,101,164)" d="M 24131,12955 L 24131,12955 
Z"/><desc>512: XPATHSTROKE_SEQ_END</desc><desc>140</desc>
+    <rect class="BoundingBox" stroke="none" fill="none" x="14477" y="4571" 
width="9656" height="8386"/>
+    <path fill="rgb(114,159,207)" stroke="none" d="M 19304,4572 C 22040,4572 
24130,6387 24130,8763 24130,11139 22040,12954 19304,12954 16568,12954 
14478,11139 14478,8763 14478,6387 16568,4572 19304,4572 Z M 14478,4572 L 
14478,4572 Z M 24131,12955 L 24131,12955 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 19304,4572 C 22040,4572 
24130,6387 24130,8763 24130,11139 22040,12954 19304,12954 16568,12954 
14478,11139 14478,8763 14478,6387 16568,4572 19304,4572 Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 14478,4572 L 14478,4572 
Z"/>
+    <path fill="none" stroke="rgb(52,101,164)" d="M 24131,12955 L 24131,12955 
Z"/>
    </g>
   </g>
  </g>
diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index 8220f728c..cc8065c20 100644
--- a/test/httpwstest.cpp
+++ b/test/httpwstest.cpp
@@ -2762,6 +2762,8 @@ void HTTPWSTest::testRenderShapeSelectionImpress()
         if (it != responseSVG.end())
             responseSVG.erase(responseSVG.begin(), ++it);
 
+        stripDescriptions(responseSVG);
+
         CPPUNIT_ASSERT(svgMatch(testname, responseSVG, "shapes_impress.svg"));
     }
     catch (const Poco::Exception& exc)
commit 6379c0b4cbde93b751580dcc3b315693ed39ebce
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu May 9 10:40:22 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    test: fix HTTPWSTest::testRenderShapeSelectionWriterImage() failure with ...
    
    ... debug core.git. Same problem, need to filter out <desc> XML
    elements.
    
    (cherry picked from commit aa5b28ceb08cfc5c0e19c8cc99ae353c47f8a2c0)
    
    Change-Id: I84973b831508e6a073d9c75868307d98a2ada370
    Reviewed-on: https://gerrit.libreoffice.org/72030
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>
    Tested-by: Michael Meeks <michael.me...@collabora.com>

diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index 4edef325e..8220f728c 100644
--- a/test/httpwstest.cpp
+++ b/test/httpwstest.cpp
@@ -2820,6 +2820,8 @@ void HTTPWSTest::testRenderShapeSelectionWriterImage()
         if (it != responseSVG.end())
             responseSVG.erase(responseSVG.begin(), ++it);
 
+        stripDescriptions(responseSVG);
+
         CPPUNIT_ASSERT(svgMatch(testname, responseSVG, 
"non_shape_writer_image.svg"));
     }
     catch (const Poco::Exception& exc)
commit 38a718fed23af99e5e4a3d5d66f554c909cfda76
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu May 9 10:30:11 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    test: fix HTTPWSTest::testRenderShapeSelectionWriter() failure with ...
    
    ... debug core.git.
    
    SVGActionWriter::ImplWriteActions() in core.git writes additional <desc>
    XML elements, guarded with a '#if OSL_DEBUG_LEVEL > 0' block.
    
    Filter these out, so the reference SVG will match both product and debug
    builds.
    
    (cherry picked from commit 0fbabb7a22b1213afe7e3f1b071c4c2876b20584)
    
    Change-Id: Iba3fb25af206c70d5a4ff5801a934dcfd74704de
    Reviewed-on: https://gerrit.libreoffice.org/72029
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>
    Tested-by: Michael Meeks <michael.me...@collabora.com>

diff --git a/.gitreview b/.gitreview
index fc87636e3..489239f60 100644
--- a/.gitreview
+++ b/.gitreview
@@ -3,5 +3,5 @@ host=logerrit
 port=29418
 project=online
 defaultremote=logerrit
-defaultbranch=master
+defaultbranch=distro/collabora/collabora-online-4
 
diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index 0c08be604..4edef325e 100644
--- a/test/httpwstest.cpp
+++ b/test/httpwstest.cpp
@@ -52,6 +52,31 @@
 
 using namespace helpers;
 
+namespace
+{
+/**
+ * Strips <desc>...</desc> strings from an SVG, some of which are only in 
debug builds, so breaks
+ * comparison with a fixed reference.
+ */
+void stripDescriptions(std::vector<char>& svg)
+{
+    while (true)
+    {
+        std::string startDesc("<desc>");
+        auto itStart = std::search(svg.begin(), svg.end(), startDesc.begin(), 
startDesc.end());
+        if (itStart == svg.end())
+            return;
+
+        std::string endDesc("</desc>");
+        auto itEnd = std::search(svg.begin(), svg.end(), endDesc.begin(), 
endDesc.end());
+        if (itEnd == svg.end())
+            return;
+
+        svg.erase(itStart, itEnd + endDesc.size());
+    }
+}
+}
+
 /// Tests the HTTP WebSocket API of loolwsd. The server has to be started 
manually before running this test.
 class HTTPWSTest : public CPPUNIT_NS::TestFixture
 {
@@ -2765,6 +2790,8 @@ void HTTPWSTest::testRenderShapeSelectionWriter()
         if (it != responseSVG.end())
             responseSVG.erase(responseSVG.begin(), ++it);
 
+        stripDescriptions(responseSVG);
+
         CPPUNIT_ASSERT(svgMatch(testname, responseSVG, "shapes_writer.svg"));
     }
     catch (const Poco::Exception& exc)
commit 644dddeb5cd765aab5d02afe2e60f18c28b913ea
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Thu May 2 16:14:12 2019 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    tests: make SVG comparison tests much more helpful.
    
    Dump the SVG we actually get as a .new file for easy upgrade and
    comparison on mismatch.
    
    Change-Id: I607a97ff27a9bf480524efc31877e46d74c5ee3d
    Reviewed-on: https://gerrit.libreoffice.org/71680
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>

diff --git a/test/helpers.hpp b/test/helpers.hpp
index 50aadada7..f5b033c04 100644
--- a/test/helpers.hpp
+++ b/test/helpers.hpp
@@ -651,6 +651,30 @@ inline void 
getServerVersion(std::shared_ptr<LOOLWebSocket>& socket,
 
 }
 
+inline bool svgMatch(const char *testname, const std::vector<char> &response, 
const char *templateFile)
+{
+    const std::vector<char> expectedSVG = 
helpers::readDataFromFile(templateFile);
+    if (expectedSVG != response)
+    {
+        TST_LOG_BEGIN("Svg mismatch: response is\n");
+        if(response.empty())
+            TST_LOG_APPEND("<empty>");
+        else
+            TST_LOG_APPEND(std::string(response.data(), response.size()));
+        TST_LOG_APPEND("\nvs. expected (from '" << templateFile << "' :\n");
+        TST_LOG_APPEND(std::string(expectedSVG.data(), expectedSVG.size()));
+        std::string newName = templateFile;
+        newName += ".new";
+        TST_LOG_APPEND("Updated template writing to: " << newName << "\n");
+        TST_LOG_END;
+        FILE *of = fopen(Poco::Path(TDOC, newName).toString().c_str(), "w");
+        fwrite(response.data(), response.size(), 1, of);
+        fclose(of);
+        return false;
+    }
+    return true;
+}
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/test/httpwstest.cpp b/test/httpwstest.cpp
index b2ba3ed70..0c08be604 100644
--- a/test/httpwstest.cpp
+++ b/test/httpwstest.cpp
@@ -2729,6 +2729,7 @@ void HTTPWSTest::testRenderShapeSelectionImpress()
         std::shared_ptr<LOOLWebSocket> socket = loadDocAndGetSocket(_uri, 
documentURL, testname);
 
         sendTextFrame(socket, "uno .uno:SelectAll", testname);
+        std::this_thread::sleep_for(std::chrono::milliseconds(250));
         sendTextFrame(socket, "rendershapeselection mimetype=image/svg+xml", 
testname);
         std::vector<char> responseSVG = getResponseMessage(socket, 
"shapeselectioncontent:", testname);
         CPPUNIT_ASSERT(!responseSVG.empty());
@@ -2736,8 +2737,7 @@ void HTTPWSTest::testRenderShapeSelectionImpress()
         if (it != responseSVG.end())
             responseSVG.erase(responseSVG.begin(), ++it);
 
-        const std::vector<char> expectedSVG = 
helpers::readDataFromFile("shapes_impress.svg");
-        CPPUNIT_ASSERT(expectedSVG == responseSVG);
+        CPPUNIT_ASSERT(svgMatch(testname, responseSVG, "shapes_impress.svg"));
     }
     catch (const Poco::Exception& exc)
     {
@@ -2757,6 +2757,7 @@ void HTTPWSTest::testRenderShapeSelectionWriter()
 
         // Select the shape with SHIFT + F4
         sendKeyPress(socket, 0, 771 | skShift, testname);
+        std::this_thread::sleep_for(std::chrono::milliseconds(250));
         sendTextFrame(socket, "rendershapeselection mimetype=image/svg+xml", 
testname);
         std::vector<char> responseSVG = getResponseMessage(socket, 
"shapeselectioncontent:", testname);
         CPPUNIT_ASSERT(!responseSVG.empty());
@@ -2764,8 +2765,7 @@ void HTTPWSTest::testRenderShapeSelectionWriter()
         if (it != responseSVG.end())
             responseSVG.erase(responseSVG.begin(), ++it);
 
-        const std::vector<char> expectedSVG = 
helpers::readDataFromFile("shape_writer.svg");
-        CPPUNIT_ASSERT(expectedSVG == responseSVG);
+        CPPUNIT_ASSERT(svgMatch(testname, responseSVG, "shapes_writer.svg"));
     }
     catch (const Poco::Exception& exc)
     {
@@ -2785,6 +2785,7 @@ void HTTPWSTest::testRenderShapeSelectionWriterImage()
 
         // Select the shape with SHIFT + F4
         sendKeyPress(socket, 0, 771 | skShift, testname);
+        std::this_thread::sleep_for(std::chrono::milliseconds(250));
         sendTextFrame(socket, "rendershapeselection mimetype=image/svg+xml", 
testname);
         std::vector<char> responseSVG = getResponseMessage(socket, 
"shapeselectioncontent:", testname);
         CPPUNIT_ASSERT(!responseSVG.empty());
@@ -2792,8 +2793,7 @@ void HTTPWSTest::testRenderShapeSelectionWriterImage()
         if (it != responseSVG.end())
             responseSVG.erase(responseSVG.begin(), ++it);
 
-        const std::vector<char> expectedSVG = 
helpers::readDataFromFile("non_shape_writer_image.svg");
-        CPPUNIT_ASSERT(expectedSVG == responseSVG);
+        CPPUNIT_ASSERT(svgMatch(testname, responseSVG, 
"non_shape_writer_image.svg"));
     }
     catch (const Poco::Exception& exc)
     {
commit c334ac894ea13180f1e7b0b4c2369aa0d71a9443
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Thu Jun 13 14:02:33 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    Sync bundled LOKit headers with core
    
    apart from typos, changes in comments ...
    1) 88014e265a91753b69b70b5f4b246921d16051de is missing from 
distro/collabora/cp-6.0-29
       it's just a nice to have additional check, not important
    2) #ifdef IOS parts are not important in general, because we don't build 
the app from
       this branch
    3) Gesture event support was missing
    
    Change-Id: I9695cbf628e88a96ce9ea78edc77220872e0ac9e

diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.h 
b/bundled/include/LibreOfficeKit/LibreOfficeKit.h
index 133f73875..7f7c0c24a 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.h
@@ -328,18 +328,6 @@ struct _LibreOfficeKitDocumentClass
                             const int width, const int height,
                             const double dpiscale);
 
-#ifdef IOS
-    /// @see lok::Document::paintTileToCGContext().
-    void (*paintTileToCGContext) (LibreOfficeKitDocument* pThis,
-                                  void* rCGContext,
-                                  const int nCanvasWidth,
-                                  const int nCanvasHeight,
-                                  const int nTilePosX,
-                                  const int nTilePosY,
-                                  const int nTileWidth,
-                                  const int nTileHeight);
-#endif // IOS
-
 // CERTIFICATE AND SIGNING
 
     /// @see lok::Document::insertCertificate().
@@ -356,6 +344,7 @@ struct _LibreOfficeKitDocumentClass
 
     /// @see lok::Document::getSignatureState().
     int (*getSignatureState) (LibreOfficeKitDocument* pThis);
+// END CERTIFICATE AND SIGNING
 
     /// @see lok::Document::renderShapeSelection
     size_t (*renderShapeSelection)(LibreOfficeKitDocument* pThis, char** 
pOutput);
@@ -363,6 +352,20 @@ struct _LibreOfficeKitDocumentClass
     /// @see lok::Document::createViewWithOptions().
     int (*createViewWithOptions) (LibreOfficeKitDocument* pThis, const char* 
pOptions);
 
+    /// @see lok::Document::postWindowGestureEvent().
+    void (*postWindowGestureEvent) (LibreOfficeKitDocument* pThis,
+                                  unsigned nWindowId,
+                                  const char* pType,
+                                  int nX,
+                                  int nY,
+                                  int nOffset);
+
+    /// @see lok::Document::selectPart().
+    void (*selectPart) (LibreOfficeKitDocument* pThis, int nPart, int nSelect);
+
+    /// @see lok::Document::moveSelectedParts().
+    void (*moveSelectedParts) (LibreOfficeKitDocument* pThis, int nPosition, 
bool bDuplicate);
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
index a72275ed1..da7c65b70 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -561,34 +561,6 @@ public:
         mpDoc->pClass->postWindowExtTextInputEvent(mpDoc, nWindowId, nType, 
pText);
     }
 
-#ifdef IOS
-    /**
-     * Renders a subset of the document to a Core Graphics context.
-     *
-     * Note that the buffer size and the tile size implicitly supports
-     * rendering at different zoom levels, as the number of rendered pixels and
-     * the rendered rectangle of the document are independent.
-     *
-     * @param rCGContext the CGContextRef, cast to a void*.
-     * @param nCanvasHeight number of pixels in a column of pBuffer.
-     * @param nTilePosX logical X position of the top left corner of the 
rendered rectangle, in TWIPs.
-     * @param nTilePosY logical Y position of the top left corner of the 
rendered rectangle, in TWIPs.
-     * @param nTileWidth logical width of the rendered rectangle, in TWIPs.
-     * @param nTileHeight logical height of the rendered rectangle, in TWIPs.
-     */
-    void paintTileToCGContext(void* rCGContext,
-                              const int nCanvasWidth,
-                              const int nCanvasHeight,
-                              const int nTilePosX,
-                              const int nTilePosY,
-                              const int nTileWidth,
-                              const int nTileHeight)
-    {
-        return mpDoc->pClass->paintTileToCGContext(mpDoc, rCGContext, 
nCanvasWidth, nCanvasHeight,
-                                                   nTilePosX, nTilePosY, 
nTileWidth, nTileHeight);
-    }
-#endif // IOS
-
     /**
      *  Insert certificate (in binary form) to the certificate store.
      */
@@ -633,6 +605,37 @@ public:
         return mpDoc->pClass->renderShapeSelection(mpDoc, pOutput);
     }
 
+    /**
+     * Posts a gesture event to the window with given id.
+     *
+     * @param nWindowId
+     * @param pType Event type, like panStart, panEnd, panUpdate.
+     * @param nX horizontal position in document coordinates
+     * @param nY vertical position in document coordinates
+     * @param nOffset difference value from when the gesture started to 
current value
+     */
+    void postWindowGestureEvent(unsigned nWindowId,
+                              const char* pType,
+                              int nX, int nY, int nOffset)
+    {
+        return mpDoc->pClass->postWindowGestureEvent(mpDoc, nWindowId, pType, 
nX, nY, nOffset);
+    }
+
+    /// Set a part's selection mode.
+    /// nSelect is 0 to deselect, 1 to select, and 2 to toggle.
+    void selectPart(int nPart, int nSelect)
+    {
+        mpDoc->pClass->selectPart(mpDoc, nPart, nSelect);
+    }
+
+    /// Moves the selected pages/slides to a new position.
+    /// nPosition is the new position where the selection
+    /// should go. bDuplicate when true will copy instead of move.
+    void moveSelectedParts(int nPosition, bool bDuplicate)
+    {
+        mpDoc->pClass->moveSelectedParts(mpDoc, nPosition, bDuplicate);
+    }
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h 
b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
index 33d235af8..850e544c6 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -352,7 +352,7 @@ typedef enum
 
     /**
      * The size and/or the position of the view cursor changed. A view cursor
-     * is a cursor of another view, the current view can't change it.
+     * is a cursor of an other view, the current view can't change it.
      *
      * The payload format:
      *
diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKitInit.h 
b/bundled/include/LibreOfficeKit/LibreOfficeKitInit.h
index 6667d2b92..5c600b502 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKitInit.h
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKitInit.h
@@ -287,22 +287,13 @@ static LibreOfficeKit *lok_init_2( const char 
*install_path,  const char *user_p
         }
         free( imp_lib );
         // dlhandle is "leaked"
-        // coverity[leaked_storage] - on purpose
+        // coverity[leaked_storage]
         return pSym( install_path );
     }
 
-    if (user_profile_url != NULL && user_profile_url[0] == '/')
-    {
-        // It should be either a file: URL or a vnd.sun.star.pathname: URL.
-        fprintf( stderr, "second parameter to lok_init_2 '%s' should be a URL, 
not a pathname\n", user_profile_url );
-        lok_dlclose( dlhandle );
-        free( imp_lib );
-        return NULL;
-    }
-
     free( imp_lib );
     // dlhandle is "leaked"
-    // coverity[leaked_storage] - on purpose
+    // coverity[leaked_storage]
     return pSym2( install_path, user_profile_url );
 #else
     return libreofficekit_hook_2( install_path, user_profile_url );
@@ -339,7 +330,7 @@ int lok_preinit( const char *install_path,  const char 
*user_profile_url )
     free( imp_lib );
 
     // dlhandle is "leaked"
-    // coverity[leaked_storage] - on purpose
+    // coverity[leaked_storage]
     return pSym( install_path, user_profile_url );
 }
 #endif
commit 0ead78f38e728c1fb2aa347007258df775d508f7
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Thu Jun 13 13:34:12 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    fix test/Makefile.am:133: error: comment following trailing backslash
    
    Change-Id: I48e211688c51f124258f1073c6e81b2141017e77

diff --git a/test/Makefile.am b/test/Makefile.am
index eab241a95..8b4bb0ed0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -129,7 +129,7 @@ check-local:
 TESTS = unit-convert.la unit-prefork.la unit-tilecache.la unit-timeout.la \
         unit-oauth.la unit-wopi.la unit-wopi-saveas.la \
         unit-wopi-ownertermination.la unit-wopi-versionrestore.la \
-        unit-wopi-documentconflict.la unit_wopi_renamefile.la \
+        unit-wopi-documentconflict.la unit_wopi_renamefile.la
 # TESTS = unit-client.la
 # TESTS += unit-admin.la
 # TESTS += unit-storage.la
commit 6f08c95f6bf92d3111ff0887753ce692b40ee394
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Fri Apr 12 20:16:44 2019 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    perftrace: consume PROFILE_FRAME messages and chart lok ProfileZones.
    
    Also - enable performance tracing in the kit when we're logging at
    trace level.
    
    Change-Id: I838e8d7769b0ead8508c4482c58e0e2564dcee91
    Reviewed-on: https://gerrit.libreoffice.org/71202
    Reviewed-by: Ashod Nakashian <ashnak...@gmail.com>
    Tested-by: Ashod Nakashian <ashnak...@gmail.com>
    Reviewed-on: https://gerrit.libreoffice.org/72872
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h 
b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
index b82e28d93..33d235af8 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -611,6 +611,12 @@ typedef enum
      * On-load notification of the document signature status.
      */
     LOK_CALLBACK_SIGNATURE_STATUS = 40,
+
+    /**
+     * Profiling tracing information single string of multiple lines
+     * containing <pid> <timestamp> and zone start/stop information
+     */
+    LOK_CALLBACK_PROFILE_FRAME = 41
 }
 LibreOfficeKitCallbackType;
 
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 9617a96b7..c4ec709c9 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -2152,6 +2152,7 @@ void ChildSession::loKitCallback(const int type, const 
std::string& payload)
         sendTextFrame("signaturestatus: " + payload);
         break;
 
+    case LOK_CALLBACK_PROFILE_FRAME:
     case LOK_CALLBACK_DOCUMENT_PASSWORD:
     case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY:
         // these are not handled here.
diff --git a/kit/ForKit.cpp b/kit/ForKit.cpp
index 38c15d215..9b3571efb 100644
--- a/kit/ForKit.cpp
+++ b/kit/ForKit.cpp
@@ -528,6 +528,10 @@ int main(int argc, char** argv)
         return Application::EXIT_SOFTWARE;
     }
 
+    // Enable built in profiling dumps
+    if (Log::logger().trace())
+        ::setenv("SAL_PROFILEZONE_EVENTS", "1", 0);
+
     // Initialize LoKit
     if (!globalPreinit(loTemplate))
     {
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 05dbdc722..02ed30337 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -1247,7 +1247,7 @@ public:
             self->setDocumentPassword(type);
             return;
         }
-        else if(type == LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE)
+        else if (type == LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE)
         {
             for (auto& it : self->_sessions)
             {
@@ -1259,6 +1259,8 @@ public:
             }
             return;
         }
+        else if (type == LOK_CALLBACK_PROFILE_FRAME)
+            return; // already trace dumped above.
 
         // Broadcast leftover status indicator callbacks to all clients
         self->broadcastCallbackToClients(type, payload);
diff --git a/kit/KitHelper.hpp b/kit/KitHelper.hpp
index b6225feda..88d090428 100644
--- a/kit/KitHelper.hpp
+++ b/kit/KitHelper.hpp
@@ -131,6 +131,8 @@ namespace LOKitHelper
             return "CONTEXT_CHANGED";
         case LOK_CALLBACK_SIGNATURE_STATUS:
             return "SIGNATURE_STATUS";
+        case LOK_CALLBACK_PROFILE_FRAME:
+            return "PROFILE_FRAME";
        }
 
         assert(!"Missing LOK_CALLBACK type");
diff --git a/scripts/perftrace.pl b/scripts/perftrace.pl
new file mode 100755
index 000000000..c779b95b5
--- /dev/null
+++ b/scripts/perftrace.pl
@@ -0,0 +1,394 @@
+#!/usr/bin/perl -w
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+use strict;
+
+my @input = <STDIN>;
+
+my %emitters;
+my $log_start_date;
+my $log_start_time;
+my @log_start;
+my @events;
+
+my %last_times;     # $time for last key
+my %last_event_idx; # $events[$idx] for last key
+
+# Google Chrome Trace Event Format if set
+my $json = 1;
+
+sub escape($)
+{
+    my $str = shift;
+    $str =~ s/\\/\\\\/g;
+
+    if ($json)
+    {
+        $str =~ s/\t/\\t/g;
+        $str =~ s/\"/\\"/g; # json - and html
+    }
+    else
+    {
+        $str =~ s/\$/\\\$/g;
+        $str =~ s/\'/\\'/g;
+        $str =~ s/\"/\\"/g;
+        $str =~ s/\&/&amp;/g;
+        $str =~ s/\#/&#35;/g;
+        $str =~ s/\>/&gt;/g;
+        $str =~ s/\</&lt;/g;
+    }
+    $str =~ s/[\r\n]+/\\n/g;
+    return $str;
+}
+
+# 23:34:16.123456
+sub splittime($$)
+{
+    my $lineno = shift;
+    my $time = shift;
+    $time =~ m/^(\d\d):(\d\d):(\d\d)\.(\d+)$/ || die "Invalid time at line 
$lineno: '$time'";
+    return ($1, $2, $3, $4);
+}
+
+sub offset_microsecs($$)
+{
+    my @time = splittime(shift, shift);
+
+    my $usec = 0  + $time[0] - $log_start[0];
+    $usec = $usec * 60;
+    $usec = $usec + $time[1] - $log_start[1];
+    $usec = $usec * 60;
+    $usec = $usec + $time[2] - $log_start[2];
+    $usec = $usec * 1000 * 1000;
+    $usec = $usec + $time[3];
+
+    return $usec;
+}
+
+# Important things that happen in pairs
+my @event_pairs = (
+    {
+      name => 'Initialize wsd.',
+      type => 'INF',
+      emitter => '^loolwsd$',
+      start => 'Initializing wsd.\.*',
+      end => 'Listening to prisoner connections.*' },
+    {
+      name => 'initialize forkit',
+      type => 'INF',
+      emitter => '^forkit$',
+      start => 'Initializing frk.\.*',
+      end => 'ForKit process is ready.*' },
+    { # Load
+      emitter => "^lokit_",
+      start => 'Loading url.*for session',
+      end => '^Document loaded in .*ms$' },
+    { # Save - save to a local file.
+      name => 'save to local',
+      emitter => '^docbroker',
+      start => '^Saving doc',
+      end => 'unocommandresult:.*commandName.*\.uno:Save.*success'
+    },
+    { # Save - to storage
+      name => 'save to storage',
+      emitter => '^docbroker',
+      start => '^Saving to storage docKey',
+      end => '^(Saved docKey.* to URI)|(Save skipped as document)',
+    }
+    );
+
+# Idle events
+my @idleend_types = (
+    '^Poll completed'
+    );
+
+my @idlestart_types = (
+    '^Document::ViewCallback end\.'
+    );
+
+my %pair_starts;
+my %proc_names;
+
+sub match_list($@)
+{
+    my $message = shift;
+    while (my $match =  shift) {
+        if ($message =~ m/$match/) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+sub get_event_type($$$)
+{
+    my ($type, $emitter, $message) = @_;
+    return 'idle_end' if (match_list($message, @idleend_types));
+    return 'idle_start' if (match_list($message, @idlestart_types));
+    return '';
+}
+
+sub consume($$$$$$$$$)
+{
+    my ($lineno, $proc, $pid, $tid, $time, $emitter, $type, $message, $line) = 
@_;
+
+    $pid = int($pid);
+    $tid = int($tid);
+
+    # print STDERR "$emitter, $type, $time, $message, $line\n";
+
+    $time = offset_microsecs($lineno, $time) if ($json); # microseconds from 
start
+
+    # accumulate all threads / processes
+    if (!defined $emitters{$emitter}) {
+        $emitters{$emitter} = (scalar keys %emitters) + 1;
+        if ($json) {
+            push @events, "{\"name\": \"thread_name\", \"thread_sort_index\": 
-$tid, \"ph\": \"M\", \"pid\": $pid, \"tid\": $tid, \"args\": { \"name\" : 
\"$emitter\" } }";
+        }
+    }
+    if (!defined $proc_names{$pid}) {
+        $proc_names{$pid} = 1;
+        if ($json) {
+            push @events, "{\"name\": \"process_name\", 
\"process_sort_index\": -$pid, \"ph\": \"M\", \"pid\": $pid, \"args\": { 
\"name\" : \"$proc\" } }";
+        }
+    }
+
+    if ($type eq 'PROF') {
+        # sw::DocumentTimerManager m_aFireIdleJobsTimer: stop 0.047 ms
+        if ($message =~ m/^(.*): stop ([\d\.]+) ms$/) {
+            my $dur_ms = $2;
+            my $dur_us = $dur_ms * 1000.0;
+            my $msg = $1;
+            $time = $time - $dur_us;
+            push @events, "{\"pid\":$pid, \"tid\":$tid, \"ts\":$time, 
\"dur\":$dur_us, \"ph\":\"X\", \"name\":\"$msg\", \"args\":{ \"ms\":$dur_ms } 
}";
+        } else {
+            die "Unknown prof message: '$message'";
+        }
+        return;
+    }
+
+    my $handled = 0;
+    foreach my $match (@event_pairs) {
+       next if (defined $match->{type} && $type ne $match->{type});
+       next if (defined $match->{emitter} && !($emitter =~ 
m/$match->{emitter}/));
+
+       my $start = $match->{start};
+       my $end = $match->{end};
+       my $key;
+       $key = $type if (defined $match->{type});
+       $key .= $emitter.$start;
+       if ($message =~ m/$start/s) {
+#          print STDERR "matched start $key -> $message vs. $start\n";
+           defined $pair_starts{$key} && die "key $key - event $start ($end) 
starts and fails to finish at line: $lineno";
+           $pair_starts{$key} = $time;
+           last;
+       } elsif ($message =~ m/$end/s) {
+#          print STDERR "matched end $key -> $message vs. $end\n";
+           defined $pair_starts{$key} || die "key $key - event $start ($end) 
ends but failed to start at line: $lineno";
+
+           my $content_e = escape($start . $line);
+           my $title_e = escape($match->{name});
+           my $start_time = $pair_starts{$key};
+           my $end_time = $time;
+
+           if ($json)
+           {
+               my $dur = $end_time - $start_time;
+               my $ms = int ($dur / 1000.0);
+               push @events, "{\"pid\":$pid, \"tid\":$tid, \"ts\":$start_time, 
\"dur\":$dur, \"ph\":\"X\", \"name\":\"$title_e\", \"args\":{ \"ms\":$ms } }";
+           }
+           else
+           {
+               my $id = (scalar @events) + 1;
+               push @events, "{id: $id, group: $emitters{$emitter}, ".
+                   "start: new Date('$log_start_date $start_time'), ".
+                   "end: new Date('$log_start_date $end_time'), ".
+                   "content: '$content_e', title: '$title_e'}";
+           }
+           $pair_starts{$key} = undef;
+           last;
+       }
+    }
+
+    my $content_e = escape($message. " " . $line);
+    if ($json)
+    {
+        my $event_type = get_event_type($type, $emitter, $message);
+
+        # join events to the last time
+        my $dur = 100; # 0.1ms default
+        my $key = "$pid-$tid";
+        if (defined($last_times{$key})) {
+            $dur = $time - $last_times{$key};
+            my $idx = $last_event_idx{$key};
+
+            $dur = 1 if ($event_type eq 'idle_end' && $dur > 1);
+            $events[$idx] =~ s/\"dur\":10/\"dur\":$dur/;
+        }
+        $last_times{$key} = $time;
+        $last_event_idx{$key} = scalar @events;
+
+        my $json_type = "\"ph\":\"X\", \"s\":\"p\"";
+        my $replace_dur = 10;
+        $replace_dur = 1 if ($event_type eq 'idle_start'); # miss the regexp
+        push @events, "{\"pid\":$pid, \"tid\":$tid, \"ts\":$time, 
\"dur\":$replace_dur, $json_type, \"name\":\"$content_e\" }";
+    }
+    else
+    {
+        my $id = (scalar @events) + 1;
+        push @events, "{id: $id, group: $emitters{$emitter}, ".
+            "start: new Date('$log_start_date $time'), ".
+            "end: new Date('$log_start_date $time)') + new Date(1), ".
+            "content: '$content_e', title: ''}";
+    }
+}
+
+sub parseProfileFrames($$$$$)
+{
+    my ($lineno, $proc, $pid, $emitter, $message) = @_;
+    my @lines = split(/\n/, $message);
+
+    foreach my $line (@lines) {
+        next if ($line =~ m/start$/); # all data we need is in the end.
+        if ($line =~ m/^(\d+)\s+(\d+)\.(\d+)\s+(.*$)/) {
+            my ($tid, $secs, $fractsecs, $realmsg) = ($1, $2, $3, $4);
+            #       print STDERR "Profile frame '$line'\n";
+            # FIXME: silly to complicate and then re-parse this I guess ...
+            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = 
gmtime($secs);
+            my $time = sprintf("%.2d:%.2d:%02.6f", $hour, $min, 
"$sec.$fractsecs");
+#           print STDERR "time '$time' from '$secs' - " . time() . "\n";
+            consume($lineno, $proc, $pid, $tid, $time, $emitter, 'PROF', 
$realmsg, '');
+        }
+    }
+}
+
+# Open in chrome://tracing
+sub emit_json()
+{
+    my $events_json = join(",\n", @events);
+
+    print STDOUT << "JSONEND"
+{
+"traceEvents": [
+   $events_json
+],
+"displayTimeUnit":"ms",
+"meta_user": "online",
+"meta_cpu_count" : 8
+}
+JSONEND
+;
+}
+
+sub emit_js()
+{
+    my @groups;
+    foreach my $emitter (sort { $emitters{$a} <=> $emitters{$b} } keys 
%emitters) {
+        push @groups, "{id: $emitters{$emitter}, content: '$emitter'}";
+    }
+
+    my $groups_json = join(",\n", @groups);
+    my $items_json = join(",\n", @events);
+
+    my $start_time = "2019-03-27 04:34:57.807344";
+    my $end_time = "2019-03-27 04:35:28.911621";
+
+    print STDOUT <<"HTMLEND"
+<html>
+<head>
+  <title>Online timeline / profile</title>
+  <script src="http://visjs.org/dist/vis.js";></script>
+  <link href="http://visjs.org/dist/vis-timeline-graph2d.min.css"; 
rel="stylesheet" type="text/css" />
+</head>
+
+<body onresize="/*timeline.checkResize();*/">
+
+<h1>Online timeline / profile</h1>
+
+<div id="profile"></div>
+
+<script>
+  var groups = new vis.DataSet([ $groups_json ]);
+  var items = new vis.DataSet([ $items_json ]);
+
+  var options = {
+    stack: false,
+    start: new Date('$start_time'),
+    end: new Date('$end_time'),
+    editable: false,
+    margin: { item: 10, axis: 5 },
+    orientation: 'top'
+  };
+
+  var container = document.getElementById('profile');
+  timeline = new vis.Timeline(container, null, options);
+  timeline.setGroups(groups);
+  timeline.setItems(items);
+
+</script>
+</body>
+</html>
+HTMLEND
+;
+}
+
+# wsd-29885-29885 2019-03-27 ...
+if ($input[0] =~ m/^\S+\s([\d-]+)\s+([\d:\.]+)\s+/)
+{
+    $log_start_date = $1;
+    $log_start_time = $2;
+    @log_start = splittime(0, $2);
+    print STDERR "reading log from $log_start_date / $log_start_time\n";
+} else {
+    die "Malformed log line or no input: $input[0]";
+}
+
+# parse all the lines
+my $lineno = 0;
+while (my $line = $input[$lineno++]) {
+    my ($pevent, $pdetail);
+
+    $line =~ s/\r*\n*//g;
+
+    # wsd-26974-26974 2019-03-27 03:45:46.735736 [ loolwsd ] INF  Initializing 
wsd. Local time: Wed 2019-03-27 03:45:46+0000. Log level is [8].| 
common/Log.cpp:191
+    if ($line =~ 
m/^(\w+)-(\d+)-(\d+)\s+\S+\s+(\S+)\s+\[\s+(\S+)\s+\]\s+(\S+)\s+(.+)\|\s+(\S+)$/)
 {
+        consume($lineno, $1, $2, $3, $4, $5, $6, $7, $8);
+
+    } elsif ($line =~ 
m/^(\w+)-(\d+)-(\d+)\s+\S+\s+(\S+)\s+\[\s+(\S+)\s+\]\s+(\S+)\s+(.+)$/) { # 
split lines ...
+        my ($proc, $pid, $tid, $time, $emitter, $type, $message, $line) = ($1, 
$2, $3, $4, $5, $6, $7);
+        while (my $next = $input[$lineno++]) {
+            # ... | kit/Kit.cpp:1272
+            if ($next =~ m/^(.*)\|\s+(\S+)$/)
+            {
+                $message = $message . $1;
+                $line = $2;
+                last;
+            } else {
+                $message = $message . $next;
+            }
+        }
+
+        # Profile frames are special
+        if ($type eq 'TRC' && $emitter eq 'lo_startmain' &&
+            $message =~ /.*Document::GlobalCallback PROFILE_FRAME.*/) {
+            parseProfileFrames($lineno, $proc, $pid, $emitter, $message);
+        } else {
+            consume($lineno, $proc, $pid, $tid, $time, $emitter, $type, 
$message, $line);
+        }
+    } else {
+        die "Poorly formed line on " . ($lineno - 1) . " - is 
logging.file.flush set to true ? '$line'\n";
+    }
+}
+
+if ($json) {
+    emit_json();
+} else {
+    emit_js();
+}
+
diff --git a/tools/KitClient.cpp b/tools/KitClient.cpp
index 6cafbe48e..bbf9f089b 100644
--- a/tools/KitClient.cpp
+++ b/tools/KitClient.cpp
@@ -84,6 +84,7 @@ extern "C"
             CASE(CLIPBOARD_CHANGED);
             CASE(CONTEXT_CHANGED);
             CASE(SIGNATURE_STATUS);
+            CASE(PROFILE_FRAME);
 #undef CASE
         }
         std::cout << " payload: " << payload << std::endl;
commit a29adf716cc9166552288ab97c6058eef02c9b0f
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Thu Jun 13 13:32:41 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    Bump version to 4.0.5
    
    Change-Id: Iaf3c7a827ec01d396d684eefb3b75c31e7b15c1f

diff --git a/configure.ac b/configure.ac
index ac8f0db37..6cec31107 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
 
 AC_PREREQ([2.63])
 
-AC_INIT([loolwsd], [4.0.4], [libreoffice@lists.freedesktop.org])
+AC_INIT([loolwsd], [4.0.5], [libreoffice@lists.freedesktop.org])
 LT_INIT([shared, disable-static, dlopen])
 
 AM_INIT_AUTOMAKE([1.10 subdir-objects tar-pax -Wno-portability])
commit bf1576f23a1b446320165041c5a9c25bbcb31f60
Author:     Tor Lillqvist <t...@collabora.com>
AuthorDate: Tue Jun 11 17:02:57 2019 +0300
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    Avoid unhandled exception
    
    Change-Id: I2a168a79f334ca5048a85f98350aaa5c08bd1ad8
    Reviewed-on: https://gerrit.libreoffice.org/73948
    Reviewed-by: Tor Lillqvist <t...@collabora.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/loleaflet/src/layer/AnnotationManager.js 
b/loleaflet/src/layer/AnnotationManager.js
index c8a623639..8ff4b6a04 100644
--- a/loleaflet/src/layer/AnnotationManager.js
+++ b/loleaflet/src/layer/AnnotationManager.js
@@ -807,6 +807,14 @@ L.AnnotationManager = L.Class.extend({
                        };
                }
 
+               // What if this._initialLayoutData is still undefined when we 
get here? (I.e. if
+               // contentWrapperClass.length == 0.) No idea. Using
+               // this._initialLayoutData.menuWidth below will lead to an 
unhandled exception.
+               // Maybe best to just return then? Somebody who understands the 
code could fix this
+               // better, perhaps.
+               if (this._initialLayoutData === undefined)
+                       return;
+
                var menuClass = $('.loleaflet-annotation-menu');
                if ((this._initialLayoutData.menuWidth === undefined) && 
menuClass.length > 0) {
                        this._initialLayoutData.menuWidth = 
parseInt(menuClass.css('width'));
commit 697979bc272d45efb07e52da54f336e73625cca1
Author:     Tor Lillqvist <t...@collabora.com>
AuthorDate: Fri May 17 17:06:08 2019 +0300
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    tdf#124906: Hack for dead keys in iOS app and iOS Safari against Online
    
    Handle key events fairly separately for the iOS app and Mobile Safari
    than for other mobile platforms. Use the key property of the keypress
    event and avoid looking at the keyCode property of keyboard events.
    
    There is still one problem left: After typing the dead key, it is not
    displayed temporarily "waiting" for the following letter, as it
    should. But that didn't work before either in the iOS app, as far as I see.
    
    Change-Id: I9df174ba294f855697df59dd016b39cd1b3d905b
    Reviewed-on: https://gerrit.libreoffice.org/73946
    Reviewed-by: Tor Lillqvist <t...@collabora.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index d1f65b240..ea3bccbb7 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -18,6 +18,9 @@ L.Control.MobileInput = L.Control.extend({
                });
 
                this._cursorHandler.on('dragend', this.onDragEnd, this);
+               this._currentKeysDown = {};
+               this._ignoreKeypress = false;
+               this._isMobileSafariOriOSApp = window.ThisIsTheiOSApp || 
navigator.platform === 'iPad' || navigator.platform === 'iPhone';
        },
 
        onAdd: function () {
@@ -101,11 +104,12 @@ L.Control.MobileInput = L.Control.extend({
                this._textArea.setAttribute('spellcheck', 'false');
                L.DomEvent.on(this._textArea, stopEvents, 
L.DomEvent.stopPropagation)
                        .on(this._textArea, 'keydown keypress keyup', 
this.onKeyEvents, this)
-                       .on(this._textArea, 'compositionstart compositionupdate 
compositionend', this.onCompEvents, this)
                        .on(this._textArea, 'textInput', this.onTextInput, this)
-                       .on(this._textArea, 'input', this.onInput, this)
                        .on(this._textArea, 'focus', this.onGotFocus, this)
                        .on(this._textArea, 'blur', this.onLostFocus, this);
+               if (!this._isMobileSafariOriOSApp)
+                       L.DomEvent.on(this._textArea, 'compositionstart 
compositionupdate compositionend', this.onCompEvents, this)
+                               .on(this._textArea, 'input', this.onInput, 
this);
        },
 
        _getSurrogatePair: function(codePoint) {
@@ -122,6 +126,7 @@ L.Control.MobileInput = L.Control.extend({
                    unoKeyCode = handler._toUNOKeyCode(keyCode);
 
                this._keyHandled = this._keyHandled || false;
+               // console.log('==> onKeyEvents: ' + e.type + ':' + e.key + ' 
keyCode=' + keyCode + ' charCode=' + charCode + ' unoKeyCode=' + unoKeyCode + ' 
_keyHandled=' + this._keyHandled + ' _isComposing=' + this._isComposing)
                if (this._isComposing) {
                        if (keyCode === 229 && charCode === 0) {
                                return;
@@ -139,12 +144,42 @@ L.Control.MobileInput = L.Control.extend({
                        // key ignored
                }
                else if (e.type === 'keydown') {
+                       if (this._isMobileSafariOriOSApp) {
+                               if (!this._currentKeysDown[e.key])
+                                       this._currentKeysDown[e.key] = 1;
+                               else
+                                       this._currentKeysDown[e.key]++;
+                               if (this._currentKeysDown[e.key] > 1)
+                                       this._ignoreKeypress = true;
+                       }
                        this._keyHandled = false;
+                       // console.log('    _keyHandled := false');
                        if (handler.handleOnKeyDownKeys[keyCode] && charCode 
=== 0) {
                                docLayer._postKeyboardEvent('input', charCode, 
unoKeyCode);
                                this._lastInput = unoKeyCode;
                        }
                }
+               else if (this._isMobileSafariOriOSApp &&
+                        e.type === 'keypress') {
+                       if (!this._ignoreKeypress) {
+                               // e.key can be longer than one, for instance 
if you press a dead diacritic
+                               // key followed by a letter that it can't 
combine with, like ¨ t => '¨t'.
+                               // But e.key is longer than one also in the 
case of control keys where for
+                               // instance e.key == 'Enter'. Detect the latter 
by comparing e.key against
+                               // e.code.
+                               if (e.key !== e.code) {
+                                       var i;
+                                       for (i = 0; i < e.key.length; ++i) {
+                                               
docLayer._postKeyboardEvent('input', e.key[i].charCodeAt(), 0);
+                                               
docLayer._postKeyboardEvent('up', e.key[i].charCodeAt(), 0);
+                                       }
+                               }
+                               else {
+                                       docLayer._postKeyboardEvent('input', 
charCode, unoKeyCode);
+                                       docLayer._postKeyboardEvent('up', 
charCode, unoKeyCode);
+                               }
+                       }
+               }
                else if ((e.type === 'keypress') && 
(!handler.handleOnKeyDownKeys[keyCode] || charCode !== 0)) {
                        if (charCode === keyCode && charCode !== 13) {
                                // Chrome sets keyCode = charCode for printable 
keys
@@ -167,20 +202,29 @@ L.Control.MobileInput = L.Control.extend({
                        }
                        this._lastInput = unoKeyCode;
                        this._keyHandled = true;
+                       // console.log('    _keyHandled := true');
                }
                else if (e.type === 'keyup') {
-                       if (charCode <= 0xFFFF) {
+                       if (this._isMobileSafariOriOSApp) {
+                               // Yes, forget all keys that are pressed at the 
same time as soon as one
+                               // of them goes up. This seems to match what 
events the system sends.
+                               this._currentKeysDown = {};
+                       }
+                       else if (!this._ignoreKeypress && charCode <= 0xFFFF) {
                                // For non-BMP characters we generated both 
'input' and 'up' events
                                // above already.
                                docLayer._postKeyboardEvent('up', charCode, 
unoKeyCode);
                        }
+                       this._ignoreKeypress = false;
                        this._lastInput = null;
                        this._keyHandled = true;
+                       // console.log('    _keyHandled := true');
                }
                L.DomEvent.stopPropagation(e);
        },
 
        onCompEvents: function (e) {
+               // console.log('==> onCompEvents: ' + e.type);
                var map = this._map;
 
                if (e.type === 'compositionstart' || e.type === 
'compositionupdate') {
@@ -197,6 +241,7 @@ L.Control.MobileInput = L.Control.extend({
        },
 
        onTextInput: function (e) {
+               // console.log('==> onTextInput: _keyHandled=' + 
this._keyHandled);
                if (!this._keyHandled) {
                        this._textData = e.data;
                        this._textArea.value = '';
@@ -206,6 +251,7 @@ L.Control.MobileInput = L.Control.extend({
        },
 
        onInput: function (e) {
+               // console.log('==> onInput: inputType=' + e.inputType);
                var backSpace = this._map.keyboard._toUNOKeyCode(8);
                var i;
 
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 661f4f82c..11f2995e7 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1545,6 +1545,7 @@ L.TileLayer = L.GridLayer.extend({
        },
 
        _postKeyboardEvent: function(type, charcode, keycode) {
+               // console.log('==> _postKeyboardEvent type=' + type + ' 
charcode=' + charcode + ' keycode=' + keycode);
                if (this._docType === 'spreadsheet' && this._prevCellCursor && 
type === 'input') {
                        if (keycode === 1030) { // PgUp
                                if (this._cellCursorOnPgUp) {
@@ -1570,6 +1571,7 @@ L.TileLayer = L.GridLayer.extend({
 
        // if winId=0, then event is posted on the document
        _postCompositionEvent: function(winId, type, text) {
+               // console.log('==> _postCompositionEvent type=' + type + ' 
text="' + text + '"');
                this._map._socket.sendMessage('textinput id=' + winId + ' 
type=' + type + ' text=' + encodeURIComponent(text));
        },
 
commit 425e0874cde393a685de00ab3dce2faf908c695f
Author:     Tor Lillqvist <t...@collabora.com>
AuthorDate: Thu Mar 28 00:07:15 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    tdf#124179: Make it possible to drag an image using a touch gesture
    
    First select the image (so that the circular handles show up), then
    drag it.
    
    Change-Id: Idda55423bd38675841ed2a1ed21143d765b83ed1
    Reviewed-on: https://gerrit.libreoffice.org/73945
    Reviewed-by: Tor Lillqvist <t...@collabora.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/loleaflet/src/dom/DomEvent.js b/loleaflet/src/dom/DomEvent.js
index 67e04c4ce..0d59d54aa 100644
--- a/loleaflet/src/dom/DomEvent.js
+++ b/loleaflet/src/dom/DomEvent.js
@@ -178,6 +178,9 @@ L.DomEvent = {
 
        getMousePosition: function (e, container) {
                if (!container) {
+                       if (e.clientX === undefined && e.touches !== undefined)
+                               return new L.Point(e.touches[0].clientX, 
e.touches[0].clientY);
+
                        return new L.Point(e.clientX, e.clientY);
                }
 
@@ -192,6 +195,11 @@ L.DomEvent = {
                        left = top = 0;
                }
 
+               if (e.clientX === undefined && e.touches !== undefined)
+                       return new L.Point(
+                               e.touches[0].clientX - left - 
container.clientLeft,
+                               e.touches[0].clientY - top - 
container.clientTop);
+
                return new L.Point(
                        e.clientX - left - container.clientLeft,
                        e.clientY - top - container.clientTop);
diff --git a/loleaflet/src/layer/vector/SVGGroup.js 
b/loleaflet/src/layer/vector/SVGGroup.js
index da8a40079..12c3a2931 100644
--- a/loleaflet/src/layer/vector/SVGGroup.js
+++ b/loleaflet/src/layer/vector/SVGGroup.js
@@ -9,6 +9,11 @@ L.SVGGroup = L.Layer.extend({
                noClip: true
        },
 
+       lastTouchEvent: {
+               clientX: 0,
+               clientY: 0
+       },
+
        initialize: function (bounds, options) {
                L.setOptions(this, options);
                this._bounds = bounds;
@@ -47,6 +52,11 @@ L.SVGGroup = L.Layer.extend({
        },
 
        _onDragStart: function(evt) {
+               if (evt.type === 'touchstart') {
+                       this.lastTouchEvent.clientX = evt.touches[0].clientX;
+                       this.lastTouchEvent.clientY = evt.touches[0].clientY;
+               }
+
                if (!this._dragShape)
                        return;
                this._moved = false;
@@ -54,6 +64,9 @@ L.SVGGroup = L.Layer.extend({
                L.DomEvent.on(this._dragShape, 'mousemove', this._onDrag, this);
                L.DomEvent.on(this._dragShape, 'mouseup', this._onDragEnd, 
this);
 
+               L.DomEvent.on(this._dragShape, 'touchmove', this._onDrag, this);
+               L.DomEvent.on(this._dragShape, 'touchend', this._onDragEnd, 
this);
+
                var data = {
                        originalEvent: evt,
                        containerPoint: 
this._map.mouseEventToContainerPoint(evt)
@@ -65,6 +78,11 @@ L.SVGGroup = L.Layer.extend({
        },
 
        _onDrag: function(evt) {
+               if (evt.type === 'touchmove') {
+                       this.lastTouchEvent.clientX = evt.touches[0].clientX;
+                       this.lastTouchEvent.clientY = evt.touches[0].clientY;
+               }
+
                if (!this._dragShape)
                        return;
 
@@ -82,11 +100,17 @@ L.SVGGroup = L.Layer.extend({
        },
 
        _onDragEnd: function(evt) {
+               if (evt.type === 'touchend' && evt.touches.length == 0)
+                       evt.touches[0] = {clientX: this.lastTouchEvent.clientX, 
clientY: this.lastTouchEvent.clientY};
+
                if (!this._dragShape)
                        return;
                L.DomEvent.off(this._dragShape, 'mousemove', this._onDrag, 
this);
                L.DomEvent.off(this._dragShape, 'mouseup', this._onDragEnd, 
this);
 
+               L.DomEvent.off(this._dragShape, 'touchmove', this._onDrag, 
this);
+               L.DomEvent.off(this._dragShape, 'touchend', this._onDragEnd, 
this);
+
                this._moved = false;
                this._hideEmbeddedSVG();
                var pos = this._map.mouseEventToLatLng(evt);
@@ -129,6 +153,7 @@ L.SVGGroup = L.Layer.extend({
                        this._path.appendChild(this._rect._path);
                        this._dragShape = this._rect._path;
                        L.DomEvent.on(this._rect._path, 'mousedown', 
this._onDragStart, this);
+                       L.DomEvent.on(this._rect._path, 'touchstart', 
this._onDragStart, this);
                }
                this._update();
        },
commit 7a14949d862334a376cb3dfa180e69167c25288a
Author:     Tor Lillqvist <t...@collabora.com>
AuthorDate: Tue Mar 26 01:53:21 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:39 2019 +0200

    tdf#124178: Handle non-BMP character input on mobile devices
    
    Sadly, we must split such into a surrogate pair.
    
    Change-Id: I59e493963cf71d14d389c58a94b17d16aec2311e
    Reviewed-on: https://gerrit.libreoffice.org/73942
    Reviewed-by: Tor Lillqvist <t...@collabora.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/loleaflet/src/control/Control.MobileInput.js 
b/loleaflet/src/control/Control.MobileInput.js
index 467f44dad..d1f65b240 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -108,6 +108,12 @@ L.Control.MobileInput = L.Control.extend({
                        .on(this._textArea, 'blur', this.onLostFocus, this);
        },
 
+       _getSurrogatePair: function(codePoint) {
+               var highSurrogate = Math.floor((codePoint - 0x10000) / 0x400) + 
0xD800;
+               var lowSurrogate = (codePoint - 0x10000) % 0x400 + 0xDC00;
+               return [highSurrogate, lowSurrogate];
+       },
+
        onKeyEvents: function (e) {
                var keyCode = e.keyCode,
                    charCode = e.charCode,
@@ -147,12 +153,27 @@ L.Control.MobileInput = L.Control.extend({
                                unoKeyCode = handler._toUNOKeyCode(keyCode);
                        }
 
-                       docLayer._postKeyboardEvent('input', charCode, 
unoKeyCode);
+                       if (charCode > 0xFFFF) {
+                               // We must handle non-BMP code points as two 
separate key events
+                               // because the sad VCL KeyEvent only takes a 
16-bit "characters".
+                               var surrogatePair = 
this._getSurrogatePair(charCode);
+                               docLayer._postKeyboardEvent('input', 
surrogatePair[0], unoKeyCode);
+                               docLayer._postKeyboardEvent('up', 
surrogatePair[0], unoKeyCode);
+                               docLayer._postKeyboardEvent('input', 
surrogatePair[1], unoKeyCode);
+                               docLayer._postKeyboardEvent('up', 
surrogatePair[1], unoKeyCode);
+                       }
+                       else {
+                               docLayer._postKeyboardEvent('input', charCode, 
unoKeyCode);
+                       }
                        this._lastInput = unoKeyCode;
                        this._keyHandled = true;
                }
                else if (e.type === 'keyup') {
-                       docLayer._postKeyboardEvent('up', charCode, unoKeyCode);
+                       if (charCode <= 0xFFFF) {
+                               // For non-BMP characters we generated both 
'input' and 'up' events
+                               // above already.
+                               docLayer._postKeyboardEvent('up', charCode, 
unoKeyCode);
+                       }
                        this._lastInput = null;
                        this._keyHandled = true;
                }
commit 4563b8ab9c4f61297366f098627525ab4b5d4222
Author:     Szymon Kłos <szymon.k...@collabora.com>
AuthorDate: Thu Apr 4 18:36:21 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:38 2019 +0200

    Calc scrolling with selection fix
    
    Scrolling with selected sheet was endless.
    This change allows scrolling using mouse wheel
    when scrollbar is close to the end of screen
    and prevents from doubled updates.
    
    Extension of work done before in:
    ec24337b11e097e98266c51d8fe56c42a8ec613a
    
    Change-Id: Id76818158c0d9988b323ec52a408efd5ae6a9da5
    Reviewed-on: https://gerrit.libreoffice.org/70266
    Reviewed-by: Szymon Kłos <szymon.k...@collabora.com>
    Tested-by: Szymon Kłos <szymon.k...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/70290
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 64949d2a044986ef992e323ad675e0c808496563)
    Reviewed-on: https://gerrit.libreoffice.org/73935
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/loleaflet/js/jquery.mCustomScrollbar.js 
b/loleaflet/js/jquery.mCustomScrollbar.js
index abaa9b9e0..14bf9d9a2 100644
--- a/loleaflet/js/jquery.mCustomScrollbar.js
+++ b/loleaflet/js/jquery.mCustomScrollbar.js
@@ -643,6 +643,15 @@ and dependencies (minified).
                                        
                                        if($this.data(pluginPfx)){ /* check if 
plugin has initialized */
                                        
+                                               /* Ugly hack extension: When 
vertical scrollbar position
+                                                  was very close to the end of 
spreadsheet, next part of
+                                                  the document has to be 
loaded. This contidion is fulfilled
+                                                  in that case. We need to 
ignore it to prevent scrollbar
+                                                  from reaching the end what 
blocks possibility to scroll down. */
+                                               if(!window.ThisIsAMobileApp && 
options && options.timeout == undefined
+                                                       && 
options.calledFromInvalidateCursorMsg == undefined)
+                                                       return;
+
                                                var 
d=$this.data(pluginPfx),o=d.opt,
                                                        /* method default 
options */
                                                        methodDefaults={
commit 188b382376418d588c82312053b106bd93df1d4d
Author:     Tor Lillqvist <t...@collabora.com>
AuthorDate: Thu Jan 24 12:55:00 2019 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:38 2019 +0200

    tdf#122359: Un-revert and edit in a attempt to reach a compromise
    
    Change-Id: I0f23c70b3c4a07bae24e490eb8d1a5e56a4ba9ee
    Reviewed-on: https://gerrit.libreoffice.org/70289
    Reviewed-by: Tor Lillqvist <t...@collabora.com>
    Tested-by: Tor Lillqvist <t...@collabora.com>
    (cherry picked from commit 4506d7c2de2c20c4211300ac58cecd2a42e7bb27)
    Reviewed-on: https://gerrit.libreoffice.org/73934
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/loleaflet/js/jquery.mCustomScrollbar.js 
b/loleaflet/js/jquery.mCustomScrollbar.js
index 2785ec65c..abaa9b9e0 100644
--- a/loleaflet/js/jquery.mCustomScrollbar.js
+++ b/loleaflet/js/jquery.mCustomScrollbar.js
@@ -2138,8 +2138,7 @@ and dependencies (minified).
                                                // hidden part of the document 
(for instance when pressing enter on the
                                                // last visible line). The 
options.timeout==1 is a silly way to detect
                                                // the mouse-wheel scrolling.
-                                               if((window.ThisIsAMobileApp && 
(options.drag || options.timeout===1 || 
options.calledFromInvalidateCursorMsg==true)) ||
-                                                  (!window.ThisIsAMobileApp)) {
+                                               if(!window.ThisIsAMobileApp || 
options.drag || options.timeout===1 || 
options.calledFromInvalidateCursorMsg==true){
                                                        /* callbacks: 
whileScrolling */
                                                        
if(_cb("whileScrolling")){_mcs(); o.callbacks.whileScrolling.call(el[0]);}
                                                }
diff --git a/loleaflet/src/control/Control.Scroll.js 
b/loleaflet/src/control/Control.Scroll.js
index adb85d55b..ab3fc47c6 100644
--- a/loleaflet/src/control/Control.Scroll.js
+++ b/loleaflet/src/control/Control.Scroll.js
@@ -158,11 +158,7 @@ L.Control.Scroll = L.Control.extend({
 
        _onScrollTo: function (e) {
                // triggered by the document (e.g. search result out of the 
viewing area)
-               if (window.ThisIsAMobileApp) {
-                       $('.scroll-container').mCustomScrollbar('scrollTo', 
[e.y, e.x], {calledFromInvalidateCursorMsg: e.calledFromInvalidateCursorMsg});
-               } else {
-                       $('.scroll-container').mCustomScrollbar('scrollTo', 
[e.y, e.x]);
-               }
+               $('.scroll-container').mCustomScrollbar('scrollTo', [e.y, e.x], 
{calledFromInvalidateCursorMsg: e.calledFromInvalidateCursorMsg});
        },
 
        _onScrollBy: function (e) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 00759ca65..661f4f82c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1634,11 +1634,7 @@ L.TileLayer = L.GridLayer.extend({
                        if (!zoom && !(this._selectionHandles.start && 
this._selectionHandles.start.isDragged) &&
                            !(this._selectionHandles.end && 
this._selectionHandles.end.isDragged) &&
                            !(docLayer._followEditor || docLayer._followUser)) {
-                               if (window.ThisIsAMobileApp) {
-                                       this._map.fire('scrollto', {x: 
center.x, y: center.y, calledFromInvalidateCursorMsg: scroll !== undefined});
-                               } else {
-                                       this._map.fire('scrollto', {x: 
center.x, y: center.y});
-                               }
+                               this._map.fire('scrollto', {x: center.x, y: 
center.y, calledFromInvalidateCursorMsg: scroll !== undefined});
                        }
                }
 
commit 8518b5fed5d6825847a7db27d2983c8d9ddf3c1f
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Wed May 22 10:54:36 2019 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:38 2019 +0200

    tests for chunked transfer encoding parser.
    
    Change-Id: Ic55669ab7cc55bb44e8f7a00f30231b44f10535a
    Reviewed-on: https://gerrit.libreoffice.org/72749
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/test/UnitHTTP.cpp b/test/UnitHTTP.cpp
index d0530fe10..e86593f22 100644
--- a/test/UnitHTTP.cpp
+++ b/test/UnitHTTP.cpp
@@ -13,6 +13,7 @@
 
 #include <helpers.hpp>
 #include <Poco/Util/Application.h>
+#include <Poco/Net/StreamSocket.h>
 #include <Poco/Net/StringPartSource.h>
 #include <Poco/Net/HTMLForm.h>
 #include <Poco/Net/HTTPRequest.h>
@@ -37,9 +38,9 @@ public:
         config.setBool("ssl.enable", true);
     }
 
-    // FIXME: can hook with (UnitWSD::get().handleHttpRequest(request, 
message, socket)) ...
-    void invokeTest() override
+    void testContinue()
     {
+        std::cerr << "testContinue\n";
         for (int i = 0; i < 3; ++i)
         {
             std::unique_ptr<Poco::Net::HTTPClientSession> 
session(helpers::createSession(Poco::URI(helpers::getTestServerURI())));
@@ -81,9 +82,135 @@ public:
                 return;
             }
         }
-        // Give those convertors time to save and cleanup.
-        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+    }
+
+    void writeString(const std::shared_ptr<Poco::Net::StreamSocket> &socket, 
std::string str)
+    {
+        socket->sendBytes(str.c_str(), str.size());
+    }
+
+    bool expectString(const std::shared_ptr<Poco::Net::StreamSocket> &socket, 
std::string str)
+    {
+        char buffer[str.size() + 64] = { 0, };
+        int got = socket->receiveBytes(buffer, str.size());
+        if (got != (int)str.size() ||
+            strncmp(buffer, str.c_str(), got))
+        {
+            std::cerr << "testChunks got " << got << " mismatching strings '" 
<< buffer << " vs. expected '" << str << "'\n";
+            exitTest(TestResult::Failed);
+            return false;
+        }
+        else
+            return true;
+    }
+
+    void testChunks()
+    {
+        std::cerr << "testChunks\n";
+
+        std::shared_ptr<Poco::Net::StreamSocket> socket = 
helpers::createRawSocket();
+
+        writeString(
+            socket,
+            "POST /lool/convert-to/txt HTTP/1.1\r\n"
+            "Host: localhost:9980\r\n"
+            "User-Agent: looltests/1.2.3\r\n"
+            "Accept: */*\r\n"
+            "Expect: 100-continue\r\n"
+            "Transfer-Encoding: chunked\r\n"
+            "Content-Type: multipart/form-data; "
+            "boundary=------------------------5a0cd5c881663db4\r\n\r\n");
+        if (!expectString(
+                socket,
+                "HTTP/1.1 100 Continue\r\n\r\n"))
+            return;
+
+#define START_CHUNK_HEX(len) len "\r\n"
+#define END_CHUNK "\r\n"
+        writeString(
+            socket,
+            START_CHUNK_HEX("8A")
+            "--------------------------5a0cd5c881663db4\r\n"
+            "Content-Disposition: form-data; name=\"data\"; 
filename=\"test.txt\"\r\n"
+            "Content-Type: text/plain\r\n"
+            "\r\n"
+            END_CHUNK
+
+            START_CHUNK_HEX("12")
+            "This is some text."
+            END_CHUNK
+
+            START_CHUNK_HEX("1")
+            "\n"
+            END_CHUNK
+
+            "  4 room:for expansion!! cf. leading spaces and nasies 
<>!\"\'?=)\r\n"
+            "And "
+            END_CHUNK
+
+            START_CHUNK_HEX("1")
+            "s"
+            END_CHUNK
+
+            START_CHUNK_HEX("a")
+            "ome more.\n"
+            END_CHUNK
+            );
+        writeString(
+            socket,
+            START_CHUNK_HEX("30")
+            "\r\n"
+            "--------------------------5a0cd5c881663db4--\r\n"
+            END_CHUNK);
+
+        writeString(socket, START_CHUNK_HEX("0"));
+
+        char buffer[4096] = { 0, };
+        int got = socket->receiveBytes(buffer, 4096);
+        std::string start =
+            "HTTP/1.0 200 OK\r\n"
+            "Content-Disposition: attachment; filename=\"test.txt\"\r\n";
+
+        if (strncmp(buffer, start.c_str(), start.size()))
+        {
+            std::cerr << "missing pre-amble " << got << " '" << buffer << " 
vs. expected '" << start << "'\n";
+            exitTest(TestResult::Failed);
+            return;
+        }
 
+        // TODO: check content-length etc.
+
+        const char *ptr = strstr(buffer, "\r\n\r\n");
+        if (!ptr)
+        {
+            std::cerr << "missing separator " << got << " '" << buffer << "\n";
+            exitTest(TestResult::Failed);
+            return;
+        }
+
+        // Oddly we need another read to get the content.
+        got = socket->receiveBytes(buffer, 4096);
+        if (got >=0 )
+            buffer[got] = '\0';
+        else
+        {
+            std::cerr << "No content returned " << got << "\n";
+            exitTest(TestResult::Failed);
+            return;
+        }
+
+        if (strcmp(buffer, "\357\273\277This is some text.\nAnd some more.\n"))
+        {
+            std::cerr << "unexpected file content " << got << " '" << buffer 
<< "\n";
+            exitTest(TestResult::Failed);
+            return;
+        }
+    }
+
+    void invokeTest() override
+    {
+        testChunks();
+        testContinue();
         std::cerr << "All tests passed.\n";
         exitTest(TestResult::Ok);
     }
diff --git a/test/helpers.hpp b/test/helpers.hpp
index 13b351762..50aadada7 100644
--- a/test/helpers.hpp
+++ b/test/helpers.hpp
@@ -21,6 +21,8 @@
 #include <Poco/Net/HTTPResponse.h>
 #include <Poco/Net/HTTPSClientSession.h>
 #include <Poco/Net/NetException.h>
+#include <Poco/Net/StreamSocket.h>
+#include <Poco/Net/SecureStreamSocket.h>
 #include <Poco/Net/Socket.h>
 #include <Poco/Path.h>
 #include <Poco/StringTokenizer.h>
@@ -166,18 +168,33 @@ Poco::Net::HTTPClientSession* createSession(const 
Poco::URI& uri)
 #endif
 }
 
-inline
-std::string const & getTestServerURI()
+inline int getClientPort()
 {
     static const char* clientPort = std::getenv("LOOL_TEST_CLIENT_PORT");
+    return clientPort? atoi(clientPort) : DEFAULT_CLIENT_PORT_NUMBER;
+}
+
+inline std::shared_ptr<Poco::Net::StreamSocket> createRawSocket()
+{
+    return
+#if ENABLE_SSL
+        std::make_shared<Poco::Net::SecureStreamSocket>
+#else
+        std::make_shared<Poco::Net::StreamSocket>
+#endif
+            (Poco::Net::SocketAddress("127.0.0.1", getClientPort()));
+}
 
+inline
+std::string const & getTestServerURI()
+{
     static std::string serverURI(
 #if ENABLE_SSL
             "https://127.0.0.1:";
 #else
             "http://127.0.0.1:";
 #endif
-            + (clientPort? std::string(clientPort) : 
std::to_string(DEFAULT_CLIENT_PORT_NUMBER)));
+            + std::to_string(getClientPort()));
 
     return serverURI;
 }
commit bad044a89ecd86a997f314e103047074970d26fc
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Wed May 22 11:25:28 2019 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:38 2019 +0200

    Initial chunked transfer encoding.
    
    Important for convert-to on larger documents and/or with newer curls.
    
    Change-Id: Id18be6d22741a3af7cee39a069c509e4f662977b
    Reviewed-on: https://gerrit.libreoffice.org/72748
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/common/Util.hpp b/common/Util.hpp
index 07af2a6a4..0190f953b 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -162,6 +162,18 @@ namespace Util
     // Extract all json entries into a map.
     std::map<std::string, std::string> JsonToMap(const std::string& 
jsonString);
 
+    inline int hexDigitFromChar(char c)
+    {
+        if (c >= '0' && c <= '9')
+            return c - '0';
+        else if (c >= 'a' && c <= 'f')
+            return c - 'a' + 10;
+        else if (c >= 'A' && c <= 'F')
+            return c - 'A' + 10;
+        else
+            return -1;
+    }
+
     /// Dump a lineof data as hex
     inline std::string stringifyHexLine(
                             const std::vector<char> &buffer,
@@ -230,6 +242,17 @@ namespace Util
         os.flush();
     }
 
+    inline std::string dumpHex (const char *legend, const char *prefix,
+                                const std::vector<char>::iterator &startIt,
+                                const std::vector<char>::iterator &endIt,
+                                bool skipDup = true, const unsigned int width 
= 32)
+    {
+        std::ostringstream oss;
+        std::vector<char> data(startIt, endIt);
+        dumpHex(oss, legend, prefix, data, skipDup, width);
+        return oss.str();
+    }
+
     /// Trim spaces from the left. Just spaces.
     inline std::string& ltrim(std::string& s)
     {
diff --git a/net/Socket.cpp b/net/Socket.cpp
index 2737c6f1c..5b863c8ae 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -432,14 +432,21 @@ bool ServerSocket::bind(Type type, int port)
 
 #ifndef MOBILEAPP
 
+// For a verbose life, tweak here:
+#if 0
+#  define LOG_CHUNK(X) LOG_TRC(X)
+#else
+#  define LOG_CHUNK(X)
+#endif
+
 bool StreamSocket::parseHeader(const char *clientName,
                                Poco::MemoryInputStream &message,
                                Poco::Net::HTTPRequest &request,
-                               size_t *requestSize)
+                               MessageMap *map)
 {
     LOG_TRC("#" << getFD() << " handling incoming " << _inBuffer.size() << " 
bytes.");
 
-    assert(!requestSize || *requestSize == 0);
+    assert(!map || (map->_headerSize == 0 && map->_messageSize == 0));
 
     // Find the end of the header, if any.
     static const std::string marker("\r\n\r\n");
@@ -453,8 +460,11 @@ bool StreamSocket::parseHeader(const char *clientName,
 
     // Skip the marker.
     itBody += marker.size();
-    if (requestSize)
-        *requestSize = static_cast<size_t>(itBody - _inBuffer.begin());
+    if (map) // a reasonable guess so far
+    {
+        map->_headerSize = static_cast<size_t>(itBody - _inBuffer.begin());
+        map->_messageSize = map->_headerSize;
+    }
 
     try
     {
@@ -485,6 +495,8 @@ bool StreamSocket::parseHeader(const char *clientName,
             LOG_DBG("Not enough content yet: ContentLength: " << contentLength 
<< ", available: " << available);
             return false;
         }
+        if (map)
+            map->_messageSize += contentLength;
 
         if (request.getExpectContinue() && !_sentHTTPContinue)
         {
@@ -494,6 +506,79 @@ bool StreamSocket::parseHeader(const char *clientName,
                  sizeof("HTTP/1.1 100 Continue\r\n\r\n") - 1);
             _sentHTTPContinue = true;
         }
+
+        if (request.getChunkedTransferEncoding())
+        {
+            // keep the header
+            if (map)
+                map->_spans.push_back(std::pair<size_t, size_t>(0, itBody - 
_inBuffer.begin()));
+
+            int chunk = 0;
+            while (itBody != _inBuffer.end())
+            {
+                auto chunkStart = itBody;
+
+                // skip whitespace
+                for (; itBody != _inBuffer.end() && isascii(*itBody) && 
isspace(*itBody); ++itBody)
+                    ; // skip.
+
+                // each chunk is preceeded by its length in hex.
+                size_t chunkLen = 0;
+                for (; itBody != _inBuffer.end(); ++itBody)
+                {
+                    int digit = Util::hexDigitFromChar(*itBody);
+                    if (digit >= 0)
+                        chunkLen = chunkLen * 16 + digit;
+                    else
+                        break;
+                }
+
+                LOG_CHUNK("Chunk of length " << chunkLen);
+
+                for (; itBody != _inBuffer.end() && *itBody != '\n'; ++itBody)
+                    ; // skip to end of line
+
+                if (itBody != _inBuffer.end())
+                    itBody++; /* \n */;
+
+                // skip the chunk.
+                auto chunkOffset = itBody - _inBuffer.begin();
+                auto chunkAvailable = _inBuffer.size() - chunkOffset;
+
+                if (chunkLen == 0) // we're complete.
+                {
+                    map->_messageSize = chunkOffset;
+                    return true;
+                }
+
+                if (chunkLen > chunkAvailable + 2)
+                {
+                    LOG_DBG("Not enough content yet in chunk " << chunk <<
+                            " starting at offset " << (chunkStart - 
_inBuffer.begin()) <<
+                            " chunk len: " << chunkLen << ", available: " << 
chunkAvailable);
+                    return false;
+                }
+                itBody += chunkLen;
+
+                map->_spans.push_back(std::pair<size_t,size_t>(chunkOffset, 
chunkLen));
+
+                if (*itBody != '\r' || *(itBody + 1) != '\n')
+                {
+                    LOG_ERR("Missing \\r\\n at end of chunk " << chunk << " of 
length " << chunkLen);
+                    LOG_CHUNK("Chunk " << chunk << " is: \n" << 
Util::dumpHex("", "", chunkStart, itBody + 1, false));
+                    return false; // TODO: throw something sensible in this 
case
+                }
+                else
+                {
+                    LOG_CHUNK("Chunk " << chunk << " is: \n" << 
Util::dumpHex("", "", chunkStart, itBody + 1, false));
+                }
+
+                itBody+=2;
+                chunk++;
+            }
+            LOG_TRC("Not enough chunks yet, so far " << chunk << " chunks of 
total length " << (itBody - _inBuffer.begin()));
+            return false;
+        }
     }
     catch (const Poco::Exception& exc)
     {
@@ -513,6 +598,39 @@ bool StreamSocket::parseHeader(const char *clientName,
     return true;
 }
 
+bool StreamSocket::compactChunks(MessageMap *map)
+{
+    assert (map);
+    if (!map->_spans.size())
+        return false; // single message.
+
+    LOG_CHUNK("Pre-compact " << map->_spans.size() << " chunks: \n" <<
+              Util::dumpHex("", "", _inBuffer.begin(), _inBuffer.end(), 
false));
+
+    char *first = &_inBuffer[0];
+    char *dest = first;
+    for (auto &span : map->_spans)
+    {
+        std::memmove(dest, &_inBuffer[span.first], span.second);
+        dest += span.second;
+    }
+
+    // Erase the duplicate bits.
+    size_t newEnd = dest - first;
+    size_t gap = map->_messageSize - newEnd;
+    _inBuffer.erase(_inBuffer.begin() + newEnd, _inBuffer.begin() + 
map->_messageSize);
+
+    LOG_CHUNK("Post-compact with erase of " << newEnd << " to " << 
map->_messageSize << " giving: \n" <<
+              Util::dumpHex("", "", _inBuffer.begin(), _inBuffer.end(), 
false));
+
+    // shrink our size to fit
+    map->_messageSize -= gap;
+
+    dumpState(std::cerr);
+
+    return true;
+}
+
 namespace HttpHelper
 {
     void sendUncompressedFileContent(const std::shared_ptr<StreamSocket>& 
socket,
diff --git a/net/Socket.hpp b/net/Socket.hpp
index de20991e1..49cfeab49 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -930,9 +930,21 @@ public:
         return socket;
     }
 
+        /// Messages can be in chunks, only parts of message being valid.
+    struct MessageMap {
+        MessageMap() : _headerSize(0), _messageSize(0) {}
+        /// Size of HTTP headers
+        size_t _headerSize;
+        /// Entire size of data associated with this message
+        size_t _messageSize;
+        // offset + lengths to collate into the real stream
+        std::vector<std::pair<size_t, size_t>> _spans;
+    };
+
     /// Remove the first @count bytes from input buffer
-    void eraseFirstInputBytes(size_t count)
+    void eraseFirstInputBytes(const MessageMap &map)
     {
+        size_t count = map._headerSize;
         size_t toErase = std::min(count, _inBuffer.size());
         if (toErase < count)
             LOG_ERR("#" << getFD() << ": attempted to remove: " << count << " 
which is > size: " << _inBuffer.size() << " clamped to " << toErase);
@@ -940,12 +952,16 @@ public:
             _inBuffer.erase(_inBuffer.begin(), _inBuffer.begin() + count);
     }
 
+    /// Compacts chunk headers away leaving just the data we want
+    /// returns true if we did any re-sizing/movement of _inBuffer.
+    bool compactChunks(MessageMap *map);
+
     /// Detects if we have an HTTP header in the provided message and
     /// populates a request for that.
     bool parseHeader(const char *clientLoggingName,
                      Poco::MemoryInputStream &message,
                      Poco::Net::HTTPRequest &request,
-                     size_t *requestSize = nullptr);
+                     MessageMap *map = nullptr);
 
     /// Get input/output statistics on this stream
     void getIOStats(uint64_t &sent, uint64_t &recv)
@@ -966,6 +982,8 @@ public:
 
 protected:
 
+    std::vector<std::pair<size_t, size_t>> findChunks(Poco::Net::HTTPRequest 
&request);
+
     /// Called when a polling event is received.
     /// @events is the mask of events that triggered the wake.
     void handlePoll(SocketDisposition &disposition,
diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp
index 472360ab6..f483661be 100644
--- a/net/WebSocketHandler.hpp
+++ b/net/WebSocketHandler.hpp
@@ -683,7 +683,7 @@ protected:
         LOG_TRC("Incoming client websocket upgrade response: " << 
std::string(&socket->getInBuffer()[0], socket->getInBuffer().size()));
 
         bool bOk = false;
-        size_t responseSize = 0;
+        StreamSocket::MessageMap map;
 
         try
         {
@@ -699,7 +699,7 @@ protected:
                                           marker.begin(), marker.end());
 
                 if (itBody != socket->getInBuffer().end())
-                    responseSize = itBody - socket->getInBuffer().begin() + 
marker.size();
+                    map._headerSize = itBody - socket->getInBuffer().begin() + 
marker.size();
             }
 
             if (response.getStatus() == 
Poco::Net::HTTPResponse::HTTP_SWITCHING_PROTOCOLS &&
@@ -728,7 +728,7 @@ protected:
             LOG_DBG("handleClientUpgrade exception caught.");
         }
 
-        if (!bOk || responseSize == 0)
+        if (!bOk || map._headerSize == 0)
         {
             LOG_ERR("Bad websocker server response.");
 
@@ -737,7 +737,7 @@ protected:
         }
 
         setWebSocket();
-        socket->eraseFirstInputBytes(responseSize);
+        socket->eraseFirstInputBytes(map);
     }
 #endif
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 90659923a..89171b339 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -1826,9 +1826,7 @@ private:
         try
         {
 #ifndef MOBILEAPP
-            size_t requestSize = 0;
-
-            if (!socket->parseHeader("Prisoner", message, request, 
&requestSize))
+            if (!socket->parseHeader("Prisoner", message, request))
                 return;
 
             LOG_TRC("Child connection with URI [" << 
LOOLWSD::anonymizeUrl(request.getURI()) << "].");
@@ -2048,16 +2046,23 @@ private:
             return;
         }
 
-        Poco::MemoryInputStream message(&socket->getInBuffer()[0],
-                                        socket->getInBuffer().size());;
+        Poco::MemoryInputStream startmessage(&socket->getInBuffer()[0],
+                                             socket->getInBuffer().size());;
         Poco::Net::HTTPRequest request;
-        size_t requestSize = 0;
 
-        if (!socket->parseHeader("Client", message, request, &requestSize))
+        StreamSocket::MessageMap map;
+        if (!socket->parseHeader("Client", startmessage, request, &map))
             return;
 
         try
         {
+            // We may need to re-write the chunks moving the inBuffer.
+            socket->compactChunks(&map);
+            Poco::MemoryInputStream message(&socket->getInBuffer()[0],
+                                            socket->getInBuffer().size());
+            // update the read cursor - headers are not altered by chunks.
+            message.seekg(startmessage.tellg(), std::ios::beg);
+
             // Check and remove the ServiceRoot from the request.getURI()
             if (!Util::startsWith(request.getURI(), LOOLWSD::ServiceRoot))
                 throw BadRequestException("The request does not start with 
prefix: " + LOOLWSD::ServiceRoot);
@@ -2166,7 +2171,7 @@ private:
 
         // if we succeeded - remove the request from our input buffer
         // we expect one request per socket
-        socket->eraseFirstInputBytes(requestSize);
+        socket->eraseFirstInputBytes(map);
 #else
         Poco::Net::HTTPRequest request;
         handleClientWsUpgrade(request, 
std::string(socket->getInBuffer().data(), socket->getInBuffer().size()), 
disposition);
@@ -2331,7 +2336,8 @@ private:
         return "application/octet-stream";
     }
 
-    void handlePostRequest(const Poco::Net::HTTPRequest& request, 
Poco::MemoryInputStream& message,
+    void handlePostRequest(const Poco::Net::HTTPRequest& request,
+                           Poco::MemoryInputStream& message,
                            SocketDisposition &disposition)
     {
         LOG_INF("Post request: [" << LOOLWSD::anonymizeUrl(request.getURI()) 
<< "]");
commit 945e7a926667c7456c282a23f0da10c2095a879c
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Wed May 22 02:54:12 2019 +0100
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jun 17 13:21:38 2019 +0200

    Initial HTTP Expect: 100-continue implementation.
    
    Change-Id: Ic9aa59cac5103151d91f6eb59d12313e545c7916
    Reviewed-on: https://gerrit.libreoffice.org/72746
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Tested-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/net/Socket.cpp b/net/Socket.cpp
index 2d35f6b02..2737c6f1c 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -485,6 +485,15 @@ bool StreamSocket::parseHeader(const char *clientName,
             LOG_DBG("Not enough content yet: ContentLength: " << contentLength 
<< ", available: " << available);
             return false;
         }
+
+        if (request.getExpectContinue() && !_sentHTTPContinue)
+        {
+            LOG_TRC("#" << getFD() << " got Expect: 100-continue, sending 
Continue");
+            // FIXME: should validate authentication headers early too.
+            send("HTTP/1.1 100 Continue\r\n\r\n",
+                 sizeof("HTTP/1.1 100 Continue\r\n\r\n") - 1);
+            _sentHTTPContinue = true;
+        }
     }
     catch (const Poco::Exception& exc)
     {
diff --git a/net/Socket.hpp b/net/Socket.hpp
index 2f0217d76..de20991e1 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -782,6 +782,7 @@ public:
         _bytesRecvd(0),
         _wsState(WSState::HTTP),
         _closed(false),
+        _sentHTTPContinue(false),
         _shutdownSignalled(false)
     {
         LOG_DBG("StreamSocket ctor #" << fd);
@@ -1139,6 +1140,9 @@ protected:
     /// True if we are already closed.
     bool _closed;
 
+    /// True if we've received a Continue in response to an Expect: 
100-continue
+    bool _sentHTTPContinue;
+
     /// True when shutdown was requested via shutdown().
     bool _shutdownSignalled;
 };
diff --git a/test/Makefile.am b/test/Makefile.am
index eacb3355a..eab241a95 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -17,7 +17,7 @@ noinst_LTLIBRARIES = \
        unit-timeout.la unit-prefork.la \
        unit-storage.la unit-client.la \
        unit-admin.la unit-tilecache.la \
-       unit-fuzz.la unit-oob.la unit-oauth.la \
+       unit-fuzz.la unit-oob.la unit-http.la unit-oauth.la \
        unit-wopi.la unit-wopi-saveas.la \
        unit-wopi-ownertermination.la unit-wopi-versionrestore.la \
        unit-wopi-documentconflict.la unit_wopi_renamefile.la \
@@ -85,6 +85,7 @@ fakesockettest_LDADD = $(CPPUNIT_LIBS)
 
 # unit test modules:
 unit_oob_la_SOURCES = UnitOOB.cpp
+unit_http_la_SOURCES = UnitHTTP.cpp
 unit_fuzz_la_SOURCES = UnitFuzz.cpp
 unit_admin_la_SOURCES = UnitAdmin.cpp
 unit_admin_la_LIBADD = $(CPPUNIT_LIBS)
@@ -129,7 +130,6 @@ TESTS = unit-convert.la unit-prefork.la unit-tilecache.la 
unit-timeout.la \
         unit-oauth.la unit-wopi.la unit-wopi-saveas.la \
         unit-wopi-ownertermination.la unit-wopi-versionrestore.la \

... etc. - the rest is truncated
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to