Files
wxWidgets/src/msw/webview_edge.cpp
Tobias Taschner 46d123b953 WebViewEdge: Allow edge webview2 installation during runtime
Check if edge webview runtime is available in the call to
wxWebView::IsBackendAvailable() instead of only doing it during
process initialization. This allows an application to install
the Edge WebView2 Runtime during runtime and use the edge webview
afterwards without restarting the process.
2020-11-06 09:19:17 +01:00

901 lines
25 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/msw/webview_edge.cpp
// Purpose: wxMSW Edge Chromium wxWebView backend implementation
// Author: Markus Pingel
// Created: 2019-12-15
// Copyright: (c) 2019 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include "wx/msw/webview_edge.h"
#if wxUSE_WEBVIEW && wxUSE_WEBVIEW_EDGE
#include "wx/filename.h"
#include "wx/module.h"
#include "wx/log.h"
#include "wx/stdpaths.h"
#include "wx/thread.h"
#include "wx/private/jsscriptwrapper.h"
#include "wx/private/json.h"
#include "wx/msw/private.h"
#include "wx/msw/private/cotaskmemptr.h"
#include "wx/msw/private/webview_edge.h"
#include <wrl/event.h>
#include <Objbase.h>
using namespace Microsoft::WRL;
wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewEdge, wxWebView);
#define WX_ERROR2_CASE(error, wxerror) \
case error: \
event.SetString(#error); \
event.SetInt(wxerror); \
break;
// WebView2Loader typedefs
typedef HRESULT (__stdcall *CreateCoreWebView2EnvironmentWithOptions_t)(
PCWSTR browserExecutableFolder,
PCWSTR userDataFolder,
ICoreWebView2EnvironmentOptions* environmentOptions,
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* environment_created_handler);
typedef HRESULT(__stdcall *GetAvailableCoreWebView2BrowserVersionString_t)(
PCWSTR browserExecutableFolder,
LPWSTR* versionInfo);
CreateCoreWebView2EnvironmentWithOptions_t wxCreateCoreWebView2EnvironmentWithOptions = NULL;
GetAvailableCoreWebView2BrowserVersionString_t wxGetAvailableCoreWebView2BrowserVersionString = NULL;
bool wxWebViewEdgeImpl::ms_isInitialized = false;
wxDynamicLibrary wxWebViewEdgeImpl::ms_loaderDll;
wxWebViewEdgeImpl::wxWebViewEdgeImpl(wxWebViewEdge* webview):
m_ctrl(webview)
{
}
wxWebViewEdgeImpl::~wxWebViewEdgeImpl()
{
if (m_webView)
{
m_webView->remove_NavigationCompleted(m_navigationCompletedToken);
m_webView->remove_NavigationStarting(m_navigationStartingToken);
m_webView->remove_NewWindowRequested(m_newWindowRequestedToken);
m_webView->remove_DocumentTitleChanged(m_documentTitleChangedToken);
m_webView->remove_ContentLoading(m_contentLoadingToken);
}
}
bool wxWebViewEdgeImpl::Create()
{
m_initialized = false;
m_isBusy = false;
m_pendingContextMenuEnabled = -1;
m_pendingAccessToDevToolsEnabled = -1;
m_historyLoadingFromList = false;
m_historyEnabled = true;
m_historyPosition = -1;
wxString userDataPath = wxStandardPaths::Get().GetUserLocalDataDir();
HRESULT hr = wxCreateCoreWebView2EnvironmentWithOptions(
nullptr,
userDataPath.wc_str(),
nullptr,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(this,
&wxWebViewEdgeImpl::OnEnvironmentCreated).Get());
if (FAILED(hr))
{
wxLogApiError("CreateWebView2EnvironmentWithOptions", hr);
return false;
}
else
return true;
}
HRESULT wxWebViewEdgeImpl::OnEnvironmentCreated(
HRESULT WXUNUSED(result), ICoreWebView2Environment* environment)
{
environment->QueryInterface(IID_PPV_ARGS(&m_webViewEnvironment));
m_webViewEnvironment->CreateCoreWebView2Controller(
m_ctrl->GetHWND(),
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
this, &wxWebViewEdgeImpl::OnWebViewCreated).Get());
return S_OK;
}
bool wxWebViewEdgeImpl::Initialize()
{
if (ms_isInitialized)
return true;
if (!ms_loaderDll.Load("WebView2Loader.dll", wxDL_DEFAULT | wxDL_QUIET))
return false;
// Try to load functions from loader DLL
wxDL_INIT_FUNC(wx, CreateCoreWebView2EnvironmentWithOptions, ms_loaderDll);
wxDL_INIT_FUNC(wx, GetAvailableCoreWebView2BrowserVersionString, ms_loaderDll);
if (!wxGetAvailableCoreWebView2BrowserVersionString || !wxCreateCoreWebView2EnvironmentWithOptions)
return false;
// Check if a Edge browser can be found by the loader DLL
wxCoTaskMemPtr<wchar_t> versionStr;
HRESULT hr = wxGetAvailableCoreWebView2BrowserVersionString(NULL, &versionStr);
if (SUCCEEDED(hr) && versionStr)
{
ms_isInitialized = true;
return true;
}
else
wxLogApiError("GetCoreWebView2BrowserVersionInfo", hr);
return false;
}
void wxWebViewEdgeImpl::Uninitialize()
{
if (ms_isInitialized)
{
ms_loaderDll.Unload();
ms_isInitialized = false;
}
}
void wxWebViewEdgeImpl::UpdateBounds()
{
RECT r;
wxCopyRectToRECT(m_ctrl->GetClientRect(), r);
if (m_webView)
m_webViewController->put_Bounds(r);
}
HRESULT wxWebViewEdgeImpl::OnNavigationStarting(ICoreWebView2* WXUNUSED(sender), ICoreWebView2NavigationStartingEventArgs* args)
{
m_isBusy = true;
wxString evtURL;
wxCoTaskMemPtr<wchar_t> uri;
if (SUCCEEDED(args->get_Uri(&uri)))
evtURL = wxString(uri);
wxWebViewEvent event(wxEVT_WEBVIEW_NAVIGATING, m_ctrl->GetId(), evtURL, wxString());
event.SetEventObject(m_ctrl);
m_ctrl->HandleWindowEvent(event);
if (!event.IsAllowed())
args->put_Cancel(true);
return S_OK;
}
HRESULT wxWebViewEdgeImpl::OnNavigationCompleted(ICoreWebView2* WXUNUSED(sender), ICoreWebView2NavigationCompletedEventArgs* args)
{
BOOL isSuccess;
if (FAILED(args->get_IsSuccess(&isSuccess)))
isSuccess = false;
m_isBusy = false;
wxString uri = m_ctrl->GetCurrentURL();
if (!isSuccess)
{
COREWEBVIEW2_WEB_ERROR_STATUS status;
wxWebViewEvent event(wxEVT_WEBVIEW_ERROR, m_ctrl->GetId(), uri, wxString());
event.SetEventObject(m_ctrl);
if (SUCCEEDED(args->get_WebErrorStatus(&status)))
{
switch (status)
{
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN, wxWEBVIEW_NAV_ERR_OTHER)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT, wxWEBVIEW_NAV_ERR_CERTIFICATE)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_EXPIRED, wxWEBVIEW_NAV_ERR_CERTIFICATE)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CLIENT_CERTIFICATE_CONTAINS_ERRORS, wxWEBVIEW_NAV_ERR_CERTIFICATE)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_REVOKED, wxWEBVIEW_NAV_ERR_CERTIFICATE)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID, wxWEBVIEW_NAV_ERR_CERTIFICATE)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_SERVER_UNREACHABLE, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_TIMEOUT, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_ERROR_HTTP_INVALID_SERVER_RESPONSE, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_RESET, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_CANNOT_CONNECT, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_HOST_NAME_NOT_RESOLVED, wxWEBVIEW_NAV_ERR_CONNECTION)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED, wxWEBVIEW_NAV_ERR_USER_CANCELLED)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_REDIRECT_FAILED, wxWEBVIEW_NAV_ERR_OTHER)
WX_ERROR2_CASE(COREWEBVIEW2_WEB_ERROR_STATUS_UNEXPECTED_ERROR, wxWEBVIEW_NAV_ERR_OTHER)
}
}
m_ctrl->HandleWindowEvent(event);
}
else
{
if (m_historyEnabled && !m_historyLoadingFromList &&
(uri == m_ctrl->GetCurrentURL()) ||
(m_ctrl->GetCurrentURL().substr(0, 4) == "file" &&
wxFileName::URLToFileName(m_ctrl->GetCurrentURL()).GetFullPath() == uri))
{
// If we are not at the end of the list, then erase everything
// between us and the end before adding the new page
if (m_historyPosition != static_cast<int>(m_historyList.size()) - 1)
{
m_historyList.erase(m_historyList.begin() + m_historyPosition + 1,
m_historyList.end());
}
wxSharedPtr<wxWebViewHistoryItem> item(new wxWebViewHistoryItem(uri, m_ctrl->GetCurrentTitle()));
m_historyList.push_back(item);
m_historyPosition++;
}
//Reset as we are done now
m_historyLoadingFromList = false;
wxWebViewEvent evt(wxEVT_WEBVIEW_NAVIGATED, m_ctrl->GetId(), uri, wxString());
m_ctrl->HandleWindowEvent(evt);
}
return S_OK;
}
HRESULT wxWebViewEdgeImpl::OnNewWindowRequested(ICoreWebView2* WXUNUSED(sender), ICoreWebView2NewWindowRequestedEventArgs* args)
{
wxCoTaskMemPtr<wchar_t> uri;
wxString evtURL;
if (SUCCEEDED(args->get_Uri(&uri)))
evtURL = wxString(uri);
wxWebViewNavigationActionFlags navFlags = wxWEBVIEW_NAV_ACTION_OTHER;
BOOL isUserInitiated;
if (SUCCEEDED(args->get_IsUserInitiated(&isUserInitiated)) && isUserInitiated)
navFlags = wxWEBVIEW_NAV_ACTION_USER;
wxWebViewEvent evt(wxEVT_WEBVIEW_NEWWINDOW, m_ctrl->GetId(), evtURL, wxString(), navFlags);
m_ctrl->HandleWindowEvent(evt);
args->put_Handled(true);
return S_OK;
}
HRESULT wxWebViewEdgeImpl::OnDocumentTitleChanged(ICoreWebView2* WXUNUSED(sender), IUnknown* WXUNUSED(args))
{
wxWebViewEvent event(wxEVT_WEBVIEW_TITLE_CHANGED,
m_ctrl->GetId(), m_ctrl->GetCurrentURL(), "");
event.SetString(m_ctrl->GetCurrentTitle());
event.SetEventObject(m_ctrl);
m_ctrl->HandleWindowEvent(event);
return S_OK;
}
HRESULT wxWebViewEdgeImpl::OnContentLoading(ICoreWebView2* WXUNUSED(sender), ICoreWebView2ContentLoadingEventArgs* WXUNUSED(args))
{
wxWebViewEvent event(wxEVT_WEBVIEW_LOADED, m_ctrl->GetId(),
m_ctrl->GetCurrentURL(), "");
event.SetEventObject(m_ctrl);
m_ctrl->HandleWindowEvent(event);
return S_OK;
}
HRESULT wxWebViewEdgeImpl::OnWebViewCreated(HRESULT result, ICoreWebView2Controller* webViewController)
{
if (FAILED(result))
{
wxLogApiError("WebView2::WebViewCreated", result);
return result;
}
HRESULT hr = webViewController->get_CoreWebView2(&m_webView);
if (FAILED(hr))
{
wxLogApiError("WebView2::WebViewCreated (get_CoreWebView2)", hr);
return result;
}
m_webViewController = webViewController;
m_initialized = true;
UpdateBounds();
// Connect and handle the various WebView events
m_webView->add_NavigationStarting(
Callback<ICoreWebView2NavigationStartingEventHandler>(
this, &wxWebViewEdgeImpl::OnNavigationStarting).Get(),
&m_navigationStartingToken);
m_webView->add_NavigationCompleted(
Callback<ICoreWebView2NavigationCompletedEventHandler>(
this, &wxWebViewEdgeImpl::OnNavigationCompleted).Get(),
&m_navigationCompletedToken);
m_webView->add_NewWindowRequested(
Callback<ICoreWebView2NewWindowRequestedEventHandler>(
this, &wxWebViewEdgeImpl::OnNewWindowRequested).Get(),
&m_newWindowRequestedToken);
m_webView->add_DocumentTitleChanged(
Callback<ICoreWebView2DocumentTitleChangedEventHandler>(
this, &wxWebViewEdgeImpl::OnDocumentTitleChanged).Get(),
&m_documentTitleChangedToken);
m_webView->add_ContentLoading(
Callback<ICoreWebView2ContentLoadingEventHandler>(
this, &wxWebViewEdgeImpl::OnContentLoading).Get(),
&m_contentLoadingToken);
if (m_pendingContextMenuEnabled != -1)
{
m_ctrl->EnableContextMenu(m_pendingContextMenuEnabled == 1);
m_pendingContextMenuEnabled = -1;
}
if (m_pendingAccessToDevToolsEnabled != -1)
{
m_ctrl->EnableAccessToDevTools(m_pendingAccessToDevToolsEnabled == 1);
m_pendingContextMenuEnabled = -1;
}
if (!m_pendingURL.empty())
{
m_ctrl->LoadURL(m_pendingURL);
m_pendingURL.clear();
}
return S_OK;
}
ICoreWebView2Settings* wxWebViewEdgeImpl::GetSettings()
{
if (!m_webView)
return NULL;
ICoreWebView2Settings* settings;
HRESULT hr = m_webView->get_Settings(&settings);
if (FAILED(hr))
{
wxLogApiError("WebView2::get_Settings", hr);
return NULL;
}
return settings;
}
wxWebViewEdge::~wxWebViewEdge()
{
Unbind(wxEVT_SHOW, &wxWebViewEdge::OnShow, this);
delete m_impl;
}
bool wxWebViewEdge::Create(wxWindow* parent,
wxWindowID id,
const wxString& url,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
if (!wxWebViewEdgeImpl::Initialize())
return false;
if (!wxControl::Create(parent, id, pos, size, style,
wxDefaultValidator, name))
{
return false;
}
m_impl = new wxWebViewEdgeImpl(this);
if (!m_impl->Create())
return false;
Bind(wxEVT_SIZE, &wxWebViewEdge::OnSize, this);
Bind(wxEVT_SHOW, &wxWebViewEdge::OnShow, this);
LoadURL(url);
return true;
}
void wxWebViewEdge::OnSize(wxSizeEvent& event)
{
m_impl->UpdateBounds();
event.Skip();
}
void wxWebViewEdge::OnShow(wxShowEvent& event)
{
if (m_impl->m_webView)
m_impl->m_webViewController->put_IsVisible(event.IsShown());
event.Skip();
}
void wxWebViewEdge::LoadURL(const wxString& url)
{
if (!m_impl->m_webView)
{
m_impl->m_pendingURL = url;
return;
}
HRESULT hr = m_impl->m_webView->Navigate(url.wc_str());
if (FAILED(hr))
wxLogApiError("WebView2::Navigate", hr);
}
void wxWebViewEdge::LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem> item)
{
int pos = -1;
for (unsigned int i = 0; i < m_impl->m_historyList.size(); i++)
{
//We compare the actual pointers to find the correct item
if (m_impl->m_historyList[i].get() == item.get())
pos = i;
}
wxASSERT_MSG(pos != static_cast<int>(m_impl->m_historyList.size()), "invalid history item");
m_impl->m_historyLoadingFromList = true;
LoadURL(item->GetUrl());
m_impl->m_historyPosition = pos;
}
wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewEdge::GetBackwardHistory()
{
wxVector<wxSharedPtr<wxWebViewHistoryItem> > backhist;
//As we don't have std::copy or an iterator constructor in the wxwidgets
//native vector we construct it by hand
for (int i = 0; i < m_impl->m_historyPosition; i++)
{
backhist.push_back(m_impl->m_historyList[i]);
}
return backhist;
}
wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewEdge::GetForwardHistory()
{
wxVector<wxSharedPtr<wxWebViewHistoryItem> > forwardhist;
//As we don't have std::copy or an iterator constructor in the wxwidgets
//native vector we construct it by hand
for (int i = m_impl->m_historyPosition + 1; i < static_cast<int>(m_impl->m_historyList.size()); i++)
{
forwardhist.push_back(m_impl->m_historyList[i]);
}
return forwardhist;
}
bool wxWebViewEdge::CanGoForward() const
{
if (m_impl->m_historyEnabled)
return m_impl->m_historyPosition != static_cast<int>(m_impl->m_historyList.size()) - 1;
else
return false;
}
bool wxWebViewEdge::CanGoBack() const
{
if (m_impl->m_historyEnabled)
return m_impl->m_historyPosition > 0;
else
return false;
}
void wxWebViewEdge::GoBack()
{
LoadHistoryItem(m_impl->m_historyList[m_impl->m_historyPosition - 1]);
}
void wxWebViewEdge::GoForward()
{
LoadHistoryItem(m_impl->m_historyList[m_impl->m_historyPosition + 1]);
}
void wxWebViewEdge::ClearHistory()
{
m_impl->m_historyList.clear();
m_impl->m_historyPosition = -1;
}
void wxWebViewEdge::EnableHistory(bool enable)
{
m_impl->m_historyEnabled = enable;
m_impl->m_historyList.clear();
m_impl->m_historyPosition = -1;
}
void wxWebViewEdge::Stop()
{
if (m_impl->m_webView)
m_impl->m_webView->Stop();
}
void wxWebViewEdge::Reload(wxWebViewReloadFlags WXUNUSED(flags))
{
if (m_impl->m_webView)
m_impl->m_webView->Reload();
}
wxString wxWebViewEdge::GetPageSource() const
{
wxString text;
const_cast<wxWebViewEdge*>(this)->RunScript("document.documentElement.outerHTML;", &text);
return text;
}
wxString wxWebViewEdge::GetPageText() const
{
wxString text;
const_cast<wxWebViewEdge*>(this)->RunScript("document.body.innerText;", &text);
return text;
}
bool wxWebViewEdge::IsBusy() const
{
return m_impl->m_isBusy;
}
wxString wxWebViewEdge::GetCurrentURL() const
{
wxCoTaskMemPtr<wchar_t> uri;
if (m_impl->m_webView && SUCCEEDED(m_impl->m_webView->get_Source(&uri)))
return wxString(uri);
else
return wxString();
}
wxString wxWebViewEdge::GetCurrentTitle() const
{
wxCoTaskMemPtr<wchar_t> title;
if (m_impl->m_webView && SUCCEEDED(m_impl->m_webView->get_DocumentTitle(&title)))
return wxString(title);
else
return wxString();
}
void wxWebViewEdge::SetZoomType(wxWebViewZoomType)
{
// only wxWEBVIEW_ZOOM_TYPE_LAYOUT is supported
}
wxWebViewZoomType wxWebViewEdge::GetZoomType() const
{
return wxWEBVIEW_ZOOM_TYPE_LAYOUT;
}
bool wxWebViewEdge::CanSetZoomType(wxWebViewZoomType type) const
{
return (type == wxWEBVIEW_ZOOM_TYPE_LAYOUT);
}
void wxWebViewEdge::Print()
{
RunScript("window.print();");
}
wxWebViewZoom wxWebViewEdge::GetZoom() const
{
double old_zoom_factor = 0.0;
m_impl->m_webViewController->get_ZoomFactor(&old_zoom_factor);
if (old_zoom_factor > 1.7)
return wxWEBVIEW_ZOOM_LARGEST;
if (old_zoom_factor > 1.3)
return wxWEBVIEW_ZOOM_LARGE;
if (old_zoom_factor > 0.8)
return wxWEBVIEW_ZOOM_MEDIUM;
if (old_zoom_factor > 0.6)
return wxWEBVIEW_ZOOM_SMALL;
return wxWEBVIEW_ZOOM_TINY;
}
float wxWebViewEdge::GetZoomFactor() const
{
double old_zoom_factor = 0.0;
m_impl->m_webViewController->get_ZoomFactor(&old_zoom_factor);
return old_zoom_factor;
}
void wxWebViewEdge::SetZoom(wxWebViewZoom zoom)
{
double old_zoom_factor = 0.0;
m_impl->m_webViewController->get_ZoomFactor(&old_zoom_factor);
double zoom_factor = 1.0;
switch (zoom)
{
case wxWEBVIEW_ZOOM_LARGEST:
zoom_factor = 2.0;
break;
case wxWEBVIEW_ZOOM_LARGE:
zoom_factor = 1.5;
break;
case wxWEBVIEW_ZOOM_MEDIUM:
zoom_factor = 1.0;
break;
case wxWEBVIEW_ZOOM_SMALL:
zoom_factor = 0.75;
break;
case wxWEBVIEW_ZOOM_TINY:
zoom_factor = 0.5;
break;
default:
break;
}
SetZoomFactor(zoom_factor);
}
void wxWebViewEdge::SetZoomFactor(float zoom)
{
m_impl->m_webViewController->put_ZoomFactor(zoom);
}
bool wxWebViewEdge::CanCut() const
{
return QueryCommandEnabled("cut");
}
bool wxWebViewEdge::CanCopy() const
{
return QueryCommandEnabled("copy");
}
bool wxWebViewEdge::CanPaste() const
{
return QueryCommandEnabled("paste");
}
void wxWebViewEdge::Cut()
{
ExecCommand("cut");
}
void wxWebViewEdge::Copy()
{
ExecCommand("copy");
}
void wxWebViewEdge::Paste()
{
ExecCommand("paste");
}
bool wxWebViewEdge::CanUndo() const
{
return QueryCommandEnabled("undo");
}
bool wxWebViewEdge::CanRedo() const
{
return QueryCommandEnabled("redo");
}
void wxWebViewEdge::Undo()
{
ExecCommand("undo");
}
void wxWebViewEdge::Redo()
{
ExecCommand("redo");
}
long wxWebViewEdge::Find(const wxString& WXUNUSED(text), int WXUNUSED(flags))
{
// TODO: not implemented in SDK (could probably be implemented by script)
return -1;
}
//Editing functions
void wxWebViewEdge::SetEditable(bool WXUNUSED(enable))
{
wxLogError("Not available");
}
bool wxWebViewEdge::IsEditable() const
{
return false;
}
void wxWebViewEdge::SelectAll()
{
RunScript("window.getSelection().selectAllChildren(document);");
}
bool wxWebViewEdge::HasSelection() const
{
wxString rangeCountStr;
const_cast<wxWebViewEdge*>(this)->RunScript("window.getSelection().rangeCount;", &rangeCountStr);
return rangeCountStr != "0";
}
void wxWebViewEdge::DeleteSelection()
{
ExecCommand("delete");
}
wxString wxWebViewEdge::GetSelectedText() const
{
wxString selectedText;
const_cast<wxWebViewEdge*>(this)->RunScript("window.getSelection().toString();", &selectedText);
return selectedText;
}
wxString wxWebViewEdge::GetSelectedSource() const
{
// TODO: not implemented in SDK (could probably be implemented by script)
return wxString();
}
void wxWebViewEdge::ClearSelection()
{
RunScript("window.getSelection().empty();");
}
void wxWebViewEdge::EnableContextMenu(bool enable)
{
wxCOMPtr<ICoreWebView2Settings> settings(m_impl->GetSettings());
if (settings)
settings->put_AreDefaultContextMenusEnabled(enable);
else
m_impl->m_pendingContextMenuEnabled = enable ? 1 : 0;
}
bool wxWebViewEdge::IsContextMenuEnabled() const
{
wxCOMPtr<ICoreWebView2Settings> settings(m_impl->GetSettings());
if (settings)
{
BOOL menusEnabled = TRUE;
settings->get_AreDefaultContextMenusEnabled(&menusEnabled);
if (!menusEnabled)
return false;
}
return true;
}
void wxWebViewEdge::EnableAccessToDevTools(bool enable)
{
wxCOMPtr<ICoreWebView2Settings> settings(m_impl->GetSettings());
if (settings)
settings->put_AreDevToolsEnabled(enable);
else
m_impl->m_pendingAccessToDevToolsEnabled = enable ? 1 : 0;
}
bool wxWebViewEdge::IsAccessToDevToolsEnabled() const
{
wxCOMPtr<ICoreWebView2Settings> settings(m_impl->GetSettings());
if (settings)
{
BOOL devToolsEnabled = TRUE;
settings->get_AreDevToolsEnabled(&devToolsEnabled);
if (!devToolsEnabled)
return false;
}
return true;
}
void* wxWebViewEdge::GetNativeBackend() const
{
return m_impl->m_webView;
}
bool wxWebViewEdge::QueryCommandEnabled(const wxString& command) const
{
wxString resultStr;
const_cast<wxWebViewEdge*>(this)->RunScript(
wxString::Format("function f(){ return document.queryCommandEnabled('%s'); } f();", command), &resultStr);
return resultStr.IsSameAs("true", false);
}
void wxWebViewEdge::ExecCommand(const wxString& command)
{
RunScript(wxString::Format("document.execCommand('%s');", command));
}
bool wxWebViewEdge::RunScriptSync(const wxString& javascript, wxString* output)
{
bool scriptExecuted = false;
// Start script execution
HRESULT executionResult = m_impl->m_webView->ExecuteScript(javascript.wc_str(), Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[&scriptExecuted, &executionResult, output](HRESULT error, PCWSTR result) -> HRESULT
{
// Handle script execution callback
if (error == S_OK)
{
if (output)
output->assign(result);
}
else
executionResult = error;
scriptExecuted = true;
return S_OK;
}).Get());
// Wait for script exection
while (!scriptExecuted)
wxYield();
if (FAILED(executionResult))
{
if (output)
output->Printf("%s (0x%08lx)", wxSysErrorMsgStr(executionResult), executionResult);
return false;
}
else
return true;
}
bool wxWebViewEdge::RunScript(const wxString& javascript, wxString* output)
{
wxJSScriptWrapper wrapJS(javascript, &m_runScriptCount);
// This string is also used as an error indicator: it's cleared if there is
// no error or used in the warning message below if there is one.
wxString result;
if (RunScriptSync(wrapJS.GetWrappedCode(), &result)
&& result == wxS("true"))
{
if (RunScriptSync(wrapJS.GetUnwrappedOutputCode() + ";", &result))
{
if (output)
// Try to decode JSON string or return original
// result if it's not a valid JSON string
if (!wxJSON::DecodeString(result, output))
*output = result;
result.clear();
}
RunScriptSync(wrapJS.GetCleanUpCode());
}
if (!result.empty())
{
wxLogWarning(_("Error running JavaScript: %s"), result);
return false;
}
return true;
}
void wxWebViewEdge::RegisterHandler(wxSharedPtr<wxWebViewHandler> handler)
{
// TODO: could maybe be implemented via IWebView2WebView5::add_WebResourceRequested
wxLogDebug("Registering handlers is not supported");
}
void wxWebViewEdge::DoSetPage(const wxString& html, const wxString& WXUNUSED(baseUrl))
{
if (m_impl->m_webView)
m_impl->m_webView->NavigateToString(html.wc_str());
}
// wxWebViewFactoryEdge
bool wxWebViewFactoryEdge::IsAvailable()
{
return wxWebViewEdgeImpl::Initialize();
}
// ----------------------------------------------------------------------------
// Module ensuring all global/singleton objects are destroyed on shutdown.
// ----------------------------------------------------------------------------
class wxWebViewEdgeModule : public wxModule
{
public:
wxWebViewEdgeModule()
{
}
virtual bool OnInit() wxOVERRIDE
{
return true;
}
virtual void OnExit() wxOVERRIDE
{
wxWebViewEdgeImpl::Uninitialize();
}
private:
wxDECLARE_DYNAMIC_CLASS(wxWebViewEdgeModule);
};
wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewEdgeModule, wxModule);
#endif // wxUSE_WEBVIEW && wxUSE_WEBVIEW_EDGE