Improvements to xwidget on macOS (bug#60703)

* src/nsxwidget.m ()
([XwWebView initWithFrame:configuration:xwidget:])
(nsxwidget_init):  Fixed memory leaks: when sending an alloc
message to an object, send an autorelease message to any objects
we won't explictly release.
([XwWebView webView:didFinishNavigation:]): Second string to
store in 'store_xwidget_event_string' is "load finished" rather
than empty string.
([XwWebView webView:didStartProvisionalNavigation:])
([XwWebView webView:didReceiveServerRedirectForProvisionalNavigation:])
([XwWebView webView:didCommitNavigation:]): New functions.
(nsxwidget_webkit_estimated_load_progress): New function.
(nsxwidget_webkit_stop_loading): New function.
* src/xwidget.c (Fxwidget_webkit_estimated_load_progress): Call
'nsxwidget_webkit_estimated_load_progress' if we're on MacOS.
(Fxwidget_webkit_stop_loading): Call 'nsxwidget_webkit_stop_loading'
if we're on MacOS.
(syms_of_xwidget): Define symbol for function.
'xwidget_webkit_estimated_load_progress' if we're on MacOS.
* src/nsxwidget.h: Signature for functions
'nsxwidget_webkit_estimated_load_progress' and
'nsxwidget_webkit_stop_loading'.
* lisp/xwidget.el (xwidget-webkit-current-url): Message URL rather
than return value of 'kill-new' (which is always nil).
This commit is contained in:
Andrew De Angelis 2023-02-23 22:47:41 -05:00 committed by Eli Zaretskii
parent 3f43a16bc6
commit a137f71c67
4 changed files with 114 additions and 54 deletions

View file

@ -925,7 +925,8 @@ Return the buffer."
"Display the current xwidget webkit URL and place it on the `kill-ring'." "Display the current xwidget webkit URL and place it on the `kill-ring'."
(interactive nil xwidget-webkit-mode) (interactive nil xwidget-webkit-mode)
(let ((url (xwidget-webkit-uri (xwidget-webkit-current-session)))) (let ((url (xwidget-webkit-uri (xwidget-webkit-current-session))))
(message "URL: %s" (kill-new (or url ""))))) (when url (kill-new url))
(message "URL: %s" url)))
(defun xwidget-webkit-browse-history () (defun xwidget-webkit-browse-history ()
"Display a buffer containing the history of page loads." "Display a buffer containing the history of page loads."

View file

@ -36,6 +36,8 @@ Lisp_Object nsxwidget_webkit_uri (struct xwidget *xw);
Lisp_Object nsxwidget_webkit_title (struct xwidget *xw); Lisp_Object nsxwidget_webkit_title (struct xwidget *xw);
void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri); void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri);
void nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos); void nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos);
double nsxwidget_webkit_estimated_load_progress(struct xwidget *xw);
void nsxwidget_webkit_stop_loading (struct xwidget *xw);
void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change); void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change);
void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
Lisp_Object fun); Lisp_Object fun);

View file

@ -57,12 +57,13 @@ @interface XwWebView : WKWebView
@end @end
@implementation XwWebView : WKWebView @implementation XwWebView : WKWebView
- (id)initWithFrame:(CGRect)frame - (id) initWithFrame:(CGRect)frame
configuration:(WKWebViewConfiguration *)configuration configuration:(WKWebViewConfiguration *)configuration
xwidget:(struct xwidget *)xw xwidget:(struct xwidget *)xw
{ {
/* Script controller to add script message handler and user script. */ /* Script controller to add script message handler and user script. */
WKUserContentController *scriptor = [[WKUserContentController alloc] init]; WKUserContentController *scriptor = [[[WKUserContentController alloc] init]
autorelease];
configuration.userContentController = scriptor; configuration.userContentController = scriptor;
/* Enable inspect element context menu item for debugging. */ /* Enable inspect element context menu item for debugging. */
@ -81,7 +82,8 @@ - (id)initWithFrame:(CGRect)frame
if (self) if (self)
{ {
self.xw = xw; self.xw = xw;
self.urlScriptBlocked = [[NSMutableDictionary alloc] init]; self.urlScriptBlocked = [[[NSMutableDictionary alloc] init]
autorelease];
self.navigationDelegate = self; self.navigationDelegate = self;
self.UIDelegate = self; self.UIDelegate = self;
self.customUserAgent = self.customUserAgent =
@ -89,23 +91,48 @@ - (id)initWithFrame:(CGRect)frame
@" AppleWebKit/603.3.8 (KHTML, like Gecko)" @" AppleWebKit/603.3.8 (KHTML, like Gecko)"
@" Version/11.0.1 Safari/603.3.8"; @" Version/11.0.1 Safari/603.3.8";
[scriptor addScriptMessageHandler:self name:@"keyDown"]; [scriptor addScriptMessageHandler:self name:@"keyDown"];
[scriptor addUserScript:[[WKUserScript alloc] WKUserScript *userScript = [[[WKUserScript alloc]
initWithSource:xwScript initWithSource:xwScript
injectionTime: injectionTime:
WKUserScriptInjectionTimeAtDocumentStart WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO]]; forMainFrameOnly:NO] autorelease];
[scriptor addUserScript:userScript];
} }
return self; return self;
} }
- (void)webView:(WKWebView *)webView /* These 4 functions emulate the behavior of webkit_view_load_changed_cb
in the GTK implementation*/
- (void) webView:(WKWebView *)webView
didFinishNavigation:(WKNavigation *)navigation didFinishNavigation:(WKNavigation *)navigation
{ {
if (EQ (Fbuffer_live_p (self.xw->buffer), Qt)) if (EQ (Fbuffer_live_p (self.xw->buffer), Qt))
store_xwidget_event_string (self.xw, "load-changed", ""); store_xwidget_event_string (self.xw, "load-changed", "load-finished");
} }
- (void)webView:(WKWebView *)webView - (void) webView:(WKWebView *)webView
didStartProvisionalNavigation:(WKNavigation *)navigation
{
if (EQ (Fbuffer_live_p (self.xw->buffer), Qt))
store_xwidget_event_string (self.xw, "load-changed", "load-started");
}
- (void) webView:(WKWebView *)webView
didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
{
if (EQ (Fbuffer_live_p (self.xw->buffer), Qt))
store_xwidget_event_string (self.xw, "load-changed", "load-redirected");
}
/* Start loading WKWebView */
- (void) webView:(WKWebView *)webView
didCommitNavigation:(WKNavigation *)navigation
{
if (EQ (Fbuffer_live_p (self.xw->buffer), Qt))
store_xwidget_event_string (self.xw, "load-changed", "load-committed");
}
- (void) webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{ {
@ -114,13 +141,13 @@ - (void)webView:(WKWebView *)webView
decisionHandler (WKNavigationActionPolicyAllow); decisionHandler (WKNavigationActionPolicyAllow);
break; break;
default: default:
// decisionHandler (WKNavigationActionPolicyCancel); /* decisionHandler (WKNavigationActionPolicyCancel); */
decisionHandler (WKNavigationActionPolicyAllow); decisionHandler (WKNavigationActionPolicyAllow);
break; break;
} }
} }
- (void)webView:(WKWebView *)webView - (void) webView:(WKWebView *)webView
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{ {
@ -166,7 +193,7 @@ - (void)webView:(WKWebView *)webView
/* No additional new webview or emacs window will be created /* No additional new webview or emacs window will be created
for <a ... target="_blank">. */ for <a ... target="_blank">. */
- (WKWebView *)webView:(WKWebView *)webView - (WKWebView *) webView:(WKWebView *)webView
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
forNavigationAction:(WKNavigationAction *)navigationAction forNavigationAction:(WKNavigationAction *)navigationAction
windowFeatures:(WKWindowFeatures *)windowFeatures windowFeatures:(WKWindowFeatures *)windowFeatures
@ -177,7 +204,7 @@ - (WKWebView *)webView:(WKWebView *)webView
} }
/* Open panel for file upload. */ /* Open panel for file upload. */
- (void)webView:(WKWebView *)webView - (void) webView:(WKWebView *)webView
runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters
initiatedByFrame:(WKFrameInfo *)frame initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler
@ -197,13 +224,13 @@ - (void)webView:(WKWebView *)webView
- Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet). - Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet).
*/ */
- (void)mouseDown:(NSEvent *)event - (void) mouseDown:(NSEvent *)event
{ {
[self.xw->xv->emacswindow mouseDown:event]; [self.xw->xv->emacswindow mouseDown:event];
[super mouseDown:event]; [super mouseDown:event];
} }
- (void)mouseUp:(NSEvent *)event - (void) mouseUp:(NSEvent *)event
{ {
[self.xw->xv->emacswindow mouseUp:event]; [self.xw->xv->emacswindow mouseUp:event];
[super mouseUp:event]; [super mouseUp:event];
@ -214,7 +241,7 @@ - (void)mouseUp:(NSEvent *)event
emacs as first responder to avoid focus held in an input element emacs as first responder to avoid focus held in an input element
with matching text. */ with matching text. */
- (void)keyDown:(NSEvent *)event - (void) keyDown:(NSEvent *)event
{ {
Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil); Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil);
Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ()); Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ());
@ -250,7 +277,7 @@ - (void)keyDown:(NSEvent *)event
}]; }];
} }
- (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray - (void) interpretKeyEvents:(NSArray<NSEvent *> *)eventArray
{ {
/* We should do nothing and do not forward (default implementation /* We should do nothing and do not forward (default implementation
if we not override here) to let emacs collect key events and ask if we not override here) to let emacs collect key events and ask
@ -258,7 +285,7 @@ - (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray
} }
static NSString *xwScript; static NSString *xwScript;
+ (void)initialize + (void) initialize
{ {
/* Find out if an input element has focus. /* Find out if an input element has focus.
Message to script message handler when 'C-g' key down. */ Message to script message handler when 'C-g' key down. */
@ -284,7 +311,7 @@ + (void)initialize
/* Confirming to WKScriptMessageHandler, listens concerning keyDown in /* Confirming to WKScriptMessageHandler, listens concerning keyDown in
webkit. Currently 'C-g'. */ webkit. Currently 'C-g'. */
- (void)userContentController:(WKUserContentController *)userContentController - (void) userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message didReceiveScriptMessage:(WKScriptMessage *)message
{ {
if ([message.body isEqualToString:@"C-g"]) if ([message.body isEqualToString:@"C-g"])
@ -343,6 +370,20 @@ - (void)userContentController:(WKUserContentController *)userContentController
} }
} }
double
nsxwidget_webkit_estimated_load_progress (struct xwidget *xw)
{
XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
return xwWebView.estimatedProgress;
}
void
nsxwidget_webkit_stop_loading (struct xwidget *xw)
{
XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
[xwWebView stopLoading];
}
void void
nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change) nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change)
{ {
@ -430,7 +471,7 @@ - (void)userContentController:(WKUserContentController *)userContentController
} }
else if (result && FUNCTIONP (fun)) else if (result && FUNCTIONP (fun))
{ {
// NSLog (@"result=%@, type=%@", result, [result class]); /* NSLog (@"result=%@, type=%@", result, [result class]); */
Lisp_Object lisp_value = js_to_lisp (result); Lisp_Object lisp_value = js_to_lisp (result);
store_xwidget_js_callback_event (xw, fun, lisp_value); store_xwidget_js_callback_event (xw, fun, lisp_value);
} }
@ -440,19 +481,20 @@ - (void)userContentController:(WKUserContentController *)userContentController
/* Window containing an xwidget. */ /* Window containing an xwidget. */
@implementation XwWindow @implementation XwWindow
- (BOOL)isFlipped { return YES; } - (BOOL) isFlipped { return YES; }
@end @end
/* Xwidget model, macOS Cocoa part. */ /* Xwidget model, macOS Cocoa part. */
void void
nsxwidget_init(struct xwidget *xw) nsxwidget_init (struct xwidget *xw)
{ {
block_input (); block_input ();
NSRect rect = NSMakeRect (0, 0, xw->width, xw->height); NSRect rect = NSMakeRect (0, 0, xw->width, xw->height);
xw->xwWidget = [[XwWebView alloc] xw->xwWidget = [[XwWebView alloc]
initWithFrame:rect initWithFrame:rect
configuration:[[WKWebViewConfiguration alloc] init] configuration:[[[WKWebViewConfiguration alloc] init]
autorelease]
xwidget:xw]; xwidget:xw];
xw->xwWindow = [[XwWindow alloc] xw->xwWindow = [[XwWindow alloc]
initWithFrame:rect]; initWithFrame:rect];
@ -470,16 +512,18 @@ - (BOOL)isFlipped { return YES; }
((XwWebView *) xw->xwWidget).configuration.userContentController; ((XwWebView *) xw->xwWidget).configuration.userContentController;
[scriptor removeAllUserScripts]; [scriptor removeAllUserScripts];
[scriptor removeScriptMessageHandlerForName:@"keyDown"]; [scriptor removeScriptMessageHandlerForName:@"keyDown"];
[scriptor release];
if (xw->xv) if (xw->xv)
xw->xv->model = Qnil; /* Make sure related view stale. */ xw->xv->model = Qnil; /* Make sure related view stale. */
/* This stops playing audio when a xwidget-webkit buffer is /* This stops playing audio when a xwidget-webkit buffer is
killed. I could not find other solution. */ killed. I could not find other solution.
TODO: improve this */
nsxwidget_webkit_goto_uri (xw, "about:blank"); nsxwidget_webkit_goto_uri (xw, "about:blank");
[((XwWebView *) xw->xwWidget).urlScriptBlocked release]; [((XwWebView *) xw->xwWidget).urlScriptBlocked release];
[xw->xwWidget removeFromSuperviewWithoutNeedingDisplay]; [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay];
[xw->xwWidget release]; [xw->xwWidget release];
[xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
[xw->xwWindow release]; [xw->xwWindow release];
@ -507,7 +551,7 @@ - (BOOL)isFlipped { return YES; }
/* Xwidget view, macOS Cocoa part. */ /* Xwidget view, macOS Cocoa part. */
@implementation XvWindow : NSView @implementation XvWindow : NSView
- (BOOL)isFlipped { return YES; } - (BOOL) isFlipped { return YES; }
@end @end
void void

View file

@ -3063,6 +3063,36 @@ DEFUN ("xwidget-webkit-title",
#endif #endif
} }
DEFUN ("xwidget-webkit-estimated-load-progress",
Fxwidget_webkit_estimated_load_progress, Sxwidget_webkit_estimated_load_progress,
1, 1, 0, doc: /* Get the estimated load progress of XWIDGET, a WebKit widget.
Return a value ranging from 0.0 to 1.0, based on how close XWIDGET
is to completely loading its page. */)
(Lisp_Object xwidget)
{
struct xwidget *xw;
#ifdef USE_GTK
WebKitWebView *webview;
#endif
double value;
CHECK_LIVE_XWIDGET (xwidget);
xw = XXWIDGET (xwidget);
CHECK_WEBKIT_WIDGET (xw);
block_input ();
#ifdef USE_GTK
webview = WEBKIT_WEB_VIEW (xw->widget_osr);
value = webkit_web_view_get_estimated_load_progress (webview);
#elif defined NS_IMPL_COCOA
value = nsxwidget_webkit_estimated_load_progress (xw);
#endif
unblock_input ();
return make_float (value);
}
DEFUN ("xwidget-webkit-goto-uri", DEFUN ("xwidget-webkit-goto-uri",
Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri, Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
2, 2, 0, 2, 2, 0,
@ -3810,28 +3840,6 @@ LIMIT is not specified or nil, it is treated as `50'. */)
return list3 (back, here, forward); return list3 (back, here, forward);
} }
DEFUN ("xwidget-webkit-estimated-load-progress",
Fxwidget_webkit_estimated_load_progress, Sxwidget_webkit_estimated_load_progress,
1, 1, 0, doc: /* Get the estimated load progress of XWIDGET, a WebKit widget.
Return a value ranging from 0.0 to 1.0, based on how close XWIDGET
is to completely loading its page. */)
(Lisp_Object xwidget)
{
struct xwidget *xw;
WebKitWebView *webview;
double value;
CHECK_LIVE_XWIDGET (xwidget);
xw = XXWIDGET (xwidget);
CHECK_WEBKIT_WIDGET (xw);
block_input ();
webview = WEBKIT_WEB_VIEW (xw->widget_osr);
value = webkit_web_view_get_estimated_load_progress (webview);
unblock_input ();
return make_float (value);
}
#endif #endif
DEFUN ("xwidget-webkit-set-cookie-storage-file", DEFUN ("xwidget-webkit-set-cookie-storage-file",
@ -3874,19 +3882,23 @@ This will stop any data transfer that may still be in progress inside
XWIDGET as part of loading a page. */) XWIDGET as part of loading a page. */)
(Lisp_Object xwidget) (Lisp_Object xwidget)
{ {
#ifdef USE_GTK
struct xwidget *xw; struct xwidget *xw;
#ifdef USE_GTK
WebKitWebView *webview; WebKitWebView *webview;
#endif
CHECK_LIVE_XWIDGET (xwidget); CHECK_LIVE_XWIDGET (xwidget);
xw = XXWIDGET (xwidget); xw = XXWIDGET (xwidget);
CHECK_WEBKIT_WIDGET (xw); CHECK_WEBKIT_WIDGET (xw);
block_input (); block_input ();
#ifdef USE_GTK
webview = WEBKIT_WEB_VIEW (xw->widget_osr); webview = WEBKIT_WEB_VIEW (xw->widget_osr);
webkit_web_view_stop_loading (webview); webkit_web_view_stop_loading (webview);
unblock_input (); #elif defined NS_IMPL_COCOA
nsxwidget_webkit_stop_loading (xw);
#endif #endif
unblock_input ();
return Qnil; return Qnil;
} }
@ -3936,8 +3948,9 @@ syms_of_xwidget (void)
#ifdef USE_GTK #ifdef USE_GTK
defsubr (&Sxwidget_webkit_load_html); defsubr (&Sxwidget_webkit_load_html);
defsubr (&Sxwidget_webkit_back_forward_list); defsubr (&Sxwidget_webkit_back_forward_list);
defsubr (&Sxwidget_webkit_estimated_load_progress);
#endif #endif
defsubr (&Sxwidget_webkit_estimated_load_progress);
defsubr (&Skill_xwidget); defsubr (&Skill_xwidget);
DEFSYM (QCxwidget, ":xwidget"); DEFSYM (QCxwidget, ":xwidget");