[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-cd-4' - 33 commits - bundled/include common/Util.hpp configure.ac discovery.xml .gitreview kit/ChildSession.cpp kit/ForKit.cpp kit/Kit.cpp kit/KitHelper.hpp loleaflet/js loleaflet/src loolwsd.xml.in net/Socket.cpp net/Socket.hpp net/WebSocketHandler.hpp scripts/perftrace.pl test/data test/helpers.hpp test/httpcrashtest.cpp test/httpwstest.cpp test/Makefile.am test/UnitHTTP.cpp test/UnitWOPILoadEncoded.cpp test/WopiTestServer.hpp tools/KitClient.cpp wsd/ClientSession.cpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp
Michael Meeks (via logerrit)
logerrit at kemper.freedesktop.org
Mon Jun 17 11:22:15 UTC 2019
.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.meeks at collabora.com>
AuthorDate: Thu May 2 17:03:18 2019 +0100
Commit: Andras Timar <andras.timar at 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 <kendy at collabora.com>
Tested-by: Jan Holesovsky <kendy at 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.meeks at collabora.com>
AuthorDate: Sat May 4 22:50:00 2019 +0100
Commit: Andras Timar <andras.timar at 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 <ashnakash at gmail.com>
Tested-by: Jan Holesovsky <kendy at 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 <vmiklos at collabora.com>
AuthorDate: Thu May 9 14:25:21 2019 +0200
Commit: Andras Timar <andras.timar at 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.meeks at collabora.com>
Tested-by: Michael Meeks <michael.meeks at 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 <vmiklos at collabora.com>
AuthorDate: Thu May 9 10:40:22 2019 +0200
Commit: Andras Timar <andras.timar at 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.meeks at collabora.com>
Tested-by: Michael Meeks <michael.meeks at 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 <vmiklos at collabora.com>
AuthorDate: Thu May 9 10:30:11 2019 +0200
Commit: Andras Timar <andras.timar at 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.meeks at collabora.com>
Tested-by: Michael Meeks <michael.meeks at 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.meeks at collabora.com>
AuthorDate: Thu May 2 16:14:12 2019 +0100
Commit: Andras Timar <andras.timar at 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 <kendy at collabora.com>
Tested-by: Jan Holesovsky <kendy at 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.timar at collabora.com>
AuthorDate: Thu Jun 13 14:02:33 2019 +0200
Commit: Andras Timar <andras.timar at 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.timar at collabora.com>
AuthorDate: Thu Jun 13 13:34:12 2019 +0200
Commit: Andras Timar <andras.timar at 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.meeks at collabora.com>
AuthorDate: Fri Apr 12 20:16:44 2019 +0100
Commit: Andras Timar <andras.timar at 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 <ashnakash at gmail.com>
Tested-by: Ashod Nakashian <ashnakash at gmail.com>
Reviewed-on: https://gerrit.libreoffice.org/72872
Tested-by: Andras Timar <andras.timar at 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/\&/&/g;
+ $str =~ s/\#/#/g;
+ $str =~ s/\>/>/g;
+ $str =~ s/\</</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.timar at collabora.com>
AuthorDate: Thu Jun 13 13:32:41 2019 +0200
Commit: Andras Timar <andras.timar at 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 at lists.freedesktop.org])
+AC_INIT([loolwsd], [4.0.5], [libreoffice at 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 <tml at collabora.com>
AuthorDate: Tue Jun 11 17:02:57 2019 +0300
Commit: Andras Timar <andras.timar at 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 <tml at collabora.com>
Reviewed-by: Andras Timar <andras.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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 <tml at collabora.com>
AuthorDate: Fri May 17 17:06:08 2019 +0300
Commit: Andras Timar <andras.timar at 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 <tml at collabora.com>
Reviewed-by: Andras Timar <andras.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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 <tml at collabora.com>
AuthorDate: Thu Mar 28 00:07:15 2019 +0200
Commit: Andras Timar <andras.timar at 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 <tml at collabora.com>
Reviewed-by: Andras Timar <andras.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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 <tml at collabora.com>
AuthorDate: Tue Mar 26 01:53:21 2019 +0200
Commit: Andras Timar <andras.timar at 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 <tml at collabora.com>
Reviewed-by: Andras Timar <andras.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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.klos at collabora.com>
AuthorDate: Thu Apr 4 18:36:21 2019 +0200
Commit: Andras Timar <andras.timar at 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.klos at collabora.com>
Tested-by: Szymon Kłos <szymon.klos at collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/70290
Reviewed-by: Jan Holesovsky <kendy at collabora.com>
Tested-by: Jan Holesovsky <kendy at collabora.com>
(cherry picked from commit 64949d2a044986ef992e323ad675e0c808496563)
Reviewed-on: https://gerrit.libreoffice.org/73935
Reviewed-by: Andras Timar <andras.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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 <tml at collabora.com>
AuthorDate: Thu Jan 24 12:55:00 2019 +0200
Commit: Andras Timar <andras.timar at 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 <tml at collabora.com>
Tested-by: Tor Lillqvist <tml at collabora.com>
(cherry picked from commit 4506d7c2de2c20c4211300ac58cecd2a42e7bb27)
Reviewed-on: https://gerrit.libreoffice.org/73934
Reviewed-by: Andras Timar <andras.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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.meeks at collabora.com>
AuthorDate: Wed May 22 10:54:36 2019 +0100
Commit: Andras Timar <andras.timar at 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.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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.meeks at collabora.com>
AuthorDate: Wed May 22 11:25:28 2019 +0100
Commit: Andras Timar <andras.timar at 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.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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.meeks at collabora.com>
AuthorDate: Wed May 22 02:54:12 2019 +0100
Commit: Andras Timar <andras.timar at 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.timar at collabora.com>
Tested-by: Andras Timar <andras.timar at 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
More information about the Libreoffice-commits
mailing list