[Libreoffice-commits] online.git: loolwsd/ChildProcessSession.cpp loolwsd/ChildProcessSession.hpp loolwsd/LOKitHelper.hpp loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/LOOLWSD.cpp loolwsd/Makefile.am

Henry Castro hcastro at collabora.com
Wed Dec 23 09:04:01 PST 2015


 loolwsd/ChildProcessSession.cpp |  820 ++++++++++++++++++++++++++++++++++++++++
 loolwsd/ChildProcessSession.hpp |   64 +++
 loolwsd/LOKitHelper.hpp         |    2 
 loolwsd/LOOLSession.cpp         |  778 -------------------------------------
 loolwsd/LOOLSession.hpp         |   48 --
 loolwsd/LOOLWSD.cpp             |    1 
 loolwsd/Makefile.am             |    4 
 7 files changed, 889 insertions(+), 828 deletions(-)

New commits:
commit 1f43e99ac7465342d2e0fbff7a313844d45a4807
Author: Henry Castro <hcastro at collabora.com>
Date:   Sat Dec 12 14:23:44 2015 -0500

    loolwsd: Refactored ChildProcessSession
    
    ChildProcessSession class now moved to own files.
    
    Change-Id: Ic67c8563ada51f23c83e06631ad913af610d395c
    Reviewed-on: https://gerrit.libreoffice.org/20895
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Henry Castro <hcastro at collabora.com>

diff --git a/loolwsd/ChildProcessSession.cpp b/loolwsd/ChildProcessSession.cpp
new file mode 100644
index 0000000..ab92478
--- /dev/null
+++ b/loolwsd/ChildProcessSession.cpp
@@ -0,0 +1,820 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <iostream>
+
+#include <Poco/File.h>
+#include <Poco/JSON/Object.h>
+#include <Poco/JSON/Parser.h>
+#include <Poco/Net/WebSocket.h>
+#include <Poco/Path.h>
+#include <Poco/Process.h>
+#include <Poco/String.h>
+#include <Poco/StringTokenizer.h>
+#include <Poco/URI.h>
+#include <Poco/Util/Application.h>
+
+#include "ChildProcessSession.hpp"
+#include "LOKitHelper.hpp"
+#include "LOOLProtocol.hpp"
+#include "LOOLWSD.hpp"
+#include "Util.hpp"
+
+using namespace LOOLProtocol;
+
+using Poco::File;
+using Poco::IOException;
+using Poco::JSON::Object;
+using Poco::JSON::Parser;
+using Poco::Net::WebSocket;
+using Poco::Path;
+using Poco::Process;
+using Poco::ProcessHandle;
+using Poco::StringTokenizer;
+using Poco::URI;
+using Poco::Util::Application;
+
+ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit, std::string childId) :
+    LOOLSession(ws, Kind::ToMaster),
+    _loKitDocument(NULL),
+    _loKit(loKit),
+    _childId(childId),
+    _clientPart(0)
+{
+    std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
+}
+
+ChildProcessSession::~ChildProcessSession()
+{
+    std::cout << Util::logPrefix() << "ChildProcessSession dtor this=" << this << std::endl;
+    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
+        _loKit->pClass->registerCallback(_loKit, 0, 0);
+}
+
+bool ChildProcessSession::handleInput(const char *buffer, int length)
+{
+    std::string firstLine = getFirstLine(buffer, length);
+    StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+
+    Application::instance().logger().information(Util::logPrefix() + _kindString + ",Input," + getAbbreviatedMessage(buffer, length));
+
+    if (tokens[0] == "canceltiles")
+    {
+        // this command makes sense only on the command queue level, nothing
+        // to do here
+        return true;
+    }
+    else if (tokens[0] == "commandvalues")
+    {
+        return getCommandValues(buffer, length, tokens);
+    }
+    else if (tokens[0] == "partpagerectangles")
+    {
+        return getPartPageRectangles(buffer, length);
+    }
+    else if (tokens[0] == "load")
+    {
+        if (_docURL != "")
+        {
+            sendTextFrame("error: cmd=load kind=docalreadyloaded");
+            return false;
+        }
+        return loadDocument(buffer, length, tokens);
+    }
+    else if (_docURL == "")
+    {
+        sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded");
+        return false;
+    }
+    else if (tokens[0] == "renderfont")
+    {
+        sendFontRendering(buffer, length, tokens);
+    }
+    else if (tokens[0] == "setclientpart")
+    {
+        return setClientPart(buffer, length, tokens);
+    }
+    else if (tokens[0] == "setpage")
+    {
+        return setPage(buffer, length, tokens);
+    }
+    else if (tokens[0] == "status")
+    {
+        return getStatus(buffer, length);
+    }
+    else if (tokens[0] == "tile")
+    {
+        sendTile(buffer, length, tokens);
+    }
+    else
+    {
+        // All other commands are such that they always require a LibreOfficeKitDocument session,
+        // i.e. need to be handled in a child process.
+
+        assert(tokens[0] == "clientzoom" ||
+               tokens[0] == "downloadas" ||
+               tokens[0] == "getchildid" ||
+               tokens[0] == "gettextselection" ||
+               tokens[0] == "paste" ||
+               tokens[0] == "insertfile" ||
+               tokens[0] == "key" ||
+               tokens[0] == "mouse" ||
+               tokens[0] == "uno" ||
+               tokens[0] == "selecttext" ||
+               tokens[0] == "selectgraphic" ||
+               tokens[0] == "resetselection" ||
+               tokens[0] == "saveas");
+
+        if (_docType != "text" && _loKitDocument->pClass->getPart(_loKitDocument) != _clientPart)
+        {
+            _loKitDocument->pClass->setPart(_loKitDocument, _clientPart);
+        }
+        if (tokens[0] == "clientzoom")
+        {
+            return clientZoom(buffer, length, tokens);
+        }
+        else if (tokens[0] == "downloadas")
+        {
+            return downloadAs(buffer, length, tokens);
+        }
+        else if (tokens[0] == "getchildid")
+        {
+            return getChildId();
+        }
+        else if (tokens[0] == "gettextselection")
+        {
+            return getTextSelection(buffer, length, tokens);
+        }
+        else if (tokens[0] == "paste")
+        {
+            return paste(buffer, length, tokens);
+        }
+        else if (tokens[0] == "insertfile")
+        {
+            return insertFile(buffer, length, tokens);
+        }
+        else if (tokens[0] == "key")
+        {
+            return keyEvent(buffer, length, tokens);
+        }
+        else if (tokens[0] == "mouse")
+        {
+            return mouseEvent(buffer, length, tokens);
+        }
+        else if (tokens[0] == "uno")
+        {
+            return unoCommand(buffer, length, tokens);
+        }
+        else if (tokens[0] == "selecttext")
+        {
+            return selectText(buffer, length, tokens);
+        }
+        else if (tokens[0] == "selectgraphic")
+        {
+            return selectGraphic(buffer, length, tokens);
+        }
+        else if (tokens[0] == "resetselection")
+        {
+            return resetSelection(buffer, length, tokens);
+        }
+        else if (tokens[0] == "saveas")
+        {
+            return saveAs(buffer, length, tokens);
+        }
+        else
+        {
+            assert(false);
+        }
+    }
+    return true;
+}
+
+extern "C"
+{
+    static void myCallback(int nType, const char* pPayload, void* pData)
+    {
+        ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData);
+
+        switch ((LibreOfficeKitCallbackType) nType)
+        {
+        case LOK_CALLBACK_INVALIDATE_TILES:
+            {
+                int curPart = srv->_loKitDocument->pClass->getPart(srv->_loKitDocument);
+                srv->sendTextFrame("curpart: part=" + std::to_string(curPart));
+                if (srv->_docType == "text")
+                {
+                    curPart = 0;
+                }
+                StringTokenizer tokens(std::string(pPayload), " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+                if (tokens.count() == 4)
+                {
+                    int x, y, width, height;
+
+                    try {
+                        x = std::stoi(tokens[0]);
+                        y = std::stoi(tokens[1]);
+                        width = std::stoi(tokens[2]);
+                        height = std::stoi(tokens[3]);
+                    }
+                    catch (std::out_of_range&)
+                    {
+                        // something went wrong, invalidate everything
+                        Application::instance().logger().information(Util::logPrefix() + "Ignoring integer values out of range: " + pPayload);
+                        x = 0;
+                        y = 0;
+                        width = INT_MAX;
+                        height = INT_MAX;
+                    }
+
+                    srv->sendTextFrame("invalidatetiles:"
+                                       " part=" + std::to_string(curPart) +
+                                       " x=" + std::to_string(x) +
+                                       " y=" + std::to_string(y) +
+                                       " width=" + std::to_string(width) +
+                                       " height=" + std::to_string(height));
+                }
+                else
+                {
+                    srv->sendTextFrame("invalidatetiles: " + std::string(pPayload));
+                }
+            }
+            break;
+        case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+            srv->sendTextFrame("invalidatecursor: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_TEXT_SELECTION:
+            srv->sendTextFrame("textselection: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_TEXT_SELECTION_START:
+            srv->sendTextFrame("textselectionstart: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_TEXT_SELECTION_END:
+            srv->sendTextFrame("textselectionend: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_CURSOR_VISIBLE:
+            srv->sendTextFrame("cursorvisible: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_GRAPHIC_SELECTION:
+            srv->sendTextFrame("graphicselection: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_CELL_CURSOR:
+            srv->sendTextFrame("cellcursor: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_CELL_FORMULA:
+            srv->sendTextFrame("cellformula: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_MOUSE_POINTER:
+            srv->sendTextFrame("mousepointer: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_HYPERLINK_CLICKED:
+            srv->sendTextFrame("hyperlinkclicked: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_STATE_CHANGED:
+            srv->sendTextFrame("statechanged: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_STATUS_INDICATOR_START:
+            srv->sendTextFrame("statusindicatorstart:");
+            break;
+        case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
+            srv->sendTextFrame("statusindicatorsetvalue: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
+            srv->sendTextFrame("statusindicatorfinish:");
+            break;
+        case LOK_CALLBACK_SEARCH_NOT_FOUND:
+            srv->sendTextFrame("searchnotfound: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
+            srv->sendTextFrame("searchresultselection: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
+            srv->getStatus("", 0);
+            srv->getPartPageRectangles("", 0);
+            break;
+        case LOK_CALLBACK_SET_PART:
+            srv->sendTextFrame("setpart: " + std::string(pPayload));
+            break;
+        case LOK_CALLBACK_UNO_COMMAND_RESULT:
+            srv->sendTextFrame("unocommandresult: " + std::string(pPayload));
+            break;
+        }
+    }
+}
+
+bool ChildProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens)
+{
+    int part = -1;
+    if (tokens.count() < 2)
+    {
+        sendTextFrame("error: cmd=load kind=syntax");
+        return false;
+    }
+
+    std::string timestamp;
+    parseDocOptions(tokens, part, timestamp);
+
+    URI aUri;
+    try
+    {
+        aUri = URI(_docURL);
+    }
+    catch(Poco::SyntaxException&)
+    {
+        sendTextFrame("error: cmd=load kind=uriinvalid");
+        return false;
+    }
+
+    if (aUri.empty())
+    {
+        sendTextFrame("error: cmd=load kind=uriempty");
+        return false;
+    }
+
+    // The URL in the request is the original one, not visible in the chroot jail.
+    // The child process uses the fixed name jailDocumentURL.
+
+    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
+        _loKit->pClass->registerCallback(_loKit, myCallback, this);
+
+    if (aUri.isRelative() || aUri.getScheme() == "file")
+        aUri = URI( URI("file://"), Path(jailDocumentURL + Path::separator() + std::to_string(Process::id()),
+                    Path(aUri.getPath()).getFileName()).toString() );
+
+    if ((_loKitDocument = _loKit->pClass->documentLoad(_loKit, aUri.toString().c_str())) == NULL)
+    {
+        sendTextFrame("error: cmd=load kind=failed");
+        Application::instance().logger().information(Util::logPrefix() + "Failed to load: " + aUri.toString() + ", error is: " + _loKit->pClass->getError(_loKit));
+        return false;
+    }
+
+    std::string renderingOptions;
+    if (!_docOptions.empty())
+    {
+        Poco::JSON::Parser parser;
+        Poco::Dynamic::Var var = parser.parse(_docOptions);
+        Poco::JSON::Object::Ptr object = var.extract<Poco::JSON::Object::Ptr>();
+        renderingOptions = object->get("rendering").toString();
+    }
+
+    _loKitDocument->pClass->initializeForRendering(_loKitDocument, (renderingOptions.empty() ? nullptr : renderingOptions.c_str()));
+
+    if ( _docType != "text" && part != -1)
+    {
+        _clientPart = part;
+        _loKitDocument->pClass->setPart(_loKitDocument, part);
+    }
+
+    if (!getStatus(buffer, length))
+        return false;
+
+    _loKitDocument->pClass->registerCallback(_loKitDocument, myCallback, this);
+
+    return true;
+}
+
+void ChildProcessSession::sendFontRendering(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    std::string font, decodedFont;
+    int width, height;
+    unsigned char *pixmap;
+
+    if (tokens.count() < 2 ||
+        !getTokenString(tokens[1], "font", font))
+    {
+        sendTextFrame("error: cmd=renderfont kind=syntax");
+        return;
+    }
+
+    URI::decode(font, decodedFont);
+    std::string response = "renderfont: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n";
+
+    std::vector<char> output;
+    output.resize(response.size());
+    std::memcpy(output.data(), response.data(), response.size());
+
+    Poco::Timestamp timestamp;
+    pixmap = _loKitDocument->pClass->renderFont(_loKitDocument, decodedFont.c_str(), &width, &height);
+    std::cout << Util::logPrefix() << "renderFont called, font[" << font << "] rendered in " << double(timestamp.elapsed())/1000 <<  "ms" << std::endl;
+
+    if (pixmap != nullptr) {
+        if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, LOK_TILEMODE_RGBA))
+        {
+            sendTextFrame("error: cmd=renderfont kind=failure");
+            delete[] pixmap;
+            return;
+        }
+        delete[] pixmap;
+    }
+
+    sendBinaryFrame(output.data(), output.size());
+}
+
+bool ChildProcessSession::getStatus(const char* /*buffer*/, int /*length*/)
+{
+    std::string status = "status: " + LOKitHelper::documentStatus(_loKitDocument);
+    StringTokenizer tokens(status, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+    if (!getTokenString(tokens[1], "type", _docType))
+    {
+        Application::instance().logger().information(Util::logPrefix() + "failed to get document type from" + status);
+    }
+    sendTextFrame(status);
+
+    return true;
+}
+
+bool ChildProcessSession::getCommandValues(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    std::string command;
+    if (tokens.count() != 2 || !getTokenString(tokens[1], "command", command))
+    {
+        sendTextFrame("error: cmd=commandvalues kind=syntax");
+        return false;
+    }
+    sendTextFrame("commandvalues: " + std::string(_loKitDocument->pClass->getCommandValues(_loKitDocument, command.c_str())));
+    return true;
+}
+
+bool ChildProcessSession::getPartPageRectangles(const char* /*buffer*/, int /*length*/)
+{
+    sendTextFrame("partpagerectangles: " + std::string(_loKitDocument->pClass->getPartPageRectangles(_loKitDocument)));
+    return true;
+}
+
+void ChildProcessSession::sendTile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
+
+    if (tokens.count() < 8 ||
+        !getTokenInteger(tokens[1], "part", part) ||
+        !getTokenInteger(tokens[2], "width", width) ||
+        !getTokenInteger(tokens[3], "height", height) ||
+        !getTokenInteger(tokens[4], "tileposx", tilePosX) ||
+        !getTokenInteger(tokens[5], "tileposy", tilePosY) ||
+        !getTokenInteger(tokens[6], "tilewidth", tileWidth) ||
+        !getTokenInteger(tokens[7], "tileheight", tileHeight))
+    {
+        sendTextFrame("error: cmd=tile kind=syntax");
+        return;
+    }
+
+    if (part < 0 ||
+        width <= 0 ||
+        height <= 0 ||
+        tilePosX < 0 ||
+        tilePosY < 0 ||
+        tileWidth <= 0 ||
+        tileHeight <= 0)
+    {
+        sendTextFrame("error: cmd=tile kind=invalid");
+        return;
+    }
+
+    std::string response = "tile: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n";
+
+    std::vector<char> output;
+    output.reserve(4 * width * height);
+    output.resize(response.size());
+    std::memcpy(output.data(), response.data(), response.size());
+
+    unsigned char *pixmap = new unsigned char[4 * width * height];
+    memset(pixmap, 0, 4 * width * height);
+
+    if (_docType != "text" && part != _loKitDocument->pClass->getPart(_loKitDocument))
+    {
+        _loKitDocument->pClass->setPart(_loKitDocument, part);
+    }
+
+    Poco::Timestamp timestamp;
+    _loKitDocument->pClass->paintTile(_loKitDocument, pixmap, width, height, tilePosX, tilePosY, tileWidth, tileHeight);
+    std::cout << Util::logPrefix() << "paintTile called, tile at [" << tilePosX << ", " << tilePosY << "] rendered in " << double(timestamp.elapsed())/1000 <<  "ms" << std::endl;
+
+    LibreOfficeKitTileMode mode = static_cast<LibreOfficeKitTileMode>(_loKitDocument->pClass->getTileMode(_loKitDocument));
+    if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, mode))
+    {
+        sendTextFrame("error: cmd=tile kind=failure");
+        return;
+    }
+
+    delete[] pixmap;
+
+    sendBinaryFrame(output.data(), output.size());
+}
+
+bool ChildProcessSession::clientZoom(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    int tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight;
+
+    if (tokens.count() != 5 ||
+        !getTokenInteger(tokens[1], "tilepixelwidth", tilePixelWidth) ||
+        !getTokenInteger(tokens[2], "tilepixelheight", tilePixelHeight) ||
+        !getTokenInteger(tokens[3], "tiletwipwidth", tileTwipWidth) ||
+        !getTokenInteger(tokens[4], "tiletwipheight", tileTwipHeight))
+    {
+        sendTextFrame("error: cmd=clientzoom kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->setClientZoom(_loKitDocument, tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight);
+    return true;
+}
+bool ChildProcessSession::downloadAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    std::string name, id, format, filterOptions;
+
+    if (tokens.count() < 5 ||
+        !getTokenString(tokens[1], "name", name) ||
+        !getTokenString(tokens[2], "id", id))
+    {
+        sendTextFrame("error: cmd=downloadas kind=syntax");
+        return false;
+    }
+
+    getTokenString(tokens[3], "format", format);
+
+    if (getTokenString(tokens[4], "options", filterOptions))
+    {
+        if (tokens.count() > 5)
+        {
+            filterOptions += Poco::cat(std::string(" "), tokens.begin() + 5, tokens.end());
+        }
+    }
+
+    std::string tmpDir, url;
+    File *file = NULL;
+    do
+    {
+        if (file != NULL)
+        {
+            delete file;
+        }
+        tmpDir = std::to_string((((Poco::UInt64)LOOLWSD::_rng.next()) << 32) | LOOLWSD::_rng.next() | 1);
+        url = jailDocumentURL + "/" + tmpDir + "/" + name;
+        file = new File(url);
+    } while (file->exists());
+    delete file;
+
+    _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(),
+            format.size() == 0 ? NULL :format.c_str(),
+            filterOptions.size() == 0 ? NULL : filterOptions.c_str());
+
+    sendTextFrame("downloadas: jail=" + _childId + " dir=" + tmpDir + " name=" + name +
+            " port=" + std::to_string(LOOLWSD::portNumber) + " id=" + id);
+    return true;
+}
+
+bool ChildProcessSession::getChildId()
+{
+    sendTextFrame("getchildid: id=" + _childId);
+    return true;
+}
+
+bool ChildProcessSession::getTextSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    std::string mimeType;
+
+    if (tokens.count() != 2 ||
+        !getTokenString(tokens[1], "mimetype", mimeType))
+    {
+        sendTextFrame("error: cmd=gettextselection kind=syntax");
+        return false;
+    }
+
+    char *textSelection = _loKitDocument->pClass->getTextSelection(_loKitDocument, mimeType.c_str(), NULL);
+
+    sendTextFrame("textselectioncontent: " + std::string(textSelection));
+    return true;
+}
+
+bool ChildProcessSession::paste(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    std::string mimeType;
+    std::string data;
+
+    if (tokens.count() < 3 || !getTokenString(tokens[1], "mimetype", mimeType) || !getTokenString(tokens[2], "data", data))
+    {
+        sendTextFrame("error: cmd=paste kind=syntax");
+        return false;
+    }
+
+    data = Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).substr(strlen("data="));
+
+    _loKitDocument->pClass->paste(_loKitDocument, mimeType.c_str(), data.c_str(), std::strlen(data.c_str()));
+
+    return true;
+}
+
+bool ChildProcessSession::insertFile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    std::string name, type;
+
+    if (tokens.count() != 3 ||
+        !getTokenString(tokens[1], "name", name) ||
+        !getTokenString(tokens[2], "type", type))
+    {
+        sendTextFrame("error: cmd=insertfile kind=syntax");
+        return false;
+    }
+
+    if (type == "graphic")
+    {
+        std::string fileName = "file://" + jailDocumentURL + "/insertfile/" + name;
+        std::string command = ".uno:InsertGraphic";
+        std::string arguments = "{"
+            "\"FileName\":{"
+                "\"type\":\"string\","
+                "\"value\":\"" + fileName + "\""
+            "}}";
+        _loKitDocument->pClass->postUnoCommand(_loKitDocument, command.c_str(), arguments.c_str(), false);
+    }
+
+    return true;
+}
+
+bool ChildProcessSession::keyEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    int type, charcode, keycode;
+
+    if (tokens.count() != 4 ||
+        !getTokenKeyword(tokens[1], "type",
+                         {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
+                         type) ||
+        !getTokenInteger(tokens[2], "char", charcode) ||
+        !getTokenInteger(tokens[3], "key", keycode))
+    {
+        sendTextFrame("error: cmd=key kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->postKeyEvent(_loKitDocument, type, charcode, keycode);
+
+    return true;
+}
+
+bool ChildProcessSession::mouseEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    int type, x, y, count, buttons, modifier;
+
+    if (tokens.count() != 7 ||
+        !getTokenKeyword(tokens[1], "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[5], "buttons", buttons) ||
+        !getTokenInteger(tokens[6], "modifier", modifier))
+    {
+        sendTextFrame("error: cmd=mouse kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->postMouseEvent(_loKitDocument, type, x, y, count, buttons, modifier);
+
+    return true;
+}
+
+bool ChildProcessSession::unoCommand(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    if (tokens.count() == 1)
+    {
+        sendTextFrame("error: cmd=uno kind=syntax");
+        return false;
+    }
+
+    // we need to get LOK_CALLBACK_UNO_COMMAND_RESULT callback when saving
+    bool bNotify = (tokens[1] == ".uno:Save");
+
+    if (tokens.count() == 2)
+    {
+        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), 0, bNotify);
+    }
+    else
+    {
+        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).c_str(), bNotify);
+    }
+
+    return true;
+}
+
+bool ChildProcessSession::selectText(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    int type, x, y;
+
+    if (tokens.count() != 4 ||
+        !getTokenKeyword(tokens[1], "type",
+                         {{"start", LOK_SETTEXTSELECTION_START},
+                          {"end", LOK_SETTEXTSELECTION_END},
+                          {"reset", LOK_SETTEXTSELECTION_RESET}},
+                         type) ||
+        !getTokenInteger(tokens[2], "x", x) ||
+        !getTokenInteger(tokens[3], "y", y))
+    {
+        sendTextFrame("error: cmd=selecttext kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->setTextSelection(_loKitDocument, type, x, y);
+
+    return true;
+}
+
+bool ChildProcessSession::selectGraphic(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    int type, x, y;
+
+    if (tokens.count() != 4 ||
+        !getTokenKeyword(tokens[1], "type",
+                         {{"start", LOK_SETGRAPHICSELECTION_START},
+                          {"end", LOK_SETGRAPHICSELECTION_END}},
+                         type) ||
+        !getTokenInteger(tokens[2], "x", x) ||
+        !getTokenInteger(tokens[3], "y", y))
+    {
+        sendTextFrame("error: cmd=selectgraphic kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->setGraphicSelection(_loKitDocument, type, x, y);
+
+    return true;
+}
+
+bool ChildProcessSession::resetSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    if (tokens.count() != 1)
+    {
+        sendTextFrame("error: cmd=resetselection kind=syntax");
+        return false;
+    }
+
+    _loKitDocument->pClass->resetSelection(_loKitDocument);
+
+    return true;
+}
+
+bool ChildProcessSession::saveAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    std::string url, format, filterOptions;
+
+    if (tokens.count() < 4 ||
+        !getTokenString(tokens[1], "url", url))
+    {
+        sendTextFrame("error: cmd=saveas kind=syntax");
+        return false;
+    }
+
+    getTokenString(tokens[2], "format", format);
+
+    if (getTokenString(tokens[3], "options", filterOptions))
+    {
+        if (tokens.count() > 4)
+        {
+            filterOptions += Poco::cat(std::string(" "), tokens.begin() + 4, tokens.end());
+        }
+    }
+
+    bool success = _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(),
+            format.size() == 0 ? NULL :format.c_str(),
+            filterOptions.size() == 0 ? NULL : filterOptions.c_str());
+
+    sendTextFrame("saveas: url=" + url);
+    std::string successStr = success ? "true" : "false";
+    sendTextFrame("unocommandresult: {"
+            "\"commandName\":\"saveas\","
+            "\"success\":\"" + successStr + "\"}");
+
+    return true;
+}
+
+bool ChildProcessSession::setClientPart(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    if (tokens.count() < 2 ||
+        !getTokenInteger(tokens[1], "part", _clientPart))
+    {
+        return false;
+    }
+    return true;
+}
+
+bool ChildProcessSession::setPage(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
+{
+    int page;
+    if (tokens.count() < 2 ||
+        !getTokenInteger(tokens[1], "page", page))
+    {
+        sendTextFrame("error: cmd=setpage kind=invalid");
+        return false;
+    }
+    _loKitDocument->pClass->setPart(_loKitDocument, page);
+    return true;
+}
+
+
diff --git a/loolwsd/ChildProcessSession.hpp b/loolwsd/ChildProcessSession.hpp
new file mode 100644
index 0000000..d2665d0
--- /dev/null
+++ b/loolwsd/ChildProcessSession.hpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_CHILDPROCESSSESSION_HPP
+#define INCLUDED_CHILDPROCESSSESSION_HPP
+
+#include "LOOLSession.hpp"
+
+class ChildProcessSession final : public LOOLSession
+{
+public:
+    ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit, std::string _childId);
+    virtual ~ChildProcessSession();
+
+    virtual bool handleInput(const char *buffer, int length) override;
+
+    virtual bool getStatus(const char *buffer, int length);
+
+    virtual bool getCommandValues(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
+    virtual bool getPartPageRectangles(const char *buffer, int length) override;
+
+    LibreOfficeKitDocument *_loKitDocument;
+    std::string _docType;
+
+ protected:
+    virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
+
+    virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
+    virtual void sendFontRendering(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
+    bool clientZoom(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool downloadAs(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool getChildId();
+    bool getTextSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool paste(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool insertFile(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool keyEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool mouseEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool unoCommand(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool selectText(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool selectGraphic(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool resetSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool saveAs(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool setClientPart(const char *buffer, int length, Poco::StringTokenizer& tokens);
+    bool setPage(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
+    std::string _loSubPath;
+    LibreOfficeKit *_loKit;
+    std::string _childId;
+
+ private:
+    int _clientPart;
+};
+
+#endif
+
diff --git a/loolwsd/LOKitHelper.hpp b/loolwsd/LOKitHelper.hpp
index 33adeb8..fb0cdbc 100644
--- a/loolwsd/LOKitHelper.hpp
+++ b/loolwsd/LOKitHelper.hpp
@@ -18,6 +18,7 @@
 
 namespace LOKitHelper
 {
+    inline
     std::string documentTypeToString(LibreOfficeKitDocumentType type)
     {
         switch (type)
@@ -35,6 +36,7 @@ namespace LOKitHelper
         }
     }
 
+    inline
     std::string documentStatus(LibreOfficeKitDocument *loKitDocument)
     {
         std::string typeString(documentTypeToString(static_cast<LibreOfficeKitDocumentType>(loKitDocument->pClass->getDocumentType(loKitDocument))));
diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index 7c208bb..db59e76 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -51,7 +51,6 @@
 #include <Poco/Net/SocketAddress.h>
 #include <Poco/FileStream.h>
 
-#include "LOKitHelper.hpp"
 #include "LOOLProtocol.hpp"
 #include "LOOLSession.hpp"
 #include "LOOLWSD.hpp"
@@ -169,781 +168,4 @@ void LOOLSession::parseDocOptions(const StringTokenizer& tokens, int& part, std:
     }
 }
 
-ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit, std::string childId) :
-    LOOLSession(ws, Kind::ToMaster),
-    _loKitDocument(NULL),
-    _loKit(loKit),
-    _childId(childId),
-    _clientPart(0)
-{
-    std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
-}
-
-ChildProcessSession::~ChildProcessSession()
-{
-    std::cout << Util::logPrefix() << "ChildProcessSession dtor this=" << this << std::endl;
-    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
-        _loKit->pClass->registerCallback(_loKit, 0, 0);
-}
-
-bool ChildProcessSession::handleInput(const char *buffer, int length)
-{
-    std::string firstLine = getFirstLine(buffer, length);
-    StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-
-    Application::instance().logger().information(Util::logPrefix() + _kindString + ",Input," + getAbbreviatedMessage(buffer, length));
-
-    if (tokens[0] == "canceltiles")
-    {
-        // this command makes sense only on the command queue level, nothing
-        // to do here
-        return true;
-    }
-    else if (tokens[0] == "commandvalues")
-    {
-        return getCommandValues(buffer, length, tokens);
-    }
-    else if (tokens[0] == "partpagerectangles")
-    {
-        return getPartPageRectangles(buffer, length);
-    }
-    else if (tokens[0] == "load")
-    {
-        if (_docURL != "")
-        {
-            sendTextFrame("error: cmd=load kind=docalreadyloaded");
-            return false;
-        }
-        return loadDocument(buffer, length, tokens);
-    }
-    else if (_docURL == "")
-    {
-        sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded");
-        return false;
-    }
-    else if (tokens[0] == "renderfont")
-    {
-        sendFontRendering(buffer, length, tokens);
-    }
-    else if (tokens[0] == "setclientpart")
-    {
-        return setClientPart(buffer, length, tokens);
-    }
-    else if (tokens[0] == "setpage")
-    {
-        return setPage(buffer, length, tokens);
-    }
-    else if (tokens[0] == "status")
-    {
-        return getStatus(buffer, length);
-    }
-    else if (tokens[0] == "tile")
-    {
-        sendTile(buffer, length, tokens);
-    }
-    else
-    {
-        // All other commands are such that they always require a LibreOfficeKitDocument session,
-        // i.e. need to be handled in a child process.
-
-        assert(tokens[0] == "clientzoom" ||
-               tokens[0] == "downloadas" ||
-               tokens[0] == "getchildid" ||
-               tokens[0] == "gettextselection" ||
-               tokens[0] == "paste" ||
-               tokens[0] == "insertfile" ||
-               tokens[0] == "key" ||
-               tokens[0] == "mouse" ||
-               tokens[0] == "uno" ||
-               tokens[0] == "selecttext" ||
-               tokens[0] == "selectgraphic" ||
-               tokens[0] == "resetselection" ||
-               tokens[0] == "saveas");
-
-        if (_docType != "text" && _loKitDocument->pClass->getPart(_loKitDocument) != _clientPart)
-        {
-            _loKitDocument->pClass->setPart(_loKitDocument, _clientPart);
-        }
-        if (tokens[0] == "clientzoom")
-        {
-            return clientZoom(buffer, length, tokens);
-        }
-        else if (tokens[0] == "downloadas")
-        {
-            return downloadAs(buffer, length, tokens);
-        }
-        else if (tokens[0] == "getchildid")
-        {
-            return getChildId();
-        }
-        else if (tokens[0] == "gettextselection")
-        {
-            return getTextSelection(buffer, length, tokens);
-        }
-        else if (tokens[0] == "paste")
-        {
-            return paste(buffer, length, tokens);
-        }
-        else if (tokens[0] == "insertfile")
-        {
-            return insertFile(buffer, length, tokens);
-        }
-        else if (tokens[0] == "key")
-        {
-            return keyEvent(buffer, length, tokens);
-        }
-        else if (tokens[0] == "mouse")
-        {
-            return mouseEvent(buffer, length, tokens);
-        }
-        else if (tokens[0] == "uno")
-        {
-            return unoCommand(buffer, length, tokens);
-        }
-        else if (tokens[0] == "selecttext")
-        {
-            return selectText(buffer, length, tokens);
-        }
-        else if (tokens[0] == "selectgraphic")
-        {
-            return selectGraphic(buffer, length, tokens);
-        }
-        else if (tokens[0] == "resetselection")
-        {
-            return resetSelection(buffer, length, tokens);
-        }
-        else if (tokens[0] == "saveas")
-        {
-            return saveAs(buffer, length, tokens);
-        }
-        else
-        {
-            assert(false);
-        }
-    }
-    return true;
-}
-
-extern "C"
-{
-    static void myCallback(int nType, const char* pPayload, void* pData)
-    {
-        ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData);
-
-        switch ((LibreOfficeKitCallbackType) nType)
-        {
-        case LOK_CALLBACK_INVALIDATE_TILES:
-            {
-                int curPart = srv->_loKitDocument->pClass->getPart(srv->_loKitDocument);
-                srv->sendTextFrame("curpart: part=" + std::to_string(curPart));
-                if (srv->_docType == "text")
-                {
-                    curPart = 0;
-                }
-                StringTokenizer tokens(std::string(pPayload), " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-                if (tokens.count() == 4)
-                {
-                    int x, y, width, height;
-
-                    try {
-                        x = std::stoi(tokens[0]);
-                        y = std::stoi(tokens[1]);
-                        width = std::stoi(tokens[2]);
-                        height = std::stoi(tokens[3]);
-                    }
-                    catch (std::out_of_range&)
-                    {
-                        // something went wrong, invalidate everything
-                        Application::instance().logger().information(Util::logPrefix() + "Ignoring integer values out of range: " + pPayload);
-                        x = 0;
-                        y = 0;
-                        width = INT_MAX;
-                        height = INT_MAX;
-                    }
-
-                    srv->sendTextFrame("invalidatetiles:"
-                                       " part=" + std::to_string(curPart) +
-                                       " x=" + std::to_string(x) +
-                                       " y=" + std::to_string(y) +
-                                       " width=" + std::to_string(width) +
-                                       " height=" + std::to_string(height));
-                }
-                else
-                {
-                    srv->sendTextFrame("invalidatetiles: " + std::string(pPayload));
-                }
-            }
-            break;
-        case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
-            srv->sendTextFrame("invalidatecursor: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_TEXT_SELECTION:
-            srv->sendTextFrame("textselection: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_TEXT_SELECTION_START:
-            srv->sendTextFrame("textselectionstart: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_TEXT_SELECTION_END:
-            srv->sendTextFrame("textselectionend: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_CURSOR_VISIBLE:
-            srv->sendTextFrame("cursorvisible: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_GRAPHIC_SELECTION:
-            srv->sendTextFrame("graphicselection: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_CELL_CURSOR:
-            srv->sendTextFrame("cellcursor: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_CELL_FORMULA:
-            srv->sendTextFrame("cellformula: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_MOUSE_POINTER:
-            srv->sendTextFrame("mousepointer: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_HYPERLINK_CLICKED:
-            srv->sendTextFrame("hyperlinkclicked: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_STATE_CHANGED:
-            srv->sendTextFrame("statechanged: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_STATUS_INDICATOR_START:
-            srv->sendTextFrame("statusindicatorstart:");
-            break;
-        case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
-            srv->sendTextFrame("statusindicatorsetvalue: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
-            srv->sendTextFrame("statusindicatorfinish:");
-            break;
-        case LOK_CALLBACK_SEARCH_NOT_FOUND:
-            srv->sendTextFrame("searchnotfound: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
-            srv->sendTextFrame("searchresultselection: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
-            srv->getStatus("", 0);
-            srv->getPartPageRectangles("", 0);
-            break;
-        case LOK_CALLBACK_SET_PART:
-            srv->sendTextFrame("setpart: " + std::string(pPayload));
-            break;
-        case LOK_CALLBACK_UNO_COMMAND_RESULT:
-            srv->sendTextFrame("unocommandresult: " + std::string(pPayload));
-            break;
-        }
-    }
-}
-
-bool ChildProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens)
-{
-    int part = -1;
-    if (tokens.count() < 2)
-    {
-        sendTextFrame("error: cmd=load kind=syntax");
-        return false;
-    }
-
-    std::string timestamp;
-    parseDocOptions(tokens, part, timestamp);
-
-    URI aUri;
-    try
-    {
-        aUri = URI(_docURL);
-    }
-    catch(Poco::SyntaxException&)
-    {
-        sendTextFrame("error: cmd=load kind=uriinvalid");
-        return false;
-    }
-
-    if (aUri.empty())
-    {
-        sendTextFrame("error: cmd=load kind=uriempty");
-        return false;
-    }
-
-    // The URL in the request is the original one, not visible in the chroot jail.
-    // The child process uses the fixed name jailDocumentURL.
-
-    if (LIBREOFFICEKIT_HAS(_loKit, registerCallback))
-        _loKit->pClass->registerCallback(_loKit, myCallback, this);
-
-    if (aUri.isRelative() || aUri.getScheme() == "file")
-        aUri = URI( URI("file://"), Path(jailDocumentURL + Path::separator() + std::to_string(Process::id()),
-                    Path(aUri.getPath()).getFileName()).toString() );
-
-    if ((_loKitDocument = _loKit->pClass->documentLoad(_loKit, aUri.toString().c_str())) == NULL)
-    {
-        sendTextFrame("error: cmd=load kind=failed");
-        Application::instance().logger().information(Util::logPrefix() + "Failed to load: " + aUri.toString() + ", error is: " + _loKit->pClass->getError(_loKit));
-        return false;
-    }
-
-    std::string renderingOptions;
-    if (!_docOptions.empty())
-    {
-        Poco::JSON::Parser parser;
-        Poco::Dynamic::Var var = parser.parse(_docOptions);
-        Poco::JSON::Object::Ptr object = var.extract<Poco::JSON::Object::Ptr>();
-        renderingOptions = object->get("rendering").toString();
-    }
-
-    _loKitDocument->pClass->initializeForRendering(_loKitDocument, (renderingOptions.empty() ? nullptr : renderingOptions.c_str()));
-
-    if ( _docType != "text" && part != -1)
-    {
-        _clientPart = part;
-        _loKitDocument->pClass->setPart(_loKitDocument, part);
-    }
-
-    if (!getStatus(buffer, length))
-        return false;
-
-    _loKitDocument->pClass->registerCallback(_loKitDocument, myCallback, this);
-
-    return true;
-}
-
-void ChildProcessSession::sendFontRendering(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    std::string font, decodedFont;
-    int width, height;
-    unsigned char *pixmap;
-
-    if (tokens.count() < 2 ||
-        !getTokenString(tokens[1], "font", font))
-    {
-        sendTextFrame("error: cmd=renderfont kind=syntax");
-        return;
-    }
-
-    URI::decode(font, decodedFont);
-    std::string response = "renderfont: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n";
-
-    std::vector<char> output;
-    output.resize(response.size());
-    std::memcpy(output.data(), response.data(), response.size());
-
-    Poco::Timestamp timestamp;
-    pixmap = _loKitDocument->pClass->renderFont(_loKitDocument, decodedFont.c_str(), &width, &height);
-    std::cout << Util::logPrefix() << "renderFont called, font[" << font << "] rendered in " << double(timestamp.elapsed())/1000 <<  "ms" << std::endl;
-
-    if (pixmap != nullptr) {
-        if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, LOK_TILEMODE_RGBA))
-        {
-            sendTextFrame("error: cmd=renderfont kind=failure");
-            delete[] pixmap;
-            return;
-        }
-        delete[] pixmap;
-    }
-
-    sendBinaryFrame(output.data(), output.size());
-}
-
-bool ChildProcessSession::getStatus(const char* /*buffer*/, int /*length*/)
-{
-    std::string status = "status: " + LOKitHelper::documentStatus(_loKitDocument);
-    StringTokenizer tokens(status, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
-    if (!getTokenString(tokens[1], "type", _docType))
-    {
-        Application::instance().logger().information(Util::logPrefix() + "failed to get document type from" + status);
-    }
-    sendTextFrame(status);
-
-    return true;
-}
-
-bool ChildProcessSession::getCommandValues(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    std::string command;
-    if (tokens.count() != 2 || !getTokenString(tokens[1], "command", command))
-    {
-        sendTextFrame("error: cmd=commandvalues kind=syntax");
-        return false;
-    }
-    sendTextFrame("commandvalues: " + std::string(_loKitDocument->pClass->getCommandValues(_loKitDocument, command.c_str())));
-    return true;
-}
-
-bool ChildProcessSession::getPartPageRectangles(const char* /*buffer*/, int /*length*/)
-{
-    sendTextFrame("partpagerectangles: " + std::string(_loKitDocument->pClass->getPartPageRectangles(_loKitDocument)));
-    return true;
-}
-
-void ChildProcessSession::sendTile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight;
-
-    if (tokens.count() < 8 ||
-        !getTokenInteger(tokens[1], "part", part) ||
-        !getTokenInteger(tokens[2], "width", width) ||
-        !getTokenInteger(tokens[3], "height", height) ||
-        !getTokenInteger(tokens[4], "tileposx", tilePosX) ||
-        !getTokenInteger(tokens[5], "tileposy", tilePosY) ||
-        !getTokenInteger(tokens[6], "tilewidth", tileWidth) ||
-        !getTokenInteger(tokens[7], "tileheight", tileHeight))
-    {
-        sendTextFrame("error: cmd=tile kind=syntax");
-        return;
-    }
-
-    if (part < 0 ||
-        width <= 0 ||
-        height <= 0 ||
-        tilePosX < 0 ||
-        tilePosY < 0 ||
-        tileWidth <= 0 ||
-        tileHeight <= 0)
-    {
-        sendTextFrame("error: cmd=tile kind=invalid");
-        return;
-    }
-
-    std::string response = "tile: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n";
-
-    std::vector<char> output;
-    output.reserve(4 * width * height);
-    output.resize(response.size());
-    std::memcpy(output.data(), response.data(), response.size());
-
-    unsigned char *pixmap = new unsigned char[4 * width * height];
-    memset(pixmap, 0, 4 * width * height);
-
-    if (_docType != "text" && part != _loKitDocument->pClass->getPart(_loKitDocument))
-    {
-        _loKitDocument->pClass->setPart(_loKitDocument, part);
-    }
-
-    Poco::Timestamp timestamp;
-    _loKitDocument->pClass->paintTile(_loKitDocument, pixmap, width, height, tilePosX, tilePosY, tileWidth, tileHeight);
-    std::cout << Util::logPrefix() << "paintTile called, tile at [" << tilePosX << ", " << tilePosY << "] rendered in " << double(timestamp.elapsed())/1000 <<  "ms" << std::endl;
-
-    LibreOfficeKitTileMode mode = static_cast<LibreOfficeKitTileMode>(_loKitDocument->pClass->getTileMode(_loKitDocument));
-    if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, mode))
-    {
-        sendTextFrame("error: cmd=tile kind=failure");
-        return;
-    }
-
-    delete[] pixmap;
-
-    sendBinaryFrame(output.data(), output.size());
-}
-
-bool ChildProcessSession::clientZoom(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    int tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight;
-
-    if (tokens.count() != 5 ||
-        !getTokenInteger(tokens[1], "tilepixelwidth", tilePixelWidth) ||
-        !getTokenInteger(tokens[2], "tilepixelheight", tilePixelHeight) ||
-        !getTokenInteger(tokens[3], "tiletwipwidth", tileTwipWidth) ||
-        !getTokenInteger(tokens[4], "tiletwipheight", tileTwipHeight))
-    {
-        sendTextFrame("error: cmd=clientzoom kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->setClientZoom(_loKitDocument, tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight);
-    return true;
-}
-bool ChildProcessSession::downloadAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    std::string name, id, format, filterOptions;
-
-    if (tokens.count() < 5 ||
-        !getTokenString(tokens[1], "name", name) ||
-        !getTokenString(tokens[2], "id", id))
-    {
-        sendTextFrame("error: cmd=downloadas kind=syntax");
-        return false;
-    }
-
-    getTokenString(tokens[3], "format", format);
-
-    if (getTokenString(tokens[4], "options", filterOptions))
-    {
-        if (tokens.count() > 5)
-        {
-            filterOptions += Poco::cat(std::string(" "), tokens.begin() + 5, tokens.end());
-        }
-    }
-
-    std::string tmpDir, url;
-    File *file = NULL;
-    do
-    {
-        if (file != NULL)
-        {
-            delete file;
-        }
-        tmpDir = std::to_string((((Poco::UInt64)LOOLWSD::_rng.next()) << 32) | LOOLWSD::_rng.next() | 1);
-        url = jailDocumentURL + "/" + tmpDir + "/" + name;
-        file = new File(url);
-    } while (file->exists());
-    delete file;
-
-    _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(),
-            format.size() == 0 ? NULL :format.c_str(),
-            filterOptions.size() == 0 ? NULL : filterOptions.c_str());
-
-    sendTextFrame("downloadas: jail=" + _childId + " dir=" + tmpDir + " name=" + name +
-            " port=" + std::to_string(LOOLWSD::portNumber) + " id=" + id);
-    return true;
-}
-
-bool ChildProcessSession::getChildId()
-{
-    sendTextFrame("getchildid: id=" + _childId);
-    return true;
-}
-
-bool ChildProcessSession::getTextSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    std::string mimeType;
-
-    if (tokens.count() != 2 ||
-        !getTokenString(tokens[1], "mimetype", mimeType))
-    {
-        sendTextFrame("error: cmd=gettextselection kind=syntax");
-        return false;
-    }
-
-    char *textSelection = _loKitDocument->pClass->getTextSelection(_loKitDocument, mimeType.c_str(), NULL);
-
-    sendTextFrame("textselectioncontent: " + std::string(textSelection));
-    return true;
-}
-
-bool ChildProcessSession::paste(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    std::string mimeType;
-    std::string data;
-
-    if (tokens.count() < 3 || !getTokenString(tokens[1], "mimetype", mimeType) || !getTokenString(tokens[2], "data", data))
-    {
-        sendTextFrame("error: cmd=paste kind=syntax");
-        return false;
-    }
-
-    data = Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).substr(strlen("data="));
-
-    _loKitDocument->pClass->paste(_loKitDocument, mimeType.c_str(), data.c_str(), std::strlen(data.c_str()));
-
-    return true;
-}
-
-bool ChildProcessSession::insertFile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    std::string name, type;
-
-    if (tokens.count() != 3 ||
-        !getTokenString(tokens[1], "name", name) ||
-        !getTokenString(tokens[2], "type", type))
-    {
-        sendTextFrame("error: cmd=insertfile kind=syntax");
-        return false;
-    }
-
-    if (type == "graphic")
-    {
-        std::string fileName = "file://" + jailDocumentURL + "/insertfile/" + name;
-        std::string command = ".uno:InsertGraphic";
-        std::string arguments = "{"
-            "\"FileName\":{"
-                "\"type\":\"string\","
-                "\"value\":\"" + fileName + "\""
-            "}}";
-        _loKitDocument->pClass->postUnoCommand(_loKitDocument, command.c_str(), arguments.c_str(), false);
-    }
-
-    return true;
-}
-
-bool ChildProcessSession::keyEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    int type, charcode, keycode;
-
-    if (tokens.count() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
-                         {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
-                         type) ||
-        !getTokenInteger(tokens[2], "char", charcode) ||
-        !getTokenInteger(tokens[3], "key", keycode))
-    {
-        sendTextFrame("error: cmd=key kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->postKeyEvent(_loKitDocument, type, charcode, keycode);
-
-    return true;
-}
-
-bool ChildProcessSession::mouseEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    int type, x, y, count, buttons, modifier;
-
-    if (tokens.count() != 7 ||
-        !getTokenKeyword(tokens[1], "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[5], "buttons", buttons) ||
-        !getTokenInteger(tokens[6], "modifier", modifier))
-    {
-        sendTextFrame("error: cmd=mouse kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->postMouseEvent(_loKitDocument, type, x, y, count, buttons, modifier);
-
-    return true;
-}
-
-bool ChildProcessSession::unoCommand(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    if (tokens.count() == 1)
-    {
-        sendTextFrame("error: cmd=uno kind=syntax");
-        return false;
-    }
-
-    // we need to get LOK_CALLBACK_UNO_COMMAND_RESULT callback when saving
-    bool bNotify = (tokens[1] == ".uno:Save");
-
-    if (tokens.count() == 2)
-    {
-        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), 0, bNotify);
-    }
-    else
-    {
-        _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).c_str(), bNotify);
-    }
-
-    return true;
-}
-
-bool ChildProcessSession::selectText(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    int type, x, y;
-
-    if (tokens.count() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
-                         {{"start", LOK_SETTEXTSELECTION_START},
-                          {"end", LOK_SETTEXTSELECTION_END},
-                          {"reset", LOK_SETTEXTSELECTION_RESET}},
-                         type) ||
-        !getTokenInteger(tokens[2], "x", x) ||
-        !getTokenInteger(tokens[3], "y", y))
-    {
-        sendTextFrame("error: cmd=selecttext kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->setTextSelection(_loKitDocument, type, x, y);
-
-    return true;
-}
-
-bool ChildProcessSession::selectGraphic(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    int type, x, y;
-
-    if (tokens.count() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
-                         {{"start", LOK_SETGRAPHICSELECTION_START},
-                          {"end", LOK_SETGRAPHICSELECTION_END}},
-                         type) ||
-        !getTokenInteger(tokens[2], "x", x) ||
-        !getTokenInteger(tokens[3], "y", y))
-    {
-        sendTextFrame("error: cmd=selectgraphic kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->setGraphicSelection(_loKitDocument, type, x, y);
-
-    return true;
-}
-
-bool ChildProcessSession::resetSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    if (tokens.count() != 1)
-    {
-        sendTextFrame("error: cmd=resetselection kind=syntax");
-        return false;
-    }
-
-    _loKitDocument->pClass->resetSelection(_loKitDocument);
-
-    return true;
-}
-
-bool ChildProcessSession::saveAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    std::string url, format, filterOptions;
-
-    if (tokens.count() < 4 ||
-        !getTokenString(tokens[1], "url", url))
-    {
-        sendTextFrame("error: cmd=saveas kind=syntax");
-        return false;
-    }
-
-    getTokenString(tokens[2], "format", format);
-
-    if (getTokenString(tokens[3], "options", filterOptions))
-    {
-        if (tokens.count() > 4)
-        {
-            filterOptions += Poco::cat(std::string(" "), tokens.begin() + 4, tokens.end());
-        }
-    }
-
-    bool success = _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(),
-            format.size() == 0 ? NULL :format.c_str(),
-            filterOptions.size() == 0 ? NULL : filterOptions.c_str());
-
-    sendTextFrame("saveas: url=" + url);
-    std::string successStr = success ? "true" : "false";
-    sendTextFrame("unocommandresult: {"
-            "\"commandName\":\"saveas\","
-            "\"success\":\"" + successStr + "\"}");
-
-    return true;
-}
-
-bool ChildProcessSession::setClientPart(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    if (tokens.count() < 2 ||
-        !getTokenInteger(tokens[1], "part", _clientPart))
-    {
-        return false;
-    }
-    return true;
-}
-
-bool ChildProcessSession::setPage(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens)
-{
-    int page;
-    if (tokens.count() < 2 ||
-        !getTokenInteger(tokens[1], "page", page))
-    {
-        sendTextFrame("error: cmd=setpage kind=invalid");
-        return false;
-    }
-    _loKitDocument->pClass->setPart(_loKitDocument, page);
-    return true;
-}
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 8b7175c..22a372f 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -106,54 +106,6 @@ inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT,
     }
 }
 
-class ChildProcessSession final : public LOOLSession
-{
-public:
-    ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit, std::string _childId);
-    virtual ~ChildProcessSession();
-
-    virtual bool handleInput(const char *buffer, int length) override;
-
-    virtual bool getStatus(const char *buffer, int length);
-
-    virtual bool getCommandValues(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    virtual bool getPartPageRectangles(const char *buffer, int length) override;
-
-    LibreOfficeKitDocument *_loKitDocument;
-    std::string _docType;
-
- protected:
-    virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
-
-    virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    virtual void sendFontRendering(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    bool clientZoom(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool downloadAs(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool getChildId();
-    bool getTextSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool paste(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool insertFile(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool keyEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool mouseEvent(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool unoCommand(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool selectText(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool selectGraphic(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool resetSelection(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool saveAs(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool setClientPart(const char *buffer, int length, Poco::StringTokenizer& tokens);
-    bool setPage(const char *buffer, int length, Poco::StringTokenizer& tokens);
-
-    std::string _loSubPath;
-    LibreOfficeKit *_loKit;
-    std::string _childId;
-
- private:
-    int _clientPart;
-};
-
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp
index 42aa0b4..b89fb9c 100644
--- a/loolwsd/LOOLWSD.cpp
+++ b/loolwsd/LOOLWSD.cpp
@@ -106,6 +106,7 @@ DEALINGS IN THE SOFTWARE.
 #include "LOOLProtocol.hpp"
 #include "LOOLSession.hpp"
 #include "MasterProcessSession.hpp"
+#include "ChildProcessSession.hpp"
 #include "LOOLWSD.hpp"
 #include "MessageQueue.hpp"
 #include "Util.hpp"
diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am
index 3a81c34..e34292f 100644
--- a/loolwsd/Makefile.am
+++ b/loolwsd/Makefile.am
@@ -7,7 +7,7 @@ dist_bin_SCRIPTS = loolwsd-systemplate-setup
 AM_CPPFLAGS = -pthread
 AM_LDFLAGS = -pthread
 
-loolwsd_SOURCES = LOOLWSD.cpp LOOLSession.cpp MasterProcessSession.cpp MessageQueue.cpp TileCache.cpp Util.cpp LOOLProtocol.cpp
+loolwsd_SOURCES = LOOLWSD.cpp LOOLSession.cpp MasterProcessSession.cpp ChildProcessSession.cpp MessageQueue.cpp TileCache.cpp Util.cpp LOOLProtocol.cpp
 
 noinst_PROGRAMS = loadtest connect lokitclient
 
@@ -17,7 +17,7 @@ connect_SOURCES = Connect.cpp Util.cpp LOOLProtocol.cpp
 
 lokitclient_SOURCES = LOKitClient.cpp Util.cpp
 
-noinst_HEADERS = LOKitHelper.hpp LOOLProtocol.hpp LOOLSession.hpp MasterProcessSession.hpp LOOLWSD.hpp LoadTest.hpp MessageQueue.hpp TileCache.hpp Util.hpp Png.hpp \
+noinst_HEADERS = LOKitHelper.hpp LOOLProtocol.hpp LOOLSession.hpp MasterProcessSession.hpp ChildProcessSession.hpp LOOLWSD.hpp LoadTest.hpp MessageQueue.hpp TileCache.hpp Util.hpp Png.hpp \
                  bundled/include/LibreOfficeKit/LibreOfficeKit.h bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h \
                  bundled/include/LibreOfficeKit/LibreOfficeKitInit.h bundled/include/LibreOfficeKit/LibreOfficeKitTypes.h
 


More information about the Libreoffice-commits mailing list