[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-3-0' - kit/ChildSession.cpp kit/ChildSession.hpp kit/Kit.cpp loleaflet/dist loleaflet/src test/WhiteBoxTests.cpp wsd/ClientSession.cpp wsd/DocumentBroker.cpp

Pranav Kant pranavk at collabora.co.uk
Wed Nov 29 14:01:55 UTC 2017


 kit/ChildSession.cpp                       |  125 +++++++++++++++--
 kit/ChildSession.hpp                       |   15 +-
 kit/Kit.cpp                                |  122 +---------------
 loleaflet/dist/loleaflet.css               |    1 
 loleaflet/src/control/Control.LokDialog.js |  211 ++++++++++++++++++-----------
 loleaflet/src/control/Control.Menubar.js   |    2 
 loleaflet/src/control/Toolbar.js           |   14 -
 loleaflet/src/layer/tile/TileLayer.js      |    4 
 test/WhiteBoxTests.cpp                     |    2 
 wsd/ClientSession.cpp                      |    4 
 wsd/DocumentBroker.cpp                     |   40 -----
 11 files changed, 273 insertions(+), 267 deletions(-)

New commits:
commit c1c70a5be042bcab64e1d36d20adc1dac01c50c4
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Sat Nov 25 01:59:48 2017 +0530

    Dialog tunneling changes after LOK API changes
    
    Consists of following commits:
    
    Allow sending binary websocket frames from IDocument interface
    
    Change-Id: I89f304e69e0c7b5f87bd2883a775d5c256674ef0
    (cherry picked from commit 5355c52e109701d6a652c3fb20439a181ac652a6)
    
    Adapt to dialog API changes in LOK
    
    ... and bypass some unncessary DocumentBroker handling of dialog
    messages.
    
    Change-Id: I378dff7a9786479baaa43f5fde9d30f35bc7f948
    (cherry picked from commit a19121bbbc65124393fa9e429fec87554b015317)
    
    lokdialog: Change the dialog container width too
    
    Change-Id: If3f937da472db20c39f402562ad559080509956d
    (cherry picked from commit 8ad03c61ee1a028da6e69bcd2dfeb512ca280c5b)
    
    lokdialog: Restore dialog container size to original
    
    Change-Id: I3a7bb1ffdb4f50776868fee6eade44f194d290e0
    (cherry picked from commit e6dbbef8f588eb2d3982f299cca67ab267ebae96)
    
    Bringing it to the top of the world
    
    I don't have much idea how the vex overlay's z-index is 1111. We don't
    set it anywhere. Anyhow, the dialog still needs to be lower than vex.
    Hence, the 1105.
    
    Change-Id: Idbd9beeb84d626a80580a7f7ca75f73edb89f06b
    (cherry picked from commit 91d6755781aac8aaeb9ed85b87a2880edd74c9b4)
    
    lokdialog: request paint without mentioning bounds for the first time
    
    Ideally, we should get the bounds of the dialog from the backend and we
    do but those bounds are not correct for some reason. Till it's fixed in
    core, we better not mention any bounds.
    
    Change-Id: I88928df624948f991656f37b1a7c4f0c3bdd46a6
    (cherry picked from commit a98d0f6f2fd6862a768280ca8fed2c66b58addaf)
    
    lokdialog: Make sure you always send the bounds with paint request
    
    ... otherwise, in the backend we use a larger buffer to paint the dialog
    which is a memory waste.
    
    Change-Id: I2e97087f54b1a5340af6db216bfc7c40c0574b60
    (cherry picked from commit 6942536c678485d96fa19edb603877ee881d0c34)
    
    lokdialog: Handle size_changed; factor out common code
    
    Change-Id: Ie4374ae1e2f5ba29b239cb37eb9fe4c5991094b0
    (cherry picked from commit b6117c2182082ef2a76afe105f5de30f69da534e)
    
    lokdialog: Remove dialog artifacts after socket disconnection
    
    Change-Id: I199178761cfa715043185964142c9156e1a0b50f
    (cherry picked from commit c996c04d8e2444a64784cf1ad8d7efa8519f4267)
    
    lokdialog: Remove getDialogInfo call; correct dialog size handling
    
    Change-Id: Ieff59baa984982bd8126102dafc5a97f673a4150
    (cherry picked from commit b8cd4a6a7aa7be46b4e19a1b387c1f7fa2f6b13d)
    Reviewed-on: https://gerrit.libreoffice.org/45488
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 80e008fc..ca4c855e 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -202,6 +202,10 @@ bool ChildSession::_handleInput(const char *buffer, int length)
     {
         return getStatus(buffer, length);
     }
+    else if (tokens[0] == "dialog" || tokens[0] == "dialogchild")
+    {
+        return renderDialog(buffer, length, tokens);
+    }
     else if (tokens[0] == "tile" || tokens[0] == "tilecombine")
     {
         assert(false && "Tile traffic should go through the DocumentBroker-LoKit WS.");
@@ -759,16 +763,23 @@ bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/,
                             const LokEventTargetEnum target)
 {
     int type, charcode, keycode;
-    std::string dialogId;
+    unsigned dialogId = 0;
     unsigned counter = 1;
-    unsigned minTotal = 4; // cmdname, type, char, key are strictly required
+    unsigned expectedTokens = 4; // cmdname(key), type, char, key are strictly required
     if (target == LokEventTargetEnum::Dialog || target == LokEventTargetEnum::DialogChild)
     {
-        getTokenString(tokens[counter++], "dialogid", dialogId);
-        minTotal++; // other params still necessarily required
+        if (tokens.size() <= counter ||
+            !getTokenUInt32(tokens[counter++], "dialogid", dialogId))
+        {
+            LOG_ERR("Dialog key event expects a valid dialogid= attribute");
+            sendTextFrame("error: cmd=" + std::string(tokens[0]) + " kind=syntax");
+            return false;
+        }
+        else // dialogid= attribute is found
+            expectedTokens++;
     }
 
-    if (tokens.size() != minTotal ||
+    if (tokens.size() != expectedTokens ||
         !getTokenKeyword(tokens[counter++], "type",
                          {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
                          type) ||
@@ -801,8 +812,8 @@ bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/,
         getLOKitDocument()->setView(_viewId);
         getLOKitDocument()->postKeyEvent(type, charcode, keycode);
     }
-    else if (!dialogId.empty())
-        getLOKitDocument()->postDialogKeyEvent(dialogId.c_str(), type, charcode, keycode);
+    else if (dialogId != 0)
+        getLOKitDocument()->postDialogKeyEvent(dialogId, type, charcode, keycode);
 
     return true;
 }
@@ -818,16 +829,22 @@ bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/,
     int buttons = 1; // left button
     int modifier = 0;
 
-    std::string dialogId;
+    unsigned dialogId = 0;
     unsigned counter = 1;
-    unsigned minTotal = 5; // cmdname, type, x, y, count are strictly required
+    unsigned minTokens = 5; // cmdname(mouse), type, x, y, count are strictly required
     if (target == LokEventTargetEnum::Dialog || target == LokEventTargetEnum::DialogChild)
     {
-        getTokenString(tokens[counter++], "dialogid", dialogId);
-        minTotal++; // other params still necessarily required
+        if (tokens.size() <= counter ||
+            !getTokenUInt32(tokens[counter++], "dialogid", dialogId))
+        {
+            LOG_ERR("Dialog mouse event expects a valid dialogid= attribute");
+            success = false;
+        }
+        else // dialogid= attribute is found
+            minTokens++;
     }
 
-    if (tokens.size() < minTotal ||
+    if (tokens.size() < minTokens ||
         !getTokenKeyword(tokens[counter++], "type",
                          {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
                           {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
@@ -863,10 +880,10 @@ bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/,
         getLOKitDocument()->postMouseEvent(type, x, y, count, buttons, modifier);
         break;
     case LokEventTargetEnum::Dialog:
-        getLOKitDocument()->postDialogMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+        getLOKitDocument()->postDialogMouseEvent(dialogId, type, x, y, count, buttons, modifier);
         break;
     case LokEventTargetEnum::DialogChild:
-        getLOKitDocument()->postDialogChildMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+        getLOKitDocument()->postDialogChildMouseEvent(dialogId, type, x, y, count, buttons, modifier);
         break;
     default:
         assert(false && "Unsupported mouse target type");
@@ -933,6 +950,86 @@ bool ChildSession::selectText(const char* /*buffer*/, int /*length*/, const std:
     return true;
 }
 
+bool ChildSession::renderDialog(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+{
+    std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
+    getLOKitDocument()->setView(_viewId);
+    const bool isChild = (tokens[0] == "dialogchild");
+
+    unsigned dialogId = 0;
+    if (tokens.size() > 1)
+    {
+        std::istringstream reader(tokens[1]);
+        reader >> dialogId;
+    }
+
+    int startX = 0, startY = 0;
+    int bufferWidth = 800, bufferHeight = 600;
+    std::string paintRectangle;
+    if (tokens.size() > 2 && getTokenString(tokens[2], "rectangle", paintRectangle))
+    {
+        const std::vector<std::string> rectParts = LOOLProtocol::tokenize(paintRectangle.c_str(), paintRectangle.length(), ',');
+        startX = std::atoi(rectParts[0].c_str());
+        startY = std::atoi(rectParts[1].c_str());
+        bufferWidth = std::atoi(rectParts[2].c_str());
+        bufferHeight = std::atoi(rectParts[3].c_str());
+    }
+    else
+        LOG_WRN("dialog command doesn't specify a rectangle= attribute.");
+
+    size_t pixmapDataSize = 4 * bufferWidth * bufferHeight;
+    std::vector<unsigned char> pixmap(pixmapDataSize);
+    int width = bufferWidth, height = bufferHeight;
+    std::string response;
+    if (isChild)
+    {
+        Timestamp timestamp;
+
+        getLOKitDocument()->paintActiveFloatingWindow(dialogId, pixmap.data(), width, height);
+        LOG_TRC("paintActiveFloatingWindow for dialogId [" << dialogId << "] returned floating window with size "
+                << width << "X" << height << " "
+                << "rendered in " << (timestamp.elapsed()/1000.)
+                << "ms (" << (width * height) / (timestamp.elapsed()) << " MP/s).");
+
+        response = "dialogchildpaint: id=" + tokens[1] + " width=" + std::to_string(width) + " height=" + std::to_string(height) + "\n";
+    }
+    else
+    {
+        Timestamp timestamp;
+        getLOKitDocument()->paintDialog(dialogId, pixmap.data(), startX, startY, width, height);
+
+        const double area = width * height;
+        LOG_TRC("paintDialog for " << dialogId << " returned " << width << "X" << height
+                << "@(" << startX << "," << startY << ")"
+                << "and rendered in " << (timestamp.elapsed()/1000.)
+                << "ms (" << area / (timestamp.elapsed()) << " MP/s).");
+
+        response = "dialogpaint: id=" + tokens[1] +
+            " width=" + std::to_string(width) + " height=" + std::to_string(height);
+
+        if (!paintRectangle.empty())
+            response += " rectangle=" + paintRectangle;
+
+        response += "\n";
+    }
+
+    std::vector<char> output;
+    output.reserve(response.size() + pixmapDataSize);
+    output.resize(response.size());
+    std::memcpy(output.data(), response.data(), response.size());
+
+    // TODO: use png cache for dialogs too
+    if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, width, height, bufferWidth, bufferHeight, output, LOK_TILEMODE_RGBA))
+    {
+        LOG_ERR("Failed to encode into PNG.");
+        return false;
+    }
+
+    LOG_TRC("Sending response (" << output.size() << " bytes) for: " << response);
+    sendBinaryFrame(output.data(), output.size());
+    return true;
+}
+
 bool ChildSession::selectGraphic(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
 {
     int type, x, y;
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 417f0bb2..ad9486a2 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -17,6 +17,7 @@
 #define LOK_USE_UNSTABLE_API
 #include <LibreOfficeKit/LibreOfficeKit.hxx>
 
+#include <Poco/Net/WebSocket.h>
 #include <Poco/Thread.h>
 
 #include "Common.hpp"
@@ -70,7 +71,7 @@ public:
 
     virtual std::shared_ptr<TileQueue>& getTileQueue() = 0;
 
-    virtual bool sendTextFrame(const std::string& message) = 0;
+    virtual bool sendFrame(const char* buffer, int length, int flags = Poco::Net::WebSocket::FRAME_TEXT) = 0;
 };
 
 struct RecordedEvent
@@ -157,13 +158,18 @@ public:
 
     void loKitCallback(const int type, const std::string& payload);
 
-    bool sendTextFrame(const char* buffer, const int length) override
+    bool sendTextFrame(const char* buffer, int length) override
     {
         const auto msg = "client-" + getId() + ' ' + std::string(buffer, length);
-
         const auto lock = getLock();
+        return _docManager.sendFrame(msg.data(), msg.size(), Poco::Net::WebSocket::FRAME_TEXT);
+    }
 
-        return _docManager.sendTextFrame(msg);
+    bool sendBinaryFrame(const char* buffer, int length) override
+    {
+        const auto msg = "client-" + getId() + ' ' + std::string(buffer, length);
+        const auto lock = getLock();
+        return _docManager.sendFrame(msg.data(), msg.size(), Poco::Net::WebSocket::FRAME_BINARY);
     }
 
     using Session::sendTextFrame;
@@ -188,6 +194,7 @@ private:
     bool unoCommand(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool selectText(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool selectGraphic(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool renderDialog(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool resetSelection(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool saveAs(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool setClientPart(const char* buffer, int length, const std::vector<std::string>& tokens);
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 1a7d87c1..02516ea3 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -846,111 +846,6 @@ public:
         ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
-    void renderDialog(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
-    {
-        assert(ws && "Expected a non-null websocket.");
-
-        const bool child = tokens[0] == "dialogchild";
-
-        std::unique_lock<std::mutex> lock(_documentMutex);
-        if (!_loKitDocument)
-        {
-            LOG_ERR("Dialog rendering requested before loading document.");
-            return;
-        }
-
-        if (_loKitDocument->getViewsCount() <= 0)
-        {
-            LOG_ERR("Dialog rendering requested without views.");
-            return;
-        }
-
-        int startX = 0, startY = 0;
-        int bufferWidth = 800, bufferHeight = 600; // hopefully, this is big enough
-        std::string paintRectangle;
-        // find the rectangle to paint, if specified
-        if (tokens.size() >= 3 && getTokenString(tokens[2], "rectangle", paintRectangle))
-        {
-            const std::vector<std::string> rectParts = LOOLProtocol::tokenize(paintRectangle.c_str(), paintRectangle.length(), ',');
-            startX = std::atoi(rectParts[0].c_str());
-            startY = std::atoi(rectParts[1].c_str());
-            bufferWidth = std::atoi(rectParts[2].c_str());
-            bufferHeight = std::atoi(rectParts[3].c_str());
-        }
-
-        size_t pixmapDataSize = 4 * bufferWidth * bufferHeight;
-        std::vector<unsigned char> pixmap(pixmapDataSize);
-
-        char* pDialogTitle = nullptr;
-        int width = bufferWidth;
-        int height = bufferHeight;
-        std::string response;
-        if (child)
-        {
-            Timestamp timestamp;
-            _loKitDocument->paintActiveFloatingWindow(tokens[1].c_str(), pixmap.data(), width, height);
-            const auto elapsed = timestamp.elapsed();
-            const double area = width * height;
-            LOG_TRC("paintActiveFloatingWindow for " << tokens[1] << " returned floating window "
-                    << width << "X" << height << " "
-                    << "rendered in " << (elapsed/1000.)
-                    << "ms (" << area / elapsed << " MP/s).");
-
-            response = "dialogchildpaint: id=" + tokens[1] + " width=" + std::to_string(width) + " height=" + std::to_string(height) + "\n";
-        }
-        else
-        {
-            Timestamp timestamp;
-            _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), startX, startY, width, height);
-            const auto elapsed = timestamp.elapsed();
-
-            int dialogWidth = 0;
-            int dialogHeight = 0;
-            _loKitDocument->getDialogInfo(tokens[1].c_str(), &pDialogTitle, dialogWidth, dialogHeight);
-
-            std::string encodedDialogTitle;
-            if (pDialogTitle)
-            {
-                std::string aDialogTitle(pDialogTitle);
-                URI::encode(aDialogTitle, "", encodedDialogTitle);
-                free(pDialogTitle);
-            }
-
-            // rendered width, height cannot be less than the dialog width, height
-            width = std::min(width, dialogWidth);
-            height = std::min(height, dialogHeight);
-            const double area = width * height;
-
-            LOG_TRC("paintDialog for " << tokens[1] << " returned " << width << "X" << height
-                    << "@(" << startX << "," << startY << ")"
-                    << "and rendered in " << (elapsed/1000.)
-                    << "ms (" << area / elapsed << " MP/s).");
-
-            response = "dialogpaint: id=" + tokens[1] + " title=" + encodedDialogTitle +
-                " dialogwidth=" + std::to_string(dialogWidth) + " dialogheight=" + std::to_string(dialogHeight);
-
-            if (!paintRectangle.empty())
-                response += " rectangle=" + paintRectangle;
-
-            response += "\n";
-        }
-
-        std::vector<char> output;
-        output.reserve(response.size() + pixmapDataSize);
-        output.resize(response.size());
-        std::memcpy(output.data(), response.data(), response.size());
-
-        // TODO: use png cache for dialogs too
-        if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, width, height, bufferWidth, bufferHeight, output, LOK_TILEMODE_RGBA))
-        {
-            LOG_ERR("Failed to encode into PNG.");
-            return;
-        }
-
-        LOG_TRC("Sending response (" << output.size() << " bytes) for: " << response);
-        ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
-    }
-
     void renderCombinedTiles(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
     {
         assert(ws && "Expected a non-null websocket.");
@@ -1080,22 +975,27 @@ public:
         ws->sendFrame(response.data(), response.size(), WebSocket::FRAME_BINARY);
     }
 
-    bool sendTextFrame(const std::string& message) override
+    bool sendTextFrame(const std::string& message)
+    {
+        return sendFrame(message.data(), message.size());
+    }
+
+    bool sendFrame(const char* buffer, int length, int flags = Poco::Net::WebSocket::FRAME_TEXT) override
     {
         try
         {
             if (!_ws || _ws->poll(Poco::Timespan(0), Poco::Net::Socket::SelectMode::SELECT_ERROR))
             {
-                LOG_ERR("Child Doc: Bad socket while sending [" << getAbbreviatedMessage(message) << "].");
+                LOG_ERR("Child Doc: Bad socket while sending [" << getAbbreviatedMessage(buffer, length) << "].");
                 return false;
             }
 
-            _ws->sendFrame(message.data(), message.size());
+            _ws->sendFrame(buffer, length, flags);
             return true;
         }
         catch (const Exception& exc)
         {
-            LOG_ERR("Document::sendTextFrame: Exception: " << exc.displayText() <<
+            LOG_ERR("Document::sendFrame: Exception: " << exc.displayText() <<
                     (exc.nested() ? "( " + exc.nested()->displayText() + ")" : ""));
         }
 
@@ -1775,10 +1675,6 @@ private:
                 {
                     renderCombinedTiles(tokens, _ws);
                 }
-                else if (tokens[0] == "dialog" || tokens[0] == "dialogchild")
-                {
-                    renderDialog(tokens, _ws);
-                }
                 else if (LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                 {
                     forwardToChild(tokens[0], input);
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index 48456535..d97fc008 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -341,6 +341,7 @@ body {
 	height: auto;
 	border: none;
 	background-color: transparent;
+	z-index: 1105;
 }
 
 .lokdialog.ui-dialog-content.ui-widget-content {
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 0914d9b6..0001aa5d 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -4,29 +4,98 @@
 
 /* global vex $ map */
 L.Control.LokDialog = L.Control.extend({
+
+	dialogIdPrefix: 'lokdialog-',
+
 	onAdd: function (map) {
 		map.on('dialogpaint', this._onDialogPaint, this);
 		map.on('dialogchildpaint', this._onDialogChildPaint, this);
 		map.on('dialogchild', this._onDialogChildMsg, this);
 		map.on('dialog', this._onDialogMsg, this);
 		map.on('opendialog', this._openDialog, this);
+		map.on('docloaded', this._docLoaded, this);
 	},
 
 	_dialogs: {},
 
+	_docLoaded: function(e) {
+		if (!e.status) {
+			$('.lokdialog_container').remove();
+			$('.lokdialogchild-canvas').remove();
+		}
+	},
+
 	_isOpen: function(dialogId) {
 		return this._dialogs[dialogId] &&
 			this._dialogs[dialogId].open &&
 			$('#' + dialogId).length > 0;
 	},
 
+	_toRawDlgId: function(dialogId) {
+		return dialogId.replace(this.dialogIdPrefix, '');
+	},
+
+	// Create a rectangle string of form "x,y,width,height"
+	// if params are missing, assumes 0,0,dialog width, dialog height
+	_createRectStr: function(x, y, width, height) {
+		if (!width)
+			width = this._width;
+		if (!height)
+			height = this._height;
+		if (!x)
+			x = 0;
+		if (!y)
+			y = 0;
+
+		return [x, y, width, height].join(',');
+	},
+
+	_sendDialogCommand: function(dialogId, rectangle, child) {
+		dialogId = dialogId.replace(this.dialogIdPrefix, '');
+
+		var dialogCmd = 'dialog';
+		if (child)
+			dialogCmd = 'dialogchild';
+
+		if (rectangle)
+			rectangle = rectangle.replace(/ /g, '');
+
+		this._map._socket.sendMessage(dialogCmd + ' ' + dialogId + (rectangle ? ' rectangle=' + rectangle : ''));
+	},
+
+	_isRectangleValid: function(rect) {
+		rect = rect.split(',');
+		if (parseInt(rect[0]) < 0 || parseInt(rect[1]) < 0 || parseInt(rect[2]) < 0 || parseInt(rect[3]) < 0)
+			return false;
+		return true;
+	},
+
 	_onDialogMsg: function(e) {
-		e.dialogId = e.dialogId.replace('.uno:', '');
-		if (e.action === 'invalidate') {
-			// ignore any invalidate callbacks when we have closed the dialog
+		e.dialogId = this.dialogIdPrefix + e.dialogId;
+		if (e.action === 'created') {
+			this._width = parseInt(e.size.split(',')[0]);
+			this._height = parseInt(e.size.split(',')[1]);
+			this._launchDialog(e.dialogId);
+			this._sendDialogCommand(e.dialogId, this._createRectStr());
+		} else if (e.action === 'invalidate') {
 			if (this._isOpen(e.dialogId)) {
-				this._map.sendDialogCommand(e.dialogId, e.rectangle);
+				if (!this._isRectangleValid(e.rectangle))
+					return;
+
+				if (!e.rectangle)
+					e.rectangle = '0,0,' + this._width + ',' + this._height;
+				this._sendDialogCommand(e.dialogId, e.rectangle);
 			}
+		} else if (e.action === 'size_changed') {
+			this._width = parseInt(e.size.split(',')[0]);
+			this._height = parseInt(e.size.split(',')[1]);
+
+			// FIXME: we don't really have to destroy and launch the dialog again but do it for
+			// now because the size sent to us previously in 'created' cb is not correct
+			$('#' + e.dialogId).remove();
+			this._launchDialog(e.dialogId);
+			$('#' + e.dialogId).dialog('option', 'title', this._title);
+			this._sendDialogCommand(e.dialogId, this._createRectStr());
 		} else if (e.action === 'cursor_invalidate') {
 			if (this._isOpen(e.dialogId) && !!e.rectangle) {
 				var rectangle = e.rectangle.split(',');
@@ -38,6 +107,9 @@ L.Control.LokDialog = L.Control.extend({
 				// set the position of the lokdialog-cursor
 				$(this._dialogs[e.dialogId].cursor).css({left: x, top: y});
 			}
+		} else if (e.action === 'title_changed') {
+			this._title = e.title;
+			$('#' + e.dialogId).dialog('option', 'title', e.title);
 		} else if (e.action === 'cursor_visible') {
 			var visible = e.visible === 'true';
 			if (visible)
@@ -50,31 +122,24 @@ L.Control.LokDialog = L.Control.extend({
 	},
 
 	_openDialog: function(e) {
-		e.dialogId = e.dialogId.replace('.uno:', '');
-		this._dialogs[e.dialogId] = {open: true};
-
-		this._map.sendDialogCommand(e.dialogId);
+		this._map.sendUnoCommand(e.uno);
 	},
 
 	_launchDialogCursor: function(dialogId) {
-		if (!this._isOpen(dialogId))
-			return;
-
 		this._dialogs[dialogId].cursor = L.DomUtil.create('div', 'leaflet-cursor-container', L.DomUtil.get(dialogId));
 		var cursor = L.DomUtil.create('div', 'leaflet-cursor lokdialog-cursor', this._dialogs[dialogId].cursor);
 		cursor.id = dialogId + '-cursor';
 		L.DomUtil.addClass(cursor, 'blinking-cursor');
 	},
 
-	_launchDialog: function(dialogId, width, height) {
+	_launchDialog: function(dialogId) {
 		var canvas = '<div class="lokdialog" style="padding: 0px; margin: 0px; overflow: hidden;" id="' + dialogId + '">' +
-		    '<canvas class="lokdialog_canvas" tabindex="0" id="' + dialogId + '-canvas" width="' + width + 'px" height="' + height + 'px"></canvas>' +
+		    '<canvas class="lokdialog_canvas" tabindex="0" id="' + dialogId + '-canvas" width="' + this._width + 'px" height="' + this._height + 'px"></canvas>' +
 		    '</div>';
 		$(document.body).append(canvas);
 		var that = this;
 		$('#' + dialogId).dialog({
-			width: width,
-			height: 'auto',
+			width: this._width,
 			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
 			modal: false,
 			closeOnEscape: true,
@@ -85,10 +150,11 @@ L.Control.LokDialog = L.Control.extend({
 			}
 		});
 
+		this._dialogs[dialogId] = { open: true };
+
 		// don't make 'TAB' focus on this button; we want to cycle focus in the lok dialog with each TAB
 		$('.lokdialog_container button.ui-dialog-titlebar-close').attr('tabindex', '-1').blur();
 
-		// attach the mouse/key events
 		$('#' + dialogId + '-canvas').on('mousedown', function(e) {
 			var buttons = 0;
 			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
@@ -97,7 +163,6 @@ L.Control.LokDialog = L.Control.extend({
 			var modifier = 0;
 			that._postDialogMouseEvent('buttondown', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
 		});
-
 		$('#' + dialogId + '-canvas').on('mouseup', function(e) {
 			var buttons = 0;
 			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
@@ -106,42 +171,30 @@ L.Control.LokDialog = L.Control.extend({
 			var modifier = 0;
 			that._postDialogMouseEvent('buttonup', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
 		});
-
 		$('#' + dialogId + '-canvas').on('keyup keypress keydown', function(e) {
 			e.dialogId = dialogId;
 			that._handleDialogKeyEvent(e);
 		});
-
 		$('#' + dialogId + '-canvas').on('contextmenu', function() {
 			return false;
 		});
 
-		// set the dialog's cursor
 		this._launchDialogCursor(dialogId);
-
-		if (!this._dialogs[dialogId] || !this._dialogs[dialogId].open)
-			this._dialogs[dialogId] = { open: true };
 	},
 
 	_postDialogMouseEvent: function(type, dialogid, x, y, count, buttons, modifier) {
-		if (!dialogid.startsWith('.uno:'))
-			dialogid = '.uno:' + dialogid;
-
-		this._map._socket.sendMessage('dialogmouse dialogid=' + dialogid +  ' type=' + type +
+		this._map._socket.sendMessage('dialogmouse dialogid=' + this._toRawDlgId(dialogid) +  ' type=' + type +
 		                              ' x=' + x + ' y=' + y + ' count=' + count +
 		                              ' buttons=' + buttons + ' modifier=' + modifier);
 	},
 
 	_postDialogKeyboardEvent: function(type, dialogid, charcode, keycode) {
-		this._map._socket.sendMessage('dialogkey dialogid=' + dialogid + ' type=' + type +
+		this._map._socket.sendMessage('dialogkey dialogid=' + this._toRawDlgId(dialogid) + ' type=' + type +
 		                              ' char=' + charcode + ' key=' + keycode);
 	},
 
 	_postDialogChildMouseEvent: function(type, dialogid, x, y, count, buttons, modifier) {
-		if (!dialogid.startsWith('.uno:'))
-			dialogid = '.uno:' + dialogid;
-
-		this._map._socket.sendMessage('dialogchildmouse dialogid=' + dialogid +  ' type=' + type +
+		this._map._socket.sendMessage('dialogchildmouse dialogid=' + this._toRawDlgId(dialogid) +  ' type=' + type +
 		                              ' x=' + x + ' y=' + y + ' count=' + count +
 		                              ' buttons=' + buttons + ' modifier=' + modifier);
 	},
@@ -193,7 +246,6 @@ L.Control.LokDialog = L.Control.extend({
 		if (!this._isOpen(dialogId))
 			return;
 
-		$('#' + dialogId).dialog('option', 'title', decodeURIComponent(title));
 		var img = new Image();
 		var canvas = document.getElementById(dialogId + '-canvas');
 		var ctx = canvas.getContext('2d');
@@ -211,39 +263,42 @@ L.Control.LokDialog = L.Control.extend({
 		img.src = imgData;
 	},
 
-	_isSameSize: function(dialogId, newWidth, newHeight) {
-		var ret = false;
-		if (this._isOpen(dialogId))
-		{
-			var oldWidth = $('#' + dialogId + '-canvas').width();
-			var oldHeight = $('#' + dialogId + '-canvas').height();
-			if (oldWidth == newWidth && oldHeight == newHeight)
-				ret = true;
-		}
-
-		return ret;
-	},
-
 	// Binary dialog msg recvd from core
 	_onDialogPaint: function (e) {
-		var dialogId = e.id.replace('.uno:', '');
-		// is our request to open dialog still valid?
-		if (!this._dialogs[dialogId] || !this._dialogs[dialogId].open)
+		var dialogId = this.dialogIdPrefix + e.id;
+		if (!this._isOpen(dialogId))
 			return;
 
-		if (!this._isOpen(dialogId)) {
-			this._launchDialog(dialogId, e.dialogWidth, e.dialogHeight);
-		} else if (!this._isSameSize(dialogId, e.dialogWidth, e.dialogHeight)) {
-			var canvas = document.getElementById(dialogId + '-canvas');
-			canvas.width = e.dialogWidth;
-			canvas.height = e.dialogHeight;
+		// FIXME: as a precaution, if we get larger width or height here than what we got in 'created'
+		// callback, then adjust the dialog canvas size
+		var changed = false;
+		var canvas = document.getElementById(dialogId + '-canvas');
+		if (e.width > this._width) {
+			changed = true;
+			this._width = e.width;
+			canvas.width = e.width;
+			$('#' + dialogId).dialog('option', 'width', e.width);
+		}
+
+		if (e.height > this._height) {
+			changed = true;
+			this._height = e.height;
+			canvas.height = e.height;
+			$('#' + dialogId).dialog('option', 'height', e.height);
+		}
+
+		if (changed) {
+			this._sendDialogCommand(dialogId, this._createRectStr());
+			return;
 		}
 
 		this._paintDialog(dialogId, e.title, e.rectangle, e.dialog);
 	},
 
+	// Dialog Child Methods
+
 	_onDialogChildPaint: function(e) {
-		var dialogId = e.id.replace('.uno:', '');
+		var dialogId = this.dialogIdPrefix + e.id;
 		var img = new Image();
 		var canvas = document.getElementById(dialogId + '-floating');
 		canvas.width = e.width;
@@ -265,6 +320,9 @@ L.Control.LokDialog = L.Control.extend({
 
 	_onDialogChildClose: function(dialogId) {
 		$('#' + dialogId + '-floating').remove();
+		// remove any extra height allocated for the parent container
+		var canvasHeight = document.getElementById(dialogId + '-canvas').height;
+		$('#' + dialogId).height(canvasHeight + 'px');
 	},
 
 	_isDialogChildUnchanged: function(dialogId, left, top) {
@@ -286,24 +344,16 @@ L.Control.LokDialog = L.Control.extend({
 		return true;
 	},
 
-	_launchDialogChild: function(e) {
-		var positions = e.position.split(',');
-		var left = parseInt(positions[0]);
-		var top = parseInt(positions[1]);
-		// ignore spurious "0, 0" dialog child position recvd from backend
-		if (e.position === '0, 0' || this._isDialogChildUnchanged(e.dialogId, left, top)) {
-			// ignore
-			return;
-		}
+	_removeDialogChild: function(dialogId) {
+		$('#' + dialogId + '-floating').remove();
+	},
 
-		// remove any existing floating element if there's any
-		$('#' + e.dialogId + '-floating').remove();
-		var floatingCanvas = '<canvas class="lokdialogchild-canvas" id="' + e.dialogId + '-floating"></canvas>';
-		$('#' + e.dialogId).append(floatingCanvas);
-		$('#' + e.dialogId + '-floating').css({position: 'absolute', left: left, top: top});
+	_createDialogChild: function(dialogId, top, left) {
+		var floatingCanvas = '<canvas class="lokdialogchild-canvas" id="' + dialogId + '-floating"></canvas>';
+		$('#' + dialogId).append(floatingCanvas);
+		$('#' + dialogId + '-floating').css({position: 'absolute', left: left, top: top});
 
 		var that = this;
-		var dialogId = e.dialogId;
 		// attach events
 		$('#' + dialogId + '-floating').on('mousedown', function(e) {
 			var buttons = 0;
@@ -332,13 +382,26 @@ L.Control.LokDialog = L.Control.extend({
 		});
 	},
 
+	_launchDialogChildIfRequired: function(e) {
+		var positions = e.position.split(',');
+		var left = parseInt(positions[0]);
+		var top = parseInt(positions[1]);
+
+		if (e.position === '0, 0' || // FIXME: we get incorrect "0, 0" position for floating windows as first message
+		    this._isDialogChildUnchanged(e.dialogId, left, top)) // no need to create the html element; we can just repaint it
+			return;
+
+		this._removeDialogChild(e.dialogId);
+		this._createDialogChild(e.dialogId, top, left);
+	},
+
 	_onDialogChildMsg: function(e) {
-		e.dialogId = e.dialogId.replace('.uno:', '');
+		e.dialogId = this.dialogIdPrefix + e.dialogId;
 		if (e.action === 'invalidate') {
 			if (this._isOpen(e.dialogId))
 			{
-				this._map.sendDialogCommand(e.dialogId, false /* no json */, true /* dialog child*/);
-				this._launchDialogChild(e);
+				this._sendDialogCommand(e.dialogId, false /* no json */, true /* dialog child*/);
+				this._launchDialogChildIfRequired(e);
 			}
 		} else if (e.action === 'close') {
 			this._onDialogChildClose(e.dialogId);
diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 9ea8fcc9..28d5491f 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -798,7 +798,7 @@ L.Control.Menubar = L.Control.extend({
 		} else if (type === 'action') {
 			self._executeAction(item);
 		} else if (type === 'dialog') {
-			map.fire('opendialog', {dialogId: $(item).data('id')});
+			map.fire('opendialog', {uno: $(item).data('id')});
 		}
 
 		if ($(item).data('id') !== 'insertcomment')
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index 66514d7b..337188a0 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -142,20 +142,6 @@ L.Map.include({
 		}
 	},
 
-	sendDialogCommand: function (command, rectangle, child) {
-		if (this._permission === 'edit') {
-			if (!command.startsWith('.uno:'))
-				command = '.uno:' + command;
-			var dialogCmd = 'dialog';
-			if (child)
-				dialogCmd = 'dialogchild';
-			// make sure there are no spaces in rectangle
-			if (rectangle)
-				rectangle = rectangle.replace(/ /g, '');
-			this._socket.sendMessage(dialogCmd + ' ' + command + (rectangle ? ' rectangle=' + rectangle : ''));
-		}
-	},
-
 	toggleCommandState: function (unoState) {
 		if (this._permission === 'edit') {
 			if (!unoState.startsWith('.uno:')) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 7ee9c90c..3d416b19 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1200,8 +1200,8 @@ L.TileLayer = L.GridLayer.extend({
 			dialog: img,
 			title: command.title,
 			// TODO: add id too
-			dialogWidth: command.dialogwidth,
-			dialogHeight: command.dialogheight,
+			width: command.width,
+			height: command.height,
 			rectangle: command.rectangle
 		});
 	},
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index 665d0bfe..5ec14b4f 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -384,7 +384,7 @@ public:
         return _tileQueue;
     }
 
-    bool sendTextFrame(const std::string& /*message*/) override
+    bool sendFrame(const char* /*buffer*/, int /*length*/, int /*flags*/) override
     {
         return true;
     }
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index d2951538..663a55fc 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -222,10 +222,6 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     {
         return sendTile(buffer, length, tokens, docBroker);
     }
-    else if (tokens[0] == "dialog" || tokens[0] == "dialogchild")
-    {
-        docBroker->handleDialogRequest(std::string(buffer, length));
-    }
     else if (tokens[0] == "tilecombine")
     {
         return sendCombinedTiles(buffer, length, tokens, docBroker);
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 69cd515b..fad24344 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1140,14 +1140,6 @@ bool DocumentBroker::handleInput(const std::vector<char>& payload)
         {
             handleTileCombinedResponse(payload);
         }
-        else if (command == "dialogpaint:")
-        {
-            handleDialogPaintResponse(payload, false);
-        }
-        else if (command == "dialogchildpaint:")
-        {
-            handleDialogPaintResponse(payload, true);
-        }
         else if (command == "errortoall:")
         {
             LOG_CHECK_RET(message->tokens().size() == 3, false);
@@ -1239,13 +1231,6 @@ void DocumentBroker::handleTileRequest(TileDesc& tile,
     _debugRenderedTileCount++;
 }
 
-void DocumentBroker::handleDialogRequest(const std::string& dialogCmd)
-{
-    assertCorrectThread();
-    std::unique_lock<std::mutex> lock(_mutex);
-    _childProcess->sendTextFrame(dialogCmd);
-}
-
 void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
                                                const std::shared_ptr<ClientSession>& session)
 {
@@ -1346,31 +1331,6 @@ void DocumentBroker::handleTileResponse(const std::vector<char>& payload)
     }
 }
 
-void DocumentBroker::handleDialogPaintResponse(const std::vector<char>& payload, bool child)
-{
-    const std::string firstLine = getFirstLine(payload);
-    LOG_DBG("Handling " << (child ? "dialogchildpaint" : "dialogpaint") << " " << firstLine);
-
-    const auto length = payload.size();
-    if (firstLine.size() < static_cast<std::string::size_type>(length) - 1)
-    {
-        const auto buffer = payload.data();
-        const auto offset = firstLine.size() + 1;
-
-        auto msgPayload = std::make_shared<Message>(firstLine,
-                                                    Message::Dir::Out,
-                                                    payload.size());
-        msgPayload->append("\n", 1);
-        msgPayload->append(buffer + offset, payload.size() - offset);
-
-        std::unique_lock<std::mutex> lock(_mutex);
-        for (const auto& sessionIt : _sessions)
-        {
-            sessionIt.second->enqueueSendMessage(msgPayload);
-        }
-    }
-}
-
 void DocumentBroker::handleTileCombinedResponse(const std::vector<char>& payload)
 {
     const std::string firstLine = getFirstLine(payload);


More information about the Libreoffice-commits mailing list