[Libreoffice-commits] online.git: Branch 'private/mmeeks/thread-png' - 21 commits - bundled/include common/Log.cpp common/Log.hpp common/Png.hpp common/Util.cpp configure.ac gtk/README ios/Mobile.xcodeproj kit/ForKit.cpp kit/Kit.cpp loleaflet/html loleaflet/src loolkitconfig.xcu net/Socket.cpp net/Socket.hpp wsd/DocumentBroker.cpp wsd/LOOLWSD.cpp wsd/TileCache.cpp wsd/TileDesc.hpp

Michael Meeks (via logerrit) logerrit at kemper.freedesktop.org
Sat Dec 7 01:50:24 UTC 2019


Rebased ref, commits from common ancestor:
commit 629b69b4d606d0402fd58a3ecc427c6548ee6227
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Sat Apr 20 01:53:12 2019 +0100
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Fri Apr 26 19:59:31 2019 +0100

    PNG compression - thread it.
    
    Change-Id: I238fe6701a1d1cb486473c67faba8c56e9c98dcb

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 25050e04b..6b66449b4 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -737,6 +737,101 @@ private:
 static FILE* ProcSMapsFile = nullptr;
 #endif
 
+class ThreadPool {
+    std::mutex _mutex;
+    std::condition_variable _cond;
+    std::condition_variable _complete;
+    typedef std::function<void()> ThreadFn;
+    std::queue<ThreadFn> _work;
+    std::vector<std::thread> _threads;
+    size_t _working;
+    std::atomic<bool> _exit;
+public:
+    ThreadPool()
+        : _working(0),
+          _exit(false)
+    {
+        int maxConcurrency = 2;
+#if MOBILEAPP
+#  warning "Good defaults ? - 2 for iOS, 4 for Android ?"
+#else
+        const char *max = getenv("MAX_CONCURRENCY");
+        if (max)
+            maxConcurrency = atoi(max);
+#endif
+        LOG_TRC("PNG compression thread pool size " << maxConcurrency);
+        for (int i = 1; i < maxConcurrency; ++i)
+            _threads.push_back(std::thread(&ThreadPool::work, this));
+    }
+    ~ThreadPool()
+    {
+        assert(_working == 0);
+        _exit = true;
+        _cond.notify_all();
+        for (auto &it : _threads)
+            it.join();
+    }
+
+    size_t count() const
+    {
+        return _work.size();
+    }
+
+    void pushWorkUnlocked(const ThreadFn &fn)
+    {
+        _work.push(fn);
+    }
+
+    void runOne(std::unique_lock< std::mutex >& lock)
+    {
+        assert(!_work.empty());
+
+        ThreadFn fn = _work.front();
+        _work.pop();
+        _working++;
+        lock.unlock();
+
+        try {
+            fn();
+        } catch (...)
+        {
+            LOG_ERR("Exception during worker fn execution");
+        }
+
+        lock.lock();
+        _working--;
+        if (_work.empty() && _working == 0)
+            _complete.notify_all();
+    }
+
+    void run()
+    {
+        std::unique_lock< std::mutex > lock(_mutex);
+        assert(_working == 0);
+
+        // Avoid notifying threads if we don't need to.
+        bool useThreads = _threads.size() > 1 && _work.size() > 1;
+        if (useThreads)
+            _cond.notify_all();
+
+        while(!_work.empty())
+            runOne(lock);
+
+        if (useThreads && (_working > 0 || !_work.empty()))
+            _complete.wait(lock, [this]() { return _working == 0 && _work.empty(); } );
+    }
+
+    void work()
+    {
+        std::unique_lock< std::mutex > lock(_mutex);
+        while (!_exit)
+        {
+            _cond.wait(lock, [this]() { return !_work.empty(); });
+            runOne(lock);
+        }
+    }
+};
+
 /// A document container.
 /// Owns LOKitDocument instance and connections.
 /// Manages the lifetime of a document.
@@ -948,6 +1043,14 @@ public:
         renderTiles(tileCombined, true);
     }
 
+    static void pushRendered(std::vector<TileDesc> &renderedTiles,
+                             const TileDesc &desc, TileWireId wireId, size_t imgSize)
+    {
+        renderedTiles.push_back(desc);
+        renderedTiles.back().setWireId(wireId);
+        renderedTiles.back().setImgSize(imgSize);
+    }
+
     void renderTiles(TileCombined &tileCombined, bool combined)
     {
         auto& tiles = tileCombined.getTiles();
@@ -1016,6 +1119,9 @@ public:
         const int pixelWidth = tileCombined.getWidth();
         const int pixelHeight = tileCombined.getHeight();
 
+        static ThreadPool pool;
+
+        std::vector<TileDesc> renderedTiles;
         size_t tileIndex = 0;
         for (Util::Rectangle& tileRect : tileRecs)
         {
@@ -1035,13 +1141,15 @@ public:
                 // The tile content is identical to what the client already has, so skip it
                 LOG_TRC("Match for tile #" << tileIndex << " at (" << positionX << "," <<
                         positionY << ") oldhash==hash (" << hash << "), wireId: " << wireId << " skipping");
-                tiles.erase(tiles.begin() + tileIndex);
+                tileIndex++;
                 continue;
             }
 
-            size_t imgSize;
+            size_t imgSize = -1;
+            if (_pngCache.copyFromCache(hash, output, imgSize))
+                pushRendered(renderedTiles, tiles[tileIndex], wireId, imgSize);
 
-            if (!_pngCache.copyFromCache(hash, output, imgSize))
+            else
             {
                 LOG_DBG("PNG cache with hash " << hash << " missed.");
 
@@ -1051,43 +1159,52 @@ public:
                                             pixelWidth, pixelHeight,
                                             mode);
 
-                PngCache::CacheData data(new std::vector< char >() );
-                data->reserve(pixmapWidth * pixmapHeight * 1);
+                // Queue to be executed inside 'run'
+                pool.pushWorkUnlocked([=,&output,&pixmap,&tiles,&renderedTiles](){
+                        PngCache::CacheData data(new std::vector< char >() );
+                        data->reserve(pixmapWidth * pixmapHeight * 1);
+
+                        /*
+                         * Disable for now - pushed in error.
+                         *
+                         if (_deltaGen.createDelta(pixmap, startX, startY, width, height,
+                                                   bufferWidth, bufferHeight,
+                                                   output, wid, oldWid))
+                         else ...
+                        */
+
+                        LOG_DBG("Encode a new png for tile #" << tileIndex);
+                        if (!Png::encodeSubBufferToPNG(pixmap.data(), offsetX, offsetY, pixelWidth, pixelHeight,
+                                                       pixmapWidth, pixmapHeight, *data, mode))
+                        {
+                            // FIXME: Return error.
+                            // sendTextFrame("error: cmd=tile kind=failure");
+                            LOG_ERR("Failed to encode tile into PNG.");
+                            return;
+                        }
 
-/*
- *Disable for now - pushed in error.
- *
-                if (_deltaGen.createDelta(pixmap, startX, startY, width, height,
-                                          bufferWidth, bufferHeight,
-                                          output, wid, oldWid))
-                else ...
-*/
-
-                LOG_DBG("Encode a new png for this tile.");
-                if (!Png::encodeSubBufferToPNG(pixmap.data(), offsetX, offsetY, pixelWidth, pixelHeight,
-                                               pixmapWidth, pixmapHeight, *data, mode))
-                {
-                    // FIXME: Return error.
-                    // sendTextFrame("error: cmd=tile kind=failure");
-                    LOG_ERR("Failed to encode tile into PNG.");
-                    return;
-                }
+                        LOG_DBG("Tile " << tileIndex << " is " << data->size() << " bytes.");
+                        std::unique_lock<std::mutex> pngLock(_pngMutex);
 
-                output.insert(output.end(), data->begin(), data->end());
-                imgSize = data->size();
-                _pngCache.addToCache(data, wireId, hash);
+                        output.insert(output.end(), data->begin(), data->end());
+                        _pngCache.addToCache(data, wireId, hash);
+                        pushRendered(renderedTiles, tiles[tileIndex], wireId, data->size());
+                    });
             }
-
             LOG_TRC("Encoded tile #" << tileIndex << " at (" << positionX << "," << positionY << ") with oldWireId=" <<
                     tiles[tileIndex].getOldWireId() << ", hash=" << hash << " wireId: " << wireId << " in " << imgSize << " bytes.");
-            if (imgSize == 0)
+            tileIndex++;
+        }
+
+        pool.run();
+
+        for (auto &i : renderedTiles)
+        {
+            if (i.getImgSize() == 0)
             {
                 LOG_ERR("Encoded 0-sized tile!");
                 assert(!"0-sized tile enocded!");
             }
-            tiles[tileIndex].setWireId(wireId);
-            tiles[tileIndex].setImgSize(imgSize);
-            tileIndex++;
         }
 
         elapsed = timestamp.elapsed();
@@ -1103,7 +1220,7 @@ public:
 
         std::string tileMsg;
         if (combined)
-            tileMsg = tileCombined.serialize("tilecombine:", ADD_DEBUG_RENDERID);
+            tileMsg = tileCombined.serialize("tilecombine:", ADD_DEBUG_RENDERID, renderedTiles);
         else
             tileMsg = tiles[0].serialize("tile:", ADD_DEBUG_RENDERID);
 
@@ -1996,6 +2113,8 @@ private:
     std::shared_ptr<TileQueue> _tileQueue;
     SocketPoll& _socketPoll;
     std::shared_ptr<WebSocketHandler> _websocketHandler;
+
+    std::mutex _pngMutex;
     PngCache _pngCache;
 
     // Document password provided
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 57ddfd705..116e7c560 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -89,7 +89,6 @@ using Poco::Net::PartHandler;
 #include <Poco/StreamCopier.h>
 #include <Poco/StringTokenizer.h>
 #include <Poco/TemporaryFile.h>
-#include <Poco/ThreadPool.h>
 #include <Poco/URI.h>
 #include <Poco/Util/AbstractConfiguration.h>
 #include <Poco/Util/HelpFormatter.h>
diff --git a/wsd/TileDesc.hpp b/wsd/TileDesc.hpp
index 77fb46b26..d30b718da 100644
--- a/wsd/TileDesc.hpp
+++ b/wsd/TileDesc.hpp
@@ -357,27 +357,33 @@ public:
     std::string serialize(const std::string& prefix = std::string(),
                           const std::string& suffix = std::string()) const
     {
+        return serialize(prefix, suffix, _tiles);
+    }
+
+    std::string serialize(const std::string& prefix, const std::string &suffix,
+                          const std::vector<TileDesc> &tiles) const
+    {
         std::ostringstream oss;
         oss << prefix
             << " part=" << _part
             << " width=" << _width
             << " height=" << _height
             << " tileposx=";
-        for (const auto& tile : _tiles)
+        for (const auto& tile : tiles)
         {
             oss << tile.getTilePosX() << ',';
         }
         oss.seekp(-1, std::ios_base::cur); // Seek back over last comma, overwritten below.
 
         oss << " tileposy=";
-        for (const auto& tile : _tiles)
+        for (const auto& tile : tiles)
         {
             oss << tile.getTilePosY() << ',';
         }
         oss.seekp(-1, std::ios_base::cur); // Ditto.
 
         oss << " imgsize=";
-        for (const auto& tile : _tiles)
+        for (const auto& tile : tiles)
         {
             oss << tile.getImgSize() << ','; // Ditto.
         }
@@ -387,14 +393,14 @@ public:
             << " tileheight=" << _tileHeight;
 
         oss << " ver=";
-        for (const auto& tile : _tiles)
+        for (const auto& tile : tiles)
         {
             oss << tile.getVersion() << ',';
         }
         oss.seekp(-1, std::ios_base::cur); // Ditto.
 
         oss << " oldwid=";
-        for (const auto& tile : _tiles)
+        for (const auto& tile : tiles)
         {
             oss << tile.getOldWireId() << ',';
         }
@@ -403,7 +409,7 @@ public:
         oss << " wid=";
 
         bool comma = false;
-        for (const auto& tile : _tiles)
+        for (const auto& tile : tiles)
         {
             if (comma)
                 oss << ',';
commit 92562991bd5f8dcd821d9ed7e2d9003d2eac732a
Author:     Andras Timar <andras.timar at collabora.com>
AuthorDate: Tue Apr 23 10:05:05 2019 +0200
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Tue Apr 23 10:05:05 2019 +0200

    change default HelpRootURL to 'https://help.libreoffice.org/help.html?'
    
    Change-Id: I6463ad84b5c0df2860504f4e51f87b5537107f42

diff --git a/loolkitconfig.xcu b/loolkitconfig.xcu
index bbe1942d9..6ad397fc7 100644
--- a/loolkitconfig.xcu
+++ b/loolkitconfig.xcu
@@ -8,7 +8,7 @@
 <item oor:path="/org.openoffice.Office.Common/Accessibility"><prop oor:name="ListBoxMaximumLineCount" oor:op="fuse"><value>11</value></prop></item>
 
 <!-- The Help root URL, or empty for no help (hides the help buttons) -->
-<item oor:path="/org.openoffice.Office.Common/Help"><prop oor:name="HelpRootURL" oor:op="fuse"><value>https://help.collaboraoffice.com/help.html?</value></prop></item>
+<item oor:path="/org.openoffice.Office.Common/Help"><prop oor:name="HelpRootURL" oor:op="fuse"><value>https://help.libreoffice.org/help.html?</value></prop></item>
 
 <!-- Enable spell-checking by default -->
 <item oor:path="/org.openoffice.Office.Linguistic/SpellChecking"><prop oor:name="IsSpellAuto" oor:op="fuse"><value>true</value></prop></item>
commit fdc731a07d805b3c2e7aa4e2b45c22afc39a5761
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Tue Apr 23 09:10:11 2019 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Tue Apr 23 09:10:18 2019 +0200

    gtk: fix typo
    
    Change-Id: Ib8bcf8b578f1d926f26a0ebae15cfa36e5d922d2

diff --git a/gtk/README b/gtk/README
index 576504df3..ae9528379 100644
--- a/gtk/README
+++ b/gtk/README
@@ -2,7 +2,7 @@ This is a GTK+ Webkit app that is intended to work similarly enough to
 the iOS app being developed in the "ios" folder, and the Android app
 being developed in the "android" folder, that (some kinds of) problems
 in them also show up in this app, and can be investigated by people
-with no Android, Mac, oriOS device.
+with no Android, Mac, or iOS device.
 
 How to build this:
 
commit 69995fa0f1f96ba91460272904de91f46520b260
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Tue Mar 12 11:06:49 2019 -0400
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Tue Apr 23 07:47:04 2019 +0200

    Move the Help URL to a new config path
    
    Change-Id: I76468d1c032689099b371449aedc0e5583f0ae2d
    Reviewed-on: https://gerrit.libreoffice.org/69107
    Reviewed-by: Andras Timar <andras.timar at collabora.com>
    Tested-by: Andras Timar <andras.timar at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/71087

diff --git a/loolkitconfig.xcu b/loolkitconfig.xcu
index a9b7f0562..bbe1942d9 100644
--- a/loolkitconfig.xcu
+++ b/loolkitconfig.xcu
@@ -8,7 +8,7 @@
 <item oor:path="/org.openoffice.Office.Common/Accessibility"><prop oor:name="ListBoxMaximumLineCount" oor:op="fuse"><value>11</value></prop></item>
 
 <!-- The Help root URL, or empty for no help (hides the help buttons) -->
-<item oor:path="/org.openoffice.Office.Common/Accessibility"><prop oor:name="HelpURL" oor:op="fuse"><value>https://help.collaboraoffice.com/help.html?</value></prop></item>
+<item oor:path="/org.openoffice.Office.Common/Help"><prop oor:name="HelpRootURL" oor:op="fuse"><value>https://help.collaboraoffice.com/help.html?</value></prop></item>
 
 <!-- Enable spell-checking by default -->
 <item oor:path="/org.openoffice.Office.Linguistic/SpellChecking"><prop oor:name="IsSpellAuto" oor:op="fuse"><value>true</value></prop></item>
commit e8bcf063300f6032f79fd21d19fdfebbfa753a48
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Mon Mar 11 22:14:20 2019 -0400
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Tue Apr 23 07:46:46 2019 +0200

    Add help URL to loolkitconfig.xcu
    
    When blank the help button is hidden from
    all dialogs.
    
    Change-Id: Ibd43411bed496bfd3bc81056ebc8657fb77fb8fe
    Reviewed-on: https://gerrit.libreoffice.org/69072
    Reviewed-by: Andras Timar <andras.timar at collabora.com>
    Tested-by: Andras Timar <andras.timar at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/71086

diff --git a/loolkitconfig.xcu b/loolkitconfig.xcu
index 8e00eabcc..a9b7f0562 100644
--- a/loolkitconfig.xcu
+++ b/loolkitconfig.xcu
@@ -5,7 +5,10 @@
 <item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="UseLocking" oor:op="fuse"><value>false</value></prop></item>
 
 <!-- Dialogs have limited real estate, limit drop-down height -->
-<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="ListBoxMaximumLineCount" oor:op="fuse"><value>11</value></prop></item>
+<item oor:path="/org.openoffice.Office.Common/Accessibility"><prop oor:name="ListBoxMaximumLineCount" oor:op="fuse"><value>11</value></prop></item>
+
+<!-- The Help root URL, or empty for no help (hides the help buttons) -->
+<item oor:path="/org.openoffice.Office.Common/Accessibility"><prop oor:name="HelpURL" oor:op="fuse"><value>https://help.collaboraoffice.com/help.html?</value></prop></item>
 
 <!-- Enable spell-checking by default -->
 <item oor:path="/org.openoffice.Office.Linguistic/SpellChecking"><prop oor:name="IsSpellAuto" oor:op="fuse"><value>true</value></prop></item>
commit 4f4bcbc9822f749072ebd90854cb01cae80fe512
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Tue Mar 12 11:55:43 2019 -0400
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Tue Apr 23 07:46:22 2019 +0200

    leaflet: do not disable the Close Document menu for read-only docs
    
    Change-Id: I6a52b0bd807ae68ea6bf59e103c004375afae50b
    Reviewed-on: https://gerrit.libreoffice.org/69110
    Reviewed-by: Andras Timar <andras.timar at collabora.com>
    Tested-by: Andras Timar <andras.timar at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/71088

diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index d0e8c5724..9a005f3f4 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -426,7 +426,7 @@ L.Control.Menubar = L.Control.extend({
 		allowedViewModeActions: [
 			'downloadas-pdf', 'downloadas-odt', 'downloadas-doc', 'downloadas-docx', 'downloadas-rtf', // file menu
 			'downloadas-odp', 'downloadas-ppt', 'downloadas-pptx', 'print', // file menu
-			'downloadas-ods', 'downloadas-xls', 'downloadas-xlsx', // file menu
+			'downloadas-ods', 'downloadas-xls', 'downloadas-xlsx', 'closedocument', // file menu
 			'fullscreen', 'zoomin', 'zoomout', 'zoomreset', // view menu
 			'about', 'keyboard-shortcuts' // help menu
 		]
commit ede2c3432f1d25c1c74b94f8fd5686fd59750e7b
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Sat Apr 20 23:32:37 2019 +0300
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 04:52:05 2019 +0200

    tdf#124235: Fix the same problem on all platforms the same way
    
    Change-Id: Ie7ede59841898a0738af7e44a3c0fe89db1cd3ee
    Reviewed-on: https://gerrit.libreoffice.org/71023
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index a58fd6008..ce6940698 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -658,17 +658,7 @@ L.Control.LokDialog = L.Control.extend({
 		var dialogTitle = $('.lokdialog_notitle');
 		if (dialogTitle != null && dialogTitle.length == 0) {
 			var dialogTitleBar = $('.ui-dialog-titlebar');
-			// tdf#124235: At least in the iOS app, multiplying with
-			// L.getDpiScaleFactor() below causes the child of a combo box to be
-			// displaced from the fixed part. I see the same problem also when using
-			// Safari on a Retina Mac against normal online. But as I don't know whether
-			// it happens also for other browsers on other platforms on hidpi displays,
-			// I will fix this for the iOS app only for now.
-			if (!window.ThisIsTheiOSApp) {
-				top += dialogTitleBar.outerHeight() * L.getDpiScaleFactor();
-			} else {
-				top += dialogTitleBar.outerHeight();
-			}
+			top += dialogTitleBar.outerHeight();
 		}
 
 		floatingCanvas.id = strId + '-floating';
commit 83a73e24e0c8fb622e721b40fdde273ea003a195
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Jan 24 06:53:37 2019 -0500
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:44:07 2019 +0200

    wsd: set the language when creating a new view
    
    This uses the new createViewWithOptions API to
    set the language of the user at the point of
    creating a new view.
    
    Change-Id: Ibf3e0af1b0cc300126388c1217692d8d969be0f3
    Reviewed-on: https://gerrit.libreoffice.org/67501
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/bundled/include/LibreOfficeKit/LibreOfficeKit.h
index f2c8dd40f..62bd3c429 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.h
@@ -375,6 +375,9 @@ struct _LibreOfficeKitDocumentClass
                                   int nY,
                                   int nOffset);
 
+    /// @see lok::Document::createViewWithOptions().
+    int (*createViewWithOptions) (LibreOfficeKitDocument* pThis, const char* pOptions);
+
 #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 6bebf3661..7027298a1 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -440,13 +440,17 @@ public:
     }
 
     /**
-     * Create a new view for an existing document.
+     * Create a new view for an existing document with
+     * options similar to documentLoadWithOptions.
      * By default a loaded document has 1 view.
      * @return the ID of the new view.
      */
-    int createView()
+    int createView(const char* pOptions = nullptr)
     {
-        return mpDoc->pClass->createView(mpDoc);
+        if (LIBREOFFICEKIT_DOCUMENT_HAS(mpDoc, createViewWithOptions))
+            return mpDoc->pClass->createViewWithOptions(mpDoc, pOptions);
+        else
+            return mpDoc->pClass->createView(mpDoc);
     }
 
     /**
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 759e78e24..25050e04b 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -1571,6 +1571,10 @@ private:
     {
         const std::string sessionId = session->getId();
 
+        std::string options;
+        if (!lang.empty())
+            options = "Language=" + lang;
+
         std::unique_lock<std::mutex> lock(_documentMutex);
 
         if (!_loKitDocument)
@@ -1594,10 +1598,6 @@ private:
             _jailedUrl = uri;
             _isDocPasswordProtected = false;
 
-            std::string options;
-            if (!lang.empty())
-                options = "Language=" + lang;
-
             LOG_DBG("Calling lokit::documentLoad(" << uriAnonym << ", \"" << options << "\").");
             Timestamp timestamp;
             _loKitDocument.reset(_loKit->documentLoad(uri.c_str(), options.c_str()));
@@ -1666,8 +1666,8 @@ private:
                 }
             }
 
-            LOG_INF("Creating view to url [" << uriAnonym << "] for session [" << sessionId << "].");
-            _loKitDocument->createView();
+            LOG_INF("Creating view to url [" << uriAnonym << "] for session [" << sessionId << "] with " << options << '.');
+            _loKitDocument->createView(options.c_str());
             LOG_TRC("View to url [" << uriAnonym << "] created.");
         }
 
commit ca1a83f96b013e171130596dd1486b1416ddbea3
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Mon Apr 22 12:03:49 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:02:36 2019 +0200

    wsd: reduce tile logging from debug to trace level
    
    These are very frequent and not very useful without
    the ability to trace them across the system, which
    are all done at trace level. So it's highly unlikely
    that these would be used at debug.
    
    Change-Id: I479f2ead1bbd2996b9972082e00ebf984072f785
    Reviewed-on: https://gerrit.libreoffice.org/71073
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 53d3e2dbe..40465c0ce 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1395,7 +1395,7 @@ void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
 
         // Forward to child to render.
         const std::string req = newTileCombined.serialize("tilecombine");
-        LOG_DBG("Sending residual tilecombine: " << req);
+        LOG_TRC("Sending uncached residual tilecombine request to Kit: " << req);
         _childProcess->sendTextFrame(req);
     }
 
@@ -1527,7 +1527,7 @@ void DocumentBroker::sendRequestedTiles(const std::shared_ptr<ClientSession>& se
 
             // Forward to child to render.
             const std::string req = newTileCombined.serialize("tilecombine");
-            LOG_DBG("Some of the tiles were not prerendered. Sending residual tilecombine: " << req);
+            LOG_TRC("Some of the tiles were not prerendered. Sending residual tilecombine: " << req);
             _childProcess->sendTextFrame(req);
         }
     }
diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp
index 287e0ba77..a4f16a5f0 100644
--- a/wsd/TileCache.cpp
+++ b/wsd/TileCache.cpp
@@ -311,7 +311,7 @@ void TileCache::invalidateTiles(int part, int x, int y, int width, int height)
     {
         if (intersectsTile(it->first, part, x, y, width, height))
         {
-            LOG_DBG("Removing tile: " << it->first.serialize());
+            LOG_TRC("Removing tile: " << it->first.serialize());
             it = _cache.erase(it);
         }
         else
commit 2ec5023dbf4e8fd8d59a82f9040ad88933a10aa2
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Mon Apr 22 12:02:58 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:02:09 2019 +0200

    wsd: assert on 0-sized tiles
    
    Change-Id: I10a20bb5a3ad31e368f554bdc5e1701a7ff6a22d
    Reviewed-on: https://gerrit.libreoffice.org/71072
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index d098cd773..759e78e24 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -1080,6 +1080,11 @@ public:
 
             LOG_TRC("Encoded tile #" << tileIndex << " at (" << positionX << "," << positionY << ") with oldWireId=" <<
                     tiles[tileIndex].getOldWireId() << ", hash=" << hash << " wireId: " << wireId << " in " << imgSize << " bytes.");
+            if (imgSize == 0)
+            {
+                LOG_ERR("Encoded 0-sized tile!");
+                assert(!"0-sized tile enocded!");
+            }
             tiles[tileIndex].setWireId(wireId);
             tiles[tileIndex].setImgSize(imgSize);
             tileIndex++;
commit c45dfe194bbcb10514761c3564f42a8ff7f3fc17
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Mon Apr 22 12:00:19 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:01:46 2019 +0200

    leaflet: process the most common message first
    
    The 'tile:' message is the most common and most
    latency sensitive message, so give it priority.
    
    Change-Id: Id5790369cd493423a47acab8a3d5107ce91b0d39
    Reviewed-on: https://gerrit.libreoffice.org/71071
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index b02b00943..464a1f1df 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -810,20 +810,22 @@ L.Socket = L.Class.extend({
 			this._map.fire('docloaded', {status: true});
 		}
 
-		// these can arrive very early during the startup
-		if (textMsg.startsWith('statusindicatorstart:')) {
-			this._map.fire('statusindicator', {statusType : 'start'});
-			return;
-		}
-		else if (textMsg.startsWith('statusindicatorsetvalue:')) {
-			var value = textMsg.match(/\d+/g)[0];
-			this._map.fire('statusindicator', {statusType : 'setvalue', value : value});
-			return;
-		}
-		else if (textMsg.startsWith('statusindicatorfinish:')) {
-			this._map.fire('statusindicator', {statusType : 'finish'});
-			this._map._fireInitComplete('statusindicatorfinish');
-			return;
+		// These can arrive very early during the startup, and never again.
+		if (textMsg.startsWith('statusindicator')) {
+			if (textMsg.startsWith('statusindicatorstart:')) {
+				this._map.fire('statusindicator', {statusType : 'start'});
+				return;
+			}
+			else if (textMsg.startsWith('statusindicatorsetvalue:')) {
+				var value = textMsg.match(/\d+/g)[0];
+				this._map.fire('statusindicator', {statusType : 'setvalue', value : value});
+				return;
+			}
+			else if (textMsg.startsWith('statusindicatorfinish:')) {
+				this._map.fire('statusindicator', {statusType : 'finish'});
+				this._map._fireInitComplete('statusindicatorfinish');
+				return;
+			}
 		}
 
 		if (this._map._docLayer) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 42c880e57..43256a15e 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -359,7 +359,11 @@ L.TileLayer = L.GridLayer.extend({
 	},
 
 	_onMessage: function (textMsg, img) {
-		if (textMsg.startsWith('commandvalues:')) {
+		// 'tile:' is the most common message type; keep this the first.
+		if (textMsg.startsWith('tile:')) {
+			this._onTileMsg(textMsg, img);
+		}
+		else if (textMsg.startsWith('commandvalues:')) {
 			this._onCommandValuesMsg(textMsg);
 		}
 		else if (textMsg.startsWith('cursorvisible:')) {
@@ -447,9 +451,6 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('textselectionstart:')) {
 			this._onTextSelectionStartMsg(textMsg);
 		}
-		else if (textMsg.startsWith('tile:')) {
-			this._onTileMsg(textMsg, img);
-		}
 		else if (textMsg.startsWith('windowpaint:')) {
 			this._onDialogPaintMsg(textMsg, img);
 		}
@@ -812,7 +813,7 @@ L.TileLayer = L.GridLayer.extend({
 		//first time document open, set last cursor position
 		if (this.lastCursorPos.lat === 0 && this.lastCursorPos.lng === 0)
 			this.lastCursorPos = cursorPos;
-		
+
 		var updateCursor = false;
 		if ((this.lastCursorPos.lat !== cursorPos.lat) || (this.lastCursorPos.lng !== cursorPos.lng)) {
 			updateCursor = true;
commit c7ac68e8e31d2cf2179c12c817a09ad24c0cc22a
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Sat Apr 20 14:58:48 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:01:30 2019 +0200

    wsd: tile serializer now supports adding a suffix
    
    Moves appending tokens into the serializer and
    avoids making extra copies of itself.
    
    Change-Id: I62d374e69d9c4a55643ea20cb5f8c2b9c75c88c5
    Reviewed-on: https://gerrit.libreoffice.org/71022
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 76dddab46..d098cd773 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -121,9 +121,9 @@ static std::string ObfuscatedFileId;
 #endif
 
 #if ENABLE_DEBUG
-#  define ADD_DEBUG_RENDERID(s) ((s)+ " renderid=" + Util::UniqueId())
+#  define ADD_DEBUG_RENDERID (" renderid=" + Util::UniqueId() + '\n')
 #else
-#  define ADD_DEBUG_RENDERID(s) (s)
+#  define ADD_DEBUG_RENDERID ("\n")
 #endif
 
 #if !MOBILEAPP
@@ -1098,9 +1098,9 @@ public:
 
         std::string tileMsg;
         if (combined)
-            tileMsg = ADD_DEBUG_RENDERID(tileCombined.serialize("tilecombine:")) + "\n";
+            tileMsg = tileCombined.serialize("tilecombine:", ADD_DEBUG_RENDERID);
         else
-            tileMsg = ADD_DEBUG_RENDERID(tiles[0].serialize("tile:")) + "\n";
+            tileMsg = tiles[0].serialize("tile:", ADD_DEBUG_RENDERID);
 
         LOG_TRC("Sending back painted tiles for " << tileMsg << " of size " << output.size() << " bytes) for: " << tileMsg);
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 8524725ff..53d3e2dbe 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -48,6 +48,12 @@ using namespace LOOLProtocol;
 
 using Poco::JSON::Object;
 
+#if ENABLE_DEBUG
+#  define ADD_DEBUG_RENDERID (" renderid=cached\n")
+#else
+#  define ADD_DEBUG_RENDERID ("\n")
+#endif
+
 void ChildProcess::setDocumentBroker(const std::shared_ptr<DocumentBroker>& docBroker)
 {
     assert(docBroker && "Invalid DocumentBroker instance.");
@@ -1335,11 +1341,7 @@ void DocumentBroker::handleTileRequest(TileDesc& tile,
     TileCache::Tile cachedTile = _tileCache->lookupTile(tile);
     if (cachedTile)
     {
-#if ENABLE_DEBUG
-        const std::string response = tile.serialize("tile:") + " renderid=cached\n";
-#else
-        const std::string response = tile.serialize("tile:") + '\n';
-#endif
+        const std::string response = tile.serialize("tile:", ADD_DEBUG_RENDERID);
         session->sendTile(response, cachedTile);
         return;
     }
@@ -1452,7 +1454,6 @@ void DocumentBroker::sendRequestedTiles(const std::shared_ptr<ClientSession>& se
     float tilesOnFlyUpperLimit = 0;
     if (normalizedVisArea.hasSurface() && session->getTileWidthInTwips() != 0 && session->getTileHeightInTwips() != 0)
     {
-
         const int tilesFitOnWidth = std::ceil(normalizedVisArea.getRight() / session->getTileWidthInTwips()) -
                                     std::ceil(normalizedVisArea.getLeft() / session->getTileWidthInTwips()) + 1;
         const int tilesFitOnHeight = std::ceil(normalizedVisArea.getBottom() / session->getTileHeightInTwips()) -
@@ -1500,11 +1501,7 @@ void DocumentBroker::sendRequestedTiles(const std::shared_ptr<ClientSession>& se
             if (cachedTile)
             {
                 //TODO: Combine the response to reduce latency.
-#if ENABLE_DEBUG
-                const std::string response = tile.serialize("tile:") + " renderid=cached\n";
-#else
-                const std::string response = tile.serialize("tile:") + "\n";
-#endif
+                const std::string response = tile.serialize("tile:", ADD_DEBUG_RENDERID);
                 session->sendTile(response, cachedTile);
             }
             else
diff --git a/wsd/TileDesc.hpp b/wsd/TileDesc.hpp
index abb922b3f..77fb46b26 100644
--- a/wsd/TileDesc.hpp
+++ b/wsd/TileDesc.hpp
@@ -29,20 +29,21 @@ typedef uint64_t TileBinaryHash;
 class TileDesc
 {
 public:
-    TileDesc(int part, int width, int height, int tilePosX, int tilePosY, int tileWidth, int tileHeight, int ver, int imgSize, int id, bool broadcast) :
-        _part(part),
-        _width(width),
-        _height(height),
-        _tilePosX(tilePosX),
-        _tilePosY(tilePosY),
-        _tileWidth(tileWidth),
-        _tileHeight(tileHeight),
-        _ver(ver),
-        _imgSize(imgSize),
-        _id(id),
-        _broadcast(broadcast),
-        _oldWireId(0),
-        _wireId(0)
+    TileDesc(int part, int width, int height, int tilePosX, int tilePosY, int tileWidth,
+             int tileHeight, int ver, int imgSize, int id, bool broadcast)
+        : _part(part)
+        , _width(width)
+        , _height(height)
+        , _tilePosX(tilePosX)
+        , _tilePosY(tilePosY)
+        , _tileWidth(tileWidth)
+        , _tileHeight(tileHeight)
+        , _ver(ver)
+        , _imgSize(imgSize)
+        , _id(id)
+        , _broadcast(broadcast)
+        , _oldWireId(0)
+        , _wireId(0)
     {
         if (_part < 0 ||
             _width <= 0 ||
@@ -138,7 +139,8 @@ public:
 
     /// Serialize this instance into a string.
     /// Optionally prepend a prefix.
-    std::string serialize(const std::string& prefix = "") const
+    std::string serialize(const std::string& prefix = std::string(),
+                          const std::string& suffix = std::string()) const
     {
         std::ostringstream oss;
         oss << prefix
@@ -170,6 +172,7 @@ public:
             oss << " broadcast=yes";
         }
 
+        oss << suffix;
         return oss.str();
     }
 
@@ -351,7 +354,8 @@ public:
 
     /// Serialize this instance into a string.
     /// Optionally prepend a prefix.
-    std::string serialize(const std::string& prefix = "") const
+    std::string serialize(const std::string& prefix = std::string(),
+                          const std::string& suffix = std::string()) const
     {
         std::ostringstream oss;
         oss << prefix
@@ -397,15 +401,19 @@ public:
         oss.seekp(-1, std::ios_base::cur); // Ditto
 
         oss << " wid=";
+
+        bool comma = false;
         for (const auto& tile : _tiles)
         {
-            oss << tile.getWireId() << ',';
+            if (comma)
+                oss << ',';
+
+            oss << tile.getWireId();
+            comma = true;
         }
-        oss.seekp(-1, std::ios_base::cur); // See beow.
 
-        // Make sure we don't return a potential trailing comma that
-        // we have seeked back over but not overwritten after all.
-        return oss.str().substr(0, oss.tellp());
+        oss << suffix;
+        return oss.str();
     }
 
     /// Deserialize a TileDesc from a tokenized string.
commit c053dc38b2b1c8da9685fbeabb88e63d5669593d
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Sat Apr 20 14:10:38 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:01:17 2019 +0200

    wsd: set vector size when constructing
    
    Change-Id: I68718554017b47b6df1c6bf3b997483d4c753136
    Reviewed-on: https://gerrit.libreoffice.org/71021
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 281853b42..76dddab46 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -29,8 +29,10 @@
 #include <climits>
 #include <condition_variable>
 #include <cstdlib>
+#include <cstring>
 #include <iostream>
 #include <memory>
+#include <string>
 #include <sstream>
 #include <thread>
 
@@ -1102,8 +1104,7 @@ public:
 
         LOG_TRC("Sending back painted tiles for " << tileMsg << " of size " << output.size() << " bytes) for: " << tileMsg);
 
-        std::shared_ptr<std::vector<char>> response = std::make_shared<std::vector<char>>();
-        response->resize(tileMsg.size() + output.size());
+        std::shared_ptr<std::vector<char>> response = std::make_shared<std::vector<char>>(tileMsg.size() + output.size());
         std::copy(tileMsg.begin(), tileMsg.end(), response->begin());
         std::copy(output.begin(), output.end(), response->begin() + tileMsg.size());
 
commit ce78fec310ab5ab6aecabb73cad7d782afcb885f
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Sat Apr 20 11:39:20 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:01:00 2019 +0200

    wsd: reuse ostringstream when logging
    
    This is faster and reduces memory fragmentation.
    
    Also, cleans up the logging macros and implementation.
    
    Change-Id: I7fb00da041d1261c694c4b48b67a3c66ad0cbf8d
    Reviewed-on: https://gerrit.libreoffice.org/71020
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/common/Log.cpp b/common/Log.cpp
index b892c5b4b..46cfd6095 100644
--- a/common/Log.cpp
+++ b/common/Log.cpp
@@ -126,6 +126,21 @@ namespace Log
         return buffer;
     }
 
+    // Reuse the same buffer to minimize memory fragmentation.
+    static thread_local std::ostringstream Oss;
+
+    std::ostringstream& begin(const char* level)
+    {
+        // Reset the oss.
+        Oss.clear();
+        Oss.seekp(0);
+
+        // Output the prefix.
+        char buffer[1024];
+        Oss << Log::prefix(buffer, sizeof(buffer) - 1, level) << std::boolalpha;
+        return Oss;
+    }
+
     void signalLogPrefix()
     {
         char buffer[1024];
diff --git a/common/Log.hpp b/common/Log.hpp
index bdc654b39..f4a51d8da 100644
--- a/common/Log.hpp
+++ b/common/Log.hpp
@@ -54,6 +54,9 @@ namespace Log
 
     char* prefix(char* buffer, std::size_t len, const char* level);
 
+    /// Starts logging by generating the prefix and returning an oss.
+    std::ostringstream& begin(const char* level);
+
     inline bool traceEnabled() { return logger().trace(); }
     inline bool debugEnabled() { return logger().debug(); }
     inline bool infoEnabled() { return logger().information(); }
@@ -237,31 +240,29 @@ namespace Log
 #define LOG_FILE_NAME(f) (strrchr(f, '/')+1)
 #endif
 
-#define LOG_END(LOG, FILEP)                             \
-    do                                                  \
-    {                                                   \
-        if (FILEP)                                      \
-            LOG << "| " << LOG_FILE_NAME(__FILE__) << ':' << __LINE__; \
+#define LOG_END(LOG, FILEP)                                                                        \
+    do                                                                                             \
+    {                                                                                              \
+        if (FILEP)                                                                                 \
+            LOG << "| " << LOG_FILE_NAME(__FILE__) << ':' << __LINE__;                             \
     } while (false)
 
 #ifdef __ANDROID__
 
-#define LOG_BODY_(LOG, PRIO, LVL, X, FILEP)                                                 \
-    char b_[1024];                                                                          \
-    std::ostringstream oss_(Log::prefix(b_, sizeof(b_) - 1, LVL), std::ostringstream::ate); \
-    oss_ << std::boolalpha << X;                                                            \
-    LOG_END(oss_, FILEP);                                                                   \
+#define LOG_BODY_(LOG, PRIO, LVL, X, FILEP)                                                        \
+    std::ostringstream& oss_ = Log::begin(LVL);                                                    \
+    oss_ << X;                                                                                     \
+    LOG_END(oss_, FILEP);                                                                          \
     ((void)__android_log_print(ANDROID_LOG_DEBUG, "loolwsd", "%s %s", LVL, oss_.str().c_str()))
 
 #else
 
-#define LOG_BODY_(LOG, PRIO, LVL, X, FILEP)                                                 \
-    Poco::Message m_(LOG.name(), "", Poco::Message::PRIO_##PRIO);                           \
-    char b_[1024];                                                                          \
-    std::ostringstream oss_(Log::prefix(b_, sizeof(b_) - 1, LVL), std::ostringstream::ate); \
-    oss_ << std::boolalpha << X;                                                            \
-    LOG_END(oss_, FILEP);                                                                   \
-    m_.setText(oss_.str());                                                                 \
+#define LOG_BODY_(LOG, PRIO, LVL, X, FILEP)                                                        \
+    Poco::Message m_(LOG.name(), "", Poco::Message::PRIO_##PRIO);                                  \
+    std::ostringstream& oss_ = Log::begin(LVL);                                                    \
+    oss_ << X;                                                                                     \
+    LOG_END(oss_, FILEP);                                                                          \
+    m_.setText(oss_.str());                                                                        \
     LOG.log(m_);
 
 #endif
commit ed0efde542b9d48b0b48a733f12b302a8b36c0a0
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Sat Apr 20 15:23:32 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:00:44 2019 +0200

    wsd: use thread_local instead of __thread
    
    The former is the standard C++ approach
    and is reportedly faster than __thread
    (at least with gcc).
    
    Change-Id: Ibdefd32172774a280637f73dd062282b7bf62025
    Reviewed-on: https://gerrit.libreoffice.org/71019
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/common/Util.cpp b/common/Util.cpp
index ab21e0c96..06a715320 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -497,7 +497,7 @@ namespace Util
         return replace(r, "\n", " / ");
     }
 
-    static __thread char ThreadName[32] = {0};
+    static thread_local char ThreadName[32] = {0};
 
     void setThreadName(const std::string& s)
     {
@@ -537,7 +537,7 @@ namespace Util
     }
 
 #ifdef __linux
-    static __thread pid_t ThreadTid = 0;
+    static thread_local pid_t ThreadTid = 0;
 
     pid_t getThreadId()
 #else
commit e76d27188ee11f1387d56b851dc73d83f093be48
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Fri Apr 19 20:09:22 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:00:31 2019 +0200

    wsd: use fast deflate level for png
    
    The default deflate level of 6 is quite slow
    and the benefits are hardly worth the high
    latency that users experience.
    
    Tested on a writer document with some small
    images and a few pages of text:
    
    Level 4 gives virtually identical compression
    ratio to level 6, but is between 5-10% faster.
    
    Level 3 runs almost twice as fast as level 6,
    but the output is typically 2-3x larger.
    
    Perhaps this should be exposed via config
    so it would be possible to reduce latency
    due to compression when CPU is scarce but
    network bandwidth ample, and vice versa.
    
    Change-Id: Iba88eea8f180d11458b33c68389e797234df1a60
    Reviewed-on: https://gerrit.libreoffice.org/71018
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/common/Png.hpp b/common/Png.hpp
index 1cd03f085..d4e1c2cf5 100644
--- a/common/Png.hpp
+++ b/common/Png.hpp
@@ -136,6 +136,12 @@ bool encodeSubBufferToPNG(unsigned char* pixmap, size_t startX, size_t startY,
 
 #if MOBILEAPP
     png_set_compression_level(png_ptr, Z_BEST_SPEED);
+#else
+    // Level 4 gives virtually identical compression
+    // ratio to level 6, but is between 5-10% faster.
+    // Level 3 runs almost twice as fast, but the
+    // output is typically 2-3x larger.
+    png_set_compression_level(png_ptr, 4);
 #endif
 
 #ifdef IOS
commit 90c8dd41b94072f3712a0847553abb9b6b908697
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Fri Apr 19 20:06:27 2019 -0400
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:00:19 2019 +0200

    wsd: logging, comments, and const correctness
    
    Change-Id: Ibfbef282e951a80fb145239df4bbcc9f3e4c8e13
    Reviewed-on: https://gerrit.libreoffice.org/71017
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/common/Png.hpp b/common/Png.hpp
index 726454d9c..1cd03f085 100644
--- a/common/Png.hpp
+++ b/common/Png.hpp
@@ -173,7 +173,9 @@ bool encodeSubBufferToPNG(unsigned char* pixmap, size_t startX, size_t startY,
 
     totalDuration += duration;
     nCalls += 1;
-    LOG_TRC("Average PNG compression time after " << std::to_string(nCalls) << " calls: " << (totalDuration / static_cast<double>(nCalls)));
+    LOG_TRC("PNG compression took " << duration << " ms (" << output.size()
+                                    << " bytes). Average after " << std::to_string(nCalls)
+                                    << " calls: " << (totalDuration / static_cast<double>(nCalls)));
 
     png_destroy_write_struct(&png_ptr, &info_ptr);
 
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 65d386b72..281853b42 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -430,7 +430,6 @@ private:
             return _wireId;
         }
     } ;
-
     size_t _cacheSize;
     static const size_t CacheSizeSoftLimit = (1024 * 4 * 32); // 128k of cache
     static const size_t CacheSizeHardLimit = CacheSizeSoftLimit * 2;
@@ -474,9 +473,9 @@ private:
             for (auto it = _cache.begin(); it != _cache.end(); ++it)
                 avgHits += it->second.getHitCount();
 
-            LOG_DBG("cache " << _cache.size() << " items total size " <<
-                    _cacheSize << " current hits " << avgHits << ", total hit rate " <<
-                    (_cacheHits * 100. / _cacheTests) << "% at balance start");
+            LOG_DBG("PNG cache has " << _cache.size() << " items, total size " <<
+                    _cacheSize << ", current hits " << avgHits << ", total hit rate " <<
+                    (_cacheHits * 100. / _cacheTests) << "% at balance start.");
             avgHits /= _cache.size();
 
             for (auto it = _cache.begin(); it != _cache.end();)
@@ -502,8 +501,8 @@ private:
                 }
             }
 
-            LOG_DBG("cache " << _cache.size() << " items total size " <<
-                    _cacheSize << " after balance");
+            LOG_DBG("PNG cache has " << _cache.size() << " items, total size " <<
+                    _cacheSize << " after balance.");
         }
     }
 
commit 6b82d245faa3aa727c4e8b37be13e0746ca95f6c
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Wed Apr 17 22:46:16 2019 +0100
Commit:     Ashod Nakashian <ashnakash at gmail.com>
CommitDate: Tue Apr 23 03:00:07 2019 +0200

    Unipoll: integrate with the LOK mainloop in a single thread.
    
    Unfortunately processing multiple events from the Kit socket
    is causing massive document invalidations, for unknown
    reasons. As such, for now we have to process one event
    at a time, until the source of the invalidations is found
    and fixed.
    
    Without the invalidation, the average tile rendering
    roundtrip is about 3x faster than with the invalidations
    and the maximum roundrip is at least 2x faster.
    
    Change-Id: Iafbf9ccc2b80656cb71c208b598080f72d201ca2
    Reviewed-on: https://gerrit.libreoffice.org/70906
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/bundled/include/LibreOfficeKit/LibreOfficeKit.h
index 0e596f506..f2c8dd40f 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.h
@@ -104,6 +104,12 @@ struct _LibreOfficeKitClass
                            const int nCertificateBinarySize,
                            const unsigned char* pPrivateKeyBinary,
                            const int nPrivateKeyBinarySize);
+
+    /// @see lok::Office::runLoop()
+    void (*runLoop) (LibreOfficeKit* pThis,
+                     LibreOfficeKitPollCallback pPollCallback,
+                     LibreOfficeKitWakeCallback pWakeCallback,
+                     void* pData);
 };
 
 #define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize)
diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
index bd678b0b8..6bebf3661 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -644,7 +644,6 @@ public:
     {
         return mpDoc->pClass->postWindowGestureEvent(mpDoc, nWindowId, pType, nX, nY, nOffset);
     }
-
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
@@ -819,6 +818,24 @@ public:
                                             pCertificateBinary, nCertificateBinarySize,
                                             pPrivateKeyBinary, nPrivateKeyBinarySize);
     }
+
+    /**
+     * Runs the main-loop in the current thread. To trigger this
+     * mode you need to putenv a SAL_LOK_OPTIONS containing 'unipoll'.
+     * The @pPollCallback is called to poll for events from the Kit client
+     * and the @pWakeCallback can be called by internal LibreOfficeKit threads
+     * to wake the caller of 'runLoop' ie. the main thread.
+     *
+     * it is expected that runLoop does not return until Kit exit.
+     *
+     * @pData is a context/closure passed to both methods.
+     */
+    void runLoop(LibreOfficeKitPollCallback pPollCallback,
+                 LibreOfficeKitWakeCallback pWakeCallback,
+                 void* pData)
+    {
+        mpThis->pClass->runLoop(mpThis, pPollCallback, pWakeCallback, pData);
+    }
 };
 
 /// Factory method to create a lok::Office instance.
diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKitTypes.h b/bundled/include/LibreOfficeKit/LibreOfficeKitTypes.h
index 2e9078ff4..e12ddad19 100644
--- a/bundled/include/LibreOfficeKit/LibreOfficeKitTypes.h
+++ b/bundled/include/LibreOfficeKit/LibreOfficeKitTypes.h
@@ -22,6 +22,12 @@ extern "C"
  */
 typedef void (*LibreOfficeKitCallback)(int nType, const char* pPayload, void* pData);
 
+/** @see lok::Office::runLoop().
+    @since LibreOffice 6.3
+ */
+typedef int (*LibreOfficeKitPollCallback)(void* pData, int timeoutUs);
+typedef void (*LibreOfficeKitWakeCallback)(void* pData);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/kit/ForKit.cpp b/kit/ForKit.cpp
index fe5fd225a..bce34780f 100644
--- a/kit/ForKit.cpp
+++ b/kit/ForKit.cpp
@@ -528,9 +528,11 @@ int main(int argc, char** argv)
         return Application::EXIT_SOFTWARE;
     }
 
-    // Enable built in profiling dumps
+    // Set various options we need.
+    std::string options = "unipoll";
     if (Log::logger().trace())
-        ::setenv("SAL_PROFILEZONE_EVENTS", "1", 0);
+        options += ":profile_events";
+    ::setenv("SAL_LOK_OPTIONS", options.c_str(), 0);
 
     // Initialize LoKit
     if (!globalPreinit(loTemplate))
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 991ac51c8..65d386b72 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -2152,6 +2152,72 @@ void documentViewCallback(const int type, const char* payload, void* data)
     Document::ViewCallback(type, payload, data);
 }
 
+/// Called by LOK main-loop
+int pollCallback(void* pData, int timeoutUs)
+{
+    if (!pData)
+        return 0;
+
+    // The maximum number of extra events to process beyond the first.
+    //FIXME: When processing more than one event, full-document
+    //FIXME: invalidations happen (for some reason), so disable for now.
+    int maxExtraEvents = 0;
+    int eventsSignalled = 0;
+
+    int timeoutMs = timeoutUs / 1000;
+
+    SocketPoll* pSocketPoll = reinterpret_cast<SocketPoll*>(pData);
+    if (timeoutMs < 0)
+    {
+        // Flush at most 1 + maxExtraEvents, or return when nothing left.
+        while (pSocketPoll->poll(0) > 0 && maxExtraEvents-- > 0)
+            ++eventsSignalled;
+    }
+    else
+    {
+        const auto startTime = std::chrono::steady_clock::now();
+        do
+        {
+            // Flush at most maxEvents+1, or return when nothing left.
+            if (pSocketPoll->poll(timeoutMs) <= 0)
+                break;
+
+            const auto now = std::chrono::steady_clock::now();
+            const auto elapsedTimeMs
+                = std::chrono::duration_cast<std::chrono::milliseconds>(now - startTime)
+                .count();
+            if (elapsedTimeMs >= timeoutMs)
+                break;
+
+            timeoutMs -= elapsedTimeMs;
+            ++eventsSignalled;
+        }
+        while (maxExtraEvents-- > 0);
+    }
+
+#if !MOBILEAPP
+    if (document && document->purgeSessions() == 0)
+    {
+        LOG_INF("Last session discarded. Setting TerminationFlag");
+        TerminationFlag = true;
+        return -1;
+    }
+#endif
+
+    // Report the number of events we processsed.
+    return eventsSignalled;
+}
+
+/// Called by LOK main-loop
+void wakeCallback(void* pData)
+{
+    if (pData)
+    {
+        SocketPoll* pSocketPoll = reinterpret_cast<SocketPoll*>(pData);
+        pSocketPoll->wakeup();
+    }
+}
+
 #ifndef BUILDING_TESTS
 
 void lokit_main(
@@ -2344,13 +2410,14 @@ void lokit_main(
         std::string tmpSubdir = Util::createRandomTmpDir();
         ::setenv("TMPDIR", tmpSubdir.c_str(), 1);
 
+        LibreOfficeKit *kit;
         {
             const char *instdir = instdir_path.c_str();
             const char *userdir = userdir_url.c_str();
 #ifndef KIT_IN_PROCESS
-            LibreOfficeKit* kit = UnitKit::get().lok_init(instdir, userdir);
+            kit = UnitKit::get().lok_init(instdir, userdir);
 #else
-            LibreOfficeKit* kit = nullptr;
+            kit = nullptr;
 #ifdef FUZZER
             if (LOOLWSD::DummyLOK)
                 kit = dummy_lok_init_2(instdir, userdir);
@@ -2465,25 +2532,28 @@ void lokit_main(
         }
 #endif
 
-        while (!TerminationFlag)
+        if (!LIBREOFFICEKIT_HAS(kit, runLoop))
         {
-            mainKit.poll(SocketPoll::DefaultPollTimeoutMs);
-
-#if !MOBILEAPP
-            if (document && document->purgeSessions() == 0)
-            {
-                LOG_INF("Last session discarded. Setting TerminationFlag");
-                TerminationFlag = true;
-            }
-#endif
+            LOG_ERR("Kit is missing Unipoll API");
+            std::cout << "Fatal: out of date LibreOfficeKit - no Unipoll API\n";
+            std::_Exit(Application::EXIT_SOFTWARE);
         }
 
+        LOG_INF("Kit unipoll loop run");
+
+        loKit->runLoop(pollCallback, wakeCallback, &mainKit);
+
         LOG_INF("Kit poll terminated.");
 
 #if MOBILEAPP
         SocketPoll::wakeupWorld();
 #endif
 
+        // Trap the signal handler, if invoked,
+        // to prevent exiting.
+        LOG_INF("Process finished.");
+        Log::shutdown();
+
         // Let forkit handle the jail cleanup.
     }
     catch (const Exception& exc)
diff --git a/net/Socket.cpp b/net/Socket.cpp
index fb7e701ee..0f2a68346 100644
--- a/net/Socket.cpp
+++ b/net/Socket.cpp
@@ -376,7 +376,7 @@ void ServerSocket::dumpState(std::ostream& os)
 void SocketDisposition::execute()
 {
     // We should have hard ownership of this socket.
-    assert(_socket->getThreadOwner() == std::this_thread::get_id());
+    // assert(_socket->getThreadOwner() == std::this_thread::get_id());
     if (_socketMove)
     {
         // Drop pretentions of ownership before _socketMove.
diff --git a/net/Socket.hpp b/net/Socket.hpp
index c1c17b2be..ded409804 100644
--- a/net/Socket.hpp
+++ b/net/Socket.hpp
@@ -301,7 +301,7 @@ public:
                     Log::to_string(_owner) << " but called from " <<
                     std::this_thread::get_id() << " (" << Util::getThreadId() << ").");
 
-        assert(sameThread);
+        // assert(sameThread);
     }
 
 protected:
@@ -470,11 +470,13 @@ public:
                     Log::to_string(_owner) << " (" << Util::getThreadId() <<
                     ") but called from " << std::this_thread::get_id() << ", stop: " << _stop);
 
-        assert(_stop || sameThread);
+        // assert(_stop || sameThread);
     }
 
     /// Poll the sockets for available data to read or buffer to write.
-    void poll(int timeoutMaxMs)
+    /// Returns the return-value of poll(2): 0 on timeout,
+    /// -1 for error, and otherwise the number of events signalled.
+    int poll(int timeoutMaxMs)
     {
         assertCorrectThread();
 
@@ -554,7 +556,7 @@ public:
 
         // This should only happen when we're stopping.
         if (_pollSockets.size() != size)
-            return;
+            return rc;
 
         // Fire the poll callbacks and remove dead fds.
         std::chrono::steady_clock::time_point newNow =
@@ -584,6 +586,8 @@ public:
 
             disposition.execute();
         }
+
+        return rc;
     }
 
     /// Write to a wakeup descriptor
commit 0ae32391d0797b8d02fead8f5bd422eeee47002a
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Mon Apr 22 16:08:49 2019 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Mon Apr 22 17:33:25 2019 +0300

    There is no l10n directory with localisation JSON files in the iOS app
    
    The localisation is handled differently on iOS, see the
    L10N_IOS_ALL_JS stuff in loleaflet/Makefile.am and the script
    loleaflet/util/create-l10n-all-js.pl.
    
    That part of loleaflet.html is not needed in the iOS app. Whether it
    will be needed in the Android app remains to be seen. Possibly we will
    want to handle localisation in that the same way as in the iOS app. Or
    not. So for now, do this change only for iOS.

diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4
index 256fc4db7..4acb7f875 100644
--- a/loleaflet/html/loleaflet.html.m4
+++ b/loleaflet/html/loleaflet.html.m4
@@ -89,11 +89,14 @@ ifelse(MOBILEAPP,[true],
 ifelse(IOSAPP,[true],
   [<link rel="stylesheet" href="Branding/branding.css">])
 ifelse(MOBILEAPP,[true],
-  [<link rel="localizations" href="l10n/uno-localizations-override.json" type="application/vnd.oftn.l10n+json"/>
-   <link rel="localizations" href="l10n/localizations.json" type="application/vnd.oftn.l10n+json"/>
-   <link rel="localizations" href="l10n/locore-localizations.json" type="application/vnd.oftn.l10n+json"/>
-   <link rel="localizations" href="l10n/help-localizations.json" type="application/vnd.oftn.l10n+json"/>
-   <link rel="localizations" href="l10n/uno-localizations.json" type="application/vnd.oftn.l10n+json"/>],
+  [
+   ifelse(IOSAPP,[true],
+     [],
+     [<link rel="localizations" href="l10n/uno-localizations-override.json" type="application/vnd.oftn.l10n+json"/>
+      <link rel="localizations" href="l10n/localizations.json" type="application/vnd.oftn.l10n+json"/>
+      <link rel="localizations" href="l10n/locore-localizations.json" type="application/vnd.oftn.l10n+json"/>
+      <link rel="localizations" href="l10n/help-localizations.json" type="application/vnd.oftn.l10n+json"/>
+      <link rel="localizations" href="l10n/uno-localizations.json" type="application/vnd.oftn.l10n+json"/>])],
   [<link rel="localizations" href="%SERVICE_ROOT%/loleaflet/%VERSION%/l10n/uno-localizations-override.json" type="application/vnd.oftn.l10n+json"/>
    <link rel="localizations" href="%SERVICE_ROOT%/loleaflet/%VERSION%/l10n/localizations.json" type="application/vnd.oftn.l10n+json"/>
    <link rel="localizations" href="%SERVICE_ROOT%/loleaflet/%VERSION%/l10n/locore-localizations.json" type="application/vnd.oftn.l10n+json"/>
commit 60581962af6daecc5d18c2c26f372ad28c0f8e94
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Mon Apr 22 15:22:04 2019 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Mon Apr 22 17:23:20 2019 +0300

    Add branding to the iOS app
    
    A configure argument, --with-iosapp-branding, should point to a
    directory containing a branding.css file and possibly other files that
    branding.css references, to be bundled and used by the iOS app. The
    directory structure ends upp in the app bundle as Branding. The
    generated loleaflet.html for the iOS app references
    Branding/branding.css unconditionally.

diff --git a/configure.ac b/configure.ac
index 988c36a62..9fd6bfaee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -82,6 +82,12 @@ AC_ARG_WITH([iosapp-fonts],
                              [Point to a directory containing .ttf or .otf files to be bundled in the iOS app (and
                               thus installed on the device for use of the LO core code).]))
 
+AC_ARG_WITH([iosapp-branding],
+              AS_HELP_STRING([--with-iosapp-branding=<path>],
+                             [Point to a directory containing a branding.css file and possibly other files it references,
+                              to be bundled and used by the iOS app. The directory structure is copied to
+                              "loleaflet/dist/branding" and that directory ends upp in the app bundle as "branding".]))
+
 AC_ARG_ENABLE([gtkapp],
               AS_HELP_STRING([--enable-gtkapp],
                              [Use in a tree where the only purpose is to build the gtk+ "app" that is supposed
@@ -731,6 +737,18 @@ AS_IF([test "$ENABLE_IOSAPP" = "true"],
                                echo '<string>Fonts/'$(basename $fname)'</string>'
                             done`
           fi
+          rm -rf ios/Mobile/Branding
+          mkdir ios/Mobile/Branding
+          if test "$with_iosapp_branding" != no -a -d "$with_iosapp_branding"; then
+              AC_MSG_NOTICE([copying branding files])
+              mkdir -p loleaflet/dist/branding
+              (cd "$with_iosapp_branding" && tar cf - .) | (cd ios/Mobile/Branding && tar xf -)
+          else
+              # A Branding/branding.css file must exist, it is
+              # referenced unconditionally in loleaflet.html in the
+              # iOS app.
+              touch ios/Mobile/Branding/branding.css
+          fi
        fi
       ])
 AC_SUBST(IOSAPP_FONTS)
diff --git a/ios/Mobile.xcodeproj/project.pbxproj b/ios/Mobile.xcodeproj/project.pbxproj
index 119e90303..c4ec1ae61 100644
--- a/ios/Mobile.xcodeproj/project.pbxproj
+++ b/ios/Mobile.xcodeproj/project.pbxproj
@@ -15,6 +15,7 @@
 		BE00F8A821396585001CE2D4 /* images in Resources */ = {isa = PBXBuildFile; fileRef = BE00F89E21396585001CE2D4 /* images */; };
 		BE00F8B5213ED543001CE2D4 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BE00F8B4213ED543001CE2D4 /* libiconv.tbd */; };
 		BE00F8B7213ED573001CE2D4 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BE00F8B6213ED573001CE2D4 /* libz.tbd */; };
+		BE18C7DE226DE09A001AD27E /* Branding in Resources */ = {isa = PBXBuildFile; fileRef = BE18C7DD226DE09A001AD27E /* Branding */; };
 		BE5EB5C1213FE29900E0826C /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5B9213FE29900E0826C /* Log.cpp */; };
 		BE5EB5C2213FE29900E0826C /* SpookyV2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5BA213FE29900E0826C /* SpookyV2.cpp */; };
 		BE5EB5C3213FE29900E0826C /* Session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE5EB5BB213FE29900E0826C /* Session.cpp */; };
@@ -75,6 +76,7 @@
 		BE00F89E21396585001CE2D4 /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; name = images; path = ../../../loleaflet/dist/images; sourceTree = "<group>"; };
 		BE00F8B4213ED543001CE2D4 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
 		BE00F8B6213ED573001CE2D4 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
+		BE18C7DD226DE09A001AD27E /* Branding */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Branding; path = Mobile/Branding; sourceTree = SOURCE_ROOT; };
 		BE34D10F218B66B600815297 /* docsh.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = docsh.cxx; path = "../../ios-device/sw/source/uibase/app/docsh.cxx"; sourceTree = "<group>"; };
 		BE34D110218B66B600815297 /* docstyle.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = docstyle.cxx; path = "../../ios-device/sw/source/uibase/app/docstyle.cxx"; sourceTree = "<group>"; };
 		BE34D111218B66B600815297 /* docshdrw.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = docshdrw.cxx; path = "../../ios-device/sw/source/uibase/app/docshdrw.cxx"; sourceTree = "<group>"; };
@@ -670,6 +672,7 @@
 		BE00F8922139494E001CE2D4 /* Resources */ = {
 			isa = PBXGroup;
 			children = (
+				BE18C7DD226DE09A001AD27E /* Branding */,
 				BEB0E5D821C7CA800085A0CF /* Settings.bundle */,
 				BE80E46721B7066C00859C97 /* Templates */,
 				BE80E43121AD92F600859C97 /* Fonts */,
@@ -1704,6 +1707,7 @@
 				BE8D773D2136762600AC58EA /* LaunchScreen.storyboard in Resources */,
 				BE00F8A121396585001CE2D4 /* bundle.css in Resources */,
 				BE8D85CF214055F3009F1860 /* udkapi.rdb in Resources */,
+				BE18C7DE226DE09A001AD27E /* Branding in Resources */,
 				BE8D85D5214055F3009F1860 /* unorc in Resources */,
 				BE8D85D1214055F3009F1860 /* services.rdb in Resources */,
 				BE00F8A021396585001CE2D4 /* loleaflet.html in Resources */,
diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4
index 7ef44f036..256fc4db7 100644
--- a/loleaflet/html/loleaflet.html.m4
+++ b/loleaflet/html/loleaflet.html.m4
@@ -86,6 +86,8 @@ ifelse(MOBILEAPP,[true],
   ])]dnl
 )dnl
 <!--%BRANDING_CSS%--> <!-- add your logo here -->
+ifelse(IOSAPP,[true],
+  [<link rel="stylesheet" href="Branding/branding.css">])
 ifelse(MOBILEAPP,[true],
   [<link rel="localizations" href="l10n/uno-localizations-override.json" type="application/vnd.oftn.l10n+json"/>
    <link rel="localizations" href="l10n/localizations.json" type="application/vnd.oftn.l10n+json"/>
commit 9ada3e35a138e871ac261082b30b6a47357a2b5d
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Sat Apr 20 23:25:01 2019 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Sat Apr 20 23:25:01 2019 +0300

    Typo fix
    
    Change-Id: I6e4692a19d1e03c16c40eada5c4eca99b229601a

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 3282b9d2e..a58fd6008 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -661,7 +661,7 @@ L.Control.LokDialog = L.Control.extend({
 			// tdf#124235: At least in the iOS app, multiplying with
 			// L.getDpiScaleFactor() below causes the child of a combo box to be
 			// displaced from the fixed part. I see the same problem also when using
-			// Safari on a Retuna Mac against normal online. But as I don't know whether
+			// Safari on a Retina Mac against normal online. But as I don't know whether
 			// it happens also for other browsers on other platforms on hidpi displays,
 			// I will fix this for the iOS app only for now.
 			if (!window.ThisIsTheiOSApp) {


More information about the Libreoffice-commits mailing list