[Libreoffice-commits] online.git: 9 commits - common/Session.cpp common/SigUtil.cpp common/Util.cpp kit/ChildSession.cpp kit/ChildSession.hpp loleaflet/README loleaflet/reference.html loleaflet/spec loleaflet/src loolwsd.xml.in tools/Stress.cpp wsd/Admin.cpp wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp wsd/LOOLWSD.hpp wsd/protocol.txt wsd/Storage.hpp wsd/TraceFile.hpp

Ashod Nakashian ashod.nakashian at collabora.co.uk
Mon Feb 6 03:51:37 UTC 2017


 common/Session.cpp                             |    4 
 common/SigUtil.cpp                             |    1 
 common/Util.cpp                                |    2 
 kit/ChildSession.cpp                           |   13 --
 kit/ChildSession.hpp                           |    1 
 loleaflet/README                               |    3 
 loleaflet/reference.html                       |   25 ----
 loleaflet/spec/loleaflet/control/PartsSpec.js  |   11 -
 loleaflet/spec/loleaflet/control/SearchSpec.js |   20 +--
 loleaflet/src/control/Parts.js                 |   15 --
 loleaflet/src/core/Socket.js                   |    5 
 loleaflet/src/layer/tile/TileLayer.js          |   50 --------
 loolwsd.xml.in                                 |    4 
 tools/Stress.cpp                               |    8 +
 wsd/Admin.cpp                                  |    4 
 wsd/ClientSession.cpp                          |   24 ----
 wsd/ClientSession.hpp                          |    3 
 wsd/DocumentBroker.cpp                         |   24 ++--
 wsd/DocumentBroker.hpp                         |   50 +++++---
 wsd/LOOLWSD.cpp                                |   62 +++++++---
 wsd/LOOLWSD.hpp                                |    6 +
 wsd/Storage.hpp                                |    2 
 wsd/TraceFile.hpp                              |  150 ++++++++++++++++++++++++-
 wsd/protocol.txt                               |    8 -
 24 files changed, 277 insertions(+), 218 deletions(-)

New commits:
commit 11c08d766351bd80de4a05c5916c740c154b8d10
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Feb 5 22:08:23 2017 -0500

    wsd: remove logging of detailed memory stats
    
    Change-Id: Id804cc342d786e745d7ac214ea72b198bf0b639b

diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index 41af5b9..73b75a8 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -380,8 +380,6 @@ unsigned Admin::getTotalMemoryUsage()
     const size_t kitsPssKb = _model.getKitsMemoryUsage();
     const size_t totalMem = wsdPssKb + forkitRssKb + kitsPssKb;
 
-    LOG_TRC("Total mem: " << totalMem << ", wsd pss: " << wsdPssKb <<
-            ", forkit rss: " << forkitRssKb << ", kits pss: " << kitsPssKb);
     return totalMem;
 }
 
commit 7d58df49fabe287f1a3b5baa4df069008893a6f6
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Feb 5 21:59:08 2017 -0500

    wsd: new config to enable/disable snapshots when recording trace
    
    By default snapshots are disabled, since trace recording
    is enabled, to avoid unexpectedly flooding the disk.
    
    Change-Id: I6c8728e14801f0a72accde1378455ec0e6046e3e

diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index 55e9ae9..66fb786 100644
--- a/loolwsd.xml.in
+++ b/loolwsd.xml.in
@@ -35,8 +35,8 @@
 
     <loleaflet_logging desc="Logging in the browser console" default="@LOLEAFLET_LOGGING@">@LOLEAFLET_LOGGING@</loleaflet_logging>
 
-    <trace desc="Dump commands and notifications for replay" enable="true">
-        <path desc="Output path to hold trace file and docs. Use '%' for timestamp to avoid overwriting." compress="true">/tmp/looltrace-%.gz</path>
+    <trace desc="Dump commands and notifications for replay. When 'snapshot' is true, the source file is copied to the path first." enable="true">
+        <path desc="Output path to hold trace file and docs. Use '%' for timestamp to avoid overwriting." compress="true" snapshot="false">/tmp/looltrace-%.gz</path>
         <filter>
             <message desc="Regex pattern of messages to exclude"></message>
         </filter>
diff --git a/tools/Stress.cpp b/tools/Stress.cpp
index bb6a6ac..a7e90d1 100644
--- a/tools/Stress.cpp
+++ b/tools/Stress.cpp
@@ -305,7 +305,9 @@ private:
 
                 if (rec.Payload.find(NewSession) == 0)
                 {
-                    const auto& uri = rec.Payload.substr(NewSession.size());
+                    const auto uriOrig = rec.Payload.substr(NewSession.size());
+                    std::string uri;
+                    Poco::URI::decode(uriOrig, uri);
                     auto it = _sessions.find(uri);
                     if (it != _sessions.end())
                     {
@@ -328,7 +330,9 @@ private:
                 }
                 else if (rec.Payload.find(EndSession) == 0)
                 {
-                    const auto& uri = rec.Payload.substr(EndSession.size());
+                    const auto uriOrig = rec.Payload.substr(EndSession.size());
+                    std::string uri;
+                    Poco::URI::decode(uriOrig, uri);
                     auto it = _sessions.find(uri);
                     if (it != _sessions.end())
                     {
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 41aead4..59453a8 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -145,10 +145,12 @@ std::string DocumentBroker::getDocKey(const Poco::URI& uri)
     return docKey;
 }
 
-DocumentBroker::DocumentBroker(const Poco::URI& uriPublic,
+DocumentBroker::DocumentBroker(const std::string& uri,
+                               const Poco::URI& uriPublic,
                                const std::string& docKey,
                                const std::string& childRoot,
                                const std::shared_ptr<ChildProcess>& childProcess) :
+    _uriOrig(uri),
     _uriPublic(uriPublic),
     _docKey(docKey),
     _childRoot(childRoot),
@@ -363,7 +365,7 @@ bool DocumentBroker::load(std::shared_ptr<ClientSession>& session, const std::st
         _lastFileModifiedTime = Poco::File(_storage->getLocalRootPath()).getLastModified();
         _tileCache.reset(new TileCache(uriPublic.toString(), _lastFileModifiedTime, _cacheRoot));
 
-        LOOLWSD::dumpNewSessionTrace(getJailId(), sessionId, uriPublic.getPath(), _storage->getRootFilePath());
+        LOOLWSD::dumpNewSessionTrace(getJailId(), sessionId, _uriOrig, _storage->getRootFilePath());
     }
 
     // Since document has been loaded, send the stats if its WOPI
@@ -677,8 +679,7 @@ size_t DocumentBroker::removeSession(const std::string& id)
         auto it = _sessions.find(id);
         if (it != _sessions.end())
         {
-            const Poco::URI& uriPublic = it->second->getPublicUri();
-            LOOLWSD::dumpEndSessionTrace(getJailId(), id, uriPublic.getPath());
+            LOOLWSD::dumpEndSessionTrace(getJailId(), id, _uriOrig);
 
             const auto readonly = (it->second ? it->second->isReadOnly() : false);
             _sessions.erase(it);
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 76c7562..a401b5a 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -207,7 +207,8 @@ public:
     /// Dummy document broker that is marked to destroy.
     DocumentBroker();
 
-    DocumentBroker(const Poco::URI& uriPublic,
+    DocumentBroker(const std::string& uri,
+                   const Poco::URI& uriPublic,
                    const std::string& docKey,
                    const std::string& childRoot,
                    const std::shared_ptr<ChildProcess>& childProcess);
@@ -331,6 +332,7 @@ private:
     bool forwardToClient(const std::shared_ptr<Message>& payload);
 
 private:
+    const std::string _uriOrig;
     const Poco::URI _uriPublic;
     const std::string _docKey;
     const std::string _childRoot;
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index a8e4b0a..9e30ee1 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -619,7 +619,7 @@ private:
                     }
 
                     LOG_DBG("New DocumentBroker for docKey [" << docKey << "].");
-                    auto docBroker = std::make_shared<DocumentBroker>(uriPublic, docKey, LOOLWSD::ChildRoot, child);
+                    auto docBroker = std::make_shared<DocumentBroker>(fromPath, uriPublic, docKey, LOOLWSD::ChildRoot, child);
                     child->setDocumentBroker(docBroker);
 
                     cleanupDocBrokers();
@@ -860,7 +860,7 @@ private:
             int retry = 3;
             while (retry-- > 0)
             {
-                auto docBroker = findOrCreateDocBroker(docKey, ws, id, uriPublic);
+                auto docBroker = findOrCreateDocBroker(uri, docKey, ws, id, uriPublic);
                 if (docBroker)
                 {
                     auto session = createNewClientSession(ws, id, uriPublic, docBroker, isReadOnly);
@@ -909,7 +909,8 @@ private:
     /// Otherwise, creates and adds a new one to DocBrokers.
     /// May return null if terminating or MaxDocuments limit is reached.
     /// After returning a valid instance DocBrokers must be cleaned up after exceptions.
-    static std::shared_ptr<DocumentBroker> findOrCreateDocBroker(const std::string& docKey,
+    static std::shared_ptr<DocumentBroker> findOrCreateDocBroker(const std::string& uri,
+                                                                 const std::string& docKey,
                                                                  std::shared_ptr<LOOLWebSocket>& ws,
                                                                  const std::string& id,
                                                                  const Poco::URI& uriPublic)
@@ -1014,13 +1015,14 @@ private:
 
         if (!docBroker)
         {
-            docBroker = createNewDocBroker(docKey, ws, uriPublic);
+            docBroker = createNewDocBroker(uri, docKey, ws, uriPublic);
         }
 
         return docBroker;
     }
 
-    static std::shared_ptr<DocumentBroker> createNewDocBroker(const std::string& docKey,
+    static std::shared_ptr<DocumentBroker> createNewDocBroker(const std::string& uri,
+                                                              const std::string& docKey,
                                                               std::shared_ptr<LOOLWebSocket>& ws,
                                                               const Poco::URI& uriPublic)
     {
@@ -1045,7 +1047,7 @@ private:
 
         // Set the one we just created.
         LOG_DBG("New DocumentBroker for docKey [" << docKey << "].");
-        auto docBroker = std::make_shared<DocumentBroker>(uriPublic, docKey, LOOLWSD::ChildRoot, child);
+        auto docBroker = std::make_shared<DocumentBroker>(uri, uriPublic, docKey, LOOLWSD::ChildRoot, child);
         child->setDocumentBroker(docBroker);
         DocBrokers.emplace(docKey, docBroker);
         LOG_TRC("Have " << DocBrokers.size() << " DocBrokers after inserting [" << docKey << "].");
@@ -1903,7 +1905,8 @@ void LOOLWSD::initialize(Application& self)
         }
 
         const auto compress = getConfigValue<bool>(conf, "trace.path[@compress]", false);
-        TraceDumper.reset(new TraceFileWriter(path, recordOutgoing, compress, filters));
+        const auto takeSnapshot = getConfigValue<bool>(conf, "trace.path[@snapshot]", false);
+        TraceDumper.reset(new TraceFileWriter(path, recordOutgoing, compress, takeSnapshot, filters));
         LOG_INF("Command trace dumping enabled to file: " << path);
     }
 
diff --git a/wsd/TraceFile.hpp b/wsd/TraceFile.hpp
index 4ae57f5..347e5f6 100644
--- a/wsd/TraceFile.hpp
+++ b/wsd/TraceFile.hpp
@@ -53,11 +53,15 @@ public:
 class TraceFileWriter
 {
 public:
-    TraceFileWriter(const std::string& path, const bool recordOugoing, const bool compress,
+    TraceFileWriter(const std::string& path,
+                    const bool recordOugoing,
+                    const bool compress,
+                    const bool takeSnapshot,
                     const std::vector<std::string>& filters) :
         _epochStart(Poco::Timestamp().epochMicroseconds()),
         _recordOutgoing(recordOugoing),
         _compress(compress),
+        _takeSnapshot(takeSnapshot),
         _path(Poco::Path(path).parent().toString()),
         _filter(true),
         _stream(processPath(path), compress ? std::ios::binary : std::ios::out),
@@ -81,31 +85,33 @@ public:
     {
         std::unique_lock<std::mutex> lock(_mutex);
 
-        std::string snapshot;
+        std::string snapshot = uri;
 
-        const auto url = Poco::URI(uri).getPath();
-
-        const auto it = _urlToSnapshot.find(url);
-        if (it != _urlToSnapshot.end())
+        if (_takeSnapshot)
         {
-            snapshot = it->second.Snapshot;
-            it->second.SessionCount++;
-        }
-        else
-        {
-            // Create a snapshot file.
-            const Poco::Path origPath(localPath);
-            std::string filename = origPath.getBaseName();
-            filename += '_' + Poco::DateTimeFormatter::format(Poco::DateTime(), "%Y%m%d_%H~%M~%S");
-            filename += '.' + origPath.getExtension();
-            snapshot = Poco::Path(_path, filename).toString();
-
-            LOG_TRC("TraceFile: Copying local file [" << localPath << "] to snapshot [" << snapshot << "].");
-            Poco::File(localPath).copyTo(snapshot);
-            snapshot = Poco::URI(Poco::URI("file://"), snapshot).toString();
-
-            LOG_TRC("TraceFile: Mapped URL " << url << " to " << snapshot);
-            _urlToSnapshot.emplace(url, SnapshotData(snapshot));
+            const auto url = Poco::URI(uri).getPath();
+            const auto it = _urlToSnapshot.find(url);
+            if (it != _urlToSnapshot.end())
+            {
+                snapshot = it->second.Snapshot;
+                it->second.SessionCount++;
+            }
+            else
+            {
+                // Create a snapshot file.
+                const Poco::Path origPath(localPath);
+                std::string filename = origPath.getBaseName();
+                filename += '_' + Poco::DateTimeFormatter::format(Poco::DateTime(), "%Y%m%d_%H~%M~%S");
+                filename += '.' + origPath.getExtension();
+                snapshot = Poco::Path(_path, filename).toString();
+
+                LOG_TRC("TraceFile: Copying local file [" << localPath << "] to snapshot [" << snapshot << "].");
+                Poco::File(localPath).copyTo(snapshot);
+                snapshot = Poco::URI(Poco::URI("file://"), snapshot).toString();
+
+                LOG_TRC("TraceFile: Mapped URL " << url << " to " << snapshot);
+                _urlToSnapshot.emplace(url, SnapshotData(snapshot));
+            }
         }
 
         const auto data = "NewSession: " + snapshot;
@@ -117,10 +123,9 @@ public:
     {
         std::unique_lock<std::mutex> lock(_mutex);
 
-        std::string snapshot;
+        std::string snapshot = uri;
 
         const auto url = Poco::URI(uri).getPath();
-
         const auto it = _urlToSnapshot.find(url);
         if (it != _urlToSnapshot.end())
         {
@@ -134,11 +139,11 @@ public:
             {
                 it->second.SessionCount--;
             }
-
-            const auto data = "EndSession: " + snapshot;
-            writeLocked(id, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Event));
-            flushLocked();
         }
+
+        const auto data = "EndSession: " + snapshot;
+        writeLocked(id, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Event));
+        flushLocked();
     }
 
     void writeEvent(const std::string& id, const std::string& sessionId, const std::string& data)
@@ -282,6 +287,7 @@ private:
     const Poco::Int64 _epochStart;
     const bool _recordOutgoing;
     const bool _compress;
+    const bool _takeSnapshot;
     const std::string _path;
     Util::RegexListMatcher _filter;
     std::ofstream _stream;
commit 57212b69b80267d4230891de517ba91420facab2
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Feb 5 19:35:54 2017 -0500

    wsd: take snapshot of source file before trace recording
    
    Change-Id: Ia3e25467a20d2442dcf0497c21a8fdce2efedbc2

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index b5b9b98..41aead4 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -353,7 +353,8 @@ bool DocumentBroker::load(std::shared_ptr<ClientSession>& session, const std::st
         Poco::DigestOutputStream dos(sha1);
         Poco::StreamCopier::copyStream(istr, dos);
         dos.close();
-        LOG_INF("SHA1 of [" << localPath << "]: " << Poco::DigestEngine::digestToHex(sha1.digest()));
+        LOG_INF("SHA1 for DocKey [" << _docKey << "] of [" << localPath << "]: " <<
+                Poco::DigestEngine::digestToHex(sha1.digest()));
 
         _uriJailed = Poco::URI(Poco::URI("file://"), localPath);
         _filename = fileInfo._filename;
@@ -361,6 +362,8 @@ bool DocumentBroker::load(std::shared_ptr<ClientSession>& session, const std::st
         // Use the local temp file's timestamp.
         _lastFileModifiedTime = Poco::File(_storage->getLocalRootPath()).getLastModified();
         _tileCache.reset(new TileCache(uriPublic.toString(), _lastFileModifiedTime, _cacheRoot));
+
+        LOOLWSD::dumpNewSessionTrace(getJailId(), sessionId, uriPublic.getPath(), _storage->getRootFilePath());
     }
 
     // Since document has been loaded, send the stats if its WOPI
@@ -674,6 +677,9 @@ size_t DocumentBroker::removeSession(const std::string& id)
         auto it = _sessions.find(id);
         if (it != _sessions.end())
         {
+            const Poco::URI& uriPublic = it->second->getPublicUri();
+            LOOLWSD::dumpEndSessionTrace(getJailId(), id, uriPublic.getPath());
+
             const auto readonly = (it->second ? it->second->isReadOnly() : false);
             _sessions.erase(it);
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index b4862fa..a8e4b0a 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -863,11 +863,11 @@ private:
                 auto docBroker = findOrCreateDocBroker(docKey, ws, id, uriPublic);
                 if (docBroker)
                 {
-                    auto session = createNewClientSession(uri, ws, id, uriPublic, docBroker, isReadOnly);
+                    auto session = createNewClientSession(ws, id, uriPublic, docBroker, isReadOnly);
                     if (session)
                     {
                         // Process the request in an exception-safe way.
-                        processGetRequest(uri, ws, id, docBroker, session);
+                        processGetRequest(ws, id, docBroker, session);
                         break;
                     }
                 }
@@ -1053,8 +1053,7 @@ private:
         return docBroker;
     }
 
-    static std::shared_ptr<ClientSession> createNewClientSession(const std::string& uri,
-                                                                 std::shared_ptr<LOOLWebSocket>& ws,
+    static std::shared_ptr<ClientSession> createNewClientSession(std::shared_ptr<LOOLWebSocket>& ws,
                                                                  const std::string& id,
                                                                  const Poco::URI& uriPublic,
                                                                  const std::shared_ptr<DocumentBroker>& docBroker,
@@ -1092,7 +1091,6 @@ private:
             // (UserCanWrite param).
             auto session = std::make_shared<ClientSession>(id, ws, docBroker, uriPublic, isReadOnly);
 
-            LOOLWSD::dumpEventTrace(docBroker->getJailId(), id, "NewSession: " + uri);
             docBroker->addSession(session);
 
             lock.unlock();
@@ -1142,8 +1140,7 @@ private:
     }
 
     /// Process GET requests.
-    static void processGetRequest(const std::string& uri,
-                                  std::shared_ptr<LOOLWebSocket>& ws,
+    static void processGetRequest(std::shared_ptr<LOOLWebSocket>& ws,
                                   const std::string& id,
                                   const std::shared_ptr<DocumentBroker>& docBroker,
                                   const std::shared_ptr<ClientSession>& session)
@@ -1190,7 +1187,6 @@ private:
                 removeDocBrokerSession(docBroker);
             }
 
-            LOOLWSD::dumpEventTrace(docBroker->getJailId(), id, "EndSession: " + uri);
             LOG_INF("Finishing GET request handler for session [" << id << "].");
         }
         catch (const UnauthorizedRequestException& exc)
@@ -1970,6 +1966,33 @@ void LOOLWSD::initializeSSL()
     Poco::Net::SSLManager::instance().initializeClient(consoleClientHandler, invalidClientCertHandler, sslClientContext);
 }
 
+void LOOLWSD::dumpNewSessionTrace(const std::string& id, const std::string& sessionId, const std::string& uri, const std::string& path)
+{
+    if (TraceDumper)
+    {
+        try
+        {
+            TraceDumper->newSession(id, sessionId, uri, path);
+        }
+        catch (const std::exception& exc)
+        {
+            LOG_WRN("Exception in tracer newSession: " << exc.what());
+        }
+    }
+}
+
+void LOOLWSD::dumpEndSessionTrace(const std::string& id, const std::string& sessionId, const std::string& uri)
+{
+        try
+        {
+            TraceDumper->endSession(id, sessionId, uri);
+        }
+        catch (const std::exception& exc)
+        {
+            LOG_WRN("Exception in tracer newSession: " << exc.what());
+        }
+}
+
 void LOOLWSD::dumpEventTrace(const std::string& id, const std::string& sessionId, const std::string& data)
 {
     if (TraceDumper)
diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp
index 536b18e..ab0b479 100644
--- a/wsd/LOOLWSD.hpp
+++ b/wsd/LOOLWSD.hpp
@@ -64,6 +64,12 @@ public:
         return LOOLWSD::SSLTermination.get();
     }
 
+    /// Trace a new session and take a snapshot of the file.
+    static void dumpNewSessionTrace(const std::string& id, const std::string& sessionId, const std::string& uri, const std::string& path);
+
+    /// Trace the end of a session.
+    static void dumpEndSessionTrace(const std::string& id, const std::string& sessionId, const std::string& uri);
+
     static void dumpEventTrace(const std::string& id, const std::string& sessionId, const std::string& data);
 
     static void dumpIncomingTrace(const std::string& id, const std::string& sessionId, const std::string& data);
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 69b1ea9..4f642d6 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -78,6 +78,8 @@ public:
 
     const std::string getUri() const { return _uri.toString(); }
 
+    const std::string& getRootFilePath() const { return _jailedFilePath; };
+
     bool isLoaded() const { return _isLoaded; }
 
     /// Returns the basic information about the file.
diff --git a/wsd/TraceFile.hpp b/wsd/TraceFile.hpp
index 059cbc3..4ae57f5 100644
--- a/wsd/TraceFile.hpp
+++ b/wsd/TraceFile.hpp
@@ -18,6 +18,8 @@
 #include <Poco/DeflatingStream.h>
 #include <Poco/InflatingStream.h>
 
+#include "Protocol.hpp"
+#include "Log.hpp"
 #include "Util.hpp"
 
 /// Dumps commands and notification trace.
@@ -56,6 +58,7 @@ public:
         _epochStart(Poco::Timestamp().epochMicroseconds()),
         _recordOutgoing(recordOugoing),
         _compress(compress),
+        _path(Poco::Path(path).parent().toString()),
         _filter(true),
         _stream(processPath(path), compress ? std::ios::binary : std::ios::out),
         _deflater(_stream, Poco::DeflatingStreamBuf::STREAM_GZIP)
@@ -74,6 +77,70 @@ public:
         _stream.close();
     }
 
+    void newSession(const std::string& id, const std::string& sessionId, const std::string& uri, const std::string& localPath)
+    {
+        std::unique_lock<std::mutex> lock(_mutex);
+
+        std::string snapshot;
+
+        const auto url = Poco::URI(uri).getPath();
+
+        const auto it = _urlToSnapshot.find(url);
+        if (it != _urlToSnapshot.end())
+        {
+            snapshot = it->second.Snapshot;
+            it->second.SessionCount++;
+        }
+        else
+        {
+            // Create a snapshot file.
+            const Poco::Path origPath(localPath);
+            std::string filename = origPath.getBaseName();
+            filename += '_' + Poco::DateTimeFormatter::format(Poco::DateTime(), "%Y%m%d_%H~%M~%S");
+            filename += '.' + origPath.getExtension();
+            snapshot = Poco::Path(_path, filename).toString();
+
+            LOG_TRC("TraceFile: Copying local file [" << localPath << "] to snapshot [" << snapshot << "].");
+            Poco::File(localPath).copyTo(snapshot);
+            snapshot = Poco::URI(Poco::URI("file://"), snapshot).toString();
+
+            LOG_TRC("TraceFile: Mapped URL " << url << " to " << snapshot);
+            _urlToSnapshot.emplace(url, SnapshotData(snapshot));
+        }
+
+        const auto data = "NewSession: " + snapshot;
+        writeLocked(id, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Event));
+        flushLocked();
+    }
+
+    void endSession(const std::string& id, const std::string& sessionId, const std::string& uri)
+    {
+        std::unique_lock<std::mutex> lock(_mutex);
+
+        std::string snapshot;
+
+        const auto url = Poco::URI(uri).getPath();
+
+        const auto it = _urlToSnapshot.find(url);
+        if (it != _urlToSnapshot.end())
+        {
+            snapshot = it->second.Snapshot;
+            if (it->second.SessionCount == 1)
+            {
+                // Last session, remove the mapping.
+                _urlToSnapshot.erase(it);
+            }
+            else
+            {
+                it->second.SessionCount--;
+            }
+
+            const auto data = "EndSession: " + snapshot;
+            writeLocked(id, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Event));
+            flushLocked();
+        }
+    }
+
     void writeEvent(const std::string& id, const std::string& sessionId, const std::string& data)
     {
         std::unique_lock<std::mutex> lock(_mutex);
@@ -88,6 +155,42 @@ public:
 
         if (_filter.match(data))
         {
+            // Remap the URL to the snapshot.
+            if (LOOLProtocol::matchPrefix("load", data))
+            {
+                auto tokens = LOOLProtocol::tokenize(data);
+                if (tokens.size() >= 2)
+                {
+                    std::string url;
+                    if (LOOLProtocol::getTokenString(tokens[1], "url", url))
+                    {
+                        std::string decodedUrl;
+                        Poco::URI::decode(url, decodedUrl);
+                        auto uriPublic = Poco::URI(decodedUrl);
+                        if (uriPublic.isRelative() || uriPublic.getScheme() == "file")
+                        {
+                            uriPublic.normalize();
+                        }
+
+                        url = uriPublic.getPath();
+                        const auto it = _urlToSnapshot.find(url);
+                        if (it != _urlToSnapshot.end())
+                        {
+                            LOG_TRC("TraceFile: Mapped URL: " << url << " to " << it->second.Snapshot);
+                            tokens[1] = "url=" + it->second.Snapshot;
+                            std::string newData;
+                            for (const auto& token : tokens)
+                            {
+                                newData += token + ' ';
+                            }
+
+                            writeLocked(id, sessionId, newData, static_cast<char>(TraceFileRecord::Direction::Incoming));
+                            return;
+                        }
+                    }
+                }
+            }
+
             writeLocked(id, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Incoming));
         }
     }
@@ -151,19 +254,40 @@ private:
         }
 
         std::string res = path.substr(0, pos);
-        res += Poco::DateTimeFormatter::format(Poco::DateTime(), "%Y-%m-%d_%H:%M:%S");
+        res += Poco::DateTimeFormatter::format(Poco::DateTime(), "%Y%m%d_%H~%M~%S");
         res += path.substr(pos + 1);
         return res;
     }
 
 private:
+    struct SnapshotData
+    {
+        SnapshotData(const std::string& snapshot) :
+            Snapshot(snapshot)
+        {
+            SessionCount = 1;
+        }
+
+        SnapshotData(const SnapshotData& other) :
+            Snapshot(other.Snapshot)
+        {
+            SessionCount = other.SessionCount.load();
+        }
+
+        std::string Snapshot;
+        std::atomic<size_t> SessionCount;
+    };
+
+private:
     const Poco::Int64 _epochStart;
     const bool _recordOutgoing;
     const bool _compress;
+    const std::string _path;
     Util::RegexListMatcher _filter;
     std::ofstream _stream;
     Poco::DeflatingOutputStream _deflater;
     std::mutex _mutex;
+    std::map<std::string, SnapshotData> _urlToSnapshot;
 };
 
 /// Trace-file parser class.
commit 64335b41270c6d5c0bc30b3a9534cb91dd3726bd
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Feb 5 17:53:33 2017 -0500

    wsd: correctly initialize forkit pid in Admin
    
    Change-Id: I376742fa324f207d77fa290046657f17f8872b27

diff --git a/common/Util.cpp b/common/Util.cpp
index bb64025..1fc1adc 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -196,7 +196,7 @@ namespace Util
 
     size_t getMemoryUsageRSS(const Poco::Process::PID pid)
     {
-        if (pid == -1)
+        if (pid <= 0)
         {
             return 0;
         }
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index 9dc608b..41af5b9 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -285,7 +285,7 @@ void AdminRequestHandler::handleRequest(HTTPServerRequest& request, HTTPServerRe
 /// An admin command processor.
 Admin::Admin() :
     _model(AdminModel()),
-    _forKitPid(0)
+    _forKitPid(-1)
 {
     LOG_INF("Admin ctor.");
 
commit a7afc59e517e20183c0632ce374ad93bde576c59
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Feb 5 12:13:32 2017 -0500

    wsd: trace files support timestamp to prevent overwritting on restart
    
    Change-Id: Ided928e7428d35f9ed322720ea306e090bdd0c38

diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index c9547e3..55e9ae9 100644
--- a/loolwsd.xml.in
+++ b/loolwsd.xml.in
@@ -36,7 +36,7 @@
     <loleaflet_logging desc="Logging in the browser console" default="@LOLEAFLET_LOGGING@">@LOLEAFLET_LOGGING@</loleaflet_logging>
 
     <trace desc="Dump commands and notifications for replay" enable="true">
-        <path desc="Output file path" compress="true">/tmp/looltrace.gz</path>
+        <path desc="Output path to hold trace file and docs. Use '%' for timestamp to avoid overwriting." compress="true">/tmp/looltrace-%.gz</path>
         <filter>
             <message desc="Regex pattern of messages to exclude"></message>
         </filter>
diff --git a/wsd/TraceFile.hpp b/wsd/TraceFile.hpp
index 62616ac..059cbc3 100644
--- a/wsd/TraceFile.hpp
+++ b/wsd/TraceFile.hpp
@@ -13,6 +13,8 @@
 #include <string>
 #include <vector>
 
+#include <Poco/DateTime.h>
+#include <Poco/DateTimeFormatter.h>
 #include <Poco/DeflatingStream.h>
 #include <Poco/InflatingStream.h>
 
@@ -55,7 +57,7 @@ public:
         _recordOutgoing(recordOugoing),
         _compress(compress),
         _filter(true),
-        _stream(path, compress ? std::ios::binary : std::ios::out),
+        _stream(processPath(path), compress ? std::ios::binary : std::ios::out),
         _deflater(_stream, Poco::DeflatingStreamBuf::STREAM_GZIP)
     {
         for (const auto& f : filters)
@@ -140,6 +142,20 @@ private:
         }
     }
 
+    static std::string processPath(const std::string& path)
+    {
+        const auto pos = path.find('%');
+        if (pos == std::string::npos)
+        {
+            return path;
+        }
+
+        std::string res = path.substr(0, pos);
+        res += Poco::DateTimeFormatter::format(Poco::DateTime(), "%Y-%m-%d_%H:%M:%S");
+        res += path.substr(pos + 1);
+        return res;
+    }
+
 private:
     const Poco::Int64 _epochStart;
     const bool _recordOutgoing;
commit 0476d1d9f234e25423b01bce37edf9a96ab6d4a2
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Feb 5 00:26:55 2017 -0500

    wsd: don't complain to the user on idle termination
    
    We no longer tell the clinet "This is embarrassing..."
    when we disconnect and unload an idle document. Instead,
    the client UI remains greyed out so the user can resume
    as if it was inactive (and reload the document in this case).
    
    Also, we now always send the "close: " message prior
    to shutting down a client websocket. This is more
    reasonable and consistent when we intentionally disconnect,
    so clients can rely on it to signal intent and give reason.
    
    Otherwise, a disconnection without this application-level
    message should be unexpected and is therefore reasonable
    to show the "This is embarrassing..." message.
    
    Change-Id: Ic7439bcc9267be155586ccd5d122e9fe60225516

diff --git a/common/Session.cpp b/common/Session.cpp
index b320522..6c24684 100644
--- a/common/Session.cpp
+++ b/common/Session.cpp
@@ -195,6 +195,10 @@ void Session::shutdown(Poco::UInt16 statusCode, const std::string& statusMessage
     {
         LOG_TRC("Shutting down WS [" << getName() << "] with statusCode [" <<
                 statusCode << "] and reason [" << statusMessage << "].");
+
+        // See protocol.txt for this application-level close frame.
+        const std::string msg = "close: " + statusMessage;
+        _ws->sendFrame(msg.data(), msg.size());
         _ws->shutdown(statusCode, statusMessage);
     }
 }
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 9f90adb..8e8c20c 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -295,6 +295,10 @@ L.Socket = L.Class.extend({
 				this._map.fire('postMessage', {msgId: 'Session_Closed'});
 			}
 
+			if (textMsg === 'idle') {
+				this._map._active = false;
+			}
+
 			if (textMsg === 'ownertermination') {
 				this._map.remove();
 			}
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 928fa0e..b5b9b98 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1063,7 +1063,7 @@ void DocumentBroker::childSocketTerminated()
     {
         try
         {
-            pair.second->shutdown(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY);
+            pair.second->shutdown(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY, "");
         }
         catch (const std::exception& ex)
         {
@@ -1084,8 +1084,6 @@ void DocumentBroker::terminateChild(std::unique_lock<std::mutex>& lock, const st
     {
         try
         {
-            // See protocol.txt for this application-level close frame.
-            pair.second->sendTextFrame("close: " + closeReason);
             pair.second->shutdown(Poco::Net::WebSocket::WS_ENDPOINT_GOING_AWAY, closeReason);
         }
         catch (const std::exception& ex)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index d14f3e5..76c7562 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -305,7 +305,7 @@ public:
     /// We must be called under lock and it must be
     /// passed to us so we unlock before waiting on
     /// the ChildProcess thread, which can take our lock.
-    void terminateChild(std::unique_lock<std::mutex>& lock, const std::string& closeReason = "");
+    void terminateChild(std::unique_lock<std::mutex>& lock, const std::string& closeReason);
 
     /// Get the PID of the associated child process
     Poco::Process::PID getPid() const { return _childProcess->getPid(); }
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 82ba7e8..b4862fa 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -284,7 +284,7 @@ bool cleanupDocBrokers()
             LOG_INF("Removing " << (idle ? "idle" : "dead") <<
                     " DocumentBroker for docKey [" << it->first << "].");
             it = DocBrokers.erase(it);
-            docBroker->terminateChild(lock);
+            docBroker->terminateChild(lock, idle ? "idle" : "");
         }
         else
         {
@@ -685,7 +685,7 @@ private:
                         // At this point we're done.
                         LOG_DBG("Removing DocumentBroker for docKey [" << docKey << "].");
                         DocBrokers.erase(docKey);
-                        docBroker->terminateChild(docLock);
+                        docBroker->terminateChild(docLock, "");
                         LOG_TRC("Have " << DocBrokers.size() << " DocBrokers after removing [" << docKey << "].");
                     }
                     else
@@ -1137,7 +1137,7 @@ private:
         {
             LOG_INF("Removing unloaded DocumentBroker for docKey [" << docKey << "].");
             DocBrokers.erase(docKey);
-            docBroker->terminateChild(lock);
+            docBroker->terminateChild(lock, "");
         }
     }
 
commit b095e061731f81b15d914dcf360328826a9cdd5f
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sat Feb 4 15:46:52 2017 -0500

    wsd: fix crash when unloading idle documents
    
    A race condition between the client socket thread
    and the idle-document cleanup caused segfault
    on the websocket.
    
    Now the ChildProcess object doesn't reset
    the websocket on closing, rather on destruction.
    
    Change-Id: I10d0dfb1ba677a65479df85b7a53de8c5f1b44c3

diff --git a/common/SigUtil.cpp b/common/SigUtil.cpp
index bf7292c..a5f87fa 100644
--- a/common/SigUtil.cpp
+++ b/common/SigUtil.cpp
@@ -281,7 +281,6 @@ namespace SigUtil
             std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs));
         }
 
-        LOG_WRN("Cannot terminate PID: " << pid);
         return false;
     }
 }
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index c454612..928fa0e 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -677,14 +677,15 @@ size_t DocumentBroker::removeSession(const std::string& id)
             const auto readonly = (it->second ? it->second->isReadOnly() : false);
             _sessions.erase(it);
 
-            // Let the child know the client has disconnected.
-            const std::string msg("child-" + id + " disconnect");
-            _childProcess->sendTextFrame(msg);
-
             const auto count = _sessions.size();
             LOG_TRC("Removed " << (readonly ? "readonly" : "non-readonly") <<
                     " session [" << id << "] from docKey [" <<
                     _docKey << "] to have " << count << " sessions.");
+
+            // Let the child know the client has disconnected.
+            const std::string msg("child-" + id + " disconnect");
+            _childProcess->sendTextFrame(msg);
+
             return count;
         }
         else
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 8c416ba..d14f3e5 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -59,8 +59,14 @@ public:
 
     ~ChildProcess()
     {
-        LOG_DBG("~ChildProcess dtor [" << _pid << "].");
-        close(true);
+        if (_pid > 0)
+        {
+            LOG_DBG("~ChildProcess dtor [" << _pid << "].");
+            close(true);
+
+            // No need for the socket anymore.
+            _ws.reset();
+        }
     }
 
     void setDocumentBroker(const std::shared_ptr<DocumentBroker>& docBroker)
@@ -71,13 +77,12 @@ public:
 
     void stop()
     {
-        LOG_DBG("Stopping ChildProcess [" << _pid << "]");
         _stop = true;
-
         try
         {
             if (isAlive())
             {
+                LOG_DBG("Stopping ChildProcess [" << _pid << "]");
                 sendTextFrame("exit");
             }
         }
@@ -89,36 +94,45 @@ public:
 
     void close(const bool rude)
     {
+        if (_pid < 0)
+        {
+            return;
+        }
+
         try
         {
             LOG_DBG("Closing ChildProcess [" << _pid << "].");
+
+            // First mark to stop the thread so it knows it's intentional.
             stop();
 
+            // Shutdown the socket to break the thread if blocked on it.
             if (_ws)
             {
                 _ws->shutdown();
             }
 
+            // Now should be quick to exit the thread; wait.
             if (_thread.joinable())
             {
                 _thread.join();
             }
-
-            _ws.reset();
-            if (_pid != -1 && rude && kill(_pid, 0) != 0 && errno != ESRCH)
-            {
-                LOG_INF("Killing child [" << _pid << "].");
-                if (SigUtil::killChild(_pid))
-                {
-                    LOG_ERR("Cannot terminate lokit [" << _pid << "]. Abandoning.");
-                }
-            }
         }
         catch (const std::exception& ex)
         {
             LOG_ERR("Error while closing child process: " << ex.what());
         }
 
+        // Kill or abandon the child.
+        if (_pid != -1 && rude && kill(_pid, 0) != 0 && errno != ESRCH)
+        {
+            LOG_INF("Killing child [" << _pid << "].");
+            if (SigUtil::killChild(_pid))
+            {
+                LOG_ERR("Cannot terminate lokit [" << _pid << "]. Abandoning.");
+            }
+        }
+
         _pid = -1;
     }
 
@@ -157,7 +171,7 @@ public:
             return (_pid > 1 && _ws && kill(_pid, 0) == 0 &&
                     !_ws->poll(Poco::Timespan(0), Poco::Net::Socket::SelectMode::SELECT_ERROR));
         }
-        catch (const std::exception& exc)
+        catch (const std::exception&)
         {
         }
 
commit 5cac95e6a975a7c1de297ef37b1e2ec43732cc9e
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Feb 5 11:31:48 2017 -0500

    loleaflet: remove pagepartrectangles request and handling in loleaflet
    
    Change-Id: Ib83e7c0ca05a7d7d1a3d30675ef3cf5804f5ac4c

diff --git a/loleaflet/README b/loleaflet/README
index 1c08241..caa5e52 100644
--- a/loleaflet/README
+++ b/loleaflet/README
@@ -291,9 +291,6 @@ Writer pages:
             + e.currentPage = the page on which the cursor lies
             + e.pages = number of pages
             + e.docType = document type, should be 'text'
-        map.on('partpagerectangles', function (e) {}) where:
-            + e.pixelRectangles = An array of bounds representing each page's dimension in pixels on the current zoom level
-            + e.twipsRectangles = An array of bounds representing each page's dimension in twips.
 
 Error:
     - events
diff --git a/loleaflet/reference.html b/loleaflet/reference.html
index 839d072..57d329c 100644
--- a/loleaflet/reference.html
+++ b/loleaflet/reference.html
@@ -1923,11 +1923,6 @@ unexpected behaviour.</h4>
 		<td>Fired when the number of pages changes.</td>
 	</tr>
 	<tr>
-		<td><code><b>partpagerectangles</b></code></td>
-		<td><code><a href="#partpagerectangles-event">PartPageRectanglesEvent</a></code></td>
-		<td>Fired when the document is ready, contains information about the size of each page.</td>
-	</tr>
-	<tr>
 		<td><code><b>print</b></code></td>
 		<td><code><a href="#print-event">PrintEvent</a></code></td>
 		<td>Fired when the URL for the PDF export is ready.</td>
@@ -2186,26 +2181,6 @@ The <code>id</code> property of ErrorEvent can have the following values:
 	</tr>
 </table>
 
-<h3 id="partpagerectangles-event">PartPageRectangles</h3>
-
-<table data-id='events'>
-	<tr>
-		<th class="width100">property</th>
-		<th>type</th>
-		<th>description</th>
-	</tr>
-	<tr>
-		<td><code><b>pixelRectangles</b></code></td>
-        <td><code><a href="#bounds">Bounds[]</a></code></td>
-		<td>An array of bounds representing each page's dimension in pixels on the current zoom level.</td>
-	</tr>
-	<tr>
-		<td><code><b>twipsRectangles</b></code></td>
-		<td><code><a href="#bounds">Bounds[]</a></code></td>
-		<td>An array of bounds representing each page's dimension in twips.</td>
-	</tr>
-</table>
-
 <h3 id="print-event">PrintEvent</h3>
 
 <table data-id='events'>
diff --git a/loleaflet/spec/loleaflet/control/PartsSpec.js b/loleaflet/spec/loleaflet/control/PartsSpec.js
index 26df9d3..e662459 100644
--- a/loleaflet/spec/loleaflet/control/PartsSpec.js
+++ b/loleaflet/spec/loleaflet/control/PartsSpec.js
@@ -145,17 +145,6 @@ describe('Parts and Pages', function () {
 			expect(Math.floor(map.getDocSize().y)).to.be(2946);
 		});
 
-		it('Get page sizes', function () {
-			var pageSizes = map.getPageSizes();
-			expect(pageSizes).to.only.have.keys(['twips', 'pixels']);
-			expect(pageSizes.twips.length).to.be(map.getNumberOfPages());
-			expect(pageSizes.pixels.length).to.be(map.getNumberOfPages());
-			expect(pageSizes.twips[0].min.equals(new L.Point(284, 284))).to.be.ok();
-			expect(pageSizes.twips[0].max.equals(new L.Point(12190, 17122))).to.be.ok();
-			expect(pageSizes.twips[1].min.equals(new L.Point(284, 17406))).to.be.ok();
-			expect(pageSizes.twips[1].max.equals(new L.Point(12190, 34244))).to.be.ok();
-		});
-
 		it('Get document type', function () {
 			expect(map.getDocType()).to.be('text');
 		});
diff --git a/loleaflet/spec/loleaflet/control/SearchSpec.js b/loleaflet/spec/loleaflet/control/SearchSpec.js
index 6956f8d..da9dec1 100644
--- a/loleaflet/spec/loleaflet/control/SearchSpec.js
+++ b/loleaflet/spec/loleaflet/control/SearchSpec.js
@@ -13,14 +13,6 @@ describe('Search', function () {
 			edit: false,
 			readOnly: false
 		});
-
-		map.once('partpagerectangles', function(e) {
-			done();
-		});
-
-		setTimeout(function() {
-			done(new Error('No response for partpagerectangles'));
-		}, 5000);
 	});
 
 	afterEach(function () {
@@ -52,7 +44,7 @@ describe('Search', function () {
 				expect(e.count).to.be(1);
 				expect(e.results[0].part).to.be(0);
 				// the first page contains the search result
-				expect(map.getPageSizes().pixels[0].contains(e.results[0].rectangles[0])).to.be.ok();
+				//expect(map.getPageSizes().pixels[0].contains(e.results[0].rectangles[0])).to.be.ok();
 				done();
 			});
 			map.search('doc');
@@ -64,7 +56,7 @@ describe('Search', function () {
 				expect(e.count).to.be(1);
 				expect(e.results[0].part).to.be(0);
 				// the second page contains the search result
-				expect(map.getPageSizes().pixels[1].contains(e.results[0].rectangles[0])).to.be.ok();
+				//expect(map.getPageSizes().pixels[1].contains(e.results[0].rectangles[0])).to.be.ok();
 				done();
 			});
 			map.search('doc', true);
@@ -94,10 +86,10 @@ describe('Search', function () {
 				expect(e.results.length).to.be(5);
 				// first 4 results are in first page
 				for (var i = 0; i < e.count - 1; i++) {
-					expect(map.getPageSizes().pixels[0].contains(e.results[i].rectangles[0])).to.be.ok();
+					//expect(map.getPageSizes().pixels[0].contains(e.results[i].rectangles[0])).to.be.ok();
 				}
 				// last result is in second page
-				expect(map.getPageSizes().pixels[1].contains(e.results[e.count - 1].rectangles[0])).to.be.ok();
+				//expect(map.getPageSizes().pixels[1].contains(e.results[e.count - 1].rectangles[0])).to.be.ok();
 				done();
 			});
 			map.highlightAll('doc');
@@ -111,7 +103,7 @@ describe('Search', function () {
 				// Output of previous highlight all operation is still cached
 				expect(map._docLayer._searchResults.length).to.be(5);
 				// the first page contains the search result
-				expect(map.getPageSizes().pixels[0].contains(e.results[0].rectangles[0])).to.be.ok();
+				//expect(map.getPageSizes().pixels[0].contains(e.results[0].rectangles[0])).to.be.ok();
 				done();
 			});
 			map.search('doc');
@@ -125,7 +117,7 @@ describe('Search', function () {
 				// Output of previous highlight all operation is still cached
 				expect(map._docLayer._searchResults.length).to.be(5);
 				// the second page contains the search result
-				expect(map.getPageSizes().pixels[1].contains(e.results[0].rectangles[0])).to.be.ok();
+				//expect(map.getPageSizes().pixels[1].contains(e.results[0].rectangles[0])).to.be.ok();
 				done();
 			});
 			map.search('doc', true);
diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index 41f36e9..691942f 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -61,14 +61,7 @@ L.Map.include({
 
 		var docLayer = this._docLayer;
 		if (docLayer._docType === 'text') {
-			if (index >= docLayer._partPageRectanglesTwips.length) {
-				return;
-			}
-			var part = 0;
-			var tilePosX = docLayer._partPageRectanglesTwips[index].min.x;
-			var tilePosY = docLayer._partPageRectanglesTwips[index].min.y;
-			var tileWidth = docLayer._partPageRectanglesTwips[index].max.x - tilePosX;
-			var tileHeight = docLayer._partPageRectanglesTwips[index].max.y - tilePosY;
+			return;
 		}
 		else {
 			part = index;
@@ -294,12 +287,6 @@ L.Map.include({
 		return this._docLayer._docPixelSize;
 	},
 
-	getPageSizes: function () {
-		return {
-			twips: this._docLayer._partPageRectanglesTwips,
-			pixels: this._docLayer._partPageRectanglesPixels};
-	},
-
 	getDocType: function () {
 		if (!this._docLayer)
 			return null;
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 245fa56..9f90adb 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -131,7 +131,6 @@ L.Socket = L.Class.extend({
 			msg += ' options=' + JSON.stringify(options);
 		}
 		this._doSend(msg);
-		this._doSend('partpagerectangles');
 		for (var i = 0; i < this._msgQueue.length; i++) {
 			this._doSend(this._msgQueue[i].msg, this._msgQueue[i].coords);
 		}
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index a724ed8..6e3ae9c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -135,8 +135,6 @@ L.TileLayer = L.GridLayer.extend({
 		this._msgQueue = [];
 		this._toolbarCommandValues = {};
 		this._previewInvalidations = [];
-		this._partPageRectanglesTwips = [];
-		this._partPageRectanglesPixels = [];
 		this._clientZoom = 'tilepixelwidth=' + this._tileWidthPx + ' ' +
 			'tilepixelheight=' + this._tileHeightPx + ' ' +
 			'tiletwipwidth=' + this.options.tileWidthTwips + ' ' +
@@ -187,7 +185,6 @@ L.TileLayer = L.GridLayer.extend({
 		map.on('drop', this._onDrop, this);
 
 		map.on('zoomend', this._onUpdateCursor, this);
-		map.on('zoomend', this._onUpdatePartPageRectangles, this);
 		if (this._docType === 'spreadsheet') {
 			map.on('zoomend', this._onCellCursorShift, this);
 		}
@@ -343,9 +340,6 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('mousepointer:')) {
 			this._onMousePointerMsg(textMsg);
 		}
-		else if (textMsg.startsWith('partpagerectangles:')) {
-			this._onPartPageRectanglesMsg(textMsg);
-		}
 		else if (textMsg.startsWith('renderfont:')) {
 			this._onRenderFontMsg(textMsg, img);
 		}
@@ -855,34 +849,6 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
-	_onPartPageRectanglesMsg: function (textMsg) {
-		textMsg = textMsg.substring(19);
-		var pages = textMsg.split(';');
-		this._partPageRectanglesTwips = [];
-		this._partPageRectanglesPixels = [];
-		for (var i = 0; i < pages.length; i++) {
-			var strTwips = pages[i].match(/\d+/g);
-			if (!strTwips) {
-				// probably not a text file
-				return;
-			}
-			var topLeftTwips = new L.Point(parseInt(strTwips[0]), parseInt(strTwips[1]));
-			var offset = new L.Point(parseInt(strTwips[2]), parseInt(strTwips[3]));
-			var bottomRightTwips = topLeftTwips.add(offset);
-			var pageBoundsTwips = new L.Bounds(topLeftTwips, bottomRightTwips);
-			this._partPageRectanglesTwips.push(pageBoundsTwips);
-			var pageBoundsPixels = new L.Bounds(
-					this._twipsToPixels(topLeftTwips),
-					this._twipsToPixels(bottomRightTwips));
-			this._partPageRectanglesPixels.push(pageBoundsPixels);
-		}
-		this._map.fire('partpagerectangles', {
-			pixelRectangles: this._partPageRectanglesPixels,
-			twipsRectangles: this._partPageRectanglesTwips
-		});
-		this._onCurrentPageUpdate();
-	},
-
 	_onRenderFontMsg: function (textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		this._map.fire('renderfont', {
@@ -1843,22 +1809,6 @@ L.TileLayer = L.GridLayer.extend({
 		}
 	},
 
-	_onUpdatePartPageRectangles: function () {
-		if (this._partPageRectanglesPixels.length > 0) {
-			this._partPageRectanglesPixels = [];
-			for (var i = 0; i < this._partPageRectanglesTwips.length; i++) {
-				var pageBounds = new L.Bounds(
-						this._twipsToPixels(this._partPageRectanglesTwips[i].min),
-						this._twipsToPixels(this._partPageRectanglesTwips[i].max));
-				this._partPageRectanglesPixels.push(pageBounds);
-			}
-			this._map.fire('partpagerectangles', {
-				pixelRectangles: this._partPageRectanglesPixels,
-				twipsRectangles: this._partPageRectanglesTwips
-			});
-		}
-	},
-
 	// Cells can change position during changes of zoom level in calc
 	// hence we need to request an updated cell cursor position for this level.
 	_onCellCursorShift: function (force) {
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index 2e63de2..79705e9 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -149,10 +149,6 @@ uno <command>
 
     <command> is a line of text.
 
-partpagerectangles
-
-    Invokes lok::Document::getPartPageRectangles().
-
 clientvisiblearea x=<x> y=<y> width=<width> height=<height>
 
     Invokes lok::Document::setClientVisibleArea().
@@ -300,10 +296,6 @@ statechanged: <key>=<value>
     Notifies client of state changed events of <key>.
     Eg: 'statechanged: .uno:Undo=enabled'
 
-partpagerectangles: <payload>
-
-    Payload format is the same as LOK_CALLBACK_TEXT_SELECTION.
-
 textselectioncontent: <content>
 
     Current selection's content
commit 5dad626b6da4c8aee14211e8df1f3c81da1dc99d
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sat Feb 4 14:41:33 2017 -0500

    wsd: remove handling of depricated partpagerectangles command
    
    Change-Id: I13285e83179ab7654b6fd21a6548730e74d67425

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 4be66f2..9035fed 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -154,10 +154,6 @@ bool ChildSession::_handleInput(const char *buffer, int length)
     {
         return getCommandValues(buffer, length, tokens);
     }
-    else if (tokens[0] == "partpagerectangles")
-    {
-        return getPartPageRectangles(buffer, length);
-    }
     else if (tokens[0] == "load")
     {
         if (_isDocLoaded)
@@ -529,14 +525,6 @@ bool ChildSession::getCommandValues(const char* /*buffer*/, int /*length*/, cons
     return success;
 }
 
-bool ChildSession::getPartPageRectangles(const char* /*buffer*/, int /*length*/)
-{
-    // We don't support partpagerectangles any more, will be removed in the
-    // next version
-    sendTextFrame("partpagerectangles: ");
-    return true;
-}
-
 bool ChildSession::clientZoom(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
 {
     int tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight;
@@ -1150,7 +1138,6 @@ void ChildSession::loKitCallback(const int type, const std::string& payload)
             lock.unlock();
 
             getStatus("", 0);
-            getPartPageRectangles("", 0);
         }
         break;
     case LOK_CALLBACK_SET_PART:
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 0daa619..3eabc74 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -112,7 +112,6 @@ public:
     virtual ~ChildSession();
 
     bool getStatus(const char* buffer, int length);
-    bool getPartPageRectangles(const char* buffer, int length);
     int getViewId() const { return _viewId; }
     void setViewId(const int viewId) { _viewId = viewId; }
     const std::string& getViewUserId() const { return _userId; }
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 63db162..f8079cb 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -171,7 +171,10 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     }
     else if (tokens[0] == "partpagerectangles")
     {
-        return getPartPageRectangles(buffer, length, docBroker);
+        // We don't support partpagerectangles any more, will be removed in the
+        // next version
+        sendTextFrame("partpagerectangles: ");
+        return true;
     }
     else if (tokens[0] == "ping")
     {
@@ -294,18 +297,6 @@ bool ClientSession::getCommandValues(const char *buffer, int length, const std::
     return forwardToChild(std::string(buffer, length), docBroker);
 }
 
-bool ClientSession::getPartPageRectangles(const char *buffer, int length,
-                                          const std::shared_ptr<DocumentBroker>& docBroker)
-{
-    std::string partPageRectangles;
-    if (docBroker->tileCache().getTextFile("partpagerectangles.txt", partPageRectangles))
-    {
-        return sendTextFrame(partPageRectangles);
-    }
-
-    return forwardToChild(std::string(buffer, length), docBroker);
-}
-
 bool ClientSession::sendFontRendering(const char *buffer, int length, const std::vector<std::string>& tokens,
                                       const std::shared_ptr<DocumentBroker>& docBroker)
 {
@@ -623,13 +614,6 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt
                 }
             }
         }
-        else if (tokens[0] == "partpagerectangles:")
-        {
-            if (tokens.size() > 1 && !tokens[1].empty())
-            {
-                docBroker->tileCache().saveTextFile(std::string(buffer, length), "partpagerectangles.txt");
-            }
-        }
         else if (tokens[0] == "invalidatetiles:")
         {
             assert(firstLine.size() == static_cast<std::string::size_type>(length));
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 234b8fd..17c5c0d 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -122,9 +122,6 @@ private:
                    const std::shared_ptr<DocumentBroker>& docBroker);
     bool getCommandValues(const char* buffer, int length, const std::vector<std::string>& tokens,
                           const std::shared_ptr<DocumentBroker>& docBroker);
-    bool getPartPageRectangles(const char* buffer, int length,
-                               const std::shared_ptr<DocumentBroker>& docBroker);
-
     bool sendTile(const char* buffer, int length, const std::vector<std::string>& tokens,
                   const std::shared_ptr<DocumentBroker>& docBroker);
     bool sendCombinedTiles(const char* buffer, int length, const std::vector<std::string>& tokens,


More information about the Libreoffice-commits mailing list