mirror of
https://github.com/ONLYOFFICE/desktop-sdk.git
synced 2026-02-10 18:15:05 +08:00
Add run external processes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ Thumbs.db
|
|||||||
.idea/
|
.idea/
|
||||||
*Makefile*
|
*Makefile*
|
||||||
*qmake.stash
|
*qmake.stash
|
||||||
|
.qtc_clangd
|
||||||
|
|||||||
@ -121,6 +121,9 @@ SOURCES += \
|
|||||||
$$PWD/src/keychain.cpp \
|
$$PWD/src/keychain.cpp \
|
||||||
$$PWD/src/filelocker.cpp
|
$$PWD/src/filelocker.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/src/cefwrapper/external_process.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$CORE_ROOT_DIR/Common/OfficeFileFormatChecker2.cpp \
|
$$CORE_ROOT_DIR/Common/OfficeFileFormatChecker2.cpp \
|
||||||
$$CORE_ROOT_DIR/Common/3dParty/pole/pole.cpp \
|
$$CORE_ROOT_DIR/Common/3dParty/pole/pole.cpp \
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#include "./external_process.h"
|
||||||
|
|
||||||
#include "./client_renderer.h"
|
#include "./client_renderer.h"
|
||||||
|
|
||||||
@ -592,7 +593,7 @@ namespace asc_client_renderer
|
|||||||
IMPLEMENT_REFCOUNTING(CLocalFileConvertV8Handler);
|
IMPLEMENT_REFCOUNTING(CLocalFileConvertV8Handler);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CAscEditorNativeV8Handler : public CefV8Handler, public CAIToolsHelper
|
class CAscEditorNativeV8Handler : public CefV8Handler, public CAIToolsHelper, public NSProcesses::CProcessRunnerCallback
|
||||||
{
|
{
|
||||||
class CSavedPageInfo
|
class CSavedPageInfo
|
||||||
{
|
{
|
||||||
@ -717,6 +718,10 @@ namespace asc_client_renderer
|
|||||||
bool m_bIsMacrosesSupport;
|
bool m_bIsMacrosesSupport;
|
||||||
bool m_bIsPluginsSupport;
|
bool m_bIsPluginsSupport;
|
||||||
|
|
||||||
|
// External processes
|
||||||
|
CefRefPtr<CefFrame> m_frame;
|
||||||
|
NSProcesses::CProcessManager* m_external_processes;
|
||||||
|
|
||||||
CAscEditorNativeV8Handler(const std::wstring& sUrl)
|
CAscEditorNativeV8Handler(const std::wstring& sUrl)
|
||||||
{
|
{
|
||||||
m_etType = AscEditorType::etUndefined;
|
m_etType = AscEditorType::etUndefined;
|
||||||
@ -767,6 +772,8 @@ namespace asc_client_renderer
|
|||||||
|
|
||||||
m_bEditorsCloudFeaturesCheck = false;
|
m_bEditorsCloudFeaturesCheck = false;
|
||||||
m_arCloudFeaturesBlackList.push_back("personal.onlyoffice.com");
|
m_arCloudFeaturesBlackList.push_back("personal.onlyoffice.com");
|
||||||
|
|
||||||
|
m_external_processes = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckDefaults()
|
void CheckDefaults()
|
||||||
@ -811,6 +818,9 @@ namespace asc_client_renderer
|
|||||||
|
|
||||||
virtual ~CAscEditorNativeV8Handler()
|
virtual ~CAscEditorNativeV8Handler()
|
||||||
{
|
{
|
||||||
|
RELEASEOBJECT(m_external_processes);
|
||||||
|
m_frame.reset();
|
||||||
|
|
||||||
if (m_pAES_Key)
|
if (m_pAES_Key)
|
||||||
NSOpenSSL::openssl_free(m_pAES_Key);
|
NSOpenSSL::openssl_free(m_pAES_Key);
|
||||||
NSBase::Release(m_pLocalApplicationFonts);
|
NSBase::Release(m_pLocalApplicationFonts);
|
||||||
@ -939,6 +949,33 @@ return undefined; \n\
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void process_callback(const int& id, const NSProcesses::StreamType& type, const std::string& message) OVERRIDE
|
||||||
|
{
|
||||||
|
if (m_frame && m_external_processes)
|
||||||
|
{
|
||||||
|
std::string sMessageDst = message;
|
||||||
|
NSStringUtils::string_replaceA(sMessageDst, "\\", "\\\\");
|
||||||
|
NSStringUtils::string_replaceA(sMessageDst, "\"", "\\\"");
|
||||||
|
NSStringUtils::string_replaceA(sMessageDst, "\r", "");
|
||||||
|
NSStringUtils::string_replaceA(sMessageDst, "\n", "");
|
||||||
|
|
||||||
|
std::string sCode = "(function(){\n\
|
||||||
|
if (!window._external_process_callback) return;\n\
|
||||||
|
if (!window._external_process_callback[" + std::to_string(id) + "]) return;\n\
|
||||||
|
window._external_process_callback[" + std::to_string(id) + "]._onprocess(" + std::to_string((int)type) + ", \"" + sMessageDst + "\");\n\
|
||||||
|
})();";
|
||||||
|
|
||||||
|
CefPostTask(TID_RENDERER, base::BindOnce([](CefRefPtr<CefFrame> frame, const std::string& code) {
|
||||||
|
if (frame) {
|
||||||
|
frame->ExecuteJavaScript(
|
||||||
|
code,
|
||||||
|
frame->GetURL(), 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
m_frame, sCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool Execute(const CefString& sMessageName, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) OVERRIDE
|
virtual bool Execute(const CefString& sMessageName, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) OVERRIDE
|
||||||
{
|
{
|
||||||
std::string name = sMessageName.ToString();
|
std::string name = sMessageName.ToString();
|
||||||
@ -2425,6 +2462,35 @@ window.AscDesktopEditor.attachEvent=function(name,callback){if(undefined===windo
|
|||||||
window.AscDesktopEditor.LocalFileTemplates=function(e){window.__lang_checker_templates__=e||\"\",window.__resize_checker_templates__||(window.__resize_checker_templates__=!0,window.addEventListener(\"resize\",function(){window.AscDesktopEditor._LocalFileTemplates(window.__lang_checker_templates__,(100*window.devicePixelRatio)>>0)})),window.AscDesktopEditor._LocalFileTemplates(window.__lang_checker_templates__,(100*window.devicePixelRatio)>>0)};";
|
window.AscDesktopEditor.LocalFileTemplates=function(e){window.__lang_checker_templates__=e||\"\",window.__resize_checker_templates__||(window.__resize_checker_templates__=!0,window.addEventListener(\"resize\",function(){window.AscDesktopEditor._LocalFileTemplates(window.__lang_checker_templates__,(100*window.devicePixelRatio)>>0)})),window.AscDesktopEditor._LocalFileTemplates(window.__lang_checker_templates__,(100*window.devicePixelRatio)>>0)};";
|
||||||
|
|
||||||
_frame->ExecuteJavaScript(sCodeInitJS, _frame->GetURL(), 0);
|
_frame->ExecuteJavaScript(sCodeInitJS, _frame->GetURL(), 0);
|
||||||
|
|
||||||
|
std::string sUrl = _frame->GetURL().ToString();
|
||||||
|
if (0 == sUrl.find("file:///") || 0 == sUrl.find("onlyoffice://"))
|
||||||
|
{
|
||||||
|
std::string sCode = "function ExternalProcess(command)\n\
|
||||||
|
{\n\
|
||||||
|
this.command = command;this.id = -1;\n\
|
||||||
|
this.start = function() {\n\
|
||||||
|
this.id = AscDesktopEditor._createProcess(this.command);\n\
|
||||||
|
window._external_process_callback[this.id] = this;\n\
|
||||||
|
};\n\
|
||||||
|
this.end = function() {\n\
|
||||||
|
if (window._external_process_callback[this.id])\n\
|
||||||
|
delete window._external_process_callback[this.id];\n\
|
||||||
|
AscDesktopEditor._endProcess(this.id);\n\
|
||||||
|
};\n\
|
||||||
|
this._onprocess = function(type, message) {\n\
|
||||||
|
if (2 === type && window._external_process_callback[this.id])\n\
|
||||||
|
delete window._external_process_callback[this.id];\n\
|
||||||
|
if (this.onprocess)\n\
|
||||||
|
this.onprocess(type, message);\n\
|
||||||
|
};\n\
|
||||||
|
if (!window._external_process_callback) {\n\
|
||||||
|
window._external_process_callback = {};\n\
|
||||||
|
}\n\
|
||||||
|
}";
|
||||||
|
|
||||||
|
_frame->ExecuteJavaScript(sCode, _frame->GetURL(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -4505,6 +4571,24 @@ window.AscDesktopEditor.CallInFrame(\"" +
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (name == "_createProcess")
|
||||||
|
{
|
||||||
|
if (!m_external_processes)
|
||||||
|
m_external_processes = new NSProcesses::CProcessManager(this);
|
||||||
|
|
||||||
|
std::string command = arguments[0]->GetStringValue().ToString();
|
||||||
|
int process_id = m_external_processes->Start(command);
|
||||||
|
|
||||||
|
retval = CefV8Value::CreateInt(process_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (name == "_endProcess")
|
||||||
|
{
|
||||||
|
int process_id = arguments[0]->GetIntValue();
|
||||||
|
if (m_external_processes)
|
||||||
|
m_external_processes->End(process_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Function does not exist.
|
// Function does not exist.
|
||||||
return false;
|
return false;
|
||||||
@ -5097,6 +5181,17 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
|||||||
message_router_ = CefMessageRouterRendererSide::Create(config);
|
message_router_ = CefMessageRouterRendererSide::Create(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void OnBrowserCreated(CefRefPtr<client::ClientAppRenderer> app,
|
||||||
|
CefRefPtr<CefBrowser> browser,
|
||||||
|
CefRefPtr<CefDictionaryValue> extra_info) OVERRIDE
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnBrowserDestroyed(CefRefPtr<client::ClientAppRenderer> app,
|
||||||
|
CefRefPtr<CefBrowser> browser) OVERRIDE
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual void OnContextCreated(CefRefPtr<client::ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE
|
virtual void OnContextCreated(CefRefPtr<client::ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE
|
||||||
{
|
{
|
||||||
message_router_->OnContextCreated(browser, frame, context);
|
message_router_->OnContextCreated(browser, frame, context);
|
||||||
@ -5119,7 +5214,7 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
|||||||
|
|
||||||
CefRefPtr<CefV8Handler> handler = pWrapper;
|
CefRefPtr<CefV8Handler> handler = pWrapper;
|
||||||
|
|
||||||
#define EXTEND_METHODS_COUNT 190
|
#define EXTEND_METHODS_COUNT 192
|
||||||
const char* methods[EXTEND_METHODS_COUNT] = {
|
const char* methods[EXTEND_METHODS_COUNT] = {
|
||||||
"Copy",
|
"Copy",
|
||||||
"Paste",
|
"Paste",
|
||||||
@ -5384,6 +5479,9 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
|||||||
"_convertFileExternal",
|
"_convertFileExternal",
|
||||||
"_onConvertFileExternal",
|
"_onConvertFileExternal",
|
||||||
|
|
||||||
|
"_createProcess",
|
||||||
|
"_endProcess",
|
||||||
|
|
||||||
NULL};
|
NULL};
|
||||||
|
|
||||||
ExtendObject(obj, handler, methods);
|
ExtendObject(obj, handler, methods);
|
||||||
@ -5453,6 +5551,8 @@ return this.split(str).join(newStr);\
|
|||||||
#else
|
#else
|
||||||
browser->SendProcessMessage(PID_BROWSER, message);
|
browser->SendProcessMessage(PID_BROWSER, message);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
pWrapper->m_frame = curFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnContextReleased(CefRefPtr<client::ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE
|
virtual void OnContextReleased(CefRefPtr<client::ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE
|
||||||
|
|||||||
211
ChromiumBasedEditors/lib/src/cefwrapper/external_process.h
Normal file
211
ChromiumBasedEditors/lib/src/cefwrapper/external_process.h
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/process.hpp>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "../../../../../core/DesktopEditor/common/File.h"
|
||||||
|
#include "../../../../../core/DesktopEditor/graphics/TemporaryCS.h"
|
||||||
|
|
||||||
|
namespace NSProcesses
|
||||||
|
{
|
||||||
|
enum class StreamType
|
||||||
|
{
|
||||||
|
StdOut,
|
||||||
|
StdErr,
|
||||||
|
Stop
|
||||||
|
};
|
||||||
|
|
||||||
|
class CProcessRunnerCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CProcessRunnerCallback(){}
|
||||||
|
virtual ~CProcessRunnerCallback(){}
|
||||||
|
|
||||||
|
virtual void process_callback(const int& id, const StreamType& type, const std::string& message) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CProcessRunner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CProcessRunner(const int& id, const std::string& command, CProcessRunnerCallback* cb)
|
||||||
|
: m_command(command), m_callback(cb), m_running(false), m_id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CProcessRunner()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
if (m_running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_running = true;
|
||||||
|
|
||||||
|
m_worker = std::thread([this]() {
|
||||||
|
run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
if (!m_running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_running = false;
|
||||||
|
if (m_proc.valid() && m_proc.running())
|
||||||
|
m_proc.terminate();
|
||||||
|
|
||||||
|
if (m_worker.joinable())
|
||||||
|
m_worker.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_id()
|
||||||
|
{
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
boost::process::ipstream out;
|
||||||
|
boost::process::ipstream err;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::wstring commandW = UTF8_TO_U(m_command);
|
||||||
|
m_proc = boost::process::child(commandW, boost::process::std_out > out, boost::process::std_err > err);
|
||||||
|
#else
|
||||||
|
m_proc = boost::process::child(m_command, boost::process::std_out > out, boost::process::std_err > err);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
m_callback->process_callback(m_id, StreamType::StdErr, std::string("Failed to start process: ") + ex.what() + "\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread t_out([this, &out]() {
|
||||||
|
std::string line;
|
||||||
|
while (m_running && std::getline(out, line))
|
||||||
|
{
|
||||||
|
m_callback->process_callback(m_id, StreamType::StdOut, line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
std::thread t_err([this, &err]() {
|
||||||
|
std::string line;
|
||||||
|
while (m_running && std::getline(err, line))
|
||||||
|
{
|
||||||
|
m_callback->process_callback(m_id, StreamType::StdErr, line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (m_proc.running())
|
||||||
|
m_proc.wait();
|
||||||
|
|
||||||
|
m_callback->process_callback(m_id, StreamType::Stop, "");
|
||||||
|
|
||||||
|
t_out.join();
|
||||||
|
t_err.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_command;
|
||||||
|
CProcessRunnerCallback* m_callback;
|
||||||
|
std::thread m_worker;
|
||||||
|
std::atomic<bool> m_running;
|
||||||
|
boost::process::child m_proc;
|
||||||
|
int m_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CProcessManager : public CProcessRunnerCallback
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<CProcessRunner*> m_processes;
|
||||||
|
int m_counter;
|
||||||
|
bool m_enable_callback;
|
||||||
|
CProcessRunnerCallback* m_callback;
|
||||||
|
|
||||||
|
NSCriticalSection::CRITICAL_SECTION m_cs;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CProcessManager(CProcessRunnerCallback* cb)
|
||||||
|
{
|
||||||
|
m_cs.InitializeCriticalSection();
|
||||||
|
m_counter = 1;
|
||||||
|
m_enable_callback = true;
|
||||||
|
m_callback = cb;
|
||||||
|
}
|
||||||
|
~CProcessManager()
|
||||||
|
{
|
||||||
|
StopAll();
|
||||||
|
m_cs.DeleteCriticalSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Start(const std::string& command)
|
||||||
|
{
|
||||||
|
m_cs.Enter();
|
||||||
|
int cur_id = m_counter++;
|
||||||
|
CProcessRunner* runner = new CProcessRunner(cur_id, command, this);
|
||||||
|
m_processes.push_back(runner);
|
||||||
|
m_cs.Leave();
|
||||||
|
runner->start();
|
||||||
|
return cur_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void End(const int& id)
|
||||||
|
{
|
||||||
|
CTemporaryCS oCS(&m_cs);
|
||||||
|
|
||||||
|
for (std::vector<CProcessRunner*>::iterator iter = m_processes.begin(); iter != m_processes.end(); iter++)
|
||||||
|
{
|
||||||
|
CProcessRunner* runner = *iter;
|
||||||
|
if (runner->get_id() == id)
|
||||||
|
{
|
||||||
|
delete runner;
|
||||||
|
m_processes.erase(iter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopAll()
|
||||||
|
{
|
||||||
|
CTemporaryCS oCS(&m_cs);
|
||||||
|
m_enable_callback = false;
|
||||||
|
for (std::vector<CProcessRunner*>::iterator iter = m_processes.begin(); iter != m_processes.end(); iter++)
|
||||||
|
{
|
||||||
|
CProcessRunner* runner = *iter;
|
||||||
|
delete runner;
|
||||||
|
}
|
||||||
|
m_processes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisableCallbacks()
|
||||||
|
{
|
||||||
|
CTemporaryCS oCS(&m_cs);
|
||||||
|
m_enable_callback = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void process_callback(const int& id, const StreamType& type, const std::string& message)
|
||||||
|
{
|
||||||
|
CTemporaryCS oCS(&m_cs);
|
||||||
|
|
||||||
|
if (!m_enable_callback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_callback->process_callback(id, type, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user