diff --git a/.gitignore b/.gitignore index 5d115b05..f034a769 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ .packages build/ # If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock +**/*.lock # Directory created by dartdoc # If you don't generate documentation locally you can remove this line. @@ -69,4 +69,7 @@ app.*.map.json /android/app/release /macos/third/cef/Chromium Embedded Framework.framework -/macos/third/cef/libcef_dll_wrapper.a \ No newline at end of file +/macos/third/cef/libcef_dll_wrapper.a + +third/cef/ +linux/prebuilt.tar.bz2 diff --git a/README.md b/README.md index a489b034..682b9114 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,46 @@ Then follow the below steps inside the `macos/` folder of the cloned reposito For Linux, just adding `webview_cef` to your `pubspec.yaml` (e.g. by running `flutter pub add webview_cef`) does the job. +#### Optional: choose CEF flavor (Linux only) + +You can control which CEF package is downloaded by passing a CMake variable when building your app: + +- `DOWNLOAD_CEF_FLAVOR=standard` (default): downloads the full CEF package. +- `DOWNLOAD_CEF_FLAVOR=minimal`: downloads a smaller, minimal package to reduce size. + +Example (Flutter app): add this line near the top of your app's `linux/CMakeLists.txt` (before plugin registration): + +``` +set(DOWNLOAD_CEF_FLAVOR minimal CACHE STRING "") +``` + +#### Native file dialogs (Linux) + +On Linux, file dialogs are handled natively via GTK (Open, Save, and Select Folder). Multiple selection and basic file filters are supported. No additional Flutter plugins are required. + +#### Debug vs. Release CEF artifacts (Linux) + +When building in Debug, if Debug CEF binaries are not present, the build will automatically fall back to the Release `libcef.so` and resources. This helps local development but may limit debug symbol availability. To get full debug info, install the Debug CEF bundle. + +### Initialization options + +You can configure CEF at startup. Call this once before creating any WebViews: + +```dart +await WebviewManager().initialize( + userAgent: "MyApp/1.0", // optional: appended to default UA + cachePath: "/path/to/cache", // optional: writable directory for cache + persistSessionCookies: true, // effective only if cachePath is set + persistUserPreferences: true, // effective only if cachePath is set + enableGPU: false, // set true to enable GPU acceleration +); +``` + +Notes: +- If `cachePath` is empty or omitted, CEF runs in-memory (incognito-like) and nothing is persisted to disk. +- `persistSessionCookies` and `persistUserPreferences` require a non-empty `cachePath`. +- On Linux, enabling GPU can reduce software-rendering warnings for WebGL, but driver support varies by system. + ## TODOs > Pull requests are welcome. @@ -117,12 +157,35 @@ This demo is a simple webview app that can be used to test the `webview_cef` plu ### Screenshots -| Windows | macOS | Linux | -| --- | --- | --- | -| | | | -| | | | -| | image | | +| Windows | macOS | Linux | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| | | | +| | | | +| | image | | ## Credits +## Contributing + +Pull requests are welcome. There is no formal contribution guide yet; please use a feature/issue-based branch name and a clear PR description. Suggested branch naming: + +- `feat/cache-path-and-cef-flavor` +- `fix/` +- `chore/update-deps` + +Before opening a PR: + +- Run on at least one target OS (Linux/Windows/macOS if your change affects it). +- Update README or example usage when adding new behavior. +- Keep changes focused and add brief comments where behavior differs by platform. + +### New options added + +- Initialize with cache path and persistence flags: + - Dart: `await WebviewManager().initialize(userAgent: "...", cachePath: "/path/to/cache", persistSessionCookies: true, persistUserPreferences: true);` + - Native: Wired to `CefSettings.cache_path`, `persist_session_cookies`, and `persist_user_preferences`. +- Linux only: `DOWNLOAD_CEF_FLAVOR` CMake option to fetch `minimal` or `standard` CEF builds. +- Linux: Native GTK file dialogs (Open/Save/Select Folder) with multiple selection and basic filters. +- Optional `enableGPU` flag at initialization to toggle hardware acceleration. + This project is inspired from [**`flutter_webview_windows`**](https://github.com/jnschulze/flutter-webview-windows). diff --git a/common/webview_app.cc b/common/webview_app.cc index 8b8869f9..4686ac70 100644 --- a/common/webview_app.cc +++ b/common/webview_app.cc @@ -12,126 +12,140 @@ #include "include/views/cef_window.h" #include "include/wrapper/cef_helpers.h" -namespace { - -// When using the Views framework this object provides the delegate -// implementation for the CefWindow that hosts the Views-based browser. -class SimpleWindowDelegate : public CefWindowDelegate { -public: - explicit SimpleWindowDelegate(CefRefPtr browser_view) - : browser_view_(browser_view) {} - - void OnWindowCreated(CefRefPtr window) override { - // Add the browser view and show the window. - window->AddChildView(browser_view_); - window->Show(); - - // Give keyboard focus to the browser view. - browser_view_->RequestFocus(); - } - - void OnWindowDestroyed(CefRefPtr window) override { - browser_view_ = nullptr; - } - - bool CanClose(CefRefPtr window) override { - // Allow the window to close if the browser says it's OK. - CefRefPtr browser = browser_view_->GetBrowser(); - if (browser) - return browser->GetHost()->TryCloseBrowser(); - return true; - } - - CefSize GetPreferredSize(CefRefPtr view) override { - return CefSize(1280, 720); - } - -private: - CefRefPtr browser_view_; - - IMPLEMENT_REFCOUNTING(SimpleWindowDelegate); - DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate); -}; - -class SimpleBrowserViewDelegate : public CefBrowserViewDelegate { -public: - SimpleBrowserViewDelegate() {} - - bool OnPopupBrowserViewCreated(CefRefPtr browser_view, - CefRefPtr popup_browser_view, - bool is_devtools) override { - // Create a new top-level Window for the popup. It will show itself after - // creation. - CefWindow::CreateTopLevelWindow( - new SimpleWindowDelegate(popup_browser_view)); - - // We created the Window. - return true; - } - -private: - IMPLEMENT_REFCOUNTING(SimpleBrowserViewDelegate); - DISALLOW_COPY_AND_ASSIGN(SimpleBrowserViewDelegate); -}; +namespace +{ + + // When using the Views framework this object provides the delegate + // implementation for the CefWindow that hosts the Views-based browser. + class SimpleWindowDelegate : public CefWindowDelegate + { + public: + explicit SimpleWindowDelegate(CefRefPtr browser_view) + : browser_view_(browser_view) {} + + void OnWindowCreated(CefRefPtr window) override + { + // Add the browser view and show the window. + window->AddChildView(browser_view_); + window->Show(); + + // Give keyboard focus to the browser view. + browser_view_->RequestFocus(); + } + + void OnWindowDestroyed(CefRefPtr window) override + { + browser_view_ = nullptr; + } + + bool CanClose(CefRefPtr window) override + { + // Allow the window to close if the browser says it's OK. + CefRefPtr browser = browser_view_->GetBrowser(); + if (browser) + return browser->GetHost()->TryCloseBrowser(); + return true; + } + + CefSize GetPreferredSize(CefRefPtr view) override + { + return CefSize(1280, 720); + } + + private: + CefRefPtr browser_view_; + + IMPLEMENT_REFCOUNTING(SimpleWindowDelegate); + DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate); + }; + + class SimpleBrowserViewDelegate : public CefBrowserViewDelegate + { + public: + SimpleBrowserViewDelegate() {} -} // namespace + bool OnPopupBrowserViewCreated(CefRefPtr browser_view, + CefRefPtr popup_browser_view, + bool is_devtools) override + { + // Create a new top-level Window for the popup. It will show itself after + // creation. + CefWindow::CreateTopLevelWindow( + new SimpleWindowDelegate(popup_browser_view)); + + // We created the Window. + return true; + } + + private: + IMPLEMENT_REFCOUNTING(SimpleBrowserViewDelegate); + DISALLOW_COPY_AND_ASSIGN(SimpleBrowserViewDelegate); + }; -WebviewApp::WebviewApp(CefRefPtr handler) { +} // namespace + +WebviewApp::WebviewApp(CefRefPtr handler) +{ m_handler = handler; } WebviewApp::ProcessType WebviewApp::GetProcessType(CefRefPtr command_line) { // The command-line flag won't be specified for the browser process. - if (!command_line->HasSwitch("type")) + if (!command_line->HasSwitch("type")) { return BrowserProcess; } - const std::string& process_type = command_line->GetSwitchValue("type"); - if (process_type == "renderer") - return RendererProcess; + const std::string &process_type = command_line->GetSwitchValue("type"); + if (process_type == "renderer") + return RendererProcess; #if defined(OS_LINUX) - else if (process_type == "zygote") - return ZygoteProcess; + else if (process_type == "zygote") + return ZygoteProcess; #endif - return OtherProcess; + return OtherProcess; } void WebviewApp::OnBeforeCommandLineProcessing(const CefString &process_type, CefRefPtr command_line) { // Pass additional command-line flags to the browser process. - if (process_type.empty()) - { - if (!m_bEnableGPU) - { - command_line->AppendSwitch("disable-gpu"); - command_line->AppendSwitch("disable-gpu-compositing"); - } - - command_line->AppendSwitch("disable-web-security"); //disable web security - command_line->AppendSwitch("allow-running-insecure-content"); //allow running insecure content in secure pages - // Don't create a "GPUCache" directory when cache-path is unspecified. - command_line->AppendSwitch("disable-gpu-shader-disk-cache"); //disable gpu shader disk cache - command_line->AppendSwitch("no-sanbox"); - - //http://www.chromium.org/developers/design-documents/process-models - if (m_uMode == 1) - { - command_line->AppendSwitch("process-per-site"); //each site in its own process - command_line->AppendSwitchWithValue("renderer-process-limit ", "8"); //limit renderer process count to decrease memory usage - } - else if (m_uMode == 2) - { - command_line->AppendSwitch("process-per-tab"); //each tab in its own process - } - else if (m_uMode == 3) - { - command_line->AppendSwitch("single-process"); //all in one process - } - command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); //autoplay policy for media - - //Support cross domain requests + if (process_type.empty()) + { + if (!m_bEnableGPU) + { + command_line->AppendSwitch("disable-gpu"); + command_line->AppendSwitch("disable-gpu-compositing"); + } + + command_line->AppendSwitch("disable-web-security"); // disable web security + command_line->AppendSwitch("allow-running-insecure-content"); // allow running insecure content in secure pages + // Don't create a "GPUCache" directory when cache-path is unspecified. + command_line->AppendSwitch("disable-gpu-shader-disk-cache"); // disable gpu shader disk cache + command_line->AppendSwitch("no-sanbox"); + + // http://www.chromium.org/developers/design-documents/process-models + if (m_uMode == 1) + { + command_line->AppendSwitch("process-per-site"); // each site in its own process + command_line->AppendSwitchWithValue("renderer-process-limit ", "8"); // limit renderer process count to decrease memory usage + } + else if (m_uMode == 2) + { + command_line->AppendSwitch("process-per-tab"); // each tab in its own process + } + else if (m_uMode == 3) + { + command_line->AppendSwitch("single-process"); // all in one process + } + command_line->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); // autoplay policy for media + + // Enable CEF logging for diagnostics + command_line->AppendSwitch("enable-logging"); + command_line->AppendSwitchWithValue("log-severity", "info"); + command_line->AppendSwitchWithValue("log-file", "/tmp/cef_debug.log"); + + // Support cross domain requests std::string values = command_line->GetSwitchValue("disable-features"); if (values == "") { @@ -148,12 +162,12 @@ void WebviewApp::OnBeforeCommandLineProcessing(const CefString &process_type, Ce command_line->AppendSwitchWithValue("disable-features", values); // for unsafe domain, add domain to whitelist - if (!m_strFilterDomain.empty()) - { - command_line->AppendSwitch("ignore-certificate-errors"); //ignore certificate errors - command_line->AppendSwitchWithValue("unsafely-treat-insecure-origin-as-secure", - m_strFilterDomain); - } + if (!m_strFilterDomain.empty()) + { + command_line->AppendSwitch("ignore-certificate-errors"); // ignore certificate errors + command_line->AppendSwitchWithValue("unsafely-treat-insecure-origin-as-secure", + m_strFilterDomain); + } } #ifdef __APPLE__ @@ -161,22 +175,21 @@ void WebviewApp::OnBeforeCommandLineProcessing(const CefString &process_type, Ce command_line->AppendSwitch("single-process"); #endif #ifdef __linux__ - + #endif } void WebviewApp::OnContextInitialized() { CEF_REQUIRE_UI_THREAD(); -// CefBrowserSettings browser_settings; -// browser_settings.windowless_frame_rate = 60; -// -// CefWindowInfo window_info; -// window_info.SetAsWindowless(0); -// -// // create browser -// CefBrowserHost::CreateBrowser(window_info, m_handler, "", browser_settings, nullptr, nullptr); - + // CefBrowserSettings browser_settings; + // browser_settings.windowless_frame_rate = 60; + // + // CefWindowInfo window_info; + // window_info.SetAsWindowless(0); + // + // // create browser + // CefBrowserHost::CreateBrowser(window_info, m_handler, "", browser_settings, nullptr, nullptr); } // CefRefPtr WebviewApp::GetDefaultClient() { @@ -191,7 +204,7 @@ void WebviewApp::SetUnSafelyTreatInsecureOriginAsSecure(const CefString &strFilt void WebviewApp::OnWebKitInitialized() { - //inject js function for jssdk + // inject js function for jssdk std::string extensionCode = R"( var external = {}; var clientSdk = {}; @@ -266,7 +279,8 @@ void WebviewApp::OnWebKitInitialized() void WebviewApp::OnBrowserCreated(CefRefPtr browser, CefRefPtr extra_info) { - if (!m_render_js_bridge.get()) { + if (!m_render_js_bridge.get()) + { m_render_js_bridge.reset(new CefJSBridge); } } @@ -306,8 +320,8 @@ void WebviewApp::OnUncaughtException(CefRefPtr browser, CefRefPtr browser, CefRefPtr frame, CefRefPtr node) - { - //Get node attribute +{ + // Get node attribute bool is_editable = (node.get() && node->IsEditable()); CefRefPtr message = CefProcessMessage::Create(kFocusedNodeChangedMessage); message->GetArgumentList()->SetBool(0, is_editable); @@ -322,12 +336,12 @@ void WebviewApp::OnFocusedNodeChanged(CefRefPtr browser, CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, CefRefPtr message) { - const CefString& message_name = message->GetName(); + const CefString &message_name = message->GetName(); if (message_name == kExecuteJsCallbackMessage) { - int callbackId = message->GetArgumentList()->GetInt(0); - bool error = message->GetArgumentList()->GetBool(1); - CefString result = message->GetArgumentList()->GetString(2); + int callbackId = message->GetArgumentList()->GetInt(0); + bool error = message->GetArgumentList()->GetBool(1); + CefString result = message->GetArgumentList()->GetString(2); if (m_render_js_bridge.get()) { m_render_js_bridge->ExecuteJSCallbackFunc(callbackId, error, result); diff --git a/common/webview_handler.cc b/common/webview_handler.cc index 71bc682f..271f7850 100644 --- a/common/webview_handler.cc +++ b/common/webview_handler.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include "include/base/cef_callback.h" #include "include/cef_app.h" @@ -26,72 +27,124 @@ // webview_handler.cc:567:24: error: no matching function for call to 'to_string' namespace stringpatch { - template < typename T > std::string to_string( const T& n ) + template + std::string to_string(const T &n) { - std::ostringstream stm ; - stm << n ; - return stm.str() ; + std::ostringstream stm; + stm << n; + return stm.str(); } } #include "webview_js_handler.h" +#include "webview_plugin.h" +#include -namespace { -// The only browser that currently get focused -CefRefPtr current_focused_browser_ = nullptr; +namespace +{ + class LambdaTask : public CefTask + { + public: + explicit LambdaTask(std::function f) : f_(std::move(f)) {} + void Execute() override + { + if (f_) + f_(); + } -// Returns a data: URI with the specified contents. -std::string GetDataURI(const std::string& data, const std::string& mime_type) { - return "data:" + mime_type + ";base64," + - CefURIEncode(CefBase64Encode(data.data(), data.size()), false) - .ToString(); + private: + std::function f_; + IMPLEMENT_REFCOUNTING(LambdaTask); + }; } -} // namespace +namespace +{ + // The only browser that currently get focused + CefRefPtr current_focused_browser_ = nullptr; -WebviewHandler::WebviewHandler() { + // Returns a data: URI with the specified contents. + std::string GetDataURI(const std::string &data, const std::string &mime_type) + { + return "data:" + mime_type + ";base64," + + CefURIEncode(CefBase64Encode(data.data(), data.size()), false) + .ToString(); + } + // Normalize frame identifier across different CEF builds where + // CefFrame::GetIdentifier() can return either an integer id or a CefString. +#if __linux__ + inline int64_t FrameIdToInt64(const CefString &id) + { + // Non-throwing conversion using strtoll; returns 0 on failure. + const std::string s = id.ToString(); + const char *str = s.c_str(); + char *end = nullptr; + long long v = std::strtoll(str, &end, 10); + if (end == str) + { + return 0; + } + return static_cast(v); + } +#else + inline int64_t FrameIdToInt64(const int64_t id) + { + return id; + } +#endif + +} // namespace + +WebviewHandler::WebviewHandler() +{ } -WebviewHandler::~WebviewHandler() { +WebviewHandler::~WebviewHandler() +{ browser_map_.clear(); js_callbacks_.clear(); } bool WebviewHandler::OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, - CefProcessId source_process, CefRefPtr message) + CefProcessId source_process, CefRefPtr message) { - std::string message_name = message->GetName(); + std::string message_name = message->GetName(); if (message_name == kFocusedNodeChangedMessage) { current_focused_browser_ = browser; bool editable = message->GetArgumentList()->GetBool(0); onFocusedNodeChangeMessage(browser->GetIdentifier(), editable); - if (editable) { + if (editable) + { onImeCompositionRangeChangedMessage(browser->GetIdentifier(), message->GetArgumentList()->GetInt(1), message->GetArgumentList()->GetInt(2)); } } - else if(message_name == kJSCallCppFunctionMessage) + else if (message_name == kJSCallCppFunctionMessage) { CefString fun_name = message->GetArgumentList()->GetString(0); - CefString param = message->GetArgumentList()->GetString(1); - int js_callback_id = message->GetArgumentList()->GetInt(2); + CefString param = message->GetArgumentList()->GetString(1); + int js_callback_id = message->GetArgumentList()->GetInt(2); - if (fun_name.empty() || !(browser.get())) { - return false; - } + if (fun_name.empty() || !(browser.get())) + { + return false; + } onJavaScriptChannelMessage( - fun_name,param,stringpatch::to_string(js_callback_id), browser->GetIdentifier(), stringpatch::to_string(frame->GetIdentifier())); + fun_name, param, stringpatch::to_string(js_callback_id), browser->GetIdentifier(), stringpatch::to_string(frame->GetIdentifier())); } - else if(message_name == kEvaluateCallbackMessage){ + else if (message_name == kEvaluateCallbackMessage) + { CefString callbackId = message->GetArgumentList()->GetString(0); CefRefPtr param = message->GetArgumentList()->GetValue(1); - if(!callbackId.empty()){ + if (!callbackId.empty()) + { auto it = js_callbacks_.find(callbackId.ToString()); - if(it != js_callbacks_.end()){ + if (it != js_callbacks_.end()) + { it->second(param); js_callbacks_.erase(it); } @@ -101,34 +154,42 @@ bool WebviewHandler::OnProcessMessageReceived( } void WebviewHandler::OnTitleChange(CefRefPtr browser, - const CefString& title) { - //todo: title change - if(onTitleChangedEvent) { + const CefString &title) +{ + // todo: title change + if (onTitleChangedEvent) + { onTitleChangedEvent(browser->GetIdentifier(), title); } } void WebviewHandler::OnAddressChange(CefRefPtr browser, - CefRefPtr frame, - const CefString& url) { - if(onUrlChangedEvent) { + CefRefPtr frame, + const CefString &url) +{ + if (onUrlChangedEvent) + { onUrlChangedEvent(browser->GetIdentifier(), url); } } bool WebviewHandler::OnCursorChange(CefRefPtr browser, - CefCursorHandle cursor, - cef_cursor_type_t type, - const CefCursorInfo& custom_cursor_info){ - if(onCursorChangedEvent) { + CefCursorHandle cursor, + cef_cursor_type_t type, + const CefCursorInfo &custom_cursor_info) +{ + if (onCursorChangedEvent) + { onCursorChangedEvent(browser->GetIdentifier(), type); return true; } return false; } -bool WebviewHandler::OnTooltip(CefRefPtr browser, CefString& text) { - if(onTooltipEvent) { +bool WebviewHandler::OnTooltip(CefRefPtr browser, CefString &text) +{ + if (onTooltipEvent) + { onTooltipEvent(browser->GetIdentifier(), text); return true; } @@ -137,46 +198,65 @@ bool WebviewHandler::OnTooltip(CefRefPtr browser, CefString& text) { bool WebviewHandler::OnConsoleMessage(CefRefPtr browser, cef_log_severity_t level, - const CefString& message, - const CefString& source, - int line){ - if(onConsoleMessageEvent){ + const CefString &message, + const CefString &source, + int line) +{ + if (onConsoleMessageEvent) + { onConsoleMessageEvent(browser->GetIdentifier(), level, message, source, line); } return false; } -void WebviewHandler::OnAfterCreated(CefRefPtr browser) { +void WebviewHandler::OnAfterCreated(CefRefPtr browser) +{ CEF_REQUIRE_UI_THREAD(); - if (!browser->IsPopup()) { + if (!browser->IsPopup()) + { browser_map_.emplace(browser->GetIdentifier(), browser_info()); browser_map_[browser->GetIdentifier()].browser = browser; + // Track open browser count for safe shutdown. + webview_cef::NotifyBrowserCreated(); } } -bool WebviewHandler::DoClose(CefRefPtr browser) { - CEF_REQUIRE_UI_THREAD(); +bool WebviewHandler::DoClose(CefRefPtr browser) +{ + CEF_REQUIRE_UI_THREAD(); // Allow the close. For windowed browsers this will result in the OS close // event being sent. return false; } -void WebviewHandler::OnBeforeClose(CefRefPtr browser) { +void WebviewHandler::OnBeforeClose(CefRefPtr browser) +{ CEF_REQUIRE_UI_THREAD(); + if (!browser->IsPopup()) + { + // Decrement open browser count. + webview_cef::NotifyBrowserClosed(); + // Clear global focused browser reference if it matches the closing one. + if (current_focused_browser_ && current_focused_browser_->IsSame(browser)) + { + current_focused_browser_ = nullptr; + } + } } bool WebviewHandler::OnBeforePopup(CefRefPtr browser, - CefRefPtr frame, - const CefString& target_url, - const CefString& target_frame_name, - WindowOpenDisposition target_disposition, - bool user_gesture, - const CefPopupFeatures& popupFeatures, - CefWindowInfo& windowInfo, - CefRefPtr& client, - CefBrowserSettings& settings, - CefRefPtr& extra_info, - bool* no_javascript_access) { + CefRefPtr frame, + const CefString &target_url, + const CefString &target_frame_name, + WindowOpenDisposition target_disposition, + bool user_gesture, + const CefPopupFeatures &popupFeatures, + CefWindowInfo &windowInfo, + CefRefPtr &client, + CefBrowserSettings &settings, + CefRefPtr &extra_info, + bool *no_javascript_access) +{ loadUrl(browser->GetIdentifier(), target_url); return true; } @@ -196,52 +276,60 @@ void WebviewHandler::OnGotFocus(CefRefPtr browser) } void WebviewHandler::OnLoadError(CefRefPtr browser, - CefRefPtr frame, - ErrorCode errorCode, - const CefString& errorText, - const CefString& failedUrl) { + CefRefPtr frame, + ErrorCode errorCode, + const CefString &errorText, + const CefString &failedUrl) +{ CEF_REQUIRE_UI_THREAD(); - + // Allow Chrome to show the error page. if (IsChromeRuntimeEnabled()) return; - + // Don't display an error for downloaded files. if (errorCode == ERR_ABORTED) return; - + // Display a load error message using a data: URI. std::stringstream ss; ss << "" - "

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

"; - + "

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

"; + frame->LoadURL(GetDataURI(ss.str(), "text/html")); } void WebviewHandler::OnLoadStart(CefRefPtr browser, CefRefPtr frame, - CefLoadHandler::TransitionType transition_type) { - if(onLoadStart){ + CefLoadHandler::TransitionType transition_type) +{ + if (onLoadStart) + { onLoadStart(browser->GetIdentifier(), frame->GetURL()); } return; } void WebviewHandler::OnLoadEnd(CefRefPtr browser, CefRefPtr frame, - int httpStatusCode) { - if(onLoadEnd){ + int httpStatusCode) +{ + if (onLoadEnd) + { onLoadEnd(browser->GetIdentifier(), frame->GetURL()); } return; } -void WebviewHandler::CloseAllBrowsers(bool force_close) { - if (browser_map_.empty()){ +void WebviewHandler::CloseAllBrowsers(bool force_close) +{ + if (browser_map_.empty()) + { return; } - - for (auto& it : browser_map_){ + + for (auto &it : browser_map_) + { it.second.browser->GetHost()->CloseBrowser(force_close); it.second.browser = nullptr; } @@ -249,11 +337,13 @@ void WebviewHandler::CloseAllBrowsers(bool force_close) { } // static -bool WebviewHandler::IsChromeRuntimeEnabled() { +bool WebviewHandler::IsChromeRuntimeEnabled() +{ static int value = -1; - if (value == -1) { + if (value == -1) + { CefRefPtr command_line = - CefCommandLine::GetGlobalCommandLine(); + CefCommandLine::GetGlobalCommandLine(); value = command_line->HasSwitch("enable-chrome-runtime") ? 1 : 0; } return value == 1; @@ -262,7 +352,8 @@ bool WebviewHandler::IsChromeRuntimeEnabled() { void WebviewHandler::closeBrowser(int browserId) { auto it = browser_map_.find(browserId); - if(it != browser_map_.end()){ + if (it != browser_map_.end()) + { it->second.browser->GetHost()->CloseBrowser(true); it->second.browser = nullptr; browser_map_.erase(it); @@ -272,22 +363,29 @@ void WebviewHandler::closeBrowser(int browserId) void WebviewHandler::createBrowser(std::string url, std::function callback) { #ifndef OS_MAC - if(!CefCurrentlyOn(TID_UI)) { - CefPostTask(TID_UI, base::BindOnce(&WebviewHandler::createBrowser, this, url, callback)); - return; - } + if (!CefCurrentlyOn(TID_UI)) + { + auto self = this; + CefPostTask(TID_UI, new LambdaTask([self, url, callback]() + { self->createBrowser(url, callback); })); + return; + } #endif - CefBrowserSettings browser_settings ; + CefBrowserSettings browser_settings; browser_settings.windowless_frame_rate = 30; CefWindowInfo window_info; window_info.SetAsWindowless(0); callback(CefBrowserHost::CreateBrowserSync(window_info, this, url, browser_settings, nullptr, nullptr)->GetIdentifier()); } -void WebviewHandler::sendScrollEvent(int browserId, int x, int y, int deltaX, int deltaY) { +void WebviewHandler::sendScrollEvent(int browserId, int x, int y, int deltaX, int deltaY) +{ + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { CefMouseEvent ev; ev.x = x; ev.y = y; @@ -300,15 +398,16 @@ void WebviewHandler::sendScrollEvent(int browserId, int x, int y, int deltaX, in #else it->second.browser->GetHost()->SendMouseWheelEvent(ev, deltaX, deltaY); #endif - - } } void WebviewHandler::changeSize(int browserId, float a_dpi, int w, int h) { + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { it->second.dpi = a_dpi; it->second.width = w; it->second.height = h; @@ -318,47 +417,64 @@ void WebviewHandler::changeSize(int browserId, float a_dpi, int w, int h) void WebviewHandler::cursorClick(int browserId, int x, int y, bool up) { + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { CefMouseEvent ev; ev.x = x; ev.y = y; ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON; - if(up && it->second.is_dragging) { + if (up && it->second.is_dragging) + { it->second.browser->GetHost()->DragTargetDrop(ev); it->second.browser->GetHost()->DragSourceSystemDragEnded(); it->second.is_dragging = false; - } else { + } + else + { it->second.browser->GetHost()->SendMouseClickEvent(ev, CefBrowserHost::MouseButtonType::MBT_LEFT, up, 1); } } } -void WebviewHandler::cursorMove(int browserId, int x , int y, bool dragging) +void WebviewHandler::cursorMove(int browserId, int x, int y, bool dragging) { + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { CefMouseEvent ev; ev.x = x; ev.y = y; - if(dragging) { + if (dragging) + { ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON; } - if(it->second.is_dragging && dragging) { + if (it->second.is_dragging && dragging) + { it->second.browser->GetHost()->DragTargetDragOver(ev, DRAG_OPERATION_EVERY); - } else { + } + else + { it->second.browser->GetHost()->SendMouseMoveEvent(ev, false); } } } bool WebviewHandler::StartDragging(CefRefPtr browser, - CefRefPtr drag_data, - DragOperationsMask allowed_ops, - int x, - int y){ + CefRefPtr drag_data, + DragOperationsMask allowed_ops, + int x, + int y) +{ + if (!CefCurrentlyOn(TID_UI)) + return false; auto it = browser_map_.find(browser->GetIdentifier()); - if (it != browser_map_.end() && it->second.browser->IsSame(browser)) { + if (it != browser_map_.end() && it->second.browser->IsSame(browser)) + { CefMouseEvent ev; ev.x = x; ev.y = y; @@ -373,11 +489,14 @@ void WebviewHandler::OnImeCompositionRangeChanged(CefRefPtr browser, { CEF_REQUIRE_UI_THREAD(); auto it = browser_map_.find(browser->GetIdentifier()); - if(it == browser_map_.end() || !it->second.browser.get() || browser->IsPopup()) { + if (it == browser_map_.end() || !it->second.browser.get() || browser->IsPopup()) + { return; } - if (!character_bounds.empty()) { - if (it->second.is_ime_commit) { + if (!character_bounds.empty()) + { + if (it->second.is_ime_commit) + { auto lastCharacter = character_bounds.back(); it->second.prev_ime_position = lastCharacter; onImeCompositionRangeChangedMessage(browser->GetIdentifier(), lastCharacter.x + lastCharacter.width, lastCharacter.y + lastCharacter.height); @@ -386,7 +505,8 @@ void WebviewHandler::OnImeCompositionRangeChanged(CefRefPtr browser, else { auto firstCharacter = character_bounds.front(); - if (firstCharacter != it->second.prev_ime_position) { + if (firstCharacter != it->second.prev_ime_position) + { it->second.prev_ime_position = firstCharacter; onImeCompositionRangeChangedMessage(browser->GetIdentifier(), firstCharacter.x, firstCharacter.y + firstCharacter.height); } @@ -394,10 +514,120 @@ void WebviewHandler::OnImeCompositionRangeChanged(CefRefPtr browser, } } -void WebviewHandler::sendKeyEvent(CefKeyEvent& ev) +#ifdef __linux__ +#include +#endif + +bool WebviewHandler::OnFileDialog(CefRefPtr browser, + FileDialogMode mode, + const CefString &title, + const CefString &default_file_path, + const std::vector &accept_filters, + const std::vector &accept_extensions, + const std::vector &accept_descriptions, + CefRefPtr callback) +{ +#ifdef __linux__ + CEF_REQUIRE_UI_THREAD(); + + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; + bool select_folders = false; + bool allow_multiple = false; + + if (mode & FILE_DIALOG_OPEN_FOLDER) + { + action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + select_folders = true; + } + else if (mode & FILE_DIALOG_SAVE) + { + action = GTK_FILE_CHOOSER_ACTION_SAVE; + } + else if (mode & FILE_DIALOG_OPEN_MULTIPLE) + { + action = GTK_FILE_CHOOSER_ACTION_OPEN; + allow_multiple = true; + } + + GtkFileChooserNative *native = gtk_file_chooser_native_new( + title.empty() ? "Select" : std::string(title).c_str(), + nullptr, + action, + NULL, + NULL); + + GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); + gtk_file_chooser_set_select_multiple(chooser, allow_multiple); + + if (!default_file_path.empty()) + { + std::string path = default_file_path; + if (select_folders) + { + gtk_file_chooser_set_current_folder(chooser, path.c_str()); + } + else + { + gtk_file_chooser_set_filename(chooser, path.c_str()); + } + } + + for (const auto &filter : accept_filters) + { + GtkFileFilter *gtk_filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(gtk_filter, std::string(filter).c_str()); + gtk_file_filter_set_name(gtk_filter, std::string(filter).c_str()); + gtk_file_chooser_add_filter(chooser, gtk_filter); + } + + std::vector file_paths; + if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) + { + if (allow_multiple) + { + GSList *files = gtk_file_chooser_get_filenames(chooser); + for (GSList *iter = files; iter != NULL; iter = iter->next) + { + char *fname = static_cast(iter->data); + file_paths.push_back(CefString(fname)); + g_free(fname); + } + g_slist_free(files); + } + else + { + char *fname = gtk_file_chooser_get_filename(chooser); + if (fname) + { + file_paths.push_back(CefString(fname)); + g_free(fname); + } + } + } + + g_object_unref(native); + + if (!file_paths.empty()) + { + callback->Continue(file_paths); + } + else + { + callback->Cancel(); + } + return true; +#else + return false; +#endif +} + +void WebviewHandler::sendKeyEvent(CefKeyEvent &ev) { + if (!CefCurrentlyOn(TID_UI)) + return; auto browser = current_focused_browser_; - if (!browser.get()) { + if (!browser.get()) + { return; } browser->GetHost()->SendKeyEvent(ev); @@ -405,36 +635,55 @@ void WebviewHandler::sendKeyEvent(CefKeyEvent& ev) void WebviewHandler::loadUrl(int browserId, std::string url) { + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { it->second.browser->GetMainFrame()->LoadURL(url); } } -void WebviewHandler::goForward(int browserId) { +void WebviewHandler::goForward(int browserId) +{ + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { it->second.browser->GetMainFrame()->GetBrowser()->GoForward(); } } -void WebviewHandler::goBack(int browserId) { +void WebviewHandler::goBack(int browserId) +{ + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { it->second.browser->GetMainFrame()->GetBrowser()->GoBack(); } } -void WebviewHandler::reload(int browserId) { +void WebviewHandler::reload(int browserId) +{ + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { it->second.browser->GetMainFrame()->GetBrowser()->Reload(); } } -void WebviewHandler::openDevTools(int browserId) { +void WebviewHandler::openDevTools(int browserId) +{ + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it != browser_map_.end()) { + if (it != browser_map_.end()) + { CefWindowInfo windowInfo; #ifdef OS_WIN windowInfo.SetAsPopup(nullptr, "DevTools"); @@ -445,8 +694,11 @@ void WebviewHandler::openDevTools(int browserId) { void WebviewHandler::imeSetComposition(int browserId, std::string text) { + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it==browser_map_.end() || !it->second.browser.get()) { + if (it == browser_map_.end() || !it->second.browser.get()) + { return; } @@ -470,8 +722,11 @@ void WebviewHandler::imeSetComposition(int browserId, std::string text) void WebviewHandler::imeCommitText(int browserId, std::string text) { + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it==browser_map_.end() || !it->second.browser.get()) { + if (it == browser_map_.end() || !it->second.browser.get()) + { return; } @@ -482,55 +737,69 @@ void WebviewHandler::imeCommitText(int browserId, std::string text) auto selection_range_end = static_cast(0 + cTextStr.length()); CefRange selection_range = CefRange(selection_range_end, selection_range_end); #ifndef _WIN32 - it->second.browser->GetHost()->ImeSetComposition(cTextStr, underlines, CefRange(UINT32_MAX, UINT32_MAX), selection_range); + it->second.browser->GetHost()->ImeSetComposition(cTextStr, underlines, CefRange(UINT32_MAX, UINT32_MAX), selection_range); #endif it->second.browser->GetHost()->ImeCommitText(cTextStr, CefRange(UINT32_MAX, UINT32_MAX), 0); - } void WebviewHandler::setClientFocus(int browserId, bool focus) { + if (!CefCurrentlyOn(TID_UI)) + return; auto it = browser_map_.find(browserId); - if (it==browser_map_.end() || !it->second.browser.get()) { + if (it == browser_map_.end() || !it->second.browser.get()) + { return; } it->second.browser->GetHost()->SetFocus(focus); } -void WebviewHandler::setCookie(const std::string& domain, const std::string& key, const std::string& value){ +void WebviewHandler::setCookie(const std::string &domain, const std::string &key, const std::string &value) +{ + // Cookie ops belong on the IO thread. Bail if wrong thread. + if (!CefCurrentlyOn(TID_IO)) + return; CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); - if(manager){ + if (manager) + { CefCookie cookie; - CefString(&cookie.path).FromASCII("/"); - CefString(&cookie.name).FromString(key.c_str()); - CefString(&cookie.value).FromString(value.c_str()); + CefString(&cookie.path).FromASCII("/"); + CefString(&cookie.name).FromString(key.c_str()); + CefString(&cookie.value).FromString(value.c_str()); - if (!domain.empty()) { - CefString(&cookie.domain).FromString(domain.c_str()); - } + if (!domain.empty()) + { + CefString(&cookie.domain).FromString(domain.c_str()); + } - cookie.httponly = true; - cookie.secure = false; - std::string httpDomain = "https://" + domain + "/cookiestorage"; - manager->SetCookie(httpDomain, cookie, nullptr); + cookie.httponly = true; + cookie.secure = false; + std::string httpDomain = "https://" + domain + "/cookiestorage"; + manager->SetCookie(httpDomain, cookie, nullptr); } } -void WebviewHandler::deleteCookie(const std::string& domain, const std::string& key) +void WebviewHandler::deleteCookie(const std::string &domain, const std::string &key) { + if (!CefCurrentlyOn(TID_IO)) + return; CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); - if (manager) { + if (manager) + { std::string httpDomain = "https://" + domain + "/cookiestorage"; manager->DeleteCookies(httpDomain, key, nullptr); } } -void WebviewHandler::visitAllCookies(std::function>)> callback){ +void WebviewHandler::visitAllCookies(std::function>)> callback) +{ + if (!CefCurrentlyOn(TID_IO)) + return; CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); if (!manager) - { - return; - } + { + return; + } CefRefPtr cookieVisitor = new WebviewCookieVisitor(); cookieVisitor->setOnVisitComplete(callback); @@ -538,12 +807,15 @@ void WebviewHandler::visitAllCookies(std::functionVisitAllCookies(cookieVisitor); } -void WebviewHandler::visitUrlCookies(const std::string& domain, const bool& isHttpOnly, std::function>)> callback){ +void WebviewHandler::visitUrlCookies(const std::string &domain, const bool &isHttpOnly, std::function>)> callback) +{ + if (!CefCurrentlyOn(TID_IO)) + return; CefRefPtr manager = CefCookieManager::GetGlobalManager(nullptr); if (!manager) - { - return; - } + { + return; + } CefRefPtr cookieVisitor = new WebviewCookieVisitor(); cookieVisitor->setOnVisitComplete(callback); @@ -556,7 +828,7 @@ void WebviewHandler::visitUrlCookies(const std::string& domain, const bool& isHt void WebviewHandler::setJavaScriptChannels(int browserId, const std::vector channels) { std::string extensionCode = "try{"; - for(auto& channel : channels) + for (auto &channel : channels) { extensionCode += channel; extensionCode += " = (e,r) => {external.JavaScriptChannel('"; @@ -575,18 +847,21 @@ void WebviewHandler::sendJavaScriptChannelCallBack(const bool error, const std:: args->SetBool(1, error); args->SetString(2, result); auto bit = browser_map_.find(browserId); - if(bit != browser_map_.end()){ + if (bit != browser_map_.end()) + { int64_t frameIdInt = atoll(frameId.c_str()); CefRefPtr frame = bit->second.browser->GetMainFrame(); - // Return types for frame->GetIdentifier() changed, use the Linux way when updating MacOS or Windows - // versions in download.cmake + // Return types for frame->GetIdentifier() differ across CEF builds. + // On Linux in our prebuilt it returns a CefString-like type; convert to int. + // Convert current frame id to int64 in a portable way. #if __linux__ - bool identifierMatch = std::stoll(frame->GetIdentifier().ToString()) == frameIdInt; + int64_t currentFrameId = FrameIdToInt64(frame->GetIdentifier()); #else - bool identifierMatch = frame->GetIdentifier() == frameIdInt; + int64_t currentFrameId = FrameIdToInt64(frame->GetIdentifier()); #endif + bool identifierMatch = currentFrameId == frameIdInt; if (identifierMatch) { frame->SendProcessMessage(PID_RENDERER, message); @@ -597,21 +872,23 @@ void WebviewHandler::sendJavaScriptChannelCallBack(const bool error, const std:: static std::string GetCallbackId() { auto time = std::chrono::time_point_cast(std::chrono::system_clock::now()); - time_t timestamp = time.time_since_epoch().count(); + time_t timestamp = time.time_since_epoch().count(); return std::to_string(timestamp); -} +} void WebviewHandler::executeJavaScript(int browserId, const std::string code, std::function)> callback) { - if(!code.empty()) + if (!code.empty()) { auto bit = browser_map_.find(browserId); - if(bit != browser_map_.end() && bit->second.browser.get()){ + if (bit != browser_map_.end() && bit->second.browser.get()) + { CefRefPtr frame = bit->second.browser->GetMainFrame(); if (frame) { std::string finalCode = code; - if(callback != nullptr){ + if (callback != nullptr) + { std::string callbackId = GetCallbackId(); finalCode = "external.EvaluateCallback('"; @@ -621,44 +898,53 @@ void WebviewHandler::executeJavaScript(int browserId, const std::string code, st finalCode += "})());"; js_callbacks_[callbackId] = callback; } - frame->ExecuteJavaScript(finalCode, frame->GetURL(), 0); + frame->ExecuteJavaScript(finalCode, frame->GetURL(), 0); } } } } -void WebviewHandler::GetViewRect(CefRefPtr browser, CefRect &rect) { +void WebviewHandler::GetViewRect(CefRefPtr browser, CefRect &rect) +{ CEF_REQUIRE_UI_THREAD(); auto it = browser_map_.find(browser->GetIdentifier()); - if(it == browser_map_.end() || !it->second.browser.get() || browser->IsPopup()) { + if (it == browser_map_.end() || !it->second.browser.get() || browser->IsPopup()) + { return; } rect.x = rect.y = 0; - - if (it->second.width < 1) { + + if (it->second.width < 1) + { rect.width = 1; - } else { + } + else + { rect.width = it->second.width; } - - if (it->second.height < 1) { + + if (it->second.height < 1) + { rect.height = 1; - } else { + } + else + { rect.height = it->second.height; } } -bool WebviewHandler::GetScreenInfo(CefRefPtr browser, CefScreenInfo& screen_info) { - //todo: hi dpi support - screen_info.device_scale_factor = browser_map_[browser->GetIdentifier()].dpi; +bool WebviewHandler::GetScreenInfo(CefRefPtr browser, CefScreenInfo &screen_info) +{ + // todo: hi dpi support + screen_info.device_scale_factor = browser_map_[browser->GetIdentifier()].dpi; return false; } void WebviewHandler::OnPaint(CefRefPtr browser, CefRenderHandler::PaintElementType type, - const CefRenderHandler::RectList &dirtyRects, const void *buffer, int w, int h) { - if (!browser->IsPopup() && onPaintCallback != nullptr) { + const CefRenderHandler::RectList &dirtyRects, const void *buffer, int w, int h) +{ + if (!browser->IsPopup() && onPaintCallback != nullptr) + { onPaintCallback(browser->GetIdentifier(), buffer, w, h); } } - - diff --git a/common/webview_handler.h b/common/webview_handler.h index 7c3c95e4..ae796d77 100644 --- a/common/webview_handler.h +++ b/common/webview_handler.h @@ -14,13 +14,14 @@ #include "webview_cookieVisitor.h" #define ColorUNDERLINE \ - 0xFF000000 // Black SkColor value for underline, - // same as Blink. + 0xFF000000 // Black SkColor value for underline, + // same as Blink. #define ColorBKCOLOR \ - 0x00000000 // White SkColor value for background, - // same as Blink. + 0x00000000 // White SkColor value for background, + // same as Blink. -struct browser_info{ +struct browser_info +{ CefRefPtr browser; uint32_t width = 1; uint32_t height = 1; @@ -31,84 +32,89 @@ struct browser_info{ }; class WebviewHandler : public CefClient, -public CefDisplayHandler, -public CefLifeSpanHandler, -public CefFocusHandler, -public CefLoadHandler, -public CefRenderHandler{ + public CefDisplayHandler, + public CefLifeSpanHandler, + public CefFocusHandler, + public CefLoadHandler, + public CefRenderHandler, + public CefDialogHandler +{ public: - //Paint callback - std::function onPaintCallback; - //cef message event + // Paint callback + std::function onPaintCallback; + // cef message event std::function onUrlChangedEvent; std::function onTitleChangedEvent; - std::functiononCursorChangedEvent; + std::function onCursorChangedEvent; std::function onTooltipEvent; - std::functiononConsoleMessageEvent; + std::function onConsoleMessageEvent; std::function onFocusedNodeChangeMessage; std::function onImeCompositionRangeChangedMessage; - //webpage message + // webpage message std::function onJavaScriptChannelMessage; std::function onLoadStart; std::function onLoadEnd; - + explicit WebviewHandler(); ~WebviewHandler(); - + // CefClient methods: - virtual CefRefPtr GetDisplayHandler() override { + virtual CefRefPtr GetDisplayHandler() override + { return this; } - virtual CefRefPtr GetLifeSpanHandler() override { + virtual CefRefPtr GetLifeSpanHandler() override + { return this; } - virtual CefRefPtr GetFocusHandler() override { + virtual CefRefPtr GetFocusHandler() override + { return this; } virtual CefRefPtr GetLoadHandler() override { return this; } virtual CefRefPtr GetRenderHandler() override { return this; } + virtual CefRefPtr GetDialogHandler() override { return this; } - bool OnProcessMessageReceived( + bool OnProcessMessageReceived( CefRefPtr browser, - CefRefPtr frame, - CefProcessId source_process, - CefRefPtr message) override; + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override; - // CefDisplayHandler methods: virtual void OnTitleChange(CefRefPtr browser, - const CefString& title) override; + const CefString &title) override; virtual void OnAddressChange(CefRefPtr browser, CefRefPtr frame, - const CefString& url) override; + const CefString &url) override; virtual bool OnCursorChange(CefRefPtr browser, CefCursorHandle cursor, cef_cursor_type_t type, - const CefCursorInfo& custom_cursor_info) override; - virtual bool OnTooltip(CefRefPtr browser, CefString& text) override; + const CefCursorInfo &custom_cursor_info) override; + virtual bool OnTooltip(CefRefPtr browser, CefString &text) override; virtual bool OnConsoleMessage(CefRefPtr browser, cef_log_severity_t level, - const CefString& message, - const CefString& source, + const CefString &message, + const CefString &source, int line) override; - + // CefLifeSpanHandler methods: virtual void OnAfterCreated(CefRefPtr browser) override; virtual bool DoClose(CefRefPtr browser) override; virtual void OnBeforeClose(CefRefPtr browser) override; virtual bool OnBeforePopup(CefRefPtr browser, CefRefPtr frame, - const CefString& target_url, - const CefString& target_frame_name, + const CefString &target_url, + const CefString &target_frame_name, WindowOpenDisposition target_disposition, bool user_gesture, - const CefPopupFeatures& popupFeatures, - CefWindowInfo& windowInfo, - CefRefPtr& client, - CefBrowserSettings& settings, - CefRefPtr& extra_info, - bool* no_javascript_access) override; - + const CefPopupFeatures &popupFeatures, + CefWindowInfo &windowInfo, + CefRefPtr &client, + CefBrowserSettings &settings, + CefRefPtr &extra_info, + bool *no_javascript_access) override; + virtual void OnTakeFocus(CefRefPtr browser, bool next) override; virtual bool OnSetFocus(CefRefPtr browser, FocusSource source) override; virtual void OnGotFocus(CefRefPtr browser) override; @@ -117,29 +123,39 @@ public CefRenderHandler{ virtual void OnLoadError(CefRefPtr browser, CefRefPtr frame, ErrorCode errorCode, - const CefString& errorText, - const CefString& failedUrl) override; + const CefString &errorText, + const CefString &failedUrl) override; virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override; virtual void OnLoadStart(CefRefPtr browser, CefRefPtr frame, CefLoadHandler::TransitionType transition_type) override; - + // CefRenderHandler methods: - virtual void GetViewRect(CefRefPtr browser, CefRect& rect) override; - virtual void OnPaint(CefRefPtr browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) override; - virtual bool GetScreenInfo(CefRefPtr browser, CefScreenInfo& screen_info) override; + virtual void GetViewRect(CefRefPtr browser, CefRect &rect) override; + virtual void OnPaint(CefRefPtr browser, PaintElementType type, const RectList &dirtyRects, const void *buffer, int width, int height) override; + virtual bool GetScreenInfo(CefRefPtr browser, CefScreenInfo &screen_info) override; virtual bool StartDragging(CefRefPtr browser, CefRefPtr drag_data, DragOperationsMask allowed_ops, int x, int y) override; - virtual void OnImeCompositionRangeChanged(CefRefPtr browser,const CefRange& selection_range,const CefRenderHandler::RectList& character_bounds) override; + virtual void OnImeCompositionRangeChanged(CefRefPtr browser, const CefRange &selection_range, const CefRenderHandler::RectList &character_bounds) override; + + // CefDialogHandler methods (Linux: implement file dialogs via GTK) + virtual bool OnFileDialog(CefRefPtr browser, + FileDialogMode mode, + const CefString &title, + const CefString &default_file_path, + const std::vector &accept_filters, + const std::vector &accept_extensions, + const std::vector &accept_descriptions, + CefRefPtr callback) override; // Request that all existing browser windows close. void CloseAllBrowsers(bool force_close); - + // Returns true if the Chrome runtime is enabled. static bool IsChromeRuntimeEnabled(); @@ -150,7 +166,7 @@ public CefRenderHandler{ void changeSize(int browserId, float a_dpi, int width, int height); void cursorClick(int browserId, int x, int y, bool up); void cursorMove(int browserId, int x, int y, bool dragging); - void sendKeyEvent(CefKeyEvent& ev); + void sendKeyEvent(CefKeyEvent &ev); void loadUrl(int browserId, std::string url); void goForward(int browserId); void goBack(int browserId); @@ -161,15 +177,15 @@ public CefRenderHandler{ void imeCommitText(int browserId, std::string text); void setClientFocus(int browserId, bool focus); - void setCookie(const std::string& domain, const std::string& key, const std::string& value); - void deleteCookie(const std::string& domain, const std::string& key); + void setCookie(const std::string &domain, const std::string &key, const std::string &value); + void deleteCookie(const std::string &domain, const std::string &key); void visitAllCookies(std::function>)> callback); - void visitUrlCookies(const std::string& domain, const bool& isHttpOnly, std::function>)> callback); + void visitUrlCookies(const std::string &domain, const bool &isHttpOnly, std::function>)> callback); void setJavaScriptChannels(int browserId, const std::vector channels); void sendJavaScriptChannelCallBack(const bool error, const std::string result, const std::string callbackId, const int browserId, const std::string frameId); void executeJavaScript(int browserId, const std::string code, std::function)> callback = nullptr); - + private: // List of existing browser windows. Only accessed on the CEF UI thread. std::unordered_map browser_map_; @@ -177,7 +193,6 @@ public CefRenderHandler{ std::unordered_map)>> js_callbacks_; // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(WebviewHandler); - }; -#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_ +#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_ diff --git a/common/webview_plugin.cc b/common/webview_plugin.cc index 737a03fe..f46a75d7 100644 --- a/common/webview_plugin.cc +++ b/common/webview_plugin.cc @@ -9,40 +9,174 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include "include/wrapper/cef_closure_task.h" + +namespace +{ + class LambdaTask : public CefTask + { + public: + explicit LambdaTask(std::function f) : f_(std::move(f)) {} + void Execute() override + { + if (f_) + f_(); + } -namespace webview_cef { + private: + std::function f_; + IMPLEMENT_REFCOUNTING(LambdaTask); + }; + inline void RunOnCefUI(std::function fn) + { + if (CefCurrentlyOn(TID_UI)) + { + fn(); + } + else + { + CefPostTask(TID_UI, new LambdaTask(std::move(fn))); + } + } + inline void RunOnCefIO(std::function fn) + { + if (CefCurrentlyOn(TID_IO)) + { + fn(); + } + else + { + CefPostTask(TID_IO, new LambdaTask(std::move(fn))); + } + } + + // Execute on CEF UI thread and block until completion (with short timeout). + inline void RunOnCefUISync(std::function fn) + { + if (CefCurrentlyOn(TID_UI)) + { + fn(); + return; + } + std::mutex mtx; + std::condition_variable cv; + bool done = false; + CefPostTask(TID_UI, new LambdaTask([&]() + { + fn(); + std::unique_lock lk(mtx); + done = true; + cv.notify_one(); })); + std::unique_lock lk(mtx); + cv.wait_for(lk, std::chrono::seconds(2), [&]() + { return done; }); + } +} + +namespace webview_cef +{ CefMainArgs mainArgs; CefRefPtr app; CefString userAgent; + CefString cachePath; + bool enableGPU = false; + bool persistSessionCookies = false; + bool persistUserPreferences = false; bool isCefInitialized = false; + // atexit-based shutdown is disabled for Flutter integration. + static std::atomic s_openBrowsers{0}; + static std::atomic s_shutdownRequested{false}; + static std::mutex s_handlerMutex; + static std::vector> s_handlers; - WebviewPlugin::WebviewPlugin() { + // no-op + + WebviewPlugin::WebviewPlugin() + { m_handler = new WebviewHandler(); - } + { + std::lock_guard lock(s_handlerMutex); + s_handlers.push_back(m_handler); + } + } - WebviewPlugin::~WebviewPlugin() { + WebviewPlugin::~WebviewPlugin() + { uninitCallback(); - m_handler->CloseAllBrowsers(true); + RunOnCefUI([=]() + { + if (m_handler.get()) m_handler->CloseAllBrowsers(true); }); + { + std::lock_guard lock(s_handlerMutex); + s_handlers.erase(std::remove(s_handlers.begin(), s_handlers.end(), m_handler), s_handlers.end()); + } m_handler = nullptr; - if(!m_renderers.empty()){ + if (!m_renderers.empty()) + { m_renderers.clear(); } } - void WebviewPlugin::initCallback() { + void WebviewPlugin::initCallback() + { if (!m_init) { - m_handler->onPaintCallback = [=](int browserId, const void* buffer, int32_t width, int32_t height) { - if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) { + // One-time wiring from CEF events to lifecycle counters. + m_handler->onLoadStart = [=](int nBrowserId, std::string urlId) + { + if (m_invokeFunc) + { + WValue *bId = webview_value_new_int(nBrowserId); + WValue *uId = webview_value_new_string(const_cast(urlId.c_str())); + WValue *retMap = webview_value_new_map(); + webview_value_set_string(retMap, "browserId", bId); + webview_value_set_string(retMap, "urlId", uId); + m_invokeFunc("onLoadStart", retMap); + webview_value_unref(bId); + webview_value_unref(uId); + webview_value_unref(retMap); + } + }; + + m_handler->onLoadEnd = [=](int nBrowserId, std::string urlId) + { + if (m_invokeFunc) + { + WValue *bId = webview_value_new_int(nBrowserId); + WValue *uId = webview_value_new_string(const_cast(urlId.c_str())); + WValue *retMap = webview_value_new_map(); + webview_value_set_string(retMap, "browserId", bId); + webview_value_set_string(retMap, "urlId", uId); + m_invokeFunc("onLoadEnd", retMap); + webview_value_unref(bId); + webview_value_unref(uId); + webview_value_unref(retMap); + } + }; + + m_handler->onPaintCallback = [=](int browserId, const void *buffer, int32_t width, int32_t height) + { + if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) + { m_renderers[browserId]->onFrame(buffer, width, height); } }; - m_handler->onTooltipEvent = [=](int browserId, std::string text) { - if (m_invokeFunc) { - WValue* bId = webview_value_new_int(browserId); - WValue* wText = webview_value_new_string(const_cast(text.c_str())); - WValue* retMap = webview_value_new_map(); + m_handler->onTooltipEvent = [=](int browserId, std::string text) + { + if (m_invokeFunc) + { + WValue *bId = webview_value_new_int(browserId); + WValue *wText = webview_value_new_string(const_cast(text.c_str())); + WValue *retMap = webview_value_new_map(); webview_value_set_string(retMap, "browserId", bId); webview_value_set_string(retMap, "text", wText); m_invokeFunc("onTooltip", retMap); @@ -52,11 +186,13 @@ namespace webview_cef { } }; - m_handler->onCursorChangedEvent = [=](int browserId, int type) { - if(m_invokeFunc){ - WValue* bId = webview_value_new_int(browserId); - WValue* wType = webview_value_new_int(type); - WValue* retMap = webview_value_new_map(); + m_handler->onCursorChangedEvent = [=](int browserId, int type) + { + if (m_invokeFunc) + { + WValue *bId = webview_value_new_int(browserId); + WValue *wType = webview_value_new_int(type); + WValue *retMap = webview_value_new_map(); webview_value_set_string(retMap, "browserId", bId); webview_value_set_string(retMap, "type", wType); m_invokeFunc("onCursorChanged", retMap); @@ -66,14 +202,16 @@ namespace webview_cef { } }; - m_handler->onConsoleMessageEvent = [=](int browserId, int level, std::string message, std::string source, int line){ - if(m_invokeFunc){ - WValue* bId = webview_value_new_int(browserId); - WValue* wLevel = webview_value_new_int(level); - WValue* wMessage = webview_value_new_string(const_cast(message.c_str())); - WValue* wSource = webview_value_new_string(const_cast(source.c_str())); - WValue* wLine = webview_value_new_int(line); - WValue* retMap = webview_value_new_map(); + m_handler->onConsoleMessageEvent = [=](int browserId, int level, std::string message, std::string source, int line) + { + if (m_invokeFunc) + { + WValue *bId = webview_value_new_int(browserId); + WValue *wLevel = webview_value_new_int(level); + WValue *wMessage = webview_value_new_string(const_cast(message.c_str())); + WValue *wSource = webview_value_new_string(const_cast(source.c_str())); + WValue *wLine = webview_value_new_int(line); + WValue *retMap = webview_value_new_map(); webview_value_set_string(retMap, "browserId", bId); webview_value_set_string(retMap, "level", wLevel); webview_value_set_string(retMap, "message", wMessage); @@ -93,9 +231,9 @@ namespace webview_cef { { if (m_invokeFunc) { - WValue* bId = webview_value_new_int(browserId); - WValue* wUrl = webview_value_new_string(const_cast(url.c_str())); - WValue* retMap = webview_value_new_map(); + WValue *bId = webview_value_new_int(browserId); + WValue *wUrl = webview_value_new_string(const_cast(url.c_str())); + WValue *retMap = webview_value_new_map(); webview_value_set_string(retMap, "browserId", bId); webview_value_set_string(retMap, "url", wUrl); m_invokeFunc("urlChanged", retMap); @@ -109,9 +247,9 @@ namespace webview_cef { { if (m_invokeFunc) { - WValue* bId = webview_value_new_int(browserId); - WValue* wTitle = webview_value_new_string(const_cast(title.c_str())); - WValue* retMap = webview_value_new_map(); + WValue *bId = webview_value_new_int(browserId); + WValue *wTitle = webview_value_new_string(const_cast(title.c_str())); + WValue *retMap = webview_value_new_map(); webview_value_set_string(retMap, "browserId", bId); webview_value_set_string(retMap, "title", wTitle); m_invokeFunc("titleChanged", retMap); @@ -125,12 +263,12 @@ namespace webview_cef { { if (m_invokeFunc) { - WValue* retMap = webview_value_new_map(); - WValue* channel = webview_value_new_string(const_cast(channelName.c_str())); - WValue* msg = webview_value_new_string(const_cast(message.c_str())); - WValue* cbId = webview_value_new_string(const_cast(callbackId.c_str())); - WValue* bId = webview_value_new_int(browserId); - WValue* fId = webview_value_new_string(const_cast(frameId.c_str())); + WValue *retMap = webview_value_new_map(); + WValue *channel = webview_value_new_string(const_cast(channelName.c_str())); + WValue *msg = webview_value_new_string(const_cast(message.c_str())); + WValue *cbId = webview_value_new_string(const_cast(callbackId.c_str())); + WValue *bId = webview_value_new_int(browserId); + WValue *fId = webview_value_new_string(const_cast(frameId.c_str())); webview_value_set_string(retMap, "channel", channel); webview_value_set_string(retMap, "message", msg); webview_value_set_string(retMap, "callbackId", cbId); @@ -150,9 +288,9 @@ namespace webview_cef { { if (m_invokeFunc) { - WValue* bId = webview_value_new_int(int64_t(nBrowserId)); - WValue* editable = webview_value_new_bool(bEditable); - WValue* retMap = webview_value_new_map(); + WValue *bId = webview_value_new_int(int64_t(nBrowserId)); + WValue *editable = webview_value_new_bool(bEditable); + WValue *retMap = webview_value_new_map(); webview_value_set_string(retMap, "browserId", bId); webview_value_set_string(retMap, "editable", editable); m_invokeFunc("onFocusedNodeChangeMessage", retMap); @@ -166,10 +304,10 @@ namespace webview_cef { { if (m_invokeFunc) { - WValue* bId = webview_value_new_int(nBrowserId); - WValue* retMap = webview_value_new_map(); - WValue* xValue = webview_value_new_int(x); - WValue* yValue = webview_value_new_int(y); + WValue *bId = webview_value_new_int(nBrowserId); + WValue *retMap = webview_value_new_map(); + WValue *xValue = webview_value_new_int(x); + WValue *yValue = webview_value_new_int(y); webview_value_set_string(retMap, "browserId", bId); webview_value_set_string(retMap, "x", xValue); webview_value_set_string(retMap, "y", yValue); @@ -181,44 +319,12 @@ namespace webview_cef { } }; - - m_handler->onLoadStart = [=](int nBrowserId, std::string urlId) - { - if (m_invokeFunc) - { - WValue* bId = webview_value_new_int(nBrowserId); - WValue* uId = webview_value_new_string(const_cast(urlId.c_str())); - WValue* retMap = webview_value_new_map(); - webview_value_set_string(retMap, "browserId", bId); - webview_value_set_string(retMap, "urlId", uId); - m_invokeFunc("onLoadStart", retMap); - webview_value_unref(bId); - webview_value_unref(uId); - webview_value_unref(retMap); - } - }; - - m_handler->onLoadEnd = [=](int nBrowserId, std::string urlId) - { - if (m_invokeFunc) - { - WValue* bId = webview_value_new_int(nBrowserId); - WValue* uId = webview_value_new_string(const_cast(urlId.c_str())); - WValue* retMap = webview_value_new_map(); - webview_value_set_string(retMap, "browserId", bId); - webview_value_set_string(retMap, "urlId", uId); - m_invokeFunc("onLoadEnd", retMap); - webview_value_unref(bId); - webview_value_unref(uId); - webview_value_unref(retMap); - } - }; - m_init = true; } } - void WebviewPlugin::uninitCallback(){ + void WebviewPlugin::uninitCallback() + { m_handler->onPaintCallback = nullptr; m_handler->onTooltipEvent = nullptr; m_handler->onCursorChangedEvent = nullptr; @@ -231,198 +337,308 @@ namespace webview_cef { m_init = false; } - - void WebviewPlugin::HandleMethodCall(std::string name, WValue* values, std::function result) { - if (name.compare("init") == 0){ - if(!isCefInitialized){ - if(values != nullptr){ - userAgent = CefString(webview_value_get_string(values)); + void WebviewPlugin::HandleMethodCall(std::string name, WValue *values, std::function result) + { + if (name.compare("init") == 0) + { + if (!isCefInitialized) + { + if (values != nullptr) + { + // Support both legacy string (userAgent) and new map options. + WValueType vtype = webview_value_get_type(values); + if (vtype == Webview_Value_Type_String) + { + userAgent = CefString(webview_value_get_string(values)); + } + else if (vtype == Webview_Value_Type_Map) + { + // userAgent + if (auto ua = webview_value_get_by_string(values, "userAgent")) + { + if (webview_value_get_type(ua) == Webview_Value_Type_String) + { + userAgent = CefString(webview_value_get_string(ua)); + } + } + // cachePath + if (auto cp = webview_value_get_by_string(values, "cachePath")) + { + if (webview_value_get_type(cp) == Webview_Value_Type_String) + { + cachePath = CefString(webview_value_get_string(cp)); + } + } + // persist flags + if (auto psc = webview_value_get_by_string(values, "persistSessionCookies")) + { + if (webview_value_get_type(psc) == Webview_Value_Type_Bool) + { + persistSessionCookies = webview_value_get_bool(psc); + } + } + if (auto pup = webview_value_get_by_string(values, "persistUserPreferences")) + { + if (webview_value_get_type(pup) == Webview_Value_Type_Bool) + { + persistUserPreferences = webview_value_get_bool(pup); + } + } + // enableGPU + if (auto egpu = webview_value_get_by_string(values, "enableGPU")) + { + if (webview_value_get_type(egpu) == Webview_Value_Type_Bool) + { + enableGPU = webview_value_get_bool(egpu); + } + } + } + } + // Configure optional runtime toggles + if (app.get()) + { + app->SetEnableGPU(enableGPU); } startCEF(); } initCallback(); result(1, nullptr); } - else if (name.compare("quit") == 0) { - //only call this method when you want to quit the app + else if (name.compare("quit") == 0) + { + // Release plugin-held references before shutting down CEF to avoid + // "Object reference incorrectly held at CefShutdown" fatals. + uninitCallback(); + // Drop renderers on the plugin side. + if (!m_renderers.empty()) + { + m_renderers.clear(); + } + // Ask existing browsers to close on the CEF UI thread. + // Close browsers synchronously on the CEF UI thread. + RunOnCefUISync([this]() + { + if (m_handler.get()) m_handler->CloseAllBrowsers(true); }); + // Finally drop the handler reference held by the plugin. + m_handler = nullptr; + // Now perform shutdown. stopCEF(); result(1, nullptr); } - else if (name.compare("create") == 0) { + else if (name.compare("create") == 0) + { std::string url = webview_value_get_string(values); - m_handler->createBrowser(url, [=](int browserId) { + RunOnCefUI([=]() + { m_handler->createBrowser(url, [=](int browserId) + { std::shared_ptr renderer = m_createTextureFunc(); m_renderers[browserId] = renderer; WValue *response = webview_value_new_list(); webview_value_append(response, webview_value_new_int(browserId)); webview_value_append(response, webview_value_new_int(renderer->textureId)); result(1, response); - webview_value_unref(response); - }); + webview_value_unref(response); }); }); } - else if (name.compare("close") == 0) { + else if (name.compare("close") == 0) + { int browserId = int(webview_value_get_int(values)); - m_handler->closeBrowser(browserId); - if(m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) { + RunOnCefUI([=]() + { m_handler->closeBrowser(browserId); }); + if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) + { m_renderers[browserId].reset(); } result(1, nullptr); } - else if (name.compare("loadUrl") == 0) { + else if (name.compare("loadUrl") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); const auto url = webview_value_get_string(webview_value_get_list_value(values, 1)); - if(url != nullptr){ - m_handler->loadUrl(browserId, url); + if (url != nullptr) + { + RunOnCefUI([=]() + { m_handler->loadUrl(browserId, url); }); result(1, nullptr); } } - else if (name.compare("setSize") == 0) { + else if (name.compare("setSize") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); const auto dpi = webview_value_get_double(webview_value_get_list_value(values, 1)); const auto width = webview_value_get_double(webview_value_get_list_value(values, 2)); const auto height = webview_value_get_double(webview_value_get_list_value(values, 3)); - m_handler->changeSize(browserId, (float)dpi, (int)std::round(width), (int)std::round(height)); + RunOnCefUI([=]() + { m_handler->changeSize(browserId, (float)dpi, (int)std::round(width), (int)std::round(height)); }); result(1, nullptr); } - else if (name.compare("cursorClickDown") == 0 - || name.compare("cursorClickUp") == 0 - || name.compare("cursorMove") == 0 - || name.compare("cursorDragging") == 0) { + else if (name.compare("cursorClickDown") == 0 || name.compare("cursorClickUp") == 0 || name.compare("cursorMove") == 0 || name.compare("cursorDragging") == 0) + { result(cursorAction(values, name), nullptr); } - else if (name.compare("setScrollDelta") == 0) { + else if (name.compare("setScrollDelta") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); auto x = webview_value_get_int(webview_value_get_list_value(values, 1)); auto y = webview_value_get_int(webview_value_get_list_value(values, 2)); auto deltaX = webview_value_get_int(webview_value_get_list_value(values, 3)); auto deltaY = webview_value_get_int(webview_value_get_list_value(values, 4)); - m_handler->sendScrollEvent(browserId, (int)x, (int)y, (int)deltaX, (int)deltaY); + RunOnCefUI([=]() + { m_handler->sendScrollEvent(browserId, (int)x, (int)y, (int)deltaX, (int)deltaY); }); result(1, nullptr); } - else if (name.compare("goForward") == 0) { + else if (name.compare("goForward") == 0) + { int browserId = int(webview_value_get_int(values)); - m_handler->goForward(browserId); + RunOnCefUI([=]() + { m_handler->goForward(browserId); }); result(1, nullptr); } - else if (name.compare("goBack") == 0) { + else if (name.compare("goBack") == 0) + { int browserId = int(webview_value_get_int(values)); - m_handler->goBack(browserId); + RunOnCefUI([=]() + { m_handler->goBack(browserId); }); result(1, nullptr); } - else if (name.compare("reload") == 0) { + else if (name.compare("reload") == 0) + { int browserId = int(webview_value_get_int(values)); - m_handler->reload(browserId); + RunOnCefUI([=]() + { m_handler->reload(browserId); }); result(1, nullptr); } - else if (name.compare("openDevTools") == 0) { + else if (name.compare("openDevTools") == 0) + { int browserId = int(webview_value_get_int(values)); - m_handler->openDevTools(browserId); + RunOnCefUI([=]() + { m_handler->openDevTools(browserId); }); result(1, nullptr); } - else if (name.compare("imeSetComposition") == 0) { + else if (name.compare("imeSetComposition") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); const auto text = webview_value_get_string(webview_value_get_list_value(values, 1)); - m_handler->imeSetComposition(browserId, text); + RunOnCefUI([=]() + { m_handler->imeSetComposition(browserId, text); }); result(1, nullptr); - } - else if (name.compare("imeCommitText") == 0) { + } + else if (name.compare("imeCommitText") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); const auto text = webview_value_get_string(webview_value_get_list_value(values, 1)); - m_handler->imeCommitText(browserId, text); + RunOnCefUI([=]() + { m_handler->imeCommitText(browserId, text); }); result(1, nullptr); - } - else if (name.compare("setClientFocus") == 0) { + } + else if (name.compare("setClientFocus") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); - if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) { + if (m_renderers.find(browserId) != m_renderers.end() && m_renderers[browserId] != nullptr) + { m_renderers[browserId].get()->isFocused = webview_value_get_bool(webview_value_get_list_value(values, 1)); - m_handler->setClientFocus(browserId, m_renderers[browserId].get()->isFocused); + RunOnCefUI([=]() + { m_handler->setClientFocus(browserId, m_renderers[browserId].get()->isFocused); }); } result(1, nullptr); } - else if(name.compare("setCookie") == 0){ + else if (name.compare("setCookie") == 0) + { const auto domain = webview_value_get_string(webview_value_get_list_value(values, 0)); const auto key = webview_value_get_string(webview_value_get_list_value(values, 1)); const auto value = webview_value_get_string(webview_value_get_list_value(values, 2)); - m_handler->setCookie(domain, key, value); + RunOnCefIO([=]() + { m_handler->setCookie(domain, key, value); }); result(1, nullptr); } - else if (name.compare("deleteCookie") == 0) { + else if (name.compare("deleteCookie") == 0) + { const auto domain = webview_value_get_string(webview_value_get_list_value(values, 0)); const auto key = webview_value_get_string(webview_value_get_list_value(values, 1)); - m_handler->deleteCookie(domain, key); + RunOnCefIO([=]() + { m_handler->deleteCookie(domain, key); }); result(1, nullptr); } - else if (name.compare("visitAllCookies") == 0) { - m_handler->visitAllCookies([=](std::map> cookies){ - WValue* retMap = webview_value_new_map(); - for (auto &cookie : cookies) - { - WValue* tempMap = webview_value_new_map(); - for (auto &c : cookie.second) - { - WValue * val = webview_value_new_string(const_cast(c.second.c_str())); - webview_value_set_string(tempMap, c.first.c_str(), val); - webview_value_unref(val); + else if (name.compare("visitAllCookies") == 0) + { + RunOnCefIO([=]() + { m_handler->visitAllCookies([=](std::map> cookies) + { + WValue* retMap = webview_value_new_map(); + for (auto &cookie : cookies) { + WValue* tempMap = webview_value_new_map(); + for (auto &c : cookie.second) { + WValue * val = webview_value_new_string(const_cast(c.second.c_str())); + webview_value_set_string(tempMap, c.first.c_str(), val); + webview_value_unref(val); + } + webview_value_set_string(retMap, cookie.first.c_str(), tempMap); + webview_value_unref(tempMap); } - webview_value_set_string(retMap, cookie.first.c_str(), tempMap); - webview_value_unref(tempMap); - } - result(1, retMap); - webview_value_unref(retMap); - }); + result(1, retMap); + webview_value_unref(retMap); }); }); } - else if (name.compare("visitUrlCookies") == 0) { + else if (name.compare("visitUrlCookies") == 0) + { const auto domain = webview_value_get_string(webview_value_get_list_value(values, 0)); const auto isHttpOnly = webview_value_get_bool(webview_value_get_list_value(values, 1)); - m_handler->visitUrlCookies(domain, isHttpOnly,[=](std::map> cookies){ - WValue* retMap = webview_value_new_map(); - for (auto &cookie : cookies) - { - WValue* tempMap = webview_value_new_map(); - for (auto &c : cookie.second) - { - WValue * val = webview_value_new_string(const_cast(c.second.c_str())); - webview_value_set_string(tempMap, c.first.c_str(), val); - webview_value_unref(val); + RunOnCefIO([=]() + { m_handler->visitUrlCookies(domain, isHttpOnly, [=](std::map> cookies) + { + WValue* retMap = webview_value_new_map(); + for (auto &cookie : cookies) { + WValue* tempMap = webview_value_new_map(); + for (auto &c : cookie.second) { + WValue * val = webview_value_new_string(const_cast(c.second.c_str())); + webview_value_set_string(tempMap, c.first.c_str(), val); + webview_value_unref(val); + } + webview_value_set_string(retMap, cookie.first.c_str(), tempMap); + webview_value_unref(tempMap); } - webview_value_set_string(retMap, cookie.first.c_str(), tempMap); - webview_value_unref(tempMap); - } - result(1, retMap); - webview_value_unref(retMap); - }); + result(1, retMap); + webview_value_unref(retMap); }); }); } - else if(name.compare("setJavaScriptChannels") == 0){ + else if (name.compare("setJavaScriptChannels") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); WValue *list = webview_value_get_list_value(values, 1); auto len = webview_value_get_len(list); std::vector channels; - for(size_t i = 0; i < len; i++){ + for (size_t i = 0; i < len; i++) + { auto channel = webview_value_get_string(webview_value_get_list_value(list, i)); channels.push_back(channel); } - m_handler->setJavaScriptChannels(browserId, channels); + RunOnCefUI([=]() + { m_handler->setJavaScriptChannels(browserId, channels); }); result(1, nullptr); } - else if (name.compare("sendJavaScriptChannelCallBack") == 0) { + else if (name.compare("sendJavaScriptChannelCallBack") == 0) + { const auto error = webview_value_get_bool(webview_value_get_list_value(values, 0)); const auto ret = webview_value_get_string(webview_value_get_list_value(values, 1)); const auto callbackId = webview_value_get_string(webview_value_get_list_value(values, 2)); const auto browserId = int(webview_value_get_int(webview_value_get_list_value(values, 3))); const auto frameId = webview_value_get_string(webview_value_get_list_value(values, 4)); - m_handler->sendJavaScriptChannelCallBack(error, ret, callbackId, browserId, frameId); + RunOnCefUI([=]() + { m_handler->sendJavaScriptChannelCallBack(error, ret, callbackId, browserId, frameId); }); result(1, nullptr); } - else if(name.compare("executeJavaScript") == 0){ + else if (name.compare("executeJavaScript") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); const auto code = webview_value_get_string(webview_value_get_list_value(values, 1)); - m_handler->executeJavaScript(browserId, code); + RunOnCefUI([=]() + { m_handler->executeJavaScript(browserId, code); }); result(1, nullptr); } - else if(name.compare("evaluateJavascript") == 0){ + else if (name.compare("evaluateJavascript") == 0) + { int browserId = int(webview_value_get_int(webview_value_get_list_value(values, 0))); const auto code = webview_value_get_string(webview_value_get_list_value(values, 1)); - m_handler->executeJavaScript(browserId, code, [=](CefRefPtr values){ + RunOnCefUI([=]() + { m_handler->executeJavaScript(browserId, code, [=](CefRefPtr values) + { WValue* retValue; if (values == nullptr) { @@ -474,27 +690,35 @@ namespace webview_cef { } result(1, retValue); - webview_value_unref(retValue); - }); + webview_value_unref(retValue); }); }); } - else { + else + { result = 0; } } - void WebviewPlugin::sendKeyEvent(CefKeyEvent& ev) + void WebviewPlugin::sendKeyEvent(CefKeyEvent &ev) { - m_handler->sendKeyEvent(ev); - if(ev.type == KEYEVENT_RAWKEYDOWN && ev.windows_key_code == 0x7B && (ev.modifiers & EVENTFLAG_CONTROL_DOWN) != 0){ - for(auto render : m_renderers){ - if(render.second.get()->isFocused){ - m_handler->openDevTools(render.first); + // Ensure key events are sent on the CEF UI thread. + CefKeyEvent ev_copy = ev; + RunOnCefUI([this, ev_copy]() mutable + { m_handler->sendKeyEvent(ev_copy); }); + if (ev.type == KEYEVENT_RAWKEYDOWN && ev.windows_key_code == 0x7B && (ev.modifiers & EVENTFLAG_CONTROL_DOWN) != 0) + { + for (auto render : m_renderers) + { + if (render.second.get()->isFocused) + { + RunOnCefUI([=]() + { m_handler->openDevTools(render.first); }); } } } } - void WebviewPlugin::setInvokeMethodFunc(std::function func){ + void WebviewPlugin::setInvokeMethodFunc(std::function func) + { m_invokeFunc = func; } @@ -502,37 +726,51 @@ namespace webview_cef { { m_createTextureFunc = func; } - - bool WebviewPlugin::getAnyBrowserFocused(){ - for(auto render : m_renderers){ - if(render.second != nullptr && render.second.get()->isFocused){ + + bool WebviewPlugin::getAnyBrowserFocused() + { + for (auto render : m_renderers) + { + if (render.second != nullptr && render.second.get()->isFocused) + { return true; } } return false; } - - int WebviewPlugin::cursorAction(WValue *args, std::string name) { - if (!args || webview_value_get_len(args) != 3) { + + int WebviewPlugin::cursorAction(WValue *args, std::string name) + { + if (!args || webview_value_get_len(args) != 3) + { return 0; } int browserId = int(webview_value_get_int(webview_value_get_list_value(args, 0))); int x = int(webview_value_get_int(webview_value_get_list_value(args, 1))); int y = int(webview_value_get_int(webview_value_get_list_value(args, 2))); - if (!x && !y) { + if (!x && !y) + { return 0; } - if (name.compare("cursorClickDown") == 0) { - m_handler->cursorClick(browserId, x, y, false); + if (name.compare("cursorClickDown") == 0) + { + RunOnCefUI([=]() + { m_handler->cursorClick(browserId, x, y, false); }); } - else if (name.compare("cursorClickUp") == 0) { - m_handler->cursorClick(browserId, x, y, true); + else if (name.compare("cursorClickUp") == 0) + { + RunOnCefUI([=]() + { m_handler->cursorClick(browserId, x, y, true); }); } - else if (name.compare("cursorMove") == 0) { - m_handler->cursorMove(browserId, x, y, false); + else if (name.compare("cursorMove") == 0) + { + RunOnCefUI([=]() + { m_handler->cursorMove(browserId, x, y, false); }); } - else if (name.compare("cursorDragging") == 0) { - m_handler->cursorMove(browserId, x, y, true); + else if (name.compare("cursorDragging") == 0) + { + RunOnCefUI([=]() + { m_handler->cursorMove(browserId, x, y, true); }); } return 1; } @@ -547,7 +785,8 @@ namespace webview_cef { { #ifdef OS_MAC CefScopedLibraryLoader loader; - if(!loader.LoadInMain()) { + if (!loader.LoadInMain()) + { printf("load cef err"); } #endif @@ -561,44 +800,137 @@ namespace webview_cef { CefSettings cefs; cefs.windowless_rendering_enabled = true; cefs.no_sandbox = true; - if(!userAgent.empty()){ + if (!userAgent.empty()) + { CefString(&cefs.user_agent_product) = userAgent; } - //locale language setting - //CefString(&cefs.locale) = "zh-CN"; + if (!cachePath.empty()) + { + CefString(&cefs.cache_path) = cachePath; + } + // Encourage unique root cache path to avoid singleton collisions (warning seen at runtime). + if (!cachePath.empty()) + { + CefString(&cefs.root_cache_path) = cachePath; + } + cefs.persist_session_cookies = persistSessionCookies; + // Some CEF builds do not expose persist_user_preferences on CefSettings. + // If needed, this can be implemented via CefRequestContextSettings per-context. + // locale language setting + // CefString(&cefs.locale) = "zh-CN"; #ifdef OS_MAC - //cef message loop handle by MainApplication on mac + // cef message loop handle by MainApplication on mac cefs.external_message_pump = true; - //CefString(&cefs.browser_subprocess_path) = "/Library/Chaches"; //the helper Program path + // CefString(&cefs.browser_subprocess_path) = "/Library/Chaches"; //the helper Program path + +#else +#ifdef __linux__ + // Use CEF's multi-threaded message loop on Linux. + cefs.multi_threaded_message_loop = true; #else - //cef message run in another thread on windows/linux + // cef message run in another thread on windows cefs.multi_threaded_message_loop = true; #endif - CefInitialize(mainArgs, cefs, app.get(), nullptr); +#endif + if (CefInitialize(mainArgs, cefs, app.get(), nullptr)) + { + isCefInitialized = true; + // Disable atexit shutdown for Flutter apps; lifecycle managed explicitly via 'quit'. + } } - void doMessageLoopWork(){ + void doMessageLoopWork() + { CefDoMessageLoopWork(); } - void SwapBufferFromBgraToRgba(void* _dest, const void* _src, int width, int height) { - int32_t* dest = (int32_t*)_dest; - int32_t* src = (int32_t*)_src; + bool IsCefInitialized() { return isCefInitialized; } + + void SwapBufferFromBgraToRgba(void *_dest, const void *_src, int width, int height) + { + int32_t *dest = (int32_t *)_dest; + int32_t *src = (int32_t *)_src; int32_t rgba; int32_t bgra; int length = width * height; - for (int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) + { bgra = src[i]; // BGRA in hex = 0xAARRGGBB. - rgba = (bgra & 0x00ff0000) >> 16 // Red >> Blue. - | (bgra & 0xff00ff00) // Green Alpha. - | (bgra & 0x000000ff) << 16; // Blue >> Red. + rgba = (bgra & 0x00ff0000) >> 16 // Red >> Blue. + | (bgra & 0xff00ff00) // Green Alpha. + | (bgra & 0x000000ff) << 16; // Blue >> Red. dest[i] = rgba; } } - void stopCEF() - { + static void deferred_shutdown() + { + // Attempt shutdown only when requested. + if (!s_shutdownRequested || !isCefInitialized) + return; + + // Ask all handlers to close browsers synchronously on the UI thread. + { + std::lock_guard lock(s_handlerMutex); + for (auto &h : s_handlers) + { + if (!h.get()) + continue; + RunOnCefUISync([h]() + { + if (h.get()) + h->CloseAllBrowsers(true); }); + } + } + + // Wait up to 2 seconds for browsers to close. + for (int i = 0; i < 1000; ++i) + { + if (s_openBrowsers.load() == 0) + break; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + // Drop all remaining handler references before CefShutdown to avoid + // "Object reference incorrectly held at CefShutdown" fatals. + { + std::lock_guard lock(s_handlerMutex); + s_handlers.clear(); + } + + // Release the global application reference as well. + app = nullptr; + CefShutdown(); - } + isCefInitialized = false; + s_shutdownRequested = false; + } + + void stopCEF() + { + // Mark a shutdown request and try now; if browsers still open it will complete later. + s_shutdownRequested = true; + deferred_shutdown(); + } + + bool hasOpenBrowsers() + { + return s_openBrowsers.load() > 0; + } + + void NotifyBrowserCreated() + { + s_openBrowsers.fetch_add(1); + } + + void NotifyBrowserClosed() + { + int prev = s_openBrowsers.fetch_sub(1); + if (prev <= 1) + { + // If this was the last browser, attempt deferred shutdown. + deferred_shutdown(); + } + } } diff --git a/common/webview_plugin.h b/common/webview_plugin.h index f0337d9c..84e423ba 100644 --- a/common/webview_plugin.h +++ b/common/webview_plugin.h @@ -6,42 +6,53 @@ #include #include -namespace webview_cef { - class WebviewTexture{ +namespace webview_cef +{ + class WebviewTexture + { public: - virtual ~WebviewTexture(){} - virtual void onFrame(const void* buffer, int width, int height){} + virtual ~WebviewTexture() {} + virtual void onFrame(const void *buffer, int width, int height) {} int64_t textureId = 0; bool isFocused = false; }; - class WebviewPlugin { + class WebviewPlugin + { public: WebviewPlugin(); ~WebviewPlugin(); void initCallback(); void uninitCallback(); - void HandleMethodCall(std::string name, WValue* values, std::function result); - void sendKeyEvent(CefKeyEvent& ev); - void setInvokeMethodFunc(std::function func); + void HandleMethodCall(std::string name, WValue *values, std::function result); + void sendKeyEvent(CefKeyEvent &ev); + void setInvokeMethodFunc(std::function func); void setCreateTextureFunc(std::function()> func); bool getAnyBrowserFocused(); - private : + private: int cursorAction(WValue *args, std::string name); - std::function m_invokeFunc; - std::function()> m_createTextureFunc; + std::function m_invokeFunc; + std::function()> m_createTextureFunc; CefRefPtr m_handler; - CefRefPtr m_app; - std::unordered_map> m_renderers; - bool m_init = false; + CefRefPtr m_app; + std::unordered_map> m_renderers; + bool m_init = false; }; int initCEFProcesses(CefMainArgs args); int initCEFProcesses(); void startCEF(); void doMessageLoopWork(); - void SwapBufferFromBgraToRgba(void* _dest, const void* _src, int width, int height); + + // Returns true if CEF has been initialized. + bool IsCefInitialized(); + void SwapBufferFromBgraToRgba(void *_dest, const void *_src, int width, int height); void stopCEF(); + + // Browser lifecycle tracking to enable safe shutdown. + bool hasOpenBrowsers(); + void NotifyBrowserCreated(); + void NotifyBrowserClosed(); } -#endif //WEBVIEW_PLUGIN_H +#endif // WEBVIEW_PLUGIN_H diff --git a/example/lib/main.dart b/example/lib/main.dart index 18c8bbad..9e5a1932 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,14 +2,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:webview_cef/webview_cef.dart'; -import 'package:webview_cef/src/webview_inject_user_script.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override State createState() => _MyAppState(); @@ -64,7 +63,12 @@ class _MyAppState extends State { // Platform messages are asynchronous, so we initialize in an async method. Future initPlatformState() async { - await WebviewManager().initialize(userAgent: "test/userAgent"); + await WebviewManager().initialize( + userAgent: "test/userAgent", + enableGPU: true, + cachePath: "/tmp/webview_cef_cache", + persistSessionCookies: true, + persistUserPreferences: true); String url = "www.baidu.com"; _textController.text = url; //unified interface for all platforms set user agent @@ -80,7 +84,7 @@ class _MyAppState extends State { JavascriptChannel( name: 'Print', onMessageReceived: (JavascriptMessage message) { - print(message.message); + debugPrint(message.message); _controller.sendJavaScriptChannelCallBack( false, "{'code':'200','message':'print succeed!'}", @@ -94,13 +98,13 @@ class _MyAppState extends State { _controller.executeJavaScript("function abc(e){return 'abc:'+ e}"); _controller .evaluateJavascript("abc('test')") - .then((value) => print(value)); + .then((value) => debugPrint(value)); }, onLoadStart: (controller, url) { - print("onLoadStart => $url"); + debugPrint("onLoadStart => $url"); }, onLoadEnd: (controller, url) { - print("onLoadEnd => $url"); + debugPrint("onLoadEnd => $url"); }, )); diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt index b010f0bf..4421cac2 100644 --- a/example/linux/CMakeLists.txt +++ b/example/linux/CMakeLists.txt @@ -8,6 +8,7 @@ set(BINARY_NAME "example") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID set(APPLICATION_ID "com.example.example") +set(DOWNLOAD_CEF_FLAVOR minimal CACHE STRING "") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 55cee472..bda9e0ee 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,7 +6,7 @@ description: Demonstrates how to use the webview_cef plugin. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.17.1 <3.0.0" + sdk: ">=2.17.1 <4.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -39,7 +39,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^2.0.0 + flutter_lints: ^6.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/lib/src/webview.dart b/lib/src/webview.dart index 96be9c4c..459d82c6 100644 --- a/lib/src/webview.dart +++ b/lib/src/webview.dart @@ -2,9 +2,8 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:webview_cef/src/webview_inject_user_script.dart'; +import 'package:flutter/widgets.dart'; import 'webview_manager.dart'; import 'webview_events_listener.dart'; @@ -12,6 +11,13 @@ import 'webview_javascript.dart'; import 'webview_textinput.dart'; import 'webview_tooltip.dart'; +class _ScrollEvent { + final Offset pos; + final int dx; + final int dy; + const _ScrollEvent(this.pos, this.dx, this.dy); +} + class WebViewController extends ValueNotifier { WebViewController(this._pluginChannel, this._index, {Widget? loading}) : super(false) { @@ -38,20 +44,27 @@ class WebViewController extends ValueNotifier { WebviewEventsListener? _listener; WebviewEventsListener? get listener => _listener; - get onJavascriptChannelMessage => (final String channelName, - final String message, final String callbackId, final String frameId) { - if (_javascriptChannels.containsKey(channelName)) { - _javascriptChannels[channelName]!.onMessageReceived( - JavascriptMessage(message, callbackId, frameId)); - } else { - print('Channel "$channelName" is not exstis'); - } - }; + void Function(String, String, String, String)? + get onJavascriptChannelMessage => ( + final String channelName, + final String message, + final String callbackId, + final String frameId, + ) { + if (_javascriptChannels.containsKey(channelName)) { + _javascriptChannels[channelName]!.onMessageReceived( + JavascriptMessage(message, callbackId, frameId), + ); + } else { + debugPrint('Channel "$channelName" is not exists'); + } + }; - get onToolTip => _onToolTip; - get onCursorChanged => _onCursorChanged; - get onFocusedNodeChangeMessage => _onFocusedNodeChangeMessage; - get onImeCompositionRangeChangedMessage => + void Function(String)? get onToolTip => _onToolTip; + void Function(int)? get onCursorChanged => _onCursorChanged; + void Function(bool)? get onFocusedNodeChangeMessage => + _onFocusedNodeChangeMessage; + void Function(int, int)? get onImeCompositionRangeChangedMessage => _onImeCompositionRangeChangedMessage; /// Initializes the underlying platform view. @@ -62,7 +75,8 @@ class WebViewController extends ValueNotifier { _creatingCompleter = Completer(); try { await WebviewManager().ready; - List args = await _pluginChannel.invokeMethod('create', url); + final List args = + await _pluginChannel.invokeMethod('create', url); _browserId = args[0] as int; _textureId = args[1] as int; WebviewManager().onBrowserCreated(_index, _browserId); @@ -76,7 +90,7 @@ class WebViewController extends ValueNotifier { return _creatingCompleter.future; } - setWebviewListener(WebviewEventsListener listener) { + void setWebviewListener(WebviewEventsListener listener) { _listener = listener; } @@ -138,8 +152,10 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel - .invokeMethod('imeSetComposition', [_browserId, composingText]); + return _pluginChannel.invokeMethod('imeSetComposition', [ + _browserId, + composingText, + ]); } Future imeCommitText(String composingText) async { @@ -147,8 +163,10 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel - .invokeMethod('imeCommitText', [_browserId, composingText]); + return _pluginChannel.invokeMethod('imeCommitText', [ + _browserId, + composingText, + ]); } Future setClientFocus(bool focus) async { @@ -163,32 +181,43 @@ class WebViewController extends ValueNotifier { if (_isDisposed) { return; } - assert(value); + await ready; _assertJavascriptChannelNamesAreUnique(channels); for (var channel in channels) { _javascriptChannels[channel.name] = channel; } - return _pluginChannel.invokeMethod('setJavaScriptChannels', - [_browserId, _extractJavascriptChannelNames(channels).toList()]); + return _pluginChannel.invokeMethod('setJavaScriptChannels', [ + _browserId, + _extractJavascriptChannelNames(channels).toList(), + ]); } Future sendJavaScriptChannelCallBack( - bool error, String result, String callbackId, String frameId) async { + bool error, + String result, + String callbackId, + String frameId, + ) async { if (_isDisposed) { return; } - assert(value); - return _pluginChannel.invokeMethod('sendJavaScriptChannelCallBack', - [error, result, callbackId, _browserId, frameId]); + await ready; + return _pluginChannel.invokeMethod('sendJavaScriptChannelCallBack', [ + error, + result, + callbackId, + _browserId, + frameId, + ]); } Future executeJavaScript(String code) async { if (_isDisposed) { return; } - assert(value); + await ready; return _pluginChannel.invokeMethod('executeJavaScript', [_browserId, code]); } @@ -196,9 +225,11 @@ class WebViewController extends ValueNotifier { if (_isDisposed) { return; } - assert(value); - return _pluginChannel - .invokeMethod('evaluateJavascript', [_browserId, code]); + await ready; + return _pluginChannel.invokeMethod('evaluateJavascript', [ + _browserId, + code, + ]); } /// Moves the virtual cursor to [position]. @@ -207,8 +238,11 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel.invokeMethod( - 'cursorMove', [_browserId, position.dx.round(), position.dy.round()]); + return _pluginChannel.invokeMethod('cursorMove', [ + _browserId, + position.dx.round(), + position.dy.round(), + ]); } Future _cursorDragging(Offset position) async { @@ -216,8 +250,11 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel.invokeMethod('cursorDragging', - [_browserId, position.dx.round(), position.dy.round()]); + return _pluginChannel.invokeMethod('cursorDragging', [ + _browserId, + position.dx.round(), + position.dy.round(), + ]); } Future _cursorClickDown(Offset position) async { @@ -225,8 +262,11 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel.invokeMethod('cursorClickDown', - [_browserId, position.dx.round(), position.dy.round()]); + return _pluginChannel.invokeMethod('cursorClickDown', [ + _browserId, + position.dx.round(), + position.dy.round(), + ]); } Future _cursorClickUp(Offset position) async { @@ -234,8 +274,11 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel.invokeMethod('cursorClickUp', - [_browserId, position.dx.round(), position.dy.round()]); + return _pluginChannel.invokeMethod('cursorClickUp', [ + _browserId, + position.dx.round(), + position.dy.round(), + ]); } /// Sets the horizontal and vertical scroll delta. @@ -244,8 +287,13 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel.invokeMethod('setScrollDelta', - [_browserId, position.dx.round(), position.dy.round(), dx, dy]); + return _pluginChannel.invokeMethod('setScrollDelta', [ + _browserId, + position.dx.round(), + position.dy.round(), + dx, + dy, + ]); } /// Sets the surface size to the provided [size]. @@ -254,8 +302,12 @@ class WebViewController extends ValueNotifier { return; } assert(value); - return _pluginChannel - .invokeMethod('setSize', [_browserId, dpi, size.width, size.height]); + return _pluginChannel.invokeMethod('setSize', [ + _browserId, + dpi, + size.width, + size.height, + ]); } Set _extractJavascriptChannelNames(Set channels) { @@ -265,23 +317,24 @@ class WebViewController extends ValueNotifier { } void _assertJavascriptChannelNamesAreUnique( - final Set? channels) { + final Set? channels, + ) { if (channels == null || channels.isEmpty) { return; } assert(_extractJavascriptChannelNames(channels).length == channels.length); } - Function(String)? _onToolTip; - Function(int)? _onCursorChanged; - Function(bool editable)? _onFocusedNodeChangeMessage; - Function(int, int)? _onImeCompositionRangeChangedMessage; + void Function(String)? _onToolTip; + void Function(int)? _onCursorChanged; + void Function(bool)? _onFocusedNodeChangeMessage; + void Function(int, int)? _onImeCompositionRangeChangedMessage; } class WebView extends StatefulWidget { final WebViewController controller; - const WebView(this.controller, {Key? key}) : super(key: key); + const WebView(this.controller, {super.key}); @override WebViewState createState() => WebViewState(); @@ -294,6 +347,14 @@ class WebViewState extends State with WebeViewTextInput { bool isPrimaryFocus = false; WebviewTooltip? _tooltip; MouseCursor _mouseType = SystemMouseCursors.basic; + // Cache last reported surface parameters to avoid redundant platform calls. + Size? _lastReportedSize; + double? _lastReportedDpi; + // Coalescing state for high-frequency input events. + Offset? _lastHoverPos; + Offset? _lastDragPos; + _ScrollEvent? _lastScroll; + bool _inputFlushScheduled = false; WebViewController get _controller => widget.controller; @@ -342,7 +403,10 @@ class WebViewState extends State with WebeViewTextInput { _controller._onImeCompositionRangeChangedMessage = (x, y) { final box = _key.currentContext!.findRenderObject() as RenderBox; updateIMEComposionPosition( - x.toDouble(), y.toDouble(), box.localToGlobal(Offset.zero)); + x.toDouble(), + y.toDouble(), + box.localToGlobal(Offset.zero), + ); }; _controller._onToolTip = (final String text) { @@ -375,8 +439,9 @@ class WebViewState extends State with WebeViewTextInput { }; // Report initial surface size - WidgetsBinding.instance - .addPostFrameCallback((_) => _reportSurfaceSize(context)); + WidgetsBinding.instance.addPostFrameCallback( + (_) => _reportSurfaceSize(context), + ); } @override @@ -413,7 +478,8 @@ class WebViewState extends State with WebeViewTextInput { child: SizeChangedLayoutNotifier( child: Listener( onPointerHover: (ev) { - _controller._cursorMove(ev.localPosition); + _lastHoverPos = ev.localPosition; + _scheduleInputFlush(); _tooltip?.cursorOffset = ev.position; }, onPointerDown: (ev) { @@ -432,17 +498,26 @@ class WebViewState extends State with WebeViewTextInput { _controller._cursorClickUp(ev.localPosition); }, onPointerMove: (ev) { - _controller._cursorDragging(ev.localPosition); + _lastDragPos = ev.localPosition; + _scheduleInputFlush(); }, onPointerSignal: (signal) { if (signal is PointerScrollEvent) { - _controller._setScrollDelta(signal.localPosition, - signal.scrollDelta.dx.round(), signal.scrollDelta.dy.round()); + _lastScroll = _ScrollEvent( + signal.localPosition, + signal.scrollDelta.dx.round(), + signal.scrollDelta.dy.round(), + ); + _scheduleInputFlush(); } }, onPointerPanZoomUpdate: (event) { - _controller._setScrollDelta(event.localPosition, - event.panDelta.dx.round(), event.panDelta.dy.round()); + _lastScroll = _ScrollEvent( + event.localPosition, + event.panDelta.dx.round(), + event.panDelta.dy.round(), + ); + _scheduleInputFlush(); }, child: MouseRegion( cursor: _mouseType, @@ -458,8 +533,39 @@ class WebViewState extends State with WebeViewTextInput { final box = _key.currentContext?.findRenderObject() as RenderBox?; if (box != null) { await _controller.ready; - unawaited( - _controller._setSize(dpi, Size(box.size.width, box.size.height))); + final Size sz = Size(box.size.width, box.size.height); + // Only notify platform when size or dpi actually changed. + if (_lastReportedSize != sz || _lastReportedDpi != dpi) { + _lastReportedSize = sz; + _lastReportedDpi = dpi; + unawaited(_controller._setSize(dpi, sz)); + } } } + + void _scheduleInputFlush() { + if (_inputFlushScheduled) return; + _inputFlushScheduled = true; + WidgetsBinding.instance.addPostFrameCallback((_) { + _inputFlushScheduled = false; + // Drain latest hover + final hover = _lastHoverPos; + if (hover != null) { + _lastHoverPos = null; + _controller._cursorMove(hover); + } + // Drain latest drag + final drag = _lastDragPos; + if (drag != null) { + _lastDragPos = null; + _controller._cursorDragging(drag); + } + // Drain latest scroll + final scroll = _lastScroll; + if (scroll != null) { + _lastScroll = null; + _controller._setScrollDelta(scroll.pos, scroll.dx, scroll.dy); + } + }); + } } diff --git a/lib/src/webview_inject_user_script.dart b/lib/src/webview_inject_user_script.dart index 51cfac28..cfc6b112 100644 --- a/lib/src/webview_inject_user_script.dart +++ b/lib/src/webview_inject_user_script.dart @@ -1,3 +1,4 @@ +// ignore: constant_identifier_names enum ScriptInjectTime { LOAD_START, LOAD_END } class UserScript { @@ -15,10 +16,14 @@ class InjectUserScripts { } List retrieveLoadStartInjectScripts() { - return userScripts.where((e) => e.injectTime == ScriptInjectTime.LOAD_START).toList(); + return userScripts + .where((e) => e.injectTime == ScriptInjectTime.LOAD_START) + .toList(); } List retrieveLoadEndInjectScripts() { - return userScripts.where((e) => e.injectTime == ScriptInjectTime.LOAD_END).toList(); + return userScripts + .where((e) => e.injectTime == ScriptInjectTime.LOAD_END) + .toList(); } -} \ No newline at end of file +} diff --git a/lib/src/webview_javascript.dart b/lib/src/webview_javascript.dart index f0c71c70..fd18e1c3 100644 --- a/lib/src/webview_javascript.dart +++ b/lib/src/webview_javascript.dart @@ -1,4 +1,5 @@ /// A message that was sent by JavaScript code running in a [WebView]. +library webview_javascript; class JavascriptMessage { /// Constructs a JavaScript message object. @@ -22,10 +23,8 @@ class JavascriptChannel { /// Constructs a Javascript channel. /// /// The parameters `name` and `onMessageReceived` must not be null. - JavascriptChannel({ - required this.name, - required this.onMessageReceived, - }) : assert(_validChannelNames.hasMatch(name)); + JavascriptChannel({required this.name, required this.onMessageReceived}) + : assert(_validChannelNames.hasMatch(name)); /// The channel's name. /// @@ -45,4 +44,4 @@ class JavascriptChannel { } /// Callback type for handling messages sent from Javascript running in a web view. -typedef void JavascriptMessageHandler(JavascriptMessage message); +typedef JavascriptMessageHandler = void Function(JavascriptMessage message); diff --git a/lib/src/webview_manager.dart b/lib/src/webview_manager.dart index f9ee6fa9..b0464f07 100644 --- a/lib/src/webview_manager.dart +++ b/lib/src/webview_manager.dart @@ -1,38 +1,48 @@ import 'dart:async'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_cef/src/webview_inject_user_script.dart'; import 'webview.dart'; +/// Singleton manager that initializes CEF and coordinates WebView instances. class WebviewManager extends ValueNotifier { static final WebviewManager _instance = WebviewManager._internal(); factory WebviewManager() => _instance; - late Completer _creatingCompleter; + // Tracks the current initialization operation; null when not started yet. + Completer? _creatingCompleter; final MethodChannel pluginChannel = const MethodChannel("webview_cef"); final Map _webViews = {}; - final Map _injectUserScripts = {}; + final Map _injectUserScripts = + {}; final Map _tempWebViews = {}; - final Map _tempInjectUserScripts = {}; + final Map _tempInjectUserScripts = + {}; int nextIndex = 1; - get ready => _creatingCompleter.future; + Future get ready => _creatingCompleter?.future ?? Future.value(); + /// Create a WebView and return its controller. + /// + /// - [loading]: Placeholder shown until the native view is ready. + /// - [injectUserScripts]: Scripts to inject automatically on LOAD_START/LOAD_END. WebViewController createWebView({ Widget? loading, InjectUserScripts? injectUserScripts, }) { int browserIndex = nextIndex++; - final controller = - WebViewController(pluginChannel, browserIndex, loading: loading); + final controller = WebViewController( + pluginChannel, + browserIndex, + loading: loading, + ); _tempWebViews[browserIndex] = controller; _tempInjectUserScripts[browserIndex] = injectUserScripts; @@ -47,27 +57,84 @@ class WebviewManager extends ValueNotifier { WebviewManager._internal() : super(false); - Future initialize({String? userAgent}) async { + /// Initialize the native CEF runtime (idempotent). + /// + /// Parameters: + /// - [userAgent]: Optional product string appended to the default UA. + /// - [cachePath]: Directory for the global browser cache. If empty, runs in + /// memory (incognito-like) and no data is persisted to disk. Must be writable. + /// - [persistSessionCookies]: Persist session cookies to disk. Effective only + /// when [cachePath] is non-empty. + /// - [persistUserPreferences]: Persist user preferences (JSON) in [cachePath]. + /// Effective only when [cachePath] is non-empty. + /// - [enableGPU]: Enable GPU acceleration (true/false). + Future initialize({ + String? userAgent, + String? cachePath, + bool persistSessionCookies = false, + bool persistUserPreferences = false, + bool enableGPU = false, + Duration initTimeout = const Duration(seconds: 5), + }) async { + // Idempotent: if already initialized, return immediately. + if (value) { + return Future.value(); + } + // Re-entrant: if an initialization is in-flight, await the same future. + if (_creatingCompleter?.isCompleted == false) { + return _creatingCompleter!.future; + } _creatingCompleter = Completer(); try { - if (userAgent != null && userAgent.isNotEmpty) { - await pluginChannel.invokeMethod('init', userAgent); + // Build init options map; keep legacy behavior if only userAgent provided + final hasAnyOption = (userAgent != null && userAgent.isNotEmpty) || + (cachePath != null && cachePath.isNotEmpty) || + persistSessionCookies || + persistUserPreferences || + enableGPU; + + if (hasAnyOption) { + final Map opts = {}; + if (userAgent != null && userAgent.isNotEmpty) { + opts['userAgent'] = userAgent; + } + if (cachePath != null && cachePath.isNotEmpty) { + opts['cachePath'] = cachePath; + } + if (persistSessionCookies) opts['persistSessionCookies'] = true; + if (persistUserPreferences) opts['persistUserPreferences'] = true; + if (enableGPU) opts['enableGPU'] = true; + await pluginChannel.invokeMethod('init', opts).timeout(initTimeout, + onTimeout: () { + throw PlatformException( + code: 'INIT_TIMEOUT', + message: + 'webview_cef init did not respond. Verify CEF binaries and plugin initialization on the platform side.', + ); + }); } else { - await pluginChannel.invokeMethod('init'); + await pluginChannel.invokeMethod('init').timeout(initTimeout, + onTimeout: () { + throw PlatformException( + code: 'INIT_TIMEOUT', + message: + 'webview_cef init did not respond. Verify CEF binaries and plugin initialization on the platform side.', + ); + }); } pluginChannel.setMethodCallHandler(methodCallhandler); // Wait for the platform to complete initialization. await Future.delayed(const Duration(milliseconds: 300)); - _creatingCompleter.complete(); + _creatingCompleter?.complete(); value = true; } on PlatformException catch (e) { - _creatingCompleter.completeError(e); + _creatingCompleter?.completeError(e); } - return _creatingCompleter.future; + return _creatingCompleter?.future; } @override - Future dispose() async { + void dispose() { super.dispose(); pluginChannel.setMethodCallHandler(null); _webViews.clear(); @@ -85,33 +152,33 @@ class WebviewManager extends ValueNotifier { switch (call.method) { case "urlChanged": int browserId = call.arguments["browserId"] as int; - _webViews[browserId] - ?.listener - ?.onUrlChanged - ?.call(call.arguments["url"] as String); + _webViews[browserId]?.listener?.onUrlChanged?.call( + call.arguments["url"] as String, + ); return; case "titleChanged": int browserId = call.arguments["browserId"] as int; - _webViews[browserId] - ?.listener - ?.onTitleChanged - ?.call(call.arguments["title"] as String); + _webViews[browserId]?.listener?.onTitleChanged?.call( + call.arguments["title"] as String, + ); return; case "onConsoleMessage": int browserId = call.arguments["browserId"] as int; _webViews[browserId]?.listener?.onConsoleMessage?.call( - call.arguments["level"] as int, - call.arguments["message"] as String, - call.arguments["source"] as String, - call.arguments["line"] as int); + call.arguments["level"] as int, + call.arguments["message"] as String, + call.arguments["source"] as String, + call.arguments["line"] as int, + ); return; case 'javascriptChannelMessage': int browserId = call.arguments['browserId'] as int; _webViews[browserId]?.onJavascriptChannelMessage?.call( - call.arguments['channel'] as String, - call.arguments['message'] as String, - call.arguments['callbackId'] as String, - call.arguments['frameId'] as String); + call.arguments['channel'] as String, + call.arguments['message'] as String, + call.arguments['callbackId'] as String, + call.arguments['frameId'] as String, + ); return; case 'onTooltip': int browserId = call.arguments['browserId'] as int; @@ -119,53 +186,63 @@ class WebviewManager extends ValueNotifier { return; case 'onCursorChanged': int browserId = call.arguments['browserId'] as int; - _webViews[browserId] - ?.onCursorChanged - ?.call(call.arguments['type'] as int); + _webViews[browserId]?.onCursorChanged?.call( + call.arguments['type'] as int, + ); return; case 'onFocusedNodeChangeMessage': int browserId = call.arguments['browserId'] as int; bool editable = call.arguments['editable'] as bool; - _webViews[browserId]?.onFocusedNodeChangeMessage(editable); + _webViews[browserId]?.onFocusedNodeChangeMessage?.call(editable); return; case 'onImeCompositionRangeChangedMessage': int browserId = call.arguments['browserId'] as int; - _webViews[browserId] - ?.onImeCompositionRangeChangedMessage - ?.call(call.arguments['x'] as int, call.arguments['y'] as int); + _webViews[browserId]?.onImeCompositionRangeChangedMessage?.call( + call.arguments['x'] as int, + call.arguments['y'] as int, + ); return; case 'onLoadStart': int browserId = call.arguments["browserId"] as int; String urlId = call.arguments["urlId"] as String; - await _injectUserScriptIfNeeds(browserId, _injectUserScripts[browserId]?.retrieveLoadStartInjectScripts() ?? []); + await _injectUserScriptIfNeeds( + browserId, + _injectUserScripts[browserId]?.retrieveLoadStartInjectScripts() ?? [], + ); WebViewController controller = - _webViews[browserId] as WebViewController; + _webViews[browserId] as WebViewController; _webViews[browserId]?.listener?.onLoadStart?.call(controller, urlId); return; case 'onLoadEnd': int browserId = call.arguments["browserId"] as int; String urlId = call.arguments["urlId"] as String; - await _injectUserScriptIfNeeds(browserId, _injectUserScripts[browserId]?.retrieveLoadEndInjectScripts() ?? []); + await _injectUserScriptIfNeeds( + browserId, + _injectUserScripts[browserId]?.retrieveLoadEndInjectScripts() ?? [], + ); WebViewController controller = - _webViews[browserId] as WebViewController; + _webViews[browserId] as WebViewController; _webViews[browserId]?.listener?.onLoadEnd?.call(controller, urlId); return; default: } } - Future _injectUserScriptIfNeeds(int browserId, List scripts) async { + Future _injectUserScriptIfNeeds( + int browserId, + List scripts, + ) async { if (scripts.isEmpty) return; - - await _webViews[browserId]?.ready; - - scripts.forEach((script) async { - await _webViews[browserId]?.executeJavaScript(script.script); - },); + final controller = _webViews[browserId]; + if (controller == null) return; + await controller.ready; + for (final script in scripts) { + await controller.executeJavaScript(script.script); + } } Future setCookie(String domain, String key, String val) async { @@ -184,12 +261,17 @@ class WebviewManager extends ValueNotifier { } Future visitUrlCookies(String domain, bool isHttpOnly) async { + /// Visit cookies for the specified [domain]. + /// + /// - [isHttpOnly]: If true, include HttpOnly cookies in the results. assert(value); return pluginChannel.invokeMethod('visitUrlCookies', [domain, isHttpOnly]); } Future quit() async { - //only call this method when you want to quit the app + /// Stop the global CEF runtime. + /// + /// Only call this when your app is exiting or you no longer need any webviews. assert(value); return pluginChannel.invokeMethod('quit'); } diff --git a/lib/src/webview_textinput.dart b/lib/src/webview_textinput.dart index 99039b41..eaf7249a 100644 --- a/lib/src/webview_textinput.dart +++ b/lib/src/webview_textinput.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; mixin WebeViewTextInput implements DeltaTextInputClient { @@ -11,7 +12,7 @@ mixin WebeViewTextInput implements DeltaTextInputClient { TextInputConnection? _textInputConnection; - attachTextInputClient() { + void attachTextInputClient() { _textInputConnection?.close(); _textInputConnection = TextInput.attach( this, const TextInputConfiguration(enableDeltaModel: true)); @@ -21,11 +22,11 @@ mixin WebeViewTextInput implements DeltaTextInputClient { // _textInputConnection } - detachTextInputClient() { + void detachTextInputClient() { _textInputConnection?.close(); } - updateIMEComposionPosition(double x, double y, Offset offset) { + void updateIMEComposionPosition(double x, double y, Offset offset) { /// 1.It always displays at the last position, which should be a bug in the Flutter engine. /// 2.If switch windows and switch back, this function can run well once.I think there must have a flush function called when switching windows /// 3.Windows can run well, but Linux can't. @@ -36,7 +37,7 @@ mixin WebeViewTextInput implements DeltaTextInputClient { @override didChangeInputControl( TextInputControl? oldControl, TextInputControl? newControl) { - print("changed"); + debugPrint("changed"); } @override diff --git a/lib/src/webview_tooltip.dart b/lib/src/webview_tooltip.dart index a8fb178e..2d9f7019 100644 --- a/lib/src/webview_tooltip.dart +++ b/lib/src/webview_tooltip.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; class WebviewTooltip { WebviewTooltip(BuildContext context) { - _overlayState = Overlay.of(context)!; + _overlayState = Overlay.of(context); _box = context.findRenderObject() as RenderBox; } late OverlayState _overlayState; @@ -60,7 +60,8 @@ class WebviewTooltip { color: Colors.white, borderRadius: const BorderRadius.all(Radius.circular(4)), boxShadow: [ - BoxShadow(blurRadius: 2, color: Colors.black.withOpacity(.2)) + BoxShadow( + blurRadius: 2, color: Colors.black.withValues(alpha: .2)) ], ), child: RichText( diff --git a/lib/webview_cef.dart b/lib/webview_cef.dart index 3a5fdc7f..9ed19d0b 100644 --- a/lib/webview_cef.dart +++ b/lib/webview_cef.dart @@ -3,3 +3,4 @@ export 'src/webview.dart'; export 'src/webview_events_listener.dart'; export 'src/webview_javascript.dart'; export 'src/webview_textinput.dart'; +export 'src/webview_inject_user_script.dart'; diff --git a/lib/webview_cef_method_channel.dart b/lib/webview_cef_method_channel.dart index a68dd2eb..0e21d5c5 100644 --- a/lib/webview_cef_method_channel.dart +++ b/lib/webview_cef_method_channel.dart @@ -11,7 +11,8 @@ class MethodChannelWebviewCef extends WebviewCefPlatform { @override Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); + final version = + await methodChannel.invokeMethod('getPlatformVersion'); return version; } } diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 1af6a673..30da2f78 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -20,12 +20,34 @@ message(WARNING "${patch_output}") # Setup CEF include(${CMAKE_CURRENT_SOURCE_DIR}/../third/download.cmake) +# Allow overriding from parent with -DDOWNLOAD_CEF_FLAVOR=minimal|standard +if(NOT DEFINED DOWNLOAD_CEF_FLAVOR) + set(DOWNLOAD_CEF_FLAVOR "standard") +endif() +message(STATUS "CEF download flavor: ${DOWNLOAD_CEF_FLAVOR}") prepare_prebuilt_files(${CMAKE_CURRENT_SOURCE_DIR}/../third/cef) set(CEF_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../third/cef") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CEF_ROOT}/cmake") find_package(CEF REQUIRED) +# Some prebuilt CEF packages don’t include Debug binaries on Linux. +# If the Debug lib is missing, fall back to the Release lib to allow Debug builds of the plugin. +if(NOT EXISTS "${CEF_LIB_DEBUG}" AND EXISTS "${CEF_LIB_RELEASE}") + message(WARNING "CEF Debug lib not found: ${CEF_LIB_DEBUG}. Falling back to Release lib: ${CEF_LIB_RELEASE} for Debug configuration.") + set(CEF_LIB_DEBUG "${CEF_LIB_RELEASE}") +endif() + +# Likewise, adjust CEF directories if Debug tree is missing to avoid missing chrome-sandbox and others. +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(EXISTS "${CEF_ROOT}/Release/libcef.so" AND NOT EXISTS "${CEF_ROOT}/Debug/libcef.so") + message(WARNING "CEF Debug tree not found; using Release directories for binaries and resources.") + set(CEF_BINARY_DIR "${CEF_ROOT}/Release") + # CEF_RESOURCE_DIR typically points to Resources under root. + set(CEF_RESOURCE_DIR "${CEF_ROOT}/Resources") + endif() +endif() + # This value is used when generating builds using this plugin, so it must # not be changed. set(PLUGIN_NAME "webview_cef_plugin") diff --git a/linux/webview_cef_plugin.cc b/linux/webview_cef_plugin.cc index 6de3d47f..94aef700 100644 --- a/linux/webview_cef_plugin.cc +++ b/linux/webview_cef_plugin.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include "webview_cef_keyevent.h" #include "webview_cef_texture.h" @@ -33,13 +34,33 @@ class WebviewTextureRenderer : public webview_cef::WebviewTexture { register_ = texture_register; texture = webview_cef_texture_new(); - fl_texture_registrar_register_texture(register_, FL_TEXTURE(texture)); textureId = (int64_t)texture; + struct RegData + { + FlTextureRegistrar *registrar; + WebviewCefTexture *texture; + }; + RegData *data = new RegData{register_, texture}; + g_main_context_invoke(nullptr, (GSourceFunc) + [](gpointer user_data) -> gboolean + { + std::unique_ptr d(static_cast(user_data)); + fl_texture_registrar_register_texture(d->registrar, FL_TEXTURE(d->texture)); + return G_SOURCE_REMOVE; }, data); } virtual ~WebviewTextureRenderer() { - fl_texture_registrar_unregister_texture(register_, FL_TEXTURE(texture)); + struct UnregData + { + FlTextureRegistrar *registrar; + WebviewCefTexture *texture; + }; + UnregData *data = new UnregData{register_, texture}; + g_main_context_invoke(nullptr, (GSourceFunc) + [](gpointer user_data) -> gboolean + { + std::unique_ptr d(static_cast(user_data)); + fl_texture_registrar_unregister_texture(d->registrar, FL_TEXTURE(d->texture)); + return G_SOURCE_REMOVE; }, data); register_ = nullptr; } @@ -48,10 +69,21 @@ class WebviewTextureRenderer : public webview_cef::WebviewTexture texture->width = width; texture->height = height; const auto size = width * height * 4; - delete texture->buffer; + delete[] texture->buffer; texture->buffer = new uint8_t[size]; webview_cef::SwapBufferFromBgraToRgba((void *)texture->buffer, buffer, width, height); - fl_texture_registrar_mark_texture_frame_available(register_, FL_TEXTURE(texture)); + + struct NotifyData + { + FlTextureRegistrar *registrar; + WebviewCefTexture *texture; + }; + NotifyData *data = new NotifyData{register_, texture}; + g_main_context_invoke(nullptr, (GSourceFunc) + [](gpointer user_data) -> gboolean + { + std::unique_ptr d(static_cast(user_data)); + fl_texture_registrar_mark_texture_frame_available(d->registrar, FL_TEXTURE(d->texture)); + return G_SOURCE_REMOVE; }, data); } FlTextureRegistrar *register_; WebviewCefTexture *texture; @@ -215,28 +247,62 @@ static void webview_cef_plugin_handle_method_call( const gchar *method = fl_method_call_get_name(method_call); WValue *encodeArgs = encode_flvalue_to_wvalue(fl_method_call_get_args(method_call)); g_object_ref(method_call); - self->m_plugin->HandleMethodCall(method, encodeArgs, [=](int ret, WValue *responseArgs){ - if (ret > 0){ - fl_method_call_respond_success(method_call, encode_wavlue_to_flvalue(responseArgs), nullptr); - } - else if (ret < 0){ - fl_method_call_respond_error(method_call, "error", "error", encode_wavlue_to_flvalue(responseArgs), nullptr); - } - else{ - fl_method_call_respond_not_implemented(method_call, nullptr); - } - g_object_unref(method_call); - }); + if (g_strcmp0(method, "init") == 0) + { + // Respond immediately to avoid blocking Flutter while heavy init runs. + fl_method_call_respond_success(method_call, fl_value_new_null(), nullptr); + g_object_unref(method_call); + + // Schedule actual initialization on the GTK main loop so the response is flushed first. + struct InitData + { + WebviewCefPlugin *self; + WValue *args; + }; + InitData *data = new InitData{self, webview_value_ref(encodeArgs)}; + g_main_context_invoke(nullptr, (GSourceFunc) + [](gpointer user_data) -> gboolean + { + std::unique_ptr d(static_cast(user_data)); + // Invoke the existing init handler but discard its result since we've already responded. + d->self->m_plugin->HandleMethodCall("init", d->args, [](int /*ret*/, WValue* /*responseArgs*/) {}); + webview_value_unref(d->args); + return G_SOURCE_REMOVE; }, data); + } + else + { + // For all other calls, marshal response back to GTK main thread. + self->m_plugin->HandleMethodCall(method, encodeArgs, [=](int ret, WValue *responseArgs) + { + struct ResponseData { + FlMethodCall* method_call; + int ret; + WValue* responseArgs; + }; + ResponseData* data = new ResponseData{method_call, ret, responseArgs ? webview_value_ref(responseArgs) : nullptr}; + g_main_context_invoke(nullptr, (GSourceFunc)+[](gpointer user_data) -> gboolean { + std::unique_ptr d(static_cast(user_data)); + if (d->ret > 0) { + fl_method_call_respond_success(d->method_call, d->responseArgs ? encode_wavlue_to_flvalue(d->responseArgs) : fl_value_new_null(), nullptr); + } else if (d->ret < 0) { + fl_method_call_respond_error(d->method_call, "error", "error", d->responseArgs ? encode_wavlue_to_flvalue(d->responseArgs) : fl_value_new_null(), nullptr); + } else { + fl_method_call_respond_not_implemented(d->method_call, nullptr); + } + if (d->responseArgs) webview_value_unref(d->responseArgs); + g_object_unref(d->method_call); + return G_SOURCE_REMOVE; + }, data); }); + } webview_value_unref(encodeArgs); } static void webview_cef_plugin_dispose(GObject *object) { + // Remove this window's plugin instance from the global registry first. webviewPlugins.erase(WEBVIEW_CEF_PLUGIN(object)->m_window); - WEBVIEW_CEF_PLUGIN(object)->m_plugin = nullptr; - if(webviewPlugins.empty()){ - webview_cef::stopCEF(); - } + // Explicitly release the shared_ptr to break cycles before GObject teardown. + WEBVIEW_CEF_PLUGIN(object)->m_plugin.reset(); + // CEF shutdown is handled explicitly via 'quit' or by the atexit handler. G_OBJECT_CLASS(webview_cef_plugin_parent_class)->dispose(object); } @@ -245,7 +311,8 @@ static void webview_cef_plugin_class_init(WebviewCefPluginClass *klass) G_OBJECT_CLASS(klass)->dispose = webview_cef_plugin_dispose; } -static void webview_cef_plugin_init(WebviewCefPlugin *self) { +static void webview_cef_plugin_init(WebviewCefPlugin *self) +{ self->m_plugin = std::make_shared(); } @@ -275,16 +342,29 @@ void webview_cef_plugin_register_with_registrar(FlPluginRegistrar *registrar) g_object_ref(plugin), g_object_unref); - plugin->m_plugin->setInvokeMethodFunc([=](std::string method, WValue *arguments) { - FlValue *args = encode_wavlue_to_flvalue(arguments); - fl_method_channel_invoke_method(channel, method.c_str(), args, NULL, NULL, NULL); - fl_value_unref(args); - }); + // Ensure method channel invocations happen on the platform (GTK) thread. + plugin->m_plugin->setInvokeMethodFunc([channel](std::string method, WValue *arguments) + { + struct InvokeData { + FlMethodChannel* channel; + std::string method; + WValue* args; + }; + InvokeData* data = new InvokeData{channel, method, webview_value_ref(arguments)}; - plugin->m_plugin->setCreateTextureFunc([=](){ + g_main_context_invoke(nullptr, (GSourceFunc)+[](gpointer user_data) -> gboolean { + std::unique_ptr d(static_cast(user_data)); + FlValue *args = encode_wavlue_to_flvalue(d->args); + fl_method_channel_invoke_method(d->channel, d->method.c_str(), args, NULL, NULL, NULL); + fl_value_unref(args); + webview_value_unref(d->args); + return G_SOURCE_REMOVE; + }, data); }); + + plugin->m_plugin->setCreateTextureFunc([=]() + { std::shared_ptr renderer = std::make_shared(plugin->m_textureRegister); - return std::dynamic_pointer_cast(renderer); - }); + return std::dynamic_pointer_cast(renderer); }); g_object_unref(plugin); } @@ -322,16 +402,19 @@ FLUTTER_PLUGIN_EXPORT gboolean processKeyEventForCEF(GtkWidget *widget, GdkEvent // is apparently just how webkit handles it and what it expects. key_event.unmodified_character = '\r'; } - else if((windows_key_code == KeyboardCode::VKEY_V) && (key_event.modifiers & EVENTFLAG_CONTROL_DOWN) && (event->type == GDK_KEY_PRESS)){ - //try to fix copy request freeze process problem(flutter engine will send a copy request when ctrl+v pressed) + else if ((windows_key_code == KeyboardCode::VKEY_V) && (key_event.modifiers & EVENTFLAG_CONTROL_DOWN) && (event->type == GDK_KEY_PRESS)) + { + // try to fix copy request freeze process problem(flutter engine will send a copy request when ctrl+v pressed) int res = 0; - if(system("xclip -o -sel clipboard | xclip -i -sel clipboard &>/dev/null") == 0){ + if (system("xclip -o -sel clipboard | xclip -i -sel clipboard &>/dev/null") == 0) + { res = system("xclip -o -sel clipboard | xclip -i &>/dev/null"); } // Suppress unused variable warning (void)res; } - else { + else + { // FIXME: fix for non BMP chars key_event.unmodified_character = static_cast(gdk_keyval_to_unicode(event->keyval)); diff --git a/pubspec.yaml b/pubspec.yaml index b6a29cb3..9740a12d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,12 +11,12 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.2 + plugin_platform_interface: ^2.1.8 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_lints: ^6.0.0 flutter: plugin: diff --git a/test/webview_cef_method_channel_test.dart b/test/webview_cef_method_channel_test.dart index 5257ecdf..4ffc79e4 100644 --- a/test/webview_cef_method_channel_test.dart +++ b/test/webview_cef_method_channel_test.dart @@ -6,16 +6,20 @@ void main() { MethodChannelWebviewCef platform = MethodChannelWebviewCef(); const MethodChannel channel = MethodChannel('webview_cef'); + // Ensure test binding is initialized TestWidgetsFlutterBinding.ensureInitialized(); setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; - }); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + channel, + (MethodCall methodCall) async => '42', + ); }); tearDown(() { - channel.setMockMethodCallHandler(null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); test('getPlatformVersion', () async { diff --git a/test/webview_cef_test.dart b/test/webview_cef_test.dart index fb53a113..0dd6cae4 100644 --- a/test/webview_cef_test.dart +++ b/test/webview_cef_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:webview_cef/webview_cef.dart'; +// import 'package:webview_cef/webview_cef.dart'; // Not used: wrapper class not present in this package import 'package:webview_cef/webview_cef_platform_interface.dart'; import 'package:webview_cef/webview_cef_method_channel.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -19,10 +19,9 @@ void main() { }); test('getPlatformVersion', () async { - WebviewCef webviewCefPlugin = WebviewCef(); MockWebviewCefPlatform fakePlatform = MockWebviewCefPlatform(); WebviewCefPlatform.instance = fakePlatform; - expect(await webviewCefPlugin.getPlatformVersion(), '42'); + expect(await WebviewCefPlatform.instance.getPlatformVersion(), '42'); }); } diff --git a/test/webview_main_test.dart b/test/webview_main_test.dart deleted file mode 100644 index 56067ef8..00000000 --- a/test/webview_main_test.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:webview_cef_example/main.dart'; - -void main() { - testWidgets('Verify the button changes text upon click', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify initial button text - final initialTextFinder = find.text('Click me'); - expect(initialTextFinder, findsOneWidget); - - // Tap the button which should change its text - await tester.tap(initialTextFinder); - await tester.pump(); // Rebuild the widget after the state has changed - - // Verify the button's text has changed after being clicked - expect(find.text('Clicked!'), findsOneWidget); - }); -} diff --git a/third/cef/.gitignore b/third/cef/.gitignore deleted file mode 100644 index 5b5dd31f..00000000 --- a/third/cef/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -cmake -Debug -libcef_dll -libcef_dll_wrapper -Release -Resources -version.txt diff --git a/third/cef/include/base/cef_atomic_flag.h b/third/cef/include/base/cef_atomic_flag.h deleted file mode 100644 index 3a2fdbc1..00000000 --- a/third/cef/include/base/cef_atomic_flag.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef CEF_INCLUDE_BASE_CEF_ATOMIC_FLAG_H_ -#define CEF_INCLUDE_BASE_CEF_ATOMIC_FLAG_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "base/synchronization/atomic_flag.h" - -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -#include - -#include - -#include "include/base/cef_thread_checker.h" - -namespace base { - -/// -/// A flag that can safely be set from one thread and read from other threads. -/// -/// This class IS NOT intended for synchronization between threads. -/// -class AtomicFlag { - public: - AtomicFlag(); - - AtomicFlag(const AtomicFlag&) = delete; - AtomicFlag& operator=(const AtomicFlag&) = delete; - - ~AtomicFlag(); - - /// - /// Set the flag. Must always be called from the same thread. - /// - void Set(); - - /// - /// Returns true iff the flag was set. If this returns true, the current - /// thread is guaranteed to be synchronized with all memory operations on the - /// thread which invoked Set() up until at least the first call to Set() on - /// it. - /// - bool IsSet() const { - // Inline here: this has a measurable performance impact on base::WeakPtr. - return flag_.load(std::memory_order_acquire) != 0; - } - - /// - /// Resets the flag. Be careful when using this: callers might not expect - /// IsSet() to return false after returning true once. - /// - void UnsafeResetForTesting(); - - private: - std::atomic flag_{0}; - base::ThreadChecker set_thread_checker_; -}; - -} // namespace base - -#endif // !USING_CHROMIUM_INCLUDES - -#endif // CEF_INCLUDE_BASE_CEF_ATOMIC_FLAG_H_ diff --git a/third/cef/include/base/cef_atomic_ref_count.h b/third/cef/include/base/cef_atomic_ref_count.h deleted file mode 100644 index 38e8f93b..00000000 --- a/third/cef/include/base/cef_atomic_ref_count.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This is a low level implementation of atomic semantics for reference -// counting. Please use cef_ref_counted.h directly instead. -// -// The Chromium implementation includes annotations to avoid some false -// positives when using data race detection tools. Annotations are not -// currently supported by the CEF implementation. - -#ifndef CEF_INCLUDE_BASE_CEF_ATOMIC_REF_COUNT_H_ -#define CEF_INCLUDE_BASE_CEF_ATOMIC_REF_COUNT_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "base/atomic_ref_count.h" - -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -#include - -namespace base { - -class AtomicRefCount { - public: - constexpr AtomicRefCount() : ref_count_(0) {} - explicit constexpr AtomicRefCount(int initial_value) - : ref_count_(initial_value) {} - - /// - /// Increment a reference count. - /// Returns the previous value of the count. - /// - int Increment() { return Increment(1); } - - /// - /// Increment a reference count by "increment", which must exceed 0. - /// Returns the previous value of the count. - /// - int Increment(int increment) { - return ref_count_.fetch_add(increment, std::memory_order_relaxed); - } - - /// - /// Decrement a reference count, and return whether the result is non-zero. - /// Insert barriers to ensure that state written before the reference count - /// became zero will be visible to a thread that has just made the count zero. - /// - bool Decrement() { - // TODO(jbroman): Technically this doesn't need to be an acquire operation - // unless the result is 1 (i.e., the ref count did indeed reach zero). - // However, there are toolchain issues that make that not work as well at - // present (notably TSAN doesn't like it). - return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1; - } - - /// - /// Return whether the reference count is one. If the reference count is used - /// in the conventional way, a refrerence count of 1 implies that the current - /// thread owns the reference and no other thread shares it. This call - /// performs the test for a reference count of one, and performs the memory - /// barrier needed for the owning thread to act on the object, knowing that it - /// has exclusive access to the object. - /// - bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; } - - /// - /// Return whether the reference count is zero. With conventional object - /// referencing counting, the object will be destroyed, so the reference count - /// should never be zero. Hence this is generally used for a debug check. - /// - bool IsZero() const { - return ref_count_.load(std::memory_order_acquire) == 0; - } - - /// - /// Returns the current reference count (with no barriers). This is subtle, - /// and should be used only for debugging. - /// - int SubtleRefCountForDebug() const { - return ref_count_.load(std::memory_order_relaxed); - } - - private: - std::atomic_int ref_count_; -}; - -} // namespace base - -#endif // !USING_CHROMIUM_INCLUDES - -#endif // CEF_INCLUDE_BASE_CEF_ATOMIC_REF_COUNT_H_ diff --git a/third/cef/include/base/cef_auto_reset.h b/third/cef/include/base/cef_auto_reset.h deleted file mode 100644 index be3a05da..00000000 --- a/third/cef/include/base/cef_auto_reset.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// base::AutoReset<> is useful for setting a variable to a new value only within -// a particular scope. An base::AutoReset<> object resets a variable to its -// original value upon destruction, making it an alternative to writing -// "var = false;" or "var = old_val;" at all of a block's exit points. -// -// This should be obvious, but note that an base::AutoReset<> instance should -// have a shorter lifetime than its scoped_variable, to prevent invalid memory -// writes when the base::AutoReset<> object is destroyed. - -#ifndef CEF_INCLUDE_BASE_CEF_AUTO_RESET_H_ -#define CEF_INCLUDE_BASE_CEF_AUTO_RESET_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "base/auto_reset.h" -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -#include - -namespace base { - -template -class AutoReset { - public: - template - AutoReset(T* scoped_variable, U&& new_value) - : scoped_variable_(scoped_variable), - original_value_( - std::exchange(*scoped_variable_, std::forward(new_value))) {} - - AutoReset(AutoReset&& other) - : scoped_variable_(std::exchange(other.scoped_variable_, nullptr)), - original_value_(std::move(other.original_value_)) {} - - AutoReset& operator=(AutoReset&& rhs) { - scoped_variable_ = std::exchange(rhs.scoped_variable_, nullptr); - original_value_ = std::move(rhs.original_value_); - return *this; - } - - ~AutoReset() { - if (scoped_variable_) { - *scoped_variable_ = std::move(original_value_); - } - } - - private: - T* scoped_variable_; - T original_value_; -}; - -} // namespace base - -#endif // !USING_CHROMIUM_INCLUDES - -#endif // CEF_INCLUDE_BASE_CEF_AUTO_RESET_H_ diff --git a/third/cef/include/base/cef_basictypes.h b/third/cef/include/base/cef_basictypes.h deleted file mode 100644 index 4f39e83c..00000000 --- a/third/cef/include/base/cef_basictypes.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef CEF_INCLUDE_BASE_CEF_BASICTYPES_H_ -#define CEF_INCLUDE_BASE_CEF_BASICTYPES_H_ -#pragma once - -#include // For UINT_MAX -#include // For size_t - -#include "include/base/cef_build.h" - -// The NSPR system headers define 64-bit as |long| when possible, except on -// Mac OS X. In order to not have typedef mismatches, we do the same on LP64. -// -// On Mac OS X, |long long| is used for 64-bit types for compatibility with -// format macros even in the LP64 model. -#if defined(__LP64__) && !defined(OS_MAC) && !defined(OS_OPENBSD) -typedef long int64; -typedef unsigned long uint64; -#else -typedef long long int64; -typedef unsigned long long uint64; -#endif - -// TODO: Remove these type guards. These are to avoid conflicts with -// obsolete/protypes.h in the Gecko SDK. -#ifndef _INT32 -#define _INT32 -typedef int int32; -#endif - -// TODO: Remove these type guards. These are to avoid conflicts with -// obsolete/protypes.h in the Gecko SDK. -#ifndef _UINT32 -#define _UINT32 -typedef unsigned int uint32; -#endif - -#ifndef _INT16 -#define _INT16 -typedef short int16; -#endif - -#ifndef _UINT16 -#define _UINT16 -typedef unsigned short uint16; -#endif - -// UTF-16 character type. -#ifndef char16 -#if defined(WCHAR_T_IS_UTF16) -typedef wchar_t char16; -#elif defined(WCHAR_T_IS_UTF32) -typedef unsigned short char16; -#endif -#endif - -#endif // CEF_INCLUDE_BASE_CEF_BASICTYPES_H_ diff --git a/third/cef/include/base/cef_bind.h b/third/cef/include/base/cef_bind.h deleted file mode 100644 index 770e373e..00000000 --- a/third/cef/include/base/cef_bind.h +++ /dev/null @@ -1,391 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// -/// \file -/// base::BindOnce() and base::BindRepeating() are helpers for creating -/// base::OnceCallback and base::RepeatingCallback objects respectively. -/// -/// For a runnable object of n-arity, the base::Bind*() family allows partial -/// application of the first m arguments. The remaining n - m arguments must be -/// passed when invoking the callback with Run(). -/// -///
-///   // The first argument is bound at callback creation; the remaining
-///   // two must be passed when calling Run() on the callback object.
-///   base::OnceCallback cb = base::BindOnce(
-///       [](short x, int y, long z) { return x * y * z; }, 42);
-/// 
-/// -/// When binding to a method, the receiver object must also be specified at -/// callback creation time. When Run() is invoked, the method will be invoked on -/// the specified receiver object. -/// -///
-///   class C : public base::RefCounted { void F(); };
-///   auto instance = base::MakeRefCounted();
-///   auto cb = base::BindOnce(&C::F, instance);
-///   std::move(cb).Run();  // Identical to instance->F()
-/// 
-/// -/// See https://chromium.googlesource.com/chromium/src/+/lkgr/docs/callback.md -/// for the full documentation. -/// - -// Implementation notes -// -// If you're reading the implementation, before proceeding further, you should -// read the top comment of base/internal/cef_bind_internal.h for a definition -// of common terms and concepts. - -#ifndef CEF_INCLUDE_BASE_CEF_BIND_H_ -#define CEF_INCLUDE_BASE_CEF_BIND_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "base/functional/bind.h" -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -#include -#include -#include -#include - -#include "include/base/cef_build.h" -#include "include/base/cef_compiler_specific.h" -#include "include/base/cef_template_util.h" -#include "include/base/internal/cef_bind_internal.h" - -#if defined(OS_APPLE) && !HAS_FEATURE(objc_arc) -#include "include/base/internal/cef_scoped_block_mac.h" -#endif - -namespace base { - -/// -/// Bind as OnceCallback. -/// -template -inline OnceCallback> -BindOnce(Functor&& functor, Args&&... args) { - static_assert(!cef_internal::IsOnceCallback>() || - (std::is_rvalue_reference() && - !std::is_const>()), - "BindOnce requires non-const rvalue for OnceCallback binding." - " I.e.: base::BindOnce(std::move(callback))."); - static_assert( - conjunction>...>::value, - "Use std::move() instead of base::Passed() with base::BindOnce()"); - - return cef_internal::BindImpl(std::forward(functor), - std::forward(args)...); -} - -/// -/// Bind as RepeatingCallback. -/// -template -inline RepeatingCallback> -BindRepeating(Functor&& functor, Args&&... args) { - static_assert( - !cef_internal::IsOnceCallback>(), - "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move()."); - - return cef_internal::BindImpl( - std::forward(functor), std::forward(args)...); -} - -/// -/// Special cases for binding to a base::Callback without extra bound arguments. -/// We CHECK() the validity of callback to guard against null pointers -/// accidentally ending up in posted tasks, causing hard-to-debug crashes. -/// -template -OnceCallback BindOnce(OnceCallback callback) { - CHECK(callback); - return callback; -} - -template -OnceCallback BindOnce(RepeatingCallback callback) { - CHECK(callback); - return callback; -} - -template -RepeatingCallback BindRepeating( - RepeatingCallback callback) { - CHECK(callback); - return callback; -} - -/// -/// Unretained() allows binding a non-refcounted class, and to disable -/// refcounting on arguments that are refcounted objects. -/// -/// EXAMPLE OF Unretained(): -/// -///
-///   class Foo {
-///    public:
-///     void func() { cout << "Foo:f" << endl; }
-///   };
-///
-///   // In some function somewhere.
-///   Foo foo;
-///   OnceClosure foo_callback =
-///       BindOnce(&Foo::func, Unretained(&foo));
-///   std::move(foo_callback).Run();  // Prints "Foo:f".
-/// 
-/// -/// Without the Unretained() wrapper on |&foo|, the above call would fail -/// to compile because Foo does not support the AddRef() and Release() methods. -/// -template -inline cef_internal::UnretainedWrapper Unretained(T* o) { - return cef_internal::UnretainedWrapper(o); -} - -/// -/// RetainedRef() accepts a ref counted object and retains a reference to it. -/// When the callback is called, the object is passed as a raw pointer. -/// -/// EXAMPLE OF RetainedRef(): -/// -///
-///    void foo(RefCountedBytes* bytes) {}
-///
-///    scoped_refptr bytes = ...;
-///    OnceClosure callback = BindOnce(&foo, base::RetainedRef(bytes));
-///    std::move(callback).Run();
-/// 
-/// -/// Without RetainedRef, the scoped_refptr would try to implicitly convert to -/// a raw pointer and fail compilation: -/// -///
-///    OnceClosure callback = BindOnce(&foo, bytes); // ERROR!
-/// 
-/// -template -inline cef_internal::RetainedRefWrapper RetainedRef(T* o) { - return cef_internal::RetainedRefWrapper(o); -} -template -inline cef_internal::RetainedRefWrapper RetainedRef(scoped_refptr o) { - return cef_internal::RetainedRefWrapper(std::move(o)); -} - -/// -/// Owned() transfers ownership of an object to the callback resulting from -/// bind; the object will be deleted when the callback is deleted. -/// -/// EXAMPLE OF Owned(): -/// -///
-///   void foo(int* arg) { cout << *arg << endl }
-///
-///   int* pn = new int(1);
-///   RepeatingClosure foo_callback = BindRepeating(&foo, Owned(pn));
-///
-///   foo_callback.Run();  // Prints "1"
-///   foo_callback.Run();  // Prints "1"
-///   *pn = 2;
-///   foo_callback.Run();  // Prints "2"
-///
-///   foo_callback.Reset();  // |pn| is deleted.  Also will happen when
-///                          // |foo_callback| goes out of scope.
-/// 
-/// -/// Without Owned(), someone would have to know to delete |pn| when the last -/// reference to the callback is deleted. -/// -template -inline cef_internal::OwnedWrapper Owned(T* o) { - return cef_internal::OwnedWrapper(o); -} - -template -inline cef_internal::OwnedWrapper Owned( - std::unique_ptr&& ptr) { - return cef_internal::OwnedWrapper(std::move(ptr)); -} - -/// -/// OwnedRef() stores an object in the callback resulting from -/// bind and passes a reference to the object to the bound function. -/// -/// EXAMPLE OF OwnedRef(): -/// -///
-///   void foo(int& arg) { cout << ++arg << endl }
-///
-///   int counter = 0;
-///   RepeatingClosure foo_callback = BindRepeating(&foo, OwnedRef(counter));
-///
-///   foo_callback.Run();  // Prints "1"
-///   foo_callback.Run();  // Prints "2"
-///   foo_callback.Run();  // Prints "3"
-///
-///   cout << counter;     // Prints "0", OwnedRef creates a copy of counter.
-/// 
-/// -/// Supports OnceCallbacks as well, useful to pass placeholder arguments: -/// -///
-///   void bar(int& ignore, const std::string& s) { cout << s << endl }
-///
-///   OnceClosure bar_callback = BindOnce(&bar, OwnedRef(0), "Hello");
-///
-///   std::move(bar_callback).Run(); // Prints "Hello"
-/// 
-/// -/// Without OwnedRef() it would not be possible to pass a mutable reference to -/// an object owned by the callback. -/// -template -cef_internal::OwnedRefWrapper> OwnedRef(T&& t) { - return cef_internal::OwnedRefWrapper>(std::forward(t)); -} - -/// -/// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr) -/// through a RepeatingCallback. Logically, this signifies a destructive -/// transfer of the state of the argument into the target function. Invoking -/// RepeatingCallback::Run() twice on a callback that was created with a -/// Passed() argument will CHECK() because the first invocation would have -/// already transferred ownership to the target function. -/// -/// Note that Passed() is not necessary with BindOnce(), as std::move() does the -/// same thing. Avoid Passed() in favor of std::move() with BindOnce(). -/// -/// EXAMPLE OF Passed(): -/// -///
-///   void TakesOwnership(std::unique_ptr arg) { }
-///   std::unique_ptr CreateFoo() { return std::make_unique();
-///   }
-///
-///   auto f = std::make_unique();
-///
-///   // |cb| is given ownership of Foo(). |f| is now NULL.
-///   // You can use std::move(f) in place of &f, but it's more verbose.
-///   RepeatingClosure cb = BindRepeating(&TakesOwnership, Passed(&f));
-///
-///   // Run was never called so |cb| still owns Foo() and deletes
-///   // it on Reset().
-///   cb.Reset();
-///
-///   // |cb| is given a new Foo created by CreateFoo().
-///   cb = BindRepeating(&TakesOwnership, Passed(CreateFoo()));
-///
-///   // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
-///   // no longer owns Foo() and, if reset, would not delete Foo().
-///   cb.Run();  // Foo() is now transferred to |arg| and deleted.
-///   cb.Run();  // This CHECK()s since Foo() already been used once.
-/// 
-/// -/// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and is -/// best suited for use with the return value of a function or other temporary -/// rvalues. The second takes a pointer to the scoper and is just syntactic -/// sugar to avoid having to write Passed(std::move(scoper)). -/// -/// Both versions of Passed() prevent T from being an lvalue reference. The -/// first via use of enable_if, and the second takes a T* which will not bind to -/// T&. -/// -template ::value>* = nullptr> -inline cef_internal::PassedWrapper Passed(T&& scoper) { - return cef_internal::PassedWrapper(std::move(scoper)); -} -template -inline cef_internal::PassedWrapper Passed(T* scoper) { - return cef_internal::PassedWrapper(std::move(*scoper)); -} - -/// -/// IgnoreResult() is used to adapt a function or callback with a return type to -/// one with a void return. This is most useful if you have a function with, -/// say, a pesky ignorable bool return that you want to use with PostTask or -/// something else that expect a callback with a void return. -/// -/// EXAMPLE OF IgnoreResult(): -/// -///
-///   int DoSomething(int arg) { cout << arg << endl; }
-///
-///   // Assign to a callback with a void return type.
-///   OnceCallback cb = BindOnce(IgnoreResult(&DoSomething));
-///   std::move(cb).Run(1);  // Prints "1".
-///
-///   // Prints "2" on |ml|.
-///   ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 2);
-/// 
-/// -template -inline cef_internal::IgnoreResultHelper IgnoreResult(T data) { - return cef_internal::IgnoreResultHelper(std::move(data)); -} - -#if defined(OS_APPLE) && !HAS_FEATURE(objc_arc) - -/// -/// RetainBlock() is used to adapt an Objective-C block when Automated Reference -/// Counting (ARC) is disabled. This is unnecessary when ARC is enabled, as the -/// BindOnce and BindRepeating already support blocks then. -/// -/// EXAMPLE OF RetainBlock(): -/// -///
-///   // Wrap the block and bind it to a callback.
-///   OnceCallback cb =
-///       BindOnce(RetainBlock(^(int n) { NSLog(@"%d", n); }));
-///   std::move(cb).Run(1);  // Logs "1".
-/// 
-/// -template -base::mac::ScopedBlock RetainBlock(R (^block)(Args...)) { - return base::mac::ScopedBlock(block, - base::scoped_policy::RETAIN); -} - -#endif // defined(OS_APPLE) && !HAS_FEATURE(objc_arc) - -} // namespace base - -#endif // !USING_CHROMIUM_INCLUDES - -#endif // CEF_INCLUDE_BASE_CEF_BIND_H_ diff --git a/third/cef/include/base/cef_build.h b/third/cef/include/base/cef_build.h deleted file mode 100644 index 48a088cb..00000000 --- a/third/cef/include/base/cef_build.h +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// \file -/// This file adds defines about the platform we're currently building on. -/// -///
-///  Operating System:
-///    OS_AIX / OS_ANDROID / OS_ASMJS / OS_FREEBSD / OS_FUCHSIA / OS_IOS /
-///    OS_LINUX / OS_MAC / OS_NACL (SFI or NONSFI) / OS_NETBSD / OS_OPENBSD /
-///    OS_QNX / OS_SOLARIS / OS_WIN
-///  Operating System family:
-///    OS_APPLE: IOS or MAC
-///    OS_BSD: FREEBSD or NETBSD or OPENBSD
-///    OS_POSIX: AIX or ANDROID or ASMJS or CHROMEOS or FREEBSD or IOS or LINUX
-///              or MAC or NACL or NETBSD or OPENBSD or QNX or SOLARIS
-///
-///  /!\ Note: OS_CHROMEOS is set by the build system, not this file
-///
-///  Compiler:
-///    COMPILER_MSVC / COMPILER_GCC
-///
-///  Processor:
-///    ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_MIPS / ARCH_CPU_MIPS64 /
-///    ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL / ARCH_CPU_PPC64 / ARCH_CPU_S390 /
-///    ARCH_CPU_S390X / ARCH_CPU_X86 / ARCH_CPU_X86_64
-///  Processor family:
-///    ARCH_CPU_ARM_FAMILY: ARMEL or ARM64
-///    ARCH_CPU_MIPS_FAMILY: MIPS64EL or MIPSEL or MIPS64 or MIPS
-///    ARCH_CPU_PPC64_FAMILY: PPC64
-///    ARCH_CPU_S390_FAMILY: S390 or S390X
-///    ARCH_CPU_X86_FAMILY: X86 or X86_64
-///  Processor features:
-///    ARCH_CPU_31_BITS / ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
-///    ARCH_CPU_BIG_ENDIAN / ARCH_CPU_LITTLE_ENDIAN
-/// 
-/// - -#ifndef CEF_INCLUDE_BASE_CEF_BUILD_H_ -#define CEF_INCLUDE_BASE_CEF_BUILD_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "build/build_config.h" -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -// A set of macros to use for platform detection. -#if defined(ANDROID) -#define OS_ANDROID 1 -#elif defined(__APPLE__) -// Only include TargetConditionals after testing ANDROID as some Android builds -// on the Mac have this header available and it's not needed unless the target -// is really an Apple platform. -#include -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE -#define OS_IOS 1 -#else -#define OS_MAC 1 -// For backwards compatibility. -#define OS_MACOSX 1 -#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE -#elif defined(__linux__) -#if !defined(OS_CHROMEOS) -// Do not define OS_LINUX on Chrome OS build. -// The OS_CHROMEOS macro is defined in GN. -#define OS_LINUX 1 -#endif // !defined(OS_CHROMEOS) -// Include a system header to pull in features.h for glibc/uclibc macros. -#include -#if defined(__GLIBC__) && !defined(__UCLIBC__) -// We really are using glibc, not uClibc pretending to be glibc. -#define LIBC_GLIBC 1 -#endif -#elif defined(_WIN32) -#define OS_WIN 1 -#elif defined(__Fuchsia__) -#define OS_FUCHSIA 1 -#elif defined(__FreeBSD__) -#define OS_FREEBSD 1 -#elif defined(__NetBSD__) -#define OS_NETBSD 1 -#elif defined(__OpenBSD__) -#define OS_OPENBSD 1 -#elif defined(__sun) -#define OS_SOLARIS 1 -#elif defined(__QNXNTO__) -#define OS_QNX 1 -#elif defined(_AIX) -#define OS_AIX 1 -#elif defined(__asmjs__) || defined(__wasm__) -#define OS_ASMJS 1 -#else -#error Please add support for your platform in include/base/cef_build.h -#endif -// NOTE: Adding a new port? Please follow -// https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md - -#if defined(OS_MAC) || defined(OS_IOS) -#define OS_APPLE 1 -#endif - -// For access to standard BSD features, use OS_BSD instead of a -// more specific macro. -#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) -#define OS_BSD 1 -#endif - -// For access to standard POSIXish features, use OS_POSIX instead of a -// more specific macro. -#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \ - defined(OS_FREEBSD) || defined(OS_IOS) || defined(OS_LINUX) || \ - defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_NACL) || \ - defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \ - defined(OS_SOLARIS) -#define OS_POSIX 1 -#endif - -// Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on -// Windows. -#if defined(__GNUC__) -#define COMPILER_GCC 1 -#elif defined(_MSC_VER) -#define COMPILER_MSVC 1 -#else -#error Please add support for your compiler in build/build_config.h -#endif - -// Processor architecture detection. For more info on what's defined, see: -// http://msdn.microsoft.com/en-us/library/b0084kay.aspx -// http://www.agner.org/optimize/calling_conventions.pdf -// or with gcc, run: "echo | gcc -E -dM -" -#if defined(_M_X64) || defined(__x86_64__) -#define ARCH_CPU_X86_FAMILY 1 -#define ARCH_CPU_X86_64 1 -#define ARCH_CPU_64_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#elif defined(_M_IX86) || defined(__i386__) -#define ARCH_CPU_X86_FAMILY 1 -#define ARCH_CPU_X86 1 -#define ARCH_CPU_32_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#elif defined(__s390x__) -#define ARCH_CPU_S390_FAMILY 1 -#define ARCH_CPU_S390X 1 -#define ARCH_CPU_64_BITS 1 -#define ARCH_CPU_BIG_ENDIAN 1 -#elif defined(__s390__) -#define ARCH_CPU_S390_FAMILY 1 -#define ARCH_CPU_S390 1 -#define ARCH_CPU_31_BITS 1 -#define ARCH_CPU_BIG_ENDIAN 1 -#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__) -#define ARCH_CPU_PPC64_FAMILY 1 -#define ARCH_CPU_PPC64 1 -#define ARCH_CPU_64_BITS 1 -#define ARCH_CPU_BIG_ENDIAN 1 -#elif defined(__PPC64__) -#define ARCH_CPU_PPC64_FAMILY 1 -#define ARCH_CPU_PPC64 1 -#define ARCH_CPU_64_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#elif defined(__ARMEL__) -#define ARCH_CPU_ARM_FAMILY 1 -#define ARCH_CPU_ARMEL 1 -#define ARCH_CPU_32_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#elif defined(__aarch64__) || defined(_M_ARM64) -#define ARCH_CPU_ARM_FAMILY 1 -#define ARCH_CPU_ARM64 1 -#define ARCH_CPU_64_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__) -#define ARCH_CPU_32_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#elif defined(__MIPSEL__) -#if defined(__LP64__) -#define ARCH_CPU_MIPS_FAMILY 1 -#define ARCH_CPU_MIPS64EL 1 -#define ARCH_CPU_64_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#else -#define ARCH_CPU_MIPS_FAMILY 1 -#define ARCH_CPU_MIPSEL 1 -#define ARCH_CPU_32_BITS 1 -#define ARCH_CPU_LITTLE_ENDIAN 1 -#endif -#elif defined(__MIPSEB__) -#if defined(__LP64__) -#define ARCH_CPU_MIPS_FAMILY 1 -#define ARCH_CPU_MIPS64 1 -#define ARCH_CPU_64_BITS 1 -#define ARCH_CPU_BIG_ENDIAN 1 -#else -#define ARCH_CPU_MIPS_FAMILY 1 -#define ARCH_CPU_MIPS 1 -#define ARCH_CPU_32_BITS 1 -#define ARCH_CPU_BIG_ENDIAN 1 -#endif -#else -#error Please add support for your architecture in include/base/cef_build.h -#endif - -// Type detection for wchar_t. -#if defined(OS_WIN) -#define WCHAR_T_IS_UTF16 -#elif defined(OS_FUCHSIA) -#define WCHAR_T_IS_UTF32 -#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \ - (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) -#define WCHAR_T_IS_UTF32 -#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \ - (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff) -// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to -// compile in this mode (in particular, Chrome doesn't). This is intended for -// other projects using base who manage their own dependencies and make sure -// short wchar works for them. -#define WCHAR_T_IS_UTF16 -#else -#error Please add support for your compiler in include/base/cef_build.h -#endif - -#if defined(OS_ANDROID) -// The compiler thinks std::string::const_iterator and "const char*" are -// equivalent types. -#define STD_STRING_ITERATOR_IS_CHAR_POINTER -// The compiler thinks std::u16string::const_iterator and "char16*" are -// equivalent types. -#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER -#endif - -#endif // !USING_CHROMIUM_INCLUDES - -#endif // CEF_INCLUDE_BASE_CEF_BUILD_H_ diff --git a/third/cef/include/base/cef_callback.h b/third/cef/include/base/cef_callback.h deleted file mode 100644 index bcfe4992..00000000 --- a/third/cef/include/base/cef_callback.h +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// \file -/// A callback is similar in concept to a function pointer: it wraps a runnable -/// object such as a function, method, lambda, or even another callback, -/// allowing the runnable object to be invoked later via the callback object. -/// -/// Unlike function pointers, callbacks are created with base::BindOnce() or -/// base::BindRepeating() and support partial function application. -/// -/// A base::OnceCallback may be Run() at most once; a base::RepeatingCallback -/// may be Run() any number of times. |is_null()| is guaranteed to return true -/// for a moved-from callback. -/// -///
-///   // The lambda takes two arguments, but the first argument |x| is bound at
-///   // callback creation.
-///   base::OnceCallback cb = base::BindOnce([] (int x, int y) {
-///     return x + y;
-///   }, 1);
-///   // Run() only needs the remaining unbound argument |y|.
-///   printf("1 + 2 = %d\n", std::move(cb).Run(2));  // Prints 3
-///   printf("cb is null? %s\n",
-///          cb.is_null() ? "true" : "false");  // Prints true
-///   std::move(cb).Run(2);  // Crashes since |cb| has already run.
-/// 
-/// -/// Callbacks also support cancellation. A common use is binding the receiver -/// object as a WeakPtr. If that weak pointer is invalidated, calling Run() -/// will be a no-op. Note that |IsCancelled()| and |is_null()| are distinct: -/// simply cancelling a callback will not also make it null. -/// -/// See https://chromium.googlesource.com/chromium/src/+/lkgr/docs/callback.md -/// for the full documentation. - -#ifndef CEF_INCLUDE_BASE_CEF_CALLBACK_H_ -#define CEF_INCLUDE_BASE_CEF_CALLBACK_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "base/functional/callback.h" -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -#include - -#include "include/base/cef_bind.h" -#include "include/base/cef_callback_forward.h" -#include "include/base/cef_logging.h" -#include "include/base/internal/cef_callback_internal.h" - -namespace base { - -template -class OnceCallback : public cef_internal::CallbackBase { - public: - using ResultType = R; - using RunType = R(Args...); - using PolymorphicInvoke = R (*)(cef_internal::BindStateBase*, - cef_internal::PassingType...); - - constexpr OnceCallback() = default; - OnceCallback(std::nullptr_t) = delete; - - explicit OnceCallback(cef_internal::BindStateBase* bind_state) - : cef_internal::CallbackBase(bind_state) {} - - OnceCallback(const OnceCallback&) = delete; - OnceCallback& operator=(const OnceCallback&) = delete; - - OnceCallback(OnceCallback&&) noexcept = default; - OnceCallback& operator=(OnceCallback&&) noexcept = default; - - OnceCallback(RepeatingCallback other) - : cef_internal::CallbackBase(std::move(other)) {} - - OnceCallback& operator=(RepeatingCallback other) { - static_cast(*this) = std::move(other); - return *this; - } - - R Run(Args... args) const& { - static_assert(!sizeof(*this), - "OnceCallback::Run() may only be invoked on a non-const " - "rvalue, i.e. std::move(callback).Run()."); - NOTREACHED(); - } - - R Run(Args... args) && { - // Move the callback instance into a local variable before the invocation, - // that ensures the internal state is cleared after the invocation. - // It's not safe to touch |this| after the invocation, since running the - // bound function may destroy |this|. - OnceCallback cb = std::move(*this); - PolymorphicInvoke f = - reinterpret_cast(cb.polymorphic_invoke()); - return f(cb.bind_state_.get(), std::forward(args)...); - } - - // Then() returns a new OnceCallback that receives the same arguments as - // |this|, and with the return type of |then|. The returned callback will: - // 1) Run the functor currently bound to |this| callback. - // 2) Run the |then| callback with the result from step 1 as its single - // argument. - // 3) Return the value from running the |then| callback. - // - // Since this method generates a callback that is a replacement for `this`, - // `this` will be consumed and reset to a null callback to ensure the - // originally-bound functor can be run at most once. - template - OnceCallback Then(OnceCallback then) && { - CHECK(then); - return BindOnce( - cef_internal::ThenHelper< - OnceCallback, OnceCallback>::CreateTrampoline(), - std::move(*this), std::move(then)); - } - - // This overload is required; even though RepeatingCallback is implicitly - // convertible to OnceCallback, that conversion will not used when matching - // for template argument deduction. - template - OnceCallback Then( - RepeatingCallback then) && { - CHECK(then); - return BindOnce( - cef_internal::ThenHelper< - OnceCallback, - RepeatingCallback>::CreateTrampoline(), - std::move(*this), std::move(then)); - } -}; - -template -class RepeatingCallback - : public cef_internal::CallbackBaseCopyable { - public: - using ResultType = R; - using RunType = R(Args...); - using PolymorphicInvoke = R (*)(cef_internal::BindStateBase*, - cef_internal::PassingType...); - - constexpr RepeatingCallback() = default; - RepeatingCallback(std::nullptr_t) = delete; - - explicit RepeatingCallback(cef_internal::BindStateBase* bind_state) - : cef_internal::CallbackBaseCopyable(bind_state) {} - - // Copyable and movable. - RepeatingCallback(const RepeatingCallback&) = default; - RepeatingCallback& operator=(const RepeatingCallback&) = default; - RepeatingCallback(RepeatingCallback&&) noexcept = default; - RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default; - - bool operator==(const RepeatingCallback& other) const { - return EqualsInternal(other); - } - - bool operator!=(const RepeatingCallback& other) const { - return !operator==(other); - } - - R Run(Args... args) const& { - PolymorphicInvoke f = - reinterpret_cast(this->polymorphic_invoke()); - return f(this->bind_state_.get(), std::forward(args)...); - } - - R Run(Args... args) && { - // Move the callback instance into a local variable before the invocation, - // that ensures the internal state is cleared after the invocation. - // It's not safe to touch |this| after the invocation, since running the - // bound function may destroy |this|. - RepeatingCallback cb = std::move(*this); - PolymorphicInvoke f = - reinterpret_cast(cb.polymorphic_invoke()); - return f(std::move(cb).bind_state_.get(), std::forward(args)...); - } - - // Then() returns a new RepeatingCallback that receives the same arguments as - // |this|, and with the return type of |then|. The - // returned callback will: - // 1) Run the functor currently bound to |this| callback. - // 2) Run the |then| callback with the result from step 1 as its single - // argument. - // 3) Return the value from running the |then| callback. - // - // If called on an rvalue (e.g. std::move(cb).Then(...)), this method - // generates a callback that is a replacement for `this`. Therefore, `this` - // will be consumed and reset to a null callback to ensure the - // originally-bound functor will be run at most once. - template - RepeatingCallback Then( - RepeatingCallback then) const& { - CHECK(then); - return BindRepeating( - cef_internal::ThenHelper< - RepeatingCallback, - RepeatingCallback>::CreateTrampoline(), - *this, std::move(then)); - } - - template - RepeatingCallback Then( - RepeatingCallback then) && { - CHECK(then); - return BindRepeating( - cef_internal::ThenHelper< - RepeatingCallback, - RepeatingCallback>::CreateTrampoline(), - std::move(*this), std::move(then)); - } -}; - -} // namespace base - -#endif // !USING_CHROMIUM_INCLUDES - -#endif // CEF_INCLUDE_BASE_CEF_CALLBACK_H_ diff --git a/third/cef/include/base/cef_callback_forward.h b/third/cef/include/base/cef_callback_forward.h deleted file mode 100644 index 2d227743..00000000 --- a/third/cef/include/base/cef_callback_forward.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2011 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_ -#define INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "base/functional/callback_forward.h" -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -namespace base { - -template -class OnceCallback; - -template -class RepeatingCallback; - -/// -/// Syntactic sugar to make OnceClosure and RepeatingClosure -/// easier to declare since they will be used in a lot of APIs with delayed -/// execution. -/// -using OnceClosure = OnceCallback; -using RepeatingClosure = RepeatingCallback; - -} // namespace base - -#endif // !!USING_CHROMIUM_INCLUDES - -#endif // INCLUDE_BASE_CEF_CALLBACK_FORWARD_H_ diff --git a/third/cef/include/base/cef_callback_helpers.h b/third/cef/include/base/cef_callback_helpers.h deleted file mode 100644 index 5e386440..00000000 --- a/third/cef/include/base/cef_callback_helpers.h +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) 2014 Marshall A. Greenblatt. Portions copyright (c) 2012 -// Google Inc. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the name Chromium Embedded -// Framework nor the names of its contributors may be used to endorse -// or promote products derived from this software without specific prior -// written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This defines helpful methods for dealing with Callbacks. Because Callbacks -// are implemented using templates, with a class per callback signature, adding -// methods to Callback<> itself is unattractive (lots of extra code gets -// generated). Instead, consider adding methods here. - -#ifndef CEF_INCLUDE_BASE_CEF_CALLBACK_HELPERS_H_ -#define CEF_INCLUDE_BASE_CEF_CALLBACK_HELPERS_H_ -#pragma once - -#if defined(USING_CHROMIUM_INCLUDES) -// When building CEF include the Chromium header directly. -#include "base/functional/callback_helpers.h" -#else // !USING_CHROMIUM_INCLUDES -// The following is substantially similar to the Chromium implementation. -// If the Chromium implementation diverges the below implementation should be -// updated to match. - -#include -#include -#include -#include - -#include "include/base/cef_bind.h" -#include "include/base/cef_callback.h" -#include "include/base/cef_logging.h" - -namespace base { - -namespace internal { - -template -struct IsBaseCallbackImpl : std::false_type {}; - -template -struct IsBaseCallbackImpl> : std::true_type {}; - -template -struct IsBaseCallbackImpl> : std::true_type {}; - -template -struct IsOnceCallbackImpl : std::false_type {}; - -template -struct IsOnceCallbackImpl> : std::true_type {}; - -} // namespace internal - -/// -/// IsBaseCallback::value is true when T is any of the Closure or Callback -/// family of types. -/// -template -using IsBaseCallback = internal::IsBaseCallbackImpl>; - -/// -/// IsOnceCallback::value is true when T is a OnceClosure or OnceCallback -/// type. -/// -template -using IsOnceCallback = internal::IsOnceCallbackImpl>; - -/// -/// SFINAE friendly enabler allowing to overload methods for both Repeating and -/// OnceCallbacks. -/// -/// Usage: -///
-///   template