[Libreoffice-commits] online.git: 9 commits - 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
Tue Nov 28 16:51:49 UTC 2017


 kit/ChildSession.cpp                       |  142 +++++++++++++++++++++++---
 kit/ChildSession.hpp                       |   15 ++
 kit/Kit.cpp                                |  122 +---------------------
 loleaflet/dist/loleaflet.css               |    1 
 loleaflet/src/control/Control.LokDialog.js |  158 ++++++++++++++++++-----------
 loleaflet/src/control/Control.Menubar.js   |    2 
 loleaflet/src/control/Toolbar.js           |   14 --
 test/WhiteBoxTests.cpp                     |    2 
 wsd/ClientSession.cpp                      |    4 
 wsd/DocumentBroker.cpp                     |   40 -------
 10 files changed, 253 insertions(+), 247 deletions(-)

New commits:
commit c996c04d8e2444a64784cf1ad8d7efa8519f4267
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue Nov 28 02:13:05 2017 +0530

    lokdialog: Remove dialog artifacts after socket disconnection
    
    Change-Id: I199178761cfa715043185964142c9156e1a0b50f

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 302005b7..853972a8 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -13,10 +13,18 @@ L.Control.LokDialog = L.Control.extend({
 		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 &&
commit b6117c2182082ef2a76afe105f5de30f69da534e
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue Nov 28 01:51:28 2017 +0530

    lokdialog: Handle size_changed; factor out common code
    
    Change-Id: Ie4374ae1e2f5ba29b239cb37eb9fe4c5991094b0

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index d7bbb7f1..302005b7 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -27,6 +27,21 @@ L.Control.LokDialog = L.Control.extend({
 		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, '');
 
@@ -45,16 +60,21 @@ L.Control.LokDialog = L.Control.extend({
 		if (e.action === 'created') {
 			this._width = parseInt(e.size.split(',')[0]);
 			this._height = parseInt(e.size.split(',')[1]);
+
 			this._launchDialog(e.dialogId);
-			var boundsString = '0,0,' + this._width + ',' + this._height; // no spaces in string
-			this._sendDialogCommand(e.dialogId, boundsString);
+			this._sendDialogCommand(e.dialogId, this._createRectStr());
 		} else if (e.action === 'invalidate') {
 			// ignore any invalidate callbacks when we have closed the dialog
 			if (this._isOpen(e.dialogId)) {
 				if (!e.rectangle)
-					e.rectangle = '0,0' + this._width + ',' + this._height;
+					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]);
+
+			this._sendDialogCommand(e.dialogId, this._createRectStr());
 		} else if (e.action === 'cursor_invalidate') {
 			if (this._isOpen(e.dialogId) && !!e.rectangle) {
 				var rectangle = e.rectangle.split(',');
@@ -88,14 +108,14 @@ L.Control.LokDialog = L.Control.extend({
 		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,
+			width: this._width,
 			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
 			modal: false,
 			closeOnEscape: true,
commit 6942536c678485d96fa19edb603877ee881d0c34
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Tue Nov 28 01:37:09 2017 +0530

    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

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 590214df..d7bbb7f1 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -43,17 +43,16 @@ L.Control.LokDialog = L.Control.extend({
 	_onDialogMsg: function(e) {
 		e.dialogId = this.dialogIdPrefix + e.dialogId;
 		if (e.action === 'created') {
-			var width = parseInt(e.size.split(',')[0]);
-			var height = parseInt(e.size.split(',')[1]);
-			this._launchDialog(e.dialogId, width, height);
-			// var boundsString = '0,0,' + width + ',' + height; // no spaces in string
-			// FIXME: we should pass the rectangle here but we do not yet get the correct size
-			// of the dialog from the backend, so best not to mention any rectangle here
-			// for the first time
-			this._sendDialogCommand(e.dialogId);/* boundsString */
+			this._width = parseInt(e.size.split(',')[0]);
+			this._height = parseInt(e.size.split(',')[1]);
+			this._launchDialog(e.dialogId);
+			var boundsString = '0,0,' + this._width + ',' + this._height; // no spaces in string
+			this._sendDialogCommand(e.dialogId, boundsString);
 		} else if (e.action === 'invalidate') {
 			// ignore any invalidate callbacks when we have closed the dialog
 			if (this._isOpen(e.dialogId)) {
+				if (!e.rectangle)
+					e.rectangle = '0,0' + this._width + ',' + this._height;
 				this._sendDialogCommand(e.dialogId, e.rectangle);
 			}
 		} else if (e.action === 'cursor_invalidate') {
commit a98d0f6f2fd6862a768280ca8fed2c66b58addaf
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Nov 27 20:42:48 2017 +0530

    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

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index cc5bdeb6..590214df 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -45,10 +45,12 @@ L.Control.LokDialog = L.Control.extend({
 		if (e.action === 'created') {
 			var width = parseInt(e.size.split(',')[0]);
 			var height = parseInt(e.size.split(',')[1]);
-			// make sure there are no <spaces> in the following string
-			var boundsString = '0,0,' + width + ',' + height;
 			this._launchDialog(e.dialogId, width, height);
-			this._sendDialogCommand(e.dialogId, boundsString);
+			// var boundsString = '0,0,' + width + ',' + height; // no spaces in string
+			// FIXME: we should pass the rectangle here but we do not yet get the correct size
+			// of the dialog from the backend, so best not to mention any rectangle here
+			// for the first time
+			this._sendDialogCommand(e.dialogId);/* boundsString */
 		} else if (e.action === 'invalidate') {
 			// ignore any invalidate callbacks when we have closed the dialog
 			if (this._isOpen(e.dialogId)) {
commit 91d6755781aac8aaeb9ed85b87a2880edd74c9b4
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Nov 27 19:12:56 2017 +0530

    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

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 {
commit e6dbbef8f588eb2d3982f299cca67ab267ebae96
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Nov 27 19:09:03 2017 +0530

    lokdialog: Restore dialog container size to original
    
    Change-Id: I3a7bb1ffdb4f50776868fee6eade44f194d290e0

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index a92fec95..cc5bdeb6 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -272,6 +272,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) {
commit 8ad03c61ee1a028da6e69bcd2dfeb512ca280c5b
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Nov 27 18:58:48 2017 +0530

    lokdialog: Change the dialog container width too
    
    Change-Id: If3f937da472db20c39f402562ad559080509956d

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 8affc461..a92fec95 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -95,7 +95,6 @@ L.Control.LokDialog = L.Control.extend({
 		var that = this;
 		$('#' + dialogId).dialog({
 			width: width,
-			height: 'auto',
 			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
 			modal: false,
 			closeOnEscape: true,
@@ -241,6 +240,8 @@ L.Control.LokDialog = L.Control.extend({
 			var canvas = document.getElementById(dialogId + '-canvas');
 			canvas.width = e.dialogWidth;
 			canvas.height = e.dialogHeight;
+
+			$('#' + dialogId).dialog('option', 'width', e.dialogWidth);
 		}
 
 		this._paintDialog(dialogId, e.title, e.rectangle, e.dialog);
commit a19121bbbc65124393fa9e429fec87554b015317
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Sat Nov 25 02:01:14 2017 +0530

    Adapt to dialog API changes in LOK
    
    ... and bypass some unncessary DocumentBroker handling of dialog
    messages.
    
    Change-Id: I378dff7a9786479baaa43f5fde9d30f35bc7f948

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 71feb376..3c1083c7 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.");
@@ -726,16 +730,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) ||
@@ -768,8 +779,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;
 }
@@ -785,16 +796,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},
@@ -830,10 +847,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");
@@ -900,6 +917,103 @@ 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;
+    char* pDialogTitle = nullptr;
+    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);
+
+        int dialogWidth = 0;
+        int dialogHeight = 0;
+        getLOKitDocument()->getDialogInfo(dialogId, &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 " << dialogId << " returned " << width << "X" << height
+                << "@(" << startX << "," << startY << ")"
+                << "and rendered in " << (timestamp.elapsed()/1000.)
+                << "ms (" << area / (timestamp.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 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 c949375c..b5b9209c 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -193,6 +193,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 ea348750..3b373a36 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -854,111 +854,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.");
@@ -1788,10 +1683,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/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index f64c19e4..8affc461 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -4,6 +4,9 @@
 
 /* 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);
@@ -20,12 +23,36 @@ L.Control.LokDialog = L.Control.extend({
 			$('#' + dialogId).length > 0;
 	},
 
+	_toRawDlgId: function(dialogId) {
+		return dialogId.replace(this.dialogIdPrefix, '');
+	},
+
+	_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 : ''));
+	},
+
 	_onDialogMsg: function(e) {
-		e.dialogId = e.dialogId.replace('.uno:', '');
-		if (e.action === 'invalidate') {
+		e.dialogId = this.dialogIdPrefix + e.dialogId;
+		if (e.action === 'created') {
+			var width = parseInt(e.size.split(',')[0]);
+			var height = parseInt(e.size.split(',')[1]);
+			// make sure there are no <spaces> in the following string
+			var boundsString = '0,0,' + width + ',' + height;
+			this._launchDialog(e.dialogId, width, height);
+			this._sendDialogCommand(e.dialogId, boundsString);
+		} else if (e.action === 'invalidate') {
 			// ignore any invalidate callbacks when we have closed the dialog
 			if (this._isOpen(e.dialogId)) {
-				this._map.sendDialogCommand(e.dialogId, e.rectangle);
+				this._sendDialogCommand(e.dialogId, e.rectangle);
 			}
 		} else if (e.action === 'cursor_invalidate') {
 			if (this._isOpen(e.dialogId) && !!e.rectangle) {
@@ -50,16 +77,10 @@ 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';
@@ -85,10 +106,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 +119,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 +127,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);
 	},
@@ -224,14 +233,11 @@ L.Control.LokDialog = L.Control.extend({
 
 	// 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)) {
+		if (!this._isSameSize(dialogId, e.dialogWidth, e.dialogHeight)) {
 			var canvas = document.getElementById(dialogId + '-canvas');
 			canvas.width = e.dialogWidth;
 			canvas.height = e.dialogHeight;
@@ -240,8 +246,10 @@ L.Control.LokDialog = L.Control.extend({
 		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;
@@ -284,24 +292,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;
@@ -330,13 +330,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 fda37f4b..eb825cfe 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -789,7 +789,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/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 586f1ea9..d7f6f1fc 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -221,10 +221,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);
commit 5355c52e109701d6a652c3fb20439a181ac652a6
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Sat Nov 25 01:59:48 2017 +0530

    Allow sending binary websocket frames from IDocument interface
    
    Change-Id: I89f304e69e0c7b5f87bd2883a775d5c256674ef0

diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 82c5b541..c949375c 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;
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index df2d6ce5..ea348750 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -1088,22 +1088,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() + ")" : ""));
         }
 
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;
     }


More information about the Libreoffice-commits mailing list