aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSungbin Jo2020-08-12 12:12:34 +0200
committerLars Ingebrigtsen2020-08-12 12:12:34 +0200
commitd089c4fbfc8be432dc3015a99b4044dab0a0de97 (patch)
treed4c694ec35c3df955db8566d91fdf5b36fbe1875 /src
parent38d70f79a613af771f73daaa6307baed0a59e7d7 (diff)
downloademacs-d089c4fbfc8be432dc3015a99b4044dab0a0de97.tar.gz
emacs-d089c4fbfc8be432dc3015a99b4044dab0a0de97.zip
Add xwidget support for macOS
Co-authored-by: Jaesup Kwak <veshboo@gmail.com> * configure.ac: Allow '--with-xwidgets' for "${NS_IMPL_COCOA}". * etc/NEWS: Mention new feature. * etc/TODO: Remove done TODO to implement xwidget in NeXTstep port. * lisp/xwidget.el (xwidget-webkit-clone-and-split-below) (xwidget-webkit-clone-and-split-right): New procedures. (xwidget-webkit-callback): Remove call to 'xwidget-webkit-adjust-size-to-window' as adjusting xwidget size is handled in 'x_draw_xwidget_glyph_string'. (xwidget-webkit-enable-plugins): New variable. * nextstep/templates/Info.plist.in: Add 'NSAppTransportSecurity'. * src/Makefile.in: Add nsxwidget.o for compilation. * src/emacs.c (main): Move conditional call to 'syms_of_xwidget'. * src/nsterm.m (ns_draw_glyph_string): Add case for 'XWIDGET_GLYPH'. (note_mouse_movement mouseMoved): Make it easy to resize window by dragging mode-line or vertical separator adjacent to large glyph. * src/nsxwidget.h src/nsxwidget.m: Newly added files, xwidget webkit backend for macOS Cocoa. * src/xwidget.c (Fmake_xwidget, xwidget_init_view) (x_draw_xwidget_glyph_string, xwidget_is_web_view) (Fxwidget_webkit_goto_uri, Fxwidget_webkit_zoom, Fxwidget_resize) (Fxwidget_size_request, Fdelete_xwidget_view, xwidget_end_redisplay) (kill_buffer_xwidgets): Add macOS Cocoa specific functions and code with 'NS_IMPL_COCOA' and guard GTK specific functions and code with 'USE_GTK'. (x_draw_xwidget_glyph_string): Handle adjusting xwidget size. * src/xwidget.h (xwidget, xwidget_view): Add macOS Cocoa specific fields with 'NS_IMPL_COCOA' and guard GTK specific fields with USE_GTK.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in1
-rw-r--r--src/emacs.c2
-rw-r--r--src/nsterm.m21
-rw-r--r--src/nsxwidget.h77
-rw-r--r--src/nsxwidget.m563
-rw-r--r--src/xwidget.c175
-rw-r--r--src/xwidget.h43
7 files changed, 862 insertions, 20 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
index 7141f16ec25..c5fb2ea3ab2 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -433,6 +433,7 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
433 xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \ 433 xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \
434 fontset.o dbusbind.o cygw32.o \ 434 fontset.o dbusbind.o cygw32.o \
435 nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \ 435 nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \
436 nsxwidget.o \
436 w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \ 437 w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \
437 w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \ 438 w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
438 w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \ 439 w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \
diff --git a/src/emacs.c b/src/emacs.c
index d31fa2cb287..cb04de4aab6 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1860,7 +1860,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
1860 syms_of_xfns (); 1860 syms_of_xfns ();
1861 syms_of_xmenu (); 1861 syms_of_xmenu ();
1862 syms_of_fontset (); 1862 syms_of_fontset ();
1863 syms_of_xwidget ();
1864 syms_of_xsettings (); 1863 syms_of_xsettings ();
1865#ifdef HAVE_X_SM 1864#ifdef HAVE_X_SM
1866 syms_of_xsmfns (); 1865 syms_of_xsmfns ();
@@ -1937,6 +1936,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
1937#endif /* HAVE_W32NOTIFY */ 1936#endif /* HAVE_W32NOTIFY */
1938#endif /* WINDOWSNT */ 1937#endif /* WINDOWSNT */
1939 1938
1939 syms_of_xwidget ();
1940 syms_of_threads (); 1940 syms_of_threads ();
1941 syms_of_profiler (); 1941 syms_of_profiler ();
1942 syms_of_pdumper (); 1942 syms_of_pdumper ();
diff --git a/src/nsterm.m b/src/nsterm.m
index 572b859a982..9f5916d78ed 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -49,6 +49,7 @@ GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
49#include "nsterm.h" 49#include "nsterm.h"
50#include "systime.h" 50#include "systime.h"
51#include "character.h" 51#include "character.h"
52#include "xwidget.h"
52#include "fontset.h" 53#include "fontset.h"
53#include "composite.h" 54#include "composite.h"
54#include "ccl.h" 55#include "ccl.h"
@@ -2600,7 +2601,8 @@ frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
2600} 2601}
2601 2602
2602static int 2603static int
2603ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y) 2604ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y,
2605 BOOL dragging)
2604/* ------------------------------------------------------------------------ 2606/* ------------------------------------------------------------------------
2605 Called by EmacsView on mouseMovement events. Passes on 2607 Called by EmacsView on mouseMovement events. Passes on
2606 to emacs mainstream code if we moved off of a rect of interest 2608 to emacs mainstream code if we moved off of a rect of interest
@@ -2609,17 +2611,24 @@ ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
2609{ 2611{
2610 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); 2612 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
2611 NSRect *r; 2613 NSRect *r;
2614 BOOL force_update = NO;
2612 2615
2613 // NSTRACE ("note_mouse_movement"); 2616 // NSTRACE ("note_mouse_movement");
2614 2617
2615 dpyinfo->last_mouse_motion_frame = frame; 2618 dpyinfo->last_mouse_motion_frame = frame;
2616 r = &dpyinfo->last_mouse_glyph; 2619 r = &dpyinfo->last_mouse_glyph;
2617 2620
2621 /* If the last rect is too large (ex, xwidget webkit), update at
2622 every move, or resizing by dragging modeline or vertical split is
2623 very hard to make its way. */
2624 if (dragging && (r->size.width > 32 || r->size.height > 32))
2625 force_update = YES;
2626
2618 /* Note, this doesn't get called for enter/leave, since we don't have a 2627 /* Note, this doesn't get called for enter/leave, since we don't have a
2619 position. Those are taken care of in the corresponding NSView methods. */ 2628 position. Those are taken care of in the corresponding NSView methods. */
2620 2629
2621 /* Has movement gone beyond last rect we were tracking? */ 2630 /* Has movement gone beyond last rect we were tracking? */
2622 if (x < r->origin.x || x >= r->origin.x + r->size.width 2631 if (force_update || x < r->origin.x || x >= r->origin.x + r->size.width
2623 || y < r->origin.y || y >= r->origin.y + r->size.height) 2632 || y < r->origin.y || y >= r->origin.y + r->size.height)
2624 { 2633 {
2625 ns_update_begin (frame); 2634 ns_update_begin (frame);
@@ -4368,6 +4377,10 @@ ns_draw_glyph_string (struct glyph_string *s)
4368 ns_unfocus (s->f); 4377 ns_unfocus (s->f);
4369 break; 4378 break;
4370 4379
4380 case XWIDGET_GLYPH:
4381 x_draw_xwidget_glyph_string (s);
4382 break;
4383
4371 case STRETCH_GLYPH: 4384 case STRETCH_GLYPH:
4372 ns_dumpglyphs_stretch (s); 4385 ns_dumpglyphs_stretch (s);
4373 break; 4386 break;
@@ -7065,6 +7078,7 @@ not_in_argv (NSString *arg)
7065 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe); 7078 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
7066 Lisp_Object frame; 7079 Lisp_Object frame;
7067 NSPoint pt; 7080 NSPoint pt;
7081 BOOL dragging;
7068 7082
7069 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]"); 7083 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
7070 7084
@@ -7107,7 +7121,8 @@ not_in_argv (NSString *arg)
7107 last_mouse_window = window; 7121 last_mouse_window = window;
7108 } 7122 }
7109 7123
7110 if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y)) 7124 dragging = (e.type == NSEventTypeLeftMouseDragged);
7125 if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y, dragging))
7111 help_echo_string = previous_help_echo_string; 7126 help_echo_string = previous_help_echo_string;
7112 7127
7113 XSETFRAME (frame, emacsframe); 7128 XSETFRAME (frame, emacsframe);
diff --git a/src/nsxwidget.h b/src/nsxwidget.h
new file mode 100644
index 00000000000..7e2a3e0c402
--- /dev/null
+++ b/src/nsxwidget.h
@@ -0,0 +1,77 @@
1/* Header for NS Cocoa part of xwidget and webkit widget.
2
3Copyright (C) 2019 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
20#ifndef NSXWIDGET_H_INCLUDED
21#define NSXWIDGET_H_INCLUDED
22
23/* This file can be included from non-objc files through 'xwidget.h'. */
24#ifdef __OBJC__
25#import <AppKit/NSView.h>
26#endif
27
28#include "dispextern.h"
29#include "lisp.h"
30#include "xwidget.h"
31
32/* Functions for xwidget webkit. */
33
34bool nsxwidget_is_web_view (struct xwidget *xw);
35void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri);
36void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change);
37void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
38 Lisp_Object fun);
39
40/* Functions for xwidget model. */
41
42#ifdef __OBJC__
43@interface XwWindow : NSView
44@property struct xwidget *xw;
45@end
46#endif
47
48void nsxwidget_init (struct xwidget *xw);
49void nsxwidget_kill (struct xwidget *xw);
50void nsxwidget_resize (struct xwidget *xw);
51Lisp_Object nsxwidget_get_size (struct xwidget *xw);
52
53/* Functions for xwidget view. */
54
55#ifdef __OBJC__
56@interface XvWindow : NSView
57@property struct xwidget *xw;
58@property struct xwidget_view *xv;
59@end
60#endif
61
62void nsxwidget_init_view (struct xwidget_view *xv,
63 struct xwidget *xww,
64 struct glyph_string *s,
65 int x, int y);
66void nsxwidget_delete_view (struct xwidget_view *xv);
67
68void nsxwidget_show_view (struct xwidget_view *xv);
69void nsxwidget_hide_view (struct xwidget_view *xv);
70void nsxwidget_resize_view (struct xwidget_view *xv,
71 int widget, int height);
72
73void nsxwidget_move_view (struct xwidget_view *xv, int x, int y);
74void nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y);
75void nsxwidget_set_needsdisplay (struct xwidget_view *xv);
76
77#endif /* NSXWIDGET_H_INCLUDED */
diff --git a/src/nsxwidget.m b/src/nsxwidget.m
new file mode 100644
index 00000000000..c5376dd311c
--- /dev/null
+++ b/src/nsxwidget.m
@@ -0,0 +1,563 @@
1/* NS Cocoa part implementation of xwidget and webkit widget.
2
3Copyright (C) 2019 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
20#include <config.h>
21
22#include "lisp.h"
23#include "blockinput.h"
24#include "dispextern.h"
25#include "buffer.h"
26#include "frame.h"
27#include "nsterm.h"
28#include "xwidget.h"
29
30#import <AppKit/AppKit.h>
31#import <WebKit/WebKit.h>
32
33/* Thoughts on NS Cocoa xwidget and webkit2:
34
35 Webkit2 process architecture seems to be very hostile for offscreen
36 rendering techniques, which is used by GTK xwiget implementation;
37 Specifically NSView level view sharing / copying is not working.
38
39 *** So only one view can be associcated with a model. ***
40
41 With this decision, implementation is plain and can expect best out
42 of webkit2's rationale. But process and session structures will
43 diverge from GTK xwiget. Though, cosmetically similar usages can
44 be presented and will be preferred, if agreeable.
45
46 For other widget types, OSR seems possible, but will not care for a
47 while. */
48
49/* Xwidget webkit. */
50
51@interface XwWebView : WKWebView
52<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler>
53@property struct xwidget *xw;
54/* Map url to whether javascript is blocked by
55 'Content-Security-Policy' sandbox without allow-scripts. */
56@property(retain) NSMutableDictionary *urlScriptBlocked;
57@end
58@implementation XwWebView : WKWebView
59
60- (id)initWithFrame:(CGRect)frame
61 configuration:(WKWebViewConfiguration *)configuration
62 xwidget:(struct xwidget *)xw
63{
64 /* Script controller to add script message handler and user script. */
65 WKUserContentController *scriptor = [[WKUserContentController alloc] init];
66 configuration.userContentController = scriptor;
67
68 /* Enable inspect element context menu item for debugging. */
69 [configuration.preferences setValue:@YES
70 forKey:@"developerExtrasEnabled"];
71
72 Lisp_Object enablePlugins =
73 Fintern (build_string ("xwidget-webkit-enable-plugins"), Qnil);
74 if (!EQ (Fsymbol_value (enablePlugins), Qnil))
75 configuration.preferences.plugInsEnabled = YES;
76
77 self = [super initWithFrame:frame configuration:configuration];
78 if (self)
79 {
80 self.xw = xw;
81 self.urlScriptBlocked = [[NSMutableDictionary alloc] init];
82 self.navigationDelegate = self;
83 self.UIDelegate = self;
84 self.customUserAgent =
85 @"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)"
86 @" AppleWebKit/603.3.8 (KHTML, like Gecko)"
87 @" Version/11.0.1 Safari/603.3.8";
88 [scriptor addScriptMessageHandler:self name:@"keyDown"];
89 [scriptor addUserScript:[[WKUserScript alloc]
90 initWithSource:xwScript
91 injectionTime:
92 WKUserScriptInjectionTimeAtDocumentStart
93 forMainFrameOnly:NO]];
94 }
95 return self;
96}
97
98- (void)webView:(WKWebView *)webView
99didFinishNavigation:(WKNavigation *)navigation
100{
101 if (EQ (Fbuffer_live_p (self.xw->buffer), Qt))
102 store_xwidget_event_string (self.xw, "load-changed", "");
103}
104
105- (void)webView:(WKWebView *)webView
106decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
107decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
108{
109 switch (navigationAction.navigationType) {
110 case WKNavigationTypeLinkActivated:
111 decisionHandler (WKNavigationActionPolicyAllow);
112 break;
113 default:
114 // decisionHandler (WKNavigationActionPolicyCancel);
115 decisionHandler (WKNavigationActionPolicyAllow);
116 break;
117 }
118}
119
120- (void)webView:(WKWebView *)webView
121decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
122decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
123{
124 decisionHandler (WKNavigationResponsePolicyAllow);
125
126 self.urlScriptBlocked[navigationResponse.response.URL] =
127 [NSNumber numberWithBool:NO];
128 if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]])
129 {
130 NSDictionary *headers =
131 ((NSHTTPURLResponse *) navigationResponse.response).allHeaderFields;
132 NSString *value = headers[@"Content-Security-Policy"];
133 if (value)
134 {
135 /* TODO: Sloppy parsing of 'Content-Security-Policy' value. */
136 NSRange sandbox = [value rangeOfString:@"sandbox"];
137 if (sandbox.location != NSNotFound
138 && (sandbox.location == 0
139 || [value characterAtIndex:(sandbox.location - 1)] == ' '
140 || [value characterAtIndex:(sandbox.location - 1)] == ';'))
141 {
142 NSRange allowScripts = [value rangeOfString:@"allow-scripts"];
143 if (allowScripts.location == NSNotFound
144 || allowScripts.location < sandbox.location)
145 self.urlScriptBlocked[navigationResponse.response.URL] =
146 [NSNumber numberWithBool:YES];
147 }
148 }
149 }
150}
151
152/* No additional new webview or emacs window will be created
153 for <a ... target="_blank">. */
154- (WKWebView *)webView:(WKWebView *)webView
155createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
156 forNavigationAction:(WKNavigationAction *)navigationAction
157 windowFeatures:(WKWindowFeatures *)windowFeatures
158{
159 if (!navigationAction.targetFrame.isMainFrame)
160 [webView loadRequest:navigationAction.request];
161 return nil;
162}
163
164/* Open panel for file upload. */
165- (void)webView:(WKWebView *)webView
166runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters
167initiatedByFrame:(WKFrameInfo *)frame
168completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler
169{
170 NSOpenPanel *openPanel = [NSOpenPanel openPanel];
171 openPanel.canChooseFiles = YES;
172 openPanel.canChooseDirectories = NO;
173 openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
174 if ([openPanel runModal] == NSModalResponseOK)
175 completionHandler (openPanel.URLs);
176 else
177 completionHandler (nil);
178}
179
180/* By forwarding mouse events to emacs view (frame)
181 - Mouse click in webview selects the window contains the webview.
182 - Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet).
183*/
184
185- (void)mouseDown:(NSEvent *)event
186{
187 [self.xw->xv->emacswindow mouseDown:event];
188 [super mouseDown:event];
189}
190
191- (void)mouseUp:(NSEvent *)event
192{
193 [self.xw->xv->emacswindow mouseUp:event];
194 [super mouseUp:event];
195}
196
197/* Basically we want keyboard events handled by emacs unless an input
198 element has focus. Especially, while incremental search, we set
199 emacs as first responder to avoid focus held in an input element
200 with matching text. */
201
202- (void)keyDown:(NSEvent *)event
203{
204 Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil);
205 Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ());
206 if (!EQ (val, Qunbound) && !EQ (val, Qnil))
207 {
208 [self.window makeFirstResponder:self.xw->xv->emacswindow];
209 [self.xw->xv->emacswindow keyDown:event];
210 return;
211 }
212
213 /* Emacs handles keyboard events when javascript is blocked. */
214 if ([self.urlScriptBlocked[self.URL] boolValue])
215 {
216 [self.xw->xv->emacswindow keyDown:event];
217 return;
218 }
219
220 [self evaluateJavaScript:@"xwHasFocus()"
221 completionHandler:^(id result, NSError *error) {
222 if (error)
223 {
224 NSLog (@"xwHasFocus: %@", error);
225 [self.xw->xv->emacswindow keyDown:event];
226 }
227 else if (result)
228 {
229 NSNumber *hasFocus = result; /* __NSCFBoolean */
230 if (!hasFocus.boolValue)
231 [self.xw->xv->emacswindow keyDown:event];
232 else
233 [super keyDown:event];
234 }
235 }];
236}
237
238- (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray
239{
240 /* We should do nothing and do not forward (default implementation
241 if we not override here) to let emacs collect key events and ask
242 interpretKeyEvents to its superclass. */
243}
244
245static NSString *xwScript;
246+ (void)initialize
247{
248 /* Find out if an input element has focus.
249 Message to script message handler when 'C-g' key down. */
250 if (!xwScript)
251 xwScript =
252 @"function xwHasFocus() {"
253 @" var ae = document.activeElement;"
254 @" if (ae) {"
255 @" var name = ae.nodeName;"
256 @" return name == 'INPUT' || name == 'TEXTAREA';"
257 @" } else {"
258 @" return false;"
259 @" }"
260 @"}"
261 @"function xwKeyDown(event) {"
262 @" if (event.ctrlKey && event.key == 'g') {"
263 @" window.webkit.messageHandlers.keyDown.postMessage('C-g');"
264 @" }"
265 @"}"
266 @"document.addEventListener('keydown', xwKeyDown);"
267 ;
268}
269
270/* Confirming to WKScriptMessageHandler, listens concerning keyDown in
271 webkit. Currently 'C-g'. */
272- (void)userContentController:(WKUserContentController *)userContentController
273 didReceiveScriptMessage:(WKScriptMessage *)message
274{
275 if ([message.body isEqualToString:@"C-g"])
276 {
277 /* Just give up focus, no relay "C-g" to emacs, another "C-g"
278 follows will be handled by emacs. */
279 [self.window makeFirstResponder:self.xw->xv->emacswindow];
280 }
281}
282
283@end
284
285/* Xwidget webkit commands. */
286
287static Lisp_Object build_string_with_nsstr (NSString *nsstr);
288
289bool
290nsxwidget_is_web_view (struct xwidget *xw)
291{
292 return xw->xwWidget != NULL &&
293 [xw->xwWidget isKindOfClass:WKWebView.class];
294}
295/* @Note ATS - Need application transport security in 'Info.plist' or
296 remote pages will not loaded. */
297void
298nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri)
299{
300 XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
301 NSString *urlString = [NSString stringWithUTF8String:uri];
302 NSURL *url = [NSURL URLWithString:urlString];
303 NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
304 [xwWebView loadRequest:urlRequest];
305}
306
307void
308nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change)
309{
310 XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
311 xwWebView.magnification += zoom_change;
312 /* TODO: setMagnification:centeredAtPoint. */
313}
314
315/* Build lisp string */
316static Lisp_Object
317build_string_with_nsstr (NSString *nsstr)
318{
319 const char *utfstr = [nsstr UTF8String];
320 NSUInteger bytes = [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
321 return make_string (utfstr, bytes);
322}
323
324/* Recursively convert an objc native type JavaScript value to a Lisp
325 value. Mostly copied from GTK xwidget 'webkit_js_to_lisp'. */
326static Lisp_Object
327js_to_lisp (id value)
328{
329 if (value == nil || [value isKindOfClass:NSNull.class])
330 return Qnil;
331 else if ([value isKindOfClass:NSString.class])
332 return build_string_with_nsstr ((NSString *) value);
333 else if ([value isKindOfClass:NSNumber.class])
334 {
335 NSNumber *nsnum = (NSNumber *) value;
336 char type = nsnum.objCType[0];
337 if (type == 'c') /* __NSCFBoolean has type character 'c'. */
338 return nsnum.boolValue? Qt : Qnil;
339 else
340 {
341 if (type == 'i' || type == 'l')
342 return make_int (nsnum.longValue);
343 else if (type == 'f' || type == 'd')
344 return make_float (nsnum.doubleValue);
345 /* else fall through. */
346 }
347 }
348 else if ([value isKindOfClass:NSArray.class])
349 {
350 NSArray *nsarr = (NSArray *) value;
351 EMACS_INT n = nsarr.count;
352 Lisp_Object obj;
353 struct Lisp_Vector *p = allocate_vector (n);
354
355 for (ptrdiff_t i = 0; i < n; ++i)
356 p->contents[i] = js_to_lisp ([nsarr objectAtIndex:i]);
357 XSETVECTOR (obj, p);
358 return obj;
359 }
360 else if ([value isKindOfClass:NSDictionary.class])
361 {
362 NSDictionary *nsdict = (NSDictionary *) value;
363 NSArray *keys = nsdict.allKeys;
364 ptrdiff_t n = keys.count;
365 Lisp_Object obj;
366 struct Lisp_Vector *p = allocate_vector (n);
367
368 for (ptrdiff_t i = 0; i < n; ++i)
369 {
370 NSString *prop_key = (NSString *) [keys objectAtIndex:i];
371 id prop_value = [nsdict valueForKey:prop_key];
372 p->contents[i] = Fcons (build_string_with_nsstr (prop_key),
373 js_to_lisp (prop_value));
374 }
375 XSETVECTOR (obj, p);
376 return obj;
377 }
378 NSLog (@"Unhandled type in javascript result");
379 return Qnil;
380}
381
382void
383nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
384 Lisp_Object fun)
385{
386 XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
387 if ([xwWebView.urlScriptBlocked[xwWebView.URL] boolValue])
388 {
389 message ("Javascript is blocked by 'CSP: sandbox'.");
390 return;
391 }
392
393 NSString *javascriptString = [NSString stringWithUTF8String:script];
394 [xwWebView evaluateJavaScript:javascriptString
395 completionHandler:^(id result, NSError *error) {
396 if (error)
397 {
398 NSLog (@"evaluateJavaScript error : %@", error.localizedDescription);
399 NSLog (@"error script=%@", javascriptString);
400 }
401 else if (result && FUNCTIONP (fun))
402 {
403 // NSLog (@"result=%@, type=%@", result, [result class]);
404 Lisp_Object lisp_value = js_to_lisp (result);
405 store_xwidget_js_callback_event (xw, fun, lisp_value);
406 }
407 }];
408}
409
410/* Window containing an xwidget. */
411
412@implementation XwWindow
413- (BOOL)isFlipped { return YES; }
414@end
415
416/* Xwidget model, macOS Cocoa part. */
417
418void
419nsxwidget_init(struct xwidget *xw)
420{
421 block_input ();
422 NSRect rect = NSMakeRect (0, 0, xw->width, xw->height);
423 xw->xwWidget = [[XwWebView alloc]
424 initWithFrame:rect
425 configuration:[[WKWebViewConfiguration alloc] init]
426 xwidget:xw];
427 xw->xwWindow = [[XwWindow alloc]
428 initWithFrame:rect];
429 [xw->xwWindow addSubview:xw->xwWidget];
430 xw->xv = NULL; /* for 1 to 1 relationship of webkit2. */
431 unblock_input ();
432}
433
434void
435nsxwidget_kill (struct xwidget *xw)
436{
437 if (xw)
438 {
439 WKUserContentController *scriptor =
440 ((XwWebView *) xw->xwWidget).configuration.userContentController;
441 [scriptor removeAllUserScripts];
442 [scriptor removeScriptMessageHandlerForName:@"keyDown"];
443 [scriptor release];
444 if (xw->xv)
445 xw->xv->model = Qnil; /* Make sure related view stale. */
446
447 /* This stops playing audio when a xwidget-webkit buffer is
448 killed. I could not find other solution. */
449 nsxwidget_webkit_goto_uri (xw, "about:blank");
450
451 [((XwWebView *) xw->xwWidget).urlScriptBlocked release];
452 [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay];
453 [xw->xwWidget release];
454 [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
455 [xw->xwWindow release];
456 xw->xwWidget = nil;
457 }
458}
459
460void
461nsxwidget_resize (struct xwidget *xw)
462{
463 if (xw->xwWidget)
464 {
465 [xw->xwWindow setFrameSize:NSMakeSize(xw->width, xw->height)];
466 [xw->xwWidget setFrameSize:NSMakeSize(xw->width, xw->height)];
467 }
468}
469
470Lisp_Object
471nsxwidget_get_size (struct xwidget *xw)
472{
473 return list2i (xw->xwWidget.frame.size.width,
474 xw->xwWidget.frame.size.height);
475}
476
477/* Xwidget view, macOS Cocoa part. */
478
479@implementation XvWindow : NSView
480- (BOOL)isFlipped { return YES; }
481@end
482
483void
484nsxwidget_init_view (struct xwidget_view *xv,
485 struct xwidget *xw,
486 struct glyph_string *s,
487 int x, int y)
488{
489 /* 'x_draw_xwidget_glyph_string' will calculate correct position and
490 size of clip to draw in emacs buffer window. Thus, just begin at
491 origin with no crop. */
492 xv->x = x;
493 xv->y = y;
494 xv->clip_left = 0;
495 xv->clip_right = xw->width;
496 xv->clip_top = 0;
497 xv->clip_bottom = xw->height;
498
499 xv->xvWindow = [[XvWindow alloc]
500 initWithFrame:NSMakeRect (x, y, xw->width, xw->height)];
501 xv->xvWindow.xw = xw;
502 xv->xvWindow.xv = xv;
503
504 xw->xv = xv; /* For 1 to 1 relationship of webkit2. */
505 [xv->xvWindow addSubview:xw->xwWindow];
506
507 xv->emacswindow = FRAME_NS_VIEW (s->f);
508 [xv->emacswindow addSubview:xv->xvWindow];
509}
510
511void
512nsxwidget_delete_view (struct xwidget_view *xv)
513{
514 if (!EQ (xv->model, Qnil))
515 {
516 struct xwidget *xw = XXWIDGET (xv->model);
517 [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
518 xw->xv = NULL; /* Now model has no view. */
519 }
520 [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay];
521 [xv->xvWindow release];
522}
523
524void
525nsxwidget_show_view (struct xwidget_view *xv)
526{
527 xv->hidden = NO;
528 [xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left,
529 xv->y + xv->clip_top)];
530}
531
532void
533nsxwidget_hide_view (struct xwidget_view *xv)
534{
535 xv->hidden = YES;
536 [xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)];
537}
538
539void
540nsxwidget_resize_view (struct xwidget_view *xv, int width, int height)
541{
542 [xv->xvWindow setFrameSize:NSMakeSize(width, height)];
543}
544
545void
546nsxwidget_move_view (struct xwidget_view *xv, int x, int y)
547{
548 [xv->xvWindow setFrameOrigin:NSMakePoint (x, y)];
549}
550
551/* Move model window in container (view window). */
552void
553nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y)
554{
555 struct xwidget *xww = xv->xvWindow.xw;
556 [xww->xwWindow setFrameOrigin:NSMakePoint (x, y)];
557}
558
559void
560nsxwidget_set_needsdisplay (struct xwidget_view *xv)
561{
562 xv->xvWindow.needsDisplay = YES;
563}
diff --git a/src/xwidget.c b/src/xwidget.c
index 0347f1e6483..a3a3cd8d5bc 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -23,13 +23,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
23 23
24#include "lisp.h" 24#include "lisp.h"
25#include "blockinput.h" 25#include "blockinput.h"
26#include "dispextern.h"
26#include "frame.h" 27#include "frame.h"
27#include "keyboard.h" 28#include "keyboard.h"
28#include "gtkutil.h" 29#include "gtkutil.h"
29#include "sysstdio.h" 30#include "sysstdio.h"
31#include "termhooks.h"
32#include "window.h"
30 33
34/* Include xwidget bottom end headers. */
35#ifdef USE_GTK
31#include <webkit2/webkit2.h> 36#include <webkit2/webkit2.h>
32#include <JavaScriptCore/JavaScript.h> 37#include <JavaScriptCore/JavaScript.h>
38#elif defined NS_IMPL_COCOA
39#include "nsxwidget.h"
40#endif
33 41
34static struct xwidget * 42static struct xwidget *
35allocate_xwidget (void) 43allocate_xwidget (void)
@@ -48,6 +56,7 @@ allocate_xwidget_view (void)
48 56
49static struct xwidget_view *xwidget_view_lookup (struct xwidget *, 57static struct xwidget_view *xwidget_view_lookup (struct xwidget *,
50 struct window *); 58 struct window *);
59#ifdef USE_GTK
51static void webkit_view_load_changed_cb (WebKitWebView *, 60static void webkit_view_load_changed_cb (WebKitWebView *,
52 WebKitLoadEvent, 61 WebKitLoadEvent,
53 gpointer); 62 gpointer);
@@ -61,6 +70,7 @@ webkit_decide_policy_cb (WebKitWebView *,
61 WebKitPolicyDecision *, 70 WebKitPolicyDecision *,
62 WebKitPolicyDecisionType, 71 WebKitPolicyDecisionType,
63 gpointer); 72 gpointer);
73#endif
64 74
65 75
66DEFUN ("make-xwidget", 76DEFUN ("make-xwidget",
@@ -78,8 +88,10 @@ Returns the newly constructed xwidget, or nil if construction fails. */)
78 Lisp_Object title, Lisp_Object width, Lisp_Object height, 88 Lisp_Object title, Lisp_Object width, Lisp_Object height,
79 Lisp_Object arguments, Lisp_Object buffer) 89 Lisp_Object arguments, Lisp_Object buffer)
80{ 90{
91#ifdef USE_GTK
81 if (!xg_gtk_initialized) 92 if (!xg_gtk_initialized)
82 error ("make-xwidget: GTK has not been initialized"); 93 error ("make-xwidget: GTK has not been initialized");
94#endif
83 CHECK_SYMBOL (type); 95 CHECK_SYMBOL (type);
84 CHECK_FIXNAT (width); 96 CHECK_FIXNAT (width);
85 CHECK_FIXNAT (height); 97 CHECK_FIXNAT (height);
@@ -94,10 +106,11 @@ Returns the newly constructed xwidget, or nil if construction fails. */)
94 xw->kill_without_query = false; 106 xw->kill_without_query = false;
95 XSETXWIDGET (val, xw); 107 XSETXWIDGET (val, xw);
96 Vxwidget_list = Fcons (val, Vxwidget_list); 108 Vxwidget_list = Fcons (val, Vxwidget_list);
97 xw->widgetwindow_osr = NULL;
98 xw->widget_osr = NULL;
99 xw->plist = Qnil; 109 xw->plist = Qnil;
100 110
111#ifdef USE_GTK
112 xw->widgetwindow_osr = NULL;
113 xw->widget_osr = NULL;
101 if (EQ (xw->type, Qwebkit)) 114 if (EQ (xw->type, Qwebkit))
102 { 115 {
103 block_input (); 116 block_input ();
@@ -152,6 +165,9 @@ Returns the newly constructed xwidget, or nil if construction fails. */)
152 165
153 unblock_input (); 166 unblock_input ();
154 } 167 }
168#elif defined NS_IMPL_COCOA
169 nsxwidget_init (xw);
170#endif
155 171
156 return val; 172 return val;
157} 173}
@@ -187,6 +203,7 @@ xwidget_hidden (struct xwidget_view *xv)
187 return xv->hidden; 203 return xv->hidden;
188} 204}
189 205
206#ifdef USE_GTK
190static void 207static void
191xwidget_show_view (struct xwidget_view *xv) 208xwidget_show_view (struct xwidget_view *xv)
192{ 209{
@@ -220,13 +237,14 @@ offscreen_damage_event (GtkWidget *widget, GdkEvent *event,
220 if (GTK_IS_WIDGET (xv_widget)) 237 if (GTK_IS_WIDGET (xv_widget))
221 gtk_widget_queue_draw (GTK_WIDGET (xv_widget)); 238 gtk_widget_queue_draw (GTK_WIDGET (xv_widget));
222 else 239 else
223 printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n", 240 message ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
224 xv_widget); 241 xv_widget);
225 242
226 return FALSE; 243 return FALSE;
227} 244}
245#endif /* USE_GTK */
228 246
229static void 247void
230store_xwidget_event_string (struct xwidget *xw, const char *eventname, 248store_xwidget_event_string (struct xwidget *xw, const char *eventname,
231 const char *eventstr) 249 const char *eventstr)
232{ 250{
@@ -240,7 +258,7 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname,
240 kbd_buffer_store_event (&event); 258 kbd_buffer_store_event (&event);
241} 259}
242 260
243static void 261void
244store_xwidget_js_callback_event (struct xwidget *xw, 262store_xwidget_js_callback_event (struct xwidget *xw,
245 Lisp_Object proc, 263 Lisp_Object proc,
246 Lisp_Object argument) 264 Lisp_Object argument)
@@ -256,6 +274,7 @@ store_xwidget_js_callback_event (struct xwidget *xw,
256} 274}
257 275
258 276
277#ifdef USE_GTK
259void 278void
260webkit_view_load_changed_cb (WebKitWebView *webkitwebview, 279webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
261 WebKitLoadEvent load_event, 280 WebKitLoadEvent load_event,
@@ -486,6 +505,7 @@ xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
486 gtk_widget_get_window (xv->widget)); 505 gtk_widget_get_window (xv->widget));
487 return FALSE; 506 return FALSE;
488} 507}
508#endif /* USE_GTK */
489 509
490 510
491/* Initializes and does initial placement of an xwidget view on screen. */ 511/* Initializes and does initial placement of an xwidget view on screen. */
@@ -495,8 +515,10 @@ xwidget_init_view (struct xwidget *xww,
495 int x, int y) 515 int x, int y)
496{ 516{
497 517
518#ifdef USE_GTK
498 if (!xg_gtk_initialized) 519 if (!xg_gtk_initialized)
499 error ("xwidget_init_view: GTK has not been initialized"); 520 error ("xwidget_init_view: GTK has not been initialized");
521#endif
500 522
501 struct xwidget_view *xv = allocate_xwidget_view (); 523 struct xwidget_view *xv = allocate_xwidget_view ();
502 Lisp_Object val; 524 Lisp_Object val;
@@ -507,6 +529,7 @@ xwidget_init_view (struct xwidget *xww,
507 XSETWINDOW (xv->w, s->w); 529 XSETWINDOW (xv->w, s->w);
508 XSETXWIDGET (xv->model, xww); 530 XSETXWIDGET (xv->model, xww);
509 531
532#ifdef USE_GTK
510 if (EQ (xww->type, Qwebkit)) 533 if (EQ (xww->type, Qwebkit))
511 { 534 {
512 xv->widget = gtk_drawing_area_new (); 535 xv->widget = gtk_drawing_area_new ();
@@ -564,6 +587,10 @@ xwidget_init_view (struct xwidget *xww,
564 xv->x = x; 587 xv->x = x;
565 xv->y = y; 588 xv->y = y;
566 gtk_widget_show_all (xv->widgetwindow); 589 gtk_widget_show_all (xv->widgetwindow);
590#elif defined NS_IMPL_COCOA
591 nsxwidget_init_view (xv, xww, s, x, y);
592 nsxwidget_resize_view(xv, xww->width, xww->height);
593#endif
567 594
568 return xv; 595 return xv;
569} 596}
@@ -576,6 +603,7 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
576 initialization. */ 603 initialization. */
577 struct xwidget *xww = s->xwidget; 604 struct xwidget *xww = s->xwidget;
578 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); 605 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
606 int text_area_x, text_area_y, text_area_width, text_area_height;
579 int clip_right; 607 int clip_right;
580 int clip_bottom; 608 int clip_bottom;
581 int clip_top; 609 int clip_top;
@@ -587,13 +615,47 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
587 /* Do initialization here in the display loop because there is no 615 /* Do initialization here in the display loop because there is no
588 other time to know things like window placement etc. Do not 616 other time to know things like window placement etc. Do not
589 create a new view if we have found one that is usable. */ 617 create a new view if we have found one that is usable. */
618#ifdef USE_GTK
590 if (!xv) 619 if (!xv)
591 xv = xwidget_init_view (xww, s, x, y); 620 xv = xwidget_init_view (xww, s, x, y);
592 621#elif defined NS_IMPL_COCOA
593 int text_area_x, text_area_y, text_area_width, text_area_height; 622 if (!xv)
623 {
624 /* Enforce 1 to 1, model and view for macOS Cocoa webkit2. */
625 if (xww->xv)
626 {
627 if (xwidget_hidden (xww->xv))
628 {
629 Lisp_Object xvl;
630 XSETXWIDGET_VIEW (xvl, xww->xv);
631 Fdelete_xwidget_view (xvl);
632 }
633 else
634 {
635 message ("You can't share an xwidget (webkit2) among windows.");
636 return;
637 }
638 }
639 xv = xwidget_init_view (xww, s, x, y);
640 }
641#endif
594 642
595 window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y, 643 window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y,
596 &text_area_width, &text_area_height); 644 &text_area_width, &text_area_height);
645
646 /* Resize xwidget webkit if its container window size is changed in
647 some ways, for example, a buffer became hidden in small split
648 window, then it can appear front in merged whole window. */
649 if (EQ (xww->type, Qwebkit)
650 && (xww->width != text_area_width || xww->height != text_area_height))
651 {
652 Lisp_Object xwl;
653 XSETXWIDGET (xwl, xww);
654 Fxwidget_resize (xwl,
655 make_int (text_area_width),
656 make_int (text_area_height));
657 }
658
597 clip_left = max (0, text_area_x - x); 659 clip_left = max (0, text_area_x - x);
598 clip_right = max (clip_left, 660 clip_right = max (clip_left,
599 min (xww->width, text_area_x + text_area_width - x)); 661 min (xww->width, text_area_x + text_area_width - x));
@@ -616,8 +678,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
616 678
617 /* Has it moved? */ 679 /* Has it moved? */
618 if (moved) 680 if (moved)
619 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), 681 {
620 xv->widgetwindow, x + clip_left, y + clip_top); 682#ifdef USE_GTK
683 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
684 xv->widgetwindow, x + clip_left, y + clip_top);
685#elif defined NS_IMPL_COCOA
686 nsxwidget_move_view (xv, x + clip_left, y + clip_top);
687#endif
688 }
621 689
622 /* Clip the widget window if some parts happen to be outside 690 /* Clip the widget window if some parts happen to be outside
623 drawable area. An Emacs window is not a gtk window. A gtk window 691 drawable area. An Emacs window is not a gtk window. A gtk window
@@ -628,10 +696,16 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
628 || xv->clip_bottom != clip_bottom 696 || xv->clip_bottom != clip_bottom
629 || xv->clip_top != clip_top || xv->clip_left != clip_left) 697 || xv->clip_top != clip_top || xv->clip_left != clip_left)
630 { 698 {
699#ifdef USE_GTK
631 gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left, 700 gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left,
632 clip_bottom - clip_top); 701 clip_bottom - clip_top);
633 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, 702 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
634 -clip_top); 703 -clip_top);
704#elif defined NS_IMPL_COCOA
705 nsxwidget_resize_view (xv, clip_right - clip_left,
706 clip_bottom - clip_top);
707 nsxwidget_move_widget_in_view (xv, -clip_left, -clip_top);
708#endif
635 709
636 xv->clip_right = clip_right; 710 xv->clip_right = clip_right;
637 xv->clip_bottom = clip_bottom; 711 xv->clip_bottom = clip_bottom;
@@ -645,16 +719,30 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
645 xwidgets background. It's just a visual glitch though. */ 719 xwidgets background. It's just a visual glitch though. */
646 if (!xwidget_hidden (xv)) 720 if (!xwidget_hidden (xv))
647 { 721 {
722#ifdef USE_GTK
648 gtk_widget_queue_draw (xv->widgetwindow); 723 gtk_widget_queue_draw (xv->widgetwindow);
649 gtk_widget_queue_draw (xv->widget); 724 gtk_widget_queue_draw (xv->widget);
725#elif defined NS_IMPL_COCOA
726 nsxwidget_set_needsdisplay (xv);
727#endif
650 } 728 }
651} 729}
652 730
653/* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */ 731static bool
732xwidget_is_web_view (struct xwidget *xw)
733{
734#ifdef USE_GTK
735 return xw->widget_osr != NULL && WEBKIT_IS_WEB_VIEW (xw->widget_osr);
736#elif defined NS_IMPL_COCOA
737 return nsxwidget_is_web_view (xw);
738#endif
739}
740
741/* Macro that checks xwidget hold webkit web view first. */
654#define WEBKIT_FN_INIT() \ 742#define WEBKIT_FN_INIT() \
655 CHECK_XWIDGET (xwidget); \ 743 CHECK_XWIDGET (xwidget); \
656 struct xwidget *xw = XXWIDGET (xwidget); \ 744 struct xwidget *xw = XXWIDGET (xwidget); \
657 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \ 745 if (!xwidget_is_web_view (xw)) \
658 { \ 746 { \
659 fputs ("ERROR xw->widget_osr does not hold a webkit instance\n", \ 747 fputs ("ERROR xw->widget_osr does not hold a webkit instance\n", \
660 stdout); \ 748 stdout); \
@@ -670,7 +758,11 @@ DEFUN ("xwidget-webkit-goto-uri",
670 WEBKIT_FN_INIT (); 758 WEBKIT_FN_INIT ();
671 CHECK_STRING (uri); 759 CHECK_STRING (uri);
672 uri = ENCODE_FILE (uri); 760 uri = ENCODE_FILE (uri);
761#ifdef USE_GTK
673 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri)); 762 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
763#elif defined NS_IMPL_COCOA
764 nsxwidget_webkit_goto_uri (xw, SSDATA (uri));
765#endif
674 return Qnil; 766 return Qnil;
675} 767}
676 768
@@ -684,14 +776,19 @@ DEFUN ("xwidget-webkit-zoom",
684 if (FLOATP (factor)) 776 if (FLOATP (factor))
685 { 777 {
686 double zoom_change = XFLOAT_DATA (factor); 778 double zoom_change = XFLOAT_DATA (factor);
779#ifdef USE_GTK
687 webkit_web_view_set_zoom_level 780 webkit_web_view_set_zoom_level
688 (WEBKIT_WEB_VIEW (xw->widget_osr), 781 (WEBKIT_WEB_VIEW (xw->widget_osr),
689 webkit_web_view_get_zoom_level 782 webkit_web_view_get_zoom_level
690 (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change); 783 (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change);
784#elif defined NS_IMPL_COCOA
785 nsxwidget_webkit_zoom (xw, zoom_change);
786#endif
691 } 787 }
692 return Qnil; 788 return Qnil;
693} 789}
694 790
791#ifdef USE_GTK
695/* Save script and fun in the script/callback save vector and return 792/* Save script and fun in the script/callback save vector and return
696 its index. */ 793 its index. */
697static ptrdiff_t 794static ptrdiff_t
@@ -713,6 +810,7 @@ save_script_callback (struct xwidget *xw, Lisp_Object script, Lisp_Object fun)
713 ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun)); 810 ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun));
714 return idx; 811 return idx;
715} 812}
813#endif
716 814
717DEFUN ("xwidget-webkit-execute-script", 815DEFUN ("xwidget-webkit-execute-script",
718 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, 816 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
@@ -729,6 +827,7 @@ argument procedure FUN.*/)
729 827
730 script = ENCODE_SYSTEM (script); 828 script = ENCODE_SYSTEM (script);
731 829
830#ifdef USE_GTK
732 /* Protect script and fun during GC. */ 831 /* Protect script and fun during GC. */
733 intptr_t idx = save_script_callback (xw, script, fun); 832 intptr_t idx = save_script_callback (xw, script, fun);
734 833
@@ -742,6 +841,9 @@ argument procedure FUN.*/)
742 NULL, /* cancelable */ 841 NULL, /* cancelable */
743 webkit_javascript_finished_cb, 842 webkit_javascript_finished_cb,
744 (gpointer) idx); 843 (gpointer) idx);
844#elif defined NS_IMPL_COCOA
845 nsxwidget_webkit_execute_script (xw, SSDATA (script), fun);
846#endif
745 return Qnil; 847 return Qnil;
746} 848}
747 849
@@ -758,6 +860,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
758 xw->height = h; 860 xw->height = h;
759 861
760 /* If there is an offscreen widget resize it first. */ 862 /* If there is an offscreen widget resize it first. */
863#ifdef USE_GTK
761 if (xw->widget_osr) 864 if (xw->widget_osr)
762 { 865 {
763 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, 866 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
@@ -766,6 +869,9 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
766 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, 869 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
767 xw->height); 870 xw->height);
768 } 871 }
872#elif defined NS_IMPL_COCOA
873 nsxwidget_resize (xw);
874#endif
769 875
770 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) 876 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
771 { 877 {
@@ -773,8 +879,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
773 { 879 {
774 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail)); 880 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
775 if (XXWIDGET (xv->model) == xw) 881 if (XXWIDGET (xv->model) == xw)
882 {
883#ifdef USE_GTK
776 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, 884 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
777 xw->height); 885 xw->height);
886#elif defined NS_IMPL_COCOA
887 nsxwidget_resize_view(xv, xw->width, xw->height);
888#endif
889 }
778 } 890 }
779 } 891 }
780 892
@@ -793,9 +905,13 @@ Emacs allocated area accordingly. */)
793 (Lisp_Object xwidget) 905 (Lisp_Object xwidget)
794{ 906{
795 CHECK_XWIDGET (xwidget); 907 CHECK_XWIDGET (xwidget);
908#ifdef USE_GTK
796 GtkRequisition requisition; 909 GtkRequisition requisition;
797 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition); 910 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
798 return list2i (requisition.width, requisition.height); 911 return list2i (requisition.width, requisition.height);
912#elif defined NS_IMPL_COCOA
913 return nsxwidget_get_size (XXWIDGET (xwidget));
914#endif
799} 915}
800 916
801DEFUN ("xwidgetp", 917DEFUN ("xwidgetp",
@@ -872,14 +988,19 @@ DEFUN ("delete-xwidget-view",
872{ 988{
873 CHECK_XWIDGET_VIEW (xwidget_view); 989 CHECK_XWIDGET_VIEW (xwidget_view);
874 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); 990 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
991#ifdef USE_GTK
875 gtk_widget_destroy (xv->widgetwindow); 992 gtk_widget_destroy (xv->widgetwindow);
876 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
877 /* xv->model still has signals pointing to the view. There can be 993 /* xv->model still has signals pointing to the view. There can be
878 several views. Find the matching signals and delete them all. */ 994 several views. Find the matching signals and delete them all. */
879 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, 995 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
880 G_SIGNAL_MATCH_DATA, 996 G_SIGNAL_MATCH_DATA,
881 0, 0, 0, 0, 997 0, 0, 0, 0,
882 xv->widget); 998 xv->widget);
999#elif defined NS_IMPL_COCOA
1000 nsxwidget_delete_view (xv);
1001#endif
1002
1003 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
883 return Qnil; 1004 return Qnil;
884} 1005}
885 1006
@@ -1156,11 +1277,19 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1156 xwidget_end_redisplay (w->current_matrix); */ 1277 xwidget_end_redisplay (w->current_matrix); */
1157 struct xwidget_view *xv 1278 struct xwidget_view *xv
1158 = xwidget_view_lookup (glyph->u.xwidget, w); 1279 = xwidget_view_lookup (glyph->u.xwidget, w);
1280#ifdef USE_GTK
1159 /* FIXME: Is it safe to assume xwidget_view_lookup 1281 /* FIXME: Is it safe to assume xwidget_view_lookup
1160 always succeeds here? If so, this comment can be removed. 1282 always succeeds here? If so, this comment can be removed.
1161 If not, the code probably needs fixing. */ 1283 If not, the code probably needs fixing. */
1162 eassume (xv); 1284 eassume (xv);
1163 xwidget_touch (xv); 1285 xwidget_touch (xv);
1286#elif defined NS_IMPL_COCOA
1287 /* In NS xwidget, xv can be NULL for the second or
1288 later views for a model, the result of 1 to 1
1289 model view relation enforcement. */
1290 if (xv)
1291 xwidget_touch (xv);
1292#endif
1164 } 1293 }
1165 } 1294 }
1166 } 1295 }
@@ -1177,9 +1306,21 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1177 if (XWINDOW (xv->w) == w) 1306 if (XWINDOW (xv->w) == w)
1178 { 1307 {
1179 if (xwidget_touched (xv)) 1308 if (xwidget_touched (xv))
1180 xwidget_show_view (xv); 1309 {
1310#ifdef USE_GTK
1311 xwidget_show_view (xv);
1312#elif defined NS_IMPL_COCOA
1313 nsxwidget_show_view (xv);
1314#endif
1315 }
1181 else 1316 else
1182 xwidget_hide_view (xv); 1317 {
1318#ifdef USE_GTK
1319 xwidget_hide_view (xv);
1320#elif defined NS_IMPL_COCOA
1321 nsxwidget_hide_view (xv);
1322#endif
1323 }
1183 } 1324 }
1184 } 1325 }
1185 } 1326 }
@@ -1198,6 +1339,7 @@ kill_buffer_xwidgets (Lisp_Object buffer)
1198 { 1339 {
1199 CHECK_XWIDGET (xwidget); 1340 CHECK_XWIDGET (xwidget);
1200 struct xwidget *xw = XXWIDGET (xwidget); 1341 struct xwidget *xw = XXWIDGET (xwidget);
1342#ifdef USE_GTK
1201 if (xw->widget_osr && xw->widgetwindow_osr) 1343 if (xw->widget_osr && xw->widgetwindow_osr)
1202 { 1344 {
1203 gtk_widget_destroy (xw->widget_osr); 1345 gtk_widget_destroy (xw->widget_osr);
@@ -1211,6 +1353,9 @@ kill_buffer_xwidgets (Lisp_Object buffer)
1211 xfree (xmint_pointer (XCAR (cb))); 1353 xfree (xmint_pointer (XCAR (cb)));
1212 ASET (xw->script_callbacks, idx, Qnil); 1354 ASET (xw->script_callbacks, idx, Qnil);
1213 } 1355 }
1356#elif defined NS_IMPL_COCOA
1357 nsxwidget_kill (xw);
1358#endif
1214 } 1359 }
1215 } 1360 }
1216} 1361}
diff --git a/src/xwidget.h b/src/xwidget.h
index 99fa8bbd612..29f1153206f 100644
--- a/src/xwidget.h
+++ b/src/xwidget.h
@@ -29,7 +29,13 @@ struct xwidget_view;
29struct window; 29struct window;
30 30
31#ifdef HAVE_XWIDGETS 31#ifdef HAVE_XWIDGETS
32# include <gtk/gtk.h> 32
33#if defined (USE_GTK)
34#include <gtk/gtk.h>
35#elif defined (NS_IMPL_COCOA) && defined (__OBJC__)
36#import <AppKit/NSView.h>
37#import "nsxwidget.h"
38#endif
33 39
34struct xwidget 40struct xwidget
35{ 41{
@@ -54,9 +60,25 @@ struct xwidget
54 int height; 60 int height;
55 int width; 61 int width;
56 62
63#if defined (USE_GTK)
57 /* For offscreen widgets, unused if not osr. */ 64 /* For offscreen widgets, unused if not osr. */
58 GtkWidget *widget_osr; 65 GtkWidget *widget_osr;
59 GtkWidget *widgetwindow_osr; 66 GtkWidget *widgetwindow_osr;
67#elif defined (NS_IMPL_COCOA)
68# ifdef __OBJC__
69 /* For offscreen widgets, unused if not osr. */
70 NSView *xwWidget;
71 XwWindow *xwWindow;
72
73 /* Used only for xwidget types (such as webkit2) enforcing 1 to 1
74 relationship between model and view. */
75 struct xwidget_view *xv;
76# else
77 void *xwWidget;
78 void *xwWindow;
79 struct xwidget_view *xv;
80# endif
81#endif
60 82
61 /* Kill silently if Emacs is exited. */ 83 /* Kill silently if Emacs is exited. */
62 bool_bf kill_without_query : 1; 84 bool_bf kill_without_query : 1;
@@ -75,9 +97,20 @@ struct xwidget_view
75 /* The "live" instance isn't drawn. */ 97 /* The "live" instance isn't drawn. */
76 bool hidden; 98 bool hidden;
77 99
100#if defined (USE_GTK)
78 GtkWidget *widget; 101 GtkWidget *widget;
79 GtkWidget *widgetwindow; 102 GtkWidget *widgetwindow;
80 GtkWidget *emacswindow; 103 GtkWidget *emacswindow;
104#elif defined (NS_IMPL_COCOA)
105# ifdef __OBJC__
106 XvWindow *xvWindow;
107 NSView *emacswindow;
108# else
109 void *xvWindow;
110 void *emacswindow;
111# endif
112#endif
113
81 int x; 114 int x;
82 int y; 115 int y;
83 int clip_right; 116 int clip_right;
@@ -116,6 +149,14 @@ void x_draw_xwidget_glyph_string (struct glyph_string *);
116struct xwidget *lookup_xwidget (Lisp_Object spec); 149struct xwidget *lookup_xwidget (Lisp_Object spec);
117void xwidget_end_redisplay (struct window *, struct glyph_matrix *); 150void xwidget_end_redisplay (struct window *, struct glyph_matrix *);
118void kill_buffer_xwidgets (Lisp_Object); 151void kill_buffer_xwidgets (Lisp_Object);
152/* Defined in 'xwidget.c'. */
153void store_xwidget_event_string (struct xwidget *xw,
154 const char *eventname,
155 const char *eventstr);
156
157void store_xwidget_js_callback_event (struct xwidget *xw,
158 Lisp_Object proc,
159 Lisp_Object argument);
119#else 160#else
120INLINE_HEADER_BEGIN 161INLINE_HEADER_BEGIN
121INLINE void syms_of_xwidget (void) {} 162INLINE void syms_of_xwidget (void) {}