├── .gitmodules ├── .gitignore ├── src ├── exe │ ├── app_win.cc │ ├── res │ │ └── mainicon.ico │ ├── version.h.in │ ├── report_win.h │ ├── path_win.h │ ├── replace.cmake │ ├── resource.h │ ├── report_win.cc │ ├── aviutl_browser.exe.manifest │ ├── assets_win.h │ ├── callback.h │ ├── compatibility.manifest │ ├── path_win.cc │ ├── app.h │ ├── aviutl_browser.rc │ ├── assets.h │ ├── unicode_win.h │ ├── client_win.cc │ ├── app.cc │ ├── assets_win.cc │ ├── client.h │ ├── aviutl_browser_win.cc │ ├── assets.cc │ ├── CMakeLists.txt │ ├── client.cc │ └── picojson.h ├── lua │ └── browser.lua └── CMakeLists.txt ├── .gitattributes ├── CHANGELOG.md ├── LICENSE ├── CMakeLists.txt ├── .github └── workflows │ └── releaser.yml └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /src/exe/app_win.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oov/aviutl_browser/HEAD/src/exe/app_win.cc -------------------------------------------------------------------------------- /src/lua/browser.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oov/aviutl_browser/HEAD/src/lua/browser.lua -------------------------------------------------------------------------------- /src/exe/res/mainicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oov/aviutl_browser/HEAD/src/exe/res/mainicon.ico -------------------------------------------------------------------------------- /src/exe/version.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | constexpr const wchar_t *version = L"@git_tag@ ( @git_revision@ )"; 3 | -------------------------------------------------------------------------------- /src/exe/report_win.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | bool report_(HRESULT hr, LPCWSTR message); 6 | #define report(hr, msg) (report_(hr, msg)) 7 | -------------------------------------------------------------------------------- /src/exe/path_win.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | bool get_module_file_name(const HMODULE h, std::wstring& o); 7 | bool get_full_path_name(LPCWSTR path, std::wstring& o); 8 | -------------------------------------------------------------------------------- /src/exe/replace.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | if("${newline}" STREQUAL "") 3 | set(newline "CRLF") 4 | endif() 5 | configure_file(${input_file} ${output_file} @ONLY NEWLINE_STYLE ${newline}) 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | README.md text eol=crlf 2 | *.lua text eol=crlf 3 | *.anm text eol=crlf 4 | *.obj text eol=crlf 5 | *.exa text eol=crlf 6 | src/lua/contents/CREDITS.md text eol=crlf 7 | -------------------------------------------------------------------------------- /src/exe/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by aviutl_browser.rc 4 | // 5 | 6 | #define IDI_MAINICON 100 7 | 8 | // Avoid files associated with MacOS 9 | #define _X86_ 10 | 11 | // Next default values for new objects 12 | // 13 | #ifdef APSTUDIO_INVOKED 14 | #ifndef APSTUDIO_READONLY_SYMBOLS 15 | #define _APS_NO_MFC 1 16 | #define _APS_NEXT_RESOURCE_VALUE 102 17 | #define _APS_NEXT_COMMAND_VALUE 32700 18 | #define _APS_NEXT_CONTROL_VALUE 1000 19 | #define _APS_NEXT_SYMED_VALUE 102 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /src/exe/report_win.cc: -------------------------------------------------------------------------------- 1 | #include "report_win.h" 2 | 3 | #include 4 | 5 | bool report_(HRESULT hr, LPCWSTR message) { 6 | if (SUCCEEDED(hr)) { 7 | return false; 8 | } 9 | std::wstring m(message); 10 | m += L": "; 11 | { 12 | LPWSTR t = NULL; 13 | FormatMessageW( 14 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 15 | FORMAT_MESSAGE_FROM_SYSTEM, 16 | NULL, 17 | hr, 18 | LANG_USER_DEFAULT, 19 | (LPWSTR)&t, 20 | 0, 21 | NULL); 22 | m += t; 23 | LocalFree(t); 24 | } 25 | m += L"\n"; 26 | OutputDebugStringW(m.c_str()); 27 | return true; 28 | } 29 | -------------------------------------------------------------------------------- /src/exe/aviutl_browser.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/exe/assets_win.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MINIZ_HEADER_FILE_ONLY 6 | #include "miniz.c" 7 | #undef MINIZ_HEADER_FILE_ONLY 8 | 9 | #include "assets.h" 10 | 11 | class AssetsFromLocalFile : public virtual Assets { 12 | CefString base_path_; 13 | protected: 14 | virtual CefRefPtr Get(const CefString& path) override; 15 | public: 16 | bool SetBasePath(const CefString& base_path); 17 | }; 18 | 19 | class AssetsFromZip : public virtual Assets { 20 | HANDLE file_; 21 | mz_zip_archive archive_; 22 | static size_t read(void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n); 23 | protected: 24 | virtual CefRefPtr Get(const CefString& path) override; 25 | public: 26 | explicit AssetsFromZip() : file_(INVALID_HANDLE_VALUE), archive_({}) {} 27 | virtual ~AssetsFromZip() override; 28 | bool SetSourceZip(const CefString& zip_path); 29 | }; 30 | -------------------------------------------------------------------------------- /src/exe/callback.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "include/cef_app.h" 4 | 5 | class CallbackHandler : public CefV8Handler { 6 | public: 7 | CallbackHandler() {} 8 | 9 | virtual bool Execute(const CefString& name, 10 | CefRefPtr object, 11 | const CefV8ValueList& arguments, 12 | CefRefPtr& retval, 13 | CefString& exception) override { 14 | if (name == "rendered") { 15 | CefRefPtr msg(CefProcessMessage::Create("rendered")); 16 | auto a = msg->GetArgumentList(); 17 | a->SetInt(0, arguments[0]->GetIntValue()); 18 | a->SetInt(1, arguments[1]->GetIntValue()); 19 | a->SetBool(2, arguments[2]->GetBoolValue()); 20 | a->SetString(3, arguments[3]->GetStringValue()); 21 | CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg); 22 | return true; 23 | } 24 | return false; 25 | } 26 | 27 | IMPLEMENT_REFCOUNTING(CallbackHandler); 28 | }; 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.4.2 4 | 5 | - CEF を 129.0.12+gf09539f+chromium-129.0.6668.101 に更新 6 | 7 | ## 0.4.1 8 | 9 | - CEF を 99.2.15+g71e9523+chromium-99.0.4844.84 に更新 10 | 11 | ## 0.4.0 12 | 13 | - CEF を 99.2.14+g3f796b8+chromium-99.0.4844.84 に更新 14 | - `.wasm` を `application/wasm` で返すように MIME の定義を追加 15 | - `glTF表示デモ.obj` と `SVGアニメ.obj` と `ファイル参照デモ.obj` を削除 16 | - 元々機能のデモンストレーション目的で含めていましたが、一般的な実用性はないため削除しました 17 | - 配布用パッケージを自動生成するようにした 18 | 19 | ## 0.3 20 | 21 | - `tabid` パラメーターを追加した 22 | - 同じコンテンツをブラウザー上の別のタブに読み込むための識別用パラメーター 23 | - 可能な限りコンテンツ側で対応すべきなので、積極的に使う必要はありません 24 | - コンテンツを取得するときに一部のヘッダーを追加 25 | - すべてのファイルに対して `Cross-Origin-Embedder-Policy: require-corp` と `Cross-Origin-Opener-Policy: same-origin` を追加 26 | - `/userfile` に対して `Content-Security-Policy: default-src 'self'` と `Cache-Control: no-store` を追加 27 | - CEF を 90.6.7+g19ba721+chromium-90.0.4430.212 に更新 28 | 29 | ## 0.2 30 | 31 | - 一部のファイルが配布ファイルから漏れていたのを修正 32 | 33 | ## 0.1 34 | 35 | - 初版 36 | -------------------------------------------------------------------------------- /src/exe/compatibility.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 oov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/exe/path_win.cc: -------------------------------------------------------------------------------- 1 | #include "path_win.h" 2 | 3 | #include "report_win.h" 4 | 5 | bool get_module_file_name(const HMODULE h, std::wstring& o) { 6 | o.resize(0); 7 | while (o.size() < 4096) { 8 | o.resize(o.size() + MAX_PATH); 9 | DWORD r = GetModuleFileName(h, &o[0], static_cast(o.size())); 10 | if (r == 0) { 11 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetModuleFileName failed"); 12 | return false; 13 | } 14 | if (r < o.size()) { 15 | o.resize(r); 16 | return true; 17 | } 18 | } 19 | return false; 20 | } 21 | 22 | bool get_full_path_name(LPCWSTR path, std::wstring& o) { 23 | DWORD r = GetFullPathName(path, 0, NULL, NULL); 24 | if (r == 0) { 25 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetFullPathName failed"); 26 | return false; 27 | } 28 | o.resize(r); // including the terminating null character 29 | r = GetFullPathName(path, static_cast(o.size()), &o[0], NULL); 30 | if (r == 0) { 31 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetFullPathName failed"); 32 | return false; 33 | } 34 | o.resize(r); // not including the terminating null character 35 | return true; 36 | } 37 | -------------------------------------------------------------------------------- /src/exe/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "include/cef_app.h" 8 | 9 | #include "client.h" 10 | 11 | struct client_key { 12 | std::wstring path; 13 | std::wstring tabid; 14 | bool is_dir; 15 | bool operator<(const client_key& value) const 16 | { 17 | return std::tie(path, is_dir, value.tabid) < std::tie(value.path, value.is_dir, value.tabid); 18 | } 19 | }; 20 | 21 | class App : public CefApp, public CefBrowserProcessHandler, public CefRenderProcessHandler { 22 | public: 23 | explicit App(); 24 | 25 | // CefApp methods: 26 | virtual CefRefPtr GetBrowserProcessHandler() override { 27 | return this; 28 | } 29 | virtual CefRefPtr GetRenderProcessHandler() override { 30 | return this; 31 | } 32 | 33 | // CefBrowserProcessHandler methods: 34 | virtual void OnContextInitialized() override; 35 | 36 | // CefRenderProcessHandler methods: 37 | virtual void OnWebKitInitialized() override; 38 | virtual void OnContextCreated(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) override; 39 | 40 | void WaitInitialize(); 41 | bool RenderAndCapture( 42 | const std::wstring& path, 43 | const std::wstring& tabid, 44 | const bool use_dir, 45 | const bool use_devtools, 46 | const std::wstring& userfile, 47 | const std::wstring& inbuf, 48 | std::wstring& outbuf, 49 | void* image, const int width, const int height 50 | ); 51 | void CloseAllBrowsers(); 52 | private: 53 | std::mutex mtx_; 54 | std::condition_variable cv_; 55 | bool initialized_; 56 | 57 | std::map> clients_; 58 | 59 | CefRefPtr InitAssets(const std::wstring& path, const bool use_dir); 60 | 61 | IMPLEMENT_REFCOUNTING(App); 62 | }; 63 | -------------------------------------------------------------------------------- /src/exe/aviutl_browser.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #define APSTUDIO_HIDDEN_SYMBOLS 11 | #include "windows.h" 12 | #undef APSTUDIO_HIDDEN_SYMBOLS 13 | 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | 17 | ///////////////////////////////////////////////////////////////////////////// 18 | // English (U.S.) resources 19 | 20 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 21 | #ifdef _WIN32 22 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 23 | #pragma code_page(1252) 24 | #endif //_WIN32 25 | 26 | ///////////////////////////////////////////////////////////////////////////// 27 | // 28 | // Icon 29 | // 30 | 31 | // Icon with lowest ID value placed first to ensure application icon 32 | // remains consistent on all systems. 33 | IDI_MAINICON ICON "res\\mainicon.ico" 34 | 35 | 36 | #ifdef APSTUDIO_INVOKED 37 | ///////////////////////////////////////////////////////////////////////////// 38 | // 39 | // TEXTINCLUDE 40 | // 41 | 42 | 1 TEXTINCLUDE 43 | BEGIN 44 | "resource.h\0" 45 | END 46 | 47 | 2 TEXTINCLUDE 48 | BEGIN 49 | "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" 50 | "#include ""windows.h""\r\n" 51 | "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" 52 | "\0" 53 | END 54 | 55 | 3 TEXTINCLUDE 56 | BEGIN 57 | "\r\n" 58 | "\0" 59 | END 60 | 61 | #endif // APSTUDIO_INVOKED 62 | 63 | 64 | #endif // English (U.S.) resources 65 | ///////////////////////////////////////////////////////////////////////////// 66 | 67 | 68 | 69 | #ifndef APSTUDIO_INVOKED 70 | ///////////////////////////////////////////////////////////////////////////// 71 | // 72 | // Generated from the TEXTINCLUDE 3 resource. 73 | // 74 | 75 | 76 | ///////////////////////////////////////////////////////////////////////////// 77 | #endif // not APSTUDIO_INVOKED 78 | 79 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(aviutl_browser_package NONE) 4 | 5 | if("${BUILD_PACKAGE}" STREQUAL "1") 6 | 7 | set(EMOJI_VERSION "v0.0.2") 8 | set(EMOJI_URL "https://github.com/oov/aviutl_browser_emoji/releases/download/${EMOJI_VERSION}/aviutl_browser_emoji_${EMOJI_VERSION}.zip") 9 | set(EMOJI_PATH "${CMAKE_CURRENT_BINARY_DIR}/emoji.zip") 10 | set(EMOJI_DIR "${CMAKE_CURRENT_BINARY_DIR}/emoji") 11 | if(NOT EXISTS "${EMOJI_PATH}") 12 | file(DOWNLOAD "${EMOJI_URL}" "${EMOJI_PATH}") 13 | endif() 14 | if(NOT EXISTS "${EMOJI_DIR}") 15 | file(MAKE_DIRECTORY "${EMOJI_DIR}") 16 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvzf "${EMOJI_PATH}" WORKING_DIRECTORY "${EMOJI_DIR}") 17 | endif() 18 | 19 | set(MARKDOWN_VERSION "v0.0.3") 20 | set(MARKDOWN_URL "https://github.com/oov/aviutl_browser_markdown/releases/download/${MARKDOWN_VERSION}/aviutl_browser_markdown_${MARKDOWN_VERSION}.zip") 21 | set(MARKDOWN_PATH "${CMAKE_CURRENT_BINARY_DIR}/markdown.zip") 22 | set(MARKDOWN_DIR "${CMAKE_CURRENT_BINARY_DIR}/markdown") 23 | if(NOT EXISTS "${MARKDOWN_PATH}") 24 | file(DOWNLOAD "${MARKDOWN_URL}" "${MARKDOWN_PATH}") 25 | endif() 26 | if(NOT EXISTS "${MARKDOWN_DIR}") 27 | file(MAKE_DIRECTORY "${MARKDOWN_DIR}") 28 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvzf "${MARKDOWN_PATH}" WORKING_DIRECTORY "${MARKDOWN_DIR}") 29 | endif() 30 | 31 | add_custom_target(copy_script 32 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/README.md" "${PROJECT_BINARY_DIR}/bin/aviutl_browser.txt" 33 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/LICENSES.chromium.html" "${PROJECT_BINARY_DIR}/bin/LICENSES.chromium.html" 34 | COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/src/lua/browser.lua" "${PROJECT_BINARY_DIR}/bin/script/browser/browser.lua" 35 | COMMAND ${CMAKE_COMMAND} -E copy_directory "${EMOJI_DIR}" "${PROJECT_BINARY_DIR}/bin" 36 | COMMAND ${CMAKE_COMMAND} -E copy_directory "${MARKDOWN_DIR}" "${PROJECT_BINARY_DIR}/bin" 37 | ) 38 | 39 | endif() 40 | 41 | if("${BUILD_EXE}" STREQUAL "1") 42 | 43 | add_subdirectory(src) 44 | 45 | endif() 46 | -------------------------------------------------------------------------------- /src/exe/assets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "include/wrapper/cef_stream_resource_handler.h" 8 | 9 | typedef std::function(const CefString&)> AssetsFunct; 10 | 11 | class Assets : public virtual CefBaseRefCounted { 12 | typedef std::map cefstrmap; 13 | cefstrmap mime_map_; 14 | bool mime_map_initialized; 15 | std::mutex mtx_; 16 | void InitializeMimeMap(); 17 | protected: 18 | virtual CefRefPtr Get(const CefString& path) = 0; 19 | virtual void GetMimeJson(CefString& json); 20 | public: 21 | explicit Assets(); 22 | void GetMime(const CefString& path, CefString& mime); 23 | void SetDefaultHeaders(CefResponse::HeaderMap& header_map); 24 | CefRefPtr operator()(const CefString& path); 25 | IMPLEMENT_REFCOUNTING(Assets); 26 | }; 27 | 28 | class StreamResourceHandler : public CefStreamResourceHandler 29 | { 30 | private: 31 | std::string content; 32 | explicit StreamResourceHandler( 33 | int status_code, 34 | const CefString& status_text, 35 | const CefString& mime_type, 36 | CefResponse::HeaderMap header_map, 37 | CefRefPtr stream) : CefStreamResourceHandler(status_code, status_text, mime_type, header_map, stream) {}; 38 | public: 39 | static CefRefPtr CreateFromString( 40 | int status_code, 41 | const CefString& mime_type, 42 | CefResponse::HeaderMap header_map, 43 | std::string& content); 44 | static CefRefPtr CreateFromFile( 45 | int status_code, 46 | const CefString& mime_type, 47 | CefResponse::HeaderMap header_map, 48 | const CefString& filepath); 49 | static CefRefPtr CreateError( 50 | int status_code, 51 | const CefString& message); 52 | }; 53 | 54 | 55 | class StaticErrorAssets : public Assets { 56 | private: 57 | const int status_code_; 58 | const CefString message_; 59 | protected: 60 | virtual CefRefPtr Get(const CefString& path) { 61 | return StreamResourceHandler::CreateError(status_code_, message_); 62 | } 63 | public: 64 | explicit StaticErrorAssets(int status_code, const CefString& message) : status_code_(status_code), message_(message) {} 65 | }; -------------------------------------------------------------------------------- /.github/workflows/releaser.yml: -------------------------------------------------------------------------------- 1 | name: releaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v[0-9]+.[0-9]+.[0-9]+alpha[0-9]+" 7 | - "v[0-9]+.[0-9]+.[0-9]+beta[0-9]+" 8 | - "v[0-9]+.[0-9]+.[0-9]+rc[0-9]+" 9 | - "v[0-9]+.[0-9]+.[0-9]+" 10 | 11 | jobs: 12 | build: 13 | runs-on: windows-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Build 17 | shell: cmd 18 | run: | 19 | mkdir build 20 | cd build 21 | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" 22 | cmake -G "Visual Studio 17" -A x64 -DCMAKE_BUILD_TYPE=Release -DBUILD_EXE=1 .. 23 | cmake --build . --config Release 24 | - name: Upload binaries 25 | uses: actions/upload-artifact@v4 26 | with: 27 | name: exe 28 | path: | 29 | build/src/exe/Release/* 30 | create-release: 31 | needs: [build] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Get the version 35 | id: get_version 36 | run: echo ::set-output name=tag::${GITHUB_REF/refs\/tags\//} 37 | - uses: actions/checkout@v4 38 | - name: Download artifacts 39 | uses: actions/download-artifact@v4 40 | - name: Build package 41 | run: | 42 | mkdir build 43 | cd build 44 | cmake -DBUILD_PACKAGE=1 .. 45 | cmake --build . --target copy_script 46 | - name: Copy binary 47 | run: | 48 | rm exe/aviutl_browser.exp 49 | rm exe/aviutl_browser.lib 50 | mv exe build/bin/script/browser/ 51 | - name: Zip 52 | run: mkdir -p dist && cd build/bin && zip -r ../../dist/aviutl_browser_${{ steps.get_version.outputs.tag }}.zip * 53 | - name: Create Release 54 | id: create_release 55 | uses: softprops/action-gh-release@v1 56 | with: 57 | tag_name: ${{ steps.get_version.outputs.tag }} 58 | draft: true 59 | prerelease: false 60 | files: dist/* 61 | body: | 62 | ### ダウンロード 63 | 64 | - [aviutl_browser_${{ steps.get_version.outputs.tag }}.zip](https://github.com/oov/aviutl_browser/releases/download/${{ steps.get_version.outputs.tag }}/aviutl_browser_${{ steps.get_version.outputs.tag }}.zip) 65 | 66 | ### 変更点 67 | 68 | - **** 69 | -------------------------------------------------------------------------------- /src/exe/unicode_win.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | static inline HRESULT to_u16(LPCSTR src, const int srclen, std::wstring& dest) { 7 | if (srclen == 0) { 8 | dest.resize(0); 9 | return S_OK; 10 | } 11 | const int destlen = MultiByteToWideChar(CP_UTF8, 0, src, srclen, nullptr, 0); 12 | if (destlen == 0) { 13 | return HRESULT_FROM_WIN32(GetLastError()); 14 | } 15 | dest.resize(destlen); 16 | if (MultiByteToWideChar(CP_UTF8, 0, src, srclen, &dest[0], destlen) == 0) { 17 | return HRESULT_FROM_WIN32(GetLastError()); 18 | } 19 | if (srclen == -1) { 20 | dest.resize(static_cast(destlen) - 1); 21 | } 22 | return S_OK; 23 | } 24 | 25 | static inline HRESULT to_u8(LPCWSTR src, const int srclen, std::string& dest) { 26 | if (srclen == 0) { 27 | dest.resize(0); 28 | return S_OK; 29 | } 30 | const int destlen = WideCharToMultiByte(CP_UTF8, 0, src, srclen, nullptr, 0, nullptr, nullptr); 31 | if (destlen == 0) { 32 | return HRESULT_FROM_WIN32(GetLastError()); 33 | } 34 | dest.resize(destlen); 35 | if (WideCharToMultiByte(CP_UTF8, 0, src, srclen, &dest[0], destlen, nullptr, nullptr) == 0) { 36 | return HRESULT_FROM_WIN32(GetLastError()); 37 | } 38 | if (srclen == -1) { 39 | dest.resize(static_cast(destlen) - 1); 40 | } 41 | return S_OK; 42 | } 43 | 44 | static inline HRESULT to_sjis(LPCWSTR src, const int srclen, std::string& dest) { 45 | if (srclen == 0) { 46 | dest.resize(0); 47 | return S_OK; 48 | } 49 | const int destlen = WideCharToMultiByte(932, 0, src, srclen, nullptr, 0, nullptr, nullptr); 50 | if (destlen == 0) { 51 | return HRESULT_FROM_WIN32(GetLastError()); 52 | } 53 | dest.resize(destlen); 54 | if (WideCharToMultiByte(932, 0, src, srclen, &dest[0], destlen, nullptr, nullptr) == 0) { 55 | return HRESULT_FROM_WIN32(GetLastError()); 56 | } 57 | if (srclen == -1) { 58 | dest.resize(static_cast(destlen) - 1); 59 | } 60 | return S_OK; 61 | } 62 | 63 | static inline HRESULT from_sjis(LPCSTR src, const int srclen, std::wstring& dest) { 64 | if (srclen == 0) { 65 | dest.resize(0); 66 | return S_OK; 67 | } 68 | const int destlen = MultiByteToWideChar(932, 0, src, srclen, nullptr, 0); 69 | if (destlen == 0) { 70 | return HRESULT_FROM_WIN32(GetLastError()); 71 | } 72 | dest.resize(destlen); 73 | if (MultiByteToWideChar(932, 0, src, srclen, &dest[0], destlen) == 0) { 74 | return HRESULT_FROM_WIN32(GetLastError()); 75 | } 76 | if (srclen == -1) { 77 | dest.resize(static_cast(destlen) - 1); 78 | } 79 | return S_OK; 80 | } 81 | -------------------------------------------------------------------------------- /src/exe/client_win.cc: -------------------------------------------------------------------------------- 1 | #include "client.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "include/cef_app.h" 7 | #include "include/cef_browser.h" 8 | #include "include/base/cef_bind.h" 9 | #include "include/wrapper/cef_helpers.h" 10 | #include "include/wrapper/cef_closure_task.h" 11 | 12 | #include "resource.h" 13 | #include "report_win.h" 14 | #include "version.h" 15 | 16 | void Client::InitializeDevTools(CefRefPtr browser) { 17 | auto c = new PopupClient(); 18 | c->after_created = [&](CefRefPtr browser) { 19 | HWND window = browser->GetHost()->GetWindowHandle(); 20 | HANDLE icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAINICON)); 21 | if (icon) { 22 | SendMessage(window, WM_SETICON, ICON_BIG, (LPARAM)icon); 23 | SendMessage(window, WM_SETICON, ICON_SMALL, (LPARAM)icon); 24 | } 25 | else { 26 | report(HRESULT_FROM_WIN32(GetLastError()), L"LoadIcon failed"); 27 | } 28 | HMENU menu = GetSystemMenu(window, FALSE); 29 | EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); 30 | 31 | // It seems SW_SHOWDEFAULT is used inside CEF. 32 | // Since bridge.dll passes SW_HIDE in CreateProcess, so the window remains invisible as a result. 33 | ShowWindow(window, SW_SHOW); 34 | 35 | browser_list_.push_back(browser); 36 | }; 37 | c->before_close = [&](CefRefPtr browser) { 38 | for (BrowserList::iterator bit = browser_list_.begin(); bit != browser_list_.end(); ++bit) { 39 | if ((*bit)->IsSame(browser)) { 40 | browser_list_.erase(bit); 41 | break; 42 | } 43 | } 44 | }; 45 | c->do_not_close = true; 46 | CefWindowInfo wi = {}; 47 | std::wstring title(L"AviUtlBrowser DevTools v"); 48 | title += version; 49 | wi.SetAsPopup(NULL, title.c_str()); 50 | wi.style |= WS_VISIBLE; 51 | wi.ex_style |= WS_EX_APPWINDOW; 52 | CefBrowserSettings bs = {}; 53 | CefString utf8(L"UTF-8"); 54 | cef_string_utf16_set(utf8.c_str(), utf8.length(), &bs.default_encoding, false); 55 | CefPoint pt = {}; 56 | browser->GetHost()->ShowDevTools(wi, c, bs, pt); 57 | devtools_ = c; 58 | } 59 | 60 | CefRefPtr Client::GetUserFile() { 61 | LARGE_INTEGER sz = {}; 62 | { 63 | WIN32_FIND_DATA fd = {}; 64 | HANDLE h = FindFirstFile(userfile_.c_str(), &fd); 65 | if (h == INVALID_HANDLE_VALUE) { 66 | DWORD err = GetLastError(); 67 | if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { 68 | return StreamResourceHandler::CreateError(404, L"userfile is not found"); 69 | } 70 | report(HRESULT_FROM_WIN32(err), L"FindFirstFile failed"); 71 | return StreamResourceHandler::CreateError(500, L"cannot get file size"); 72 | } 73 | if (!FindClose(h)) { 74 | report(HRESULT_FROM_WIN32(GetLastError()), L"FindClose failed"); 75 | } 76 | sz.LowPart = fd.nFileSizeLow; 77 | sz.HighPart = fd.nFileSizeHigh; 78 | } 79 | CefString mime; 80 | assets_->GetMime(userfile_, mime); 81 | CefResponse::HeaderMap hm; 82 | hm.insert(std::make_pair(L"Content-Length", CefString(std::to_wstring(sz.QuadPart)))); 83 | assets_->SetDefaultHeaders(hm); 84 | hm.insert(std::make_pair(L"Content-Security-Policy", L"default-src 'self'")); 85 | hm.insert(std::make_pair(L"Cache-Control", L"no-store")); 86 | return StreamResourceHandler::CreateFromFile(200, mime, hm, userfile_); 87 | } 88 | 89 | bool Client::OnConsoleMessage(CefRefPtr browser, 90 | cef_log_severity_t level, 91 | const CefString& message, 92 | const CefString& source, 93 | int line) { 94 | #ifdef _DEBUG 95 | if (!dev_mode_) { 96 | std::wostringstream o; 97 | o << L"[aviutl_browser "; 98 | if (!source.empty()) { 99 | o << source.c_str(); 100 | o << L"("; 101 | o << line; 102 | o << L") "; 103 | } 104 | switch (level) { 105 | case LOGSEVERITY_DEBUG: 106 | o << L"DEBUG"; 107 | break; 108 | case LOGSEVERITY_INFO: 109 | o << L"INFO"; 110 | break; 111 | case LOGSEVERITY_WARNING: 112 | o << L"WARN"; 113 | break; 114 | case LOGSEVERITY_ERROR: 115 | o << L"ERROR"; 116 | break; 117 | } 118 | o << L"] "; 119 | o << message.c_str(); 120 | OutputDebugStringW(o.str().c_str()); 121 | } 122 | #endif 123 | return true; 124 | } -------------------------------------------------------------------------------- /src/exe/app.cc: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | 3 | #include 4 | 5 | #include "include/base/cef_callback.h" 6 | #include "include/base/cef_ref_counted.h" 7 | #include "include/wrapper/cef_closure_task.h" 8 | #include "include/wrapper/cef_helpers.h" 9 | 10 | #include "callback.h" 11 | 12 | App::App() : initialized_(false) { 13 | } 14 | 15 | void App::WaitInitialize() { 16 | std::unique_lock lk(mtx_); 17 | cv_.wait(lk, [&] { 18 | return initialized_ == true; 19 | }); 20 | } 21 | 22 | bool App::RenderAndCapture( 23 | const std::wstring& path, 24 | const std::wstring& tabid, 25 | const bool use_dir, 26 | const bool use_devtools, 27 | const std::wstring& userfile, 28 | const std::wstring& inbuf, 29 | std::wstring& outbuf, 30 | void* image, const int width, const int height) { 31 | std::lock_guard lk(mtx_); 32 | CefRefPtr c; 33 | const auto key = client_key{ path, tabid, use_dir }; 34 | const auto it = clients_.find(key); 35 | if (it != clients_.cend()) { 36 | c = it->second; 37 | } else { 38 | c = new Client(InitAssets(path, use_dir)); 39 | clients_[key] = c; 40 | CefWindowInfo window_info; 41 | window_info.SetAsWindowless(nullptr); 42 | CefBrowserSettings browser_settings; 43 | browser_settings.background_color = 0; 44 | CefString utf8(L"UTF-8"); 45 | cef_string_utf16_set(utf8.c_str(), utf8.length(), &browser_settings.default_encoding, false); 46 | browser_settings.javascript_access_clipboard = STATE_DISABLED; 47 | browser_settings.javascript_dom_paste = STATE_DISABLED; 48 | CefBrowserHost::CreateBrowser( 49 | window_info, 50 | c, 51 | L"https://assets.example/", 52 | browser_settings, 53 | nullptr, 54 | nullptr 55 | ); 56 | c->Wait(); 57 | } 58 | c->SetUseDevTools(use_devtools); 59 | c->SetUserFile(userfile); 60 | return c->RenderAndCapture(inbuf, outbuf, image, width, height); 61 | } 62 | 63 | void App::CloseAllBrowsers() { 64 | std::lock_guard lk(mtx_); 65 | for (auto &c : clients_) { 66 | c.second->CloseAllBrowsers(true); 67 | } 68 | clients_.clear(); 69 | CefPostTask(TID_UI, base::BindOnce(&CefQuitMessageLoop)); 70 | } 71 | 72 | void App::OnContextInitialized() { 73 | CEF_REQUIRE_UI_THREAD(); 74 | initialized_ = true; 75 | cv_.notify_all(); 76 | } 77 | 78 | void App::OnContextCreated(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) { 79 | CefRefPtr ret; 80 | CefRefPtr err; 81 | context->Eval(LR"JS(window.AviUtlBrowser = AviUtlBrowserExtension)JS", frame->GetURL(), 0, ret, err); 82 | } 83 | 84 | void App::OnWebKitInitialized() { 85 | CefString extensionCode(LR"JS( 86 | const AviUtlBrowserExtension = (()=>{ 87 | "use strict"; 88 | native function rendered(); 89 | let renderer = null; 90 | let ackElem = null; 91 | let curAck = 0; 92 | function updateAck() { 93 | if (document.body && !ackElem) { 94 | ackElem = document.createElement('div'); 95 | ackElem.style.display = 'block'; 96 | ackElem.style.position = 'fixed'; 97 | ackElem.style.zIndex = '2147483647'; 98 | ackElem.style.left = '0'; 99 | ackElem.style.top = '0'; 100 | ackElem.style.width = '1px'; 101 | ackElem.style.height = '1px'; 102 | } 103 | if (!ackElem) { 104 | return 0; 105 | } 106 | if (!ackElem.parentNode) { 107 | document.body.appendChild(ackElem); 108 | } 109 | curAck >>= 8; 110 | if (!curAck) { 111 | curAck = 0xff0000; 112 | } 113 | ackElem.style.backgroundColor = `rgb(${(curAck >>> 16) & 0xff}, ${(curAck >>> 8) & 0xff}, ${(curAck >>> 0) & 0xff})`; 114 | return curAck; 115 | } 116 | return { 117 | render(idx, param, userfile) { 118 | if (renderer) { 119 | renderer({param, userfile}).then(param => { 120 | rendered(idx, updateAck(), true, param); 121 | }).catch(err => { 122 | rendered(idx, updateAck(), false, err); 123 | }); 124 | } else { 125 | updateAck(); 126 | rendered(idx, updateAck(), false, "renderer is not registered"); 127 | } 128 | }, 129 | registerRenderer(fn) { 130 | renderer = fn; 131 | }, 132 | }; 133 | })(); 134 | )JS"); 135 | CefRefPtr h(new CallbackHandler()); 136 | CefRegisterExtension(L"v8/AviUtlBrowserExtension", extensionCode, h); 137 | } 138 | -------------------------------------------------------------------------------- /src/exe/assets_win.cc: -------------------------------------------------------------------------------- 1 | #include "assets_win.h" 2 | 3 | #include 4 | 5 | #include "unicode_win.h" 6 | #include "report_win.h" 7 | 8 | CefRefPtr AssetsFromLocalFile::Get(const CefString& path) { 9 | std::wstring tmp(base_path_.ToWString()); 10 | tmp += path; 11 | WCHAR file[MAX_PATH] = {}; 12 | DWORD len = MAX_PATH; 13 | { 14 | const HRESULT hr = PathCreateFromUrlW(tmp.c_str(), file, &len, 0); 15 | if (FAILED(hr)) { 16 | report(hr, L"PathCreateFromUrlW failed"); 17 | return StreamResourceHandler::CreateError(500, L"cannot convert url to local path"); 18 | } 19 | } 20 | file[len] = L'\0'; 21 | LARGE_INTEGER sz = {}; 22 | { 23 | WIN32_FIND_DATA fd = {}; 24 | HANDLE h = FindFirstFile(file, &fd); 25 | if (h == INVALID_HANDLE_VALUE) { 26 | DWORD err = GetLastError(); 27 | if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { 28 | std::wstring ws(path); 29 | ws += L" is not found"; 30 | return StreamResourceHandler::CreateError(404, ws.c_str()); 31 | } 32 | report(HRESULT_FROM_WIN32(err), L"FindFirstFile failed"); 33 | return StreamResourceHandler::CreateError(500, L"cannot get file size"); 34 | } 35 | if (!FindClose(h)) { 36 | report(HRESULT_FROM_WIN32(GetLastError()), L"FindClose failed"); 37 | } 38 | sz.LowPart = fd.nFileSizeLow; 39 | sz.HighPart = fd.nFileSizeHigh; 40 | } 41 | CefString mime; 42 | GetMime(path, mime); 43 | CefResponse::HeaderMap hm; 44 | hm.insert(std::make_pair(L"Content-Length", CefString(std::to_wstring(sz.QuadPart)))); 45 | SetDefaultHeaders(hm); 46 | return StreamResourceHandler::CreateFromFile(200, mime, hm, file); 47 | } 48 | 49 | bool AssetsFromLocalFile::SetBasePath(const CefString& base_path) { 50 | WCHAR tmp[MAX_PATH + 16] = {}; 51 | DWORD len = MAX_PATH + 16; 52 | HRESULT hr = UrlCreateFromPathW(reinterpret_cast(base_path.c_str()), tmp, &len, 0); 53 | if (FAILED(hr)) { 54 | report(hr, L"UrlCreateFromPathW failed"); 55 | return false; 56 | } 57 | // drop last slash 58 | if (tmp[len - 1] == L'/') { 59 | tmp[len - 1] = L'\0'; 60 | } 61 | base_path_ = tmp; 62 | return true; 63 | } 64 | 65 | size_t AssetsFromZip::read(void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) { 66 | HANDLE h = pOpaque; 67 | LARGE_INTEGER l = {}; 68 | l.QuadPart = file_ofs; 69 | DWORD r = SetFilePointer(h, l.LowPart, &l.HighPart, FILE_BEGIN); 70 | if (r == INVALID_SET_FILE_POINTER) { 71 | DWORD err = GetLastError(); 72 | if (err != NO_ERROR) { 73 | report(HRESULT_FROM_WIN32(err), L"SetFilePointer failed"); 74 | return 0; 75 | } 76 | } 77 | DWORD read = 0; 78 | if (!ReadFile(h, pBuf, static_cast(n), &read, NULL)) { 79 | report(HRESULT_FROM_WIN32(GetLastError()), L"ReadFile failed"); 80 | return 0; 81 | } 82 | return static_cast(read); 83 | } 84 | 85 | bool AssetsFromZip::SetSourceZip(const CefString& zip_path) { 86 | LARGE_INTEGER sz = {}; 87 | file_ = CreateFileW(reinterpret_cast(zip_path.c_str()), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 88 | if (file_ == INVALID_HANDLE_VALUE) { 89 | report(HRESULT_FROM_WIN32(GetLastError()), L"CreateFile failed"); 90 | return false; 91 | } 92 | if (!GetFileSizeEx(file_, &sz)) { 93 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetFileSizeEx failed"); 94 | goto close_file; 95 | } 96 | archive_.m_pRead = &AssetsFromZip::read; 97 | archive_.m_pIO_opaque = file_; 98 | if (!mz_zip_reader_init(&archive_, sz.QuadPart, 0)) { 99 | goto close_file; 100 | } 101 | return true; 102 | 103 | close_file: 104 | CloseHandle(file_); 105 | file_ = INVALID_HANDLE_VALUE; 106 | return false; 107 | } 108 | 109 | AssetsFromZip::~AssetsFromZip() { 110 | if (archive_.m_zip_mode == MZ_ZIP_MODE_READING) { 111 | mz_zip_reader_end(&archive_); 112 | } 113 | if (file_ != INVALID_HANDLE_VALUE) { 114 | CloseHandle(file_); 115 | file_ = INVALID_HANDLE_VALUE; 116 | } 117 | } 118 | 119 | CefRefPtr AssetsFromZip::Get(const CefString& path) { 120 | if (archive_.m_zip_mode != MZ_ZIP_MODE_READING) { 121 | return StreamResourceHandler::CreateError(500, L"target zip file is not ready"); 122 | } 123 | std::string u8path; 124 | { 125 | const HRESULT hr = to_u8(reinterpret_cast(path.c_str()), static_cast(path.size()), u8path); 126 | if (FAILED(hr)) { 127 | return StreamResourceHandler::CreateError(500, L"failed to encode filename to utf-8"); 128 | } 129 | } 130 | // trim first slash 131 | u8path = u8path.substr(1); 132 | const int r = mz_zip_reader_locate_file(&archive_, u8path.c_str(), nullptr, MZ_ZIP_FLAG_CASE_SENSITIVE); 133 | if (r == -1) { 134 | return StreamResourceHandler::CreateError(404, L"file not found from zip"); 135 | } 136 | mz_zip_archive_file_stat fs = {}; 137 | if (!mz_zip_reader_file_stat(&archive_, r, &fs)) { 138 | return StreamResourceHandler::CreateError(500, L"failed to get file size from zip"); 139 | } 140 | std::string content; 141 | content.resize(fs.m_uncomp_size); 142 | if (!mz_zip_reader_extract_to_mem(&archive_, r, &content[0], content.size(), 0)) { 143 | return StreamResourceHandler::CreateError(500, L"failed to extract file from zip"); 144 | } 145 | CefString mime; 146 | GetMime(path, mime); 147 | CefResponse::HeaderMap hm; 148 | hm.insert(std::make_pair(L"Content-Length", CefString(std::to_wstring(content.size())))); 149 | SetDefaultHeaders(hm); 150 | return StreamResourceHandler::CreateFromString(200, mime, hm, content); 151 | } -------------------------------------------------------------------------------- /src/exe/client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "include/wrapper/cef_stream_resource_handler.h" 8 | #include "include/cef_client.h" 9 | 10 | #include "assets.h" 11 | 12 | class PopupClient : public CefClient, public CefLifeSpanHandler { 13 | public: 14 | std::function browser)> after_created; 15 | std::function browser)> before_close; 16 | std::function browser)> close; 17 | bool do_not_close; 18 | virtual CefRefPtr GetLifeSpanHandler() override { return this; } 19 | virtual void OnAfterCreated(CefRefPtr browser) override { after_created(browser); } 20 | virtual void OnBeforeClose(CefRefPtr browser) override { before_close(browser); } 21 | virtual bool OnBeforePopup(CefRefPtr browser, 22 | CefRefPtr frame, 23 | const CefString& target_url, 24 | const CefString& target_frame_name, 25 | WindowOpenDisposition target_disposition, 26 | bool user_gesture, 27 | const CefPopupFeatures& popupFeatures, 28 | CefWindowInfo& windowInfo, 29 | CefRefPtr& client, 30 | CefBrowserSettings& settings, 31 | CefRefPtr& extra_info, 32 | bool* no_javascript_access) override { 33 | return true; 34 | } 35 | virtual bool DoClose(CefRefPtr browser) { return do_not_close; } 36 | IMPLEMENT_REFCOUNTING(PopupClient); 37 | }; 38 | 39 | class Client : public CefClient, 40 | public CefDisplayHandler, 41 | public CefLifeSpanHandler, 42 | public CefLoadHandler, 43 | public CefRenderHandler, 44 | public CefRequestHandler, 45 | public CefResourceRequestHandler { 46 | public: 47 | explicit Client(CefRefPtr assets); 48 | ~Client(); 49 | 50 | // CefClient methods: 51 | virtual CefRefPtr GetDisplayHandler() override { return this; } 52 | virtual CefRefPtr GetLifeSpanHandler() override { return this; } 53 | virtual CefRefPtr GetLoadHandler() override { return this; } 54 | virtual CefRefPtr GetRenderHandler() override { return this; } 55 | virtual CefRefPtr GetRequestHandler() override { return this; } 56 | 57 | virtual bool OnProcessMessageReceived(CefRefPtr browser, 58 | CefRefPtr frame, 59 | CefProcessId source_process, 60 | CefRefPtr message) override; 61 | 62 | // CefDisplayHandler methods: 63 | virtual bool OnConsoleMessage(CefRefPtr browser, 64 | cef_log_severity_t level, 65 | const CefString& message, 66 | const CefString& source, 67 | int line) override; 68 | 69 | // CefLifeSpanHandler methods: 70 | virtual bool OnBeforePopup(CefRefPtr browser, 71 | CefRefPtr frame, 72 | const CefString& target_url, 73 | const CefString& target_frame_name, 74 | CefLifeSpanHandler::WindowOpenDisposition target_disposition, 75 | bool user_gesture, 76 | const CefPopupFeatures& popupFeatures, 77 | CefWindowInfo& windowInfo, 78 | CefRefPtr& client, 79 | CefBrowserSettings& settings, 80 | CefRefPtr& extra_info, 81 | bool* no_javascript_access) override; 82 | virtual void OnAfterCreated(CefRefPtr browser) override; 83 | virtual bool DoClose(CefRefPtr browser) override; 84 | virtual void OnBeforeClose(CefRefPtr browser) override; 85 | 86 | // CefLoadHandler methods: 87 | virtual void OnLoadError(CefRefPtr browser, 88 | CefRefPtr frame, 89 | ErrorCode errorCode, 90 | const CefString& errorText, 91 | const CefString& failedUrl) override; 92 | virtual void OnLoadingStateChange(CefRefPtr browser, 93 | bool isLoading, 94 | bool canGoBack, 95 | bool canGoForward) override; 96 | 97 | // CefRenderHandler methods: 98 | virtual void GetViewRect(CefRefPtr browser, CefRect& rect) override; 99 | virtual void OnPaint(CefRefPtr browser, 100 | PaintElementType type, 101 | const RectList& dirtyRects, 102 | const void* buffer, 103 | int width, 104 | int height) override; 105 | 106 | // CefRequestHandler methods: 107 | virtual CefRefPtr GetResourceRequestHandler( 108 | CefRefPtr browser, 109 | CefRefPtr frame, 110 | CefRefPtr request, 111 | bool is_navigation, 112 | bool is_download, 113 | const CefString& request_initiator, 114 | bool& disable_default_handling) override { 115 | return this; 116 | } 117 | 118 | // CefResourceRequestHandler methods: 119 | virtual CefRefPtr GetResourceHandler( 120 | CefRefPtr browser, 121 | CefRefPtr frame, 122 | CefRefPtr request) override; 123 | 124 | void CloseAllBrowsers(bool force_close); 125 | 126 | void Wait(); 127 | void SetUserFile(const std::wstring& userfile); 128 | void SetUseDevTools(const bool use_devtools); 129 | bool RenderAndCapture(const std::wstring& inbuf, std::wstring& outbuf, void* image, const int width, const int height); 130 | private: 131 | void Resize(const int width, const int height); 132 | bool Render(const std::wstring& inbuf, std::wstring& outbuf, int timeout); 133 | void Capture(void* buf, const int width, const int height); 134 | void InitializeDevTools(CefRefPtr browser); 135 | void GetUserFileHash(std::wstring& hash); 136 | CefRefPtr GetUserFile(); 137 | 138 | typedef std::list> BrowserList; 139 | BrowserList browser_list_; 140 | 141 | std::wstring userfile_, userfile_hash_; 142 | CefRefPtr devtools_; 143 | CefRefPtr assets_; 144 | 145 | bool received_result_ok_; 146 | CefString received_result_param_; 147 | uint32_t received_result_ack_; 148 | 149 | bool resized_, rendered_, loaded_; 150 | int capture_state_; 151 | int callback_sequence_, waiting_sequence_; 152 | void* buf_; 153 | int cur_width_, cur_height_; 154 | LONG loading_; 155 | 156 | std::mutex mtx_; 157 | std::condition_variable cv_; 158 | 159 | IMPLEMENT_REFCOUNTING(Client); 160 | }; 161 | -------------------------------------------------------------------------------- /src/exe/aviutl_browser_win.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "include/cef_parser.h" 5 | #include "include/cef_sandbox_win.h" 6 | 7 | #include "app.h" 8 | #include "client.h" 9 | #include "unicode_win.h" 10 | #include "path_win.h" 11 | #include "report_win.h" 12 | #include "version.h" 13 | 14 | #define CEF_USE_SANDBOX 1 15 | 16 | #if defined(CEF_USE_SANDBOX) 17 | #pragma comment(lib, "cef_sandbox.lib") 18 | #endif 19 | 20 | struct share_mem_header { 21 | uint32_t header_size; 22 | uint32_t body_size; 23 | uint32_t version; 24 | uint32_t width; 25 | uint32_t height; 26 | }; 27 | 28 | struct header { 29 | uint8_t version; 30 | uint8_t flags; 31 | uint16_t path_len; 32 | uint16_t userfile_len; 33 | uint16_t tabid_len; 34 | }; 35 | 36 | inline static BOOL read(HANDLE h, void* buf, DWORD sz) 37 | { 38 | char* b = (char*)buf; 39 | for (DWORD read(0); sz > 0; b += read, sz -= read) 40 | { 41 | if (!ReadFile(h, b, sz, &read, NULL)) 42 | { 43 | return FALSE; 44 | } 45 | } 46 | return TRUE; 47 | } 48 | 49 | inline static BOOL write(HANDLE h, const void* buf, DWORD sz) 50 | { 51 | const char* b = (char*)buf; 52 | for (DWORD written(0); sz > 0; b += written, sz -= written) { 53 | if (!WriteFile(h, b, sz, &written, NULL)) 54 | { 55 | return FALSE; 56 | } 57 | } 58 | return TRUE; 59 | } 60 | 61 | static int ipc_worker(CefRefPtr app) { 62 | HANDLE in = GetStdHandle(STD_INPUT_HANDLE); 63 | if (in == INVALID_HANDLE_VALUE) { 64 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetStdHandle(STD_INPUT_HANDLE) failed"); 65 | goto finish; 66 | } 67 | HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); 68 | if (in == INVALID_HANDLE_VALUE) { 69 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetStdHandle(STD_OUTPUT_HANDLE) failed"); 70 | goto finish; 71 | } 72 | char fmo_name[32] = {}; 73 | { 74 | if (GetEnvironmentVariableA("BRIDGE_FMO", fmo_name, 32) == 0) { 75 | DWORD err = GetLastError(); 76 | if (err == ERROR_ENVVAR_NOT_FOUND) { 77 | OutputDebugString(L"environment variable \"BRIDGE_FMO\" is not found"); 78 | goto finish; 79 | } 80 | report(HRESULT_FROM_WIN32(err), L"GetEnvironmentVariable failed"); 81 | } 82 | } 83 | HANDLE fmo = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, fmo_name); 84 | if (!fmo) { 85 | report(HRESULT_FROM_WIN32(GetLastError()), L"OpenFileMapping failed"); 86 | goto finish; 87 | } 88 | struct share_mem_header* view = (struct share_mem_header*)MapViewOfFile(fmo, FILE_MAP_WRITE, 0, 0, 0); 89 | if (!view) { 90 | report(HRESULT_FROM_WIN32(GetLastError()), L"MapViewOfFile failed"); 91 | goto close_fmo; 92 | } 93 | app->WaitInitialize(); 94 | 95 | { 96 | std::string buf; 97 | std::wstring path, userfile, tabid, win, wout; 98 | int32_t len(0); 99 | while (1) { 100 | if (!read(in, &len, sizeof(len))) { 101 | goto unmap_view; 102 | } 103 | buf.resize(len); 104 | if (!read(in, &buf[0], len)) { 105 | goto unmap_view; 106 | } 107 | size_t pos = 0; 108 | struct header* h = reinterpret_cast(&buf[pos]); 109 | if (h->version != 2) { 110 | OutputDebugString(L"Unsupported header version"); 111 | goto unmap_view; 112 | } 113 | pos += sizeof(struct header); 114 | from_sjis(&buf[pos], static_cast(h->path_len), path); 115 | pos += h->path_len; 116 | from_sjis(&buf[pos], static_cast(h->userfile_len), userfile); 117 | pos += h->userfile_len; 118 | from_sjis(&buf[pos], static_cast(h->tabid_len), tabid); 119 | pos += h->tabid_len; 120 | from_sjis(&buf[pos], static_cast(buf.size() - pos), win); 121 | const bool r = app->RenderAndCapture( 122 | path, 123 | tabid, 124 | (h->flags & 1) != 0, 125 | (h->flags & 2) != 0, 126 | userfile, 127 | win, 128 | wout, 129 | (void*)(((char*)view) + view->header_size), view->width, view->height); 130 | to_sjis(&wout[0], static_cast(wout.size()), buf); 131 | if (!FlushViewOfFile(view, 0)) { 132 | report(HRESULT_FROM_WIN32(GetLastError()), L"FlushViewOfFile failed"); 133 | goto unmap_view; 134 | } 135 | len = 1 + (int32_t)buf.size(); 136 | if (!write(out, &len, sizeof(len))) { 137 | goto unmap_view; 138 | } 139 | const char b = r ? 1 : 0; 140 | if (!write(out, &b, sizeof(b))) { 141 | goto unmap_view; 142 | } 143 | if (!buf.empty()) { 144 | if (!write(out, &buf[0], static_cast(buf.size()))) { 145 | goto unmap_view; 146 | } 147 | } 148 | } 149 | } 150 | 151 | unmap_view: 152 | if (!UnmapViewOfFile(view)) { 153 | report(HRESULT_FROM_WIN32(GetLastError()), L"UnmapViewOfFile failed"); 154 | } 155 | close_fmo: 156 | if (!CloseHandle(fmo)) { 157 | report(HRESULT_FROM_WIN32(GetLastError()), L"CloseHandle failed"); 158 | } 159 | finish: 160 | app->CloseAllBrowsers(); 161 | return 1; 162 | } 163 | 164 | static bool create_cache_dir(std::wstring& o) { 165 | std::wstring s; 166 | if (!get_module_file_name(NULL, s)) { 167 | return false; 168 | } 169 | for (auto i = s.size() - 1; i > 0; --i) { 170 | if ((s[i] == L'/' || s[i] == L'\\')) { 171 | s.resize(i + 1); 172 | s += L"cache"; 173 | break; 174 | } 175 | } 176 | if (!CreateDirectoryW(s.c_str(), NULL)) { 177 | const DWORD err = GetLastError(); 178 | if (err != ERROR_ALREADY_EXISTS) { 179 | report(HRESULT_FROM_WIN32(GetLastError()), L"CreateDirectory failed"); 180 | return false; 181 | } 182 | } 183 | o = s; 184 | return true; 185 | } 186 | 187 | int APIENTRY wWinMain(HINSTANCE hInstance, 188 | HINSTANCE hPrevInstance, 189 | LPTSTR lpCmdLine, 190 | int nCmdShow) { 191 | UNREFERENCED_PARAMETER(hPrevInstance); 192 | UNREFERENCED_PARAMETER(lpCmdLine); 193 | // CefEnableHighDPISupport(); 194 | 195 | std::thread t1; 196 | { 197 | void* sandbox_info = NULL; 198 | #if defined(CEF_USE_SANDBOX) 199 | CefScopedSandboxInfo scoped_sandbox; 200 | sandbox_info = scoped_sandbox.sandbox_info(); 201 | #endif 202 | CefMainArgs main_args(hInstance); 203 | CefRefPtr app(new App); 204 | 205 | int exit_code = CefExecuteProcess(main_args, app, sandbox_info); 206 | if (exit_code >= 0) { 207 | return exit_code; 208 | } 209 | 210 | CefSettings settings; 211 | settings.windowless_rendering_enabled = true; 212 | 213 | std::wstring cache; 214 | if (!create_cache_dir(cache)) { 215 | return 1; 216 | } 217 | CefString(&settings.root_cache_path).FromWString(cache); 218 | CefString(&settings.cache_path).FromWString(cache); 219 | 220 | std::wstring ver(L"AviUtlBrowser/"); 221 | ver += version; 222 | CefString(&settings.user_agent_product).FromWString(ver); 223 | 224 | #if !defined(CEF_USE_SANDBOX) 225 | settings.no_sandbox = true; 226 | #endif 227 | 228 | LANGID langId = GetUserDefaultLangID(); 229 | wchar_t lang[32] = {}, ctry[32] = {}; 230 | if (!GetLocaleInfo(langId, LOCALE_SISO639LANGNAME, lang, 32)) { 231 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetLocaleInfo failed"); 232 | return 1; 233 | } 234 | if (!GetLocaleInfo(langId, LOCALE_SISO3166CTRYNAME, ctry, 32)) { 235 | report(HRESULT_FROM_WIN32(GetLastError()), L"GetLocaleInfo failed"); 236 | return 1; 237 | } 238 | std::wstring locale(lang); 239 | locale += L"-"; 240 | locale += std::wstring(ctry); 241 | CefString(&settings.locale).FromWString(locale); 242 | CefString(&settings.accept_language_list).FromWString(locale); 243 | 244 | CefInitialize(main_args, settings, app.get(), sandbox_info); 245 | t1 = std::thread(ipc_worker, app); 246 | } 247 | 248 | CefRunMessageLoop(); 249 | t1.join(); 250 | CefShutdown(); 251 | return 0; 252 | } 253 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights 2 | # reserved. Use of this source code is governed by a BSD-style license that 3 | # can be found in the LICENSE file. 4 | 5 | # OVERVIEW 6 | # 7 | # CMake is a cross-platform open-source build system that can generate project 8 | # files in many different formats. It can be downloaded from 9 | # http://www.cmake.org or installed via a platform package manager. 10 | # 11 | # CMake-generated project formats that have been tested with this CEF binary 12 | # distribution include: 13 | # 14 | # Linux: Ninja, GCC 7.5.0+, Unix Makefiles 15 | # MacOS: Ninja, Xcode 12.2 to 15.0 16 | # Windows: Ninja, Visual Studio 2022 17 | # 18 | # Ninja is a cross-platform open-source tool for running fast builds using 19 | # pre-installed platform toolchains (GNU, clang, Xcode or MSVC). It can be 20 | # downloaded from http://martine.github.io/ninja/ or installed via a platform 21 | # package manager. 22 | # 23 | # CMAKE STRUCTURE 24 | # 25 | # This CEF binary distribution includes the following CMake files: 26 | # 27 | # CMakeLists.txt Bootstrap that sets up the CMake environment. 28 | # cmake/*.cmake CEF configuration files shared by all targets. 29 | # libcef_dll/CMakeLists.txt Defines the libcef_dll_wrapper target. 30 | # tests/*/CMakeLists.txt Defines the test application target. 31 | # 32 | # See the "TODO:" comments below for guidance on how to integrate this CEF 33 | # binary distribution into a new or existing CMake project. 34 | # 35 | # BUILD REQUIREMENTS 36 | # 37 | # The below requirements must be met to build this CEF binary distribution. 38 | # 39 | # - CMake version 3.21 or newer. 40 | # 41 | # - Linux requirements: 42 | # Currently supported distributions include Debian 10 (Buster), Ubuntu 18 43 | # (Bionic Beaver), and related. Ubuntu 18.04 64-bit with GCC 7.5.0+ is 44 | # recommended. Newer versions will likely also work but may not have been 45 | # tested. 46 | # Required packages include: 47 | # build-essential 48 | # libgtk3.0-dev (required by the cefclient target only) 49 | # 50 | # - MacOS requirements: 51 | # Xcode 12.2 to 15.4 building on MacOS 11.0 (Big Sur) or newer. The Xcode 52 | # command-line tools must also be installed. Newer Xcode versions may not have 53 | # been been tested and are not recommended. 54 | # 55 | # - Windows requirements: 56 | # Visual Studio 2022 building on Windows 10 or newer. Windows 10/11 64-bit is 57 | # recommended. Newer versions will likely also work but may not have been 58 | # tested. 59 | # 60 | # BUILD EXAMPLES 61 | # 62 | # The below commands will generate project files and create a Debug build of all 63 | # CEF targets using CMake and the platform toolchain. 64 | # 65 | # Start by creating and entering the CMake build output directory: 66 | # > cd path/to/cef_binary_* 67 | # > mkdir build && cd build 68 | # 69 | # To perform a Linux build using a 32-bit CEF binary distribution on a 32-bit 70 | # Linux platform or a 64-bit CEF binary distribution on a 64-bit Linux platform: 71 | # Using Unix Makefiles: 72 | # > cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug .. 73 | # > make -j4 cefclient cefsimple 74 | # 75 | # Using Ninja: 76 | # > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug .. 77 | # > ninja cefclient cefsimple 78 | # 79 | # To perform a MacOS build using a 64-bit CEF binary distribution: 80 | # Using the Xcode IDE: 81 | # > cmake -G "Xcode" -DPROJECT_ARCH="x86_64" .. 82 | # Open build\cef.xcodeproj in Xcode and select Product > Build. 83 | # 84 | # Using Ninja: 85 | # > cmake -G "Ninja" -DPROJECT_ARCH="x86_64" -DCMAKE_BUILD_TYPE=Debug .. 86 | # > ninja cefclient cefsimple 87 | # 88 | # To perform a MacOS build using an ARM64 CEF binary distribution: 89 | # Using the Xcode IDE: 90 | # > cmake -G "Xcode" -DPROJECT_ARCH="arm64" .. 91 | # Open build\cef.xcodeproj in Xcode and select Product > Build. 92 | # 93 | # Using Ninja: 94 | # > cmake -G "Ninja" -DPROJECT_ARCH="arm64" -DCMAKE_BUILD_TYPE=Debug .. 95 | # > ninja cefclient cefsimple 96 | # 97 | # To perform a Windows build using a 32-bit CEF binary distribution: 98 | # Using the Visual Studio 2022 IDE: 99 | # > cmake -G "Visual Studio 17" -A Win32 .. 100 | # Open build\cef.sln in Visual Studio and select Build > Build Solution. 101 | # 102 | # Using Ninja with Visual Studio 2022 command-line tools: 103 | # (this path may be different depending on your Visual Studio installation) 104 | # > "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars32.bat" 105 | # > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug .. 106 | # > ninja cefclient cefsimple 107 | # 108 | # To perform a Windows build using a 64-bit CEF binary distribution: 109 | # Using the Visual Studio 2022 IDE: 110 | # > cmake -G "Visual Studio 17" -A x64 .. 111 | # Open build\cef.sln in Visual Studio and select Build > Build Solution. 112 | # 113 | # Using Ninja with Visual Studio 2022 command-line tools: 114 | # (this path may be different depending on your Visual Studio installation) 115 | # > "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" 116 | # > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug .. 117 | # > ninja cefclient cefsimple 118 | # 119 | # To perform a Windows build using an ARM64 CEF binary distribution: 120 | # Using the Visual Studio 2022 IDE: 121 | # > cmake -G "Visual Studio 17" -A arm64 .. 122 | # Open build\cef.sln in Visual Studio and select Build > Build Solution. 123 | # 124 | # Using Ninja with Visual Studio 2022 command-line tools: 125 | # (this path may be different depending on your Visual Studio installation) 126 | # > "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsamd64_arm64.bat" 127 | # > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug .. 128 | # > ninja cefsimple 129 | 130 | # 131 | # Global setup. 132 | # 133 | 134 | # For VS2022 and Xcode 12+ support. 135 | cmake_minimum_required(VERSION 3.21) 136 | 137 | # Only generate Debug and Release configuration types. 138 | set(CMAKE_CONFIGURATION_TYPES Debug Release) 139 | 140 | # Project name. 141 | # TODO: Change this line to match your project name when you copy this file. 142 | project(aviutl_browser) 143 | 144 | # Use folders in the resulting project files. 145 | set_property(GLOBAL PROPERTY OS_FOLDERS ON) 146 | 147 | 148 | # 149 | # CEF_ROOT setup. 150 | # This variable must be set to locate the binary distribution. 151 | 152 | set(CEF_URL "https://cef-builds.spotifycdn.com/cef_binary_129.0.12%2Bgf09539f%2Bchromium-129.0.6668.101_windows64_minimal.tar.bz2") 153 | set(CEF_ARCHIVE_PATH "${CMAKE_BINARY_DIR}/cef.tar.bz2") 154 | set(CEF_DIR "${CMAKE_BINARY_DIR}/cef") 155 | 156 | set(CEF_ROOT "${CEF_DIR}") 157 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CEF_ROOT}/cmake") 158 | 159 | if(NOT EXISTS "${CEF_ARCHIVE_PATH}") 160 | file(DOWNLOAD "${CEF_URL}" "${CEF_ARCHIVE_PATH}") 161 | endif() 162 | 163 | if(NOT EXISTS "${CEF_DIR}") 164 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvzf "${CEF_ARCHIVE_PATH}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") 165 | file(GLOB old_dir LIST_DIRECTORIES true "${CMAKE_BINARY_DIR}/cef_binary_*") 166 | file(RENAME "${old_dir}" "${CEF_DIR}") 167 | endif() 168 | 169 | # 170 | # Load the CEF configuration. 171 | # 172 | 173 | # Execute FindCEF.cmake which must exist in CMAKE_MODULE_PATH. 174 | find_package(CEF REQUIRED) 175 | 176 | 177 | # 178 | # Define CEF-based targets. 179 | # 180 | 181 | # Include the libcef_dll_wrapper target. 182 | # Comes from the libcef_dll/CMakeLists.txt file in the binary distribution 183 | # directory. 184 | add_subdirectory(${CEF_LIBCEF_DLL_WRAPPER_PATH} libcef_dll_wrapper) 185 | 186 | add_subdirectory(exe) 187 | 188 | # Display configuration settings. 189 | PRINT_CEF_CONFIG() 190 | 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aviutl_browser 2 | 3 | AviUtl の拡張編集プラグイン上でブラウザーの表示内容を持ち込めるようにするためのプログラムです。 4 | 使用には bridge.dll ( https://github.com/oov/aviutl_bridge ) の導入が必要です。 5 | 6 | ## 注意事項 7 | 8 | aviutl_browser は無保証で提供されます。 9 | aviutl_browser を使用したこと及び使用しなかったことによるいかなる損害について、開発者は一切の責任を負いません。 10 | 11 | これに同意できない場合、あなたは aviutl_browser を使用することができません。 12 | 13 | ## ダウンロード 14 | 15 | https://github.com/oov/aviutl_browser/releases 16 | 17 | ## インストール方法 18 | 19 | exedit.auf と同じ場所にある script フォルダー内に、browser フォルダーを入れればインストール完了です。 20 | bridge.dll も必要になるので忘れずにインストールしておいてください。 21 | 22 | 拡張編集上で追加されたカスタムオブジェクトを選ぶと `browser\exe\aviutl_browser.exe` が起動されます。 23 | このプログラムがウィルス対策ソフトなどでブロックされないようにしてください。 24 | 25 | ## 表示用コンテンツの作り方 26 | 27 | やることの大筋は以下の2ステップです。 28 | 29 | 作例として `絵文字` / `Markdown` も付属しているので参考にしてください。 30 | 31 | ### 1. カスタムオブジェクト用のスクリプトを作成する 32 | 33 | カスタムオブジェクト用のスクリプトである `*.obj` を `browser` フォルダー内に作成します。 34 | このスクリプトからブラウザーを呼び出すことになります。 35 | (最終的にスクリプトが実行できればいいので、呼び出し元はアニメーション効果などでも構いません) 36 | 37 | ```lua 38 | -- ブラウザーの表示内容を受け取るために透明の画像を用意する 39 | obj.setoption("drawtarget", "tempbuffer", 1920, 1080) 40 | obj.load("tempbuffer") 41 | 42 | -- フォルダー指定の場合 43 | local ok, ret = require("browser").execute({ 44 | dir = "mycontent", -- contents フォルダー内のフォルダー名を指定する 45 | tabid = "", -- 同じコンテンツをブラウザーの別タブで開く場合は識別用の文字列を指定する、必要なければ省略可 46 | param = "文字列をJavaScriptに渡せます", -- 必要なければ省略可 47 | userfile = file, -- ファイル参照ダイアログを使う場合 48 | dev = false, -- 開発モードを有効にする場合は true 49 | }); 50 | 51 | -- abc ファイル指定の場合 52 | local ok, ret = require("browser").execute({ 53 | abc = "mycontent.abc", -- contents フォルダー内のファイル名を指定する 54 | tabid = "", -- 同じコンテンツをブラウザーの別タブで開く場合は識別用の文字列を指定する、必要なければ省略可 55 | param = "文字列をJavaScriptに渡せます", -- 必要なければ省略可 56 | userfile = file, -- ファイル参照ダイアログを使う場合 57 | dev = false, -- 開発モードを有効にする場合は true 58 | }); 59 | 60 | -- 戻り値: 61 | -- ok - 成功した場合は true, 失敗した場合は false 62 | -- ret - 成功した場合は HTML から resolve で渡した文字列 63 | ``` 64 | 65 | 開発モードを有効にすると DevTools ウィンドウが表示されます。 66 | このウィンドウで F5 を押すと HTML のリロードができるため、製作中に AviUtl を再起動しなくても HTML などの変更を反映させられます。 67 | 68 | `tabid` は同じコンテンツをブラウザー上で別のタブとして複数保持したい場合に使用できます。 69 | 例えば同じカスタムオブジェクトをレイヤー1とレイヤー2に置いて同時に表示する場合、`tabid` が同じだと毎フレーム `レイヤー1を描画` → `レイヤー2を描画` と交互に呼び出され続けることになるため、描画するたびに表示内容が変わっていることになり効率が悪くなる可能性があります。 70 | それぞれに別々の `tabid` を割り当てておけば片方のタブはレイヤー1の描画だけ、もう片方のタブはレイヤー2の描画だけを処理すれば良くなります。 71 | ただし当然別タブに読み込む分のオーバーヘッドがあるので、可能な限り HTML 側で交互に呼び出されてもパフォーマンスが落ちないように準備しておくのが望ましいです。 72 | 73 | ### 2. ブラウザーで表示するコンテンツを `browser\contents` フォルダー内に作成する 74 | 75 | コンテンツのデータ準備方法には、フォルダーを指定する方法と、abc ファイルを指定する方法の2つがあります。 76 | 付属コンテンツでは abc ファイルを指定する方法が使われています。 77 | 78 | abc ファイルの実態は「表示用データを詰め込んだ zip ファイルを作成し、拡張子を変更しただけ」のファイルです。 79 | コンテンツの利用者がファイルの中身を気にしなくてもいい場合は abc ファイルを使うとファイルが散らばりません。 80 | 逆に、適宜 HTML などに手を入れながら使うような使われ方を想定する場合はフォルダー指定のほうが適しています。 81 | 82 | ブラウザーが起動されると、フォルダーや abc ファイルのルートにある `index.html` が読み込まれます。 83 | JavaScript で `AviUtlBrowser.registerRenderer(render)` などとして関数を登録しておくと、`render` 関数が毎回の描画時に呼び出されるようになります。 84 | これを利用して、トラックバーのデータなどを毎フレーム HTML 内に反映させることでアニメーションなどを行えます。 85 | 86 | ```html 87 | 88 | 89 |

Hello world !

90 | 99 | ``` 100 | 101 | ## コンテンツ制作時の注意事項 102 | 103 | - 画面左上の1ピクセルは使用できません 104 | - 内部で画面更新を検知するために、左上の1ピクセルを使用しています 105 | 画像などを左上ピッタリから表示してしまうと左上の1ピクセルが欠けてしまうので、必要に応じて回避してください 106 | - html タグや body タグへの CSS 適用に注意 107 | - 更新検知用のドットが描画される位置がずれてしまうと、正しく更新を検知できずに AviUtl がフリーズする可能性があります 108 | - `margin` や `padding` などを確保したい場合は `div` などを作ってそこに当ててください 109 | - オンライン上のデータへアクセスするスクリプトは十分に注意して作成すること 110 | - 毎フレームアクセスするようなことをすると相手に多大な迷惑がかかるので絶対にやらないでください 111 | - 例えば「岡崎市立中央図書館事件」では1秒に1回のアクセスで逮捕されています 112 | - abc ファイルを作るときはフォルダーごと圧縮しないこと 113 | - zip ファイルのルートにはフォルダーではなく index.html が来るようにしてください 114 | - ファイル参照ボタンも使えます 115 | - Lua 側から `userfile = file` で渡すと、HTML 側で `/userfile` で受け取ることができます 116 | - 使用例: https://gist.github.com/oov/40ac2545d70527f5f2af69b7fa02a1fe 117 | - 今のところ複数のファイルを渡す仕組みは想定されていません 118 | 119 | ## FAQ 120 | 121 | ### Q. AviUtl がフリーズする、動かない 122 | 123 | サンプルとして付属しているコンテンツが動かない場合はブラウザーがブロックされずに正しく動作しているか確認してください。 124 | 自作したコンテンツが動かない場合は「コンテンツ制作時の注意事項」の内容を確認してみてください。 125 | 126 | 例えば JavaScript で `resolve()` を呼び忘れると、ブラウザ側に完了したことが伝えられないのでいつまでも待ち状態になります。 127 | 途中で例外が投げられる場合などにも注意してください。 128 | 129 | ### Q. DevTools が閉じられない 130 | 131 | 開発モードが有効なときに開く DevTools は、設計上ウィンドウを直接閉じることはできません。 132 | (閉じられるようにしてもいいけど1フレーム動かしたらまた出てくるだけなので……) 133 | 134 | ### Q. 自作の abc ファイルだと index.html が読み込めない 135 | 136 | ファイルの直下にはフォルダーではなく index.html が来るようにしてください。 137 | 138 | ## ビルドについて 139 | 140 | リリース用ファイルは GitHub Actions で自動生成しています。 141 | ビルド方法や必要になるパッケージなどは [GitHub Actions の設定ファイル](https://github.com/oov/aviutl_browser/blob/main/.github/workflows/releaser.yml) を参照してください。 142 | 143 | ## Credits 144 | 145 | aviutl_browser is made possible by the following open source softwares. 146 | 147 | ### Chromium Embedded Framework 148 | 149 | https://bitbucket.org/chromiumembedded/cef 150 | 151 | Copyright (c) 2008-2020 Marshall A. Greenblatt. Portions Copyright (c) 152 | 2006-2009 Google Inc. All rights reserved. 153 | 154 | Redistribution and use in source and binary forms, with or without 155 | modification, are permitted provided that the following conditions are 156 | met: 157 | 158 | * Redistributions of source code must retain the above copyright 159 | notice, this list of conditions and the following disclaimer. 160 | * Redistributions in binary form must reproduce the above 161 | copyright notice, this list of conditions and the following disclaimer 162 | in the documentation and/or other materials provided with the 163 | distribution. 164 | * Neither the name of Google Inc. nor the name Chromium Embedded 165 | Framework nor the names of its contributors may be used to endorse 166 | or promote products derived from this software without specific prior 167 | written permission. 168 | 169 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 170 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 171 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 172 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 173 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 174 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 175 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 176 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 177 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 178 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 179 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 180 | 181 | See also LICENSES.chromium.html for Chromium open source credit list. 182 | 183 | ### PicoJSON 184 | 185 | https://github.com/kazuho/picojson 186 | 187 | Copyright 2009-2010 Cybozu Labs, Inc. 188 | Copyright 2011-2014 Kazuho Oku 189 | All rights reserved. 190 | 191 | Redistribution and use in source and binary forms, with or without 192 | modification, are permitted provided that the following conditions are met: 193 | 194 | 1. Redistributions of source code must retain the above copyright notice, 195 | this list of conditions and the following disclaimer. 196 | 197 | 2. Redistributions in binary form must reproduce the above copyright notice, 198 | this list of conditions and the following disclaimer in the documentation 199 | and/or other materials provided with the distribution. 200 | 201 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 202 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 203 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 204 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 205 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 206 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 207 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 208 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 209 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 210 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 211 | POSSIBILITY OF SUCH DAMAGE. 212 | -------------------------------------------------------------------------------- /src/exe/assets.cc: -------------------------------------------------------------------------------- 1 | #include "assets_win.h" 2 | 3 | #include "picojson.h" 4 | 5 | struct t_status_code { 6 | int code; 7 | PCWCHAR status; 8 | }; 9 | 10 | // based on https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml 11 | static const struct t_status_code status_codes[] = { 12 | {100, L"Continue"}, // [RFC7231, Section 6.2.1] 13 | {101, L"Switching Protocols"}, // [RFC7231, Section 6.2.2] 14 | {102, L"Processing"}, // [RFC2518] 15 | {103, L"Early Hints"}, // [RFC8297] 16 | // 104 - 199 Unassigned 17 | {200, L"OK"}, // [RFC7231, Section 6.3.1] 18 | {201, L"Created"}, // [RFC7231, Section 6.3.2] 19 | {202, L"Accepted"}, // [RFC7231, Section 6.3.3] 20 | {203, L"Non - Authoritative Information"}, // [RFC7231, Section 6.3.4] 21 | {204, L"No Content"}, // [RFC7231, Section 6.3.5] 22 | {205, L"Reset Content"}, // [RFC7231, Section 6.3.6] 23 | {206, L"Partial Content"}, // [RFC7233, Section 4.1] 24 | {207, L"Multi - Status"}, // [RFC4918] 25 | {208, L"Already Reported"}, // [RFC5842] 26 | // 209 - 225 Unassigned 27 | {226, L"IM Used"}, // [RFC3229] 28 | // 227 - 299 Unassigned 29 | {300, L"Multiple Choices"}, // [RFC7231, Section 6.4.1] 30 | {301, L"Moved Permanently"}, // [RFC7231, Section 6.4.2] 31 | {302, L"Found"}, // [RFC7231, Section 6.4.3] 32 | {303, L"See Other"}, // [RFC7231, Section 6.4.4] 33 | {304, L"Not Modified"}, // [RFC7232, Section 4.1] 34 | {305, L"Use Proxy"}, // [RFC7231, Section 6.4.5] 35 | {306, L"(Unused)"}, // [RFC7231, Section 6.4.6] 36 | {307, L"Temporary Redirect"}, // [RFC7231, Section 6.4.7] 37 | {308, L"Permanent Redirect"}, // [RFC7538] 38 | // 309 - 399 Unassigned 39 | {400, L"Bad Request"}, // [RFC7231, Section 6.5.1] 40 | {401, L"Unauthorized"}, // [RFC7235, Section 3.1] 41 | {402, L"Payment Required"}, // [RFC7231, Section 6.5.2] 42 | {403, L"Forbidden"}, // [RFC7231, Section 6.5.3] 43 | {404, L"Not Found"}, // [RFC7231, Section 6.5.4] 44 | {405, L"Method Not Allowed"}, // [RFC7231, Section 6.5.5] 45 | {406, L"Not Acceptable"}, // [RFC7231, Section 6.5.6] 46 | {407, L"Proxy Authentication Required"}, // [RFC7235, Section 3.2] 47 | {408, L"Request Timeout"}, // [RFC7231, Section 6.5.7] 48 | {409, L"Conflict"}, // [RFC7231, Section 6.5.8] 49 | {410, L"Gone"}, // [RFC7231, Section 6.5.9] 50 | {411, L"Length Required"}, // [RFC7231, Section 6.5.10] 51 | {412, L"Precondition Failed"}, // [RFC7232, Section 4.2]"}, // [RFC8144, Section 3.2] 52 | {413, L"Payload Too Large"}, // [RFC7231, Section 6.5.11] 53 | {414, L"URI Too Long"}, // [RFC7231, Section 6.5.12] 54 | {415, L"Unsupported Media Type"}, // [RFC7231, Section 6.5.13]"}, // [RFC7694, Section 3] 55 | {416, L"Range Not Satisfiable"}, // [RFC7233, Section 4.4] 56 | {417, L"Expectation Failed"}, // [RFC7231, Section 6.5.14] 57 | // 418 - 420 Unassigned 58 | {421, L"Misdirected Request"}, // [RFC7540, Section 9.1.2] 59 | {422, L"Unprocessable Entity"}, // [RFC4918] 60 | {423, L"Locked"}, // [RFC4918] 61 | {424, L"Failed Dependency"}, // [RFC4918] 62 | {425, L"Too Early"}, // [RFC8470] 63 | {426, L"Upgrade Required"}, // [RFC7231, Section 6.5.15] 64 | // 427 - Unassigned 65 | {428, L"Precondition Required"}, // [RFC6585] 66 | {429, L"Too Many Requests"}, // [RFC6585] 67 | // 430 - Unassigned 68 | {431, L"Request Header Fields Too Large"}, // [RFC6585] 69 | // 432 - 450 Unassigned 70 | {451, L"Unavailable For Legal Reasons"}, // [RFC7725] 71 | // 452 - 499 Unassigned 72 | {500, L"Internal Server Error"}, // [RFC7231, Section 6.6.1] 73 | {501, L"Not Implemented"}, // [RFC7231, Section 6.6.2] 74 | {502, L"Bad Gateway"}, // [RFC7231, Section 6.6.3] 75 | {503, L"Service Unavailable"}, // [RFC7231, Section 6.6.4] 76 | {504, L"Gateway Timeout"}, // [RFC7231, Section 6.6.5] 77 | {505, L"HTTP Version Not Supported"}, // [RFC7231, Section 6.6.6] 78 | {506, L"Variant Also Negotiates"}, // [RFC2295] 79 | {507, L"Insufficient Storage"}, // [RFC4918] 80 | {508, L"Loop Detected"}, // [RFC5842] 81 | // 509 Unassigned 82 | {510, L"Not Extended"}, // [RFC2774] 83 | {511, L"Network Authentication Required"}, // [RFC6585] 84 | // 512 - 599 Unassigned 85 | }; 86 | 87 | static PCWCHAR get_status_code_description(int status_code) { 88 | const size_t len = sizeof(status_codes) / sizeof(t_status_code); 89 | for (size_t i = 0; i < len; ++i) { 90 | if (status_code == status_codes[i].code) { 91 | return status_codes[i].status; 92 | } 93 | } 94 | return L"Unknown"; 95 | } 96 | 97 | CefRefPtr StreamResourceHandler::CreateFromString( 98 | int status_code, 99 | const CefString& mime_type, 100 | CefResponse::HeaderMap header_map, 101 | std::string& content) { 102 | auto r = new StreamResourceHandler( 103 | status_code, 104 | get_status_code_description(status_code), 105 | mime_type, 106 | header_map, 107 | CefStreamReader::CreateForData((void*)&content[0], content.size()) 108 | ); 109 | r->content = std::move(content); 110 | return r; 111 | } 112 | 113 | CefRefPtr StreamResourceHandler::CreateFromFile( 114 | int status_code, 115 | const CefString& mime_type, 116 | CefResponse::HeaderMap header_map, 117 | const CefString& filepath) { 118 | auto stream = CefStreamReader::CreateForFile(filepath); 119 | if (!stream) { 120 | return StreamResourceHandler::CreateError(500, "failed to read file"); 121 | } 122 | return new CefStreamResourceHandler( 123 | status_code, 124 | get_status_code_description(status_code), 125 | mime_type, 126 | header_map, 127 | stream 128 | ); 129 | } 130 | 131 | CefRefPtr StreamResourceHandler::CreateError(int status_code, const CefString& message) { 132 | std::string html("

"); 133 | html += message; 134 | html += "

"; 135 | return StreamResourceHandler::CreateFromString(status_code, "text/html", CefResponse::HeaderMap(), html); 136 | } 137 | 138 | Assets::Assets() : mime_map_initialized(false) { 139 | } 140 | 141 | void Assets::GetMimeJson(CefString& json) { 142 | json = R"JSON({".html":"text/html",".css":"text/css",".js":"text/javascript",".mjs":"text/javascript",".txt":"text/plain",".xml":"text/xml",".csv":"text/csv",".rss":"application/rss+xml",".json":"application/json",".ico":"image/vnd.microsoft.icon",".svg":"image/svg+xml",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp",".bmp":"image/bmp",".woff":"font/woff",".woff2":"font/woff2",".otf":"font/otf",".ttf":"font/ttf",".eot":"application/vnd.ms-fontobject",".webm":"video/webm",".mp4":"video/mp4",".ts":"video/mp2t",".mpg":"video/mpeg",".mpeg":"video/mpeg",".ogv":"video/ogg",".mov":"video/quicktime",".qt":"video/quicktime",".avi":"video/x-msvideo",".mp3":"audio/mpeg",".m4a":"audio/aac",".aac":"audio/aac",".mid":"audio/midi",".oga":"audio/ogg",".opus":"audio/opus",".wav":"audio/wav",".zip":"application/zip",".7z":"application/x-7z-compressed",".tar":"application/x-tar",".rar":"application/vnd.rar",".pdf":"application/pdf",".epub":"application/epub+zip",".gz":"application/gzip",".jar":"application/java-archive",".wasm":"application/wasm"})JSON"; 143 | } 144 | 145 | CefRefPtr Assets::operator()(const CefString& path) { 146 | { 147 | std::lock_guard lk(mtx_); 148 | if (!mime_map_initialized) { 149 | InitializeMimeMap(); 150 | } 151 | } 152 | if (path.c_str()[path.size() - 1] == L'/') { 153 | std::wstring tmp(path.ToWString()); 154 | tmp += L"index.html"; 155 | return Get(tmp); 156 | } 157 | return Get(path); 158 | } 159 | 160 | void Assets::InitializeMimeMap() { 161 | mime_map_initialized = true; 162 | mime_map_.clear(); 163 | 164 | picojson::value v; 165 | { 166 | CefString json; 167 | GetMimeJson(json); 168 | const std::wstring w = json.ToWString(); 169 | auto st = w.cbegin(), ed = w.cend(); 170 | const std::string err = picojson::parse(v, st, ed); 171 | if (!err.empty() || !v.is()) { 172 | return; 173 | } 174 | } 175 | const picojson::object& obj = v.get(); 176 | for (auto i = obj.cbegin(); i != obj.cend(); ++i) { 177 | mime_map_[i->first] = i->second.to_str(); 178 | } 179 | } 180 | 181 | static int find_last_dot(const CefString& s) { 182 | int dot = -1; 183 | const CefString::char_type *ptr = s.c_str(); 184 | for (size_t p = 0; p < s.length(); ++p) { 185 | if (ptr[p] == L'.') { 186 | dot = (int)p; 187 | } 188 | } 189 | return dot; 190 | } 191 | 192 | void Assets::GetMime(const CefString& path, CefString& mime) { 193 | PCWCHAR octetStream = L"application/octet-stream"; 194 | const int last_dot = find_last_dot(path); 195 | if (last_dot == -1) { 196 | mime = octetStream; 197 | return; 198 | } 199 | 200 | std::lock_guard lk(mtx_); 201 | const CefString ext(path.c_str() + last_dot, path.length()-last_dot, false); 202 | auto it = mime_map_.find(ext); 203 | if (it == mime_map_.cend()) { 204 | mime = octetStream; 205 | return; 206 | } 207 | mime = it->second; 208 | return; 209 | } 210 | 211 | void Assets::SetDefaultHeaders(CefResponse::HeaderMap& header_map) { 212 | header_map.insert(std::make_pair(L"Cross-Origin-Embedder-Policy", L"require-corp")); 213 | header_map.insert(std::make_pair(L"Cross-Origin-Opener-Policy", L"same-origin")); 214 | } 215 | -------------------------------------------------------------------------------- /src/exe/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Source files. 3 | # 4 | 5 | # aviutl_browser sources. 6 | set(AVIUTLBROWSER_SRCS 7 | miniz.c 8 | assets.cc 9 | assets.h 10 | app.cc 11 | app.h 12 | client.cc 13 | client.h 14 | callback.h 15 | depremultipliedalpha_table.h 16 | picojson.h 17 | ) 18 | set(AVIUTLBROWSER_SRCS_LINUX 19 | aviutl_browser_linux.cc 20 | client_linux.cc 21 | ) 22 | set(AVIUTLBROWSER_SRCS_MACOSX 23 | aviutl_browser_mac.mm 24 | client_mac.mm 25 | ) 26 | set(AVIUTLBROWSER_SRCS_WINDOWS 27 | aviutl_browser.exe.manifest 28 | aviutl_browser.rc 29 | aviutl_browser_win.cc 30 | app_win.cc 31 | client_win.cc 32 | assets_win.cc 33 | assets_win.h 34 | path_win.cc 35 | path_win.h 36 | resource.h 37 | report_win.cc 38 | report_win.h 39 | unicode_win.h 40 | ) 41 | APPEND_PLATFORM_SOURCES(AVIUTLBROWSER_SRCS) 42 | source_group(aviutl_browser FILES ${AVIUTLBROWSER_SRCS}) 43 | 44 | # aviutl_browser helper sources. 45 | set(AVIUTLBROWSER_HELPER_SRCS_MACOSX 46 | process_helper_mac.cc 47 | ) 48 | APPEND_PLATFORM_SOURCES(AVIUTLBROWSER_HELPER_SRCS) 49 | source_group(aviutl_browser FILES ${AVIUTLBROWSER_HELPER_SRCS}) 50 | 51 | # aviutl_browser resources. 52 | set(AVIUTLBROWSER_RESOURCES_MAC_SRCS_MACOSX 53 | mac/Info.plist 54 | mac/aviutl_browser.icns 55 | ) 56 | APPEND_PLATFORM_SOURCES(AVIUTLBROWSER_RESOURCES_MAC_SRCS) 57 | source_group(aviutl_browser\\\\mac FILES ${AVIUTLBROWSER_RESOURCES_MAC_SRCS}) 58 | 59 | set(AVIUTLBROWSER_RESOURCES_MAC_ENGLISH_LPROJ_SRCS_MACOSX 60 | mac/English.lproj/InfoPlist.strings 61 | mac/English.lproj/MainMenu.xib 62 | ) 63 | APPEND_PLATFORM_SOURCES(AVIUTLBROWSER_RESOURCES_MAC_ENGLISH_LPROJ_SRCS) 64 | source_group(aviutl_browser\\\\mac\\\\English.lproj FILES ${AVIUTLBROWSER_RESOURCES_MAC_ENGLISH_LPROJ_SRCS}) 65 | 66 | set(AVIUTLBROWSER_RESOURCES_SRCS 67 | ${AVIUTLBROWSER_RESOURCES_MAC_SRCS} 68 | ${AVIUTLBROWSER_RESOURCES_MAC_ENGLISH_LPROJ_SRCS} 69 | ) 70 | 71 | 72 | # 73 | # Shared configuration. 74 | # 75 | 76 | # Target executable names. 77 | set(CEF_TARGET "aviutl_browser") 78 | if(OS_MACOSX) 79 | set(CEF_HELPER_TARGET "aviutl_browser_Helper") 80 | set(CEF_HELPER_OUTPUT_NAME "aviutl_browser Helper") 81 | else() 82 | # Logical target used to link the libcef library. 83 | ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}") 84 | endif() 85 | 86 | # Determine the target output directory. 87 | SET_CEF_TARGET_OUT_DIR() 88 | 89 | 90 | # 91 | # Linux configuration. 92 | # 93 | 94 | if(OS_LINUX) 95 | # Executable target. 96 | add_executable(${CEF_TARGET} ${AVIUTLBROWSER_SRCS}) 97 | SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET}) 98 | add_dependencies(${CEF_TARGET} libcef_dll_wrapper) 99 | target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS}) 100 | 101 | # Set rpath so that libraries can be placed next to the executable. 102 | set_target_properties(${CEF_TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN") 103 | set_target_properties(${CEF_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) 104 | set_target_properties(${CEF_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR}) 105 | 106 | # Copy binary and resource files to the target output directory. 107 | COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}") 108 | COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}") 109 | if (EXISTS "${CEF_BINARY_DIR}/libminigbm.so") 110 | COPY_FILES("${CEF_TARGET}" "libminigbm.so" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}") 111 | endif() 112 | 113 | # Set SUID permissions on the chrome-sandbox target. 114 | SET_LINUX_SUID_PERMISSIONS("${CEF_TARGET}" "${CEF_TARGET_OUT_DIR}/chrome-sandbox") 115 | endif() 116 | 117 | 118 | # 119 | # Mac OS X configuration. 120 | # 121 | 122 | if(OS_MACOSX) 123 | option(OPTION_USE_ARC "Build with ARC (automatic Reference Counting) on macOS." ON) 124 | if(OPTION_USE_ARC) 125 | list(APPEND CEF_COMPILER_FLAGS 126 | -fobjc-arc 127 | ) 128 | set_target_properties(${target} PROPERTIES 129 | CLANG_ENABLE_OBJC_ARC "YES" 130 | ) 131 | endif() 132 | 133 | # Output path for the main app bundle. 134 | set(CEF_APP "${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.app") 135 | 136 | # Variables referenced from the main Info.plist file. 137 | set(EXECUTABLE_NAME "${CEF_TARGET}") 138 | set(PRODUCT_NAME "${CEF_TARGET}") 139 | 140 | if(USE_SANDBOX) 141 | # Logical target used to link the cef_sandbox library. 142 | ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}") 143 | endif() 144 | 145 | # Main app bundle target. 146 | add_executable(${CEF_TARGET} MACOSX_BUNDLE ${AVIUTLBROWSER_RESOURCES_SRCS} ${AVIUTLBROWSER_SRCS}) 147 | SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET}) 148 | add_dependencies(${CEF_TARGET} libcef_dll_wrapper) 149 | target_link_libraries(${CEF_TARGET} libcef_dll_wrapper ${CEF_STANDARD_LIBS}) 150 | set_target_properties(${CEF_TARGET} PROPERTIES 151 | MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist 152 | ) 153 | 154 | # Copy the CEF framework into the Frameworks directory. 155 | add_custom_command( 156 | TARGET ${CEF_TARGET} 157 | POST_BUILD 158 | COMMAND ${CMAKE_COMMAND} -E copy_directory 159 | "${CEF_BINARY_DIR}/Chromium Embedded Framework.framework" 160 | "${CEF_APP}/Contents/Frameworks/Chromium Embedded Framework.framework" 161 | VERBATIM 162 | ) 163 | 164 | # Create the multiple Helper app bundle targets. 165 | foreach(_suffix_list ${CEF_HELPER_APP_SUFFIXES}) 166 | # Convert to a list and extract the suffix values. 167 | string(REPLACE ":" ";" _suffix_list ${_suffix_list}) 168 | list(GET _suffix_list 0 _name_suffix) 169 | list(GET _suffix_list 1 _target_suffix) 170 | list(GET _suffix_list 2 _plist_suffix) 171 | 172 | # Define Helper target and output names. 173 | set(_helper_target "${CEF_HELPER_TARGET}${_target_suffix}") 174 | set(_helper_output_name "${CEF_HELPER_OUTPUT_NAME}${_name_suffix}") 175 | 176 | # Create Helper-specific variants of the helper-Info.plist file. Do this 177 | # manually because the configure_file command (which is executed as part of 178 | # MACOSX_BUNDLE_INFO_PLIST) uses global env variables and would insert the 179 | # wrong values with multiple targets. 180 | set(_helper_info_plist "${CMAKE_CURRENT_BINARY_DIR}/helper-Info${_target_suffix}.plist") 181 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/mac/helper-Info.plist" _plist_contents) 182 | string(REPLACE "\${EXECUTABLE_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents}) 183 | string(REPLACE "\${PRODUCT_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents}) 184 | string(REPLACE "\${BUNDLE_ID_SUFFIX}" "${_plist_suffix}" _plist_contents ${_plist_contents}) 185 | file(WRITE ${_helper_info_plist} ${_plist_contents}) 186 | 187 | # Create Helper executable target. 188 | add_executable(${_helper_target} MACOSX_BUNDLE ${AVIUTLBROWSER_HELPER_SRCS}) 189 | SET_EXECUTABLE_TARGET_PROPERTIES(${_helper_target}) 190 | add_dependencies(${_helper_target} libcef_dll_wrapper) 191 | target_link_libraries(${_helper_target} libcef_dll_wrapper ${CEF_STANDARD_LIBS}) 192 | set_target_properties(${_helper_target} PROPERTIES 193 | MACOSX_BUNDLE_INFO_PLIST ${_helper_info_plist} 194 | OUTPUT_NAME ${_helper_output_name} 195 | ) 196 | 197 | if(USE_SANDBOX) 198 | target_link_libraries(${_helper_target} cef_sandbox_lib) 199 | endif() 200 | 201 | # Add the Helper as a dependency of the main executable target. 202 | add_dependencies(${CEF_TARGET} "${_helper_target}") 203 | 204 | # Copy the Helper app bundle into the Frameworks directory. 205 | add_custom_command( 206 | TARGET ${CEF_TARGET} 207 | POST_BUILD 208 | COMMAND ${CMAKE_COMMAND} -E copy_directory 209 | "${CEF_TARGET_OUT_DIR}/${_helper_output_name}.app" 210 | "${CEF_APP}/Contents/Frameworks/${_helper_output_name}.app" 211 | VERBATIM 212 | ) 213 | endforeach() 214 | 215 | # Manually process and copy over resource files. 216 | # The Xcode generator can support this via the set_target_properties RESOURCE 217 | # directive but that doesn't properly handle nested resource directories. 218 | # Remove these prefixes from input file paths. 219 | set(PREFIXES "mac/") 220 | COPY_MACOSX_RESOURCES("${AVIUTLBROWSER_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}") 221 | endif() 222 | 223 | 224 | # 225 | # Windows configuration. 226 | # 227 | 228 | if(OS_WINDOWS) 229 | # Executable target. 230 | add_executable(${CEF_TARGET} WIN32 ${AVIUTLBROWSER_SRCS}) 231 | add_dependencies(${CEF_TARGET} libcef_dll_wrapper) 232 | SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET}) 233 | target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS}) 234 | 235 | if(USE_SANDBOX) 236 | # Logical target used to link the cef_sandbox library. 237 | ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}") 238 | target_link_libraries(${CEF_TARGET} cef_sandbox_lib ${CEF_SANDBOX_STANDARD_LIBS}) 239 | endif() 240 | 241 | # Add the custom manifest files to the executable. 242 | ADD_WINDOWS_MANIFEST("${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_TARGET}" "exe") 243 | 244 | # Copy binary and resource files to the target output directory. 245 | COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}") 246 | COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}") 247 | endif() 248 | 249 | # Generate version.h 250 | 251 | find_package(Git REQUIRED) 252 | execute_process( 253 | COMMAND ${GIT_EXECUTABLE} tag --points-at HEAD 254 | WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" 255 | OUTPUT_VARIABLE git_tag 256 | ERROR_QUIET 257 | OUTPUT_STRIP_TRAILING_WHITESPACE 258 | ) 259 | if ("${git_tag}" STREQUAL "") 260 | set(git_tag "vX.X.X") 261 | endif() 262 | execute_process( 263 | COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD 264 | WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" 265 | OUTPUT_VARIABLE git_revision 266 | ERROR_QUIET 267 | OUTPUT_STRIP_TRAILING_WHITESPACE 268 | ) 269 | if ("${git_revision}" STREQUAL "") 270 | set(git_revision "unknown") 271 | endif() 272 | add_custom_target(generate_version_h 273 | COMMAND ${CMAKE_COMMAND} 274 | -Dinput_file="${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" 275 | -Doutput_file="${CMAKE_CURRENT_BINARY_DIR}/version.h" 276 | -Dnewline="LF" 277 | -Dgit_tag="${git_tag}" 278 | -Dgit_revision="${git_revision}" 279 | -P "${CMAKE_CURRENT_SOURCE_DIR}/replace.cmake" 280 | ) 281 | add_dependencies(${CEF_TARGET} generate_version_h) 282 | target_include_directories(${CEF_TARGET} PRIVATE 283 | "${CMAKE_CURRENT_BINARY_DIR}" # for version.h 284 | ) 285 | -------------------------------------------------------------------------------- /src/exe/client.cc: -------------------------------------------------------------------------------- 1 | #include "client.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "include/cef_app.h" 8 | #include "include/views/cef_browser_view.h" 9 | #include "include/base/cef_callback.h" 10 | #include "include/base/cef_ref_counted.h" 11 | #include "include/wrapper/cef_closure_task.h" 12 | #include "include/wrapper/cef_helpers.h" 13 | #include "include/cef_parser.h" 14 | 15 | #include "depremultipliedalpha_table.h" 16 | 17 | Client::Client(CefRefPtr assets) 18 | : buf_(nullptr), 19 | resized_(true), 20 | loaded_(false), 21 | rendered_(true), 22 | capture_state_(0), 23 | cur_width_(1), 24 | cur_height_(1), 25 | callback_sequence_(0), 26 | waiting_sequence_(0), 27 | loading_(0), 28 | received_result_ok_(false), 29 | received_result_param_(""), 30 | received_result_ack_(0), 31 | assets_(assets) 32 | { 33 | } 34 | 35 | Client::~Client() { 36 | } 37 | 38 | bool Client::OnProcessMessageReceived(CefRefPtr browser, 39 | CefRefPtr frame, 40 | CefProcessId source_process, 41 | CefRefPtr message) { 42 | const std::string& message_name = message->GetName(); 43 | if (message_name == "rendered") { 44 | { 45 | std::lock_guard lk(mtx_); 46 | auto a = message->GetArgumentList(); 47 | if (a->GetInt(0) == waiting_sequence_) { 48 | rendered_ = true; 49 | received_result_ack_ = a->GetInt(1); 50 | received_result_ok_ = a->GetBool(2); 51 | received_result_param_ = a->GetString(3); 52 | cv_.notify_all(); 53 | } 54 | } 55 | return true; 56 | } 57 | return false; 58 | } 59 | 60 | bool Client::OnBeforePopup(CefRefPtr browser, 61 | CefRefPtr frame, 62 | const CefString& target_url, 63 | const CefString& target_frame_name, 64 | CefLifeSpanHandler::WindowOpenDisposition target_disposition, 65 | bool user_gesture, 66 | const CefPopupFeatures& popupFeatures, 67 | CefWindowInfo& windowInfo, 68 | CefRefPtr& client, 69 | CefBrowserSettings& settings, 70 | CefRefPtr& extra_info, 71 | bool* no_javascript_access) { 72 | return true; 73 | } 74 | 75 | void Client::OnAfterCreated(CefRefPtr browser) { 76 | CEF_REQUIRE_UI_THREAD(); 77 | browser_list_.push_back(browser); 78 | } 79 | 80 | bool Client::DoClose(CefRefPtr browser) { 81 | CEF_REQUIRE_UI_THREAD(); 82 | return false; 83 | } 84 | 85 | void Client::OnBeforeClose(CefRefPtr browser) { 86 | CEF_REQUIRE_UI_THREAD(); 87 | for (BrowserList::iterator bit = browser_list_.begin(); bit != browser_list_.end(); ++bit) { 88 | if ((*bit)->IsSame(browser)) { 89 | browser_list_.erase(bit); 90 | break; 91 | } 92 | } 93 | } 94 | 95 | void Client::OnLoadingStateChange(CefRefPtr browser, 96 | bool isLoading, 97 | bool canGoBack, 98 | bool canGoForward) { 99 | if (isLoading) { 100 | InterlockedIncrement(&loading_); 101 | } 102 | else { 103 | if (InterlockedDecrement(&loading_) == 0) { 104 | std::lock_guard lk(mtx_); 105 | loaded_ = true; 106 | cv_.notify_all(); 107 | } 108 | } 109 | } 110 | 111 | std::string GetDataURI(const std::string& data, const std::string& mime_type) { 112 | return "data:" + mime_type + ";base64," + CefURIEncode(CefBase64Encode(data.data(), data.size()), false).ToString(); 113 | } 114 | 115 | void Client::OnLoadError(CefRefPtr browser, 116 | CefRefPtr frame, 117 | ErrorCode errorCode, 118 | const CefString& errorText, 119 | const CefString& failedUrl) { 120 | CEF_REQUIRE_UI_THREAD(); 121 | if (errorCode == ERR_ABORTED) { 122 | return; 123 | } 124 | std::stringstream ss; 125 | ss << "" 126 | "

Failed to load URL " 127 | << std::string(failedUrl) << " with error " << std::string(errorText) 128 | << " (" << errorCode << ").

"; 129 | frame->LoadURL(GetDataURI(ss.str(), "text/html")); 130 | } 131 | 132 | void Client::GetViewRect(CefRefPtr browser, CefRect& rect) { 133 | std::lock_guard lk(mtx_); 134 | rect.Set(0, 0, cur_width_, cur_height_); 135 | resized_ = true; 136 | cv_.notify_all(); 137 | } 138 | 139 | void Client::OnPaint(CefRefPtr browser, 140 | PaintElementType type, 141 | const RectList& dirtyRects, 142 | const void* buffer, 143 | int width, 144 | int height) { 145 | if (type != PET_VIEW || width == 0 || height == 0 || cur_width_ != width || cur_height_ != height) { 146 | return; 147 | } 148 | 149 | std::lock_guard lk(mtx_); 150 | if (capture_state_ != 0) { 151 | return; 152 | } 153 | if (!buf_) { 154 | capture_state_ = 3; 155 | cv_.notify_all(); 156 | return; 157 | } 158 | uint32_t ack = received_result_ack_, pixel = *((uint32_t*)buffer); 159 | if (ack) { 160 | if ( 161 | (pixel & 0xff000000) != 0xff000000 || 162 | ( 163 | (ack & 0x00ff0000 && ((pixel >> 16) & 0xff) <= 0x7f) || 164 | (!(ack & 0x00ff0000) && ((pixel >> 16) & 0xff) > 0x7f) 165 | ) || 166 | ( 167 | (ack & 0x0000ff00 && ((pixel >> 8) & 0xff) <= 0x7f) || 168 | (!(ack & 0x0000ff00) && ((pixel >> 8) & 0xff) > 0x7f) 169 | ) || 170 | ( 171 | (ack & 0x000000ff && (pixel & 0xff) <= 0x7f) || 172 | (!(ack & 0x000000ff) && (pixel & 0xff) > 0x7f) 173 | ) 174 | ) { 175 | capture_state_ = 2; 176 | cv_.notify_all(); 177 | return; 178 | } 179 | } 180 | 181 | int sw = width * 4, sh = height; 182 | const uint8_t* sl = (const uint8_t*)buffer; 183 | int dw = cur_width_ * 4, dh = cur_height_; 184 | uint8_t* dl = (uint8_t*)buf_; 185 | int w = std::min(sw, dw), h = std::min(sh, dh); 186 | for (int y = 0; y < h; ++y, sl += sw, dl += dw) { 187 | for (int x = 0; x < w; x += 4) { 188 | // const uint_fast16_t a = sl[x + 3]; 189 | const uint_fast16_t a = (uint_fast16_t)(sl[x + 3]) << 8; 190 | if (a > 0) { 191 | dl[x + 0] = dePremultipliedAlphaTable[a + (uint_fast16_t)(sl[x + 0])]; 192 | dl[x + 1] = dePremultipliedAlphaTable[a + (uint_fast16_t)(sl[x + 1])]; 193 | dl[x + 2] = dePremultipliedAlphaTable[a + (uint_fast16_t)(sl[x + 2])]; 194 | /* 195 | dl[x + 0] = (uint8_t)((uint_fast16_t)(sl[x + 0]) * 255 / a); 196 | dl[x + 1] = (uint8_t)((uint_fast16_t)(sl[x + 1]) * 255 / a); 197 | dl[x + 2] = (uint8_t)((uint_fast16_t)(sl[x + 2]) * 255 / a); 198 | */ 199 | } 200 | dl[x + 3] = sl[x + 3]; 201 | } 202 | } 203 | dl = (uint8_t*)buf_; 204 | dl[0] = 0; 205 | dl[1] = 0; 206 | dl[2] = 0; 207 | dl[3] = 0; 208 | capture_state_ = 1; 209 | cv_.notify_all(); 210 | } 211 | 212 | CefRefPtr Client::GetResourceHandler( 213 | CefRefPtr browser, 214 | CefRefPtr frame, 215 | CefRefPtr request) { 216 | CefURLParts u; 217 | if (!CefParseURL(request->GetURL(), u)) { 218 | return nullptr; 219 | } 220 | if (CefString(&u.scheme) == L"https" && CefString(&u.host) == L"assets.example") { 221 | auto path = CefString(&u.path); 222 | if (path == L"/userfile") { 223 | if (userfile_.empty()) { 224 | return StreamResourceHandler::CreateError(404, L"file is not provided"); 225 | } 226 | return GetUserFile(); 227 | } 228 | return (*assets_)(path); 229 | } 230 | return nullptr; 231 | } 232 | 233 | void Client::Capture(void* b, const int width, const int height) { 234 | std::unique_lock lk(mtx_); 235 | buf_ = b; 236 | do { 237 | capture_state_ = 0; 238 | cv_.wait(lk, [&] { 239 | return capture_state_ != 0; 240 | }); 241 | } while (capture_state_ == 2); 242 | } 243 | 244 | void Client::Wait() { 245 | std::unique_lock lk(mtx_); 246 | cv_.wait(lk, [&] { 247 | return loaded_ == true; 248 | }); 249 | } 250 | 251 | void Client::Resize(const int width, const int height) { 252 | std::unique_lock lk(mtx_); 253 | if (cur_width_ == width && cur_height_ == height) { 254 | return; 255 | } 256 | resized_ = false; 257 | capture_state_ = 0; 258 | cur_width_ = width; 259 | cur_height_ = height; 260 | buf_ = nullptr; 261 | 262 | CefPostTask(TID_UI, base::BindOnce(&CefBrowserHost::WasResized, browser_list_.front()->GetHost())); 263 | cv_.wait(lk, [&] { 264 | return resized_ == true && capture_state_ == 3; 265 | }); 266 | } 267 | 268 | static uint64_t cyrb64(const uint32_t* src, const size_t len, const uint32_t seed) 269 | { 270 | uint32_t h1 = 0x91eb9dc7 ^ seed, h2 = 0x41c6ce57 ^ seed; 271 | for (size_t i = 0; i < len; ++i) 272 | { 273 | h1 = (h1 ^ src[i]) * 2654435761; 274 | h2 = (h2 ^ src[i]) * 1597334677; 275 | } 276 | h1 = ((h1 ^ (h1 >> 16)) * 2246822507) ^ ((h2 ^ (h2 >> 13)) * 3266489909); 277 | h2 = ((h2 ^ (h2 >> 16)) * 2246822507) ^ ((h1 ^ (h1 >> 13)) * 3266489909); 278 | return (((uint64_t)h2) << 32) | ((uint64_t)h1); 279 | } 280 | 281 | static void to_hex(wchar_t* dst, uint64_t x) 282 | { 283 | const char* chars = "0123456789abcdef"; 284 | for (int i = 15; i >= 0; --i) 285 | { 286 | dst[i] = chars[x & 0xf]; 287 | x >>= 4; 288 | } 289 | } 290 | 291 | void Client::SetUserFile(const std::wstring& userfile) { 292 | std::unique_lock lk(mtx_); 293 | if (userfile_ == userfile) { 294 | return; 295 | } 296 | userfile_ = userfile; 297 | if (userfile_.empty()) { 298 | userfile_hash_.clear(); 299 | return; 300 | } 301 | std::string buf; 302 | const size_t l = userfile_.size() * sizeof(wchar_t); 303 | buf.resize((l + 3) & ~3); 304 | memcpy(&buf[0], &userfile_[0], l); 305 | userfile_hash_.resize(16); 306 | to_hex(&userfile_hash_[0], cyrb64(reinterpret_cast(&buf[0]), buf.size() / sizeof(uint32_t), 0x15544521)); 307 | } 308 | 309 | void Client::SetUseDevTools(const bool use_devtools) { 310 | std::unique_lock lk(mtx_); 311 | if ((use_devtools && devtools_) || (!use_devtools && !devtools_)) { 312 | return; 313 | } 314 | if (!CefCurrentlyOn(TID_UI)) { 315 | CefPostTask(TID_UI, base::BindOnce(&Client::SetUseDevTools, this, use_devtools)); 316 | return; 317 | } 318 | CefRefPtr browser = browser_list_.front(); 319 | if (use_devtools) { 320 | InitializeDevTools(browser); 321 | } 322 | else { 323 | devtools_->do_not_close = false; 324 | browser->GetHost()->CloseDevTools(); 325 | devtools_ = nullptr; 326 | } 327 | } 328 | 329 | bool Client::RenderAndCapture(const std::wstring& inbuf, std::wstring& outbuf, void* image, const int width, const int height) { 330 | Resize(width, height); 331 | Render(inbuf, outbuf, 30000); 332 | Capture(image, width, height); 333 | return true; 334 | } 335 | 336 | std::wstring buildCallbackCallCode(const std::wstring& param, const std::wstring& userfile_hash, const int sequence) { 337 | std::wostringstream o; 338 | o << L"AviUtlBrowser.render("; 339 | o << sequence; 340 | o << L",\""; 341 | for (auto c = param.cbegin(); c != param.cend(); c++) { 342 | switch (*c) { 343 | case L'\x00': o << L"\\x00"; break; 344 | case L'\x01': o << L"\\x01"; break; 345 | case L'\x02': o << L"\\x02"; break; 346 | case L'\x03': o << L"\\x03"; break; 347 | case L'\x04': o << L"\\x04"; break; 348 | case L'\x05': o << L"\\x05"; break; 349 | case L'\x06': o << L"\\x06"; break; 350 | case L'\x07': o << L"\\x07"; break; 351 | case L'\x08': o << L"\\b"; break; 352 | case L'\x09': o << L"\\t"; break; 353 | case L'\x0a': o << L"\\n"; break; 354 | case L'\x0b': o << L"\\x0b"; break; 355 | case L'\x0c': o << L"\\f"; break; 356 | case L'\x0d': o << L"\\r"; break; 357 | case L'\x0e': o << L"\\x0e"; break; 358 | case L'\x0f': o << L"\\x0f"; break; 359 | case L'\x10': o << L"\\x10"; break; 360 | case L'\x11': o << L"\\x11"; break; 361 | case L'\x12': o << L"\\x12"; break; 362 | case L'\x13': o << L"\\x13"; break; 363 | case L'\x14': o << L"\\x14"; break; 364 | case L'\x15': o << L"\\x15"; break; 365 | case L'\x16': o << L"\\x16"; break; 366 | case L'\x17': o << L"\\x17"; break; 367 | case L'\x18': o << L"\\x18"; break; 368 | case L'\x19': o << L"\\x19"; break; 369 | case L'\x1a': o << L"\\x1a"; break; 370 | case L'\x1b': o << L"\\x1b"; break; 371 | case L'\x1c': o << L"\\x1c"; break; 372 | case L'\x1d': o << L"\\x1d"; break; 373 | case L'\x1e': o << L"\\x1e"; break; 374 | case L'\x1f': o << L"\\x1f"; break; 375 | case L'\x22': o << L"\\\""; break; 376 | case L'\x5c': o << L"\\\\"; break; 377 | default: o << *c; 378 | } 379 | } 380 | o << L"\","; 381 | if (userfile_hash.empty()) { 382 | o << L"null"; 383 | } 384 | else { 385 | o << L"\""; 386 | o << userfile_hash; 387 | o << L"\""; 388 | } 389 | o << L")"; 390 | return o.str(); 391 | } 392 | 393 | bool Client::Render(const std::wstring& inbuf, std::wstring& outbuf, int timeout) { 394 | std::unique_lock lk(mtx_); 395 | rendered_ = false; 396 | waiting_sequence_ = ++callback_sequence_; 397 | { 398 | auto f = browser_list_.front()->GetMainFrame(); 399 | f->ExecuteJavaScript(buildCallbackCallCode(inbuf, userfile_hash_, waiting_sequence_), f->GetURL(), 0); 400 | } 401 | if (!cv_.wait_for(lk, std::chrono::milliseconds(timeout), [&] { 402 | return rendered_; 403 | })) { 404 | outbuf = L"timeout"; 405 | return false; 406 | } 407 | outbuf = received_result_param_; 408 | return received_result_ok_; 409 | } 410 | 411 | void Client::CloseAllBrowsers(bool force_close) { 412 | if (!CefCurrentlyOn(TID_UI)) { 413 | CefPostTask(TID_UI, base::BindOnce(&Client::CloseAllBrowsers, this, force_close)); 414 | return; 415 | } 416 | 417 | std::unique_lock lk(mtx_); 418 | for (auto it = browser_list_.cbegin(); it != browser_list_.cend(); ++it) { 419 | (*it)->GetHost()->CloseBrowser(force_close); 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /src/exe/picojson.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009-2010 Cybozu Labs, Inc. 3 | * Copyright 2011-2014 Kazuho Oku 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | #ifndef picojson_h 29 | #define picojson_h 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | // for isnan/isinf 46 | #if __cplusplus >= 201103L 47 | #include 48 | #else 49 | extern "C" { 50 | #ifdef _MSC_VER 51 | #include 52 | #elif defined(__INTEL_COMPILER) 53 | #include 54 | #else 55 | #include 56 | #endif 57 | } 58 | #endif 59 | 60 | #ifndef PICOJSON_USE_RVALUE_REFERENCE 61 | #if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600) 62 | #define PICOJSON_USE_RVALUE_REFERENCE 1 63 | #else 64 | #define PICOJSON_USE_RVALUE_REFERENCE 0 65 | #endif 66 | #endif // PICOJSON_USE_RVALUE_REFERENCE 67 | 68 | #ifndef PICOJSON_NOEXCEPT 69 | #if PICOJSON_USE_RVALUE_REFERENCE 70 | #define PICOJSON_NOEXCEPT noexcept 71 | #else 72 | #define PICOJSON_NOEXCEPT throw() 73 | #endif 74 | #endif 75 | 76 | // experimental support for int64_t (see README.mkdn for detail) 77 | #ifdef PICOJSON_USE_INT64 78 | #define __STDC_FORMAT_MACROS 79 | #include 80 | #if __cplusplus >= 201103L 81 | #include 82 | #else 83 | extern "C" { 84 | #include 85 | } 86 | #endif 87 | #endif 88 | 89 | // to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 90 | #ifndef PICOJSON_USE_LOCALE 91 | #define PICOJSON_USE_LOCALE 1 92 | #endif 93 | #if PICOJSON_USE_LOCALE 94 | extern "C" { 95 | #include 96 | } 97 | #endif 98 | 99 | #ifndef PICOJSON_ASSERT 100 | #define PICOJSON_ASSERT(e) \ 101 | do { \ 102 | if (!(e)) \ 103 | throw std::runtime_error(#e); \ 104 | } while (0) 105 | #endif 106 | 107 | #ifdef _MSC_VER 108 | #define SNPRINTF _snprintf_s 109 | #pragma warning(push) 110 | #pragma warning(disable : 4244) // conversion from int to char 111 | #pragma warning(disable : 4127) // conditional expression is constant 112 | #pragma warning(disable : 4702) // unreachable code 113 | #pragma warning(disable : 4706) // assignment within conditional expression 114 | #else 115 | #define SNPRINTF snprintf 116 | #endif 117 | 118 | namespace picojson { 119 | 120 | enum { 121 | null_type, 122 | boolean_type, 123 | number_type, 124 | string_type, 125 | array_type, 126 | object_type 127 | #ifdef PICOJSON_USE_INT64 128 | , 129 | int64_type 130 | #endif 131 | }; 132 | 133 | enum { INDENT_WIDTH = 2, DEFAULT_MAX_DEPTHS = 100 }; 134 | 135 | struct null {}; 136 | 137 | class value { 138 | public: 139 | typedef std::vector array; 140 | typedef std::map object; 141 | union _storage { 142 | bool boolean_; 143 | double number_; 144 | #ifdef PICOJSON_USE_INT64 145 | int64_t int64_; 146 | #endif 147 | std::string *string_; 148 | array *array_; 149 | object *object_; 150 | }; 151 | 152 | protected: 153 | int type_; 154 | _storage u_; 155 | 156 | public: 157 | value(); 158 | value(int type, bool); 159 | explicit value(bool b); 160 | #ifdef PICOJSON_USE_INT64 161 | explicit value(int64_t i); 162 | #endif 163 | explicit value(double n); 164 | explicit value(const std::string &s); 165 | explicit value(const array &a); 166 | explicit value(const object &o); 167 | #if PICOJSON_USE_RVALUE_REFERENCE 168 | explicit value(std::string &&s); 169 | explicit value(array &&a); 170 | explicit value(object &&o); 171 | #endif 172 | explicit value(const char *s); 173 | value(const char *s, size_t len); 174 | ~value(); 175 | value(const value &x); 176 | value &operator=(const value &x); 177 | #if PICOJSON_USE_RVALUE_REFERENCE 178 | value(value &&x) PICOJSON_NOEXCEPT; 179 | value &operator=(value &&x) PICOJSON_NOEXCEPT; 180 | #endif 181 | void swap(value &x) PICOJSON_NOEXCEPT; 182 | template bool is() const; 183 | template const T &get() const; 184 | template T &get(); 185 | template void set(const T &); 186 | #if PICOJSON_USE_RVALUE_REFERENCE 187 | template void set(T &&); 188 | #endif 189 | bool evaluate_as_boolean() const; 190 | const value &get(const size_t idx) const; 191 | const value &get(const std::string &key) const; 192 | value &get(const size_t idx); 193 | value &get(const std::string &key); 194 | 195 | bool contains(const size_t idx) const; 196 | bool contains(const std::string &key) const; 197 | std::string to_str() const; 198 | template void serialize(Iter os, bool prettify = false) const; 199 | std::string serialize(bool prettify = false) const; 200 | 201 | private: 202 | template value(const T *); // intentionally defined to block implicit conversion of pointer to bool 203 | template static void _indent(Iter os, int indent); 204 | template void _serialize(Iter os, int indent) const; 205 | std::string _serialize(int indent) const; 206 | void clear(); 207 | }; 208 | 209 | typedef value::array array; 210 | typedef value::object object; 211 | 212 | inline value::value() : type_(null_type), u_() { 213 | } 214 | 215 | inline value::value(int type, bool) : type_(type), u_() { 216 | switch (type) { 217 | #define INIT(p, v) \ 218 | case p##type: \ 219 | u_.p = v; \ 220 | break 221 | INIT(boolean_, false); 222 | INIT(number_, 0.0); 223 | #ifdef PICOJSON_USE_INT64 224 | INIT(int64_, 0); 225 | #endif 226 | INIT(string_, new std::string()); 227 | INIT(array_, new array()); 228 | INIT(object_, new object()); 229 | #undef INIT 230 | default: 231 | break; 232 | } 233 | } 234 | 235 | inline value::value(bool b) : type_(boolean_type), u_() { 236 | u_.boolean_ = b; 237 | } 238 | 239 | #ifdef PICOJSON_USE_INT64 240 | inline value::value(int64_t i) : type_(int64_type), u_() { 241 | u_.int64_ = i; 242 | } 243 | #endif 244 | 245 | inline value::value(double n) : type_(number_type), u_() { 246 | if ( 247 | #ifdef _MSC_VER 248 | !_finite(n) 249 | #elif __cplusplus >= 201103L 250 | std::isnan(n) || std::isinf(n) 251 | #else 252 | isnan(n) || isinf(n) 253 | #endif 254 | ) { 255 | throw std::overflow_error(""); 256 | } 257 | u_.number_ = n; 258 | } 259 | 260 | inline value::value(const std::string &s) : type_(string_type), u_() { 261 | u_.string_ = new std::string(s); 262 | } 263 | 264 | inline value::value(const array &a) : type_(array_type), u_() { 265 | u_.array_ = new array(a); 266 | } 267 | 268 | inline value::value(const object &o) : type_(object_type), u_() { 269 | u_.object_ = new object(o); 270 | } 271 | 272 | #if PICOJSON_USE_RVALUE_REFERENCE 273 | inline value::value(std::string &&s) : type_(string_type), u_() { 274 | u_.string_ = new std::string(std::move(s)); 275 | } 276 | 277 | inline value::value(array &&a) : type_(array_type), u_() { 278 | u_.array_ = new array(std::move(a)); 279 | } 280 | 281 | inline value::value(object &&o) : type_(object_type), u_() { 282 | u_.object_ = new object(std::move(o)); 283 | } 284 | #endif 285 | 286 | inline value::value(const char *s) : type_(string_type), u_() { 287 | u_.string_ = new std::string(s); 288 | } 289 | 290 | inline value::value(const char *s, size_t len) : type_(string_type), u_() { 291 | u_.string_ = new std::string(s, len); 292 | } 293 | 294 | inline void value::clear() { 295 | switch (type_) { 296 | #define DEINIT(p) \ 297 | case p##type: \ 298 | delete u_.p; \ 299 | break 300 | DEINIT(string_); 301 | DEINIT(array_); 302 | DEINIT(object_); 303 | #undef DEINIT 304 | default: 305 | break; 306 | } 307 | } 308 | 309 | inline value::~value() { 310 | clear(); 311 | } 312 | 313 | inline value::value(const value &x) : type_(x.type_), u_() { 314 | switch (type_) { 315 | #define INIT(p, v) \ 316 | case p##type: \ 317 | u_.p = v; \ 318 | break 319 | INIT(string_, new std::string(*x.u_.string_)); 320 | INIT(array_, new array(*x.u_.array_)); 321 | INIT(object_, new object(*x.u_.object_)); 322 | #undef INIT 323 | default: 324 | u_ = x.u_; 325 | break; 326 | } 327 | } 328 | 329 | inline value &value::operator=(const value &x) { 330 | if (this != &x) { 331 | value t(x); 332 | swap(t); 333 | } 334 | return *this; 335 | } 336 | 337 | #if PICOJSON_USE_RVALUE_REFERENCE 338 | inline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() { 339 | swap(x); 340 | } 341 | inline value &value::operator=(value &&x) PICOJSON_NOEXCEPT { 342 | swap(x); 343 | return *this; 344 | } 345 | #endif 346 | inline void value::swap(value &x) PICOJSON_NOEXCEPT { 347 | std::swap(type_, x.type_); 348 | std::swap(u_, x.u_); 349 | } 350 | 351 | #define IS(ctype, jtype) \ 352 | template <> inline bool value::is() const { \ 353 | return type_ == jtype##_type; \ 354 | } 355 | IS(null, null) 356 | IS(bool, boolean) 357 | #ifdef PICOJSON_USE_INT64 358 | IS(int64_t, int64) 359 | #endif 360 | IS(std::string, string) 361 | IS(array, array) 362 | IS(object, object) 363 | #undef IS 364 | template <> inline bool value::is() const { 365 | return type_ == number_type 366 | #ifdef PICOJSON_USE_INT64 367 | || type_ == int64_type 368 | #endif 369 | ; 370 | } 371 | 372 | #define GET(ctype, var) \ 373 | template <> inline const ctype &value::get() const { \ 374 | PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ 375 | return var; \ 376 | } \ 377 | template <> inline ctype &value::get() { \ 378 | PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ 379 | return var; \ 380 | } 381 | GET(bool, u_.boolean_) 382 | GET(std::string, *u_.string_) 383 | GET(array, *u_.array_) 384 | GET(object, *u_.object_) 385 | #ifdef PICOJSON_USE_INT64 386 | GET(double, 387 | (type_ == int64_type && (const_cast(this)->type_ = number_type, (const_cast(this)->u_.number_ = u_.int64_)), 388 | u_.number_)) 389 | GET(int64_t, u_.int64_) 390 | #else 391 | GET(double, u_.number_) 392 | #endif 393 | #undef GET 394 | 395 | #define SET(ctype, jtype, setter) \ 396 | template <> inline void value::set(const ctype &_val) { \ 397 | clear(); \ 398 | type_ = jtype##_type; \ 399 | setter \ 400 | } 401 | SET(bool, boolean, u_.boolean_ = _val;) 402 | SET(std::string, string, u_.string_ = new std::string(_val);) 403 | SET(array, array, u_.array_ = new array(_val);) 404 | SET(object, object, u_.object_ = new object(_val);) 405 | SET(double, number, u_.number_ = _val;) 406 | #ifdef PICOJSON_USE_INT64 407 | SET(int64_t, int64, u_.int64_ = _val;) 408 | #endif 409 | #undef SET 410 | 411 | #if PICOJSON_USE_RVALUE_REFERENCE 412 | #define MOVESET(ctype, jtype, setter) \ 413 | template <> inline void value::set(ctype && _val) { \ 414 | clear(); \ 415 | type_ = jtype##_type; \ 416 | setter \ 417 | } 418 | MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));) 419 | MOVESET(array, array, u_.array_ = new array(std::move(_val));) 420 | MOVESET(object, object, u_.object_ = new object(std::move(_val));) 421 | #undef MOVESET 422 | #endif 423 | 424 | inline bool value::evaluate_as_boolean() const { 425 | switch (type_) { 426 | case null_type: 427 | return false; 428 | case boolean_type: 429 | return u_.boolean_; 430 | case number_type: 431 | return u_.number_ != 0; 432 | #ifdef PICOJSON_USE_INT64 433 | case int64_type: 434 | return u_.int64_ != 0; 435 | #endif 436 | case string_type: 437 | return !u_.string_->empty(); 438 | default: 439 | return true; 440 | } 441 | } 442 | 443 | inline const value &value::get(const size_t idx) const { 444 | static value s_null; 445 | PICOJSON_ASSERT(is()); 446 | return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; 447 | } 448 | 449 | inline value &value::get(const size_t idx) { 450 | static value s_null; 451 | PICOJSON_ASSERT(is()); 452 | return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; 453 | } 454 | 455 | inline const value &value::get(const std::string &key) const { 456 | static value s_null; 457 | PICOJSON_ASSERT(is()); 458 | object::const_iterator i = u_.object_->find(key); 459 | return i != u_.object_->end() ? i->second : s_null; 460 | } 461 | 462 | inline value &value::get(const std::string &key) { 463 | static value s_null; 464 | PICOJSON_ASSERT(is()); 465 | object::iterator i = u_.object_->find(key); 466 | return i != u_.object_->end() ? i->second : s_null; 467 | } 468 | 469 | inline bool value::contains(const size_t idx) const { 470 | PICOJSON_ASSERT(is()); 471 | return idx < u_.array_->size(); 472 | } 473 | 474 | inline bool value::contains(const std::string &key) const { 475 | PICOJSON_ASSERT(is()); 476 | object::const_iterator i = u_.object_->find(key); 477 | return i != u_.object_->end(); 478 | } 479 | 480 | inline std::string value::to_str() const { 481 | switch (type_) { 482 | case null_type: 483 | return "null"; 484 | case boolean_type: 485 | return u_.boolean_ ? "true" : "false"; 486 | #ifdef PICOJSON_USE_INT64 487 | case int64_type: { 488 | char buf[sizeof("-9223372036854775808")]; 489 | SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); 490 | return buf; 491 | } 492 | #endif 493 | case number_type: { 494 | char buf[256]; 495 | double tmp; 496 | SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); 497 | #if PICOJSON_USE_LOCALE 498 | char *decimal_point = localeconv()->decimal_point; 499 | if (strcmp(decimal_point, ".") != 0) { 500 | size_t decimal_point_len = strlen(decimal_point); 501 | for (char *p = buf; *p != '\0'; ++p) { 502 | if (strncmp(p, decimal_point, decimal_point_len) == 0) { 503 | return std::string(buf, p) + "." + (p + decimal_point_len); 504 | } 505 | } 506 | } 507 | #endif 508 | return buf; 509 | } 510 | case string_type: 511 | return *u_.string_; 512 | case array_type: 513 | return "array"; 514 | case object_type: 515 | return "object"; 516 | default: 517 | PICOJSON_ASSERT(0); 518 | #ifdef _MSC_VER 519 | __assume(0); 520 | #endif 521 | } 522 | return std::string(); 523 | } 524 | 525 | template void copy(const std::string &s, Iter oi) { 526 | std::copy(s.begin(), s.end(), oi); 527 | } 528 | 529 | template struct serialize_str_char { 530 | Iter oi; 531 | void operator()(char c) { 532 | switch (c) { 533 | #define MAP(val, sym) \ 534 | case val: \ 535 | copy(sym, oi); \ 536 | break 537 | MAP('"', "\\\""); 538 | MAP('\\', "\\\\"); 539 | MAP('/', "\\/"); 540 | MAP('\b', "\\b"); 541 | MAP('\f', "\\f"); 542 | MAP('\n', "\\n"); 543 | MAP('\r', "\\r"); 544 | MAP('\t', "\\t"); 545 | #undef MAP 546 | default: 547 | if (static_cast(c) < 0x20 || c == 0x7f) { 548 | char buf[7]; 549 | SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff); 550 | copy(buf, buf + 6, oi); 551 | } else { 552 | *oi++ = c; 553 | } 554 | break; 555 | } 556 | } 557 | }; 558 | 559 | template void serialize_str(const std::string &s, Iter oi) { 560 | *oi++ = '"'; 561 | serialize_str_char process_char = {oi}; 562 | std::for_each(s.begin(), s.end(), process_char); 563 | *oi++ = '"'; 564 | } 565 | 566 | template void value::serialize(Iter oi, bool prettify) const { 567 | return _serialize(oi, prettify ? 0 : -1); 568 | } 569 | 570 | inline std::string value::serialize(bool prettify) const { 571 | return _serialize(prettify ? 0 : -1); 572 | } 573 | 574 | template void value::_indent(Iter oi, int indent) { 575 | *oi++ = '\n'; 576 | for (int i = 0; i < indent * INDENT_WIDTH; ++i) { 577 | *oi++ = ' '; 578 | } 579 | } 580 | 581 | template void value::_serialize(Iter oi, int indent) const { 582 | switch (type_) { 583 | case string_type: 584 | serialize_str(*u_.string_, oi); 585 | break; 586 | case array_type: { 587 | *oi++ = '['; 588 | if (indent != -1) { 589 | ++indent; 590 | } 591 | for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { 592 | if (i != u_.array_->begin()) { 593 | *oi++ = ','; 594 | } 595 | if (indent != -1) { 596 | _indent(oi, indent); 597 | } 598 | i->_serialize(oi, indent); 599 | } 600 | if (indent != -1) { 601 | --indent; 602 | if (!u_.array_->empty()) { 603 | _indent(oi, indent); 604 | } 605 | } 606 | *oi++ = ']'; 607 | break; 608 | } 609 | case object_type: { 610 | *oi++ = '{'; 611 | if (indent != -1) { 612 | ++indent; 613 | } 614 | for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { 615 | if (i != u_.object_->begin()) { 616 | *oi++ = ','; 617 | } 618 | if (indent != -1) { 619 | _indent(oi, indent); 620 | } 621 | serialize_str(i->first, oi); 622 | *oi++ = ':'; 623 | if (indent != -1) { 624 | *oi++ = ' '; 625 | } 626 | i->second._serialize(oi, indent); 627 | } 628 | if (indent != -1) { 629 | --indent; 630 | if (!u_.object_->empty()) { 631 | _indent(oi, indent); 632 | } 633 | } 634 | *oi++ = '}'; 635 | break; 636 | } 637 | default: 638 | copy(to_str(), oi); 639 | break; 640 | } 641 | if (indent == 0) { 642 | *oi++ = '\n'; 643 | } 644 | } 645 | 646 | inline std::string value::_serialize(int indent) const { 647 | std::string s; 648 | _serialize(std::back_inserter(s), indent); 649 | return s; 650 | } 651 | 652 | template class input { 653 | protected: 654 | Iter cur_, end_; 655 | bool consumed_; 656 | int line_; 657 | 658 | public: 659 | input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) { 660 | } 661 | int getc() { 662 | if (consumed_) { 663 | if (*cur_ == '\n') { 664 | ++line_; 665 | } 666 | ++cur_; 667 | } 668 | if (cur_ == end_) { 669 | consumed_ = false; 670 | return -1; 671 | } 672 | consumed_ = true; 673 | return *cur_ & 0xff; 674 | } 675 | void ungetc() { 676 | consumed_ = false; 677 | } 678 | Iter cur() const { 679 | if (consumed_) { 680 | input *self = const_cast *>(this); 681 | self->consumed_ = false; 682 | ++self->cur_; 683 | } 684 | return cur_; 685 | } 686 | int line() const { 687 | return line_; 688 | } 689 | void skip_ws() { 690 | while (1) { 691 | int ch = getc(); 692 | if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { 693 | ungetc(); 694 | break; 695 | } 696 | } 697 | } 698 | bool expect(const int expected) { 699 | skip_ws(); 700 | if (getc() != expected) { 701 | ungetc(); 702 | return false; 703 | } 704 | return true; 705 | } 706 | bool match(const std::string &pattern) { 707 | for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { 708 | if (getc() != *pi) { 709 | ungetc(); 710 | return false; 711 | } 712 | } 713 | return true; 714 | } 715 | }; 716 | 717 | template inline int _parse_quadhex(input &in) { 718 | int uni_ch = 0, hex; 719 | for (int i = 0; i < 4; i++) { 720 | if ((hex = in.getc()) == -1) { 721 | return -1; 722 | } 723 | if ('0' <= hex && hex <= '9') { 724 | hex -= '0'; 725 | } else if ('A' <= hex && hex <= 'F') { 726 | hex -= 'A' - 0xa; 727 | } else if ('a' <= hex && hex <= 'f') { 728 | hex -= 'a' - 0xa; 729 | } else { 730 | in.ungetc(); 731 | return -1; 732 | } 733 | uni_ch = uni_ch * 16 + hex; 734 | } 735 | return uni_ch; 736 | } 737 | 738 | template inline bool _parse_codepoint(String &out, input &in) { 739 | int uni_ch; 740 | if ((uni_ch = _parse_quadhex(in)) == -1) { 741 | return false; 742 | } 743 | if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { 744 | if (0xdc00 <= uni_ch) { 745 | // a second 16-bit of a surrogate pair appeared 746 | return false; 747 | } 748 | // first 16-bit of surrogate pair, get the next one 749 | if (in.getc() != '\\' || in.getc() != 'u') { 750 | in.ungetc(); 751 | return false; 752 | } 753 | int second = _parse_quadhex(in); 754 | if (!(0xdc00 <= second && second <= 0xdfff)) { 755 | return false; 756 | } 757 | uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); 758 | uni_ch += 0x10000; 759 | } 760 | if (uni_ch < 0x80) { 761 | out.push_back(static_cast(uni_ch)); 762 | } else { 763 | if (uni_ch < 0x800) { 764 | out.push_back(static_cast(0xc0 | (uni_ch >> 6))); 765 | } else { 766 | if (uni_ch < 0x10000) { 767 | out.push_back(static_cast(0xe0 | (uni_ch >> 12))); 768 | } else { 769 | out.push_back(static_cast(0xf0 | (uni_ch >> 18))); 770 | out.push_back(static_cast(0x80 | ((uni_ch >> 12) & 0x3f))); 771 | } 772 | out.push_back(static_cast(0x80 | ((uni_ch >> 6) & 0x3f))); 773 | } 774 | out.push_back(static_cast(0x80 | (uni_ch & 0x3f))); 775 | } 776 | return true; 777 | } 778 | 779 | template inline bool _parse_string(String &out, input &in) { 780 | while (1) { 781 | int ch = in.getc(); 782 | if (ch < ' ') { 783 | in.ungetc(); 784 | return false; 785 | } else if (ch == '"') { 786 | return true; 787 | } else if (ch == '\\') { 788 | if ((ch = in.getc()) == -1) { 789 | return false; 790 | } 791 | switch (ch) { 792 | #define MAP(sym, val) \ 793 | case sym: \ 794 | out.push_back(val); \ 795 | break 796 | MAP('"', '\"'); 797 | MAP('\\', '\\'); 798 | MAP('/', '/'); 799 | MAP('b', '\b'); 800 | MAP('f', '\f'); 801 | MAP('n', '\n'); 802 | MAP('r', '\r'); 803 | MAP('t', '\t'); 804 | #undef MAP 805 | case 'u': 806 | if (!_parse_codepoint(out, in)) { 807 | return false; 808 | } 809 | break; 810 | default: 811 | return false; 812 | } 813 | } else { 814 | out.push_back(static_cast(ch)); 815 | } 816 | } 817 | return false; 818 | } 819 | 820 | template inline bool _parse_array(Context &ctx, input &in) { 821 | if (!ctx.parse_array_start()) { 822 | return false; 823 | } 824 | size_t idx = 0; 825 | if (in.expect(']')) { 826 | return ctx.parse_array_stop(idx); 827 | } 828 | do { 829 | if (!ctx.parse_array_item(in, idx)) { 830 | return false; 831 | } 832 | idx++; 833 | } while (in.expect(',')); 834 | return in.expect(']') && ctx.parse_array_stop(idx); 835 | } 836 | 837 | template inline bool _parse_object(Context &ctx, input &in) { 838 | if (!ctx.parse_object_start()) { 839 | return false; 840 | } 841 | if (in.expect('}')) { 842 | return ctx.parse_object_stop(); 843 | } 844 | do { 845 | std::string key; 846 | if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) { 847 | return false; 848 | } 849 | if (!ctx.parse_object_item(in, key)) { 850 | return false; 851 | } 852 | } while (in.expect(',')); 853 | return in.expect('}') && ctx.parse_object_stop(); 854 | } 855 | 856 | template inline std::string _parse_number(input &in) { 857 | std::string num_str; 858 | while (1) { 859 | int ch = in.getc(); 860 | if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') { 861 | num_str.push_back(static_cast(ch)); 862 | } else if (ch == '.') { 863 | #if PICOJSON_USE_LOCALE 864 | num_str += localeconv()->decimal_point; 865 | #else 866 | num_str.push_back('.'); 867 | #endif 868 | } else { 869 | in.ungetc(); 870 | break; 871 | } 872 | } 873 | return num_str; 874 | } 875 | 876 | template inline bool _parse(Context &ctx, input &in) { 877 | in.skip_ws(); 878 | int ch = in.getc(); 879 | switch (ch) { 880 | #define IS(ch, text, op) \ 881 | case ch: \ 882 | if (in.match(text) && op) { \ 883 | return true; \ 884 | } else { \ 885 | return false; \ 886 | } 887 | IS('n', "ull", ctx.set_null()); 888 | IS('f', "alse", ctx.set_bool(false)); 889 | IS('t', "rue", ctx.set_bool(true)); 890 | #undef IS 891 | case '"': 892 | return ctx.parse_string(in); 893 | case '[': 894 | return _parse_array(ctx, in); 895 | case '{': 896 | return _parse_object(ctx, in); 897 | default: 898 | if (('0' <= ch && ch <= '9') || ch == '-') { 899 | double f; 900 | char *endp; 901 | in.ungetc(); 902 | std::string num_str(_parse_number(in)); 903 | if (num_str.empty()) { 904 | return false; 905 | } 906 | #ifdef PICOJSON_USE_INT64 907 | { 908 | errno = 0; 909 | intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); 910 | if (errno == 0 && std::numeric_limits::min() <= ival && ival <= std::numeric_limits::max() && 911 | endp == num_str.c_str() + num_str.size()) { 912 | ctx.set_int64(ival); 913 | return true; 914 | } 915 | } 916 | #endif 917 | f = strtod(num_str.c_str(), &endp); 918 | if (endp == num_str.c_str() + num_str.size()) { 919 | ctx.set_number(f); 920 | return true; 921 | } 922 | return false; 923 | } 924 | break; 925 | } 926 | in.ungetc(); 927 | return false; 928 | } 929 | 930 | class deny_parse_context { 931 | public: 932 | bool set_null() { 933 | return false; 934 | } 935 | bool set_bool(bool) { 936 | return false; 937 | } 938 | #ifdef PICOJSON_USE_INT64 939 | bool set_int64(int64_t) { 940 | return false; 941 | } 942 | #endif 943 | bool set_number(double) { 944 | return false; 945 | } 946 | template bool parse_string(input &) { 947 | return false; 948 | } 949 | bool parse_array_start() { 950 | return false; 951 | } 952 | template bool parse_array_item(input &, size_t) { 953 | return false; 954 | } 955 | bool parse_array_stop(size_t) { 956 | return false; 957 | } 958 | bool parse_object_start() { 959 | return false; 960 | } 961 | template bool parse_object_item(input &, const std::string &) { 962 | return false; 963 | } 964 | }; 965 | 966 | class default_parse_context { 967 | protected: 968 | value *out_; 969 | size_t depths_; 970 | 971 | public: 972 | default_parse_context(value *out, size_t depths = DEFAULT_MAX_DEPTHS) : out_(out), depths_(depths) { 973 | } 974 | bool set_null() { 975 | *out_ = value(); 976 | return true; 977 | } 978 | bool set_bool(bool b) { 979 | *out_ = value(b); 980 | return true; 981 | } 982 | #ifdef PICOJSON_USE_INT64 983 | bool set_int64(int64_t i) { 984 | *out_ = value(i); 985 | return true; 986 | } 987 | #endif 988 | bool set_number(double f) { 989 | *out_ = value(f); 990 | return true; 991 | } 992 | template bool parse_string(input &in) { 993 | *out_ = value(string_type, false); 994 | return _parse_string(out_->get(), in); 995 | } 996 | bool parse_array_start() { 997 | if (depths_ == 0) 998 | return false; 999 | --depths_; 1000 | *out_ = value(array_type, false); 1001 | return true; 1002 | } 1003 | template bool parse_array_item(input &in, size_t) { 1004 | array &a = out_->get(); 1005 | a.push_back(value()); 1006 | default_parse_context ctx(&a.back(), depths_); 1007 | return _parse(ctx, in); 1008 | } 1009 | bool parse_array_stop(size_t) { 1010 | ++depths_; 1011 | return true; 1012 | } 1013 | bool parse_object_start() { 1014 | if (depths_ == 0) 1015 | return false; 1016 | *out_ = value(object_type, false); 1017 | return true; 1018 | } 1019 | template bool parse_object_item(input &in, const std::string &key) { 1020 | object &o = out_->get(); 1021 | default_parse_context ctx(&o[key], depths_); 1022 | return _parse(ctx, in); 1023 | } 1024 | bool parse_object_stop() { 1025 | ++depths_; 1026 | return true; 1027 | } 1028 | 1029 | private: 1030 | default_parse_context(const default_parse_context &); 1031 | default_parse_context &operator=(const default_parse_context &); 1032 | }; 1033 | 1034 | class null_parse_context { 1035 | protected: 1036 | size_t depths_; 1037 | 1038 | public: 1039 | struct dummy_str { 1040 | void push_back(int) { 1041 | } 1042 | }; 1043 | 1044 | public: 1045 | null_parse_context(size_t depths = DEFAULT_MAX_DEPTHS) : depths_(depths) { 1046 | } 1047 | bool set_null() { 1048 | return true; 1049 | } 1050 | bool set_bool(bool) { 1051 | return true; 1052 | } 1053 | #ifdef PICOJSON_USE_INT64 1054 | bool set_int64(int64_t) { 1055 | return true; 1056 | } 1057 | #endif 1058 | bool set_number(double) { 1059 | return true; 1060 | } 1061 | template bool parse_string(input &in) { 1062 | dummy_str s; 1063 | return _parse_string(s, in); 1064 | } 1065 | bool parse_array_start() { 1066 | if (depths_ == 0) 1067 | return false; 1068 | --depths_; 1069 | return true; 1070 | } 1071 | template bool parse_array_item(input &in, size_t) { 1072 | return _parse(*this, in); 1073 | } 1074 | bool parse_array_stop(size_t) { 1075 | ++depths_; 1076 | return true; 1077 | } 1078 | bool parse_object_start() { 1079 | if (depths_ == 0) 1080 | return false; 1081 | --depths_; 1082 | return true; 1083 | } 1084 | template bool parse_object_item(input &in, const std::string &) { 1085 | ++depths_; 1086 | return _parse(*this, in); 1087 | } 1088 | bool parse_object_stop() { 1089 | return true; 1090 | } 1091 | 1092 | private: 1093 | null_parse_context(const null_parse_context &); 1094 | null_parse_context &operator=(const null_parse_context &); 1095 | }; 1096 | 1097 | // obsolete, use the version below 1098 | template inline std::string parse(value &out, Iter &pos, const Iter &last) { 1099 | std::string err; 1100 | pos = parse(out, pos, last, &err); 1101 | return err; 1102 | } 1103 | 1104 | template inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) { 1105 | input in(first, last); 1106 | if (!_parse(ctx, in) && err != NULL) { 1107 | char buf[64]; 1108 | SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); 1109 | *err = buf; 1110 | while (1) { 1111 | int ch = in.getc(); 1112 | if (ch == -1 || ch == '\n') { 1113 | break; 1114 | } else if (ch >= ' ') { 1115 | err->push_back(static_cast(ch)); 1116 | } 1117 | } 1118 | } 1119 | return in.cur(); 1120 | } 1121 | 1122 | template inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) { 1123 | default_parse_context ctx(&out); 1124 | return _parse(ctx, first, last, err); 1125 | } 1126 | 1127 | inline std::string parse(value &out, const std::string &s) { 1128 | std::string err; 1129 | parse(out, s.begin(), s.end(), &err); 1130 | return err; 1131 | } 1132 | 1133 | inline std::string parse(value &out, std::istream &is) { 1134 | std::string err; 1135 | parse(out, std::istreambuf_iterator(is.rdbuf()), std::istreambuf_iterator(), &err); 1136 | return err; 1137 | } 1138 | 1139 | template struct last_error_t { static std::string s; }; 1140 | template std::string last_error_t::s; 1141 | 1142 | inline void set_last_error(const std::string &s) { 1143 | last_error_t::s = s; 1144 | } 1145 | 1146 | inline const std::string &get_last_error() { 1147 | return last_error_t::s; 1148 | } 1149 | 1150 | inline bool operator==(const value &x, const value &y) { 1151 | if (x.is()) 1152 | return y.is(); 1153 | #define PICOJSON_CMP(type) \ 1154 | if (x.is()) \ 1155 | return y.is() && x.get() == y.get() 1156 | PICOJSON_CMP(bool); 1157 | PICOJSON_CMP(double); 1158 | PICOJSON_CMP(std::string); 1159 | PICOJSON_CMP(array); 1160 | PICOJSON_CMP(object); 1161 | #undef PICOJSON_CMP 1162 | PICOJSON_ASSERT(0); 1163 | #ifdef _MSC_VER 1164 | __assume(0); 1165 | #endif 1166 | return false; 1167 | } 1168 | 1169 | inline bool operator!=(const value &x, const value &y) { 1170 | return !(x == y); 1171 | } 1172 | } 1173 | 1174 | #if !PICOJSON_USE_RVALUE_REFERENCE 1175 | namespace std { 1176 | template <> inline void swap(picojson::value &x, picojson::value &y) { 1177 | x.swap(y); 1178 | } 1179 | } 1180 | #endif 1181 | 1182 | inline std::istream &operator>>(std::istream &is, picojson::value &x) { 1183 | picojson::set_last_error(std::string()); 1184 | const std::string err(picojson::parse(x, is)); 1185 | if (!err.empty()) { 1186 | picojson::set_last_error(err); 1187 | is.setstate(std::ios::failbit); 1188 | } 1189 | return is; 1190 | } 1191 | 1192 | inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) { 1193 | x.serialize(std::ostream_iterator(os)); 1194 | return os; 1195 | } 1196 | #ifdef _MSC_VER 1197 | #pragma warning(pop) 1198 | #endif 1199 | 1200 | #endif 1201 | --------------------------------------------------------------------------------