[Libreoffice-commits] online.git: 6 commits - loleaflet/build loleaflet/dist loleaflet/main.js loleaflet/reference.html loleaflet/src loolwsd/ClientSession.cpp loolwsd/ClientSession.hpp loolwsd/DocumentBroker.cpp loolwsd/DocumentBroker.hpp loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/protocol.txt loolwsd/Storage.cpp loolwsd/Storage.hpp

Pranav Kant pranavk at collabora.co.uk
Thu Nov 10 09:35:23 UTC 2016


 loleaflet/build/deps.js                  |    5 +
 loleaflet/dist/toolbar/toolbar.js        |    2 
 loleaflet/main.js                        |    7 +-
 loleaflet/reference.html                 |   91 +++++++++++++++++++++++++++++++
 loleaflet/src/control/Control.Menubar.js |    2 
 loleaflet/src/core/Socket.js             |   24 ++++++--
 loleaflet/src/map/Map.js                 |   69 ++++-------------------
 loleaflet/src/map/handler/Map.WOPI.js    |   66 ++++++++++++++++++++++
 loolwsd/ClientSession.cpp                |   13 ++++
 loolwsd/ClientSession.hpp                |    7 ++
 loolwsd/DocumentBroker.cpp               |   26 ++++++++
 loolwsd/DocumentBroker.hpp               |    6 --
 loolwsd/LOOLSession.cpp                  |    6 +-
 loolwsd/LOOLSession.hpp                  |    2 
 loolwsd/Storage.cpp                      |   12 +++-
 loolwsd/Storage.hpp                      |    9 ++-
 loolwsd/protocol.txt                     |   21 +++++++
 17 files changed, 288 insertions(+), 80 deletions(-)

New commits:
commit b0933b063e8b0e8e0de7eeeb9706fb291af72b5c
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue Nov 8 19:07:28 2016 +0530

    tdf#103640: Implement OwnerTermination; send application-level close frame
    
    This implements a new feature 'OwnerTermination' for WOPI based
    hosts. WOPI hosts now have to enable this feature by mentioning
    'EnableOwnerTermination' as 'true' in their CheckFileInfo
    response. If the OwnerId of the file matches that of the UserId
    of the session, this session would be able to terminate all other
    sessions currently editing the same document.
    
    The reason for this kind of document termination is sent to all
    sessions in a new application-level 'close:' message. This new message is
    similar to the CLOSE frame of WebSocket protocol which doesn't
    seem to work across all browsers as of now. Eg: Chrome -
    https://bugs.chromium.org/p/chromium/issues/detail?id=426798
    After receiving this 'close: ' message, loleaflet acts
    accordingly and tells the WOPI host why the websocket was closed
    via post message API.
    
    Change-Id: I997aa2e7805157ed599a3946a877fd32477cee1b

diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 21cc00e..87ffe91 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -190,6 +190,21 @@ L.Socket = L.Class.extend({
 
 			return;
 		}
+		else if (textMsg.startsWith('close: ')) {
+			textMsg = textMsg.substring('close: '.length);
+
+			// This is due to document owner terminating the session
+			if (textMsg === 'ownertermination') {
+				// Disconnect the websocket manually
+				this.close();
+				// Tell WOPI host about it which should handle this situation
+				this._map.fire('postMessage', {msgId: 'Session_Closed'});
+			}
+
+			this._map.remove();
+
+			return;
+		}
 		else if (textMsg.startsWith('error:') && command.errorCmd === 'internal') {
 			this._map._fatal = true;
 			if (command.errorKind === 'diskfull') {
diff --git a/loleaflet/src/map/handler/Map.WOPI.js b/loleaflet/src/map/handler/Map.WOPI.js
index 4e12dd3..d966306 100644
--- a/loleaflet/src/map/handler/Map.WOPI.js
+++ b/loleaflet/src/map/handler/Map.WOPI.js
@@ -40,6 +40,9 @@ L.Map.WOPI = L.Handler.extend({
 
 			this._postMessage('Get_Views_Resp', getMembersRespVal);
 		}
+		else if (msg.MessageId === 'Close_Session') {
+			this._map._socket.sendMessage('closedocument');
+		}
 	},
 
 	_postMessage: function(e) {
diff --git a/loolwsd/ClientSession.cpp b/loolwsd/ClientSession.cpp
index 69654da..c186619 100644
--- a/loolwsd/ClientSession.cpp
+++ b/loolwsd/ClientSession.cpp
@@ -43,6 +43,7 @@ ClientSession::ClientSession(const std::string& id,
     _docBroker(docBroker),
     _uriPublic(uriPublic),
     _isReadOnly(readOnly),
+    _isDocumentOwner(false),
     _loadPart(-1)
 {
     Log::info("ClientSession ctor [" + getName() + "].");
@@ -115,6 +116,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "clientzoom" &&
              tokens[0] != "clientvisiblearea" &&
              tokens[0] != "commandvalues" &&
+             tokens[0] != "closedocument" &&
              tokens[0] != "downloadas" &&
              tokens[0] != "getchildid" &&
              tokens[0] != "gettextselection" &&
@@ -156,6 +158,17 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     {
         return getCommandValues(buffer, length, tokens, docBroker);
     }
+    else if (tokens[0] == "closedocument")
+    {
+        // If this session is the owner of the file, let it close all sessions
+        if (_isDocumentOwner)
+        {
+            LOG_DBG("Session [" + getId() + "] requested owner termination");
+            docBroker->closeDocument("ownertermination");
+        }
+
+        return true;
+    }
     else if (tokens[0] == "partpagerectangles")
     {
         return getPartPageRectangles(buffer, length, docBroker);
diff --git a/loolwsd/ClientSession.hpp b/loolwsd/ClientSession.hpp
index 8713026..77ddcfa 100644
--- a/loolwsd/ClientSession.hpp
+++ b/loolwsd/ClientSession.hpp
@@ -37,8 +37,11 @@ public:
     std::shared_ptr<PrisonerSession> getPeer() const { return _peer; }
     bool shutdownPeer(Poco::UInt16 statusCode);
 
+    const std::string getUserId() const { return _userId; }
+
     void setUserId(const std::string& userId) { _userId = userId; }
     void setUserName(const std::string& userName) { _userName = userName; }
+    void setDocumentOwner(const bool isDocumentOwner) { _isDocumentOwner = isDocumentOwner; }
 
     /**
      * Return the URL of the saved-as document when it's ready. If called
@@ -66,7 +69,6 @@ private:
 
     bool loadDocument(const char* buffer, int length, Poco::StringTokenizer& tokens,
                       const std::shared_ptr<DocumentBroker>& docBroker);
-
     bool getStatus(const char* buffer, int length,
                    const std::shared_ptr<DocumentBroker>& docBroker);
     bool getCommandValues(const char* buffer, int length, Poco::StringTokenizer& tokens,
@@ -94,6 +96,9 @@ private:
     /// Whether the session is opened as readonly
     bool _isReadOnly;
 
+    /// Whether this session is the owner of currently opened document
+    bool _isDocumentOwner;
+
     /// Our peer that connects us to the child.
     std::shared_ptr<PrisonerSession> _peer;
 
diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp
index 723b73b..295e07c 100644
--- a/loolwsd/DocumentBroker.cpp
+++ b/loolwsd/DocumentBroker.cpp
@@ -241,6 +241,13 @@ bool DocumentBroker::load(std::shared_ptr<ClientSession>& session, const std::st
             session->sendTextFrame("wopi: postmessageorigin " + wopifileinfo._postMessageOrigin);
         }
 
+        // Mark the session as 'Document owner' if WOPI hosts supports it
+        if (wopifileinfo._enableOwnerTermination && userid == _storage->getFileInfo()._ownerId)
+        {
+            LOG_DBG("Session [" + sessionId + "] is the document owner");
+            session->setDocumentOwner(true);
+        }
+
         getInfoCallDuration = wopifileinfo._callDuration;
     }
     else if (dynamic_cast<LocalStorage*>(_storage.get()) != nullptr)
@@ -895,14 +902,22 @@ void DocumentBroker::childSocketTerminated()
     }
 }
 
-void DocumentBroker::terminateChild(std::unique_lock<std::mutex>& lock)
+void DocumentBroker::terminateChild(std::unique_lock<std::mutex>& lock, const std::string& closeReason)
 {
     Util::assertIsLocked(_mutex);
     Util::assertIsLocked(lock);
 
     LOG_INF("Terminating child [" << getPid() << "] of doc [" << _docKey << "].");
 
-    assert(_sessions.empty() && "DocumentBroker still has unremoved sessions!");
+    // Close all running sessions
+    for (auto& pair : _sessions)
+    {
+        // 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);
+    }
+
+    std::this_thread::sleep_for (std::chrono::seconds(5));
 
     // First flag to stop as it might be waiting on our lock
     // to process some incoming message.
@@ -914,4 +929,11 @@ void DocumentBroker::terminateChild(std::unique_lock<std::mutex>& lock)
     _childProcess->close(false);
 }
 
+void DocumentBroker::closeDocument(const std::string& reason)
+{
+    auto lock = getLock();
+
+    terminateChild(lock, reason);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/DocumentBroker.hpp b/loolwsd/DocumentBroker.hpp
index 7280fb6..3212715 100644
--- a/loolwsd/DocumentBroker.hpp
+++ b/loolwsd/DocumentBroker.hpp
@@ -275,6 +275,8 @@ public:
 
     int getRenderedTileCount() { return _debugRenderedTileCount; }
 
+    void closeDocument(const std::string& reason);
+
     /// Called by the ChildProcess object to notify
     /// that it has terminated on its own.
     /// This happens either when the child exists
@@ -286,7 +288,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);
+    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/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index 25bc408..db091f5 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -202,14 +202,14 @@ bool LOOLSession::handleDisconnect()
     return false;
 }
 
-void LOOLSession::shutdown(Poco::UInt16 statusCode)
+void LOOLSession::shutdown(Poco::UInt16 statusCode, const std::string& statusMessage)
 {
     if (_ws)
     {
         try
         {
-            LOG_TRC("Shutting down WS [" << getName() << "].");
-            _ws->shutdown(statusCode);
+            LOG_TRC("Shutting down WS [" << getName() << "] with statusCode [" << statusCode << "] and reason [" << statusMessage << "].");
+            _ws->shutdown(statusCode, statusMessage);
         }
         catch (const Poco::Exception &exc)
         {
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 6ef8d22..cd6b9fe 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -62,7 +62,7 @@ public:
     /// Called to handle disconnection command from socket.
     virtual bool handleDisconnect();
 
-    void shutdown(Poco::UInt16 statusCode);
+    void shutdown(Poco::UInt16 statusCode, const std::string& statusMessage = "");
 
     bool isActive() const { return _isActive; }
     void setIsActive(bool active) { _isActive = active; }
diff --git a/loolwsd/Storage.cpp b/loolwsd/Storage.cpp
index 86ccdba..0a451ae 100644
--- a/loolwsd/Storage.cpp
+++ b/loolwsd/Storage.cpp
@@ -195,7 +195,7 @@ LocalStorage::LocalFileInfo LocalStorage::getLocalFileInfo(const Poco::URI& uriP
         const auto lastModified = file.getLastModified();
         const auto size = file.getSize();
 
-        _fileInfo = FileInfo({filename, "lool", lastModified, size});
+        _fileInfo = FileInfo({filename, "localhost", lastModified, size});
     }
 
     // Set automatic userid and username
@@ -318,6 +318,7 @@ WopiStorage::WOPIFileInfo WopiStorage::getWOPIFileInfo(const Poco::URI& uriPubli
     std::string userId;
     std::string userName;
     bool canWrite = false;
+    bool enableOwnerTermination = false;
     std::string postMessageOrigin;
     std::string resMsg;
     Poco::StreamCopier::copyToString(rs, resMsg);
@@ -345,6 +346,8 @@ WopiStorage::WOPIFileInfo WopiStorage::getWOPIFileInfo(const Poco::URI& uriPubli
         canWrite = canWriteVar.isString() ? (canWriteVar.toString() == "true") : false;
         const auto postMessageOriginVar = getOrWarn(object, "PostMessageOrigin");
         postMessageOrigin = postMessageOriginVar.isString() ? postMessageOriginVar.toString() : "";
+        const auto enableOwnerTerminationVar = getOrWarn(object, "EnableOwnerTermination");
+        enableOwnerTermination = enableOwnerTerminationVar.isString() ? (enableOwnerTerminationVar.toString() == "true") : false;
     }
     else
         Log::error("WOPI::CheckFileInfo is missing JSON payload");
@@ -355,7 +358,7 @@ WopiStorage::WOPIFileInfo WopiStorage::getWOPIFileInfo(const Poco::URI& uriPubli
         _fileInfo = FileInfo({filename, ownerId, Poco::Timestamp(), size});
     }
 
-    return WOPIFileInfo({userId, userName, canWrite, postMessageOrigin, callDuration});
+    return WOPIFileInfo({userId, userName, canWrite, postMessageOrigin, enableOwnerTermination, callDuration});
 }
 
 /// uri format: http://server/<...>/wopi*/files/<id>/content
diff --git a/loolwsd/Storage.hpp b/loolwsd/Storage.hpp
index 7912230..a1eb2cf 100644
--- a/loolwsd/Storage.hpp
+++ b/loolwsd/Storage.hpp
@@ -60,7 +60,7 @@ public:
         _uri(uri),
         _localStorePath(localStorePath),
         _jailPath(jailPath),
-        _fileInfo("", Poco::Timestamp(), 0),
+        _fileInfo("", "lool", Poco::Timestamp(), 0),
         _isLoaded(false)
     {
         Log::debug("Storage ctor: " + uri.toString());
@@ -169,11 +169,13 @@ public:
                      const std::string& username,
                      const bool userCanWrite,
                      const std::string& postMessageOrigin,
+                     const bool enableOwnerTermination,
                      const std::chrono::duration<double> callDuration)
             : _userid(userid),
               _username(username),
               _userCanWrite(userCanWrite),
               _postMessageOrigin(postMessageOrigin),
+              _enableOwnerTermination(enableOwnerTermination),
               _callDuration(callDuration)
             {
             }
@@ -186,6 +188,8 @@ public:
         bool _userCanWrite;
         /// WOPI Post message property
         std::string _postMessageOrigin;
+        /// If WOPI host has enabled owner termination feature on
+        bool _enableOwnerTermination;
         /// Time it took to call WOPI's CheckFileInfo
         std::chrono::duration<double> _callDuration;
     };
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index f6035b9..7fea786 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -164,6 +164,12 @@ userinactive
 
     See 'useractive'.
 
+closedocument
+
+    This gives document owners the ability to terminate all sessions currently
+    having that document opened. This functionality is enabled only in case WOPI
+    host mentions 'EnableOwnerTermination' flag in its CheckFileInfo response
+
 server -> client
 ================
 
@@ -220,6 +226,21 @@ error: cmd=<command> kind=<kind> [code=<error_code>] [params=1,2,3,...,N]
     <code> (when provided) further specifies the error as forwarded from
     LibreOffice
 
+close: <reason>
+
+    Ask a client to close the websocket connection with <reason>.
+    Exactly similar fields are also available in WebSocket protocol's
+    CLOSE frame, but some browser implementation (google-chrome) doesn't seem to
+    handle that well. This is a temporary application-level close websocket
+    to circumvent the same.
+
+    <reason> can have following values:
+    * ownertermination - If the session close is due to 'Document owner'
+    terminating the session.
+    (Document owner is the one who has the file ownership and hence have the
+    ability to kill all other sessions if EnableOwnerTermination flag in WOPI
+    CheckFileInfo is 'true' (assumed to be 'false' by default).
+
 getchildid: id=<id>
 
     Returns the child id
commit 410936b60012c5cbbd1032c2590582821ba22e62
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue Nov 8 13:54:02 2016 +0530

    loolwsd: This is not used anymore
    
    Change-Id: I7a0405faad5a2c1e15788c084d51bb6a4a39f82f

diff --git a/loolwsd/DocumentBroker.hpp b/loolwsd/DocumentBroker.hpp
index 94a13f2..7280fb6 100644
--- a/loolwsd/DocumentBroker.hpp
+++ b/loolwsd/DocumentBroker.hpp
@@ -275,10 +275,6 @@ public:
 
     int getRenderedTileCount() { return _debugRenderedTileCount; }
 
-    /// Returns time taken in making calls to storage during load
-    /// Currently, only makes sense in case storage is WOPI
-    const std::chrono::duration<double> getStorageLoadDuration() const;
-
     /// Called by the ChildProcess object to notify
     /// that it has terminated on its own.
     /// This happens either when the child exists
commit a6d2fe80a1e7c2eee80aa5f592e5445f7c7e2b91
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue Nov 8 13:44:14 2016 +0530

    loolwsd: Read and store session owner ids
    
    Change-Id: If905344475eb6d9585eaa5a1df6882dc3a7bdb2b

diff --git a/loolwsd/Storage.cpp b/loolwsd/Storage.cpp
index ef21a8d..86ccdba 100644
--- a/loolwsd/Storage.cpp
+++ b/loolwsd/Storage.cpp
@@ -195,7 +195,7 @@ LocalStorage::LocalFileInfo LocalStorage::getLocalFileInfo(const Poco::URI& uriP
         const auto lastModified = file.getLastModified();
         const auto size = file.getSize();
 
-        _fileInfo = FileInfo({filename, lastModified, size});
+        _fileInfo = FileInfo({filename, "lool", lastModified, size});
     }
 
     // Set automatic userid and username
@@ -314,6 +314,7 @@ WopiStorage::WOPIFileInfo WopiStorage::getWOPIFileInfo(const Poco::URI& uriPubli
     // Parse the response.
     std::string filename;
     size_t size = 0;
+    std::string ownerId;
     std::string userId;
     std::string userName;
     bool canWrite = false;
@@ -334,6 +335,8 @@ WopiStorage::WOPIFileInfo WopiStorage::getWOPIFileInfo(const Poco::URI& uriPubli
         filename = getOrWarn(object, "BaseFileName").toString();
         const auto sizeVar = getOrWarn(object, "Size");
         size = std::stoul(sizeVar.toString(), nullptr, 0);
+        const auto ownerIdVar = getOrWarn(object, "OwnerId");
+        ownerId = (ownerIdVar.isString() ? ownerIdVar.toString() : "");
         const auto userIdVar = getOrWarn(object, "UserId");
         userId = (userIdVar.isString() ? userIdVar.toString() : "");
         const auto userNameVar = getOrWarn(object,"UserFriendlyName");
@@ -349,7 +352,7 @@ WopiStorage::WOPIFileInfo WopiStorage::getWOPIFileInfo(const Poco::URI& uriPubli
     if (!_fileInfo.isValid())
     {
         // WOPI doesn't support file last modified time.
-        _fileInfo = FileInfo({filename, Poco::Timestamp(), size});
+        _fileInfo = FileInfo({filename, ownerId, Poco::Timestamp(), size});
     }
 
     return WOPIFileInfo({userId, userName, canWrite, postMessageOrigin, callDuration});
diff --git a/loolwsd/Storage.hpp b/loolwsd/Storage.hpp
index a60d42b..7912230 100644
--- a/loolwsd/Storage.hpp
+++ b/loolwsd/Storage.hpp
@@ -31,9 +31,11 @@ public:
     {
     public:
         FileInfo(const std::string& filename,
+                 const std::string& ownerId,
                  const Poco::Timestamp& modifiedTime,
                  size_t size)
             : _filename(filename),
+              _ownerId(ownerId),
               _modifiedTime(modifiedTime),
               _size(size)
         {
@@ -45,6 +47,7 @@ public:
         }
 
         std::string _filename;
+        std::string _ownerId;
         Poco::Timestamp _modifiedTime;
         size_t _size;
     };
commit 7f77618f38291e9933825e60e771a20b1b658d7a
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Nov 7 22:56:54 2016 +0530

    Document PostMessage API in reference.html
    
    Change-Id: Ibdd3fc060a99f3b6185362fa3143014710873079

diff --git a/loleaflet/reference.html b/loleaflet/reference.html
index ae31f6b..a07378f 100644
--- a/loleaflet/reference.html
+++ b/loleaflet/reference.html
@@ -67,6 +67,11 @@
 			<li><a href="#loleaflet-object-values">Object values</a></li>
 			<li><a href="#loleaflet-uno-commands">Uno commands</a></li>
 		</ul>
+		<h4 style="color:red;">PostMessage API</h4>
+		<ul>
+		        <li><a href="#loleaflet-postmessage-initialization">Initialization</a></li>
+			<li><a href="#loleaflet-postmessage-sessions">Session Management</a></li>
+		</ul>
 		<h4>UI Layers</h4>
 		<ul>
 			<li><a href="#marker">Marker</a></li>
@@ -2725,6 +2730,92 @@ The <code>id</code> property of ErrorEvent can have the following values:
 	</tr>
 </table>
 
+<h2 id="loleaflet-postmessage">PostMessage API</h2>
+
+<p>PostMessage API is used to post messages to outer frame when loleaflet is
+  enclosed in a parent frame. This is useful for hosts wanting to
+  integrate LibreOffice Online in it.</p>
+
+<p>This API is mostly based
+  on <a href="https://wopi.readthedocs.io/en/latest/scenarios/postmessage.html">WOPI
+    specification</a> with few additions/modifications. All messages sent are
+  in this form</p>
+
+<pre><code class="javascript">
+{
+    "MessageId": "<MessageId>",
+    "SendTime": "<Timestamp when message is sent>",
+    "Values": {
+         "<key>": "<value>"
+    }
+}
+</code></pre>
+
+SendTime is the timestamp returned by browsers' Date.now()
+
+<h3 id="loleaflet-postmessage-initialization">Initialization</h3>
+
+<table data-id='postmessage-initialization'>
+	<tr>
+		<th>MessageId</th>
+		<th>Values</th>
+		<th>Description</th>
+	</tr>
+	<tr>
+		<td><code><b>App_LoadingStatus</b></code></td>
+		<td><code>
+		    <nobr>DocumentLoadedTime: <Number></nobr>
+		</code></td>
+		<td>When loleaflet frame is completely ready. Host can start
+		sending other messages using post message API.
+		DocumentLoadedTime is timestamp when frame is
+		ready/loaded.</td>
+	</tr>
+</table>
+
+<h3 id="loleaflet-postmessage-sessions">Session Management</h3>
+
+<table data-id='postmessage-sessions'>
+	<tr>
+		<th>MessageId</th>
+		<th>Values</th>
+		<th>Description</th>
+	</tr>
+	<tr>
+		<td><code><b>View_Added</b></code></td>
+		<td><code>
+		    <nobr>ViewId: <Number></nobr>
+		    <nobr>UserId: <String></nobr>
+		    <nobr>UserName: <String></nobr>
+		    <nobr>Color: <Number></nobr>
+		</code></td>
+		<td>A new member is added. ViewId is unique integer
+		identifying a session/view. UserId is user identity. UserName is
+		display name of the user. Color is RGB color integer
+		value.
+		</td>
+	</tr>
+	<tr>
+		<td><code><b>View_Removed</b></code></td>
+		<td><code>
+		    <nobr>ViewId: <Number></nobr>
+		</code></td>
+		<td>View with <code>ViewId</code> has closed the document.
+		</td>
+	</tr>
+	<tr>
+		<td><code><b>Get_Views_Resp</b></code></td>
+		<td><code>
+		    <nobr>ViewId: <Number></nobr>
+		    <nobr>UserId: <String></nobr>
+		    <nobr>UserName: <String></nobr>
+		    <nobr>Color: <Number></nobr>
+		</code></td>
+		<td>Give details of all current views when queried using <code>Get_Views</code>
+		</td>
+	</tr>
+</table>
+
 <h2 id="marker">Marker</h2>
 
 <p>Used to put markers on the map. Extends <a href="#layer">Layer</a>.</p>
commit 1a1792cdc185dc6792327524ea385869e7ce6e1e
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Nov 7 21:40:21 2016 +0530

    loleaflet: Group map getters together
    
    Change-Id: I576f78aaa215b6ba3aff6a5ce8498a3449080927

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 3aa3bcc..e04866a 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -144,17 +144,6 @@ L.Map = L.Evented.extend({
 		this.fire('removeview', {viewId: viewid, username: username});
 	},
 
-	getViewName: function(viewid) {
-		return this._viewInfo[viewid].username;
-	},
-
-	getViewColor: function(viewid) {
-		if (this._docLayer._docType !== 'text') {
-			return L.LOUtil.getViewIdColor(viewid);
-		}
-
-		return this._viewInfo[viewid].color;
-	},
 
 	// replaced by animation-powered implementation in Map.PanAnimation.js
 	setView: function (center, zoom) {
@@ -396,6 +385,18 @@ L.Map = L.Evented.extend({
 
 	// public methods for getting map state
 
+	getViewName: function(viewid) {
+		return this._viewInfo[viewid].username;
+	},
+
+	getViewColor: function(viewid) {
+		if (this._docLayer._docType !== 'text') {
+			return L.LOUtil.getViewIdColor(viewid);
+		}
+
+		return this._viewInfo[viewid].color;
+	},
+
 	getCenter: function () { // (Boolean) -> LatLng
 		this._checkIfLoaded();
 
commit f1f5c4ed662984ec2da62e1998bff83cfe05d422
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Nov 7 21:37:40 2016 +0530

    loleaflet: A more OO approach for WOPI post message API
    
    Abstract all the WOPI related logic in a map handler which is
    enabled only if map.options.wopi is set during map
    initialization.
    
    Change-Id: I54c5d6eecf33f88e4fd4d2b5ac9e8cf9dd001966

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 58a2ff1..b140214 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -248,6 +248,11 @@ var deps = {
 		desc: 'Handles inserting a file (image) in the document.'
 	},
 
+	WOPI: {
+		src: ['map/handler/Map.WOPI.js'],
+		desc: 'Handles WOPI related logic.'
+	},
+
 	MarkerDrag: {
 		src: ['layer/marker/Marker.Drag.js'],
 		deps: ['Marker'],
diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index 470d39b..90027f5 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -220,7 +220,7 @@ function onClick(id, item, subItem) {
 		resizeToolbar();
 	}
 	else if (id === 'close') {
-		map.WOPIPostMessage('UI_Close');
+		map.fire('postMessage', {msgId: 'UI_Close'});
 		map.remove();
 	}
 }
diff --git a/loleaflet/main.js b/loleaflet/main.js
index 831e6bf..73fbd5c 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -79,15 +79,14 @@ global.revHistoryEnabled = revHistoryEnabled;
 global.title = title;
 global.errorMessages = errorMessages;
 var docURL, docParams;
-var storageType;
+var isWopi = false;
 if (wopiSrc != '') {
     docURL = wopiSrc;
 	docParams = wopiParams;
-	storageType = 'wopi';
+	isWopi = true;
 } else {
     docURL = filePath;
 	docParams = {};
-	storageType = 'local';
 }
 
 document.title = title;
@@ -99,7 +98,7 @@ var map = L.map('map', {
     timestamp: timestamp,
     documentContainer: 'document-container',
     debug: debugMode,
-    storageType: storageType
+	wopi: isWopi
 });
 // toolbar.js (loaded in <script> tag accesses map as global variable,
 // so expose it
diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 03027d7..639eef7 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -405,7 +405,7 @@ L.Control.Menubar = L.Control.extend({
 		} else if (id === 'rev-history') {
 			// if we are being loaded inside an iframe, ask
 			// our host to show revision history mode
-			map.WOPIPostMessage('rev-history');
+			map.fire('postMessage', {msgId: 'rev-history'});
 		} else if (id === 'repair') {
 			map._socket.sendMessage('commandvalues command=.uno:DocumentRepair');
 		} else if (id === 'a4portrait') {
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 49617d5..21cc00e 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -180,11 +180,12 @@ L.Socket = L.Class.extend({
 		else if (textMsg.startsWith('wopi: ')) {
 			// Handle WOPI related messages
 			textMsg = textMsg.substring('wopi: '.length);
-			if (textMsg.startsWith('postmessageorigin ')) {
-				this._map._wopi['PostMessageOrigin'] = textMsg.substring('postmessageorigin '.length);
-				this._map._wopi['DocumentLoadedTime'] = Date.now();
+			// Store postmessageorigin property in our WOPI handler, if it exists
+			if (this._map['wopi'] && textMsg.startsWith('postmessageorigin ')) {
+				this._map['wopi'].PostMessageOrigin = textMsg.substring('postmessageorigin '.length);
+				this._map['wopi'].DocumentLoadedTime = Date.now();
 				// Tell the host that we are ready now
-				this._map.WOPIPostMessage('App_LoadingStatus', {'DocumentLoadedTime': this._map._wopi['DocumentLoadedTime']});
+				this._map.fire('postMessage', {msgId: 'App_LoadingStatus', args: {'DocumentLoadedTime': this._map['wopi'].DocumentLoadedTime}});
 			}
 
 			return;
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 46c53ee..3aa3bcc 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -123,20 +123,13 @@ L.Map = L.Evented.extend({
 
 		// View color map
 		this._viewColors = {};
-
-		// WOPI specific properties container
-		this._wopi = {};
-
-		// WOPI PostMessage Listener
-		L.DomEvent.on(window, 'message', this._WOPIPostMessageListener, this);
 	},
 
-
 	// public methods that modify map state
 
 	addView: function(viewid, userid, username, color) {
 		this._viewInfo[viewid] = {'userid': userid, 'username': username, 'color': color};
-		this.WOPIPostMessage('View_Added', {ViewId: viewid, UserId: userid, UserName: username, Color: color});
+		this.fire('postMessage', {msgId: 'View_Added', args: {ViewId: viewid, UserId: userid, UserName: username, Color: color}});
 
 		// Fire last, otherwise not all events are handled correctly.
 		this.fire('addview', {viewId: viewid, username: username});
@@ -145,7 +138,7 @@ L.Map = L.Evented.extend({
 	removeView: function(viewid) {
 		var username = this._viewInfo[viewid].username;
 		delete this._viewInfo[viewid];
-		this.WOPIPostMessage('View_Removed', {ViewId: viewid});
+		this.fire('postMessage', {msgId: 'View_Removed', args: {ViewId: viewid}});
 
 		// Fire last, otherwise not all events are handled correctly.
 		this.fire('removeview', {viewId: viewid, username: username});
@@ -163,41 +156,6 @@ L.Map = L.Evented.extend({
 		return this._viewInfo[viewid].color;
 	},
 
-	_WOPIPostMessageListener: function(e) {
-		if (!window.WOPIPostmessageReady) {
-			return;
-		}
-
-		var msg = JSON.parse(e.data);
-		if (msg.MessageId === 'Get_Views') {
-			var getMembersRespVal = [];
-			for (var viewInfoIdx in this._viewInfo) {
-				getMembersRespVal.push({
-					ViewId: viewInfoIdx,
-					UserName: this._viewInfo[viewInfoIdx].username,
-					UserId: this._viewInfo[viewInfoIdx].userid,
-					Color: this._viewInfo[viewInfoIdx].color
-				});
-			}
-
-			this.WOPIPostMessage('Get_Views_Resp', getMembersRespVal);
-		}
-	},
-
-	WOPIPostMessage: function(msgId, values) {
-		if (this.options.storageType === 'wopi' && !!this._wopi['PostMessageOrigin']) {
-			if (window.top !== window.self) {
-				var msg = {
-					'MessageId': msgId,
-					'SendTime': Date.now(),
-					'Values': values
-				};
-
-				window.parent.postMessage(JSON.stringify(msg), this._wopi['PostMessageOrigin']);
-			}
-		}
-	},
-
 	// replaced by animation-powered implementation in Map.PanAnimation.js
 	setView: function (center, zoom) {
 		zoom = zoom === undefined ? this.getZoom() : zoom;
diff --git a/loleaflet/src/map/handler/Map.WOPI.js b/loleaflet/src/map/handler/Map.WOPI.js
new file mode 100644
index 0000000..4e12dd3
--- /dev/null
+++ b/loleaflet/src/map/handler/Map.WOPI.js
@@ -0,0 +1,63 @@
+/*
+ * L.WOPI contains WOPI related logic
+ */
+
+L.Map.WOPI = L.Handler.extend({
+
+	PostMessageOrigin: false,
+	DocumentLoadedTime: false,
+
+	initialize: function(map) {
+		this._map = map;
+	},
+
+	addHooks: function() {
+		this._map.on('postMessage', this._postMessage, this);
+		L.DomEvent.on(window, 'message', this._postMessageListener, this);
+	},
+
+	removeHooks: function() {
+		this._map.off('postMessage', this._postMessage, this);
+		L.DomEvent.off(window, 'message', this._postMessageListener, this);
+	},
+
+	_postMessageListener: function(e) {
+		if (!window.WOPIPostmessageReady) {
+			return;
+		}
+
+		var msg = JSON.parse(e.data);
+		if (msg.MessageId === 'Get_Views') {
+			var getMembersRespVal = [];
+			for (var viewInfoIdx in this._map.viewInfo) {
+				getMembersRespVal.push({
+					ViewId: viewInfoIdx,
+					UserName: this._map._viewInfo[viewInfoIdx].username,
+					UserId: this._map._viewInfo[viewInfoIdx].userid,
+					Color: this._map._viewInfo[viewInfoIdx].color
+				});
+			}
+
+			this._postMessage('Get_Views_Resp', getMembersRespVal);
+		}
+	},
+
+	_postMessage: function(e) {
+		if (!this.enabled) { return; }
+
+		var msgId = e.msgId;
+		var values = e.args || {};
+		if (!!this.PostMessageOrigin && window.parent !== window.self) {
+			var msg = {
+				'MessageId': msgId,
+				'SendTime': Date.now(),
+				'Values': values
+			};
+
+			window.parent.postMessage(JSON.stringify(msg), this.PostMessageOrigin);
+		}
+	}
+});
+
+// This handler would only get 'enabled' by map if map.options.wopi = true
+L.Map.addInitHook('addHandler', 'wopi', L.Map.WOPI);


More information about the Libreoffice-commits mailing list