1080 lines
32 KiB
Plaintext
1080 lines
32 KiB
Plaintext
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/osx/webview_webkit.mm
|
|
// Purpose: wxWebViewWebKit - embeddable web kit control,
|
|
// OS X implementation of web view component
|
|
// Author: Jethro Grassie / Kevin Ollivier / Marianne Gagnon
|
|
// Modified by:
|
|
// Created: 2004-4-16
|
|
// Copyright: (c) Jethro Grassie / Kevin Ollivier / Marianne Gagnon
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// https://developer.apple.com/documentation/webkit/wkwebview
|
|
|
|
#include "wx/osx/webview_webkit.h"
|
|
|
|
#if wxUSE_WEBVIEW && wxUSE_WEBVIEW_WEBKIT && defined(__WXOSX__)
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/wx.h"
|
|
#endif
|
|
|
|
#include "wx/osx/private.h"
|
|
#include "wx/osx/core/cfref.h"
|
|
#include "wx/osx/private/available.h"
|
|
#include "wx/private/jsscriptwrapper.h"
|
|
|
|
#include "wx/hashmap.h"
|
|
#include "wx/filesys.h"
|
|
#include "wx/msgdlg.h"
|
|
#include "wx/textdlg.h"
|
|
#include "wx/filedlg.h"
|
|
|
|
#include <WebKit/WebKit.h>
|
|
#include <Foundation/NSURLError.h>
|
|
|
|
// using native types to get compile errors and warnings
|
|
|
|
#define DEBUG_WEBKIT_SIZING 0
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// macros
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewWebKit, wxWebView);
|
|
|
|
wxBEGIN_EVENT_TABLE(wxWebViewWebKit, wxControl)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
@interface WXWKWebView: WKWebView
|
|
{
|
|
wxWebViewWebKit* m_webView;
|
|
}
|
|
|
|
- (void)setWebView:(wxWebViewWebKit*)webView;
|
|
|
|
@end
|
|
|
|
@interface WebViewNavigationDelegate : NSObject<WKNavigationDelegate>
|
|
{
|
|
wxWebViewWebKit* webKitWindow;
|
|
}
|
|
|
|
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow;
|
|
|
|
@end
|
|
|
|
@interface WebViewUIDelegate : NSObject<WKUIDelegate>
|
|
{
|
|
wxWebViewWebKit* webKitWindow;
|
|
}
|
|
|
|
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow;
|
|
|
|
@end
|
|
|
|
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
|
|
@interface WebViewCustomProtocol : NSObject<WKURLSchemeHandler>
|
|
{
|
|
wxWebViewHandler* m_handler;
|
|
}
|
|
|
|
- (id)initWithHandler:(wxWebViewHandler*) handler;
|
|
|
|
@end
|
|
#endif // macOS 10.13+
|
|
|
|
@interface WebViewScriptMessageHandler: NSObject<WKScriptMessageHandler>
|
|
{
|
|
wxWebViewWebKit* webKitWindow;
|
|
}
|
|
|
|
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow;
|
|
|
|
@end
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxWebViewFactoryWebKit
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxVersionInfo wxWebViewFactoryWebKit::GetVersionInfo()
|
|
{
|
|
int verMaj, verMin, verMicro;
|
|
wxGetOsVersion(&verMaj, &verMin, &verMicro);
|
|
return wxVersionInfo("WKWebView", verMaj, verMin, verMicro);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// creation/destruction
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxWebViewWebKit::Create(wxWindow *parent,
|
|
wxWindowID winID,
|
|
const wxString& strURL,
|
|
const wxPoint& pos,
|
|
const wxSize& size, long style,
|
|
const wxString& name)
|
|
{
|
|
DontCreatePeer();
|
|
wxControl::Create(parent, winID, pos, size, style, wxDefaultValidator, name);
|
|
|
|
NSRect r = wxOSXGetFrameForControl( this, pos , size ) ;
|
|
WKWebViewConfiguration* webViewConfig = [[WKWebViewConfiguration alloc] init];
|
|
|
|
// WebKit API available since macOS 10.11 and iOS 9.0
|
|
SEL fullScreenSelector = @selector(_setFullScreenEnabled:);
|
|
if ([webViewConfig.preferences respondsToSelector:fullScreenSelector])
|
|
[webViewConfig.preferences performSelector:fullScreenSelector withObject:[NSNumber numberWithBool:YES]];
|
|
|
|
if (!m_handlers.empty())
|
|
{
|
|
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
|
|
if ( WX_IS_MACOS_AVAILABLE(10, 13) )
|
|
{
|
|
for (wxStringToWebHandlerMap::iterator it = m_handlers.begin(); it != m_handlers.end(); it++)
|
|
{
|
|
[webViewConfig setURLSchemeHandler:[[WebViewCustomProtocol alloc] initWithHandler:it->second.get()]
|
|
forURLScheme:wxCFStringRef(it->first).AsNSString()];
|
|
}
|
|
}
|
|
else
|
|
#endif // macOS 10.13+
|
|
wxLogDebug("Registering custom wxWebView handlers is not supported under macOS < 10.13");
|
|
}
|
|
|
|
m_webView = [[WXWKWebView alloc] initWithFrame:r configuration:webViewConfig];
|
|
[(WXWKWebView*)m_webView setWebView:this];
|
|
SetPeer(new wxWidgetCocoaImpl( this, m_webView ));
|
|
|
|
MacPostControlCreate(pos, size);
|
|
|
|
if (!m_customUserAgent.empty())
|
|
SetUserAgent(m_customUserAgent);
|
|
|
|
[m_webView setHidden:false];
|
|
|
|
|
|
// Register event listener interfaces
|
|
WebViewNavigationDelegate* navDelegate =
|
|
[[WebViewNavigationDelegate alloc] initWithWxWindow: this];
|
|
[m_webView addObserver:navDelegate forKeyPath:@"title" options:0 context:this];
|
|
|
|
[m_webView setNavigationDelegate:navDelegate];
|
|
|
|
m_navigationDelegate = navDelegate;
|
|
|
|
WebViewUIDelegate* uiDelegate =
|
|
[[WebViewUIDelegate alloc] initWithWxWindow: this];
|
|
|
|
[m_webView setUIDelegate:uiDelegate];
|
|
|
|
// WebKit API available since macOS 10.13 and iOS 11.0
|
|
SEL fullScreenDelegateSelector = @selector(_setFullscreenDelegate:);
|
|
if ([m_webView respondsToSelector:fullScreenDelegateSelector])
|
|
[m_webView performSelector:fullScreenDelegateSelector withObject:uiDelegate];
|
|
|
|
m_UIDelegate = uiDelegate;
|
|
|
|
LoadURL(strURL);
|
|
return true;
|
|
}
|
|
|
|
wxWebViewWebKit::~wxWebViewWebKit()
|
|
{
|
|
[m_webView removeObserver:m_navigationDelegate forKeyPath:@"title" context:this];
|
|
[m_webView setNavigationDelegate: nil];
|
|
[m_webView setUIDelegate: nil];
|
|
|
|
[m_navigationDelegate release];
|
|
[m_UIDelegate release];
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// public methods
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxWebViewWebKit::CanGoBack() const
|
|
{
|
|
if ( !m_webView )
|
|
return false;
|
|
|
|
return [m_webView canGoBack];
|
|
}
|
|
|
|
bool wxWebViewWebKit::CanGoForward() const
|
|
{
|
|
if ( !m_webView )
|
|
return false;
|
|
|
|
return [m_webView canGoForward];
|
|
}
|
|
|
|
void wxWebViewWebKit::GoBack()
|
|
{
|
|
if ( !m_webView )
|
|
return;
|
|
|
|
[m_webView goBack];
|
|
}
|
|
|
|
void wxWebViewWebKit::GoForward()
|
|
{
|
|
if ( !m_webView )
|
|
return;
|
|
|
|
[m_webView goForward];
|
|
}
|
|
|
|
bool wxWebViewWebKit::IsBusy() const
|
|
{
|
|
return m_webView.loading ? true : false;
|
|
}
|
|
|
|
void wxWebViewWebKit::Reload(wxWebViewReloadFlags flags)
|
|
{
|
|
if ( !m_webView )
|
|
return;
|
|
|
|
if (flags & wxWEBVIEW_RELOAD_NO_CACHE)
|
|
{
|
|
[m_webView reloadFromOrigin];
|
|
}
|
|
else
|
|
{
|
|
[m_webView reload];
|
|
}
|
|
}
|
|
|
|
void wxWebViewWebKit::Stop()
|
|
{
|
|
if ( !m_webView )
|
|
return;
|
|
|
|
[m_webView stopLoading];
|
|
}
|
|
|
|
void wxWebViewWebKit::Print()
|
|
{
|
|
|
|
// TODO: allow specifying the "show prompt" parameter in Print() ?
|
|
bool showPrompt = true;
|
|
|
|
if ( !m_webView )
|
|
return;
|
|
|
|
// As of macOS SDK 10.15 no offical printing API is available for WKWebView
|
|
// Try if the undocumented printOperationWithPrintInfo: is available and use it
|
|
// to create a printing operation
|
|
// https://bugs.webkit.org/show_bug.cgi?id=151276
|
|
SEL printSelector = @selector(printOperationWithPrintInfo:);
|
|
if (![m_webView respondsToSelector:printSelector])
|
|
printSelector = nil;
|
|
|
|
if (!printSelector)
|
|
{
|
|
wxLogError(_("Printing is not supported by the system web control"));
|
|
return;
|
|
}
|
|
|
|
NSPrintOperation* op = (NSPrintOperation*)[m_webView
|
|
performSelector:printSelector
|
|
withObject:[NSPrintInfo sharedPrintInfo]];
|
|
if (!op)
|
|
{
|
|
wxLogError(_("Print operation could not be initialized"));
|
|
return;
|
|
}
|
|
|
|
op.view.frame = m_webView.frame;
|
|
if (showPrompt)
|
|
{
|
|
[op setShowsPrintPanel: showPrompt];
|
|
// in my tests, the progress bar always freezes and it stops the whole
|
|
// print operation. do not turn this to true unless there is a
|
|
// workaround for the bug.
|
|
[op setShowsProgressPanel: false];
|
|
}
|
|
// Print it.
|
|
[op runOperationModalForWindow:m_webView.window
|
|
delegate:nil didRunSelector:nil contextInfo:nil];
|
|
}
|
|
|
|
void wxWebViewWebKit::SetEditable(bool WXUNUSED(enable))
|
|
{
|
|
}
|
|
|
|
bool wxWebViewWebKit::IsEditable() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxWebViewWebKit::IsAccessToDevToolsEnabled() const
|
|
{
|
|
// WebKit API available since macOS 10.11 and iOS 9.0
|
|
WKPreferences* prefs = m_webView.configuration.preferences;
|
|
SEL devToolsSelector = @selector(_developerExtrasEnabled);
|
|
id val = nil;
|
|
if ([prefs respondsToSelector:devToolsSelector])
|
|
val = [prefs performSelector:devToolsSelector];
|
|
return (val != nil);
|
|
}
|
|
|
|
void wxWebViewWebKit::EnableAccessToDevTools(bool enable)
|
|
{
|
|
// WebKit API available since macOS 10.11 and iOS 9.0
|
|
WKPreferences* prefs = m_webView.configuration.preferences;
|
|
SEL devToolsSelector = @selector(_setDeveloperExtrasEnabled:);
|
|
if ([prefs respondsToSelector:devToolsSelector])
|
|
[prefs performSelector:devToolsSelector withObject:(id)enable];
|
|
}
|
|
|
|
bool wxWebViewWebKit::SetUserAgent(const wxString& userAgent)
|
|
{
|
|
if (WX_IS_MACOS_AVAILABLE(10, 11))
|
|
{
|
|
if (m_webView)
|
|
m_webView.customUserAgent = wxCFStringRef(userAgent).AsNSString();
|
|
else
|
|
m_customUserAgent = userAgent;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void wxWebViewWebKit::SetZoomType(wxWebViewZoomType zoomType)
|
|
{
|
|
// there is only one supported zoom type at the moment so this setter
|
|
// does nothing beyond checking sanity
|
|
wxASSERT(zoomType == wxWEBVIEW_ZOOM_TYPE_LAYOUT);
|
|
}
|
|
|
|
wxWebViewZoomType wxWebViewWebKit::GetZoomType() const
|
|
{
|
|
return wxWEBVIEW_ZOOM_TYPE_LAYOUT;
|
|
}
|
|
|
|
bool wxWebViewWebKit::CanSetZoomType(wxWebViewZoomType type) const
|
|
{
|
|
switch (type)
|
|
{
|
|
// for now that's the only one that is supported
|
|
case wxWEBVIEW_ZOOM_TYPE_LAYOUT:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool wxWebViewWebKit::RunScriptSync(const wxString& javascript, wxString* output) const
|
|
{
|
|
__block bool scriptExecuted = false;
|
|
__block wxString outputStr;
|
|
__block bool scriptSuccess = false;
|
|
|
|
// Start script execution
|
|
[m_webView evaluateJavaScript:wxCFStringRef(javascript).AsNSString()
|
|
completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
|
|
if (error)
|
|
{
|
|
outputStr.assign(wxCFStringRef(error.localizedFailureReason).AsString());
|
|
}
|
|
else
|
|
{
|
|
if ([obj isKindOfClass:[NSNumber class]])
|
|
{
|
|
NSNumber* num = (NSNumber*) obj;
|
|
CFTypeID numID = CFGetTypeID((__bridge CFTypeRef)(num));
|
|
if (numID == CFBooleanGetTypeID())
|
|
outputStr = num.boolValue ? "true" : "false";
|
|
else
|
|
outputStr = wxCFStringRef::AsString(num.stringValue);
|
|
}
|
|
else if (obj)
|
|
outputStr.assign(wxCFStringRef::AsString([NSString stringWithFormat:@"%@", obj]));
|
|
|
|
scriptSuccess = true;
|
|
}
|
|
|
|
scriptExecuted = true;
|
|
}];
|
|
|
|
// Wait for script exection
|
|
while (!scriptExecuted)
|
|
wxYield();
|
|
|
|
if (output)
|
|
output->assign(outputStr);
|
|
|
|
return scriptSuccess;
|
|
}
|
|
|
|
bool wxWebViewWebKit::RunScript(const wxString& javascript, wxString* output) const
|
|
{
|
|
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.GetOutputCode() + ";", &result))
|
|
{
|
|
if (output)
|
|
*output = result;
|
|
result.clear();
|
|
}
|
|
|
|
RunScriptSync(wrapJS.GetCleanUpCode());
|
|
}
|
|
|
|
if (!result.empty())
|
|
{
|
|
wxLogWarning(_("Error running JavaScript: %s"), result);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxWebViewWebKit::AddScriptMessageHandler(const wxString& name)
|
|
{
|
|
[m_webView.configuration.userContentController addScriptMessageHandler:
|
|
[[WebViewScriptMessageHandler alloc] initWithWxWindow:this] name:wxCFStringRef(name).AsNSString()];
|
|
// Make webkit message handler available under common name
|
|
wxString js = wxString::Format("window.%s = window.webkit.messageHandlers.%s;",
|
|
name, name);
|
|
AddUserScript(js);
|
|
RunScript(js);
|
|
return true;
|
|
}
|
|
|
|
bool wxWebViewWebKit::RemoveScriptMessageHandler(const wxString& name)
|
|
{
|
|
[m_webView.configuration.userContentController removeScriptMessageHandlerForName:wxCFStringRef(name).AsNSString()];
|
|
return true;
|
|
}
|
|
|
|
bool wxWebViewWebKit::AddUserScript(const wxString& javascript,
|
|
wxWebViewUserScriptInjectionTime injectionTime)
|
|
{
|
|
WKUserScript* userScript =
|
|
[[WKUserScript alloc] initWithSource:wxCFStringRef(javascript).AsNSString()
|
|
injectionTime:(injectionTime == wxWEBVIEW_INJECT_AT_DOCUMENT_START) ?
|
|
WKUserScriptInjectionTimeAtDocumentStart : WKUserScriptInjectionTimeAtDocumentEnd
|
|
forMainFrameOnly:NO];
|
|
[m_webView.configuration.userContentController addUserScript:userScript];
|
|
return true;
|
|
}
|
|
|
|
void wxWebViewWebKit::RemoveAllUserScripts()
|
|
{
|
|
[m_webView.configuration.userContentController removeAllUserScripts];
|
|
}
|
|
|
|
void wxWebViewWebKit::LoadURL(const wxString& url)
|
|
{
|
|
[m_webView loadRequest:[NSURLRequest requestWithURL:
|
|
[NSURL URLWithString:wxCFStringRef(url).AsNSString()]]];
|
|
}
|
|
|
|
wxString wxWebViewWebKit::GetCurrentURL() const
|
|
{
|
|
return wxCFStringRef::AsString(m_webView.URL.absoluteString);
|
|
}
|
|
|
|
wxString wxWebViewWebKit::GetCurrentTitle() const
|
|
{
|
|
return wxCFStringRef::AsString(m_webView.title);
|
|
}
|
|
|
|
float wxWebViewWebKit::GetZoomFactor() const
|
|
{
|
|
return m_webView.magnification;
|
|
}
|
|
|
|
void wxWebViewWebKit::SetZoomFactor(float zoom)
|
|
{
|
|
m_webView.magnification = zoom;
|
|
}
|
|
|
|
void wxWebViewWebKit::DoSetPage(const wxString& src, const wxString& baseUrl)
|
|
{
|
|
if ( !m_webView )
|
|
return;
|
|
|
|
[m_webView loadHTMLString:wxCFStringRef( src ).AsNSString()
|
|
baseURL:[NSURL URLWithString:
|
|
wxCFStringRef( baseUrl ).AsNSString()]];
|
|
}
|
|
|
|
void wxWebViewWebKit::EnableHistory(bool WXUNUSED(enable))
|
|
{
|
|
if ( !m_webView )
|
|
return;
|
|
|
|
// TODO: implement
|
|
}
|
|
|
|
void wxWebViewWebKit::ClearHistory()
|
|
{
|
|
// TODO: implement
|
|
}
|
|
|
|
wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewWebKit::GetBackwardHistory()
|
|
{
|
|
wxVector<wxSharedPtr<wxWebViewHistoryItem> > backhist;
|
|
WKBackForwardList* history = [m_webView backForwardList];
|
|
int count = history.backList.count;
|
|
for(int i = -count; i < 0; i++)
|
|
{
|
|
WKBackForwardListItem* item = [history itemAtIndex:i];
|
|
wxString url = wxCFStringRef::AsString(item.URL.absoluteString);
|
|
wxString title = wxCFStringRef::AsString([item title]);
|
|
wxWebViewHistoryItem* wxitem = new wxWebViewHistoryItem(url, title);
|
|
wxitem->m_histItem = item;
|
|
wxSharedPtr<wxWebViewHistoryItem> itemptr(wxitem);
|
|
backhist.push_back(itemptr);
|
|
}
|
|
return backhist;
|
|
}
|
|
|
|
wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewWebKit::GetForwardHistory()
|
|
{
|
|
wxVector<wxSharedPtr<wxWebViewHistoryItem> > forwardhist;
|
|
WKBackForwardList* history = [m_webView backForwardList];
|
|
int count = history.forwardList.count;
|
|
for(int i = 1; i <= count; i++)
|
|
{
|
|
WKBackForwardListItem* item = [history itemAtIndex:i];
|
|
wxString url = wxCFStringRef::AsString(item.URL.absoluteString);
|
|
wxString title = wxCFStringRef::AsString([item title]);
|
|
wxWebViewHistoryItem* wxitem = new wxWebViewHistoryItem(url, title);
|
|
wxitem->m_histItem = item;
|
|
wxSharedPtr<wxWebViewHistoryItem> itemptr(wxitem);
|
|
forwardhist.push_back(itemptr);
|
|
}
|
|
return forwardhist;
|
|
}
|
|
|
|
void wxWebViewWebKit::LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem> item)
|
|
{
|
|
[m_webView goToBackForwardListItem:item->m_histItem];
|
|
}
|
|
|
|
bool wxWebViewWebKit::CanUndo() const
|
|
{
|
|
return [[m_webView undoManager] canUndo];
|
|
}
|
|
|
|
bool wxWebViewWebKit::CanRedo() const
|
|
{
|
|
return [[m_webView undoManager] canRedo];
|
|
}
|
|
|
|
void wxWebViewWebKit::Undo()
|
|
{
|
|
[[m_webView undoManager] undo];
|
|
}
|
|
|
|
void wxWebViewWebKit::Redo()
|
|
{
|
|
[[m_webView undoManager] redo];
|
|
}
|
|
|
|
void wxWebViewWebKit::RegisterHandler(wxSharedPtr<wxWebViewHandler> handler)
|
|
{
|
|
m_handlers[handler->GetName()] = handler;
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
// Listener interfaces
|
|
//------------------------------------------------------------
|
|
|
|
// NB: I'm still tracking this down, but it appears the Cocoa window
|
|
// still has these events fired on it while the Carbon control is being
|
|
// destroyed. Therefore, we must be careful to check both the existence
|
|
// of the Carbon control and the event handler before firing events.
|
|
|
|
@implementation WXWKWebView
|
|
|
|
- (void)setWebView:(wxWebViewWebKit *)webView
|
|
{
|
|
m_webView = webView;
|
|
}
|
|
|
|
#if !defined(__WXOSX_IPHONE__)
|
|
- (void)willOpenMenu:(NSMenu *)menu withEvent:(NSEvent *)event
|
|
{
|
|
if (m_webView && !m_webView->IsContextMenuEnabled())
|
|
[menu removeAllItems];
|
|
}
|
|
|
|
-(id)validRequestorForSendType:(NSString*)sendType returnType:(NSString*)returnType
|
|
{
|
|
if (m_webView && !m_webView->IsContextMenuEnabled())
|
|
return nil;
|
|
else
|
|
return [super validRequestorForSendType:sendType returnType:returnType];
|
|
}
|
|
|
|
- (BOOL)performKeyEquivalent:(NSEvent *)event
|
|
{
|
|
if ([event modifierFlags] & NSCommandKeyMask)
|
|
{
|
|
switch ([event.characters characterAtIndex:0])
|
|
{
|
|
case 'a':
|
|
[self selectAll:nil];
|
|
return YES;
|
|
case 'c':
|
|
m_webView->Copy();
|
|
return YES;
|
|
case 'v':
|
|
m_webView->Paste();
|
|
return YES;
|
|
case 'x':
|
|
m_webView->Cut();
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return [super performKeyEquivalent:event];
|
|
}
|
|
#endif // !defined(__WXOSX_IPHONE__)
|
|
|
|
@end
|
|
|
|
@implementation WebViewNavigationDelegate
|
|
|
|
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow
|
|
{
|
|
if (self = [super init])
|
|
{
|
|
webKitWindow = inWindow; // non retained
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context
|
|
{
|
|
if (context == webKitWindow)
|
|
{
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_TITLE_CHANGED,
|
|
webKitWindow->GetId(),
|
|
webKitWindow->GetCurrentURL(),
|
|
"");
|
|
|
|
event.SetString(webKitWindow->GetCurrentTitle());
|
|
|
|
webKitWindow->ProcessWindowEvent(event);
|
|
}
|
|
else
|
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
|
|
{
|
|
if (webKitWindow){
|
|
NSString *url = webView.URL.absoluteString;
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_NAVIGATED,
|
|
webKitWindow->GetId(),
|
|
wxCFStringRef::AsString( url ),
|
|
"");
|
|
|
|
if (webKitWindow && webKitWindow->GetEventHandler())
|
|
webKitWindow->GetEventHandler()->ProcessEvent(event);
|
|
}
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
|
|
{
|
|
if (webKitWindow){
|
|
NSString *url = webView.URL.absoluteString;
|
|
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_LOADED,
|
|
webKitWindow->GetId(),
|
|
wxCFStringRef::AsString( url ),
|
|
"");
|
|
|
|
if (webKitWindow && webKitWindow->GetEventHandler())
|
|
webKitWindow->GetEventHandler()->ProcessEvent(event);
|
|
}
|
|
}
|
|
|
|
wxString nsErrorToWxHtmlError(NSError* error, wxWebViewNavigationError* out)
|
|
{
|
|
*out = wxWEBVIEW_NAV_ERR_OTHER;
|
|
|
|
if ([[error domain] isEqualToString:NSURLErrorDomain])
|
|
{
|
|
switch ([error code])
|
|
{
|
|
case NSURLErrorCannotFindHost:
|
|
case NSURLErrorFileDoesNotExist:
|
|
case NSURLErrorRedirectToNonExistentLocation:
|
|
*out = wxWEBVIEW_NAV_ERR_NOT_FOUND;
|
|
break;
|
|
|
|
case NSURLErrorResourceUnavailable:
|
|
case NSURLErrorHTTPTooManyRedirects:
|
|
case NSURLErrorDataLengthExceedsMaximum:
|
|
case NSURLErrorBadURL:
|
|
case NSURLErrorFileIsDirectory:
|
|
*out = wxWEBVIEW_NAV_ERR_REQUEST;
|
|
break;
|
|
|
|
case NSURLErrorTimedOut:
|
|
case NSURLErrorDNSLookupFailed:
|
|
case NSURLErrorNetworkConnectionLost:
|
|
case NSURLErrorCannotConnectToHost:
|
|
case NSURLErrorNotConnectedToInternet:
|
|
//case NSURLErrorInternationalRoamingOff:
|
|
//case NSURLErrorCallIsActive:
|
|
//case NSURLErrorDataNotAllowed:
|
|
*out = wxWEBVIEW_NAV_ERR_CONNECTION;
|
|
break;
|
|
|
|
case NSURLErrorCancelled:
|
|
case NSURLErrorUserCancelledAuthentication:
|
|
*out = wxWEBVIEW_NAV_ERR_USER_CANCELLED;
|
|
break;
|
|
|
|
case NSURLErrorCannotDecodeRawData:
|
|
case NSURLErrorCannotDecodeContentData:
|
|
case NSURLErrorCannotParseResponse:
|
|
case NSURLErrorBadServerResponse:
|
|
*out = wxWEBVIEW_NAV_ERR_REQUEST;
|
|
break;
|
|
|
|
case NSURLErrorUserAuthenticationRequired:
|
|
case NSURLErrorSecureConnectionFailed:
|
|
case NSURLErrorClientCertificateRequired:
|
|
*out = wxWEBVIEW_NAV_ERR_AUTH;
|
|
break;
|
|
|
|
case NSURLErrorNoPermissionsToReadFile:
|
|
*out = wxWEBVIEW_NAV_ERR_SECURITY;
|
|
break;
|
|
|
|
case NSURLErrorServerCertificateHasBadDate:
|
|
case NSURLErrorServerCertificateUntrusted:
|
|
case NSURLErrorServerCertificateHasUnknownRoot:
|
|
case NSURLErrorServerCertificateNotYetValid:
|
|
case NSURLErrorClientCertificateRejected:
|
|
*out = wxWEBVIEW_NAV_ERR_CERTIFICATE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
wxString message = wxCFStringRef::AsString([error localizedDescription]);
|
|
NSString* detail = [error localizedFailureReason];
|
|
if (detail != NULL)
|
|
{
|
|
message = message + " (" + wxCFStringRef::AsString(detail) + ")";
|
|
}
|
|
return message;
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView
|
|
didFailNavigation:(WKNavigation *)navigation
|
|
withError:(NSError *)error;
|
|
{
|
|
if (webKitWindow){
|
|
NSString *url = webView.URL.absoluteString;
|
|
|
|
wxWebViewNavigationError type;
|
|
wxString description = nsErrorToWxHtmlError(error, &type);
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_ERROR,
|
|
webKitWindow->GetId(),
|
|
wxCFStringRef::AsString( url ),
|
|
wxEmptyString);
|
|
event.SetString(description);
|
|
event.SetInt(type);
|
|
|
|
if (webKitWindow && webKitWindow->GetEventHandler())
|
|
{
|
|
webKitWindow->GetEventHandler()->ProcessEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView
|
|
didFailProvisionalNavigation:(WKNavigation *)navigation
|
|
withError:(NSError *)error;
|
|
{
|
|
if (webKitWindow){
|
|
NSString *url = webView.URL.absoluteString;
|
|
|
|
wxWebViewNavigationError type;
|
|
wxString description = nsErrorToWxHtmlError(error, &type);
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_ERROR,
|
|
webKitWindow->GetId(),
|
|
wxCFStringRef::AsString( url ),
|
|
wxEmptyString);
|
|
event.SetString(description);
|
|
event.SetInt(type);
|
|
|
|
if (webKitWindow && webKitWindow->GetEventHandler())
|
|
webKitWindow->GetEventHandler()->ProcessEvent(event);
|
|
}
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
|
|
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
|
|
{
|
|
NSString *url = [[navigationAction.request URL] absoluteString];
|
|
wxWebViewNavigationActionFlags navFlags =
|
|
navigationAction.navigationType == WKNavigationTypeLinkActivated ?
|
|
wxWEBVIEW_NAV_ACTION_USER :
|
|
wxWEBVIEW_NAV_ACTION_OTHER;
|
|
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_NAVIGATING,
|
|
webKitWindow->GetId(),
|
|
wxCFStringRef::AsString( url ), "", navFlags);
|
|
|
|
if (webKitWindow && webKitWindow->GetEventHandler())
|
|
webKitWindow->GetEventHandler()->ProcessEvent(event);
|
|
|
|
decisionHandler(event.IsAllowed() ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
|
|
}
|
|
|
|
@end
|
|
|
|
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
|
|
@implementation WebViewCustomProtocol
|
|
|
|
- (id)initWithHandler:(wxWebViewHandler *)handler
|
|
{
|
|
m_handler = handler;
|
|
return self;
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask
|
|
WX_API_AVAILABLE_MACOS(10, 13)
|
|
{
|
|
NSURLRequest *request = urlSchemeTask.request;
|
|
NSString* path = [[request URL] absoluteString];
|
|
|
|
wxString wxpath = wxCFStringRef::AsString(path);
|
|
|
|
wxFSFile* file = m_handler->GetFile(wxpath);
|
|
|
|
if (!file)
|
|
{
|
|
NSError *error = [[NSError alloc] initWithDomain:NSURLErrorDomain
|
|
code:NSURLErrorFileDoesNotExist
|
|
userInfo:nil];
|
|
|
|
[urlSchemeTask didFailWithError:error];
|
|
|
|
[error release];
|
|
|
|
return;
|
|
}
|
|
|
|
size_t length = file->GetStream()->GetLength();
|
|
|
|
|
|
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[request URL]
|
|
MIMEType:wxCFStringRef(file->GetMimeType()).AsNSString()
|
|
expectedContentLength:length
|
|
textEncodingName:nil];
|
|
|
|
//Load the data, we malloc it so it is tidied up properly
|
|
void* buffer = malloc(length);
|
|
file->GetStream()->Read(buffer, length);
|
|
NSData *data = [[NSData alloc] initWithBytesNoCopy:buffer length:length];
|
|
|
|
//Set the data
|
|
[urlSchemeTask didReceiveResponse:response];
|
|
[urlSchemeTask didReceiveData:data];
|
|
|
|
//Notify that we have finished
|
|
[urlSchemeTask didFinish];
|
|
|
|
[data release];
|
|
|
|
[response release];
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask
|
|
WX_API_AVAILABLE_MACOS(10, 13)
|
|
{
|
|
|
|
}
|
|
|
|
@end
|
|
#endif // macOS 10.13+
|
|
|
|
|
|
@implementation WebViewUIDelegate
|
|
|
|
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow
|
|
{
|
|
if (self = [super init])
|
|
{
|
|
webKitWindow = inWindow; // non retained
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (WKWebView *)webView:(WKWebView *)webView
|
|
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
|
|
forNavigationAction:(WKNavigationAction *)navigationAction
|
|
windowFeatures:(WKWindowFeatures *)windowFeatures
|
|
{
|
|
wxWebViewNavigationActionFlags navFlags =
|
|
navigationAction.navigationType == WKNavigationTypeLinkActivated ?
|
|
wxWEBVIEW_NAV_ACTION_USER :
|
|
wxWEBVIEW_NAV_ACTION_OTHER;
|
|
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_NEWWINDOW,
|
|
webKitWindow->GetId(),
|
|
wxCFStringRef::AsString( navigationAction.request.URL.absoluteString ),
|
|
"", navFlags);
|
|
|
|
if (webKitWindow && webKitWindow->GetEventHandler())
|
|
webKitWindow->GetEventHandler()->ProcessEvent(event);
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message
|
|
initiatedByFrame:(WKFrameInfo *)frame
|
|
completionHandler:(void (^)())completionHandler
|
|
{
|
|
wxMessageDialog dlg(webKitWindow->GetParent(), wxCFStringRef::AsString(message));
|
|
dlg.ShowModal();
|
|
completionHandler();
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message
|
|
initiatedByFrame:(WKFrameInfo *)frame
|
|
completionHandler:(void (^)(BOOL))completionHandler
|
|
{
|
|
wxMessageDialog dlg(webKitWindow->GetParent(), wxCFStringRef::AsString(message),
|
|
wxMessageBoxCaptionStr, wxOK|wxCANCEL|wxCENTRE);
|
|
completionHandler(dlg.ShowModal() == wxID_OK);
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
|
|
defaultText:(NSString *)defaultText
|
|
initiatedByFrame:(WKFrameInfo *)frame
|
|
completionHandler:(void (^)(NSString * _Nullable))completionHandler
|
|
{
|
|
wxString resultText;
|
|
wxTextEntryDialog dlg(webKitWindow->GetParent(), wxCFStringRef::AsString(prompt),
|
|
wxGetTextFromUserPromptStr, wxCFStringRef::AsString(defaultText));
|
|
if (dlg.ShowModal() == wxID_OK)
|
|
resultText = dlg.GetValue();
|
|
|
|
completionHandler(wxCFStringRef(resultText).AsNSString());
|
|
}
|
|
|
|
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
|
|
- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters
|
|
initiatedByFrame:(WKFrameInfo *)frame
|
|
completionHandler:(void (^)(NSArray<NSURL *> * _Nullable))completionHandler
|
|
WX_API_AVAILABLE_MACOS(10, 12)
|
|
{
|
|
long style = wxFD_OPEN | wxFD_FILE_MUST_EXIST;
|
|
if (parameters.allowsMultipleSelection)
|
|
style |= wxFD_MULTIPLE;
|
|
|
|
wxFileDialog dlg(webKitWindow->GetParent(), wxFileSelectorPromptStr, "", "",
|
|
wxFileSelectorDefaultWildcardStr, style);
|
|
if (dlg.ShowModal() == wxID_OK)
|
|
{
|
|
wxArrayString filePaths;
|
|
dlg.GetPaths(filePaths);
|
|
NSMutableArray* urls = [[NSMutableArray alloc] init];
|
|
for (wxArrayString::iterator it = filePaths.begin(); it != filePaths.end(); it++)
|
|
[urls addObject:[NSURL fileURLWithPath:wxCFStringRef(*it).AsNSString()]];
|
|
completionHandler(urls);
|
|
[urls release];
|
|
}
|
|
else
|
|
completionHandler(nil);
|
|
}
|
|
#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
|
|
|
|
// The following WKUIDelegateMethods are undocumented as of macOS SDK 11.0,
|
|
// but are documented in the WebKit cocoa interface headers:
|
|
// https://github.com/WebKit/WebKit/blob/main/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h
|
|
|
|
- (void)_webView:(WKWebView *)webView printFrame:(WKFrameInfo*)frame
|
|
{
|
|
webKitWindow->Print();
|
|
}
|
|
|
|
- (void)SendFullscreenChangedEvent:(int)status
|
|
{
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_FULLSCREEN_CHANGED, webKitWindow->GetId(),
|
|
webKitWindow->GetCurrentURL(), wxString());
|
|
event.SetEventObject(webKitWindow);
|
|
event.SetInt(status);
|
|
webKitWindow->HandleWindowEvent(event);
|
|
}
|
|
|
|
- (void)_webViewDidEnterFullscreen:(WKWebView *)webView
|
|
{
|
|
[self SendFullscreenChangedEvent:1];
|
|
}
|
|
|
|
- (void)_webViewDidExitFullscreen:(WKWebView *)webView
|
|
{
|
|
[self SendFullscreenChangedEvent:0];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation WebViewScriptMessageHandler
|
|
|
|
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow
|
|
{
|
|
if (self = [super init])
|
|
{
|
|
webKitWindow = inWindow; // non retained
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)userContentController:(nonnull WKUserContentController *)userContentController
|
|
didReceiveScriptMessage:(nonnull WKScriptMessage *)message
|
|
{
|
|
wxWebViewEvent event(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED,
|
|
webKitWindow->GetId(),
|
|
webKitWindow->GetCurrentURL(),
|
|
"",
|
|
wxWEBVIEW_NAV_ACTION_NONE,
|
|
wxCFStringRef::AsString(message.name));
|
|
if ([message.body isKindOfClass:NSString.class])
|
|
event.SetString(wxCFStringRef::AsString(message.body));
|
|
else if ([message.body isKindOfClass:NSNumber.class])
|
|
event.SetString(wxCFStringRef::AsString(((NSNumber*)message.body).stringValue));
|
|
else if ([message.body isKindOfClass:NSDate.class])
|
|
event.SetString(wxCFStringRef::AsString(((NSDate*)message.body).description));
|
|
else if ([message.body isKindOfClass:NSNull.class])
|
|
event.SetString("null");
|
|
else if ([message.body isKindOfClass:NSDictionary.class] || [message.body isKindOfClass:NSArray.class])
|
|
{
|
|
NSError* error = nil;
|
|
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:message.body options:0 error:&error];
|
|
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
|
event.SetString(wxCFStringRef::AsString(jsonString));
|
|
}
|
|
|
|
webKitWindow->ProcessWindowEvent(event);
|
|
}
|
|
|
|
@end
|
|
|
|
#endif //wxUSE_WEBVIEW && wxUSE_WEBVIEW_WEBKIT
|