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@gmail.com>
Reviewed-by: Tor Lillqvist <tml@collabora.com>
private/tml/ios-gen2
Tor Lillqvist 2020-04-24 10:46:54 +03:00
parent 68e5901a8d
commit 7f25109f72
28 changed files with 504 additions and 153 deletions

View File

@ -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 \

View File

@ -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);

View File

@ -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: */

View File

@ -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

View File

@ -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: */

View File

@ -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();

View File

@ -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 */,

View File

@ -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) {

View File

@ -19,6 +19,7 @@
@public
int fakeClientFd;
NSURL *copyFileURL;
unsigned appDocId;
}
@property (weak) DocumentViewController *viewController;

View File

@ -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];

View File

@ -20,7 +20,6 @@
@property (strong) NSURL *slideshowURL;
- (void)bye;
+ (DocumentViewController*)singleton;
@end

View File

@ -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),
// deallocateDocumentDataForMobileAppDocId(self.document->appDocId);
[[NSFileManager defaultManager] removeItemAtURL:self.document->copyFileURL error:nil];
// The dismissViewControllerAnimated must be done on the main queue.
dispatch_async(dispatch_get_main_queue(),
^{
// Wait for lokit_main thread to exit
std::lock_guard<std::mutex> lock(LOOLWSD::lokit_main_mutex);
theSingleton = 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];
});
[self dismissDocumentViewController];
});
}
+ (DocumentViewController*)singleton {
return theSingleton;
}
@end
// vim:set shiftwidth=4 softtabstop=4 expandtab:

View File

@ -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>

View File

@ -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>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property (strong, nonatomic) UIWindow * window;
@end
// vim:set shiftwidth=4 softtabstop=4 expandtab:

View File

@ -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"
@implementation SceneDelegate
// Nothing needed so far, the window property in the .h file is enough.
@end
// vim:set shiftwidth=4 softtabstop=4 expandtab:

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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"));
}];
}

View File

@ -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

View File

@ -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)
{

View File

@ -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();

View File

@ -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;

View File

@ -653,6 +653,11 @@ public:
void alertAllUsers(const std::string& /*cmd*/, const std::string& /*kind*/) override
{
}
unsigned getMobileAppDocId() override
{
return 0;
}
};
void WhiteBoxTests::testEmptyCellCursor()

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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 [!]