summaryrefslogtreecommitdiff
path: root/desktop
diff options
context:
space:
mode:
authorSkyler Grey <skyler.grey@collabora.com>2023-08-18 13:30:35 +0000
committerCaolán McNamara <caolan.mcnamara@collabora.com>2023-09-01 16:55:37 +0200
commit8e246331f6f71320cfcc8defdd04e756a75f71cf (patch)
treec359388bf1fe8051e0a8d6a542afb86c961ebf45 /desktop
parentcb72f56977faccbda9490bc16ea3cbf7a22c01b0 (diff)
Add a FunctionBasedURPConnection and a websocket URP connector
- FunctionBasedURPConnection is used to enable a client to open a URP connection to a fresh Kit instance in COOL. - This URP connector can be used with that and https://github.com/CollaboraOnline/online/pull/6992 to use a Java Uno Remote Protocol client over websockets - For interoperability with existing Collabora Online websockets a prefix (urp ) is added to each message sent and a similar prefix (urp: ) is expected on each message recieved. This allows sending over the same websocket as other data is being transmitted through. If you are writing a bridge to work with this, you will need to add/strip the prefixes accordingly - This commit uses Java WebSocket (https://github.com/TooTallNate/Java-WebSocket) to send data over websockets. Change-Id: I2bda3d0b988bef7883f9b6829eeb5b7ae8075f27 Signed-off-by: Skyler Grey <skyler.grey@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151171 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Diffstat (limited to 'desktop')
-rw-r--r--desktop/qa/desktop_lib/test_desktop_lib.cxx4
-rw-r--r--desktop/source/lib/init.cxx227
2 files changed, 230 insertions, 1 deletions
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 35cbbfe60c15..8a55ae18172d 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -3681,10 +3681,12 @@ void DesktopLOKTest::testABI()
CPPUNIT_ASSERT_EQUAL(classOffset(15), offsetof(struct _LibreOfficeKitClass, dumpState));
CPPUNIT_ASSERT_EQUAL(classOffset(16), offsetof(struct _LibreOfficeKitClass, extractRequest));
CPPUNIT_ASSERT_EQUAL(classOffset(17), offsetof(struct _LibreOfficeKitClass, trimMemory));
+ CPPUNIT_ASSERT_EQUAL(classOffset(18), offsetof(struct _LibreOfficeKitClass, startURP));
+ CPPUNIT_ASSERT_EQUAL(classOffset(19), offsetof(struct _LibreOfficeKitClass, stopURP));
// When extending LibreOfficeKit with a new function pointer, add new assert for the offsetof the
// new function pointer and bump this assert for the size of the class.
- CPPUNIT_ASSERT_EQUAL(classOffset(18), sizeof(struct _LibreOfficeKitClass));
+ CPPUNIT_ASSERT_EQUAL(classOffset(20), sizeof(struct _LibreOfficeKitClass));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass, destroy));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass, saveAs));
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 6db6f722bee6..4b81619cbc1f 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -49,6 +49,7 @@
#include <memory>
#include <iostream>
#include <string_view>
+#include <queue>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string.hpp>
@@ -86,6 +87,7 @@
#include <comphelper/servicehelper.hxx>
#include <comphelper/sequenceashashmap.hxx>
+#include <com/sun/star/connection/XConnection.hpp>
#include <com/sun/star/document/MacroExecMode.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
@@ -108,6 +110,10 @@
#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/document/XRedlinesSupplier.hpp>
#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
+#include <com/sun/star/bridge/BridgeFactory.hpp>
+#include <com/sun/star/bridge/XBridgeFactory.hpp>
+#include <com/sun/star/bridge/XBridge.hpp>
+#include <com/sun/star/uno/XNamingService.hpp>
#include <com/sun/star/xml/crypto/SEInitializer.hpp>
#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
@@ -121,6 +127,7 @@
#include <com/sun/star/i18n/LocaleCalendar2.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <editeng/flstitem.hxx>
#ifdef IOS
@@ -228,6 +235,9 @@ using namespace css;
using namespace vcl;
using namespace desktop;
using namespace utl;
+using namespace bridge;
+using namespace uno;
+using namespace lang;
static LibLibreOffice_Impl *gImpl = nullptr;
static bool lok_preinit_2_called = false;
@@ -2567,6 +2577,13 @@ static char* lo_extractRequest(LibreOfficeKit* pThis,
static void lo_trimMemory(LibreOfficeKit* pThis, int nTarget);
+static int
+lo_startURP(LibreOfficeKit* pThis, void* pReceiveURPFromLOContext, void** pSendURPToLOContext,
+ int (*fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen),
+ int (**pfnSendURPToLO)(void* pContext, const signed char* pBuffer, int nLen));
+
+static void lo_stopURP(LibreOfficeKit* pThis, void* pSendURPToLOContext);
+
static void lo_runLoop(LibreOfficeKit* pThis,
LibreOfficeKitPollCallback pPollCallback,
LibreOfficeKitWakeCallback pWakeCallback,
@@ -2609,6 +2626,8 @@ LibLibreOffice_Impl::LibLibreOffice_Impl()
m_pOfficeClass->dumpState = lo_dumpState;
m_pOfficeClass->extractRequest = lo_extractRequest;
m_pOfficeClass->trimMemory = lo_trimMemory;
+ m_pOfficeClass->startURP = lo_startURP;
+ m_pOfficeClass->stopURP = lo_stopURP;
gOfficeClass = m_pOfficeClass;
}
@@ -3160,6 +3179,214 @@ static void lo_trimMemory(LibreOfficeKit* /* pThis */, int nTarget)
}
}
+namespace
+{
+class FunctionBasedURPInstanceProvider
+ : public ::cppu::WeakImplHelper<css::bridge::XInstanceProvider>
+{
+private:
+ css::uno::Reference<css::uno::XComponentContext> m_rContext;
+
+public:
+ FunctionBasedURPInstanceProvider(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+
+ // XInstanceProvider
+ virtual css::uno::Reference<css::uno::XInterface>
+ SAL_CALL getInstance(const OUString& aName) override;
+};
+
+// InstanceProvider
+FunctionBasedURPInstanceProvider::FunctionBasedURPInstanceProvider(
+ const Reference<XComponentContext>& rxContext)
+ : m_rContext(rxContext)
+{
+}
+
+Reference<XInterface> FunctionBasedURPInstanceProvider::getInstance(const OUString& aName)
+{
+ Reference<XInterface> rInstance;
+
+ if (aName == "StarOffice.ServiceManager")
+ {
+ rInstance.set(m_rContext->getServiceManager());
+ }
+ else if (aName == "StarOffice.ComponentContext")
+ {
+ rInstance = m_rContext;
+ }
+ else if (aName == "StarOffice.NamingService")
+ {
+ Reference<XNamingService> rNamingService(
+ m_rContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.uno.NamingService", m_rContext),
+ UNO_QUERY);
+ if (rNamingService.is())
+ {
+ rNamingService->registerObject("StarOffice.ServiceManager",
+ m_rContext->getServiceManager());
+ rNamingService->registerObject("StarOffice.ComponentContext", m_rContext);
+ rInstance = rNamingService;
+ }
+ }
+ return rInstance;
+}
+
+class FunctionBasedURPConnection : public cppu::WeakImplHelper<css::connection::XConnection>
+{
+public:
+ explicit FunctionBasedURPConnection(void*, int (*)(void* pContext, const signed char* pBuffer,
+ int nLen));
+ ~FunctionBasedURPConnection();
+
+ // These overridden member functions use "read" and "write" from the point of view of LO,
+ // i.e. the opposite to how startURP() uses them.
+ virtual sal_Int32 SAL_CALL read(Sequence<sal_Int8>& aReadBytes,
+ sal_Int32 nBytesToRead) override;
+ virtual void SAL_CALL write(const Sequence<sal_Int8>& aData) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL close() override;
+ virtual OUString SAL_CALL getDescription() override;
+ void setBridge(Reference<XBridge>);
+ int addClientURPToBuffer(const signed char* pBuffer, int nLen);
+ void* getContext();
+ inline static int g_connectionCount = 0;
+
+private:
+ std::shared_ptr<std::deque<signed char>> m_pBuffer;
+ void* m_pRecieveFromLOContext;
+ int (*m_fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen);
+ Reference<XBridge> m_URPBridge;
+ std::atomic<bool> m_closed = false;
+ std::condition_variable m_URPInBuffer;
+ std::mutex m_bufferMutex;
+};
+
+FunctionBasedURPConnection::FunctionBasedURPConnection(
+ void* pRecieveFromLOContext,
+ int (*fnRecieveFromLO)(void* pContext, const signed char* pBuffer, int nLen))
+ : m_pBuffer(std::make_shared<std::deque<signed char>>())
+ , m_pRecieveFromLOContext(pRecieveFromLOContext)
+ , m_fnReceiveURPFromLO(fnRecieveFromLO)
+{
+ g_connectionCount++;
+}
+
+FunctionBasedURPConnection::~FunctionBasedURPConnection()
+{
+ Reference<XComponent> xComp(m_URPBridge, UNO_QUERY_THROW);
+ xComp->dispose(); // TODO: check this doesn't deadlock
+}
+
+int sendURPToLO(void* pContext /* FunctionBasedURPConnection* */, const signed char* pBuffer,
+ int nLen)
+{
+ return static_cast<FunctionBasedURPConnection*>(pContext)->addClientURPToBuffer(pBuffer, nLen);
+}
+
+int FunctionBasedURPConnection::addClientURPToBuffer(const signed char* pBuffer, int nLen)
+{
+ {
+ std::scoped_lock lock(m_bufferMutex);
+
+ if (m_closed)
+ {
+ // We can't write URP to a closed connection
+ SAL_WARN("lok.urp", "A client attempted to write URP to a closed "
+ "FunctionBasedURPConnection... ignoring");
+ return 0;
+ }
+ m_pBuffer->insert(m_pBuffer->end(), pBuffer, pBuffer + nLen);
+ }
+ m_URPInBuffer.notify_one();
+ return nLen;
+}
+
+void* FunctionBasedURPConnection::getContext() { return this; }
+
+sal_Int32 FunctionBasedURPConnection::read(Sequence<sal_Int8>& aReadBytes, sal_Int32 nBytesToRead)
+{
+ if (aReadBytes.getLength() != nBytesToRead)
+ {
+ aReadBytes.realloc(nBytesToRead);
+ }
+
+ sal_Int8* result = aReadBytes.getArray();
+ // As with osl::StreamPipe, we must always read nBytesToRead...
+
+ {
+ std::unique_lock lock(m_bufferMutex);
+
+ if (nBytesToRead < 0)
+ {
+ return 0;
+ }
+ m_URPInBuffer.wait(
+ lock, [this, nBytesToRead] { return static_cast<sal_Int32>(m_pBuffer->size()) >= nBytesToRead; });
+
+ std::copy(m_pBuffer->begin(), m_pBuffer->begin() + nBytesToRead, result);
+ m_pBuffer->erase(m_pBuffer->begin(), m_pBuffer->begin() + nBytesToRead);
+ }
+
+ return nBytesToRead;
+}
+
+void FunctionBasedURPConnection::write(const Sequence<sal_Int8>& aData)
+{
+ m_fnReceiveURPFromLO(m_pRecieveFromLOContext, aData.getConstArray(), aData.getLength());
+}
+
+void FunctionBasedURPConnection::flush() {}
+
+void FunctionBasedURPConnection::close()
+{
+ SAL_INFO("lok.urp", "Requested to close FunctionBasedURPConnection");
+ m_closed = true;
+}
+
+OUString FunctionBasedURPConnection::getDescription() { return ""; }
+
+void FunctionBasedURPConnection::setBridge(Reference<XBridge> xBridge) { m_URPBridge = xBridge; }
+}
+
+static int
+lo_startURP(LibreOfficeKit* /* pThis */, void* pRecieveFromLOContext, void** ppSendToLOContext,
+ int (*fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen),
+ int (**pfnSendURPToLO)(void* pContext, const signed char* pBuffer, int nLen))
+{
+ // Here we will roughly do what desktop LO does when one passes a command-line switch like
+ // --accept=socket,port=nnnn;urp;StarOffice.ServiceManager. Except that no listening socket will
+ // be created. The communication to the URP will be through the fnReceiveURPFromLO and pfnSendURPToLO functions.
+
+ rtl::Reference<FunctionBasedURPConnection> connection(
+ new FunctionBasedURPConnection(pRecieveFromLOContext, fnReceiveURPFromLO));
+
+ *pfnSendURPToLO = sendURPToLO;
+ *ppSendToLOContext = connection->getContext();
+
+ Reference<XBridgeFactory> xBridgeFactory = css::bridge::BridgeFactory::create(xContext);
+
+ Reference<XInstanceProvider> xInstanceProvider(new FunctionBasedURPInstanceProvider(xContext));
+
+ Reference<XBridge> xBridge(xBridgeFactory->createBridge(
+ "functionurp" + OUString::number(FunctionBasedURPConnection::g_connectionCount), "urp",
+ connection, xInstanceProvider));
+
+ connection->setBridge(std::move(xBridge));
+
+ return true;
+}
+
+/**
+ * Stop a function based URP connection that you started with lo_startURP above
+ *
+ * @param pSendToLOContext a pointer to the context you got back using your ppSendToLOContext before */
+static void lo_stopURP(LibreOfficeKit* /* pThis */,
+ void* pSendToLOContext /* FunctionBasedURPConnection* */)
+{
+ static_cast<FunctionBasedURPConnection*>(pSendToLOContext)->close();
+}
+
static void lo_registerCallback (LibreOfficeKit* pThis,
LibreOfficeKitCallback pCallback,
void* pData)