common/FileUtil.cpp | 2 common/SigUtil.cpp | 6 +- common/SigUtil.hpp | 3 - configure.ac | 28 +-------- ios/Mobile/DocumentViewController.mm | 26 +++++--- loleaflet/Makefile.am | 7 ++ loleaflet/css/loleaflet.css | 6 +- loleaflet/css/menubar.css | 2 loleaflet/css/toolbar.css | 35 +++++++++-- loleaflet/html/framed.doc.html | 2 loleaflet/html/loleaflet.html.m4 | 1 loleaflet/src/control/Control.DocumentNameInput.js | 11 +++ loleaflet/src/control/Control.LokDialog.js | 5 + loleaflet/src/control/Ruler.js | 1 loleaflet/src/core/Socket.js | 19 +++--- loolwsd.xml.in | 7 ++ wsd/Admin.cpp | 26 +++++++- wsd/Admin.hpp | 4 + wsd/AdminModel.cpp | 63 ++++++++++++++++++++- wsd/AdminModel.hpp | 60 +++++++++++++++++--- wsd/ClientSession.cpp | 13 ++-- wsd/LOOLWSD.cpp | 15 +++++ 22 files changed, 267 insertions(+), 75 deletions(-)
New commits: commit f067b861c770d57e54911bee6f18fb518f2a402e Author: Andras Timar <[email protected]> AuthorDate: Wed Jul 1 18:21:31 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 18:21:31 2020 +0200 Bump version to 7.0.0.1 Change-Id: I80af9a636d00faa63359d3fc3476108f5c448806 diff --git a/configure.ac b/configure.ac index eb66b48d6..00cb4ac02 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.63]) -AC_INIT([loolwsd], [7.0.0.0.beta1], [[email protected]]) +AC_INIT([loolwsd], [7.0.0.1], [[email protected]]) LT_INIT([shared, disable-static, dlopen]) AM_INIT_AUTOMAKE([1.10 subdir-objects tar-pax -Wno-portability]) commit f486265ca70a5b120e6006127add219e63e80693 Author: Henry Castro <[email protected]> AuthorDate: Thu May 28 08:17:16 2020 -0400 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 18:20:32 2020 +0200 loleaflet: remove install node_module in configure phase Fair enough, let's not get in conflict with IOS platform, It is restored and get another solution Change-Id: I1cde236595479bdf41e29d8a30bb9d71aa196e54 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95036 Tested-by: Henry Castro <[email protected]> Reviewed-by: Henry Castro <[email protected]> diff --git a/configure.ac b/configure.ac index 53ad8954d..eb66b48d6 100644 --- a/configure.ac +++ b/configure.ac @@ -992,31 +992,13 @@ fi AC_SUBST(ENABLE_SETCAP) - -tryLinkFile () { - if test ! -r "$1"; then - AC_MSG_ERROR(["$1: file not found"]) - fi - - rm -f $2 - ln -s "$1" "$2" 2>/dev/null || - ln "$1" "$2" 2>/dev/null || - cp -p "$1" "$2" || - AC_MSG_ERROR(["cannot link or copy $1 to $2"]) -} - -if test "$srcdir" != '.'; then - mkdir -p loleaflet - tryLinkFile $srcdir/loleaflet/package.json loleaflet/package.json - tryLinkFile $srcdir/loleaflet/archived-packages loleaflet/archived-packages - cp -p $srcdir/loleaflet/npm-shrinkwrap.json loleaflet/npm-shrinkwrap.json -fi - AC_CONFIG_LINKS([discovery.xml:discovery.xml]) AC_CONFIG_LINKS([loolkitconfig.xcu:loolkitconfig.xcu]) +AC_CONFIG_LINKS([loleaflet/package.json:loleaflet/package.json]) AC_CONFIG_LINKS([cypress_test/package.json:cypress_test/package.json]) AC_CONFIG_LINKS([cypress_test/cypress.json:cypress_test/cypress.json]) AC_LINK_FILES([cypress_test/plugins], [cypress_test/plugins]) +AC_LINK_FILES([loleaflet/archived-packages], [loleaflet/archived-packages]) AC_LINK_FILES([cypress_test/eslint_plugin], [cypress_test/eslint_plugin]) APP_BRANDING_DIR= @@ -1087,6 +1069,7 @@ AC_CONFIG_FILES([Makefile gtk/Makefile test/Makefile loleaflet/Makefile + loleaflet/npm-shrinkwrap.json loolwsd.spec loolwsd.xml debian/loolwsd.postinst]) @@ -1106,9 +1089,6 @@ fi AC_CONFIG_FILES([test/run_unit.sh],[chmod +x test/run_unit.sh]) -AC_CONFIG_COMMANDS_PRE([AS_IF([test `uname -s` = "Linux"], - [echo Installing node modules packages... && (cd loleaflet && npm install)])]) - AC_OUTPUT AC_LANG_POP diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am index b23ab1a10..0d922a18d 100644 --- a/loleaflet/Makefile.am +++ b/loleaflet/Makefile.am @@ -1,6 +1,5 @@ MAKEFLAGS = --no-builtin-rules CTAGS = ctags -CONFIG_STATUS_DEPENDENCIES=$(builddir)/node_modules L10N_PO = $(wildcard $(srcdir)/po/*.po) @@ -435,6 +434,12 @@ node_modules: package.json archived-packages @npm install @touch node_modules +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status node_modules + @case '$?' in \ + *node_modules*) \ + touch Makefile;; \ + esac; + $(DIST_FOLDER)/device-%.css: $(srcdir)/css/device-%.css @mkdir -p $(dir $@) @if test -z '$(ENABLE_BROWSERSYNC)'; then \ diff --git a/loleaflet/npm-shrinkwrap.json b/loleaflet/npm-shrinkwrap.json.in similarity index 100% rename from loleaflet/npm-shrinkwrap.json rename to loleaflet/npm-shrinkwrap.json.in commit dcaf520375b86e774d56537272745d75fcf854e0 Author: Michael Meeks <[email protected]> AuthorDate: Tue Jun 30 16:40:53 2020 +0100 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:54:52 2020 +0200 Close files after copying, to avoid fd leak during convert-to. Change-Id: I24a9726bfea7805aff61d778e9f8f91c5b1ffd52 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97501 Tested-by: Aron Budea <[email protected]> Reviewed-by: Aron Budea <[email protected]> diff --git a/common/FileUtil.cpp b/common/FileUtil.cpp index 054a9461d..a2ac7a3ff 100644 --- a/common/FileUtil.cpp +++ b/common/FileUtil.cpp @@ -145,6 +145,8 @@ namespace FileUtil LOG_WRN("Unusual: file " << anonymizeUrl(fromPath) << " changed size " "during copy from " << st.st_size << " to " << bytesIn); } + close(from); + close(to); } catch (...) { commit 57672a0236d175935ed3756e09345daa9eed7dae Author: Pedro Pinto Silva <[email protected]> AuthorDate: Fri Jun 19 16:32:05 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:54:43 2020 +0200 Sidebar-panel has fixed width that is bigger than its contents, better fix - Revert display:table from #sidebar-panel - Set document-container position to right:0px when in presence of text or presentation documents that has (missing) the sidebar closed (sidebar-dock-wrapper display:none) Change-Id: Id4416e9dfa0540dd774b83b41428f021f99f4f38 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/96741 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Pedro Silva <[email protected]> (cherry picked from commit 862486f6d231178ce44a0f3902b2feb76866b5f9) Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97382 Reviewed-by: Andras Timar <[email protected]> diff --git a/loleaflet/css/loleaflet.css b/loleaflet/css/loleaflet.css index 0773c624d..41e9459fc 100644 --- a/loleaflet/css/loleaflet.css +++ b/loleaflet/css/loleaflet.css @@ -7,6 +7,10 @@ right: 0px; left: 0px; } +#document-container.sidebar-closed { + right: 0px !important; +} + #document-container.tablet { top: 36px; @@ -152,7 +156,6 @@ body { } #sidebar-panel { - display: table; padding: 0px; margin: 0px; position: relative; diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js index dfddbdfc2..1b70b1cc0 100644 --- a/loleaflet/src/control/Control.LokDialog.js +++ b/loleaflet/src/control/Control.LokDialog.js @@ -1060,6 +1060,7 @@ L.Control.LokDialog = L.Control.extend({ this._sendPaintWindowRect(id); } else { this._createSidebar(id, strId, width, height); + $('#document-container').removeClass('sidebar-closed'); } }, @@ -1365,6 +1366,7 @@ L.Control.LokDialog = L.Control.extend({ this._map.fire('editorgotfocus'); this._map.focus(); } + $('#document-container').addClass('sidebar-closed'); }, _onCalcInputBarClose: function(dialogId) { commit dca2eb66ec92705d6fa3d945e3b1763af45eac6c Author: Pedro Silva <[email protected]> AuthorDate: Mon Jun 29 22:18:09 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:54:23 2020 +0200 Desktop: document name: center icon (pencil) Change-Id: I3da93eda530fa5e660b95ddf743b68463e939292 diff --git a/loleaflet/css/toolbar.css b/loleaflet/css/toolbar.css index 20fb595a0..0a795e187 100644 --- a/loleaflet/css/toolbar.css +++ b/loleaflet/css/toolbar.css @@ -152,11 +152,10 @@ w2ui-toolbar { #document-title-pencil.editable { visibility: visible; width: 24px; - height: 22px; + height: 18px; position: absolute; left: 87%; background: url('images/baseline-edit.svg') right center no-repeat, radial-gradient(circle, #fff 20%, #fff0 100%); - border-top: solid 3px white; } #document-title-pencil:not(.editable){ commit 1a927234c57cd52c3578fad7d78b57271eeb88c8 Author: Pedro Pinto Silva <[email protected]> AuthorDate: Wed Jun 10 15:56:34 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:53:32 2020 +0200 Desktop: Set max size for document name's width Change-Id: Ic35990efd9e240ed29ae9da1e72dcd6b58a4e933 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/96053 Tested-by: Jenkins Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Andras Timar <[email protected]> diff --git a/loleaflet/css/toolbar.css b/loleaflet/css/toolbar.css index 13eb379d5..20fb595a0 100644 --- a/loleaflet/css/toolbar.css +++ b/loleaflet/css/toolbar.css @@ -149,6 +149,19 @@ w2ui-toolbar { #tb_formulabar_item_formula div table { width: 100%; } +#document-title-pencil.editable { + visibility: visible; + width: 24px; + height: 22px; + position: absolute; + left: 87%; + background: url('images/baseline-edit.svg') right center no-repeat, radial-gradient(circle, #fff 20%, #fff0 100%); + border-top: solid 3px white; +} + +#document-title-pencil:not(.editable){ + visibility: hidden; +} #document-name-input { font-size: 16px; @@ -159,25 +172,25 @@ w2ui-toolbar { #document-name-input.editable { border: none; box-shadow: 0 0 0.1px 1px #ebebeb, 0 0 2px 1px #f0f0f0; - background-image: url('images/baseline-edit.svg'); background-position: right; background-repeat: no-repeat; + padding-right: 24px; + max-width: 100%; } #document-name-input.editable:focus { border: none; box-shadow: inset 0 0 2px 1px #f0f0f0, 0 0 0.1px 1px #bbb; background-color: white; + background-image: none; } #document-name-input.editable:hover:not(:focus) { border: none; box-shadow: 0 0 0.1px 1px #d7d7d7, 0 0 3px 2px #f0f0f0; background-color: white; - background-image: url('images/baseline-edit.svg'); background-position: right; background-repeat: no-repeat; - padding-right: 2px; } #tb_editbar_item_fold table.w2ui-button { margin: 0px 14px 0px 4px !important; diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4 index 547c603c5..41f3458d8 100644 --- a/loleaflet/html/loleaflet.html.m4 +++ b/loleaflet/html/loleaflet.html.m4 @@ -145,6 +145,7 @@ m4_ifelse(MOBILEAPP,[true], <ul id="main-menu" class="sm sm-simple lo-menu readonly"></ul> <div id="document-titlebar"> <div class="document-title"> + <div id="document-title-pencil"></div> <input id="document-name-input" type="text" disabled="true" style="display: none"/> </div> </div> diff --git a/loleaflet/src/control/Control.DocumentNameInput.js b/loleaflet/src/control/Control.DocumentNameInput.js index 090cc25f9..28db57f41 100644 --- a/loleaflet/src/control/Control.DocumentNameInput.js +++ b/loleaflet/src/control/Control.DocumentNameInput.js @@ -65,7 +65,13 @@ L.Control.DocumentNameInput = L.Control.extend({ }, onDocLayerInit: function() { - $('#document-name-input').attr('size', $('#document-name-input').val().length); + var value = $('#document-name-input').val(); + if (value.length < 27) { + $('#document-name-input').attr('size', value.length); + } + else { + $('#document-name-input').attr('size', '25'); + } // FIXME: Android app would display a temporary filename, not the actual filename if (window.ThisIsTheAndroidApp) { @@ -78,6 +84,7 @@ L.Control.DocumentNameInput = L.Control.extend({ // We can now set the document name in the menu bar $('#document-name-input').prop('disabled', false); $('#document-name-input').removeClass('editable'); + $('#document-title-pencil').removeClass('editable'); $('#document-name-input').focus(function() { $(this).blur(); }); // Call decodecodeURIComponent twice: Reverse both our encoding and the encoding of // the name in the file system. @@ -99,12 +106,14 @@ L.Control.DocumentNameInput = L.Control.extend({ // Save As allowed $('#document-name-input').prop('disabled', false); $('#document-name-input').addClass('editable'); + $('#document-title-pencil').addClass('editable'); $('#document-name-input').off('keypress', this.onDocumentNameKeyPress).on('keypress', this.onDocumentNameKeyPress.bind(this)); $('#document-name-input').off('focus', this.onDocumentNameFocus).on('focus', this.onDocumentNameFocus.bind(this)); $('#document-name-input').off('blur', this.documentNameCancel).on('blur', this.documentNameCancel.bind(this)); } else { $('#document-name-input').prop('disabled', true); $('#document-name-input').removeClass('editable'); + $('#document-title-pencil').removeClass('editable'); $('#document-name-input').off('keypress', this.onDocumentNameKeyPress); } } commit 07de252ef91cf829590003939b054db67e1a5f48 Author: Pedro Pinto Silva <[email protected]> AuthorDate: Tue Jun 9 11:25:16 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:53:24 2020 +0200 Desktop: Set (on LayerInit) document name's size dynamically according to its value Change-Id: I5e15b50c4ecf51d8e713cfc0fe116b44294e1e45 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95904 Tested-by: Jenkins Tested-by: Jenkins CollaboraOffice <[email protected]> Tested-by: Pedro Silva <[email protected]> Reviewed-by: Pedro Silva <[email protected]> diff --git a/loleaflet/css/toolbar.css b/loleaflet/css/toolbar.css index 45b1531ec..13eb379d5 100644 --- a/loleaflet/css/toolbar.css +++ b/loleaflet/css/toolbar.css @@ -154,7 +154,6 @@ w2ui-toolbar { font-size: 16px; border: 1px solid transparent; background-color: transparent; - width: 200px; } #document-name-input.editable { @@ -169,7 +168,6 @@ w2ui-toolbar { border: none; box-shadow: inset 0 0 2px 1px #f0f0f0, 0 0 0.1px 1px #bbb; background-color: white; - width: 200px; } #document-name-input.editable:hover:not(:focus) { @@ -179,8 +177,7 @@ w2ui-toolbar { background-image: url('images/baseline-edit.svg'); background-position: right; background-repeat: no-repeat; - padding-right: 20px; - width: 181px; + padding-right: 2px; } #tb_editbar_item_fold table.w2ui-button { margin: 0px 14px 0px 4px !important; diff --git a/loleaflet/src/control/Control.DocumentNameInput.js b/loleaflet/src/control/Control.DocumentNameInput.js index ab22cd1a3..090cc25f9 100644 --- a/loleaflet/src/control/Control.DocumentNameInput.js +++ b/loleaflet/src/control/Control.DocumentNameInput.js @@ -65,6 +65,8 @@ L.Control.DocumentNameInput = L.Control.extend({ }, onDocLayerInit: function() { + $('#document-name-input').attr('size', $('#document-name-input').val().length); + // FIXME: Android app would display a temporary filename, not the actual filename if (window.ThisIsTheAndroidApp) { $('#document-name-input').hide(); commit f2522a260bddaf747248f149d9193914129ffc61 Author: Gabriel Masei <[email protected]> AuthorDate: Mon Jun 8 13:41:03 2020 +0300 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:52:48 2020 +0200 admin: cleanup resource consuming kits Change-Id: Ifafbadc61b788adc90c03fb92e0231f9e599c360 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95794 Tested-by: Jenkins Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Michael Meeks <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/96006 Reviewed-by: Andras Timar <[email protected]> diff --git a/common/SigUtil.cpp b/common/SigUtil.cpp index cd4c779dc..2e2216bb8 100644 --- a/common/SigUtil.cpp +++ b/common/SigUtil.cpp @@ -355,13 +355,13 @@ namespace SigUtil sigaction(SIGUSR1, &action, nullptr); } - /// Kill the given pid with SIGTERM. Returns true when the pid does not exist any more. - bool killChild(const int pid) + /// Kill the given pid with SIGKILL as default. Returns true when the pid does not exist any more. + bool killChild(const int pid, const int signal) { LOG_DBG("Killing PID: " << pid); // Don't kill anything in the fuzzer case: pid == 0 would kill the fuzzer itself, and // killing random other processes is not a great idea, either. - if (Util::isFuzzing() || kill(pid, SIGKILL) == 0 || errno == ESRCH) + if (Util::isFuzzing() || kill(pid, signal) == 0 || errno == ESRCH) { // Killed or doesn't exist. return true; diff --git a/common/SigUtil.hpp b/common/SigUtil.hpp index 9bccf0256..d3eaf9726 100644 --- a/common/SigUtil.hpp +++ b/common/SigUtil.hpp @@ -11,6 +11,7 @@ #include <atomic> #include <mutex> +#include <signal.h> #if MOBILEAPP static constexpr bool ShutdownRequestFlag(false); @@ -73,7 +74,7 @@ namespace SigUtil /// Kills a child process and returns true when /// child pid is removed from the process table /// after a certain (short) timeout. - bool killChild(const int pid); + bool killChild(const int pid, const int signal = SIGKILL); /// Dump a signal-safe back-trace void dumpBacktrace(); diff --git a/loolwsd.xml.in b/loolwsd.xml.in index e66f3526d..27e1d6e87 100644 --- a/loolwsd.xml.in +++ b/loolwsd.xml.in @@ -30,6 +30,13 @@ <limit_num_open_files desc="The maximum number of files allowed to each document process to open. 0 for unlimited." type="uint">0</limit_num_open_files> <limit_load_secs desc="Maximum number of seconds to wait for a document load to succeed. 0 for unlimited." type="uint" default="100">100</limit_load_secs> <limit_convert_secs desc="Maximum number of seconds to wait for a document conversion to succeed. 0 for unlimited." type="uint" default="100">100</limit_convert_secs> + <cleanup desc="Checks for resource consuming (bad) documents and kills associated kit process. A document is considered resource consuming (bad) if is in idle state for idle_time_secs period and memory usage passed limit_dirty_mem_mb or CPU usage passed limit_cpu_per" enable="false"> + <cleanup_interval_ms desc="Interval between two checks" type="uint" default="10000">10000</cleanup_interval_ms> + <bad_behavior_period_secs desc="Minimum time period for a document to be in bad state before associated kit process is killed. If in this period the condition for bad document is not met once then this period is reset" type="uint" default="60">60</bad_behavior_period_secs> + <idle_time_secs desc="Minimum idle time for a document to be candidate for bad state" type="uint" default="300">300</idle_time_secs> + <limit_dirty_mem_mb desc="Minimum memory usage for a document to be candidate for bad state" type="uint" default="3072">3072</limit_dirty_mem_mb> + <limit_cpu_per desc="Minimum CPU usage for a document to be candidate for bad state" type="uint" default="85">85</limit_cpu_per> + </cleanup> </per_document> <per_view desc="View-specific settings."> diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp index 80315676a..13a2c24ea 100644 --- a/wsd/Admin.cpp +++ b/wsd/Admin.cpp @@ -370,7 +370,8 @@ Admin::Admin() : _lastRecvCount(0), _cpuStatsTaskIntervalMs(DefStatsIntervalMs), _memStatsTaskIntervalMs(DefStatsIntervalMs * 2), - _netStatsTaskIntervalMs(DefStatsIntervalMs * 2) + _netStatsTaskIntervalMs(DefStatsIntervalMs * 2), + _cleanupIntervalMs(DefStatsIntervalMs * 10) { LOG_INF("Admin ctor."); @@ -396,13 +397,14 @@ Admin::~Admin() void Admin::pollingThread() { - std::chrono::steady_clock::time_point lastCPU, lastMem, lastNet; + std::chrono::steady_clock::time_point lastCPU, lastMem, lastNet, lastCleanup; _model.setThreadOwner(std::this_thread::get_id()); lastCPU = std::chrono::steady_clock::now(); lastMem = lastCPU; lastNet = lastCPU; + lastCleanup = lastCPU; while (!isStop() && !SigUtil::getTerminationFlag() && !SigUtil::getShutdownRequestFlag()) { @@ -464,6 +466,19 @@ void Admin::pollingThread() lastNet = now; } + int cleanupWait = _cleanupIntervalMs - + std::chrono::duration_cast<std::chrono::milliseconds>(now - lastCleanup).count(); + if (cleanupWait <= MinStatsIntervalMs / 2) // Close enough + { + if (_defDocProcSettings.getCleanupSettings().getEnable()) + { + cleanupResourceConsumingDocs(); + + cleanupWait += _cleanupIntervalMs; + lastCleanup = now; + } + } + // (re)-connect (with sync. DNS - urk) to one monitor at a time if (_pendingConnects.size()) { @@ -476,7 +491,7 @@ void Admin::pollingThread() } // Handle websockets & other work. - const int timeout = capAndRoundInterval(std::min(std::min(cpuWait, memWait), netWait)); + const int timeout = capAndRoundInterval(std::min(std::min(std::min(cpuWait, memWait), netWait), cleanupWait)); LOG_TRC("Admin poll for " << timeout << "ms."); poll(timeout * 1000); // continue with ms for admin, settings etc. } @@ -667,6 +682,11 @@ void Admin::notifyDocsMemDirtyChanged() _model.notifyDocsMemDirtyChanged(); } +void Admin::cleanupResourceConsumingDocs() +{ + _model.cleanupResourceConsumingDocs(); +} + void Admin::dumpState(std::ostream& os) { // FIXME: be more helpful ... diff --git a/wsd/Admin.hpp b/wsd/Admin.hpp index d556573c8..959f9b9c2 100644 --- a/wsd/Admin.hpp +++ b/wsd/Admin.hpp @@ -118,6 +118,8 @@ public: void setDefDocProcSettings(const DocProcSettings& docProcSettings, bool notifyKit) { _defDocProcSettings = docProcSettings; + _model.setDefDocProcSettings(docProcSettings); + _cleanupIntervalMs = _defDocProcSettings.getCleanupSettings().getCleanupInterval(); if (notifyKit) notifyForkit(); } @@ -143,6 +145,7 @@ private: /// under @hardModeLimit void triggerMemoryCleanup(size_t hardModeLimit); void notifyDocsMemDirtyChanged(); + void cleanupResourceConsumingDocs(); /// Round the interval up to multiples of MinStatsIntervalMs. /// This is to avoid arbitrarily small intervals that hammer the server. @@ -184,6 +187,7 @@ private: std::atomic<int> _cpuStatsTaskIntervalMs; std::atomic<int> _memStatsTaskIntervalMs; std::atomic<int> _netStatsTaskIntervalMs; + size_t _cleanupIntervalMs; DocProcSettings _defDocProcSettings; // Don't update any more frequently than this since it's excessive. diff --git a/wsd/AdminModel.cpp b/wsd/AdminModel.cpp index 967598cf3..58ef5d4d4 100644 --- a/wsd/AdminModel.cpp +++ b/wsd/AdminModel.cpp @@ -140,7 +140,7 @@ void Document::updateMemoryDirty() const time_t now = std::time(nullptr); if (now - _lastTimeSMapsRead >= 5) { - int lastMemDirty = _memoryDirty; + size_t lastMemDirty = _memoryDirty; _memoryDirty = _procSMaps ? Util::getPssAndDirtyFromSMaps(_procSMaps).second : 0; _lastTimeSMapsRead = now; if (lastMemDirty != _memoryDirty) @@ -148,6 +148,16 @@ void Document::updateMemoryDirty() } } +void Document::setLastJiffies(size_t newJ) +{ + auto now = std::chrono::system_clock::now(); + if (_lastJiffy) + _lastCpuPercentage = (100 * 1000 * (newJ - _lastJiffy) / ::sysconf(_SC_CLK_TCK)) + / std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastJiffyTime).count(); + _lastJiffy = newJ; + _lastJiffyTime = now; +} + bool Subscriber::notify(const std::string& message) { // If there is no socket, then return false to @@ -692,6 +702,57 @@ std::vector<DocBasicInfo> AdminModel::getDocumentsSortedByIdle() const return docs; } +void AdminModel::cleanupResourceConsumingDocs() +{ + DocCleanupSettings& settings = _defDocProcSettings.getCleanupSettings(); + + for (const auto& it: _documents) + { + Document *doc = it.second.get(); + if (!doc->isExpired()) + { + size_t idleTime = doc->getIdleTime(); + size_t memDirty = doc->getMemoryDirty(); + unsigned cpuPercentage = doc->getLastCpuPercentage(); + + if (idleTime >= settings.getIdleTime() && + (memDirty >= settings.getLimitDirtyMem() * 1024 || + cpuPercentage >= settings.getLimitCpu())) + { + time_t now = std::time(nullptr); + const size_t badBehaviorDuration = now - doc->getBadBehaviorDetectionTime(); + if (!doc->getBadBehaviorDetectionTime()) + { + LOG_WRN("Detected resource consuming doc [" << doc->getDocKey() << "]: idle=" + << idleTime << " s, memory=" << memDirty << " KB, CPU=" << cpuPercentage << "%."); + doc->setBadBehaviorDetectionTime(now); + } + else if (badBehaviorDuration >= settings.getBadBehaviorPeriod()) + { + // We should not try to close it nicely (closeDocument) because + // we could lose it: it will be removed from our internal lists + // but the process itself can hang and continue to exist and + // consume resources. + // Also, try first to SIGABRT the kit process so that a stack trace + // could be dumped. If the process is still alive then, at next + // iteration, try to SIGKILL it. + if (SigUtil::killChild(doc->getPid(), doc->getAbortTime() ? SIGKILL : SIGABRT)) + LOG_ERR((doc->getAbortTime() ? "Killed" : "Aborted") << " resource consuming doc [" << doc->getDocKey() << "]"); + else + LOG_ERR("Cannot " << (doc->getAbortTime() ? "kill" : "abort") << " resource consuming doc [" << doc->getDocKey() << "]"); + if (!doc->getAbortTime()) + doc->setAbortTime(time_t(nullptr)); + } + } + else if (doc->getBadBehaviorDetectionTime()) + { + doc->setBadBehaviorDetectionTime(0); + LOG_WRN("Removed doc [" << doc->getDocKey() << "] from resource consuming monitoring list"); + } + } + } +} + std::string AdminModel::getDocuments() const { assertCorrectThread(); diff --git a/wsd/AdminModel.hpp b/wsd/AdminModel.hpp index 98bd01495..0a038ad20 100644 --- a/wsd/AdminModel.hpp +++ b/wsd/AdminModel.hpp @@ -51,6 +51,30 @@ private: std::chrono::milliseconds _loadDuration; }; +struct DocCleanupSettings +{ + void setEnable(bool enable) { _enable = enable; } + bool getEnable() const { return _enable; } + void setCleanupInterval(size_t cleanupInterval) { _cleanupInterval = cleanupInterval; } + size_t getCleanupInterval() const { return _cleanupInterval; } + void setBadBehaviorPeriod(size_t badBehaviorPeriod) { _badBehaviorPeriod = badBehaviorPeriod; } + size_t getBadBehaviorPeriod() const { return _badBehaviorPeriod; } + void setIdleTime(size_t idleTime) { _idleTime = idleTime; } + size_t getIdleTime() { return _idleTime; } + void setLimitDirtyMem(size_t limitDirtyMem) { _limitDirtyMem = limitDirtyMem; } + size_t getLimitDirtyMem() const { return _limitDirtyMem; } + void setLimitCpu(size_t limitCpu) { _limitCpu = limitCpu; } + size_t getLimitCpu() const { return _limitCpu; } + +private: + bool _enable; + size_t _cleanupInterval; + size_t _badBehaviorPeriod; + size_t _idleTime; + size_t _limitDirtyMem; + size_t _limitCpu; +}; + struct DocProcSettings { void setLimitVirtMemMb(size_t limitVirtMemMb) { _limitVirtMemMb = limitVirtMemMb; } @@ -62,11 +86,15 @@ struct DocProcSettings void setLimitNumberOpenFiles(size_t limitNumberOpenFiles) { _limitNumberOpenFiles = limitNumberOpenFiles; } size_t getLimitNumberOpenFiles() const { return _limitNumberOpenFiles; } + DocCleanupSettings& getCleanupSettings() { return _docCleanupSettings; } + private: size_t _limitVirtMemMb; size_t _limitStackMemKb; size_t _limitFileSizeMb; size_t _limitNumberOpenFiles; + + DocCleanupSettings _docCleanupSettings; }; /// Containing basic information about document @@ -112,6 +140,7 @@ public: _filename(filename), _memoryDirty(0), _lastJiffy(0), + _lastCpuPercentage(0), _start(std::time(nullptr)), _lastActivity(_start), _end(0), @@ -122,7 +151,9 @@ public: _procSMaps(nullptr), _lastTimeSMapsRead(0), _isModified(false), - _hasMemDirtyChanged(true) + _hasMemDirtyChanged(true), + _badBehaviorDetectionTime(0), + _abortTime(0) { } @@ -151,13 +182,14 @@ public: unsigned getActiveViews() const { return _activeViews; } unsigned getLastJiffies() const { return _lastJiffy; } - void setLastJiffies(size_t newJ) { _lastJiffy = newJ; } + void setLastJiffies(size_t newJ); + unsigned getLastCpuPercentage(){ return _lastCpuPercentage; } const std::map<std::string, View>& getViews() const { return _views; } void updateLastActivityTime() { _lastActivity = std::time(nullptr); } void updateMemoryDirty(); - int getMemoryDirty() const { return _memoryDirty; } + size_t getMemoryDirty() const { return _memoryDirty; } std::pair<std::time_t, std::string> getSnapshot() const; const std::string getHistory() const; @@ -172,9 +204,6 @@ public: _recvBytes += recv; } - const DocProcSettings& getDocProcSettings() const { return _docProcSettings; } - void setDocProcSettings(const DocProcSettings& docProcSettings) { _docProcSettings = docProcSettings; } - std::time_t getOpenTime() const { return isExpired() ? _end - _start : getElapsedTime(); } uint64_t getSentBytes() const { return _sentBytes; } uint64_t getRecvBytes() const { return _recvBytes; } @@ -186,6 +215,10 @@ public: void setProcSMapsFD(const int smapsFD) { _procSMaps = fdopen(smapsFD, "r"); } bool hasMemDirtyChanged() const { return _hasMemDirtyChanged; } void setMemDirtyChanged(bool changeStatus) { _hasMemDirtyChanged = changeStatus; } + time_t getBadBehaviorDetectionTime(){ return _badBehaviorDetectionTime; } + void setBadBehaviorDetectionTime(time_t badBehaviorDetectionTime){ _badBehaviorDetectionTime = badBehaviorDetectionTime;} + time_t getAbortTime(){ return _abortTime; } + void setAbortTime(time_t abortTime) { _abortTime = abortTime; } std::string to_string() const; @@ -199,9 +232,11 @@ private: /// Hosted filename std::string _filename; /// The dirty (ie. un-shared) memory of the document's Kit process. - int _memoryDirty; + size_t _memoryDirty; /// Last noted Jiffy count unsigned _lastJiffy; + std::chrono::time_point<std::chrono::system_clock> _lastJiffyTime; + unsigned _lastCpuPercentage; std::time_t _start; std::time_t _lastActivity; @@ -218,10 +253,11 @@ private: FILE* _procSMaps; std::time_t _lastTimeSMapsRead; - /// Per-doc kit process settings. - DocProcSettings _docProcSettings; bool _isModified; bool _hasMemDirtyChanged; + + std::time_t _badBehaviorDetectionTime; + std::time_t _abortTime; }; /// An Admin session subscriber. @@ -332,6 +368,7 @@ public: /// Document basic info list sorted by most idle time std::vector<DocBasicInfo> getDocumentsSortedByIdle() const; + void cleanupResourceConsumingDocs(); void setViewLoadDuration(const std::string& docKey, const std::string& sessionId, std::chrono::milliseconds viewLoadDuration); void setDocWopiDownloadDuration(const std::string& docKey, std::chrono::milliseconds wopiDownloadDuration); @@ -345,6 +382,9 @@ public: void UpdateMemoryDirty(); void notifyDocsMemDirtyChanged(); + const DocProcSettings& getDefDocProcSettings() const { return _defDocProcSettings; } + void setDefDocProcSettings(const DocProcSettings& docProcSettings) { _defDocProcSettings = docProcSettings; } + static int getPidsFromProcName(const std::regex& procNameRegEx, std::vector<int> *pids); private: @@ -391,6 +431,8 @@ private: /// We check the owner even in the release builds, needs to be always correct. std::thread::id _owner; + + DocProcSettings _defDocProcSettings; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 003fcf89b..17c2484fd 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -904,6 +904,12 @@ void LOOLWSD::initialize(Application& self) { "num_prespawn_children", "1" }, { "per_document.always_save_on_exit", "false" }, { "per_document.autosave_duration_secs", "300" }, + { "per_document.cleanup.cleanup_interval_ms", "10000" }, + { "per_document.cleanup.bad_behavior_period_secs", "60" }, + { "per_document.cleanup.idle_time_secs", "300" }, + { "per_document.cleanup.limit_dirty_mem_mb", "3072" }, + { "per_document.cleanup.limit_cpu_per", "85" }, + { "per_document.cleanup[@enable]", "false" }, { "per_document.document_signing_url", VEREIGN_URL }, { "per_document.idle_timeout_secs", "3600" }, { "per_document.idlesave_duration_secs", "30" }, @@ -1290,6 +1296,15 @@ void LOOLWSD::initialize(Application& self) docProcSettings.setLimitStackMemKb(getConfigValue<int>("per_document.limit_stack_mem_kb", 0)); docProcSettings.setLimitFileSizeMb(getConfigValue<int>("per_document.limit_file_size_mb", 0)); docProcSettings.setLimitNumberOpenFiles(getConfigValue<int>("per_document.limit_num_open_files", 0)); + + DocCleanupSettings &docCleanupSettings = docProcSettings.getCleanupSettings(); + docCleanupSettings.setEnable(getConfigValue<bool>("per_document.cleanup[@enable]", false)); + docCleanupSettings.setCleanupInterval(getConfigValue<int>("per_document.cleanup.cleanup_interval_ms", 10000)); + docCleanupSettings.setBadBehaviorPeriod(getConfigValue<int>("per_document.cleanup.bad_behavior_period_secs", 60)); + docCleanupSettings.setIdleTime(getConfigValue<int>("per_document.cleanup.idle_time_secs", 300)); + docCleanupSettings.setLimitDirtyMem(getConfigValue<int>("per_document.cleanup.limit_dirty_mem_mb", 3072)); + docCleanupSettings.setLimitCpu(getConfigValue<int>("per_document.cleanup.limit_cpu_per", 85)); + Admin::instance().setDefDocProcSettings(docProcSettings, false); #if ENABLE_DEBUG commit da093e11843acef322cfa72ea28b18e4e546c253 Author: gokaysatir <[email protected]> AuthorDate: Sat May 30 11:50:34 2020 +0300 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:51:19 2020 +0200 tdf#111535 fix mispositioning bug when the page is resized. Change-Id: I67444baa78f99558906de4e8e6c45a8b6f11bce6 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95178 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Andras Timar <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95762 diff --git a/loleaflet/src/control/Ruler.js b/loleaflet/src/control/Ruler.js index 4ec19dd98..fa94ee1e9 100644 --- a/loleaflet/src/control/Ruler.js +++ b/loleaflet/src/control/Ruler.js @@ -425,6 +425,7 @@ L.Control.Ruler = L.Control.extend({ this._rFace.style.marginLeft = rulerOffset + 'px'; this.rulerOffset = rulerOffset; // Needed on different parts too.. + this._updateParagraphIndentations(); }, _moveIndentation: function(e) { commit dc361c46b700fac7454e9d57cf9fa3e6fc22313c Author: Tor Lillqvist <[email protected]> AuthorDate: Mon Jun 1 16:54:36 2020 +0300 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:50:43 2020 +0200 tdf#133278: Don't overwrite the copy of the document that we are editing When exporting a copy, let core write the copy to a temp subdirectory before invoking UIDocumentPickerViewController to select where to store it permanently. Change-Id: I3d2292414a3c824515ba6d98ad09b296e543cea9 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95295 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Tor Lillqvist <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95708 Reviewed-by: Andras Timar <[email protected]> diff --git a/ios/Mobile/DocumentViewController.mm b/ios/Mobile/DocumentViewController.mm index 54932be3f..c94161c02 100644 --- a/ios/Mobile/DocumentViewController.mm +++ b/ios/Mobile/DocumentViewController.mm @@ -16,6 +16,7 @@ #import <objc/runtime.h> #import <poll.h> +#import <sys/stat.h> #import "ios.h" #import "FakeSocket.hpp" @@ -465,8 +466,13 @@ static IMP standardImpOfInputAccessoryView = nil; // First save it in the requested format to a temporary location. First remove any // leftover identically named temporary file. + NSURL *tmpFileDirectory = [[NSFileManager.defaultManager temporaryDirectory] URLByAppendingPathComponent:@"export"]; + if (![NSFileManager.defaultManager createDirectoryAtURL:tmpFileDirectory withIntermediateDirectories:YES attributes:nil error:nil]) { + LOG_ERR("Could not create directory " << [[tmpFileDirectory path] UTF8String]); + return; + } NSString *tmpFileName = [[[self.document->copyFileURL lastPathComponent] stringByDeletingPathExtension] stringByAppendingString:[@"." stringByAppendingString:format]]; - downloadAsTmpURL = [[NSFileManager.defaultManager temporaryDirectory] URLByAppendingPathComponent:tmpFileName]; + downloadAsTmpURL = [tmpFileDirectory URLByAppendingPathComponent:tmpFileName]; std::remove([[downloadAsTmpURL path] UTF8String]); @@ -479,15 +485,15 @@ static IMP standardImpOfInputAccessoryView = nil; struct stat statBuf; if (stat([[downloadAsTmpURL path] UTF8String], &statBuf) == -1) { LOG_ERR("Could apparently not save to '" << [[downloadAsTmpURL path] UTF8String] << "'"); - } else { - UIDocumentPickerViewController *picker = - [[UIDocumentPickerViewController alloc] initWithURL:downloadAsTmpURL - inMode:UIDocumentPickerModeExportToService]; - picker.delegate = self; - [self presentViewController:picker - animated:YES - completion:nil]; + return; } + UIDocumentPickerViewController *picker = + [[UIDocumentPickerViewController alloc] initWithURL:downloadAsTmpURL + inMode:UIDocumentPickerModeExportToService]; + picker.delegate = self; + [self presentViewController:picker + animated:YES + completion:nil]; return; } } @@ -504,10 +510,12 @@ static IMP standardImpOfInputAccessoryView = nil; - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls { std::remove([[downloadAsTmpURL path] UTF8String]); + std::remove([[[downloadAsTmpURL URLByDeletingLastPathComponent] path] UTF8String]); } - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { std::remove([[downloadAsTmpURL path] UTF8String]); + std::remove([[[downloadAsTmpURL URLByDeletingLastPathComponent] path] UTF8String]); } - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view { commit 237cdba9d9cbaab6271a10d84e4fcee6002ed316 Author: Pedro Pinto Silva <[email protected]> AuthorDate: Mon Jun 1 12:59:03 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:50:33 2020 +0200 Desktop: Improve document-name-input states and discoverability - display element as an input field (some users didn't know they could rename the document) - use box-shadows instead of borders to increase the difference between states and so the text does not jump Change-Id: Id00bbcb3be27688603afdedeb25f14ba515bfe33 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95277 Tested-by: Jenkins Tested-by: Jenkins CollaboraOffice <[email protected]> Tested-by: Pedro Silva <[email protected]> Reviewed-by: Pedro Silva <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95707 Reviewed-by: Andras Timar <[email protected]> diff --git a/loleaflet/css/toolbar.css b/loleaflet/css/toolbar.css index cc80202e6..45b1531ec 100644 --- a/loleaflet/css/toolbar.css +++ b/loleaflet/css/toolbar.css @@ -157,14 +157,24 @@ w2ui-toolbar { width: 200px; } +#document-name-input.editable { + border: none; + box-shadow: 0 0 0.1px 1px #ebebeb, 0 0 2px 1px #f0f0f0; + background-image: url('images/baseline-edit.svg'); + background-position: right; + background-repeat: no-repeat; +} + #document-name-input.editable:focus { - border: 1px solid #bbbbbb; + border: none; + box-shadow: inset 0 0 2px 1px #f0f0f0, 0 0 0.1px 1px #bbb; background-color: white; width: 200px; } -#document-name-input.editable:hover { - border: 1px solid #bbbbbb; +#document-name-input.editable:hover:not(:focus) { + border: none; + box-shadow: 0 0 0.1px 1px #d7d7d7, 0 0 3px 2px #f0f0f0; background-color: white; background-image: url('images/baseline-edit.svg'); background-position: right; commit a247302ebaa60fdd2ce15a7f216988919226e669 Author: Pedro Pinto Silva <[email protected]> AuthorDate: Fri May 29 15:25:03 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:50:29 2020 +0200 Sidebar-panel has fixed width that is bigger than its contents causing a white placeholder to appear sometimes after the side panel (when it's open) and sometimes before (when it's closed) Change-Id: I6247b2bbb90943c3520dc4f22ee07966bf03d1e3 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95143 Tested-by: Jenkins CollaboraOffice <[email protected]> Tested-by: Jenkins Reviewed-by: Pedro Silva <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95706 Reviewed-by: Andras Timar <[email protected]> diff --git a/loleaflet/css/loleaflet.css b/loleaflet/css/loleaflet.css index 3ec71f0f1..0773c624d 100644 --- a/loleaflet/css/loleaflet.css +++ b/loleaflet/css/loleaflet.css @@ -152,10 +152,11 @@ body { } #sidebar-panel { + display: table; padding: 0px; margin: 0px; position: relative; - width: 100%; + width: auto; height: 100%; overflow-x: hidden; overflow-y: scroll; commit 59d121bd26ce0a03fc5ec962faee65ec85f2d0ed Author: Samuel Mehrbrodt <[email protected]> AuthorDate: Tue May 19 11:45:04 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:49:54 2020 +0200 tdf#131123 Report back save result Change-Id: Ie3dee5d344bc65c325b95f2746c9734bdd9e2f9d Reviewed-on: https://gerrit.libreoffice.org/c/online/+/94490 Tested-by: Jenkins CollaboraOffice <[email protected]> Tested-by: Jenkins Reviewed-by: Samuel Mehrbrodt <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95704 Reviewed-by: Andras Timar <[email protected]> diff --git a/loleaflet/html/framed.doc.html b/loleaflet/html/framed.doc.html index 0b68c698d..c58d306c9 100644 --- a/loleaflet/html/framed.doc.html +++ b/loleaflet/html/framed.doc.html @@ -107,6 +107,8 @@ if (msg.Values) { if (msg.Values.success == true) { document.getElementById("ModifiedStatus").innerHTML = "Saved"; + } else { + document.getElementById("ModifiedStatus").innerHTML = "Error during save"; } } } diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index a5e394c67..9f48d3145 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -358,9 +358,16 @@ L.Socket = L.Class.extend({ } else if (textMsg.startsWith('commandresult: ')) { var commandresult = JSON.parse(textMsg.substring(textMsg.indexOf('{'))); - if (commandresult['command'] === 'savetostorage' && commandresult['success']) { - // Close any open confirmation dialogs - vex.closeAll(); + if (commandresult['command'] === 'savetostorage' || commandresult['command'] === 'save') { + if (commandresult['success']) { + // Close any open confirmation dialogs + vex.closeAll(); + } + + var postMessageObj = { + success: commandresult['success'] + }; + this._map.fire('postMessage', {msgId: 'Action_Save_Resp', args: postMessageObj}); } return; } @@ -744,12 +751,6 @@ L.Socket = L.Class.extend({ } }); } - - // Issue the save response to be consistent with normal save. - var postMessageObj = { - success: true - }; - this._map.fire('postMessage', {msgId: 'Action_Save_Resp', args: postMessageObj}); } // var name = command.name; - ignored, we get the new name via the wopi's BaseFileName } diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 20b1ed011..3f37c88c4 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -552,8 +552,11 @@ bool ClientSession::_handleInput(const char *buffer, int length) constexpr bool isAutosave = false; constexpr bool isExitSave = false; - docBroker->sendUnoSave(getId(), dontTerminateEdit != 0, dontSaveIfUnmodified != 0, + bool result = docBroker->sendUnoSave(getId(), dontTerminateEdit != 0, dontSaveIfUnmodified != 0, isAutosave, isExitSave, extendedData); + std::string resultstr = result ? "true" : "false"; + std::string msg = "commandresult: { \"command\": \"save\", \"success\": " + resultstr + " }"; + docBroker->broadcastMessage(msg); } } else if (tokens.equals(0, "savetostorage")) @@ -562,10 +565,10 @@ bool ClientSession::_handleInput(const char *buffer, int length) if (tokens.size() > 1) getTokenInteger(tokens[1], "force", force); - if (docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when success is true*/, true)) - { - docBroker->broadcastMessage("commandresult: { \"command\": \"savetostorage\", \"success\": true }"); - } + bool result = docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when success is true*/, true); + std::string resultstr = result ? "true" : "false"; + std::string msg = "commandresult: { \"command\": \"savetostorage\", \"success\": " + resultstr + " }"; + docBroker->broadcastMessage(msg); } else if (tokens.equals(0, "clientvisiblearea")) { commit d9c0a480b7fa7602d32c16c093e0ad83c26edc3e Author: Pedro Pinto Silva <[email protected]> AuthorDate: Fri May 29 12:40:44 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:49:42 2020 +0200 Make sure document title bar is not above dialogs by reducing the value introduced in db32e4fc3800ffad83ea82e4835b0653d26d1bae and instead using the same z-index as its neighbour elment (main-menu) so 1000 Change-Id: I92d893d1a092e16a1455a6d1941a98e73ec63d1a Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95103 Tested-by: Jenkins Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Pedro Silva <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95264 Reviewed-by: Andras Timar <[email protected]> diff --git a/loleaflet/css/menubar.css b/loleaflet/css/menubar.css index a9031469a..cab048379 100644 --- a/loleaflet/css/menubar.css +++ b/loleaflet/css/menubar.css @@ -39,7 +39,7 @@ table-layout: fixed; border-spacing: 5px 0px; max-height: 39px; - z-index: 100000; + z-index: 1000; } .main-nav { commit ce39da18ca33176d783e5244651a90a486fce8cc Author: Marco Cecchetti <[email protected]> AuthorDate: Thu May 7 13:14:05 2020 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Jul 1 17:49:33 2020 +0200 leaflet: formula bar: missing text selection handles mobile: double tap a word double tap the next word the new word is selected but there is no selection handles Change-Id: I283dc1b177910605826102daaa28714c65190f4f Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95225 Tested-by: Jenkins Reviewed-by: Marco Cecchetti <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/online/+/95263 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Andras Timar <[email protected]> diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js index a96ae309f..dfddbdfc2 100644 --- a/loleaflet/src/control/Control.LokDialog.js +++ b/loleaflet/src/control/Control.LokDialog.js @@ -503,6 +503,9 @@ L.Control.LokDialog = L.Control.extend({ handles.end = null; } + if (!handles.start && !handles.end) + handles.draggingStopped = true; + if (!rectangles || rectangles.length < 1) { return; } _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
