[Libreoffice-commits] online.git: android/lib common/MobileApp.cpp common/MobileApp.hpp common/SigUtil.cpp common/SigUtil.hpp ios/config.h.in ios/ios.h ios/ios.mm ios/Mobile ios/Mobile.xcodeproj kit/ChildSession.cpp kit/ChildSession.hpp kit/Kit.cpp kit/Kit.hpp Makefile.am net/FakeSocket.cpp test/WhiteBoxTests.cpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp wsd/LOOLWSD.hpp
Tor Lillqvist (via logerrit)
logerrit at kemper.freedesktop.org
Fri Jun 26 11:10:12 UTC 2020
Makefile.am | 2
android/lib/src/main/cpp/androidapp.cpp | 4
common/MobileApp.cpp | 46 +++++++
common/MobileApp.hpp | 57 ++++++++
common/SigUtil.cpp | 14 --
common/SigUtil.hpp | 25 ++-
ios/Mobile.xcodeproj/project.pbxproj | 12 +
ios/Mobile/AppDelegate.mm | 32 ++--
ios/Mobile/CODocument.h | 1
ios/Mobile/CODocument.mm | 12 +
ios/Mobile/DocumentViewController.h | 1
ios/Mobile/DocumentViewController.mm | 48 ++-----
ios/Mobile/Info.plist.in | 19 ++
ios/Mobile/SceneDelegate.h | 18 ++
ios/Mobile/SceneDelegate.m | 20 +++
ios/config.h.in | 4
ios/ios.h | 3
ios/ios.mm | 4
kit/ChildSession.cpp | 12 -
kit/ChildSession.hpp | 2
kit/Kit.cpp | 209 +++++++++++++++++++++++++++-----
kit/Kit.hpp | 7 -
net/FakeSocket.cpp | 5
test/WhiteBoxTests.cpp | 5
wsd/DocumentBroker.cpp | 23 +--
wsd/DocumentBroker.hpp | 7 -
wsd/LOOLWSD.cpp | 59 ++++++---
wsd/LOOLWSD.hpp | 4
28 files changed, 503 insertions(+), 152 deletions(-)
New commits:
commit 7f25109f72738706359a63c9062764699f00f568
Author: Tor Lillqvist <tml at collabora.com>
AuthorDate: Fri Apr 24 10:46:54 2020 +0300
Commit: Tor Lillqvist <tml at collabora.com>
CommitDate: Fri Jun 26 13:09:51 2020 +0200
tdf#128502: Chunk of work to enable "multi-tasking" in the iOS app
Seems to not cause any serious regressions in the iOS app or in "make
run", but of course I am not able to run a comprehensive check of all
functionality.
Change-Id: I44a0e8d60bdbc0a885db88475961575c5e95ce88
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/93037
Tested-by: Jenkins
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Tor Lillqvist <tml at collabora.com>
diff --git a/Makefile.am b/Makefile.am
index ca868c8e7..171a07e36 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -93,6 +93,7 @@ shared_sources = common/FileUtil.cpp \
common/Session.cpp \
common/Seccomp.cpp \
common/MessageQueue.cpp \
+ common/MobileApp.cpp \
common/SigUtil.cpp \
common/SpookyV2.cpp \
common/Unit.cpp \
@@ -250,6 +251,7 @@ shared_headers = common/Common.hpp \
common/Authorization.hpp \
common/MessageQueue.hpp \
common/Message.hpp \
+ common/MobileApp.hpp \
common/Png.hpp \
common/Rectangle.hpp \
common/SigUtil.hpp \
diff --git a/android/lib/src/main/cpp/androidapp.cpp b/android/lib/src/main/cpp/androidapp.cpp
index a3f847135..7570f96ca 100644
--- a/android/lib/src/main/cpp/androidapp.cpp
+++ b/android/lib/src/main/cpp/androidapp.cpp
@@ -225,10 +225,6 @@ Java_org_libreoffice_androidlib_LOActivity_postMobileMessageNative(JNIEnv *env,
// is saved by closing it.
fakeSocketClose(closeNotificationPipeForForwardingThread[1]);
- // Flag to make the inter-thread plumbing in the Online
- // bits go away quicker.
- MobileTerminationFlag = true;
-
// Close our end of the fake socket connection to the
// ClientSession thread, so that it terminates
fakeSocketClose(currentFakeClientFd);
diff --git a/common/MobileApp.cpp b/common/MobileApp.cpp
new file mode 100644
index 000000000..15706216b
--- /dev/null
+++ b/common/MobileApp.cpp
@@ -0,0 +1,46 @@
+/* -*- 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 <cassert>
+#include <map>
+#include <mutex>
+
+#include "MobileApp.hpp"
+
+#if MOBILEAPP
+
+static std::map<unsigned, DocumentData> idToDocDataMap;
+static std::mutex idToDocDataMapMutex;
+
+DocumentData &allocateDocumentDataForMobileAppDocId(unsigned docId)
+{
+ const std::lock_guard<std::mutex> lock(idToDocDataMapMutex);
+
+ assert(idToDocDataMap.find(docId) == idToDocDataMap.end());
+ idToDocDataMap[docId] = DocumentData();
+ return idToDocDataMap[docId];
+}
+
+DocumentData &getDocumentDataForMobileAppDocId(unsigned docId)
+{
+ const std::lock_guard<std::mutex> lock(idToDocDataMapMutex);
+
+ assert(idToDocDataMap.find(docId) != idToDocDataMap.end());
+ return idToDocDataMap[docId];
+}
+
+void deallocateDocumentDataForMobileAppDocId(unsigned docId)
+{
+ assert(idToDocDataMap.find(docId) != idToDocDataMap.end());
+ idToDocDataMap.erase(docId);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/MobileApp.hpp b/common/MobileApp.hpp
new file mode 100644
index 000000000..fc816113c
--- /dev/null
+++ b/common/MobileApp.hpp
@@ -0,0 +1,57 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#if MOBILEAPP
+
+#include <LibreOfficeKit/LibreOfficeKit.hxx>
+
+#ifdef IOS
+#import "CODocument.h"
+#endif
+
+// On iOS at least we want to be able to have several documents open in the same app process.
+
+// It is somewhat complicated to make sure we access the same LibreOfficeKit object for the document
+// in both the iOS-specific Objective-C++ code and in the mostly generic Online C++ code.
+
+// We pass around a numeric ever-increasing document identifier that gets biumped for each document
+// the system asks the app to open.
+
+// For iOS, it is the static std::atomic<unsigned> appDocIdCounter in CODocument.mm.
+
+// In practice it will probably be equivalent to the DocumentBroker::DocBrokerId or the number that
+// the core SfxViewShell::GetDocId() returns, but there might be situations where multi-threading
+// and opening of several documents in sequence very quickly might cause discrepancies, so it is
+// better to usea different counter to be sure. Patches to use just one counter welcome.
+
+struct DocumentData
+{
+ lok::Document *loKitDocument;
+#ifdef IOS
+ CODocument *coDocument;
+#endif
+
+ DocumentData() :
+ loKitDocument(nullptr)
+#ifdef IOS
+ , coDocument(nil)
+#endif
+ {
+ }
+};
+
+DocumentData &allocateDocumentDataForMobileAppDocId(unsigned docId);
+DocumentData &getDocumentDataForMobileAppDocId(unsigned docId);
+void deallocateDocumentDataForMobileAppDocId(unsigned docId);
+
+#endif
diff --git a/common/SigUtil.cpp b/common/SigUtil.cpp
index 4891010db..dff88b8a6 100644
--- a/common/SigUtil.cpp
+++ b/common/SigUtil.cpp
@@ -38,14 +38,10 @@
#include "Common.hpp"
#include "Log.hpp"
+#ifndef IOS
static std::atomic<bool> TerminationFlag(false);
static std::atomic<bool> DumpGlobalState(false);
-#if MOBILEAPP
-std::atomic<bool> MobileTerminationFlag(false);
-#else
-// Mobile defines its own, which is constexpr.
static std::atomic<bool> ShutdownRequestFlag(false);
-#endif
namespace SigUtil
{
@@ -71,6 +67,7 @@ namespace SigUtil
}
#endif
+#if !MOBILEAPP
bool getDumpGlobalState()
{
return DumpGlobalState;
@@ -80,11 +77,7 @@ namespace SigUtil
{
DumpGlobalState = false;
}
-}
-#if !MOBILEAPP
-namespace SigUtil
-{
/// This traps the signal-handler so we don't _Exit
/// while dumping stack trace. It's re-entrant.
/// Used to safely increment and decrement the signal-handler trap.
@@ -384,8 +377,9 @@ namespace SigUtil
return false;
}
+#endif // !MOBILEAPP
}
-#endif // !MOBILEAPP
+#endif // !IOS
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/SigUtil.hpp b/common/SigUtil.hpp
index d3eaf9726..9cc5b69d4 100644
--- a/common/SigUtil.hpp
+++ b/common/SigUtil.hpp
@@ -13,13 +13,9 @@
#include <mutex>
#include <signal.h>
-#if MOBILEAPP
-static constexpr bool ShutdownRequestFlag(false);
-extern std::atomic<bool> MobileTerminationFlag;
-#endif
-
namespace SigUtil
{
+#ifndef IOS
/// Get the flag used to commence clean shutdown.
/// requestShutdown() is used to set the flag.
bool getShutdownRequestFlag();
@@ -33,14 +29,29 @@ namespace SigUtil
/// Only necessary in Mobile.
void resetTerminationFlag();
#endif
+#else
+ // In the mobile apps we have no need to shut down the app.
+ inline constexpr bool getShutdownRequestFlag()
+ {
+ return false;
+ }
+
+ inline constexpr bool getTerminationFlag()
+ {
+ return false;
+ }
+
+ inline void setTerminationFlag()
+ {
+ }
+#endif
+#if !MOBILEAPP
/// Get the flag to dump internal state.
bool getDumpGlobalState();
/// Reset the flag to dump internal state.
void resetDumpGlobalState();
-#if !MOBILEAPP
-
/// Wait for the signal handler, if any,
/// and prevent _Exit while collecting backtrace.
void waitSigHandlerTrap();
diff --git a/ios/Mobile.xcodeproj/project.pbxproj b/ios/Mobile.xcodeproj/project.pbxproj
index bedc02c3b..fbdb3e17c 100644
--- a/ios/Mobile.xcodeproj/project.pbxproj
+++ b/ios/Mobile.xcodeproj/project.pbxproj
@@ -66,6 +66,8 @@
BEBF3EB0246EB1C800415E87 /* RequestDetails.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEBF3EAF246EB1C800415E87 /* RequestDetails.cpp */; };
BECD984024336DD400016117 /* device-mobile.css in Resources */ = {isa = PBXBuildFile; fileRef = BECD983E24336DD400016117 /* device-mobile.css */; };
BECD984124336DD400016117 /* device-tablet.css in Resources */ = {isa = PBXBuildFile; fileRef = BECD983F24336DD400016117 /* device-tablet.css */; };
+ BEDCC84E2452F82800FB02BD /* MobileApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEDCC84C2452F82800FB02BD /* MobileApp.cpp */; };
+ BEDCC8992456FFAD00FB02BD /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BEDCC8982456FFAC00FB02BD /* SceneDelegate.m */; };
BEFB1EE121C29CC70081D757 /* L10n.mm in Sources */ = {isa = PBXBuildFile; fileRef = BEFB1EE021C29CC70081D757 /* L10n.mm */; };
/* End PBXBuildFile section */
@@ -1176,6 +1178,10 @@
BECBD41423D9C98500DA5582 /* svddrgmt.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = svddrgmt.cxx; path = "../../ios-device/svx/source/svdraw/svddrgmt.cxx"; sourceTree = "<group>"; };
BECD983E24336DD400016117 /* device-mobile.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = "device-mobile.css"; path = "../../../loleaflet/dist/device-mobile.css"; sourceTree = "<group>"; };
BECD983F24336DD400016117 /* device-tablet.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = "device-tablet.css"; path = "../../../loleaflet/dist/device-tablet.css"; sourceTree = "<group>"; };
+ BEDCC84C2452F82800FB02BD /* MobileApp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MobileApp.cpp; sourceTree = "<group>"; };
+ BEDCC84D2452F82800FB02BD /* MobileApp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MobileApp.hpp; sourceTree = "<group>"; };
+ BEDCC8972456FFAC00FB02BD /* SceneDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = "<group>"; };
+ BEDCC8982456FFAC00FB02BD /* SceneDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = "<group>"; };
BEDCC943246175E100FB02BD /* sessionlistener.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = sessionlistener.cxx; path = "../../ios-device/framework/source/services/sessionlistener.cxx"; sourceTree = "<group>"; };
BEDCC944246175E100FB02BD /* substitutepathvars.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = substitutepathvars.cxx; path = "../../ios-device/framework/source/services/substitutepathvars.cxx"; sourceTree = "<group>"; };
BEDCC945246175E100FB02BD /* pathsettings.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = pathsettings.cxx; path = "../../ios-device/framework/source/services/pathsettings.cxx"; sourceTree = "<group>"; };
@@ -1885,6 +1891,8 @@
BE58E129217F295B00249358 /* Log.hpp */,
BE5EB5BD213FE29900E0826C /* MessageQueue.cpp */,
BE58E12D217F295B00249358 /* MessageQueue.hpp */,
+ BEDCC84C2452F82800FB02BD /* MobileApp.cpp */,
+ BEDCC84D2452F82800FB02BD /* MobileApp.hpp */,
BE58E12A217F295B00249358 /* Png.hpp */,
BE5EB5BF213FE29900E0826C /* Protocol.cpp */,
BE58E12E217F295B00249358 /* Protocol.hpp */,
@@ -2276,6 +2284,8 @@
BE8D77312136762500AC58EA /* DocumentViewController.mm */,
BEFB1EDF21C29CC70081D757 /* L10n.h */,
BEFB1EE021C29CC70081D757 /* L10n.mm */,
+ BEDCC8972456FFAC00FB02BD /* SceneDelegate.h */,
+ BEDCC8982456FFAC00FB02BD /* SceneDelegate.m */,
BE80E45C21B6CEF100859C97 /* TemplateSectionHeaderView.h */,
BE80E45D21B6CEF200859C97 /* TemplateSectionHeaderView.m */,
BE80E45621B68F5000859C97 /* TemplateCollectionViewController.h */,
@@ -3032,8 +3042,10 @@
BE5EB5C3213FE29900E0826C /* Session.cpp in Sources */,
BE5EB5D22140039100E0826C /* LOOLWSD.cpp in Sources */,
BEFB1EE121C29CC70081D757 /* L10n.mm in Sources */,
+ BEDCC8992456FFAD00FB02BD /* SceneDelegate.m in Sources */,
BE5EB5C6213FE29900E0826C /* SigUtil.cpp in Sources */,
BE5EB5C1213FE29900E0826C /* Log.cpp in Sources */,
+ BEDCC84E2452F82800FB02BD /* MobileApp.cpp in Sources */,
BE5EB5DA2140363100E0826C /* ios.mm in Sources */,
BE5EB5D421400DC100E0826C /* DocumentBroker.cpp in Sources */,
BEA28377214FFD8C00848631 /* Unit.cpp in Sources */,
diff --git a/ios/Mobile/AppDelegate.mm b/ios/Mobile/AppDelegate.mm
index b76158806..9e5f19278 100644
--- a/ios/Mobile/AppDelegate.mm
+++ b/ios/Mobile/AppDelegate.mm
@@ -30,8 +30,6 @@
#import "LOOLWSD.hpp"
#import "Util.hpp"
-static LOOLWSD *loolwsd = nullptr;
-
NSString *app_locale;
static void download(NSURL *source, NSURL *destination) {
@@ -206,6 +204,9 @@ static void updateTemplates(NSData *data, NSURLResponse *response)
comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(OUString::fromUtf8(OString([app_locale UTF8String])), true));
+ // This fires off a thread running the LOKit runLoop()
+ runKitLoopInAThread();
+
// Look for the setting indicating the URL for a file containing a list of URLs for template
// documents to download. If set, start a task to download it, and then to download the listed
// templates.
@@ -247,26 +248,30 @@ static void updateTemplates(NSData *data, NSURLResponse *response)
fakeSocketSetLoggingCallback([](const std::string& line)
{
- LOG_TRC_NOFILE(line);
+ LOG_INF(line);
});
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
- assert(loolwsd == nullptr);
char *argv[2];
argv[0] = strdup([[NSBundle mainBundle].executablePath UTF8String]);
argv[1] = nullptr;
Util::setThreadName("app");
- while (true) {
- loolwsd = new LOOLWSD();
- loolwsd->run(1, argv);
- delete loolwsd;
- LOG_TRC("One run of LOOLWSD completed");
- }
+ auto loolwsd = new LOOLWSD();
+ loolwsd->run(1, argv);
+
+ // Should never return
+ assert(false);
+ NSLog(@"lolwsd->run() unexpectedly returned");
+ std::abort();
});
return YES;
}
+- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) {
+ return [UISceneConfiguration configurationWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
+}
+
- (void)applicationWillResignActive:(UIApplication *)application {
}
@@ -285,17 +290,14 @@ static void updateTemplates(NSData *data, NSURLResponse *response)
std::_Exit(1);
}
+// This method is called when you use the "Share > Open in Collabora Office" functionality in the
+// Files app. Possibly also in other use cases.
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)inputURL options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
// Ensure the URL is a file URL
if (!inputURL.isFileURL) {
return NO;
}
- // If we already have a document open, close it
- if (lok_document != nullptr && [DocumentViewController singleton] != nil) {
- [[DocumentViewController singleton] bye];
- }
-
// Reveal / import the document at the URL
DocumentBrowserViewController *documentBrowserViewController = (DocumentBrowserViewController *)self.window.rootViewController;
[documentBrowserViewController revealDocumentAtURL:inputURL importIfNeeded:YES completion:^(NSURL * _Nullable revealedDocumentURL, NSError * _Nullable error) {
diff --git a/ios/Mobile/CODocument.h b/ios/Mobile/CODocument.h
index 79f772a1b..7aa262c25 100644
--- a/ios/Mobile/CODocument.h
+++ b/ios/Mobile/CODocument.h
@@ -19,6 +19,7 @@
@public
int fakeClientFd;
NSURL *copyFileURL;
+ unsigned appDocId;
}
@property (weak) DocumentViewController *viewController;
diff --git a/ios/Mobile/CODocument.mm b/ios/Mobile/CODocument.mm
index 7eaa9c90f..c12bd2e91 100644
--- a/ios/Mobile/CODocument.mm
+++ b/ios/Mobile/CODocument.mm
@@ -32,6 +32,7 @@
#import "KitHelper.hpp"
#import "Log.hpp"
#import "LOOLWSD.hpp"
+#import "MobileApp.hpp"
#import "Protocol.hpp"
@implementation CODocument
@@ -40,6 +41,12 @@
return [NSData dataWithContentsOfFile:[copyFileURL path] options:0 error:errorPtr];
}
+// We keep a running count of opening documents here. This is not necessarily in sync with the
+// DocBrokerId in DocumentBroker due to potential parallelism when opening multiple documents in
+// quick succession.
+
+static std::atomic<unsigned> appDocIdCounter(1);
+
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)errorPtr {
// If this method is called a second time on the same CODocument object, just ignore it. This
@@ -59,10 +66,13 @@
NSURL *url = [[NSBundle mainBundle] URLForResource:@"loleaflet" withExtension:@"html"];
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
+ appDocId = appDocIdCounter++;
+ allocateDocumentDataForMobileAppDocId(appDocId).coDocument = self;
components.queryItems = @[ [NSURLQueryItem queryItemWithName:@"file_path" value:[copyFileURL absoluteString]],
[NSURLQueryItem queryItemWithName:@"closebutton" value:@"1"],
[NSURLQueryItem queryItemWithName:@"permission" value:@"edit"],
- [NSURLQueryItem queryItemWithName:@"lang" value:app_locale]
+ [NSURLQueryItem queryItemWithName:@"lang" value:app_locale],
+ [NSURLQueryItem queryItemWithName:@"appdocid" value:[NSString stringWithFormat:@"%u", appDocId]],
];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:components.URL];
diff --git a/ios/Mobile/DocumentViewController.h b/ios/Mobile/DocumentViewController.h
index 297255999..cb845b302 100644
--- a/ios/Mobile/DocumentViewController.h
+++ b/ios/Mobile/DocumentViewController.h
@@ -20,7 +20,6 @@
@property (strong) NSURL *slideshowURL;
- (void)bye;
-+ (DocumentViewController*)singleton;
@end
diff --git a/ios/Mobile/DocumentViewController.mm b/ios/Mobile/DocumentViewController.mm
index c94161c02..3f8744fe4 100644
--- a/ios/Mobile/DocumentViewController.mm
+++ b/ios/Mobile/DocumentViewController.mm
@@ -22,13 +22,12 @@
#import "FakeSocket.hpp"
#import "LOOLWSD.hpp"
#import "Log.hpp"
+#import "MobileApp.hpp"
#import "SigUtil.hpp"
#import "Util.hpp"
#import "DocumentViewController.h"
-static DocumentViewController* theSingleton = nil;
-
@interface DocumentViewController() <WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler, UIScrollViewDelegate, UIDocumentPickerDelegate> {
int closeNotificationPipeForForwardingThread[2];
NSURL *downloadAsTmpURL;
@@ -79,8 +78,6 @@ static IMP standardImpOfInputAccessoryView = nil;
- (void)viewDidLoad {
[super viewDidLoad];
- theSingleton = self;
-
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
@@ -317,10 +314,6 @@ static IMP standardImpOfInputAccessoryView = nil;
// is saved by closing it.
fakeSocketClose(self->closeNotificationPipeForForwardingThread[1]);
- // Flag to make the inter-thread plumbing in the Online
- // bits go away quicker.
- MobileTerminationFlag = true;
-
// Close our end of the fake socket connection to the
// ClientSession thread, so that it terminates
fakeSocketClose(self.document->fakeClientFd);
@@ -348,13 +341,14 @@ static IMP standardImpOfInputAccessoryView = nil;
assert(false);
});
- // First we simply send it the URL. This corresponds to the GET request with Upgrade to
- // WebSocket.
+ // First we simply send the Online C++ parts the URL and the appDocId. This corresponds
+ // to the GET request with Upgrade to WebSocket.
std::string url([[self.document->copyFileURL absoluteString] UTF8String]);
p.fd = self.document->fakeClientFd;
p.events = POLLOUT;
fakeSocketPoll(&p, 1, -1);
- fakeSocketWrite(self.document->fakeClientFd, url.c_str(), url.size());
+ std::string message(url + " " + std::to_string(self.document->appDocId));
+ fakeSocketWrite(self.document->fakeClientFd, message.c_str(), message.size());
return;
} else if ([message.body isEqualToString:@"BYE"]) {
@@ -369,7 +363,7 @@ static IMP standardImpOfInputAccessoryView = nil;
self.slideshowFile = Util::createRandomTmpDir() + "/slideshow.svg";
self.slideshowURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:self.slideshowFile.c_str()] isDirectory:NO];
- lok_document->saveAs([[self.slideshowURL absoluteString] UTF8String], "svg", nullptr);
+ getDocumentDataForMobileAppDocId(self.document->appDocId).loKitDocument->saveAs([[self.slideshowURL absoluteString] UTF8String], "svg", nullptr);
// Add a new full-screen WebView displaying the slideshow.
@@ -423,7 +417,7 @@ static IMP standardImpOfInputAccessoryView = nil;
std::string printFile = Util::createRandomTmpDir() + "/print.pdf";
NSURL *printURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:printFile.c_str()] isDirectory:NO];
- lok_document->saveAs([[printURL absoluteString] UTF8String], "pdf", nullptr);
+ getDocumentDataForMobileAppDocId(self.document->appDocId).loKitDocument->saveAs([[printURL absoluteString] UTF8String], "pdf", nullptr);
UIPrintInteractionController *pic = [UIPrintInteractionController sharedPrintController];
UIPrintInfo *printInfo = [UIPrintInfo printInfo];
@@ -476,7 +470,7 @@ static IMP standardImpOfInputAccessoryView = nil;
std::remove([[downloadAsTmpURL path] UTF8String]);
- lok_document->saveAs([[downloadAsTmpURL absoluteString] UTF8String], [format UTF8String], nullptr);
+ getDocumentDataForMobileAppDocId(self.document->appDocId).loKitDocument->saveAs([[downloadAsTmpURL absoluteString] UTF8String], [format UTF8String], nullptr);
// Then verify that it indeed was saved, and then use an
// UIDocumentPickerViewController to ask the user where to store the exported
@@ -526,31 +520,17 @@ static IMP standardImpOfInputAccessoryView = nil;
// Close one end of the socket pair, that will wake up the forwarding thread above
fakeSocketClose(closeNotificationPipeForForwardingThread[0]);
- // We can't wait for the LOOLWSD::lokit_main_mutex directly here because this is called on the
- // main queue and the main queue must be ready to execute code dispatched by the system APIs
- // used to do document saving.
- dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
- ^{
- // Wait for lokit_main thread to exit
- std::lock_guard<std::mutex> lock(LOOLWSD::lokit_main_mutex);
-
- theSingleton = nil;
+ // deallocateDocumentDataForMobileAppDocId(self.document->appDocId);
- [[NSFileManager defaultManager] removeItemAtURL:self.document->copyFileURL error:nil];
+ [[NSFileManager defaultManager] removeItemAtURL:self.document->copyFileURL error:nil];
- // And only then let the document browsing view show up again. The
- // dismissViewControllerAnimated must be done on the main queue.
- dispatch_async(dispatch_get_main_queue(),
- ^{
- [self dismissDocumentViewController];
- });
+ // The dismissViewControllerAnimated must be done on the main queue.
+ dispatch_async(dispatch_get_main_queue(),
+ ^{
+ [self dismissDocumentViewController];
});
}
-+ (DocumentViewController*)singleton {
- return theSingleton;
-}
-
@end
// vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/ios/Mobile/Info.plist.in b/ios/Mobile/Info.plist.in
index 54d68f36c..eb4b08e9b 100644
--- a/ios/Mobile/Info.plist.in
+++ b/ios/Mobile/Info.plist.in
@@ -421,6 +421,25 @@
<string>share/fonts/truetype/opens___.ttf</string>
@IOSAPP_FONTS@
</array>
+ <key>UIApplicationSceneManifest</key>
+ <dict>
+ <key>UIApplicationSupportsMultipleScenes</key>
+ <true/>
+ <key>UISceneConfigurations</key>
+ <dict>
+ <key>UIWindowSceneSessionRoleApplication</key>
+ <array>
+ <dict>
+ <key>UISceneDelegateClassName</key>
+ <string>SceneDelegate</string>
+ <key>UISceneConfigurationName</key>
+ <string>Default Configuration</string>
+ <key>UISceneStoryboardFile</key>
+ <string>Main</string>
+ </dict>
+ </array>
+ </dict>
+ </dict>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
diff --git a/ios/Mobile/SceneDelegate.h b/ios/Mobile/SceneDelegate.h
new file mode 100644
index 000000000..d0c8f4d8a
--- /dev/null
+++ b/ios/Mobile/SceneDelegate.h
@@ -0,0 +1,18 @@
+// -*- Mode: ObjC; 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/.
+
+#import <UIKit/UIKit.h>
+
+ at interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
+
+ at property (strong, nonatomic) UIWindow * window;
+
+ at end
+
+// vim:set shiftwidth=4 softtabstop=4 expandtab:
+
diff --git a/ios/Mobile/SceneDelegate.m b/ios/Mobile/SceneDelegate.m
new file mode 100644
index 000000000..b403d7d6d
--- /dev/null
+++ b/ios/Mobile/SceneDelegate.m
@@ -0,0 +1,20 @@
+// -*- Mode: ObjC; 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/.
+
+#import <UIKit/UIKit.h>
+
+#import "SceneDelegate.h"
+
+ at implementation SceneDelegate
+
+// Nothing needed so far, the window property in the .h file is enough.
+
+ at end
+
+// vim:set shiftwidth=4 softtabstop=4 expandtab:
+
diff --git a/ios/config.h.in b/ios/config.h.in
index 18ec2613d..05934c203 100644
--- a/ios/config.h.in
+++ b/ios/config.h.in
@@ -89,10 +89,10 @@
#undef LT_OBJDIR
/* Limit the maximum number of open connections */
-#define MAX_CONNECTIONS 3
+#define MAX_CONNECTIONS 100
/* Limit the maximum number of open documents */
-#define MAX_DOCUMENTS 1
+#define MAX_DOCUMENTS 100
/* Define to 1 if this is a mobileapp (eg. Android) build. */
#define MOBILEAPP 1
diff --git a/ios/ios.h b/ios/ios.h
index a4283cefb..198b9e90a 100644
--- a/ios/ios.h
+++ b/ios/ios.h
@@ -7,12 +7,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-#include <mutex>
-
#include <LibreOfficeKit/LibreOfficeKit.hxx>
extern int loolwsd_server_socket_fd;
-extern lok::Document *lok_document;
extern LibreOfficeKit *lo_kit;
diff --git a/ios/ios.mm b/ios/ios.mm
index 97c49c3e9..b5c6ac65b 100644
--- a/ios/ios.mm
+++ b/ios/ios.mm
@@ -6,8 +6,6 @@
// 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 <cstring>
-
#include "ios.h"
#import <Foundation/Foundation.h>
@@ -18,7 +16,7 @@ extern "C" {
}
int loolwsd_server_socket_fd = -1;
-lok::Document *lok_document = nullptr;
+
LibreOfficeKit *lo_kit;
// vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index ca9701e9a..cc8f7413e 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -10,6 +10,7 @@
#include <config.h>
#include "ChildSession.hpp"
+#include "MobileApp.hpp"
#include <climits>
#include <fstream>
@@ -32,10 +33,6 @@
#include <Poco/Net/AcceptCertificateHandler.h>
#endif
-#ifdef IOS
-#import "DocumentViewController.h"
-#endif
-
#include <common/FileUtil.hpp>
#include <common/JsonUtil.hpp>
#include <common/Authorization.hpp>
@@ -2486,11 +2483,10 @@ void ChildSession::loKitCallback(const int type, const std::string& payload)
if (!commandName.isEmpty() && commandName.toString() == ".uno:Save" && !success.isEmpty() && success.toString() == "true")
{
- CODocument *document = [[DocumentViewController singleton] document];
-
+ CODocument *document = getDocumentDataForMobileAppDocId(_docManager->getMobileAppDocId()).coDocument;
[document saveToURL:[document fileURL]
- forSaveOperation:UIDocumentSaveForOverwriting
- completionHandler:^(BOOL success) {
+ forSaveOperation:UIDocumentSaveForOverwriting
+ completionHandler:^(BOOL success) {
LOG_TRC("ChildSession::loKitCallback() save completion handler gets " << (success?"YES":"NO"));
}];
}
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 4c4dbb2ec..48edf98b2 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -71,6 +71,8 @@ public:
virtual bool sendFrame(const char* buffer, int length, WSOpCode opCode = WSOpCode::Text) = 0;
virtual void alertAllUsers(const std::string& cmd, const std::string& kind) = 0;
+
+ virtual unsigned getMobileAppDocId() = 0;
};
struct RecordedEvent
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 743251fef..269120e60 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -52,6 +52,7 @@
#include "ChildSession.hpp"
#include <Common.hpp>
+#include <MobileApp.hpp>
#include <FileUtil.hpp>
#include "KitHelper.hpp"
#include "Kit.hpp"
@@ -768,7 +769,8 @@ public:
const std::string& docId,
const std::string& url,
std::shared_ptr<TileQueue> tileQueue,
- const std::shared_ptr<WebSocketHandler>& websocketHandler)
+ const std::shared_ptr<WebSocketHandler>& websocketHandler,
+ unsigned mobileAppDocId)
: _loKit(loKit),
_jailId(jailId),
_docKey(docKey),
@@ -784,7 +786,8 @@ public:
_stop(false),
_isLoading(0),
_editorId(-1),
- _editorChangeWarning(false)
+ _editorChangeWarning(false),
+ _mobileAppDocId(mobileAppDocId)
{
LOG_INF("Document ctor for [" << _docKey <<
"] url [" << anonymizeUrl(_url) << "] on child [" << _jailId <<
@@ -809,6 +812,11 @@ public:
{
session.second->resetDocManager();
}
+
+#ifdef IOS
+ deallocateDocumentDataForMobileAppDocId(_mobileAppDocId);
+#endif
+
}
const std::string& getUrl() const { return _url; }
@@ -1247,6 +1255,11 @@ public:
alertAllUsers("errortoall: cmd=" + cmd + " kind=" + kind);
}
+ unsigned getMobileAppDocId() override
+ {
+ return _mobileAppDocId;
+ }
+
static void GlobalCallback(const int type, const char* p, void* data)
{
if (SigUtil::getTerminationFlag())
@@ -1465,9 +1478,6 @@ private:
#endif
LOG_INF("Document [" << anonymizeUrl(_url) << "] has no more views, but has " <<
_sessions.size() << " sessions still. Destroying the document.");
-#ifdef IOS
- lok_document = nullptr;
-#endif
#ifdef __ANDROID__
_loKitDocumentForAndroidOnly.reset();
#endif
@@ -1716,9 +1726,7 @@ private:
const double totalTime = elapsed/1000.;
LOG_DBG("Returned lokit::documentLoad(" << FileUtil::anonymizeUrl(pURL) << ") in " << totalTime << "ms.");
#ifdef IOS
- // The iOS app (and the Android one) has max one document open at a time, so we can keep
- // a pointer to it in a global.
- lok_document = _loKitDocument.get();
+ getDocumentDataForMobileAppDocId(_mobileAppDocId).loKitDocument = _loKitDocument.get();
#endif
if (!_loKitDocument || !_loKitDocument->get())
{
@@ -2135,6 +2143,8 @@ private:
#ifdef __ANDROID__
friend std::shared_ptr<lok::Document> getLOKDocumentForAndroidOnly();
#endif
+
+ const unsigned _mobileAppDocId;
};
#ifdef __ANDROID__
@@ -2153,10 +2163,42 @@ class KitSocketPoll : public SocketPoll
std::chrono::steady_clock::time_point _pollEnd;
std::shared_ptr<Document> _document;
-public:
KitSocketPoll() :
SocketPoll("kit")
{
+#ifdef IOS
+ terminationFlag = false;
+#endif
+ }
+
+public:
+ ~KitSocketPoll()
+ {
+#ifdef IOS
+ std::unique_lock<std::mutex> lock(KSPollsMutex);
+ std::shared_ptr<KitSocketPoll> p;
+ auto i = KSPolls.begin();
+ for (; i != KSPolls.end(); ++i)
+ {
+ p = i->lock();
+ if (p && p.get() == this)
+ break;
+ }
+ assert(i != KSPolls.end());
+ KSPolls.erase(i);
+#endif
+ }
+
+ static std::shared_ptr<KitSocketPoll> create()
+ {
+ KitSocketPoll *p = new KitSocketPoll();
+ auto result = std::shared_ptr<KitSocketPoll>(p);
+#ifdef IOS
+ std::unique_lock<std::mutex> lock(KSPollsMutex);
+ KSPolls.push_back(result);
+ // KSPollsCV.notify_one();
+#endif
+ return result;
}
// process pending message-queue events.
@@ -2229,8 +2271,26 @@ public:
{
_document = std::move(document);
}
+
+#ifdef IOS
+ static std::mutex KSPollsMutex;
+ // static std::condition_variable KSPollsCV;
+ static std::vector<std::weak_ptr<KitSocketPoll>> KSPolls;
+
+ std::mutex terminationMutex;
+ std::condition_variable terminationCV;
+ bool terminationFlag;
+#endif
};
+#ifdef IOS
+
+std::mutex KitSocketPoll::KSPollsMutex;
+// std::condition_variable KitSocketPoll::KSPollsCV;
+std::vector<std::weak_ptr<KitSocketPoll>> KitSocketPoll::KSPolls;
+
+#endif
+
class KitWebSocketHandler final : public WebSocketHandler
{
std::shared_ptr<TileQueue> _queue;
@@ -2238,16 +2298,18 @@ class KitWebSocketHandler final : public WebSocketHandler
std::shared_ptr<lok::Office> _loKit;
std::string _jailId;
std::shared_ptr<Document> _document;
- KitSocketPoll &_ksPoll;
+ std::shared_ptr<KitSocketPoll> _ksPoll;
+ const unsigned _mobileAppDocId;
public:
- KitWebSocketHandler(const std::string& socketName, const std::shared_ptr<lok::Office>& loKit, const std::string& jailId, KitSocketPoll& ksPoll) :
+ KitWebSocketHandler(const std::string& socketName, const std::shared_ptr<lok::Office>& loKit, const std::string& jailId, std::shared_ptr<KitSocketPoll> ksPoll, unsigned mobileAppDocId) :
WebSocketHandler(/* isClient = */ true, /* isMasking */ false),
_queue(std::make_shared<TileQueue>()),
_socketName(socketName),
_loKit(loKit),
_jailId(jailId),
- _ksPoll(ksPoll)
+ _ksPoll(ksPoll),
+ _mobileAppDocId(mobileAppDocId)
{
}
@@ -2296,14 +2358,16 @@ protected:
std::string url;
URI::decode(docKey, url);
LOG_INF("New session [" << sessionId << "] request on url [" << url << "].");
+#ifndef IOS
Util::setThreadName("kit_" + docId);
-
+#endif
if (!_document)
{
_document = std::make_shared<Document>(
_loKit, _jailId, docKey, docId, url, _queue,
- std::static_pointer_cast<WebSocketHandler>(shared_from_this()));
- _ksPoll.setDocument(_document);
+ std::static_pointer_cast<WebSocketHandler>(shared_from_this()),
+ _mobileAppDocId);
+ _ksPoll->setDocument(_document);
}
// Validate and create session.
@@ -2319,9 +2383,16 @@ protected:
LOG_INF("Terminating immediately due to parent 'exit' command.");
Log::shutdown();
std::_Exit(EX_SOFTWARE);
+#else
+#ifdef IOS
+ LOG_INF("Setting our KitSocketPoll's termination flag due to 'exit' command.");
+ std::unique_lock<std::mutex> lock(_ksPoll->terminationMutex);
+ _ksPoll->terminationFlag = true;
+ _ksPoll->terminationCV.notify_all();
#else
LOG_INF("Setting TerminationFlag due to 'exit' command.");
SigUtil::setTerminationFlag();
+#endif
_document.reset();
#endif
}
@@ -2359,6 +2430,11 @@ protected:
#if !MOBILEAPP
LOG_WRN("Kit connection lost without exit arriving from wsd. Setting TerminationFlag");
SigUtil::setTerminationFlag();
+#endif
+#ifdef IOS
+ std::unique_lock<std::mutex> lock(_ksPoll->terminationMutex);
+ _ksPoll->terminationFlag = true;
+ _ksPoll->terminationCV.notify_all();
#endif
}
};
@@ -2371,19 +2447,62 @@ void documentViewCallback(const int type, const char* payload, void* data)
/// Called by LOK main-loop the central location for data processing.
int pollCallback(void* pData, int timeoutUs)
{
+#ifndef IOS
if (!pData)
return 0;
else
return reinterpret_cast<KitSocketPoll*>(pData)->kitPoll(timeoutUs);
+#else
+ std::unique_lock<std::mutex> lock(KitSocketPoll::KSPollsMutex);
+ if (KitSocketPoll::KSPolls.size() == 0)
+ {
+ // KitSocketPoll::KSPollsCV.wait(lock);
+ lock.unlock();
+ std::this_thread::sleep_for(std::chrono::microseconds(timeoutUs));
+ }
+ else
+ {
+ std::vector<std::shared_ptr<KitSocketPoll>> v;
+ for (const auto &i : KitSocketPoll::KSPolls)
+ {
+ auto p = i.lock();
+ if (p)
+ v.push_back(p);
+ }
+ lock.unlock();
+ for (const auto &p : v)
+ p->kitPoll(timeoutUs);
+ }
+
+ // We never want to exit the main loop
+ return 0;
+#endif
}
/// Called by LOK main-loop
void wakeCallback(void* pData)
{
+#ifndef IOS
if (!pData)
return;
else
return reinterpret_cast<KitSocketPoll*>(pData)->wakeup();
+#else
+ std::unique_lock<std::mutex> lock(KitSocketPoll::KSPollsMutex);
+ if (KitSocketPoll::KSPolls.size() == 0)
+ return;
+
+ std::vector<std::shared_ptr<KitSocketPoll>> v;
+ for (const auto &i : KitSocketPoll::KSPolls)
+ {
+ auto p = i.lock();
+ if (p)
+ v.push_back(p);
+ }
+ lock.unlock();
+ for (const auto &p : v)
+ p->wakeup();
+#endif
}
void setupKitEnvironment()
@@ -2435,10 +2554,9 @@ void lokit_main(
bool queryVersion,
bool displayVersion,
#else
- const std::string& documentUri,
int docBrokerSocket,
#endif
- size_t spareKitId
+ size_t numericIdentifier
)
{
#if !MOBILEAPP
@@ -2448,7 +2566,7 @@ void lokit_main(
SigUtil::setTerminationSignals();
#endif
- Util::setThreadName("kit_spare_" + Util::encodeId(spareKitId, 3));
+ Util::setThreadName("kit_spare_" + Util::encodeId(numericIdentifier, 3));
// Reinitialize logging when forked.
const bool logToFile = std::getenv("LOOL_LOGFILE");
@@ -2497,8 +2615,6 @@ void lokit_main(
std::shared_ptr<lok::Office> loKit;
Path jailPath;
ChildSession::NoCapsForKit = noCapabilities;
-#else
- AnonymizeUserData = false;
#endif // MOBILEAPP
try
@@ -2699,8 +2815,11 @@ void lokit_main(
#else // MOBILEAPP
- // was not done by the preload
+#ifndef IOS
+ // Was not done by the preload.
+ // For iOS we call it in -[AppDelegate application: didFinishLaunchingWithOptions:]
setupKitEnvironment();
+#endif
#if defined(__linux) && !defined(__ANDROID__)
Poco::URI userInstallationURI("file", LO_PATH);
@@ -2727,16 +2846,16 @@ void lokit_main(
#endif // MOBILEAPP
- KitSocketPoll mainKit;
- mainKit.runOnClientThread(); // We will do the polling on this thread.
+ auto mainKit = KitSocketPoll::create();
+ mainKit->runOnClientThread(); // We will do the polling on this thread.
std::shared_ptr<KitWebSocketHandler> websocketHandler =
- std::make_shared<KitWebSocketHandler>("child_ws", loKit, jailId, mainKit);
+ std::make_shared<KitWebSocketHandler>("child_ws", loKit, jailId, mainKit, numericIdentifier);
#if !MOBILEAPP
- mainKit.insertNewUnixSocket(MasterLocation, pathAndQuery, websocketHandler, ProcSMapsFile);
+ mainKit->insertNewUnixSocket(MasterLocation, pathAndQuery, websocketHandler, ProcSMapsFile);
#else
- mainKit.insertNewFakeSocket(docBrokerSocket, websocketHandler);
+ mainKit->insertNewFakeSocket(docBrokerSocket, websocketHandler);
#endif
LOG_INF("New kit client websocket inserted.");
@@ -2749,6 +2868,7 @@ void lokit_main(
}
#endif
+#ifndef IOS
if (!LIBREOFFICEKIT_HAS(kit, runLoop))
{
LOG_ERR("Kit is missing Unipoll API");
@@ -2758,7 +2878,7 @@ void lokit_main(
LOG_INF("Kit unipoll loop run");
- loKit->runLoop(pollCallback, wakeCallback, &mainKit);
+ loKit->runLoop(pollCallback, wakeCallback, mainKit.get());
LOG_INF("Kit unipoll loop run terminated.");
@@ -2772,6 +2892,11 @@ void lokit_main(
// Let forkit handle the jail cleanup.
#endif
+
+#else // IOS
+ std::unique_lock<std::mutex> lock(mainKit->terminationMutex);
+ mainKit->terminationCV.wait(lock,[&]{ return mainKit->terminationFlag; } );
+#endif // !IOS
}
catch (const Exception& exc)
{
@@ -2793,7 +2918,35 @@ void lokit_main(
#endif
}
-#endif
+
+#ifdef IOS
+
+// In the iOS app we can have several documents open in the app process at the same time, thus
+// several lokit_main() functions running at the same time. We want just one LO main loop, though,
+// so we start it separately in its own thread.
+
+void runKitLoopInAThread()
+{
+ std::thread([&]
+ {
+ Util::setThreadName("lokit_runloop");
+
+ std::shared_ptr<lok::Office> loKit = std::make_shared<lok::Office>(lo_kit);
+ int dummy;
+ loKit->runLoop(pollCallback, wakeCallback, &dummy);
+
+ // Should never return
+ assert(false);
+
+ NSLog(@"loKit->runLoop() unexpectedly returned");
+
+ std::abort();
+ }).detach();
+}
+
+#endif // IOS
+
+#endif // !BUILDING_TESTS
std::string anonymizeUrl(const std::string& url)
{
diff --git a/kit/Kit.hpp b/kit/Kit.hpp
index 1e60fdd3a..a5c6332b8 100644
--- a/kit/Kit.hpp
+++ b/kit/Kit.hpp
@@ -37,12 +37,15 @@ void lokit_main(
bool queryVersionInfo,
bool displayVersion,
#else
- const std::string& documentUri,
int docBrokerSocket,
#endif
- size_t spareKitId
+ size_t numericIdentifier
);
+#ifdef IOS
+void runKitLoopInAThread();
+#endif
+
/// We need to get several env. vars right
void setupKitEnvironment();
diff --git a/net/FakeSocket.cpp b/net/FakeSocket.cpp
index e8fd6bbca..a4ef761e5 100644
--- a/net/FakeSocket.cpp
+++ b/net/FakeSocket.cpp
@@ -257,6 +257,11 @@ static bool checkForPoll(std::vector<FakeSocketPair>& fds, struct pollfd *pollfd
retval = true;
}
}
+ if (fds[pollfds[i].fd/2].shutdown[N])
+ {
+ pollfds[i].revents |= POLLHUP;
+ retval = true;
+ }
}
}
return retval;
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index a2e676198..47023d2fa 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -653,6 +653,11 @@ public:
void alertAllUsers(const std::string& /*cmd*/, const std::string& /*kind*/) override
{
}
+
+ unsigned getMobileAppDocId() override
+ {
+ return 0;
+ }
};
void WhiteBoxTests::testEmptyCellCursor()
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index da71968f6..fe99ed26a 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -153,14 +153,6 @@ public:
bool continuePolling() override
{
-#if MOBILEAPP
- if (MobileTerminationFlag)
- {
- LOG_TRC("Noticed MobileTerminationFlag.");
- MobileTerminationFlag = false;
- return false;
- }
-#endif
return TerminatingPoll::continuePolling();
}
@@ -176,7 +168,8 @@ std::atomic<unsigned> DocumentBroker::DocBrokerId(1);
DocumentBroker::DocumentBroker(ChildType type,
const std::string& uri,
const Poco::URI& uriPublic,
- const std::string& docKey) :
+ const std::string& docKey,
+ unsigned mobileAppDocId) :
_limitLifeSeconds(0),
_uriOrig(uri),
_type(type),
@@ -200,11 +193,16 @@ DocumentBroker::DocumentBroker(ChildType type,
_lockCtx(new LockContext()),
_tileVersion(0),
_debugRenderedTileCount(0),
- _wopiLoadDuration(0)
+ _wopiLoadDuration(0),
+ _mobileAppDocId(mobileAppDocId)
{
assert(!_docKey.empty());
assert(!LOOLWSD::ChildRoot.empty());
+#ifdef IOS
+ assert(_mobileAppDocId > 0);
+#endif
+
LOG_INF("DocumentBroker [" << LOOLWSD::anonymizeUrl(_uriPublic.toString()) <<
"] created with docKey [" << _docKey << ']');
}
@@ -242,7 +240,7 @@ void DocumentBroker::pollThread()
do
{
static const int timeoutMs = COMMAND_TIMEOUT_MS * 5;
- _childProcess = getNewChild_Blocks(getPublicUri().getPath());
+ _childProcess = getNewChild_Blocks();
if (_childProcess ||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() -
_threadStart).count() > timeoutMs)
@@ -253,7 +251,8 @@ void DocumentBroker::pollThread()
}
while (!_stop && _poll->continuePolling() && !SigUtil::getTerminationFlag() && !SigUtil::getShutdownRequestFlag());
#else
- _childProcess = getNewChild_Blocks(getPublicUri().getPath());
+ assert(_mobileAppDocId > 0);
+ _childProcess = getNewChild_Blocks(_mobileAppDocId);
#endif
if (!_childProcess)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 42d506629..4d09dff32 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -128,11 +128,11 @@ public:
/// Dummy document broker that is marked to destroy.
DocumentBroker();
- /// Construct DocumentBroker with URI, docKey, and root path.
DocumentBroker(ChildType type,
const std::string& uri,
const Poco::URI& uriPublic,
- const std::string& docKey);
+ const std::string& docKey,
+ unsigned mobileAppDocId = 0);
virtual ~DocumentBroker();
@@ -439,6 +439,9 @@ private:
/// Unique DocBroker ID for tracing and debugging.
static std::atomic<unsigned> DocBrokerId;
+
+ // Relevant only in the mobile apps
+ const unsigned _mobileAppDocId;
};
#if !MOBILEAPP
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 9c713fbde..8cdf9974a 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -119,6 +119,7 @@ using Poco::Net::PartHandler;
# include <Kit.hpp>
#endif
#include <Log.hpp>
+#include <MobileApp.hpp>
#include <Protocol.hpp>
#include <Session.hpp>
#if ENABLE_SSL
@@ -486,17 +487,20 @@ static size_t addNewChild(const std::shared_ptr<ChildProcess>& child)
}
#if MOBILEAPP
+#ifndef IOS
std::mutex LOOLWSD::lokit_main_mutex;
#endif
+#endif
-std::shared_ptr<ChildProcess> getNewChild_Blocks(const std::string& uri)
+std::shared_ptr<ChildProcess> getNewChild_Blocks(unsigned mobileAppDocId)
{
std::unique_lock<std::mutex> lock(NewChildrenMutex);
const auto startTime = std::chrono::steady_clock::now();
#if !MOBILEAPP
- (void)uri;
+ (void) mobileAppDocId;
+
LOG_DBG("getNewChild: Rebalancing children.");
int numPreSpawn = LOOLWSD::NumPreSpawnedChildren;
++numPreSpawn; // Replace the one we'll dispatch just now.
@@ -519,12 +523,15 @@ std::shared_ptr<ChildProcess> getNewChild_Blocks(const std::string& uri)
std::thread([&]
{
- std::lock_guard<std::mutex> lokit_main_lock(LOOLWSD::lokit_main_mutex);
+#ifndef IOS
+ std::lock_guard<std::mutex> lock(LOOLWSD::lokit_main_mutex);
Util::setThreadName("lokit_main");
-
- // Ugly to have that static global, otoh we know there is just one LOOLWSD
- // object. (Even in real Online.)
- lokit_main(uri, LOOLWSD::prisonerServerSocketFD, 1);
+#else
+ Util::setThreadName("lokit_main_" + Util::encodeId(mobileAppDocId, 3));
+#endif
+ // Ugly to have that static global LOOLWSD::prisonerServerSocketFD, Otoh we know
+ // there is just one LOOLWSD object. (Even in real Online.)
+ lokit_main(LOOLWSD::prisonerServerSocketFD, mobileAppDocId);
}).detach();
#endif
@@ -1853,7 +1860,8 @@ static std::shared_ptr<DocumentBroker>
const std::string& uri,
const std::string& docKey,
const std::string& id,
- const Poco::URI& uriPublic)
+ const Poco::URI& uriPublic,
+ unsigned mobileAppDocId = 0)
{
LOG_INF("Find or create DocBroker for docKey [" << docKey <<
"] for session [" << id << "] on url [" << LOOLWSD::anonymizeUrl(uriPublic.toString()) << "].");
@@ -1925,7 +1933,7 @@ static std::shared_ptr<DocumentBroker>
// Set the one we just created.
LOG_DBG("New DocumentBroker for docKey [" << docKey << "].");
- docBroker = std::make_shared<DocumentBroker>(type, uri, uriPublic, docKey);
+ docBroker = std::make_shared<DocumentBroker>(type, uri, uriPublic, docKey, mobileAppDocId);
DocBrokers.emplace(docKey, docBroker);
LOG_TRC("Have " << DocBrokers.size() << " DocBrokers after inserting [" << docKey << "].");
}
@@ -2453,12 +2461,26 @@ private:
socket->eraseFirstInputBytes(map);
#else
Poco::Net::HTTPRequest request;
- // The 2nd parameter is the response to the HULLO message (which we
- // respond with the path of the document)
+
+#ifdef IOS
+ // The URL of the document is sent over the FakeSocket by the code in
+ // -[DocumentViewController userContentController:didReceiveScriptMessage:] when it gets the
+ // HULLO message from the JavaScript in global.js.
+
+ // The "app document id", the numeric id of the document, from the appDocIdCounter in CODocument.mm.
+ char *space = strchr(socket->getInBuffer().data(), ' ');
+ assert(space != nullptr);
+ unsigned appDocId = std::strtoul(space + 1, nullptr, 10);
+
+ handleClientWsUpgrade(
+ request, std::string(socket->getInBuffer().data(), space - socket->getInBuffer().data()),
+ disposition, socket, appDocId);
+#else
handleClientWsUpgrade(
request, RequestDetails(std::string(socket->getInBuffer().data(),
socket->getInBuffer().size())),
disposition, socket);
+#endif
socket->getInBuffer().clear();
#endif
}
@@ -2946,7 +2968,6 @@ private:
throw BadRequestException("Invalid or unknown request.");
}
-#endif
void handleClientProxyRequest(const Poco::Net::HTTPRequest& request,
const RequestDetails &requestDetails,
@@ -3053,11 +3074,13 @@ private:
streamSocket->shutdown();
}
}
+#endif
void handleClientWsUpgrade(const Poco::Net::HTTPRequest& request,
const RequestDetails &requestDetails,
SocketDisposition& disposition,
- const std::shared_ptr<StreamSocket>& socket)
+ const std::shared_ptr<StreamSocket>& socket,
+ unsigned mobileAppDocId = 0)
{
const std::string url = requestDetails.getDocumentURI();
assert(socket && "Must have a valid socket");
@@ -3114,7 +3137,7 @@ private:
// Request a kit process for this doc.
std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker(
std::static_pointer_cast<ProtocolHandlerInterface>(ws),
- DocumentBroker::ChildType::Interactive, url, docKey, _id, uriPublic);
+ DocumentBroker::ChildType::Interactive, url, docKey, _id, uriPublic, mobileAppDocId);
if (docBroker)
{
std::shared_ptr<ClientSession> clientSession =
@@ -3510,11 +3533,13 @@ private:
void wakeupHook() override
{
+#if !MOBILEAPP
if (SigUtil::getDumpGlobalState())
{
dump_state();
SigUtil::resetDumpGlobalState();
}
+#endif
}
};
/// This thread & poll accepts incoming connections.
@@ -3971,7 +3996,7 @@ void LOOLWSD::cleanup()
int LOOLWSD::main(const std::vector<std::string>& /*args*/)
{
-#if MOBILEAPP
+#if MOBILEAPP && !defined IOS
SigUtil::resetTerminationFlag();
#endif
@@ -3992,10 +4017,6 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/)
UnitWSD::get().returnValue(returnValue);
-#if MOBILEAPP
- fakeSocketDumpState();
-#endif
-
LOG_INF("Process [loolwsd] finished.");
return returnValue;
}
diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp
index 8d9653cd5..54d63962f 100644
--- a/wsd/LOOLWSD.hpp
+++ b/wsd/LOOLWSD.hpp
@@ -34,7 +34,7 @@ class TraceFileWriter;
class DocumentBroker;
class ClipboardCache;
-std::shared_ptr<ChildProcess> getNewChild_Blocks(const std::string& uri);
+std::shared_ptr<ChildProcess> getNewChild_Blocks(unsigned mobileAppDocId = 0);
// A WSProcess object in the WSD process represents a descendant process, either the direct child
// process FORKIT or a grandchild KIT process, with which the WSD process communicates through a
@@ -257,8 +257,10 @@ public:
static std::set<const Poco::Util::AbstractConfiguration*> PluginConfigurations;
static std::chrono::time_point<std::chrono::system_clock> StartTime;
#if MOBILEAPP
+#ifndef IOS
/// This is used to be able to wait until the lokit main thread has finished (and it is safe to load a new document).
static std::mutex lokit_main_mutex;
+#endif
#endif
/// For testing only [!]
More information about the Libreoffice-commits
mailing list