[Libreoffice-commits] online.git: gtk/mobile.cpp net/FakeSocket.hpp wsd/LOOLWSD.cpp

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Wed Oct 17 19:49:45 UTC 2018


 gtk/mobile.cpp     |  217 +++++++++++++++++++++++++++++++++++++++++++++++++----
 net/FakeSocket.hpp |    2 
 wsd/LOOLWSD.cpp    |    7 +
 3 files changed, 210 insertions(+), 16 deletions(-)

New commits:
commit 452a0a46c2de3bec3799dd5cf05b81fc1ea49e5f
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Wed Oct 17 22:41:01 2018 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Wed Oct 17 22:48:14 2018 +0300

    More work on the GTK+ testbed app
    
    Add plumbing to send messages from the Online code to the JavaScript
    code, and vice versa. Similar to what is done for iOS.
    
    Sadly, it crashes. Multi-thread issues. Not surprisingly, it crashes
    when I call webkit_web_view_run_javascript() in another thread than
    the one where the GTK+ and other Webkit calls were done. I need to
    come up with some clever way to do everything from the same thread.
    
    (On iOS, I use dispatch_async(dispatch_get_main_queue(),...) to
    schedule a block (i.e., a lambda expression) to be run in the main
    thread.)

diff --git a/gtk/mobile.cpp b/gtk/mobile.cpp
index cdc711adc..b28e9129b 100644
--- a/gtk/mobile.cpp
+++ b/gtk/mobile.cpp
@@ -28,6 +28,7 @@
  */
 
 #include <iostream>
+#include <thread>
 
 #include <gtk/gtk.h>
 #include <webkit2/webkit2.h>
@@ -35,12 +36,85 @@
 #include "FakeSocket.hpp"
 #include "Log.hpp"
 #include "LOOLWSD.hpp"
+#include "Protocol.hpp"
 #include "Util.hpp"
 
 static void destroyWindowCb(GtkWidget* widget, GtkWidget* window);
 static gboolean closeWebViewCb(WebKitWebView* webView, GtkWidget* window);
 
-int loolwsd_server_socket_fd;
+const int SHOW_JS_MAXLEN = 70;
+
+int loolwsd_server_socket_fd = -1;
+
+static std::string fileURL;
+static LOOLWSD *loolwsd = nullptr;
+static int fakeClientFd;
+static int closeNotificationPipeForForwardingThread[2];
+static WebKitWebView *webView;
+
+static void send2JS_ready_callback(GObject      *source_object,
+                                   GAsyncResult *res,
+                                   gpointer     user_data)
+{
+    g_free(user_data);
+}
+
+static void send2JS(const std::vector<char>& buffer)
+{
+    LOG_TRC_NOFILE("Send to JS: " << LOOLProtocol::getAbbreviatedMessage(buffer.data(), buffer.size()));
+
+    std::string js;
+
+    // Check if the message is binary. We say that any message that isn't just a single line is
+    // "binary" even if that strictly speaking isn't the case; for instance the commandvalues:
+    // message has a long bunch of non-binary JSON on multiple lines. But _onMessage() in Socket.js
+    // handles it fine even if such a message, too, comes in as an ArrayBuffer. (Look for the
+    // "textMsg = String.fromCharCode.apply(null, imgBytes);".)
+
+    const char *newline = (const char *)memchr(buffer.data(), '\n', buffer.size());
+    if (newline != nullptr)
+    {
+        // The data needs to be an ArrayBuffer
+        js = "window.TheFakeWebSocket.onmessage({'data': Base64ToArrayBuffer('";
+        gchar *base64 = g_base64_encode((const guchar*)buffer.data(), buffer.size());
+        js = js + std::string(base64);
+        g_free(base64);
+        js = js + "')});";
+    }
+    else
+    {
+        const unsigned char *ubufp = (const unsigned char *)buffer.data();
+        std::vector<char> data;
+        for (int i = 0; i < buffer.size(); i++)
+        {
+            if (ubufp[i] < ' ' || ubufp[i] == '\'' || ubufp[i] == '\\')
+            {
+                data.push_back('\\');
+                data.push_back('x');
+                data.push_back("0123456789abcdef"[(ubufp[i] >> 4) & 0x0F]);
+                data.push_back("0123456789abcdef"[ubufp[i] & 0x0F]);
+            }
+            else
+            {
+                data.push_back(ubufp[i]);
+            }
+        }
+        data.push_back(0);
+
+        js = "window.TheFakeWebSocket.onmessage({'data': '";
+        js = js + std::string(buffer.data());
+        js = js + "'});";
+    }
+
+    std::string subjs = js.substr(0, std::min(std::string::size_type(SHOW_JS_MAXLEN), js.length()));
+    if (js.length() > SHOW_JS_MAXLEN)
+        subjs += "...";
+
+    LOG_TRC_NOFILE( "Evaluating JavaScript: " << subjs);
+
+    char *jscopy = strdup(js.c_str());
+    webkit_web_view_run_javascript(webView, jscopy, NULL, send2JS_ready_callback, jscopy);
+}
 
 static void handle_debug_message(WebKitUserContentManager *manager,
                                  WebKitJavascriptResult   *js_result,
@@ -48,9 +122,9 @@ static void handle_debug_message(WebKitUserContentManager *manager,
 {
     JSCValue *value = webkit_javascript_result_get_js_value(js_result);
     if (jsc_value_is_string(value))
-        std::cout << "From JS: debug: " << jsc_value_to_string(value) << std::endl;
+        LOG_TRC_NOFILE("From JS: debug: " << jsc_value_to_string(value));
     else
-        std::cout << "From JS: debug: some object" << std::endl;
+        LOG_TRC_NOFILE("From JS: debug: some object");
 }
 
 static void handle_lool_message(WebKitUserContentManager *manager,
@@ -58,10 +132,102 @@ static void handle_lool_message(WebKitUserContentManager *manager,
                                 gpointer                  user_data)
 {
     JSCValue *value = webkit_javascript_result_get_js_value(js_result);
+
     if (jsc_value_is_string(value))
-        std::cout << "From JS: lool: " << jsc_value_to_string(value) << std::endl;
+    {
+        gchar *string_value = jsc_value_to_string(value);
+
+        LOG_TRC_NOFILE("From JS: lool: " << string_value);
+
+        if (strcmp(string_value, "HULLO") == 0)
+        {
+            // Now we know that the JS has started completely
+
+            // Contact the permanently (during app lifetime) listening LOOLWSD server
+            // "public" socket
+            assert(loolwsd_server_socket_fd != -1);
+            int rc = fakeSocketConnect(fakeClientFd, loolwsd_server_socket_fd);
+            assert(rc != -1);
+
+            // Create a socket pair to notify the below thread when the document has been closed
+            fakeSocketPipe2(closeNotificationPipeForForwardingThread);
+
+            // Start another thread to read responses and forward them to the JavaScript
+            std::thread([]
+                        {
+                            Util::setThreadName("app2js");
+                            while (true)
+                            {
+                               struct pollfd pollfd[2];
+                               pollfd[0].fd = fakeClientFd;
+                               pollfd[0].events = POLLIN;
+                               pollfd[1].fd = closeNotificationPipeForForwardingThread[1];
+                               pollfd[1].events = POLLIN;
+                               if (fakeSocketPoll(pollfd, 2, -1) > 0)
+                               {
+                                   if (pollfd[1].revents == POLLIN)
+                                   {
+                                       // The code below handling the "BYE" fake Websocket
+                                       // message has closed the other end of the
+                                       // closeNotificationPipeForForwardingThread. Let's close
+                                       // the other end too just for cleanliness, even if a
+                                       // FakeSocket as such is not a system resource so nothing
+                                       // is saved by closing it.
+                                       fakeSocketClose(closeNotificationPipeForForwardingThread[1]);
+
+                                       // Close our end of the fake socket connection to the
+                                       // ClientSession thread, so that it terminates
+                                       fakeSocketClose(fakeClientFd);
+
+                                       return;
+                                   }
+                                   if (pollfd[0].revents == POLLIN)
+                                   {
+                                       int n = fakeSocketAvailableDataLength(fakeClientFd);
+                                       if (n == 0)
+                                           return;
+                                       std::vector<char> buf(n);
+                                       n = fakeSocketRead(fakeClientFd, buf.data(), n);
+                                       send2JS(buf);
+                                   }
+                               }
+                               else
+                                   break;
+                           }
+                           assert(false);
+                        }).detach();
+
+            // First we simply send it the URL. This corresponds to the GET request with Upgrade to
+            // WebSocket.
+            LOG_TRC_NOFILE("Actually sending to Online:" << fileURL);
+
+            struct pollfd pollfd;
+            pollfd.fd = fakeClientFd;
+            pollfd.events = POLLOUT;
+            fakeSocketPoll(&pollfd, 1, -1);
+            fakeSocketWrite(fakeClientFd, fileURL.c_str(), fileURL.size());
+        }
+        else if (strcmp(string_value, "BYE") == 0)
+        {
+            LOG_TRC_NOFILE("Document window terminating on JavaScript side. Closing our end of the socket.");
+
+            // Close one end of the socket pair, that will wake up the forwarding thread above
+            fakeSocketClose(closeNotificationPipeForForwardingThread[0]);
+
+            // ???
+        }
+        else
+        {
+            struct pollfd pollfd;
+            pollfd.fd = fakeClientFd;
+            pollfd.events = POLLOUT;
+            fakeSocketPoll(&pollfd, 1, -1);
+            fakeSocketWrite(fakeClientFd, string_value, strlen(string_value));
+        }
+        g_free(string_value);
+    }
     else
-        std::cout << "From JS: lool: some object" << std::endl;
+        LOG_TRC_NOFILE("From JS: lool: some object");
 }
 
 static void handle_error_message(WebKitUserContentManager *manager,
@@ -70,9 +236,9 @@ static void handle_error_message(WebKitUserContentManager *manager,
 {
     JSCValue *value = webkit_javascript_result_get_js_value(js_result);
     if (jsc_value_is_string(value))
-        std::cout << "From JS: error: " << jsc_value_to_string(value) << std::endl;
+        LOG_TRC_NOFILE("From JS: error: " << jsc_value_to_string(value));
     else
-        std::cout << "From JS: error: some object" << std::endl;
+        LOG_TRC_NOFILE("From JS: error: some object");
 }
 
 int main(int argc, char* argv[])
@@ -84,6 +250,24 @@ int main(int argc, char* argv[])
                                      LOG_TRC_NOFILE(line);
                                  });
 
+    std::thread([]
+                {
+                    assert(loolwsd == nullptr);
+                    char *argv[2];
+                    argv[0] = strdup("mobile");
+                    argv[1] = nullptr;
+                    Util::setThreadName("app");
+                    while (true)
+                    {
+                        loolwsd = new LOOLWSD();
+                        loolwsd->run(1, argv);
+                        delete loolwsd;
+                        LOG_TRC("One run of LOOLWSD completed");
+                    }
+                }).detach();
+
+    fakeClientFd = fakeSocketSocket();
+
     // Initialize GTK+
     gtk_init(&argc, &argv);
 
@@ -103,7 +287,7 @@ int main(int argc, char* argv[])
     webkit_user_content_manager_register_script_message_handler(userContentManager, "error");
 
     // Create a browser instance
-    WebKitWebView *webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(userContentManager));
+    webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(userContentManager));
 
     // Put the browser area into the main window
     gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(webView));
@@ -113,13 +297,17 @@ int main(int argc, char* argv[])
     g_signal_connect(main_window, "destroy", G_CALLBACK(destroyWindowCb), NULL);
     g_signal_connect(webView, "close", G_CALLBACK(closeWebViewCb), main_window);
 
+    fileURL = "file://" TOPSRCDIR "/test/data/hello-world.odt";
+
+    std::string urlAndQuery =
+        "file://" TOPSRCDIR "/loleaflet/dist/loleaflet.html"
+        "?file_path=" + fileURL +
+        "&closebutton=1"
+        "&permission=edit"
+        "&debug=true";
+
     // Load a web page into the browser instance
-    webkit_web_view_load_uri(webView,
-                             "file://" TOPSRCDIR "/loleaflet/dist/loleaflet.html"
-                             "?file_path=" TOPSRCDIR "/test/data/hello-world.odt"
-                             "&closebutton=1"
-                             "&permission=edit"
-                             "&debug=true");
+    webkit_web_view_load_uri(webView, urlAndQuery.c_str());
 
     // Make sure that when the browser area becomes visible, it will get mouse
     // and keyboard events
@@ -134,7 +322,6 @@ int main(int argc, char* argv[])
     return 0;
 }
 
-
 static void destroyWindowCb(GtkWidget* widget, GtkWidget* window)
 {
     gtk_main_quit();
diff --git a/net/FakeSocket.hpp b/net/FakeSocket.hpp
index 5b02f507f..0f80e0a23 100644
--- a/net/FakeSocket.hpp
+++ b/net/FakeSocket.hpp
@@ -16,9 +16,11 @@
 
 #include <poll.h>
 
+#ifndef __linux
 #ifndef SOCK_NONBLOCK
 #define SOCK_NONBLOCK 0x100
 #endif
+#endif
 
 void fakeSocketSetLoggingCallback(void (*)(const std::string&));
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 448b92748..1f0c112b8 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -2610,10 +2610,15 @@ private:
     /// Process the discovery.xml file and return as string.
     static std::string getDiscoveryXML()
     {
-        // http://server/hosting/discovery
+#if defined __linux && defined MOBILEAPP
+        // discovery.xml is in the top directory
+        std::string discoveryPath = Path(Application::instance().commandPath()).parent().parent().toString() + "discovery.xml";
+#else
         std::string discoveryPath = Path(Application::instance().commandPath()).parent().toString() + "discovery.xml";
+#endif
         if (!File(discoveryPath).exists())
         {
+            // http://server/hosting/discovery.xml
             discoveryPath = LOOLWSD::FileServerRoot + "/discovery.xml";
         }
 


More information about the Libreoffice-commits mailing list