mirror of
https://github.com/ONLYOFFICE/desktop-sdk.git
synced 2026-02-10 10:05:06 +08:00
Add run external processes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ Thumbs.db
|
||||
.idea/
|
||||
*Makefile*
|
||||
*qmake.stash
|
||||
.qtc_clangd
|
||||
|
||||
@ -121,6 +121,9 @@ SOURCES += \
|
||||
$$PWD/src/keychain.cpp \
|
||||
$$PWD/src/filelocker.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/src/cefwrapper/external_process.h
|
||||
|
||||
SOURCES += \
|
||||
$$CORE_ROOT_DIR/Common/OfficeFileFormatChecker2.cpp \
|
||||
$$CORE_ROOT_DIR/Common/3dParty/pole/pole.cpp \
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
*
|
||||
*/
|
||||
#include "./external_process.h"
|
||||
|
||||
#include "./client_renderer.h"
|
||||
|
||||
@ -592,7 +593,7 @@ namespace asc_client_renderer
|
||||
IMPLEMENT_REFCOUNTING(CLocalFileConvertV8Handler);
|
||||
};
|
||||
|
||||
class CAscEditorNativeV8Handler : public CefV8Handler, public CAIToolsHelper
|
||||
class CAscEditorNativeV8Handler : public CefV8Handler, public CAIToolsHelper, public NSProcesses::CProcessRunnerCallback
|
||||
{
|
||||
class CSavedPageInfo
|
||||
{
|
||||
@ -717,6 +718,10 @@ namespace asc_client_renderer
|
||||
bool m_bIsMacrosesSupport;
|
||||
bool m_bIsPluginsSupport;
|
||||
|
||||
// External processes
|
||||
CefRefPtr<CefFrame> m_frame;
|
||||
NSProcesses::CProcessManager* m_external_processes;
|
||||
|
||||
CAscEditorNativeV8Handler(const std::wstring& sUrl)
|
||||
{
|
||||
m_etType = AscEditorType::etUndefined;
|
||||
@ -767,6 +772,8 @@ namespace asc_client_renderer
|
||||
|
||||
m_bEditorsCloudFeaturesCheck = false;
|
||||
m_arCloudFeaturesBlackList.push_back("personal.onlyoffice.com");
|
||||
|
||||
m_external_processes = NULL;
|
||||
}
|
||||
|
||||
void CheckDefaults()
|
||||
@ -811,6 +818,9 @@ namespace asc_client_renderer
|
||||
|
||||
virtual ~CAscEditorNativeV8Handler()
|
||||
{
|
||||
RELEASEOBJECT(m_external_processes);
|
||||
m_frame.reset();
|
||||
|
||||
if (m_pAES_Key)
|
||||
NSOpenSSL::openssl_free(m_pAES_Key);
|
||||
NSBase::Release(m_pLocalApplicationFonts);
|
||||
@ -939,6 +949,33 @@ return undefined; \n\
|
||||
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
|
||||
{
|
||||
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)};";
|
||||
|
||||
_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;
|
||||
@ -4505,6 +4571,24 @@ window.AscDesktopEditor.CallInFrame(\"" +
|
||||
|
||||
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.
|
||||
return false;
|
||||
@ -5097,6 +5181,17 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
||||
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
|
||||
{
|
||||
message_router_->OnContextCreated(browser, frame, context);
|
||||
@ -5119,7 +5214,7 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
||||
|
||||
CefRefPtr<CefV8Handler> handler = pWrapper;
|
||||
|
||||
#define EXTEND_METHODS_COUNT 190
|
||||
#define EXTEND_METHODS_COUNT 192
|
||||
const char* methods[EXTEND_METHODS_COUNT] = {
|
||||
"Copy",
|
||||
"Paste",
|
||||
@ -5384,6 +5479,9 @@ if (targetElem) { targetElem.dispatchEvent(event); }})();";
|
||||
"_convertFileExternal",
|
||||
"_onConvertFileExternal",
|
||||
|
||||
"_createProcess",
|
||||
"_endProcess",
|
||||
|
||||
NULL};
|
||||
|
||||
ExtendObject(obj, handler, methods);
|
||||
@ -5453,6 +5551,8 @@ return this.split(str).join(newStr);\
|
||||
#else
|
||||
browser->SendProcessMessage(PID_BROWSER, message);
|
||||
#endif
|
||||
|
||||
pWrapper->m_frame = curFrame;
|
||||
}
|
||||
|
||||
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