[Libreoffice-commits] online.git: Branch 'feature/lok_dialog' - 113 commits - bundled/include common/Crypto.cpp common/Crypto.hpp common/Message.hpp common/Seccomp.cpp common/Session.cpp common/Session.hpp common/Unit.hpp configure.ac .gitignore kit/ChildSession.cpp kit/ChildSession.hpp kit/DummyLibreOfficeKit.cpp kit/Kit.cpp kit/KitHelper.hpp loleaflet/build loleaflet/debug loleaflet/dist loleaflet/main.js loleaflet/Makefile.am loleaflet/po loleaflet/src loolkitconfig.xcu loolwsd-systemplate-setup loolwsd.xml.in Makefile.am net/Socket.hpp net/Ssl.cpp test/countloolkits.hpp test/httpcrashtest.cpp test/Makefile.am test/run_unit.sh.in test/test.cpp test/test.hpp test/UnitClient.cpp test/UnitOAuth.cpp test/UnitWOPI.cpp test/WhiteBoxTests.cpp test/WopiTestServer.hpp tools/Config.cpp tools/KitClient.cpp wsd/Admin.cpp wsd/Admin.hpp wsd/AdminModel.hpp wsd/Auth.hpp wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp wsd/LOOLWSD.hpp wsd/protocol.txt ws d/reference.txt wsd/Storage.cpp wsd/Storage.hpp

Pranav Kant (via logerrit) logerrit at kemper.freedesktop.org
Sat Dec 7 01:50:42 UTC 2019


Rebased ref, commits from common ancestor:
commit d7c81cf7c807bb4e2624a02bd304cb75a1924e78
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Wed Oct 25 00:10:45 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:03 2017 -0700

    factor out more code

diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 34bcb5672..e19f33528 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -223,11 +223,11 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     }
     else if (tokens[0] == "dialog")
     {
-        return sendDialog(buffer, length, tokens, docBroker);
+        return sendDialog(buffer, length, tokens, docBroker, false);
     }
     else if (tokens[0] == "dialogchild")
     {
-        return sendDialogChild(buffer, length, tokens, docBroker);
+        return sendDialog(buffer, length, tokens, docBroker, true);
     }
     else if (tokens[0] == "tilecombine")
     {
@@ -436,32 +436,20 @@ bool ClientSession::sendTile(const char * /*buffer*/, int /*length*/, const std:
 }
 
 bool ClientSession::sendDialog(const char * /*buffer*/, int /*length*/, const std::vector<std::string>& tokens,
-                               const std::shared_ptr<DocumentBroker>& docBroker)
+                               const std::shared_ptr<DocumentBroker>& docBroker, bool child)
 {
+    const std::string dialogCmd = child ? "dialogchild" : "dialog";
     try
     {
-        docBroker->handleDialogRequest(tokens[1], shared_from_this());
-    }
-    catch (const std::exception& exc)
-    {
-        LOG_ERR("Failed to process dialog command: " << exc.what());
-        return sendTextFrame("error: cmd=dialog kind=invalid");
-    }
-
-    return true;
-}
-
-bool ClientSession::sendDialogChild(const char * /*buffer*/, int /*length*/, const std::vector<std::string>& tokens,
-                                    const std::shared_ptr<DocumentBroker>& docBroker)
-{
-    try
-    {
-        docBroker->handleDialogChildRequest(tokens[1], shared_from_this());
+        if (child)
+            docBroker->handleDialogRequest(tokens[1], shared_from_this(), true);
+        else
+            docBroker->handleDialogRequest(tokens[1], shared_from_this(), false);
     }
     catch (const std::exception& exc)
     {
-        LOG_ERR("Failed to process dialogchild command: " << exc.what());
-        return sendTextFrame("error: cmd=dialogchild kind=invalid");
+        LOG_ERR("Failed to process " + dialogCmd + " command: " << exc.what());
+        return sendTextFrame("error: cmd=" + dialogCmd + " kind=invalid");
     }
 
     return true;
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 48ea303b2..b16fe448b 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -125,9 +125,7 @@ private:
     bool sendTile(const char* buffer, int length, const std::vector<std::string>& tokens,
                   const std::shared_ptr<DocumentBroker>& docBroker);
     bool sendDialog(const char* buffer, int length, const std::vector<std::string>& tokens,
-                    const std::shared_ptr<DocumentBroker>& docBroker);
-    bool sendDialogChild(const char* buffer, int length, const std::vector<std::string>& tokens,
-                         const std::shared_ptr<DocumentBroker>& docBroker);
+                    const std::shared_ptr<DocumentBroker>& docBroker, bool child);
     bool sendCombinedTiles(const char* buffer, int length, const std::vector<std::string>& tokens,
                            const std::shared_ptr<DocumentBroker>& docBroker);
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 56a8c5a49..76255287e 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1077,11 +1077,11 @@ bool DocumentBroker::handleInput(const std::vector<char>& payload)
         }
         else if (command == "dialogpaint:")
         {
-            handleDialogPaintResponse(payload);
+            handleDialogPaintResponse(payload, false);
         }
         else if (command == "dialogchildpaint:")
         {
-            handleDialogChildPaintResponse(payload);
+            handleDialogPaintResponse(payload, true);
         }
         else if (command == "errortoall:")
         {
@@ -1175,25 +1175,14 @@ void DocumentBroker::handleTileRequest(TileDesc& tile,
 }
 
 void DocumentBroker::handleDialogRequest(const std::string& dialogId,
-                                         const std::shared_ptr<ClientSession>& /*session*/)
+                                         const std::shared_ptr<ClientSession>& /*session*/,
+                                         bool child)
 {
     assertCorrectThread();
     std::unique_lock<std::mutex> lock(_mutex);
-
-    LOG_DBG("Sending dialog render request for dialog " << dialogId);
-    const std::string request = "dialog " + dialogId;
-    _childProcess->sendTextFrame(request);
-}
-
-void DocumentBroker::handleDialogChildRequest(const std::string& dialogId,
-                                              const std::shared_ptr<ClientSession>& /*session*/)
-{
-    assertCorrectThread();
-    std::unique_lock<std::mutex> lock(_mutex);
-
-    LOG_DBG("Sending dialog child render request for dialog " << dialogId);
-    const std::string request = "dialogchild " + dialogId;
-    _childProcess->sendTextFrame(request);
+    const std::string dialogCmd = child ? "dialogchild" : "dialog";
+    LOG_DBG("Sending " + dialogCmd +  " render request for dialog " << dialogId);
+    _childProcess->sendTextFrame(dialogCmd + " " + dialogId);
 }
 
 void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
@@ -1296,35 +1285,10 @@ void DocumentBroker::handleTileResponse(const std::vector<char>& payload)
     }
 }
 
-void DocumentBroker::handleDialogPaintResponse(const std::vector<char>& payload)
-{
-    const std::string firstLine = getFirstLine(payload);
-    LOG_DBG("Handling 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::handleDialogChildPaintResponse(const std::vector<char>& payload)
+void DocumentBroker::handleDialogPaintResponse(const std::vector<char>& payload, bool child)
 {
     const std::string firstLine = getFirstLine(payload);
-    LOG_DBG("Handling dialogchildpaint: " << firstLine);
+    LOG_DBG("Handling " << (child ? "dialogchildpaint" : "dialogpaint") << " " << firstLine);
 
     const auto length = payload.size();
     if (firstLine.size() < static_cast<std::string::size_type>(length) - 1)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 3f104eda9..75e297947 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -299,15 +299,13 @@ public:
     void handleTileRequest(TileDesc& tile,
                            const std::shared_ptr<ClientSession>& session);
     void handleDialogRequest(const std::string& dialogId,
-                             const std::shared_ptr<ClientSession>& session);
-    void handleDialogChildRequest(const std::string& dialogId,
-                                  const std::shared_ptr<ClientSession>& session);
+                             const std::shared_ptr<ClientSession>& session,
+                             bool child);
     void handleTileCombinedRequest(TileCombined& tileCombined,
                                    const std::shared_ptr<ClientSession>& session);
     void cancelTileRequests(const std::shared_ptr<ClientSession>& session);
     void handleTileResponse(const std::vector<char>& payload);
-    void handleDialogPaintResponse(const std::vector<char>& payload);
-    void handleDialogChildPaintResponse(const std::vector<char>& payload);
+    void handleDialogPaintResponse(const std::vector<char>& payload, bool child);
     void handleTileCombinedResponse(const std::vector<char>& payload);
 
     void destroyIfLastEditor(const std::string& id);
commit 19e1daf70981bd56935c2a35de8693565c5d12b9
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Tue Oct 24 18:28:28 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    lokdialog: Factor out renderDialog in kit.cpp

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 076ce7b25..d2ded17f7 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -845,7 +845,7 @@ public:
         ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
-    void renderDialog(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
+    void renderDialog(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws, bool child)
     {
         assert(ws && "Expected a non-null websocket.");
 
@@ -870,66 +870,19 @@ public:
         int nWidth = nCanvasWidth;
         int nHeight = nCanvasHeight;
         Timestamp timestamp;
-        _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
-        const double area = nWidth * nHeight;
-        const auto elapsed = timestamp.elapsed();
-        LOG_TRC("paintDialog for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
-                << " and rendered in " << (elapsed/1000.) <<
-                " ms (" << area / elapsed << " MP/s).");
-
-        const std::string response = "dialogpaint: id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\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, nWidth, nHeight, nCanvasWidth, nCanvasHeight, output, LOK_TILEMODE_RGBA))
-        {
-            //FIXME: Return error.
-            //sendTextFrame("error: cmd=tile kind=failure");
-
-            LOG_ERR("Failed to encode dialog into PNG.");
-            return;
-        }
-
-        LOG_TRC("Sending render-dialog response (" << output.size() << " bytes) for: " << response);
-        ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
-    }
-
-    void renderDialogChild(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
-    {
-        assert(ws && "Expected a non-null websocket.");
-
-        const int nCanvasWidth = 800;
-        const int nCanvasHeight = 600;
-        size_t pixmapDataSize = 4 * nCanvasWidth * nCanvasHeight;
-        std::vector<unsigned char> pixmap(pixmapDataSize);
-
-        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;
-        }
+        if (child)
+            _loKitDocument->paintActiveFloatingWindow(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
+        else
+            _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
 
-        int nWidth = nCanvasWidth;
-        int nHeight = nCanvasHeight;
-        Timestamp timestamp;
-        _loKitDocument->paintActiveFloatingWindow(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
         const double area = nWidth * nHeight;
         const auto elapsed = timestamp.elapsed();
-        LOG_TRC("paintActiveFloatingWindow for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
+        LOG_TRC((child ? std::string("paintActiveFloatingWindow") : std::string("paintDialog")) +
+                " for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
                 << " and rendered in " << (elapsed/1000.) <<
                 " ms (" << area / elapsed << " MP/s).");
 
-        const std::string response = "dialogchildpaint: id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
+        const std::string response = std::string(child ? "dialogchildpaint:" : "dialogpaint:") + " id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
         std::vector<char> output;
         output.reserve(response.size() + pixmapDataSize);
         output.resize(response.size());
@@ -938,14 +891,11 @@ public:
         // TODO: use png cache for dialogs too
         if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, nWidth, nHeight, nCanvasWidth, nCanvasHeight, output, LOK_TILEMODE_RGBA))
         {
-            //FIXME: Return error.
-            //sendTextFrame("error: cmd=tile kind=failure");
-
-            LOG_ERR("Failed to encode dialog into PNG.");
+            LOG_ERR("Failed to encode into PNG.");
             return;
         }
 
-        LOG_TRC("Sending render-dialogchild response (" << output.size() << " bytes) for: " << response);
+        LOG_TRC("Sending response (" << output.size() << " bytes) for: " << response);
         ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
@@ -1775,11 +1725,11 @@ private:
                 }
                 else if (tokens[0] == "dialog")
                 {
-                    renderDialog(tokens, _ws);
+                    renderDialog(tokens, _ws, false);
                 }
                 else if (tokens[0] == "dialogchild")
                 {
-                    renderDialogChild(tokens, _ws);
+                    renderDialog(tokens, _ws, true);
                 }
                 else if (LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                 {
commit c0c9e70add29163e9d168cd719951e34d4036eac
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Tue Oct 24 17:28:51 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    lokdialog: Factor out mouse and key events

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 9d0572262..c40a68822 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -261,23 +261,23 @@ bool ChildSession::_handleInput(const char *buffer, int length)
         }
         else if (tokens[0] == "key")
         {
-            return keyEvent(buffer, length, tokens);
+            return keyEvent(buffer, length, tokens, LokEventTargetEnum::Document);
         }
         else if (tokens[0] == "dialogkey")
         {
-            return dialogKeyEvent(buffer, length, tokens);
+            return keyEvent(buffer, length, tokens, LokEventTargetEnum::Dialog);
         }
         else if (tokens[0] == "mouse")
         {
-            return mouseEvent(buffer, length, tokens);
+            return mouseEvent(buffer, length, tokens, LokEventTargetEnum::Document);
         }
         else if (tokens[0] == "dialogmouse")
         {
-            return dialogMouseEvent(buffer, length, tokens);
+            return mouseEvent(buffer, length, tokens, LokEventTargetEnum::Dialog);
         }
         else if (tokens[0] == "dialogchildmouse")
         {
-            return dialogChildMouseEvent(buffer, length, tokens);
+            return mouseEvent(buffer, length, tokens, LokEventTargetEnum::DialogChild);
         }
         else if (tokens[0] == "uno")
         {
@@ -717,17 +717,28 @@ bool ChildSession::insertFile(const char* /*buffer*/, int /*length*/, const std:
     return true;
 }
 
-bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/,
+                            const std::vector<std::string>& tokens,
+                            const LokEventTargetEnum target)
 {
     int type, charcode, keycode;
-    if (tokens.size() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
+    std::string dialogId;
+    unsigned counter = 1;
+    unsigned minTotal = 4; // cmdname, 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() != minTotal ||
+        !getTokenKeyword(tokens[counter++], "type",
                          {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
                          type) ||
-        !getTokenInteger(tokens[2], "char", charcode) ||
-        !getTokenInteger(tokens[3], "key", keycode))
+        !getTokenInteger(tokens[counter++], "char", charcode) ||
+        !getTokenInteger(tokens[counter++], "key", keycode))
     {
-        sendTextFrame("error: cmd=key kind=syntax");
+        sendTextFrame("error: cmd=" + std::string(tokens[0]) + "  kind=syntax");
         return false;
     }
 
@@ -748,47 +759,20 @@ bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/, const std::v
     }
 
     std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
-
-    getLOKitDocument()->setView(_viewId);
-
-    getLOKitDocument()->postKeyEvent(type, charcode, keycode);
-
-    return true;
-}
-
-bool ChildSession::dialogKeyEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
-{
-    int type, charcode, keycode;
-    std::string dialogId;
-    if (tokens.size() != 5 ||
-        !getTokenString(tokens[1], "dialogid", dialogId) ||
-        !getTokenKeyword(tokens[2], "type",
-                         {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
-                         type) ||
-        !getTokenInteger(tokens[3], "char", charcode) ||
-        !getTokenInteger(tokens[4], "key", keycode))
-    {
-        sendTextFrame("error: cmd=dialogkey kind=syntax");
-        return false;
-    }
-
-    // Ctrl+Tab switching browser tabs,
-    // Doesn't insert tabs.
-    constexpr auto KEY_CTRL = 0x2000;
-    constexpr auto KEY_TAB = 0x0502;
-    if (keycode == (KEY_CTRL | KEY_TAB))
+    if (target == LokEventTargetEnum::Document)
     {
-        return true;
+        getLOKitDocument()->setView(_viewId);
+        getLOKitDocument()->postKeyEvent(type, charcode, keycode);
     }
-
-    std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
-
-    getLOKitDocument()->postDialogKeyEvent(dialogId.c_str(), type, charcode, keycode);
+    else if (!dialogId.empty())
+        getLOKitDocument()->postDialogKeyEvent(dialogId.c_str(), type, charcode, keycode);
 
     return true;
 }
 
-bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/,
+                              const std::vector<std::string>& tokens,
+                              const LokEventTargetEnum target)
 {
     int type, x, y, count;
     bool success = true;
@@ -797,115 +781,60 @@ bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/, const std:
     int buttons = 1; // left button
     int modifier = 0;
 
-    if (tokens.size() < 5 ||
-        !getTokenKeyword(tokens[1], "type",
+    std::string dialogId;
+    unsigned counter = 1;
+    unsigned minTotal = 5; // cmdname, 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() < minTotal ||
+        !getTokenKeyword(tokens[counter++], "type",
                          {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
                           {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
                           {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
                          type) ||
-        !getTokenInteger(tokens[2], "x", x) ||
-        !getTokenInteger(tokens[3], "y", y) ||
-        !getTokenInteger(tokens[4], "count", count))
+        !getTokenInteger(tokens[counter++], "x", x) ||
+        !getTokenInteger(tokens[counter++], "y", y) ||
+        !getTokenInteger(tokens[counter++], "count", count))
     {
         success = false;
     }
 
     // compatibility with older loleaflets
-    if (success && tokens.size() > 5 && !getTokenInteger(tokens[5], "buttons", buttons))
+    if (success && tokens.size() > counter && !getTokenInteger(tokens[counter++], "buttons", buttons))
         success = false;
 
     // compatibility with older loleaflets
-    if (success && tokens.size() > 6 && !getTokenInteger(tokens[6], "modifier", modifier))
+    if (success && tokens.size() > counter && !getTokenInteger(tokens[counter++], "modifier", modifier))
         success = false;
 
     if (!success)
     {
-        sendTextFrame("error: cmd=mouse kind=syntax");
+        sendTextFrame("error: cmd=" +  std::string(tokens[0]) + " kind=syntax");
         return false;
     }
 
     std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
 
-    getLOKitDocument()->setView(_viewId);
-
-    getLOKitDocument()->postMouseEvent(type, x, y, count, buttons, modifier);
-
-    return true;
-}
-
-bool ChildSession::dialogMouseEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
-{
-    int type, x, y, count;
-    bool success = true;
-
-    // default values for compatibility reasons with older loleaflets
-    int buttons = 1; // left button
-    int modifier = 0;
-    std::string dialogId;
-    if (tokens.size() < 6 ||
-        !getTokenString(tokens[1], "dialogid", dialogId) ||
-        !getTokenKeyword(tokens[2], "type",
-                         {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
-                          {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
-                          {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
-                         type) ||
-        !getTokenInteger(tokens[3], "x", x) ||
-        !getTokenInteger(tokens[4], "y", y) ||
-        !getTokenInteger(tokens[5], "count", count) ||
-        !getTokenInteger(tokens[6], "buttons", buttons) ||
-        !getTokenInteger(tokens[7], "modifier", modifier))
-    {
-        success = false;
-    }
-
-    if (!success)
+    switch (target)
     {
-        sendTextFrame("error: cmd=dialogmouse kind=syntax");
-        return false;
-    }
-
-    std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
-
-    getLOKitDocument()->postDialogMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
-
-    return true;
-}
-
-bool ChildSession::dialogChildMouseEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
-{
-    int type, x, y, count;
-    bool success = true;
-
-    // default values for compatibility reasons with older loleaflets
-    int buttons = 1; // left button
-    int modifier = 0;
-    std::string dialogId;
-    if (tokens.size() < 6 ||
-        !getTokenString(tokens[1], "dialogid", dialogId) ||
-        !getTokenKeyword(tokens[2], "type",
-                         {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
-                          {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
-                          {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
-                         type) ||
-        !getTokenInteger(tokens[3], "x", x) ||
-        !getTokenInteger(tokens[4], "y", y) ||
-        !getTokenInteger(tokens[5], "count", count) ||
-        !getTokenInteger(tokens[6], "buttons", buttons) ||
-        !getTokenInteger(tokens[7], "modifier", modifier))
-    {
-        success = false;
-    }
-
-    if (!success)
-    {
-        sendTextFrame("error: cmd=dialogchildmouse kind=syntax");
-        return false;
+    case LokEventTargetEnum::Document:
+        getLOKitDocument()->setView(_viewId);
+        getLOKitDocument()->postMouseEvent(type, x, y, count, buttons, modifier);
+        break;
+    case LokEventTargetEnum::Dialog:
+        getLOKitDocument()->postDialogMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+        break;
+    case LokEventTargetEnum::DialogChild:
+        getLOKitDocument()->postDialogChildMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+        break;
+    default:
+        assert(false && "Unsupported mouse target type");
     }
 
-    std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
-
-    getLOKitDocument()->postDialogChildMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
-
     return true;
 }
 
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index b9079e565..82c5b5415 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -25,6 +25,13 @@
 
 class ChildSession;
 
+enum class LokEventTargetEnum
+{
+    Document,
+    Dialog,
+    DialogChild
+};
+
 /// An abstract interface that defines the
 /// DocumentManager interface and functionality.
 class IDocumentManager
@@ -174,11 +181,9 @@ private:
     bool getTextSelection(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool paste(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool insertFile(const char* buffer, int length, const std::vector<std::string>& tokens);
-    bool keyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool keyEvent(const char* buffer, int length, const std::vector<std::string>& tokens, const LokEventTargetEnum target);
     bool dialogKeyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
-    bool mouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
-    bool dialogMouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
-    bool dialogChildMouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool mouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens, const LokEventTargetEnum target);
     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);
commit 6c762efa7d5ffe5dfa33a32cecc2ebc4ffa3bde2
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Mon Oct 23 13:40:02 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    lokdialog: Make key events work on dialog + canvas use
    
    Start using canvas instead of img element directly.

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 9ca37b584..9d0572262 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -219,6 +219,7 @@ bool ChildSession::_handleInput(const char *buffer, int length)
                tokens[0] == "paste" ||
                tokens[0] == "insertfile" ||
                tokens[0] == "key" ||
+               tokens[0] == "dialogkey" ||
                tokens[0] == "mouse" ||
                tokens[0] == "dialogmouse" ||
                tokens[0] == "dialogchildmouse" ||
@@ -262,6 +263,10 @@ bool ChildSession::_handleInput(const char *buffer, int length)
         {
             return keyEvent(buffer, length, tokens);
         }
+        else if (tokens[0] == "dialogkey")
+        {
+            return dialogKeyEvent(buffer, length, tokens);
+        }
         else if (tokens[0] == "mouse")
         {
             return mouseEvent(buffer, length, tokens);
@@ -751,6 +756,38 @@ bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/, const std::v
     return true;
 }
 
+bool ChildSession::dialogKeyEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+{
+    int type, charcode, keycode;
+    std::string dialogId;
+    if (tokens.size() != 5 ||
+        !getTokenString(tokens[1], "dialogid", dialogId) ||
+        !getTokenKeyword(tokens[2], "type",
+                         {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
+                         type) ||
+        !getTokenInteger(tokens[3], "char", charcode) ||
+        !getTokenInteger(tokens[4], "key", keycode))
+    {
+        sendTextFrame("error: cmd=dialogkey kind=syntax");
+        return false;
+    }
+
+    // Ctrl+Tab switching browser tabs,
+    // Doesn't insert tabs.
+    constexpr auto KEY_CTRL = 0x2000;
+    constexpr auto KEY_TAB = 0x0502;
+    if (keycode == (KEY_CTRL | KEY_TAB))
+    {
+        return true;
+    }
+
+    std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
+
+    getLOKitDocument()->postDialogKeyEvent(dialogId.c_str(), type, charcode, keycode);
+
+    return true;
+}
+
 bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
 {
     int type, x, y, count;
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index fd8162aaf..b9079e565 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -175,6 +175,7 @@ private:
     bool paste(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool insertFile(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool keyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool dialogKeyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool mouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool dialogMouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool dialogChildMouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 92ae2fe98..457fe379d 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -57,9 +57,10 @@ L.Control.LokDialog = L.Control.extend({
 	},
 
 	_launchDialog: function(dialogId, width, height) {
-		var content = '<div class="lokdialog_container" id="' + dialogId + '">' +
-		    '<img class="lokdialog_content" width="' + width + '" height="' + height + '"></div>';
-		$(document.body).append(content);
+		var canvas = '<div style="padding: 0px; margin: 0px; overflow: hidden;" id="' + dialogId + '">' +
+		    '<canvas tabindex="0" id="' + dialogId + '-canvas" width="' + width + 'px" height="' + height + 'px"></canvas>' +
+		    '</div>';
+		$(document.body).append(canvas);
 		var that = this;
 		$('#' + dialogId).dialog({
 			width: width,
@@ -74,7 +75,7 @@ L.Control.LokDialog = L.Control.extend({
 		});
 
 		// attach the mouse/key events
-		$('#' + dialogId + ' > .lokdialog_content').on('mousedown', function(e) {
+		$('#' + dialogId + '-canvas').on('mousedown', function(e) {
 			var buttons = 0;
 			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
 			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
@@ -83,7 +84,7 @@ L.Control.LokDialog = L.Control.extend({
 			that._postDialogMouseEvent('buttondown', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
 		});
 
-		$('#' + dialogId + ' > .lokdialog_content').on('mouseup', function(e) {
+		$('#' + dialogId + '-canvas').on('mouseup', function(e) {
 			var buttons = 0;
 			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
 			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
@@ -92,7 +93,12 @@ L.Control.LokDialog = L.Control.extend({
 			that._postDialogMouseEvent('buttonup', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
 		});
 
-		$('#' + dialogId + ' > .lokdialog_content').on('mousemove', function(e) {
+		$('#' + dialogId + '-canvas').on('keyup keypress keydown', function(e) {
+			e.dialogId = dialogId;
+			that._handleDialogKeyEvent(e);
+		});
+
+		$('#' + dialogId + '-canvas').on('mousemove', function(e) {
 			//that._postDialogMouseEvent('move', dialogId, e.offsetX, e.offsetY, 1, 0, 0);
 		});
 
@@ -108,6 +114,12 @@ L.Control.LokDialog = L.Control.extend({
 		                              ' buttons=' + buttons + ' modifier=' + modifier);
 	},
 
+	_postDialogKeyboardEvent: function(type, dialogid, charcode, keycode) {
+		console.trace('sending: ' + type);
+		this._map._socket.sendMessage('dialogkey dialogid=' + dialogid + ' type=' + type +
+		                              ' char=' + charcode + ' key=' + keycode);
+	},
+
 	_postDialogChildMouseEvent: function(type, dialogid, x, y, count, buttons, modifier) {
 		if (!dialogid.startsWith('.uno:'))
 			dialogid = '.uno:' + dialogid;
@@ -117,24 +129,68 @@ L.Control.LokDialog = L.Control.extend({
 		                              ' buttons=' + buttons + ' modifier=' + modifier);
 	},
 
+	_handleDialogKeyEvent: function(e) {
+		console.log('handle dialog key event ' + e.type);
+		var docLayer = this._map._docLayer;
+		this.modifier = 0;
+		var shift = e.originalEvent.shiftKey ? this._map['keyboard'].keyModifier.shift : 0;
+		var ctrl = e.originalEvent.ctrlKey ? this._map['keyboard'].keyModifier.ctrl : 0;
+		var alt = e.originalEvent.altKey ? this._map['keyboard'].keyModifier.alt : 0;
+		var cmd = e.originalEvent.metaKey ? this._map['keyboard'].keyModifier.ctrl : 0;
+		var location = e.originalEvent.location;
+		this.modifier = shift | ctrl | alt | cmd;
+
+		var charCode = e.originalEvent.charCode;
+		var keyCode = e.originalEvent.keyCode;
+		var unoKeyCode = this._map['keyboard']._toUNOKeyCode(keyCode);
+
+		if (this.modifier) {
+			unoKeyCode |= this.modifier;
+			if (e.type !== 'keyup') {
+				this._postDialogKeyboardEvent('input', e.dialogId, charCode, unoKeyCode);
+				return;
+			}
+		}
+
+		if (e.type === 'keydown' && this._map['keyboard'].handleOnKeyDownKeys[keyCode]) {
+			this._postDialogKeyboardEvent('input', e.dialogId, charCode, unoKeyCode);
+		}
+		else if (e.type === 'keypress' && (!this._map['keyboard'].handleOnKeyDownKeys[keyCode] || charCode !== 0)) {
+			if (charCode === keyCode && charCode !== 13) {
+				keyCode = 0;
+				unoKeyCode = this._map['keyboard']._toUNOKeyCode(keyCode);
+			}
+			this._postDialogKeyboardEvent('input', e.dialogId, charCode, unoKeyCode);
+		}
+		else if (e.type === 'keyup') {
+			this._postDialogKeyboardEvent('up', e.dialogId, charCode, unoKeyCode);
+		}
+	},
+
 	_onDialogClose: function(dialogId) {
 		$('#' + dialogId).remove();
 		delete this._dialogs[dialogId];
 	},
 
-	_paintDialog: function(dialogId, img) {
+	_paintDialog: function(dialogId, imgData) {
 		if (!this._isOpen(dialogId))
 			return;
 
-		$('#' + dialogId + ' > .lokdialog_content').attr('src', img);
+		var img = new Image();
+		var canvas = document.getElementById(dialogId + '-canvas');
+		var ctx = canvas.getContext('2d');
+		img.onload = function() {
+			ctx.drawImage(img, 0, 0);
+		};
+		img.src = imgData;
 	},
 
 	_isSameSize: function(dialogId, newWidth, newHeight) {
 		var ret = false;
 		if (this._isOpen(dialogId))
 		{
-			var oldWidth = $('#' + dialogId + ' > .lokdialog_content').width();
-			var oldHeight = $('#' + dialogId + ' > .lokdialog_content').height();
+			var oldWidth = $('#' + dialogId + '-canvas').width();
+			var oldHeight = $('#' + dialogId + '-canvas').height();
 			if (oldWidth === newWidth && oldHeight === newHeight)
 				ret = true;
 		}
@@ -162,11 +218,60 @@ L.Control.LokDialog = L.Control.extend({
 
 	_onDialogChildPaint: function(e) {
 		var dialogId = e.id.replace('.uno:', '');
-		$('#' + dialogId + ' .lokdialog_floating_content').attr('src', e.dialog);
+		var img = new Image();
+		var canvas = document.getElementById(dialogId + '-floating');
+		canvas.width = e.width;
+		canvas.height = e.height;
+		var ctx = canvas.getContext('2d');
+		img.onload = function() {
+			ctx.drawImage(img, 0, 0);
+		};
+		img.src = e.dialog;
+	},
+
+	_onDialogChildClose: function(dialogId) {
+		$('#' + dialogId + '-floating').remove();
+	},
+
+	_isDialogChildUnchanged: function(dialogId, left, top) {
+		// get pervious dialog child's specs
+		var oldLeft = $('#' + dialogId + '-floating').css('left');
+		var oldTop = $('#' + dialogId + '-floating').css('top');
+		if (!oldLeft || !oldTop) {
+			// no left or top position set earlier; this is first dialog child placement
+			return false;
+		}
+
+		oldLeft = parseInt(oldLeft);
+		oldTop = parseInt(oldTop);
+		if (oldLeft !== left || oldTop !== top) {
+			// something changed in new dialog child
+			return false;
+		}
+
+		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;
+		}
+
+		// remove any existing floating element if there's any
+		$('#' + e.dialogId + '-floating').remove();
+		var floatingCanvas = '<canvas id="' + e.dialogId + '-floating"></canvas>';
+		$('#' + e.dialogId).append(floatingCanvas);
+		$('#' + e.dialogId + '-floating').css({position: 'absolute', left: left, top: top});
 
 		var that = this;
+		var dialogId = e.dialogId;
 		// attach events
-		$('#' + dialogId + ' .lokdialog_floating_content').on('mousedown', function(e) {
+		$('#' + dialogId + '-floating').on('mousedown', function(e) {
 			var buttons = 0;
 			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
 			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
@@ -175,7 +280,7 @@ L.Control.LokDialog = L.Control.extend({
 			that._postDialogChildMouseEvent('buttondown', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
 		});
 
-		$('#' + dialogId + ' .lokdialog_floating_content').on('mouseup', function(e) {
+		$('#' + dialogId + '-floating').on('mouseup', function(e) {
 			var buttons = 0;
 			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
 			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
@@ -184,27 +289,10 @@ L.Control.LokDialog = L.Control.extend({
 			that._postDialogChildMouseEvent('buttonup', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
 		});
 
-		$('#' + dialogId + ' .lokdialog_floating_content').on('mousemove', function(e) {
+		$('#' + dialogId + '-floating').on('mousemove', function(e) {
 			that._postDialogChildMouseEvent('move', dialogId, e.offsetX, e.offsetY, 1, 0, 0);
 		});
-	},
 
-	_onDialogChildClose: function(dialogId) {
-		$('#' + dialogId + ' .lokdialog_floating').remove();
-	},
-
-	_launchDialogChild: function(e) {
-		if (e.position === '0, 0') {
-			// ignore
-			return;
-		}
-
-		var floatingContentDiv = '<div class="lokdialog_floating"><img class="lokdialog_floating_content"></div>';
-		$('#' + e.dialogId).append(floatingContentDiv);
-		var positions = e.position.split(',');
-		var left = parseInt(positions[0]);
-		var top = parseInt(positions[1]);
-		$('#' + e.dialogId + ' > .lokdialog_floating').css({position: 'absolute', left: left, top: top});
 	},
 
 	_onDialogChildMsg: function(e) {
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index a65a04302..34bcb5672 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -134,6 +134,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "paste" &&
              tokens[0] != "insertfile" &&
              tokens[0] != "key" &&
+             tokens[0] != "dialogkey" &&
              tokens[0] != "mouse" &&
              tokens[0] != "dialogmouse" &&
              tokens[0] != "dialogchildmouse" &&
commit 1f5159822b306e7e49b3d9830499ccb533eb94e1
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Fri Oct 20 12:21:47 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    lokdialog: Mouse events on dialog child windows

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index c96046f81..9ca37b584 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -221,6 +221,7 @@ bool ChildSession::_handleInput(const char *buffer, int length)
                tokens[0] == "key" ||
                tokens[0] == "mouse" ||
                tokens[0] == "dialogmouse" ||
+               tokens[0] == "dialogchildmouse" ||
                tokens[0] == "uno" ||
                tokens[0] == "selecttext" ||
                tokens[0] == "selectgraphic" ||
@@ -269,6 +270,10 @@ bool ChildSession::_handleInput(const char *buffer, int length)
         {
             return dialogMouseEvent(buffer, length, tokens);
         }
+        else if (tokens[0] == "dialogchildmouse")
+        {
+            return dialogChildMouseEvent(buffer, length, tokens);
+        }
         else if (tokens[0] == "uno")
         {
             return unoCommand(buffer, length, tokens);
@@ -829,6 +834,44 @@ bool ChildSession::dialogMouseEvent(const char* /*buffer*/, int /*length*/, cons
     return true;
 }
 
+bool ChildSession::dialogChildMouseEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+{
+    int type, x, y, count;
+    bool success = true;
+
+    // default values for compatibility reasons with older loleaflets
+    int buttons = 1; // left button
+    int modifier = 0;
+    std::string dialogId;
+    if (tokens.size() < 6 ||
+        !getTokenString(tokens[1], "dialogid", dialogId) ||
+        !getTokenKeyword(tokens[2], "type",
+                         {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
+                          {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
+                          {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
+                         type) ||
+        !getTokenInteger(tokens[3], "x", x) ||
+        !getTokenInteger(tokens[4], "y", y) ||
+        !getTokenInteger(tokens[5], "count", count) ||
+        !getTokenInteger(tokens[6], "buttons", buttons) ||
+        !getTokenInteger(tokens[7], "modifier", modifier))
+    {
+        success = false;
+    }
+
+    if (!success)
+    {
+        sendTextFrame("error: cmd=dialogchildmouse kind=syntax");
+        return false;
+    }
+
+    std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
+
+    getLOKitDocument()->postDialogChildMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+
+    return true;
+}
+
 bool ChildSession::unoCommand(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
 {
     if (tokens.size() <= 1)
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 0a6e34a2e..fd8162aaf 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -177,6 +177,7 @@ private:
     bool keyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool mouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool dialogMouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool dialogChildMouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     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);
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index e53950ffc..a65a04302 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -136,6 +136,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "key" &&
              tokens[0] != "mouse" &&
              tokens[0] != "dialogmouse" &&
+             tokens[0] != "dialogchildmouse" &&
              tokens[0] != "partpagerectangles" &&
              tokens[0] != "ping" &&
              tokens[0] != "renderfont" &&
commit acce6a218801632d52495d74b42901c3899b2893
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Fri Oct 20 12:10:11 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    lokdialog: Support for child windows

diff --git a/common/Message.hpp b/common/Message.hpp
index 03c1eaa8a..bfd38cb7a 100644
--- a/common/Message.hpp
+++ b/common/Message.hpp
@@ -131,7 +131,8 @@ private:
         if (_tokens[0] == "tile:" ||
             _tokens[0] == "tilecombine:" ||
             _tokens[0] == "renderfont:" ||
-            _tokens[0] == "dialogpaint:")
+            _tokens[0] == "dialogpaint:" ||
+            _tokens[0] == "dialogchildpaint:")
         {
             return Type::Binary;
         }
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index d1a9b56cd..076ce7b25 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -897,6 +897,58 @@ public:
         ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
+    void renderDialogChild(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
+    {
+        assert(ws && "Expected a non-null websocket.");
+
+        const int nCanvasWidth = 800;
+        const int nCanvasHeight = 600;
+        size_t pixmapDataSize = 4 * nCanvasWidth * nCanvasHeight;
+        std::vector<unsigned char> pixmap(pixmapDataSize);
+
+        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 nWidth = nCanvasWidth;
+        int nHeight = nCanvasHeight;
+        Timestamp timestamp;
+        _loKitDocument->paintActiveFloatingWindow(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
+        const double area = nWidth * nHeight;
+        const auto elapsed = timestamp.elapsed();
+        LOG_TRC("paintActiveFloatingWindow for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
+                << " and rendered in " << (elapsed/1000.) <<
+                " ms (" << area / elapsed << " MP/s).");
+
+        const std::string response = "dialogchildpaint: id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\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, nWidth, nHeight, nCanvasWidth, nCanvasHeight, output, LOK_TILEMODE_RGBA))
+        {
+            //FIXME: Return error.
+            //sendTextFrame("error: cmd=tile kind=failure");
+
+            LOG_ERR("Failed to encode dialog into PNG.");
+            return;
+        }
+
+        LOG_TRC("Sending render-dialogchild 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.");
@@ -1725,6 +1777,10 @@ private:
                 {
                     renderDialog(tokens, _ws);
                 }
+                else if (tokens[0] == "dialogchild")
+                {
+                    renderDialogChild(tokens, _ws);
+                }
                 else if (LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                 {
                     forwardToChild(tokens[0], input);
@@ -2176,7 +2232,7 @@ void lokit_main(const std::string& childRoot,
                         TerminationFlag = true;
                     }
                     else if (tokens[0] == "tile" || tokens[0] == "tilecombine" || tokens[0] == "canceltiles" ||
-                             tokens[0] == "dialog" ||
+                             tokens[0] == "dialog" || tokens[0] == "dialogchild" ||
                              LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                     {
                         if (document)
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 066a8d79a..92ae2fe98 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -6,6 +6,8 @@
 L.Control.LokDialog = L.Control.extend({
 	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);
 	},
@@ -16,21 +18,27 @@ L.Control.LokDialog = L.Control.extend({
 		return this._dialogs[dialogId];
 	},
 
+	_transformDialogId: function(dialogId) {
+		var ret = dialogId;
+		if (dialogId === 'SpellingDialog')
+			ret = 'SpellingAndGrammarDialog';
+		else if (dialogId === 'FindReplaceDialog')
+			ret = 'SearchDialog';
+		else if (dialogId === 'AcceptRejectChangesDialog')
+			ret = 'AcceptTrackedChanges';
+		else if (dialogId === 'FieldDialog')
+			ret = 'InsertField';
+		else if (dialogId === 'BibliographyEntryDialog')
+			ret = 'InsertAuthoritiesEntry';
+		else if (dialogId === 'IndexEntryDialog')
+			ret = 'InsertIndexesEntry';
+
+		return ret;
+	},
+
 	_onDialogMsg: function(e) {
 		// FIXME: core sends a different id for spelling dialog in cbs
-		if (e.dialogId === 'SpellingDialog')
-			e.dialogId = 'SpellingAndGrammarDialog';
-		else if (e.dialogId === 'FindReplaceDialog')
-			e.dialogId = 'SearchDialog';
-		else if (e.dialogId === 'AcceptRejectChangesDialog')
-			e.dialogId = 'AcceptTrackedChanges';
-		else if (e.dialogId === 'FieldDialog')
-			e.dialogId = 'InsertField';
-		else if (e.dialogId === 'BibliographyEntryDialog')
-			e.dialogId = 'InsertAuthoritiesEntry';
-		else if (e.dialogId === 'IndexEntryDialog')
-			e.dialogId = 'InsertIndexesEntry';
-
+		e.dialogId = this._transformDialogId(e.dialogId);
 		if (e.action === 'invalidate') {
 			// ignore any invalidate callbacks when we have closed the dialog
 			if (this._isOpen(e.dialogId)) {
@@ -100,6 +108,15 @@ L.Control.LokDialog = L.Control.extend({
 		                              ' buttons=' + buttons + ' modifier=' + modifier);
 	},
 
+	_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 +
+		                              ' x=' + x + ' y=' + y + ' count=' + count +
+		                              ' buttons=' + buttons + ' modifier=' + modifier);
+	},
+
 	_onDialogClose: function(dialogId) {
 		$('#' + dialogId).remove();
 		delete this._dialogs[dialogId];
@@ -141,6 +158,66 @@ L.Control.LokDialog = L.Control.extend({
 		}
 
 		this._paintDialog(dialogId, e.dialog);
+	},
+
+	_onDialogChildPaint: function(e) {
+		var dialogId = e.id.replace('.uno:', '');
+		$('#' + dialogId + ' .lokdialog_floating_content').attr('src', e.dialog);
+
+		var that = this;
+		// attach events
+		$('#' + dialogId + ' .lokdialog_floating_content').on('mousedown', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogChildMouseEvent('buttondown', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + ' .lokdialog_floating_content').on('mouseup', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogChildMouseEvent('buttonup', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + ' .lokdialog_floating_content').on('mousemove', function(e) {
+			that._postDialogChildMouseEvent('move', dialogId, e.offsetX, e.offsetY, 1, 0, 0);
+		});
+	},
+
+	_onDialogChildClose: function(dialogId) {
+		$('#' + dialogId + ' .lokdialog_floating').remove();
+	},
+
+	_launchDialogChild: function(e) {
+		if (e.position === '0, 0') {
+			// ignore
+			return;
+		}
+
+		var floatingContentDiv = '<div class="lokdialog_floating"><img class="lokdialog_floating_content"></div>';
+		$('#' + e.dialogId).append(floatingContentDiv);
+		var positions = e.position.split(',');
+		var left = parseInt(positions[0]);
+		var top = parseInt(positions[1]);
+		$('#' + e.dialogId + ' > .lokdialog_floating').css({position: 'absolute', left: left, top: top});
+	},
+
+	_onDialogChildMsg: function(e) {
+		e.dialogId = this._transformDialogId(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);
+			}
+		} else if (e.action === 'close') {
+			this._onDialogChildClose(e.dialogId);
+		}
 	}
 });
 
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index 452f4fc90..274e91902 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -145,11 +145,14 @@ L.Map.include({
 		}
 	},
 
-	sendDialogCommand: function (command, json) {
+	sendDialogCommand: function (command, json, child) {
 		if (this._permission === 'edit') {
 			if (!command.startsWith('.uno:'))
 				command = '.uno:' + command;
-			this._socket.sendMessage('dialog ' + command + (json ? ' ' + JSON.stringify(json) : ''));
+			var dialogCmd = 'dialog';
+			if (child)
+				dialogCmd = 'dialogchild';
+			this._socket.sendMessage(dialogCmd + ' ' + command + (json ? ' ' + JSON.stringify(json) : ''));
 		}
 	},
 
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index a8e1e4425..d83b6afc0 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -581,7 +581,7 @@ L.Socket = L.Class.extend({
 				}
 			}
 		}
-		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:') && !textMsg.startsWith('dialogpaint:')) {
+		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:') && !textMsg.startsWith('dialogpaint:') && !textMsg.startsWith('dialogchildpaint:')) {
 			// log the tile msg separately as we need the tile coordinates
 			L.Log.log(textMsg, L.INCOMING);
 			if (imgBytes !== undefined) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 645193010..ccac2b631 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -440,8 +440,14 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('dialogpaint:')) {
 			this._onDialogPaintMsg(textMsg, img);
 		}
+		else if (textMsg.startsWith('dialogchildpaint:')) {
+			this._onDialogChildPaintMsg(textMsg, img);
+		}
 		else if (textMsg.startsWith('dialog:')) {
-			this._onDialogMsg(textMsg, img);
+			this._onDialogMsg(textMsg);
+		}
+		else if (textMsg.startsWith('dialogchild:')) {
+			this._onDialogChildMsg(textMsg);
 		}
 		else if (textMsg.startsWith('unocommandresult:')) {
 			this._onUnoCommandResultMsg(textMsg);
@@ -1200,12 +1206,32 @@ L.TileLayer = L.GridLayer.extend({
 		});
 	},
 
+	_onDialogChildPaintMsg: function(textMsg, img) {
+		var command = this._map._socket.parseServerCmd(textMsg);
+		var width = command.width;
+		var height = command.height;
+
+		this._map.fire('dialogchildpaint', {
+			id: command.id,
+			dialog: img,
+			// TODO: add id too
+			width: width,
+			height: height
+		});
+	},
+
 	_onDialogMsg: function(textMsg) {
 		textMsg = textMsg.substring('dialog: '.length);
 		var dialogMsg = JSON.parse(textMsg);
 		this._map.fire('dialog', dialogMsg);
 	},
 
+	_onDialogChildMsg: function(textMsg) {
+		textMsg = textMsg.substring('dialogchild: '.length);
+		var dialogMsg = JSON.parse(textMsg);
+		this._map.fire('dialogchild', dialogMsg);
+	},
+
 	_onTileMsg: function (textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var coords = this._twipsToCoords(command);
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index b240eed68..e53950ffc 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -154,7 +154,8 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "uno" &&
              tokens[0] != "useractive" &&
              tokens[0] != "userinactive" &&
-             tokens[0] != "dialog")
+             tokens[0] != "dialog" &&
+             tokens[0] != "dialogchild")
     {
         sendTextFrame("error: cmd=" + tokens[0] + " kind=unknown");
         return false;
@@ -222,6 +223,10 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     {
         return sendDialog(buffer, length, tokens, docBroker);
     }
+    else if (tokens[0] == "dialogchild")
+    {
+        return sendDialogChild(buffer, length, tokens, docBroker);
+    }
     else if (tokens[0] == "tilecombine")
     {
         return sendCombinedTiles(buffer, length, tokens, docBroker);
@@ -444,6 +449,22 @@ bool ClientSession::sendDialog(const char * /*buffer*/, int /*length*/, const st
     return true;
 }
 
+bool ClientSession::sendDialogChild(const char * /*buffer*/, int /*length*/, const std::vector<std::string>& tokens,
+                                    const std::shared_ptr<DocumentBroker>& docBroker)
+{
+    try
+    {
+        docBroker->handleDialogChildRequest(tokens[1], shared_from_this());
+    }
+    catch (const std::exception& exc)
+    {
+        LOG_ERR("Failed to process dialogchild command: " << exc.what());
+        return sendTextFrame("error: cmd=dialogchild kind=invalid");
+    }
+
+    return true;
+}
+
 bool ClientSession::sendCombinedTiles(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens,
                                       const std::shared_ptr<DocumentBroker>& docBroker)
 {
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 13ce4b1ea..48ea303b2 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -126,6 +126,8 @@ private:
                   const std::shared_ptr<DocumentBroker>& docBroker);
     bool sendDialog(const char* buffer, int length, const std::vector<std::string>& tokens,
                     const std::shared_ptr<DocumentBroker>& docBroker);
+    bool sendDialogChild(const char* buffer, int length, const std::vector<std::string>& tokens,
+                         const std::shared_ptr<DocumentBroker>& docBroker);
     bool sendCombinedTiles(const char* buffer, int length, const std::vector<std::string>& tokens,
                            const std::shared_ptr<DocumentBroker>& docBroker);
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index ca1a95042..56a8c5a49 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1079,6 +1079,10 @@ bool DocumentBroker::handleInput(const std::vector<char>& payload)
         {
             handleDialogPaintResponse(payload);
         }
+        else if (command == "dialogchildpaint:")
+        {
+            handleDialogChildPaintResponse(payload);
+        }
         else if (command == "errortoall:")
         {
             LOG_CHECK_RET(message->tokens().size() == 3, false);
@@ -1181,6 +1185,17 @@ void DocumentBroker::handleDialogRequest(const std::string& dialogId,
     _childProcess->sendTextFrame(request);
 }
 
+void DocumentBroker::handleDialogChildRequest(const std::string& dialogId,
+                                              const std::shared_ptr<ClientSession>& /*session*/)
+{
+    assertCorrectThread();
+    std::unique_lock<std::mutex> lock(_mutex);
+
+    LOG_DBG("Sending dialog child render request for dialog " << dialogId);
+    const std::string request = "dialogchild " + dialogId;
+    _childProcess->sendTextFrame(request);
+}
+
 void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
                                                const std::shared_ptr<ClientSession>& session)
 {
@@ -1306,6 +1321,31 @@ void DocumentBroker::handleDialogPaintResponse(const std::vector<char>& payload)
     }
 }
 
+void DocumentBroker::handleDialogChildPaintResponse(const std::vector<char>& payload)
+{
+    const std::string firstLine = getFirstLine(payload);
+    LOG_DBG("Handling dialogchildpaint: " << 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);
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 19529797e..3f104eda9 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -300,11 +300,14 @@ public:
                            const std::shared_ptr<ClientSession>& session);
     void handleDialogRequest(const std::string& dialogId,
                              const std::shared_ptr<ClientSession>& session);
+    void handleDialogChildRequest(const std::string& dialogId,
+                                  const std::shared_ptr<ClientSession>& session);
     void handleTileCombinedRequest(TileCombined& tileCombined,
                                    const std::shared_ptr<ClientSession>& session);
     void cancelTileRequests(const std::shared_ptr<ClientSession>& session);
     void handleTileResponse(const std::vector<char>& payload);
     void handleDialogPaintResponse(const std::vector<char>& payload);
+    void handleDialogChildPaintResponse(const std::vector<char>& payload);
     void handleTileCombinedResponse(const std::vector<char>& payload);
 
     void destroyIfLastEditor(const std::string& id);
commit 292bd72180547afe8db83f46eb5dc8e3e2ec4fe6
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Tue Oct 17 13:40:31 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    lokdialog: Keep track of dialog requests sent by us
    
    Otherwise, it's possible that we receive a binary dialog message
    triggering opening of the dialog even though we didn't request any.
    
    With this, one is now able to successfully close the dialogs.
    
    Add some dialog-name conversions too.

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 1f2d9e414..066a8d79a 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -7,6 +7,7 @@ L.Control.LokDialog = L.Control.extend({
 	onAdd: function (map) {
 		map.on('dialogpaint', this._onDialogPaint, this);
 		map.on('dialog', this._onDialogMsg, this);
+		map.on('opendialog', this._openDialog, this);
 	},
 
 	_dialogs: {},
@@ -16,24 +17,38 @@ L.Control.LokDialog = L.Control.extend({
 	},
 
 	_onDialogMsg: function(e) {
-		if (e.action === 'invalidate') {
-			// FIXME: core sends a different id for spelling dialog in 'invalidate' cb
-			if (e.dialogId === 'SpellingDialog')
-				e.dialogId = 'SpellingAndGrammarDialog';
-			else if (e.dialogId === 'FindReplaceDialog')
-				e.dialogId = 'SearchDialog';
-			else if (e.dialogId === 'AcceptRejectChangesDialog')
-				e.dialogId = 'AcceptTrackedChanges';
+		// FIXME: core sends a different id for spelling dialog in cbs
+		if (e.dialogId === 'SpellingDialog')
+			e.dialogId = 'SpellingAndGrammarDialog';
+		else if (e.dialogId === 'FindReplaceDialog')
+			e.dialogId = 'SearchDialog';
+		else if (e.dialogId === 'AcceptRejectChangesDialog')
+			e.dialogId = 'AcceptTrackedChanges';
+		else if (e.dialogId === 'FieldDialog')
+			e.dialogId = 'InsertField';
+		else if (e.dialogId === 'BibliographyEntryDialog')
+			e.dialogId = 'InsertAuthoritiesEntry';
+		else if (e.dialogId === 'IndexEntryDialog')
+			e.dialogId = 'InsertIndexesEntry';
 
+		if (e.action === 'invalidate') {
 			// ignore any invalidate callbacks when we have closed the dialog
-			if (this._isOpen(e.dialogId))
+			if (this._isOpen(e.dialogId)) {
 				this._map.sendDialogCommand(e.dialogId);
+			}
 		} else if (e.action === 'close') {
 			this._onDialogClose(e.dialogId);
 		}
 	},
 
-	_openDialog: function(dialogId, width, height) {
+	_openDialog: function(e) {
+		e.dialogId = e.dialogId.replace('.uno:', '');
+		this._dialogs[e.dialogId] = true;
+
+		this._map.sendDialogCommand(e.dialogId);
+	},
+
+	_launchDialog: function(dialogId, width, height) {
 		var content = '<div class="lokdialog_container" id="' + dialogId + '">' +
 		    '<img class="lokdialog_content" width="' + width + '" height="' + height + '"></div>';
 		$(document.body).append(content);
@@ -87,7 +102,7 @@ L.Control.LokDialog = L.Control.extend({
 
 	_onDialogClose: function(dialogId) {
 		$('#' + dialogId).remove();
-		this._dialogs[dialogId] = false;
+		delete this._dialogs[dialogId];
 	},
 
 	_paintDialog: function(dialogId, img) {
@@ -97,10 +112,32 @@ L.Control.LokDialog = L.Control.extend({
 		$('#' + dialogId + ' > .lokdialog_content').attr('src', img);
 	},
 
+	_isSameSize: function(dialogId, newWidth, newHeight) {
+		var ret = false;
+		if (this._isOpen(dialogId))
+		{
+			var oldWidth = $('#' + dialogId + ' > .lokdialog_content').width();
+			var oldHeight = $('#' + dialogId + ' > .lokdialog_content').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])
+			return;
+
 		if (!this._isOpen(dialogId)) {
-			this._openDialog(dialogId, e.width, e.height);
+			this._launchDialog(dialogId, e.width, e.height);
+		} else if (!this._isSameSize(dialogId, e.width, e.height)) {
+			// size changed - destroy the old sized dialog
+			this._onDialogClose(dialogId);
+			this._launchDialog(dialogId, e.width, e.height);
 		}
 
 		this._paintDialog(dialogId, e.dialog);
diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 327b01055..f3e559197 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -761,7 +761,7 @@ L.Control.Menubar = L.Control.extend({
 		} else if (type === 'action') {
 			self._executeAction(item);
 		} else if (type === 'dialog') {
-			map.sendDialogCommand($(item).data('id'));
+			map.fire('opendialog', {dialogId: $(item).data('id')});
 		}
 
 		if ($(item).data('id') !== 'insertcomment')
commit 65dcb5a3eb3f82432b05a80a744024eb408b754b
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Mon Oct 16 16:25:00 2017 -0700
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    do not need this

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index cfa44d842..1f2d9e414 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -50,7 +50,6 @@ L.Control.LokDialog = L.Control.extend({
 			}
 		});
 
-		that = this;
 		// attach the mouse/key events
 		$('#' + dialogId + ' > .lokdialog_content').on('mousedown', function(e) {
 			var buttons = 0;
commit 1ad56051ede25fd36701d962fb5059c3e3653871
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Fri Aug 25 18:59:27 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:23:02 2017 -0700

    lokdialog: Add more dialogs
    
    Change-Id: I7bcf9d1b18176bc5e140a7e5a36b7734f8adf6cb

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index 358b7aa8a..cfa44d842 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -20,6 +20,10 @@ L.Control.LokDialog = L.Control.extend({
 			// FIXME: core sends a different id for spelling dialog in 'invalidate' cb
 			if (e.dialogId === 'SpellingDialog')
 				e.dialogId = 'SpellingAndGrammarDialog';
+			else if (e.dialogId === 'FindReplaceDialog')
+				e.dialogId = 'SearchDialog';
+			else if (e.dialogId === 'AcceptRejectChangesDialog')
+				e.dialogId = 'AcceptTrackedChanges';
 
 			// ignore any invalidate callbacks when we have closed the dialog
 			if (this._isOpen(e.dialogId))
diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 720de86f0..327b01055 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -180,8 +180,15 @@ L.Control.Menubar = L.Control.extend({
 					{name: _('Reset to Default Language'), id: 'resetparagraph', type: 'unocommand', uno: '.uno:LanguageStatus?Language:string=Paragraph_RESET_LANGUAGES'}]},
 				{name: _('Language for entire document'), type: 'menu', menu: [
 					{name: _('Reset to Default Language'), id: 'resetlanguage', type: 'unocommand', uno:'.uno:LanguageStatus?Language:string=Default_RESET_LANGUAGES'}]},
-				{name: _('Spelling and Grammar'), uno: '.uno:SpellingAndGrammarDialog', type: 'unocommand'},
-				{name: _('Word count'), uno: '.uno:WordCountDialog', type: 'unocommand'}
+				{name: _('Search Dialog'), id: '.uno:SearchDialog', type: 'dialog'},
+				{name: _('Accept/Reject changes'), id: '.uno:AcceptTrackedChanges', type: 'dialog'},
+				{name: _('Insert Field'), id: '.uno:InsertField', type: 'dialog'},
+				{name: _('Image Map Dialog'), id: '.uno:ImageMapDialog', type: 'dialog'},
+				{name: _('Hyperlink Dialog'), id: '.uno:HyperlinkDialog', type: 'dialog'},
+				{name: _('Spelling and Grammar'), id: '.uno:SpellingAndGrammarDialog', type: 'dialog'},
+				{name: _('Insert Indexes Entry'), id: '.uno:InsertIndexesEntry', type: 'dialog'},
+				{name: _('Insert Authority Entry'), id: '.uno:InsertAuthoritiesEntry', type: 'dialog'},
+				{name: _('Word count'), id: '.uno:WordCountDialog', type: 'dialog'}
 			]},
 			{name: _('Help'), id: 'help', type: 'menu', menu: [
 				{name: _('Keyboard shortcuts'), id: 'keyboard-shortcuts', type: 'action'},
commit 460cf08c8b5a2281e614ecfd0194971dc8ef8330
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Thu Aug 24 17:17:40 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:22:59 2017 -0700

    lokdialog: support for mouse events
    
    Change-Id: I816d2b5c8ea6e2c4542b9eaee695a3d6ce9c46b7

diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index e66af1e4f..c96046f81 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -220,6 +220,7 @@ bool ChildSession::_handleInput(const char *buffer, int length)
                tokens[0] == "insertfile" ||
                tokens[0] == "key" ||
                tokens[0] == "mouse" ||
+               tokens[0] == "dialogmouse" ||
                tokens[0] == "uno" ||
                tokens[0] == "selecttext" ||
                tokens[0] == "selectgraphic" ||
@@ -264,6 +265,10 @@ bool ChildSession::_handleInput(const char *buffer, int length)
         {
             return mouseEvent(buffer, length, tokens);
         }
+        else if (tokens[0] == "dialogmouse")
+        {
+            return dialogMouseEvent(buffer, length, tokens);
+        }
         else if (tokens[0] == "uno")
         {
             return unoCommand(buffer, length, tokens);
@@ -786,6 +791,44 @@ bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/, const std:
     return true;
 }
 
+bool ChildSession::dialogMouseEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+{
+    int type, x, y, count;
+    bool success = true;
+
+    // default values for compatibility reasons with older loleaflets
+    int buttons = 1; // left button
+    int modifier = 0;
+    std::string dialogId;
+    if (tokens.size() < 6 ||
+        !getTokenString(tokens[1], "dialogid", dialogId) ||
+        !getTokenKeyword(tokens[2], "type",
+                         {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
+                          {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
+                          {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
+                         type) ||
+        !getTokenInteger(tokens[3], "x", x) ||
+        !getTokenInteger(tokens[4], "y", y) ||
+        !getTokenInteger(tokens[5], "count", count) ||
+        !getTokenInteger(tokens[6], "buttons", buttons) ||
+        !getTokenInteger(tokens[7], "modifier", modifier))
+    {
+        success = false;
+    }
+
+    if (!success)
+    {
+        sendTextFrame("error: cmd=dialogmouse kind=syntax");
+        return false;
+    }
+
+    std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
+
+    getLOKitDocument()->postDialogMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+
+    return true;
+}
+
 bool ChildSession::unoCommand(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
 {
     if (tokens.size() <= 1)
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index c3af96ecf..0a6e34a2e 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -176,6 +176,7 @@ private:
     bool insertFile(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool keyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool mouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool dialogMouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
     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);
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index a9cef890f..358b7aa8a 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -2,7 +2,7 @@
  * L.Control.LokDialog used for displaying LOK dialogs
  */
 
-/* global vex $ */
+/* global vex $ map */
 L.Control.LokDialog = L.Control.extend({
 	onAdd: function (map) {
 		map.on('dialogpaint', this._onDialogPaint, this);
@@ -24,6 +24,8 @@ L.Control.LokDialog = L.Control.extend({
 			// ignore any invalidate callbacks when we have closed the dialog
 			if (this._isOpen(e.dialogId))
 				this._map.sendDialogCommand(e.dialogId);
+		} else if (e.action === 'close') {
+			this._onDialogClose(e.dialogId);
 		}
 	},
 
@@ -44,9 +46,42 @@ L.Control.LokDialog = L.Control.extend({
 			}
 		});
 
+		that = this;
+		// attach the mouse/key events
+		$('#' + dialogId + ' > .lokdialog_content').on('mousedown', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogMouseEvent('buttondown', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + ' > .lokdialog_content').on('mouseup', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogMouseEvent('buttonup', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + ' > .lokdialog_content').on('mousemove', function(e) {
+			//that._postDialogMouseEvent('move', dialogId, e.offsetX, e.offsetY, 1, 0, 0);
+		});
+
 		this._dialogs[dialogId] = 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 +
+		                              ' x=' + x + ' y=' + y + ' count=' + count +
+		                              ' buttons=' + buttons + ' modifier=' + modifier);
+	},
+
 	_onDialogClose: function(dialogId) {
 		$('#' + dialogId).remove();
 		this._dialogs[dialogId] = false;
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 3d917813e..b240eed68 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -135,6 +135,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "insertfile" &&
              tokens[0] != "key" &&
              tokens[0] != "mouse" &&
+             tokens[0] != "dialogmouse" &&
              tokens[0] != "partpagerectangles" &&
              tokens[0] != "ping" &&
              tokens[0] != "renderfont" &&
commit 0ff2e262138ae61264143e7487f55689d081f2ea
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Wed Aug 23 19:12:48 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Wed Oct 25 00:21:09 2017 -0700

    lokdialog: open, close, paint and invalidate the dialog systematically
    
    This makes simple dialog like word count dialog, which do not require
    any key/mouse interaction with the user, work correctly -
    responding to invalidation callbacks and painting it again.
    
    Change-Id: I3eee49751ec7815c8a26a687fc1f3915911d6cd4

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index b2167be1b..a9cef890f 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -5,26 +5,67 @@
 /* global vex $ */
 L.Control.LokDialog = L.Control.extend({
 	onAdd: function (map) {
-		// TODO: Better distinction between warnings and errors
+		map.on('dialogpaint', this._onDialogPaint, this);
 		map.on('dialog', this._onDialogMsg, this);
 	},
 
-	_onDialogMsg: function (e) {
-		var dialogId = e.id.replace('.uno:', '');
+	_dialogs: {},
+
+	_isOpen: function(dialogId) {
+		return this._dialogs[dialogId];
+	},
+
+	_onDialogMsg: function(e) {
+		if (e.action === 'invalidate') {
+			// FIXME: core sends a different id for spelling dialog in 'invalidate' cb
+			if (e.dialogId === 'SpellingDialog')
+				e.dialogId = 'SpellingAndGrammarDialog';
+
+			// ignore any invalidate callbacks when we have closed the dialog
+			if (this._isOpen(e.dialogId))
+				this._map.sendDialogCommand(e.dialogId);
+		}
+	},
+
+	_openDialog: function(dialogId, width, height) {
 		var content = '<div class="lokdialog_container" id="' + dialogId + '">' +
-		    '<img class="lokdialog_content" src= ' + e.dialog + '></div>';
+		    '<img class="lokdialog_content" width="' + width + '" height="' + height + '"></div>';
 		$(document.body).append(content);
+		var that = this;
 		$('#' + dialogId).dialog({
-			width: e.width,
+			width: width,
 			height: 'auto',
 			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
 			modal: false,
 			closeOnEscape: true,
 			resizable: false,
-			close: function(e, ui) {
-				$('#' + dialogId).remove();
+			close: function() {
+				that._onDialogClose(dialogId);
 			}
 		});
+
+		this._dialogs[dialogId] = true;
+	},
+
+	_onDialogClose: function(dialogId) {
+		$('#' + dialogId).remove();
+		this._dialogs[dialogId] = false;
+	},
+
+	_paintDialog: function(dialogId, img) {
+		if (!this._isOpen(dialogId))
+			return;
+
+		$('#' + dialogId + ' > .lokdialog_content').attr('src', img);
+	},
+
+	_onDialogPaint: function (e) {
+		var dialogId = e.id.replace('.uno:', '');
+		if (!this._isOpen(dialogId)) {
+			this._openDialog(dialogId, e.width, e.height);
+		}
+
+		this._paintDialog(dialogId, e.dialog);
 	}
 });
 
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index d11b29cd5..452f4fc90 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -147,6 +147,8 @@ L.Map.include({
 
 	sendDialogCommand: function (command, json) {
 		if (this._permission === 'edit') {
+			if (!command.startsWith('.uno:'))
+				command = '.uno:' + command;
 			this._socket.sendMessage('dialog ' + command + (json ? ' ' + JSON.stringify(json) : ''));
 		}
 	},
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 7d833b01b..645193010 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -438,6 +438,9 @@ L.TileLayer = L.GridLayer.extend({
 			this._onTileMsg(textMsg, img);
 		}
 		else if (textMsg.startsWith('dialogpaint:')) {
+			this._onDialogPaintMsg(textMsg, img);
+		}
+		else if (textMsg.startsWith('dialog:')) {
 			this._onDialogMsg(textMsg, img);
 		}
 		else if (textMsg.startsWith('unocommandresult:')) {
@@ -1183,12 +1186,12 @@ L.TileLayer = L.GridLayer.extend({
 
 	},
 
-	_onDialogMsg: function(textMsg, img) {
+	_onDialogPaintMsg: function(textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var dlgWidth = command.width;
 		var dlgHeight = command.height;
 
-		this._map.fire('dialog', {
+		this._map.fire('dialogpaint', {
 			id: command.id,
 			dialog: img,
 			// TODO: add id too
@@ -1197,6 +1200,12 @@ L.TileLayer = L.GridLayer.extend({
 		});
 	},
 
+	_onDialogMsg: function(textMsg) {
+		textMsg = textMsg.substring('dialog: '.length);
+		var dialogMsg = JSON.parse(textMsg);
+		this._map.fire('dialog', dialogMsg);
+	},
+
 	_onTileMsg: function (textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var coords = this._twipsToCoords(command);
commit 50bb753dd29988b72c719c836d489027f6d47dbf
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Wed Aug 23 17:58:23 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Mon Oct 16 16:10:18 2017 -0700

    lokdialog: pass the dialog id in dialog command
    
    Change-Id: Ia0e76b1dc93483340361777cf21f23127bac91e1

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 16c68cdde..d1a9b56cd 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -877,7 +877,7 @@ public:
                 << " and rendered in " << (elapsed/1000.) <<
                 " ms (" << area / elapsed << " MP/s).");
 
-        const std::string response = "dialogpaint: width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
+        const std::string response = "dialogpaint: id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
         std::vector<char> output;
         output.reserve(response.size() + pixmapDataSize);
         output.resize(response.size());
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index e8a2daf2a..b577cafc0 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -334,7 +334,7 @@ body {
 	text-indent: 1px;
 }
 
-#lokdialog_container {
+.lokdialog_container.ui-dialog-content.ui-widget-content {
         padding: 0px;
 	overflow: hidden;
 	width: auto;
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index aeb453486..b2167be1b 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -10,18 +10,19 @@ L.Control.LokDialog = L.Control.extend({
 	},
 
 	_onDialogMsg: function (e) {
-		var content = '<div id="lokdialog_container"><img id="lokdialog_content" src= ' + e.dialog + '></div>';
+		var dialogId = e.id.replace('.uno:', '');
+		var content = '<div class="lokdialog_container" id="' + dialogId + '">' +
+		    '<img class="lokdialog_content" src= ' + e.dialog + '></div>';
 		$(document.body).append(content);
-		$('#lokdialog_container').dialog({
+		$('#' + dialogId).dialog({
 			width: e.width,
 			height: 'auto',
-			closeText: 'X',
 			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
 			modal: false,
 			closeOnEscape: true,
 			resizable: false,
 			close: function(e, ui) {
-				$('#lokdialog_container').remove();
+				$('#' + dialogId).remove();
 			}
 		});
 	}
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index e32b63d08..7d833b01b 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1189,6 +1189,7 @@ L.TileLayer = L.GridLayer.extend({
 		var dlgHeight = command.height;
 
 		this._map.fire('dialog', {
+			id: command.id,
 			dialog: img,
 			// TODO: add id too
 			width: dlgWidth,
commit b3f836865b7e96741cc2600ab29081bd8ecf3d2e
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Mon Aug 21 15:17:22 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Mon Oct 16 16:10:18 2017 -0700

    lokdialog: wrap the lok dialog image in dialog container
    
    The two test dialogs, Spelling dialog and word count dialog
    appear on the screen when invoked from the menubar.
    
    Interaction with dialogs is WIP.
    Mouse/key events and floating window still needs to be worked upon.
    
    Change-Id: I2d439465c7536d0b453c8ade503ec181a96c90d2

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 1ab853a7e..16c68cdde 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -849,7 +849,9 @@ public:
     {
         assert(ws && "Expected a non-null websocket.");
 
-        size_t pixmapDataSize = 4 * 800 * 600;
+        const int nCanvasWidth = 800;
+        const int nCanvasHeight = 600;
+        size_t pixmapDataSize = 4 * nCanvasWidth * nCanvasHeight;
         std::vector<unsigned char> pixmap(pixmapDataSize);
 
         std::unique_lock<std::mutex> lock(_documentMutex);
@@ -865,8 +867,8 @@ public:
             return;
         }
 
-        int nWidth = 0;
-        int nHeight = 0;
+        int nWidth = nCanvasWidth;
+        int nHeight = nCanvasHeight;
         Timestamp timestamp;
         _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
         const double area = nWidth * nHeight;
@@ -882,7 +884,7 @@ public:
         std::memcpy(output.data(), response.data(), response.size());
 
         // TODO: use png cache for dialogs too
-        if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, nWidth, nHeight, nWidth, nHeight, output, LOK_TILEMODE_RGBA))
+        if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, nWidth, nHeight, nCanvasWidth, nCanvasHeight, output, LOK_TILEMODE_RGBA))
         {
             //FIXME: Return error.
             //sendTextFrame("error: cmd=tile kind=failure");
@@ -955,7 +957,7 @@ public:
         const auto mode = static_cast<LibreOfficeKitTileMode>(_loKitDocument->getTileMode());
 
         std::vector<char> output;
-        output.reserve(pixmapWidth * pixmapHeight * 4);
+        output.reserve(pixmapSize);
 
         size_t tileIndex = 0;
         for (Util::Rectangle& tileRect : tileRecs)
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index a0604c5f2..e8a2daf2a 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -333,3 +333,10 @@ body {
 	font: bold 18px 'Lucida Console', Monaco, monospace;
 	text-indent: 1px;
 }
+
+#lokdialog_container {
+        padding: 0px;
+	overflow: hidden;
+	width: auto;
+	height: auto;
+}
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index da5926150..aeb453486 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -2,7 +2,7 @@
  * L.Control.LokDialog used for displaying LOK dialogs
  */
 
-/* global vex */
+/* global vex $ */
 L.Control.LokDialog = L.Control.extend({
 	onAdd: function (map) {
 		// TODO: Better distinction between warnings and errors
@@ -10,7 +10,20 @@ L.Control.LokDialog = L.Control.extend({
 	},
 
 	_onDialogMsg: function (e) {
-		console.log(e);
+		var content = '<div id="lokdialog_container"><img id="lokdialog_content" src= ' + e.dialog + '></div>';
+		$(document.body).append(content);
+		$('#lokdialog_container').dialog({
+			width: e.width,
+			height: 'auto',
+			closeText: 'X',
+			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
+			modal: false,
+			closeOnEscape: true,
+			resizable: false,
+			close: function(e, ui) {
+				$('#lokdialog_container').remove();
+			}
+		});
 	}
 });
 
commit 2669aa668da7530bccf2f2ee5b0856560d7a1931
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Fri Aug 18 18:45:58 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Mon Oct 16 16:10:17 2017 -0700

    lokdialog: loleaflet: infra for receiving lok dialog messages
    
    Change-Id: Id1aa1b31c3f2d4e3a888307d25cd7a8106613156

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 85074949c..a3009c567 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -357,6 +357,7 @@ var deps = {
 
 	ControlDialog: {
 		src: ['control/Control.js',
+		      'control/Control.LokDialog.js',
 		      'control/Control.AlertDialog.js'],
 		heading: 'Controls',
 		desc: 'Handles vex dialogs for displaying alerts'
diff --git a/loleaflet/main.js b/loleaflet/main.js
index 8a6a8568f..a08e50545 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -126,6 +126,7 @@ global.map = map;
 ////// Controls /////
 map.addControl(L.control.scroll());
 map.addControl(L.control.alertDialog());
+map.addControl(L.control.lokDialog());
 map.addControl(L.control.partsPreview());
 map.addControl(L.control.tabs());
 map.addControl(L.control.columnHeader());
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
new file mode 100644
index 000000000..da5926150
--- /dev/null
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -0,0 +1,19 @@
+/*
+ * L.Control.LokDialog used for displaying LOK dialogs
+ */
+
+/* global vex */
+L.Control.LokDialog = L.Control.extend({
+	onAdd: function (map) {
+		// TODO: Better distinction between warnings and errors
+		map.on('dialog', this._onDialogMsg, this);
+	},
+
+	_onDialogMsg: function (e) {
+		console.log(e);
+	}
+});
+
+L.control.lokDialog = function (options) {
+	return new L.Control.LokDialog(options);
+};
diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 4a6c814d0..720de86f0 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -753,6 +753,8 @@ L.Control.Menubar = L.Control.extend({
 			map.sendUnoCommand(unoCommand);
 		} else if (type === 'action') {
 			self._executeAction(item);
+		} else if (type === 'dialog') {
+			map.sendDialogCommand($(item).data('id'));
 		}
 
 		if ($(item).data('id') !== 'insertcomment')
@@ -832,6 +834,9 @@ L.Control.Menubar = L.Control.extend({
 			} else if (menu[i].type === 'unocommand') {
 				$(aItem).data('type', 'unocommand');
 				$(aItem).data('uno', menu[i].uno);
+			} else if (menu[i].type === 'dialog') {
+				$(aItem).data('type', 'dialog');
+				$(aItem).data('id', menu[i].id);
 			} else if (menu[i].type === 'separator') {
 				$(aItem).addClass('separator');
 			} else if (menu[i].type === 'action') {
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index 25693170a..d11b29cd5 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -145,6 +145,12 @@ L.Map.include({
 		}
 	},
 
+	sendDialogCommand: function (command, json) {
+		if (this._permission === 'edit') {
+			this._socket.sendMessage('dialog ' + command + (json ? ' ' + JSON.stringify(json) : ''));
+		}
+	},
+
 	toggleCommandState: function (unoState) {
 		if (this._permission === 'edit') {
 			if (!unoState.startsWith('.uno:')) {
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index c0929ffd9..a8e1e4425 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -581,7 +581,7 @@ L.Socket = L.Class.extend({
 				}
 			}
 		}
-		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:')) {
+		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:') && !textMsg.startsWith('dialogpaint:')) {
 			// log the tile msg separately as we need the tile coordinates
 			L.Log.log(textMsg, L.INCOMING);
 			if (imgBytes !== undefined) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index a5bafca55..e32b63d08 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -437,6 +437,9 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('tile:')) {
 			this._onTileMsg(textMsg, img);
 		}
+		else if (textMsg.startsWith('dialogpaint:')) {
+			this._onDialogMsg(textMsg, img);
+		}
 		else if (textMsg.startsWith('unocommandresult:')) {
 			this._onUnoCommandResultMsg(textMsg);
 		}
@@ -1180,6 +1183,19 @@ L.TileLayer = L.GridLayer.extend({
 
 	},
 
+	_onDialogMsg: function(textMsg, img) {
+		var command = this._map._socket.parseServerCmd(textMsg);
+		var dlgWidth = command.width;
+		var dlgHeight = command.height;
+
+		this._map.fire('dialog', {
+			dialog: img,
+			// TODO: add id too
+			width: dlgWidth,
+			height: dlgHeight
+		});
+	},
+
 	_onTileMsg: function (textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var coords = this._twipsToCoords(command);
commit 884978fb61e984baf37872a1b3fafbd7d112bc2b
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Fri Aug 18 18:35:51 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Mon Oct 16 16:09:55 2017 -0700

    lokdialog: Make space for dialog class
    
    Change-Id: Ic3bccac355069491afdc76db70bea8d5c570e086

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 7044f43d0..85074949c 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -357,7 +357,7 @@ var deps = {
 
 	ControlDialog: {
 		src: ['control/Control.js',
-		      'control/Control.Dialog.js'],
+		      'control/Control.AlertDialog.js'],
 		heading: 'Controls',
 		desc: 'Handles vex dialogs for displaying alerts'
 	},
diff --git a/loleaflet/debug/document/loleaflet.html b/loleaflet/debug/document/loleaflet.html
index fc239f3b1..2134f31b7 100644
--- a/loleaflet/debug/document/loleaflet.html
+++ b/loleaflet/debug/document/loleaflet.html
@@ -131,7 +131,7 @@
 
     ////// Controls /////
     map.addControl(L.control.scroll());
-    map.addControl(L.control.dialog());
+    map.addControl(L.control.alertDialog());
     map.addControl(L.control.partsPreview());
     map.addControl(L.control.tabs());
     map.addControl(L.control.columnHeader());
diff --git a/loleaflet/main.js b/loleaflet/main.js
index 5d8aaed02..8a6a8568f 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -125,7 +125,7 @@ global.map = map;
 
 ////// Controls /////
 map.addControl(L.control.scroll());
-map.addControl(L.control.dialog());
+map.addControl(L.control.alertDialog());
 map.addControl(L.control.partsPreview());
 map.addControl(L.control.tabs());
 map.addControl(L.control.columnHeader());
diff --git a/loleaflet/src/control/Control.Dialog.js b/loleaflet/src/control/Control.AlertDialog.js
similarity index 89%
rename from loleaflet/src/control/Control.Dialog.js
rename to loleaflet/src/control/Control.AlertDialog.js
index daad5e6e1..4b9372000 100644
--- a/loleaflet/src/control/Control.Dialog.js
+++ b/loleaflet/src/control/Control.AlertDialog.js
@@ -3,7 +3,7 @@
  */
 
 /* global vex */
-L.Control.Dialog = L.Control.extend({
+L.Control.AlertDialog = L.Control.extend({
 	onAdd: function (map) {
 		// TODO: Better distinction between warnings and errors
 		map.on('error', this._onError, this);
@@ -47,6 +47,6 @@ L.Control.Dialog = L.Control.extend({
 	}
 });
 
-L.control.dialog = function (options) {
-	return new L.Control.Dialog(options);
+L.control.alertDialog = function (options) {
+	return new L.Control.AlertDialog(options);
 };
commit b9a943e4ab05b21458cf2d1e4b057436744dadbb
Author:     Pranav Kant <pranavk at collabora.co.uk>
AuthorDate: Fri Aug 18 18:49:40 2017 +0530
Commit:     Pranav Kant <pranavk at collabora.co.uk>
CommitDate: Mon Oct 16 16:09:55 2017 -0700

    lokdialog: wsd: first cut at lok dialog rendering api
    
    Change-Id: Id522e0796428ed0a15ea3496e6726f51ed232e75

diff --git a/common/Message.hpp b/common/Message.hpp
index 99ccd77a8..03c1eaa8a 100644
--- a/common/Message.hpp
+++ b/common/Message.hpp
@@ -130,7 +130,8 @@ private:
     {
         if (_tokens[0] == "tile:" ||
             _tokens[0] == "tilecombine:" ||
-            _tokens[0] == "renderfont:")
+            _tokens[0] == "renderfont:" ||
+            _tokens[0] == "dialogpaint:")
         {
             return Type::Binary;
         }
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 3802ff597..1ab853a7e 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -845,6 +845,56 @@ 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.");
+
+        size_t pixmapDataSize = 4 * 800 * 600;
+        std::vector<unsigned char> pixmap(pixmapDataSize);
+
+        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 nWidth = 0;
+        int nHeight = 0;
+        Timestamp timestamp;
+        _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
+        const double area = nWidth * nHeight;
+        const auto elapsed = timestamp.elapsed();
+        LOG_TRC("paintDialog for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
+                << " and rendered in " << (elapsed/1000.) <<
+                " ms (" << area / elapsed << " MP/s).");
+

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list