1 /*********************************************************************
2 This is the back-end process for javascript.
3 This is the server, and edbrowse is the client.
4 We receive commands from edbrowse,
5 getting and setting properties for various DOM objects.
6 
7 This is the duktape version.
8 If you package this with the duktape js libraries,
9 you will need to include the MIT open source license,
10 along with the GPL, general public license.
11 
12 Exit codes are as follows:
13 0 terminate normally, as directed by edbrowse
14 1. bad arguments
15 2 cannot read or write to edbrowse
16 3 messages are out of sync
17 4 cannot create javascript runtime environmet
18 5 cannot read from stdin or write to stdout
19 6 unexpected message command from edbrowse
20 7 unexpected property type from edbrowse
21 8 unexpected class name from edbrowse
22 9 only arrays of objects are supported at this time
23 90 this program was never executed
24 99 memory allocation error or heap corruption
25 *********************************************************************/
26 
27 #include "eb.h"
28 
29 #ifdef DOSLIKE
30 #include "vsprtf.h"
31 #endif // DOSLIKE
32 
33 #include <duktape.h>
34 
35 static void processError(duk_context * cx);
36 static void jsInterruptCheck(duk_context * cx);
37 
native_error_stub_0(duk_context * cx)38 static duk_ret_t native_error_stub_0(duk_context * cx)
39 {
40 	return 0;
41 }
42 
native_error_stub_1(duk_context * cx)43 static duk_ret_t native_error_stub_1(duk_context * cx)
44 {
45 	i_puts(MSG_CompileError);
46 	return 0;
47 }
48 
49 const char *jsSourceFile;	// sourcefile providing the javascript
50 int jsLineno;			// line number
51 
52 static char *errorMessage;
53 static char *effects;
54 static int eff_l;
55 #define effectString(s) stringAndString(&effects, &eff_l, (s))
56 #define effectChar(s) stringAndChar(&effects, &eff_l, (s))
57 
58 static duk_context *context0;
59 static jsobjtype context0_obj;
60 
61 /* wrappers around duktape alloc functions: add our own header */
62 struct jsdata_wrap {
63 	union {
64 		uint64_t header;
65 		Tag *t;
66 	} u;
67 	char data[0];
68 };
69 #define jsdata_of(p) ((struct jsdata_wrap*)((char*)(p)-sizeof(struct jsdata_wrap)))
70 
watch_malloc(void * udata,size_t n)71 static void *watch_malloc(void *udata, size_t n)
72 {
73 	struct jsdata_wrap *w = malloc(n + sizeof(struct jsdata_wrap));
74 	if (!w)
75 		return NULL;
76 	w->u.t = 0;
77 	return w->data;
78 }
79 
watch_realloc(void * udata,void * p,size_t n)80 static void *watch_realloc(void *udata, void *p, size_t n)
81 {
82 	struct jsdata_wrap *w;
83 
84 	if (!p)
85 		return watch_malloc(udata, n);
86 
87 	w = jsdata_of(p);
88 	if (w->u.t != 0)
89 		debugPrint(1,
90 			   "realloc with a watched pointer, shouldn't happen");
91 	w = realloc(w, n + sizeof(struct jsdata_wrap));
92 	return w->data;
93 }
94 
watch_free(void * udata,void * p)95 static void watch_free(void *udata, void *p)
96 {
97 	Tag *t;
98 	struct jsdata_wrap *w;
99 
100 	if (!p)
101 		return;
102 
103 	w = jsdata_of(p);
104 	t = w->u.t;
105 	free(w);
106 	if (t) {
107 		debugPrint(4, "gc %p", p);
108 		t->jslink = false;
109 		killTag(t);
110 	}
111 }
112 
connectTagObject(Tag * t,jsobjtype p)113 void connectTagObject(Tag *t, jsobjtype p)
114 {
115 	struct jsdata_wrap *w = jsdata_of(p);
116 	if (w->u.t)
117 		debugPrint(1, "multiple tags connect to js pointer %p", p);
118 	w->u.t = t;
119 	t->jv = p;
120 	t->jslink = true;
121 	set_property_number_0(t->f0->cx, p, "eb$seqno", t->seqno);
122 	set_property_number_0(t->f0->cx, p, "eb$gsn", t->gsn);
123 }
124 
disconnectTagObject(Tag * t)125 void disconnectTagObject(Tag *t)
126 {
127 	struct jsdata_wrap *w;
128 	jsobjtype p = t->jv;
129 	if (!p)
130 		return;
131 	w = jsdata_of(p);
132 	if (w->u.t == NULL)
133 		debugPrint(1, "tag already disconnected from pointer %p", p);
134 	else if (w->u.t != t)
135 		debugPrint(1,
136 			   "tag disconnecting from pointer %p which is connected to some other tag",
137 			   p);
138 	w->u.t = NULL;
139 	t->jv = NULL;
140 	t->jslink = false;
141 }
142 
js_main(void)143 int js_main(void)
144 {
145 	effects = initString(&eff_l);
146 	context0 =
147 	    duk_create_heap(watch_malloc, watch_realloc, watch_free, 0, 0);
148 	if (!context0) {
149 		fprintf(stderr,
150 			"Cannot create javascript runtime environment\n");
151 		return 4;
152 	}
153 	duk_push_global_object(context0);
154 	duk_push_false(context0);
155 	duk_put_prop_string(context0, -2, "compiled");
156 	context0_obj = duk_get_heapptr(context0, -1);
157 	duk_pop(context0);
158 	return 0;
159 }				/* js_main */
160 
161 // base64 encode
native_btoa(duk_context * cx)162 static duk_ret_t native_btoa(duk_context * cx)
163 {
164 	char *t;
165 	const char *s = duk_get_string(cx, 0);
166 	if (!s)
167 		s = emptyString;
168 	t = base64Encode(s, strlen(s), false);
169 	duk_pop(cx);
170 	duk_push_string(cx, t);
171 	nzFree(t);
172 	return 1;
173 }
174 
175 // base64 decode
native_atob(duk_context * cx)176 static duk_ret_t native_atob(duk_context * cx)
177 {
178 	char *t1, *t2;
179 	const char *s = duk_get_string(cx, 0);
180 	if (!s)
181 		s = emptyString;
182 	t1 = cloneString(s);
183 	duk_pop(cx);
184 	t2 = t1 + strlen(t1);
185 	base64Decode(t1, &t2);
186 // ignore errors for now.
187 	*t2 = 0;
188 	duk_push_string(cx, t1);
189 	nzFree(t1);
190 	return 1;
191 }
192 
native_new_location(duk_context * cx)193 static duk_ret_t native_new_location(duk_context * cx)
194 {
195 	const char *s = duk_safe_to_string(cx, -1);
196 	if (s && *s) {
197 		char *t = cloneString(s);
198 /* url on one line, name of window on next line */
199 		char *u = strchr(t, '\n');
200 		*u++ = 0;
201 		debugPrint(4, "window %s|%s", t, u);
202 		javaOpensWindow(t, u);
203 		nzFree(t);
204 	}
205 	return 0;
206 }
207 
native_mywin(duk_context * cx)208 static duk_ret_t native_mywin(duk_context * cx)
209 {
210 	duk_push_global_object(cx);
211 	return 1;
212 }
213 
native_mydoc(duk_context * cx)214 static duk_ret_t native_mydoc(duk_context * cx)
215 {
216 	duk_get_global_string(cx, "document");
217 	return 1;
218 }
219 
native_hasfocus(duk_context * cx)220 static duk_ret_t native_hasfocus(duk_context * cx)
221 {
222 	duk_push_boolean(cx, foregroundWindow);
223 	return 1;
224 }
225 
native_puts(duk_context * cx)226 static duk_ret_t native_puts(duk_context * cx)
227 {
228 	const char *s = duk_safe_to_string(cx, -1);
229 	if (!s)
230 		s = emptyString;
231 	puts(s);
232 	return 0;
233 }
234 
235 // write local file
native_wlf(duk_context * cx)236 static duk_ret_t native_wlf(duk_context * cx)
237 {
238 	const char *s = duk_safe_to_string(cx, 0);
239 	int len = strlen(s);
240 	const char *filename = duk_safe_to_string(cx, 1);
241 	int fh;
242 	bool safe = false;
243 	if (stringEqual(filename, "from") || stringEqual(filename, "jslocal"))
244 		safe = true;
245 	if (filename[0] == 'f') {
246 		int i;
247 		for (i = 1; isdigit(filename[i]); ++i) ;
248 		if (i > 1 && (stringEqual(filename + i, ".js") ||
249 			      stringEqual(filename + i, ".css")))
250 			safe = true;
251 	}
252 	if (!safe)
253 		return 0;
254 	fh = open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_TEXT, MODE_rw);
255 	if (fh < 0) {
256 		fprintf(stderr, "cannot create file %s\n", filename);
257 		return 0;
258 	}
259 	if (write(fh, s, len) < len)
260 		fprintf(stderr, "cannot write file %s\n", filename);
261 	close(fh);
262 	if (stringEqual(filename, "jslocal"))
263 		writeShortCache();
264 	return 0;
265 }
266 
native_media(duk_context * cx)267 static duk_ret_t native_media(duk_context * cx)
268 {
269 	const char *s = duk_safe_to_string(cx, 0);
270 	bool rc = false;
271 	if (s && *s) {
272 		char *t = cloneString(s);
273 		rc = matchMedia(t);
274 		nzFree(t);
275 	}
276 	duk_pop(cx);
277 	duk_push_boolean(cx, rc);
278 	return 1;
279 }
280 
native_logputs(duk_context * cx)281 static duk_ret_t native_logputs(duk_context * cx)
282 {
283 	int minlev = duk_get_int(cx, 0);
284 	const char *s = duk_safe_to_string(cx, 1);
285 	duk_remove(cx, 0);
286 	if (debugLevel >= minlev && s && *s)
287 		debugPrint(3, "%s", s);
288 	jsInterruptCheck(cx);
289 	return 0;
290 }
291 
native_prompt(duk_context * cx)292 static duk_ret_t native_prompt(duk_context * cx)
293 {
294 	const char *msg = 0;
295 	const char *answer = 0;
296 	int top = duk_get_top(cx);
297 	char inbuf[80];
298 	if (top > 0) {
299 		msg = duk_safe_to_string(cx, 0);
300 		if (top > 1)
301 			answer = duk_safe_to_string(cx, 1);
302 	}
303 	if (msg && *msg) {
304 		char c, *s;
305 		printf("%s", msg);
306 /* If it doesn't end in space or question mark, print a colon */
307 		c = msg[strlen(msg) - 1];
308 		if (!isspace(c)) {
309 			if (!ispunct(c))
310 				printf(":");
311 			printf(" ");
312 		}
313 		if (answer && *answer)
314 			printf("[%s] ", answer);
315 		fflush(stdout);
316 		if (!fgets(inbuf, sizeof(inbuf), stdin))
317 			exit(5);
318 		s = inbuf + strlen(inbuf);
319 		if (s > inbuf && s[-1] == '\n')
320 			*--s = 0;
321 		if (inbuf[0])
322 			answer = inbuf;
323 	}
324 	duk_pop_n(cx, top);
325 	duk_push_string(cx, answer);
326 	return 1;
327 }
328 
native_confirm(duk_context * cx)329 static duk_ret_t native_confirm(duk_context * cx)
330 {
331 	const char *msg = duk_safe_to_string(cx, 0);
332 	bool answer = false, first = true;
333 	char c = 'n';
334 	char inbuf[80];
335 	if (msg && *msg) {
336 		while (true) {
337 			printf("%s", msg);
338 			c = msg[strlen(msg) - 1];
339 			if (!isspace(c)) {
340 				if (!ispunct(c))
341 					printf(":");
342 				printf(" ");
343 			}
344 			if (!first)
345 				printf("[y|n] ");
346 			first = false;
347 			fflush(stdout);
348 			if (!fgets(inbuf, sizeof(inbuf), stdin))
349 				exit(5);
350 			c = *inbuf;
351 			if (c && strchr("nNyY", c))
352 				break;
353 		}
354 	}
355 	duk_pop(cx);
356 	if (c == 'y' || c == 'Y')
357 		answer = true;
358 	duk_push_boolean(cx, answer);
359 	return 1;
360 }
361 
362 /* represent an object pointer in ascii */
pointer2string(const jsobjtype obj)363 static const char *pointer2string(const jsobjtype obj)
364 {
365 	static char pbuf[32];
366 	sprintf(pbuf, "%p", obj);
367 	return pbuf;
368 }				/* pointer2string */
369 
370 // Sometimes control c can interrupt long running javascript, if the script
371 // calls our native methods.
jsInterruptCheck(duk_context * cx)372 static void jsInterruptCheck(duk_context * cx)
373 {
374 	if (!intFlag)
375 		return;
376 	duk_get_global_string(cx, "eb$stopexec");
377 // this next line should fail and stop the script!
378 // Assuming we aren't in a try{} block.
379 	duk_call(cx, 0);
380 // It didn't stop the script, oh well.
381 	duk_pop(cx);
382 }
383 
getter_innerHTML(duk_context * cx)384 static duk_ret_t getter_innerHTML(duk_context * cx)
385 {
386 	duk_push_this(cx);
387 	duk_get_prop_string(cx, -1, "inner$HTML");
388 	duk_remove(cx, -2);
389 	return 1;
390 }
391 
setter_innerHTML(duk_context * cx)392 static duk_ret_t setter_innerHTML(duk_context * cx)
393 {
394 	jsobjtype thisobj, c1, c2;
395 	char *run;
396 	int run_l;
397 	const char *h = duk_safe_to_string(cx, -1);
398 	if (!h)			// should never happen
399 		h = emptyString;
400 	debugPrint(5, "setter h 1");
401 	jsInterruptCheck(cx);
402 	duk_push_this(cx);
403 // remove the preexisting children.
404 	if (duk_get_prop_string(cx, -1, "childNodes") && duk_is_array(cx, -1)) {
405 		c1 = duk_get_heapptr(cx, -1);
406 	} else {
407 // no child nodes array, don't do anything.
408 // This should never happen.
409 		duk_pop_n(cx, 3);
410 		debugPrint(5, "setter h 3");
411 		return 0;
412 	}
413 // hold this away from garbage collection
414 	duk_put_prop_string(cx, -2, "old$cn");
415 // stack now holds html and this
416 // make new childNodes array
417 	duk_get_global_string(cx, "Array");
418 	duk_pnew(cx, 0);
419 	c2 = duk_get_heapptr(cx, -1);
420 	duk_put_prop_string(cx, -2, "childNodes");
421 // stack now holds html and this
422 	duk_insert(cx, -2);
423 	duk_put_prop_string(cx, -2, "inner$HTML");
424 // stack now holds this
425 
426 	thisobj = duk_get_heapptr(cx, -1);
427 
428 // Put some tags around the html, so tidy can parse it.
429 	run = initString(&run_l);
430 	stringAndString(&run, &run_l, "<!DOCTYPE public><body>\n");
431 	stringAndString(&run, &run_l, h);
432 	if (*h && h[strlen(h) - 1] != '\n')
433 		stringAndChar(&run, &run_l, '\n');
434 	stringAndString(&run, &run_l, "</body>");
435 
436 // now turn the html into objects
437 	html_from_setter(thisobj, run);
438 	nzFree(run);
439 	debugPrint(5, "setter h 2");
440 
441 	run_function_onearg_0(cx, context0_obj, "textarea$html$crossover",
442 				thisobj);
443 
444 // mutation fix up from native code
445 	duk_push_heapptr(cx, context0_obj);
446 	duk_get_prop_string(cx, -1, "mutFixup");
447 	if (duk_is_function(cx, -1)) {
448 		duk_push_heapptr(cx, thisobj);
449 		duk_push_false(cx);
450 		duk_push_heapptr(cx, c2);
451 		duk_push_heapptr(cx, c1);
452 		duk_call(cx, 4);
453 	}
454 // stack is this mw$ retval
455 	duk_pop_2(cx);
456 	duk_del_prop_string(cx, -1, "old$cn");
457 	duk_pop(cx);
458 
459 	return 0;
460 }
461 
getter_value(duk_context * cx)462 static duk_ret_t getter_value(duk_context * cx)
463 {
464 	duk_push_this(cx);
465 	duk_get_prop_string(cx, -1, "val$ue");
466 	duk_remove(cx, -2);
467 	return 1;
468 }
469 
setter_value(duk_context * cx)470 static duk_ret_t setter_value(duk_context * cx)
471 {
472 	jsobjtype thisobj;
473 	char *t;
474 	const char *h = duk_safe_to_string(cx, -1);
475 	if (!h)			// should never happen
476 		h = emptyString;
477 	debugPrint(5, "setter v 1");
478 	t = cloneString(h);
479 	duk_push_this(cx);
480 	duk_insert(cx, -2);
481 	duk_put_prop_string(cx, -2, "val$ue");
482 	thisobj = duk_get_heapptr(cx, -1);
483 	duk_pop(cx);
484 	prepareForField(t);
485 	debugPrint(4, "value %p=%s", thisobj, t);
486 	javaSetsTagVar(thisobj, t);
487 	nzFree(t);
488 	debugPrint(5, "setter v 2");
489 	return 0;
490 }
491 
forceFrameExpand(duk_context * cx,jsobjtype thisobj)492 static void forceFrameExpand(duk_context * cx, jsobjtype thisobj)
493 {
494 	Frame *save_cf = cf;
495 	const char *save_src = jsSourceFile;
496 	int save_lineno = jsLineno;
497 	bool save_plug = pluginsOn;
498 	duk_push_true(cx);
499 	duk_put_prop_string(cx, -2, "eb$auto");
500 	pluginsOn = false;
501 	whichproc = 'e';
502 	frameExpandLine(0, thisobj);
503 	whichproc = 'j';
504 	cf = save_cf;
505 	jsSourceFile = save_src;
506 	jsLineno = save_lineno;
507 	pluginsOn = save_plug;
508 }
509 
510 // contentDocument getter setter; this is a bit complicated.
getter_cd(duk_context * cx)511 static duk_ret_t getter_cd(duk_context * cx)
512 {
513 	bool found;
514 	jsobjtype thisobj;
515 	jsInterruptCheck(cx);
516 	duk_push_this(cx);
517 	thisobj = duk_get_heapptr(cx, -1);
518 	found = duk_get_prop_string(cx, -1, "eb$auto");
519 	duk_pop(cx);
520 	if (!found)
521 		forceFrameExpand(cx, thisobj);
522 	duk_get_prop_string(cx, -1, "content$Document");
523 	duk_remove(cx, -2);
524 	return 1;
525 }
526 
527 // You can't really change contentDocument; this is a stub.
setter_cd(duk_context * cx)528 static duk_ret_t setter_cd(duk_context * cx)
529 {
530 	return 0;
531 }
532 
getter_cw(duk_context * cx)533 static duk_ret_t getter_cw(duk_context * cx)
534 {
535 	bool found;
536 	jsobjtype thisobj;
537 	jsInterruptCheck(cx);
538 	duk_push_this(cx);
539 	thisobj = duk_get_heapptr(cx, -1);
540 	found = duk_get_prop_string(cx, -1, "eb$auto");
541 	duk_pop(cx);
542 	if (!found)
543 		forceFrameExpand(cx, thisobj);
544 	duk_get_prop_string(cx, -1, "content$Window");
545 	duk_remove(cx, -2);
546 	return 1;
547 }
548 
549 // You can't really change contentWindow; this is a stub.
setter_cw(duk_context * cx)550 static duk_ret_t setter_cw(duk_context * cx)
551 {
552 	return 0;
553 }
554 
native_unframe(duk_context * cx)555 static duk_ret_t native_unframe(duk_context * cx)
556 {
557 	if (duk_is_object(cx, 0))
558 		unframe(duk_get_heapptr(cx, 0), duk_get_heapptr(cx, 1));
559 	duk_pop_2(cx);
560 	return 0;
561 }
562 
native_unframe2(duk_context * cx)563 static duk_ret_t native_unframe2(duk_context * cx)
564 {
565 	if (duk_is_object(cx, 0))
566 		unframe2(duk_get_heapptr(cx, 0));
567 	duk_pop(cx);
568 	return 0;
569 }
570 
linkageNow(duk_context * cx,char linkmode,jsobjtype o)571 static void linkageNow(duk_context * cx, char linkmode, jsobjtype o)
572 {
573 	jsInterruptCheck(cx);
574 	debugPrint(4, "linkset %s", effects + 2);
575 	javaSetsLinkage(false, linkmode, o, strchr(effects, ',') + 1);
576 	nzFree(effects);
577 	effects = initString(&eff_l);
578 }
579 
native_log_element(duk_context * cx)580 static duk_ret_t native_log_element(duk_context * cx)
581 {
582 	jsobjtype newobj = duk_get_heapptr(cx, -2);
583 	const char *tag = duk_get_string(cx, -1);
584 	char e[60];
585 	if (!newobj || !tag)
586 		return 0;
587 	debugPrint(5, "log el 1");
588 	jsInterruptCheck(cx);
589 // pass the newly created node over to edbrowse
590 	sprintf(e, "l{c|%s,%s 0x0, 0x0, ", pointer2string(newobj), tag);
591 	effectString(e);
592 	linkageNow(cx, 'c', newobj);
593 	duk_pop(cx);
594 // create the innerHTML member with its setter, this has to be done in C.
595 	duk_push_string(cx, "innerHTML");
596 	duk_push_c_function(cx, getter_innerHTML, 0);
597 	duk_push_c_function(cx, setter_innerHTML, 1);
598 	duk_def_prop(cx, -4,
599 		     (DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER |
600 		      DUK_DEFPROP_SET_ENUMERABLE));
601 	duk_push_string(cx, emptyString);
602 	duk_put_prop_string(cx, -2, "inner$HTML");
603 	duk_pop(cx);
604 	debugPrint(5, "log el 2");
605 	return 0;
606 }
607 
608 /* like the function in ebjs.c, but a different name */
fakePropName(void)609 static const char *fakePropName(void)
610 {
611 	static char fakebuf[24];
612 	static int idx = 0;
613 	++idx;
614 	sprintf(fakebuf, "cg$$%d", idx);
615 	return fakebuf;
616 }
617 
set_timeout(duk_context * cx,bool isInterval)618 static void set_timeout(duk_context * cx, bool isInterval)
619 {
620 	jsobjtype to;		// timer object
621 	bool cc_error = false;
622 	int top = duk_get_top(cx);
623 	int n = 1000;		/* default number of milliseconds */
624 	char fname[48];		/* function name */
625 	const char *fstr;	/* function string */
626 	const char *s, *fpn;
627 
628 	if (top == 0)
629 		return;		// no args
630 
631 	debugPrint(5, "timer 1");
632 // if second parameter is missing, leave milliseconds at 1000.
633 	if (top > 1) {
634 		n = duk_get_int(cx, 1);
635 		duk_pop_n(cx, top - 1);
636 	}
637 // now the function is the only thing on the stack.
638 
639 	if (duk_is_function(cx, 0)) {
640 		duk_push_string(cx, "?");
641 		duk_put_prop_string(cx, -2, "body");
642 // We use to extract the function name in moz js, don't know how to do it here.
643 		strcpy(fname, "javascript()");
644 	} else if (duk_is_string(cx, 0)) {
645 // need to make a copy of the source code.
646 		char *body = cloneString(duk_get_string(cx, 0));
647 // pull the function name out of the string, if that makes sense.
648 		fstr = body;
649 		strcpy(fname, "?");
650 		s = fstr;
651 		skipWhite(&s);
652 		if (memEqualCI(s, "javascript:", 11))
653 			s += 11;
654 		skipWhite(&s);
655 		if (isalpha(*s) || *s == '_') {
656 			char *j;
657 			for (j = fname; isalnum(*s) || *s == '_'; ++s) {
658 				if (j < fname + sizeof(fname) - 3)
659 					*j++ = *s;
660 			}
661 			strcpy(j, "()");
662 			skipWhite(&s);
663 			if (*s != '(')
664 				strcpy(fname, "?");
665 		}
666 // compile the string under the filename timer
667 		duk_push_string(cx, "timer");
668 		if (duk_pcompile(cx, 0)) {
669 			processError(cx);
670 			cc_error = true;
671 			duk_push_c_function(cx, native_error_stub_0, 0);
672 		}
673 // Now looks like a function object, just like the previous case.
674 		duk_push_string(cx, body);
675 		duk_put_prop_string(cx, -2, "body");
676 		nzFree(body);
677 	} else {
678 // oops, not a function or a string.
679 		duk_pop(cx);
680 		return;
681 	}
682 
683 	duk_push_global_object(cx);
684 	fpn = fakePropName();
685 	if (cc_error)
686 		debugPrint(3, "compile error on timer %s", fpn);
687 	duk_push_string(cx, fpn);
688 // Create a timer object.
689 	duk_get_global_string(cx, "Timer");
690 	if (duk_pnew(cx, 0)) {
691 		processError(cx);
692 		duk_pop_n(cx, 3);
693 		goto done;
694 	}
695 // stack now has function global fakePropertyName timer-object.
696 // classs is milliseconds, for debugging
697 	duk_push_int(cx, n);
698 	duk_put_prop_string(cx, -2, "class");
699 	to = duk_get_heapptr(cx, -1);
700 // protect this timer from the garbage collector.
701 	duk_def_prop(cx, 1,
702 		     (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE |
703 		      DUK_DEFPROP_CLEAR_WRITABLE |
704 		      DUK_DEFPROP_SET_CONFIGURABLE));
705 	duk_pop(cx);		// don't need global any more
706 
707 // function is contained in an ontimer handler
708 	duk_push_heapptr(cx, to);
709 	duk_insert(cx, 0);	// switch places
710 // now stack is timer_object function
711 	duk_push_string(cx, "ontimer");
712 	duk_insert(cx, 1);
713 	duk_def_prop(cx, 0,
714 		     (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_ENUMERABLE |
715 		      DUK_DEFPROP_SET_WRITABLE |
716 		      DUK_DEFPROP_CLEAR_CONFIGURABLE));
717 	duk_push_string(cx, fpn);
718 	duk_put_prop_string(cx, -2, "backlink");
719 // leaves just the timer object on the stack, which is what we want.
720 
721 	javaSetsTimeout(n, fname, to, isInterval);
722 
723 done:
724 	debugPrint(5, "timer 2");
725 }
726 
native_setTimeout(duk_context * cx)727 static duk_ret_t native_setTimeout(duk_context * cx)
728 {
729 	set_timeout(cx, false);
730 	return 1;
731 }
732 
native_setInterval(duk_context * cx)733 static duk_ret_t native_setInterval(duk_context * cx)
734 {
735 	set_timeout(cx, true);
736 	return 1;
737 }
738 
native_clearTimeout(duk_context * cx)739 static duk_ret_t native_clearTimeout(duk_context * cx)
740 {
741 	jsobjtype obj = duk_get_heapptr(cx, 0);
742 	if (!obj)
743 		return 0;
744 	javaSetsTimeout(0, "-", obj, false);
745 	return 0;
746 }
747 
native_win_close(duk_context * cx)748 static duk_ret_t native_win_close(duk_context * cx)
749 {
750 	i_puts(MSG_PageDone);
751 // I should probably freeJavaContext and close down javascript,
752 // but not sure I can do that while the js function is still running.
753 	return 0;
754 }
755 
756 // find the frame, in the current window, that goes with this.
757 // Used by document.write to put the html in the right frame.
thisFrame(duk_context * cx)758 static Frame *thisFrame(duk_context * cx)
759 {
760 	jsobjtype thisobj;
761 	Frame *f;
762 	duk_push_this(cx);
763 	thisobj = duk_get_heapptr(cx, -1);
764 	duk_pop(cx);
765 	for (f = &(cw->f0); f; f = f->next) {
766 		if (f->docobj == thisobj)
767 			break;
768 	}
769 	return f;
770 }
771 
dwrite(duk_context * cx,bool newline)772 static void dwrite(duk_context * cx, bool newline)
773 {
774 	int top = duk_get_top(cx);
775 	const char *s;
776 	Frame *f, *save_cf = cf;
777 	if (top) {
778 		duk_push_string(cx, emptyString);
779 		duk_insert(cx, 0);
780 		duk_join(cx, top);
781 	} else {
782 		duk_push_string(cx, emptyString);
783 	}
784 	s = duk_get_string(cx, 0);
785 	if (!s || !*s)
786 		return;
787 	debugPrint(4, "dwrite:%s", s);
788 	f = thisFrame(cx);
789 	if (!f)
790 		debugPrint(3,
791 			   "no frame found for document.write, using the default");
792 	else {
793 #if 0
794 		if (f != cf)
795 			debugPrint(3, "document.write on a different frame");
796 #endif
797 		cf = f;
798 	}
799 	dwStart();
800 	stringAndString(&cf->dw, &cf->dw_l, s);
801 	if (newline)
802 		stringAndChar(&cf->dw, &cf->dw_l, '\n');
803 	cf = save_cf;
804 }
805 
native_doc_write(duk_context * cx)806 static duk_ret_t native_doc_write(duk_context * cx)
807 {
808 	dwrite(cx, false);
809 	return 0;
810 }
811 
native_doc_writeln(duk_context * cx)812 static duk_ret_t native_doc_writeln(duk_context * cx)
813 {
814 	dwrite(cx, true);
815 	return 0;
816 }
817 
818 // We need to call and remember up to 3 node names, and then embed
819 // them in the side effects string, after all duktape calls have been made.
embedNodeName(duk_context * cx,jsobjtype obj)820 static const char *embedNodeName(duk_context * cx, jsobjtype obj)
821 {
822 	static char buf1[MAXTAGNAME], buf2[MAXTAGNAME], buf3[MAXTAGNAME];
823 	char *b;
824 	static int cycle = 0;
825 	const char *nodeName = 0;
826 	int length;
827 
828 	if (++cycle == 4)
829 		cycle = 1;
830 	if (cycle == 1)
831 		b = buf1;
832 	if (cycle == 2)
833 		b = buf2;
834 	if (cycle == 3)
835 		b = buf3;
836 	*b = 0;
837 
838 	duk_push_heapptr(cx, obj);
839 	if (duk_get_prop_string(cx, -1, "nodeName"))
840 		nodeName = duk_get_string(cx, -1);
841 	if (nodeName) {
842 		length = strlen(nodeName);
843 		if (length >= MAXTAGNAME)
844 			length = MAXTAGNAME - 1;
845 		strncpy(b, nodeName, length);
846 		b[length] = 0;
847 	}
848 	duk_pop_2(cx);
849 	caseShift(b, 'l');
850 	return b;
851 }				/* embedNodeName */
852 
append0(duk_context * cx,bool side)853 static void append0(duk_context * cx, bool side)
854 {
855 	unsigned i, length;
856 	jsobjtype child, thisobj;
857 	char *e;
858 	const char *thisname, *childname;
859 
860 /* we need one argument that is an object */
861 	if (duk_get_top(cx) != 1 || !duk_is_object(cx, 0))
862 		return;
863 
864 	debugPrint(5, "append 1");
865 	child = duk_get_heapptr(cx, 0);
866 	duk_push_this(cx);
867 	thisobj = duk_get_heapptr(cx, -1);
868 	if (!duk_get_prop_string(cx, -1, "childNodes") || !duk_is_array(cx, -1)) {
869 		duk_pop_2(cx);
870 		goto done;
871 	}
872 	length = duk_get_length(cx, -1);
873 // see if it's already there.
874 	for (i = 0; i < length; ++i) {
875 		duk_get_prop_index(cx, -1, i);
876 		if (child == duk_get_heapptr(cx, -1)) {
877 // child was already there, just return.
878 			duk_pop_n(cx, 3);
879 			goto done;
880 		}
881 		duk_pop(cx);
882 	}
883 
884 // add child to the end
885 	duk_push_heapptr(cx, child);
886 	duk_put_prop_index(cx, -2, length);
887 	duk_pop(cx);
888 	duk_push_string(cx, "parentNode");
889 	duk_insert(cx, 1);
890 	duk_def_prop(cx, 0,
891 		     (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE |
892 		      DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE));
893 
894 	if (!side)
895 		goto done;
896 
897 /* pass this linkage information back to edbrowse, to update its dom tree */
898 	thisname = embedNodeName(cx, thisobj);
899 	childname = embedNodeName(cx, child);
900 	asprintf(&e, "l{a|%s,%s ", pointer2string(thisobj), thisname);
901 	effectString(e);
902 	free(e);
903 	effectString(pointer2string(child));
904 	effectChar(',');
905 	effectString(childname);
906 	effectString(" 0x0, ");
907 	linkageNow(cx, 'a', thisobj);
908 
909 done:
910 	debugPrint(5, "append 2");
911 }
912 
native_apch1(duk_context * cx)913 static duk_ret_t native_apch1(duk_context * cx)
914 {
915 	append0(cx, false);
916 	return 1;
917 }
918 
native_apch2(duk_context * cx)919 static duk_ret_t native_apch2(duk_context * cx)
920 {
921 	append0(cx, true);
922 	return 1;
923 }
924 
native_insbf(duk_context * cx)925 static duk_ret_t native_insbf(duk_context * cx)
926 {
927 	unsigned i, length;
928 	int mark;
929 	jsobjtype child, item, thisobj, h;
930 	char *e;
931 	const char *thisname, *childname, *itemname;
932 
933 /* we need two objects */
934 	if (duk_get_top(cx) != 2 ||
935 	    !duk_is_object(cx, 0) || !duk_is_object(cx, 1))
936 		return 0;
937 
938 	debugPrint(5, "before 1");
939 	child = duk_get_heapptr(cx, 0);
940 	item = duk_get_heapptr(cx, 1);
941 	duk_push_this(cx);
942 	thisobj = duk_get_heapptr(cx, -1);
943 	duk_get_prop_string(cx, -1, "childNodes");
944 	if (!duk_is_array(cx, -1)) {
945 		duk_pop_n(cx, 3);
946 		goto done;
947 	}
948 	length = duk_get_length(cx, -1);
949 	mark = -1;
950 	for (i = 0; i < length; ++i) {
951 		duk_get_prop_index(cx, -1, i);
952 		h = duk_get_heapptr(cx, -1);
953 		if (child == h) {
954 			duk_pop_n(cx, 4);
955 			goto done;
956 		}
957 		if (h == item)
958 			mark = i;
959 		duk_pop(cx);
960 	}
961 
962 	if (mark < 0) {
963 		duk_pop_n(cx, 3);
964 		goto done;
965 	}
966 
967 /* push the other elements down */
968 	for (i = length; i > (unsigned)mark; --i) {
969 		duk_get_prop_index(cx, -1, i - 1);
970 		duk_put_prop_index(cx, -2, i);
971 	}
972 /* and place the child */
973 	duk_push_heapptr(cx, child);
974 	duk_put_prop_index(cx, -2, mark);
975 	duk_pop(cx);
976 	duk_push_string(cx, "parentNode");
977 	duk_insert(cx, -2);
978 	duk_remove(cx, 1);
979 	duk_def_prop(cx, 0,
980 		     (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE |
981 		      DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE));
982 
983 /* pass this linkage information back to edbrowse, to update its dom tree */
984 	thisname = embedNodeName(cx, thisobj);
985 	childname = embedNodeName(cx, child);
986 	itemname = embedNodeName(cx, item);
987 	asprintf(&e, "l{b|%s,%s ", pointer2string(thisobj), thisname);
988 	effectString(e);
989 	free(e);
990 	effectString(pointer2string(child));
991 	effectChar(',');
992 	effectString(childname);
993 	effectChar(' ');
994 	effectString(pointer2string(item));
995 	effectChar(',');
996 	effectString(itemname);
997 	effectChar(' ');
998 	linkageNow(cx, 'b', thisobj);
999 
1000 done:
1001 	debugPrint(5, "before 2");
1002 	return 1;
1003 }
1004 
native_removeChild(duk_context * cx)1005 static duk_ret_t native_removeChild(duk_context * cx)
1006 {
1007 	unsigned i, length;
1008 	int mark;
1009 	jsobjtype child, thisobj, h;
1010 	char *e;
1011 	const char *thisname, *childname;
1012 
1013 	debugPrint(5, "remove 1");
1014 // top of stack must be the object to remove.
1015 	if (!duk_is_object(cx, -1))
1016 		goto fail;
1017 	child = duk_get_heapptr(cx, -1);
1018 	duk_push_this(cx);
1019 	thisobj = duk_get_heapptr(cx, -1);
1020 	duk_get_prop_string(cx, -1, "childNodes");
1021 	if (!duk_is_array(cx, -1)) {
1022 		duk_pop_2(cx);
1023 		goto fail;
1024 	}
1025 	length = duk_get_length(cx, -1);
1026 	mark = -1;
1027 	for (i = 0; i < length; ++i) {
1028 		duk_get_prop_index(cx, -1, i);
1029 		h = duk_get_heapptr(cx, -1);
1030 		if (h == child)
1031 			mark = i;
1032 		duk_pop(cx);
1033 		if (mark >= 0)
1034 			break;
1035 	}
1036 
1037 	if (mark < 0) {
1038 		duk_pop_2(cx);
1039 		goto fail;
1040 	}
1041 
1042 /* push the other elements down */
1043 	for (i = mark + 1; i < length; ++i) {
1044 		duk_get_prop_index(cx, -1, i);
1045 		duk_put_prop_index(cx, -2, i - 1);
1046 	}
1047 	duk_set_length(cx, -1, length - 1);
1048 	duk_pop_2(cx);
1049 // missing parentnode must always be null
1050 	duk_push_null(cx);
1051 	duk_put_prop_string(cx, -2, "parentNode");
1052 
1053 /* pass this linkage information back to edbrowse, to update its dom tree */
1054 	thisname = embedNodeName(cx, thisobj);
1055 	childname = embedNodeName(cx, child);
1056 	asprintf(&e, "l{r|%s,%s ", pointer2string(thisobj), thisname);
1057 	effectString(e);
1058 	free(e);
1059 	effectString(pointer2string(child));
1060 	effectChar(',');
1061 	effectString(childname);
1062 	effectString(" 0x0, ");
1063 	linkageNow(cx, 'r', thisobj);
1064 
1065 	debugPrint(5, "remove 2");
1066 // mutation fix up from native code
1067 	duk_push_heapptr(cx, context0_obj);
1068 	duk_get_prop_string(cx, -1, "mutFixup");
1069 	if (duk_is_function(cx, -1)) {
1070 		duk_push_heapptr(cx, thisobj);
1071 		duk_push_false(cx);
1072 // exception here, push an integer where the node was.
1073 		duk_push_int(cx, mark);
1074 		duk_push_heapptr(cx, child);
1075 		duk_call(cx, 4);
1076 	}
1077 	duk_pop_2(cx);
1078 	return 1;
1079 
1080 fail:
1081 	debugPrint(5, "remove 2");
1082 	duk_pop(cx);
1083 	duk_push_null(cx);
1084 	return 1;
1085 }
1086 
native_fetchHTTP(duk_context * cx)1087 static duk_ret_t native_fetchHTTP(duk_context * cx)
1088 {
1089 	jsobjtype thisobj;
1090 	struct i_get g;
1091 	const char *incoming_url = duk_safe_to_string(cx, 0);
1092 	const char *incoming_method = duk_get_string(cx, 1);
1093 	const char *incoming_headers = duk_get_string(cx, 2);
1094 	const char *incoming_payload = duk_get_string(cx, 3);
1095 	char *outgoing_xhrheaders = NULL;
1096 	char *outgoing_xhrbody = NULL;
1097 	char *a = NULL, methchar = '?';
1098 	bool rc, async;
1099 
1100 	debugPrint(5, "xhr 1");
1101 	duk_push_this(cx);
1102 	thisobj = duk_get_heapptr(cx, -1);
1103 	duk_get_prop_string(cx, -1, "async");
1104 	async = duk_get_boolean(cx, -1);
1105 	duk_pop_2(cx);
1106 	if (!down_jsbg)
1107 		async = false;
1108 
1109 // asynchronous xhr before browse and after browse go down different paths.
1110 // So far I can't get the before browse path to work,
1111 // at least on nasa.gov, which has lots of xhrs in its onload code.
1112 // It pushes things over to timers, which work, but the page is rendered
1113 // shortly after browse time instead of at browse time, which is annoying.
1114 	if (!cw->browseMode)
1115 		async = false;
1116 
1117 	if (!incoming_url)
1118 		incoming_url = emptyString;
1119 	if (incoming_payload && *incoming_payload) {
1120 		if (incoming_method && stringEqualCI(incoming_method, "post"))
1121 			methchar = '\1';
1122 		if (asprintf(&a, "%s%c%s",
1123 			     incoming_url, methchar, incoming_payload) < 0)
1124 			i_printfExit(MSG_MemAllocError, 50);
1125 		incoming_url = a;
1126 	}
1127 
1128 	debugPrint(3, "xhr send %s", incoming_url);
1129 
1130 // async and sync are completely different
1131 	if (async) {
1132 		const char *fpn = fakePropName();
1133 // I'm going to put the tag in cf, the current frame, and hope that's right,
1134 // hope that xhr runs in a script that runs in the current frame.
1135 		Tag *t =     newTag(cf, cw->browseMode ? "object" : "script");
1136 		t->deleted = true;	// do not render this tag
1137 		t->step = 3;
1138 		t->async = true;
1139 		t->inxhr = true;
1140 		t->f0 = cf;
1141 		connectTagObject(t, thisobj);
1142 		duk_pop_n(cx, 4);
1143 // This routine will return, and javascript might stop altogether; do we need
1144 // to protect this object from garbage collection?
1145 		duk_push_global_object(cx);
1146 		duk_push_this(cx);
1147 		duk_push_string(cx, fpn);
1148 		duk_def_prop(cx, 0,
1149 			     (DUK_DEFPROP_HAVE_VALUE |
1150 			      DUK_DEFPROP_SET_ENUMERABLE |
1151 			      DUK_DEFPROP_CLEAR_WRITABLE |
1152 			      DUK_DEFPROP_SET_CONFIGURABLE));
1153 		duk_pop(cx);	// don't need global any more
1154 		duk_push_this(cx);
1155 		duk_push_string(cx, fpn);
1156 		duk_put_prop_string(cx, 0, "backlink");
1157 		duk_pop(cx);
1158 // That takes care of garbage collection.
1159 // Now everything has to be allocated.
1160 		t->href = (a ? a : cloneString(incoming_url));
1161 // overloading the innerHTML field
1162 		t->innerHTML = cloneString(incoming_headers);
1163 		if (cw->browseMode)
1164 			scriptSetsTimeout(t);
1165 		pthread_create(&t->loadthread, NULL, httpConnectBack3,
1166 			       (void *)t);
1167 		duk_push_string(cx, "async");
1168 		return 1;
1169 	}
1170 
1171 	memset(&g, 0, sizeof(g));
1172 	g.thisfile = cf->fileName;
1173 	g.uriEncoded = true;
1174 	g.url = incoming_url;
1175 	g.custom_h = incoming_headers;
1176 	g.headers_p = &outgoing_xhrheaders;
1177 	rc = httpConnect(&g);
1178 	outgoing_xhrbody = g.buffer;
1179 	nzFree(a);
1180 	if (intFlag) {
1181 		duk_get_global_string(cx, "eb$stopexec");
1182 // this next line should fail and stop the script!
1183 		duk_call(cx, 0);
1184 // It didn't stop the script, oh well.
1185 		duk_pop(cx);
1186 	}
1187 	if (outgoing_xhrheaders == NULL)
1188 		outgoing_xhrheaders = emptyString;
1189 	if (outgoing_xhrbody == NULL)
1190 		outgoing_xhrbody = emptyString;
1191 	duk_pop_n(cx, 4);
1192 	duk_push_string(cx, "");
1193 	duk_push_string(cx, "\r\n\r\n");
1194 	duk_push_int(cx, rc);
1195 	duk_push_int(cx, g.code);
1196 	duk_push_string(cx, outgoing_xhrheaders);
1197 	duk_join(cx, 3);
1198 	duk_push_string(cx, outgoing_xhrbody);
1199 	duk_join(cx, 2);
1200 	nzFree(outgoing_xhrheaders);
1201 	nzFree(outgoing_xhrbody);
1202 
1203 	debugPrint(5, "xhr 2");
1204 	return 1;
1205 }
1206 
native_resolveURL(duk_context * cx)1207 static duk_ret_t native_resolveURL(duk_context * cx)
1208 {
1209 	const char *base = duk_get_string(cx, -2);
1210 	const char *rel = duk_get_string(cx, -1);
1211 	char *outgoing_url;
1212 	if (!base)
1213 		base = emptyString;
1214 	if (!rel)
1215 		rel = emptyString;
1216 	outgoing_url = resolveURL(base, rel);
1217 	if (outgoing_url == NULL)
1218 		outgoing_url = emptyString;
1219 	duk_pop_2(cx);
1220 	duk_push_string(cx, outgoing_url);
1221 	nzFree(outgoing_url);
1222 	return 1;
1223 }
1224 
native_formSubmit(duk_context * cx)1225 static duk_ret_t native_formSubmit(duk_context * cx)
1226 {
1227 	jsobjtype thisobj;
1228 	duk_push_this(cx);
1229 	thisobj = duk_get_heapptr(cx, -1);
1230 	duk_pop(cx);
1231 	debugPrint(4, "submit %p", thisobj);
1232 	javaSubmitsForm(thisobj, false);
1233 	return 0;
1234 }
1235 
native_formReset(duk_context * cx)1236 static duk_ret_t native_formReset(duk_context * cx)
1237 {
1238 	jsobjtype thisobj;
1239 	duk_push_this(cx);
1240 	thisobj = duk_get_heapptr(cx, -1);
1241 	duk_pop(cx);
1242 	debugPrint(4, "reset %p", thisobj);
1243 	javaSubmitsForm(thisobj, true);
1244 	return 0;
1245 }
1246 
1247 /*********************************************************************
1248 Maintain a copy of the cookie string that is relevant for this web page.
1249 Include a leading semicolon, looking like
1250 ; foo=73838; bar=j_k_qqr; bas=21998999
1251 The setter folds a new cookie into this string,
1252 and also passes the cookie back to edbrowse to put in the cookie jar.
1253 *********************************************************************/
1254 
1255 static char *cookieCopy;
1256 static int cook_l;
1257 
startCookie(void)1258 static void startCookie(void)
1259 {
1260 	const char *url = cf->fileName;
1261 	bool secure = false;
1262 	const char *proto;
1263 	char *s;
1264 	nzFree(cookieCopy);
1265 	cookieCopy = initString(&cook_l);
1266 	stringAndString(&cookieCopy, &cook_l, "; ");
1267 	if (url) {
1268 		proto = getProtURL(url);
1269 		if (proto && stringEqualCI(proto, "https"))
1270 			secure = true;
1271 		sendCookies(&cookieCopy, &cook_l, url, secure);
1272 		if (memEqualCI(cookieCopy, "; cookie: ", 10)) {	// should often happen
1273 			strmove(cookieCopy + 2, cookieCopy + 10);
1274 			cook_l -= 8;
1275 		}
1276 		if ((s = strstr(cookieCopy, "\r\n"))) {
1277 			*s = 0;
1278 			cook_l -= 2;
1279 		}
1280 	}
1281 }
1282 
native_getcook(duk_context * cx)1283 static duk_ret_t native_getcook(duk_context * cx)
1284 {
1285 	startCookie();
1286 	duk_push_string(cx, cookieCopy + 2);
1287 	return 1;
1288 }
1289 
native_setcook(duk_context * cx)1290 static duk_ret_t native_setcook(duk_context * cx)
1291 {
1292 	const char *newcook = duk_get_string(cx, 0);
1293 	debugPrint(5, "cook 1");
1294 	if (newcook) {
1295 		const char *s = strchr(newcook, '=');
1296 		if(s && s > newcook) {
1297 			duk_get_global_string(cx, "eb$url");
1298 			receiveCookie(duk_get_string(cx, -1), newcook);
1299 			duk_pop(cx);
1300 		}
1301 	}
1302 	debugPrint(5, "cook 2");
1303 	return 0;
1304 }
1305 
native_css_start(duk_context * cx)1306 static duk_ret_t native_css_start(duk_context * cx)
1307 {
1308 	cssDocLoad(duk_get_heapptr(cx, 0), cloneString(duk_get_string(cx, 1)),
1309 		   duk_get_boolean(cx, 2));
1310 	return 0;
1311 }
1312 
1313 // querySelectorAll
native_qsa(duk_context * cx)1314 static duk_ret_t native_qsa(duk_context * cx)
1315 {
1316 	jsobjtype root = 0, ao;
1317 	const char *selstring = duk_get_string(cx, 0);
1318 	int top = duk_get_top(cx);
1319 	if (top > 2) {
1320 		duk_pop_n(cx, top - 2);
1321 		top = 2;
1322 	}
1323 	if (top == 2) {
1324 		if (duk_is_object(cx, 1))
1325 			root = duk_get_heapptr(cx, 1);
1326 	}
1327 	if (!root) {
1328 		duk_push_this(cx);
1329 		root = duk_get_heapptr(cx, -1);
1330 		duk_pop(cx);
1331 	}
1332 	jsInterruptCheck(cx);
1333 	ao = querySelectorAll(selstring, root);
1334 	duk_pop_n(cx, top);
1335 	duk_push_heapptr(cx, ao);
1336 	return 1;
1337 }
1338 
1339 // querySelector
native_qs(duk_context * cx)1340 static duk_ret_t native_qs(duk_context * cx)
1341 {
1342 	jsobjtype root = 0, ao;
1343 	const char *selstring = duk_get_string(cx, 0);
1344 	int top = duk_get_top(cx);
1345 	if (top > 2) {
1346 		duk_pop_n(cx, top - 2);
1347 		top = 2;
1348 	}
1349 	if (top == 2) {
1350 		if (duk_is_object(cx, 1))
1351 			root = duk_get_heapptr(cx, 1);
1352 	}
1353 	if (!root) {
1354 		duk_push_this(cx);
1355 		root = duk_get_heapptr(cx, -1);
1356 		duk_pop(cx);
1357 	}
1358 	jsInterruptCheck(cx);
1359 	ao = querySelector(selstring, root);
1360 	duk_pop_n(cx, top);
1361 	if (ao)
1362 		duk_push_heapptr(cx, ao);
1363 	else
1364 		duk_push_undefined(cx);
1365 	return 1;
1366 }
1367 
1368 // querySelector0
native_qs0(duk_context * cx)1369 static duk_ret_t native_qs0(duk_context * cx)
1370 {
1371 	jsobjtype root;
1372 	bool rc;
1373 	const char *selstring = duk_get_string(cx, 0);
1374 	duk_push_this(cx);
1375 	root = duk_get_heapptr(cx, -1);
1376 	duk_pop(cx);
1377 	jsInterruptCheck(cx);
1378 	rc = querySelector0(selstring, root);
1379 	duk_pop(cx);
1380 	duk_push_boolean(cx, rc);
1381 	return 1;
1382 }
1383 
native_cssApply(duk_context * cx)1384 static duk_ret_t native_cssApply(duk_context * cx)
1385 {
1386 	jsInterruptCheck(cx);
1387 	if (duk_is_object(cx, 1) && duk_is_object(cx, 2))
1388 		cssApply(duk_get_heapptr(cx, 0), duk_get_heapptr(cx, 1),
1389 			 duk_get_heapptr(cx, 2));
1390 	duk_pop_n(cx, 3);
1391 	return 0;
1392 }
1393 
native_cssText(duk_context * cx)1394 static duk_ret_t native_cssText(duk_context * cx)
1395 {
1396 	jsobjtype thisobj;
1397 	const char *rulestring = duk_get_string(cx, 0);
1398 	duk_push_this(cx);
1399 	thisobj = duk_get_heapptr(cx, -1);
1400 	cssText(thisobj, rulestring);
1401 	duk_pop_2(cx);
1402 	return 0;
1403 }
1404 
createJavaContext_0(Frame * f)1405 void createJavaContext_0(Frame *f)
1406 {
1407 	static int seqno;
1408 	duk_context * cx;
1409 
1410 	duk_push_thread_new_globalenv(context0);
1411 	cx = f->cx = duk_get_context(context0, -1);
1412 	if (!cx)
1413 		return;
1414 	debugPrint(3, "create js context %d", duk_get_top(context0) - 1);
1415 // the global object, which will become window,
1416 // and the document object.
1417 	duk_push_global_object(cx);
1418 	f->winobj = duk_get_heapptr(cx, 0);
1419 	duk_push_string(cx, "document");
1420 	duk_push_object(cx);
1421 	f->docobj = duk_get_heapptr(cx, 2);
1422 	duk_def_prop(cx, 0,
1423 		     (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE |
1424 		      DUK_DEFPROP_CLEAR_WRITABLE |
1425 		      DUK_DEFPROP_CLEAR_CONFIGURABLE));
1426 	duk_pop(cx);
1427 
1428 // bind native functions here
1429 	duk_push_c_function(cx, native_new_location, 1);
1430 	duk_put_global_string(cx, "eb$newLocation");
1431 	duk_push_c_function(cx, native_mywin, 0);
1432 	duk_put_global_string(cx, "my$win");
1433 	duk_push_c_function(cx, native_mydoc, 0);
1434 	duk_put_global_string(cx, "my$doc");
1435 	duk_push_c_function(cx, native_puts, 1);
1436 	duk_put_global_string(cx, "eb$puts");
1437 	duk_push_c_function(cx, native_wlf, 2);
1438 	duk_put_global_string(cx, "eb$wlf");
1439 	duk_push_c_function(cx, native_media, 1);
1440 	duk_put_global_string(cx, "eb$media");
1441 	duk_push_c_function(cx, native_btoa, 1);
1442 	duk_put_global_string(cx, "btoa");
1443 	duk_push_c_function(cx, native_atob, 1);
1444 	duk_put_global_string(cx, "atob");
1445 	duk_push_c_function(cx, native_unframe, 2);
1446 	duk_put_global_string(cx, "eb$unframe");
1447 	duk_push_c_function(cx, native_unframe2, 1);
1448 	duk_put_global_string(cx, "eb$unframe2");
1449 	duk_push_c_function(cx, native_logputs, 2);
1450 	duk_put_global_string(cx, "eb$logputs");
1451 	duk_push_c_function(cx, native_prompt, DUK_VARARGS);
1452 	duk_put_global_string(cx, "prompt");
1453 	duk_push_c_function(cx, native_confirm, 1);
1454 	duk_put_global_string(cx, "confirm");
1455 	duk_push_c_function(cx, native_log_element, 2);
1456 	duk_put_global_string(cx, "eb$logElement");
1457 	duk_push_c_function(cx, native_setTimeout, DUK_VARARGS);
1458 	duk_put_global_string(cx, "setTimeout");
1459 	duk_push_c_function(cx, native_setInterval, DUK_VARARGS);
1460 	duk_put_global_string(cx, "setInterval");
1461 	duk_push_c_function(cx, native_clearTimeout, 1);
1462 	duk_put_global_string(cx, "clearTimeout");
1463 	duk_push_c_function(cx, native_clearTimeout, 1);
1464 	duk_put_global_string(cx, "clearInterval");
1465 	duk_push_c_function(cx, native_win_close, 0);
1466 	duk_put_global_string(cx, "close");
1467 	duk_push_c_function(cx, native_fetchHTTP, 4);
1468 	duk_put_global_string(cx, "eb$fetchHTTP");
1469 	duk_push_c_function(cx, native_resolveURL, 2);
1470 	duk_put_global_string(cx, "eb$resolveURL");
1471 	duk_push_c_function(cx, native_formSubmit, 0);
1472 	duk_put_global_string(cx, "eb$formSubmit");
1473 	duk_push_c_function(cx, native_formReset, 0);
1474 	duk_put_global_string(cx, "eb$formReset");
1475 	duk_push_c_function(cx, native_getcook, 0);
1476 	duk_put_global_string(cx, "eb$getcook");
1477 	duk_push_c_function(cx, native_setcook, 1);
1478 	duk_put_global_string(cx, "eb$setcook");
1479 	duk_push_c_function(cx, getter_cd, 0);
1480 	duk_put_global_string(cx, "eb$getter_cd");
1481 	duk_push_c_function(cx, getter_cw, 0);
1482 	duk_put_global_string(cx, "eb$getter_cw");
1483 	duk_push_c_function(cx, native_css_start, 3);
1484 	duk_put_global_string(cx, "eb$cssDocLoad");
1485 	duk_push_c_function(cx, native_qsa, DUK_VARARGS);
1486 	duk_put_global_string(cx, "querySelectorAll");
1487 	duk_push_c_function(cx, native_qs, DUK_VARARGS);
1488 	duk_put_global_string(cx, "querySelector");
1489 	duk_push_c_function(cx, native_qs0, 1);
1490 	duk_put_global_string(cx, "querySelector0");
1491 	duk_push_c_function(cx, native_cssApply, 3);
1492 	duk_put_global_string(cx, "eb$cssApply");
1493 	duk_push_c_function(cx, native_cssText, 1);
1494 	duk_put_global_string(cx, "eb$cssText");
1495 
1496 	duk_push_heapptr(cx, f->docobj);	// native document methods
1497 
1498 	duk_push_c_function(cx, native_hasfocus, 0);
1499 	duk_put_prop_string(cx, -2, "hasFocus");
1500 	duk_push_c_function(cx, native_doc_write, DUK_VARARGS);
1501 	duk_put_prop_string(cx, -2, "write");
1502 	duk_push_c_function(cx, native_doc_writeln, DUK_VARARGS);
1503 	duk_put_prop_string(cx, -2, "writeln");
1504 	duk_push_c_function(cx, native_apch1, 1);
1505 	duk_put_prop_string(cx, -2, "eb$apch1");
1506 	duk_push_c_function(cx, native_apch2, 1);
1507 	duk_put_prop_string(cx, -2, "eb$apch2");
1508 	duk_push_c_function(cx, native_insbf, 2);
1509 	duk_put_prop_string(cx, -2, "eb$insbf");
1510 	duk_push_c_function(cx, native_removeChild, 1);
1511 	duk_put_prop_string(cx, -2, "removeChild");
1512 
1513 // document.ctx$ is the context number
1514 	duk_push_number(cx, ++seqno);
1515 	duk_put_prop_string(cx, -2, "ctx$");
1516 // document.eb$seqno = 0
1517 	duk_push_number(cx, 0);
1518 	duk_put_prop_string(cx, -2, "eb$seqno");
1519 
1520 	duk_pop(cx); // document
1521 
1522 // Link to the master context, i.e. the master window.
1523 // This is denoted mw$ throughout.
1524 // For security reasons, it is only used for third party deminimization
1525 // and other debugging tools.
1526 // It is a huge security risk to share dom classes via this mechanism,
1527 // even though it would be more efficient.
1528 	duk_push_global_object(cx);
1529 	duk_push_string(cx, "mw$");
1530 	duk_push_heapptr(cx, context0_obj);
1531 	duk_def_prop(cx, -3,
1532 		     (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE |
1533 		      DUK_DEFPROP_CLEAR_WRITABLE |
1534 		      DUK_DEFPROP_CLEAR_CONFIGURABLE));
1535 	duk_pop(cx);
1536 
1537 // Sequence is to set cf->fileName, then createContext(), so for a short time,
1538 // we can rely on that variable.
1539 // Let's make it more permanent, per context.
1540 // Has to be nonwritable for security reasons.
1541 	duk_push_global_object(cx);
1542 	duk_push_string(cx, "eb$url");
1543 	duk_push_string(cx, cf->fileName);
1544 	duk_def_prop(cx, -3,
1545 		     (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE |
1546 		      DUK_DEFPROP_CLEAR_WRITABLE |
1547 		      DUK_DEFPROP_CLEAR_CONFIGURABLE));
1548 	duk_pop(cx);
1549 
1550 // setupJavaDom() in ebjs.c does the rest.
1551 }				/* createJavaContext_0 */
1552 
freeJavaContext_0(jsobjtype cx)1553 void freeJavaContext_0(jsobjtype cx)
1554 {
1555 	int i, top = duk_get_top(context0);
1556 	for (i = 0; i < top; ++i) {
1557 		if (cx == duk_get_context(context0, i)) {
1558 			duk_remove(context0, i);
1559 			debugPrint(3, "remove js context %d", i);
1560 			break;
1561 		}
1562 	}
1563 }				/* freeJavaContext_0 */
1564 
1565 // determine the type of the element on the top of the stack.
top_proptype(duk_context * cx)1566 static enum ej_proptype top_proptype(duk_context * cx)
1567 {
1568 	double d;
1569 	int n;
1570 	switch (duk_get_type(cx, -1)) {
1571 	case DUK_TYPE_NUMBER:
1572 		d = duk_get_number(cx, -1);
1573 		n = d;
1574 		return (n == d ? EJ_PROP_INT : EJ_PROP_FLOAT);
1575 	case DUK_TYPE_STRING:
1576 		return EJ_PROP_STRING;
1577 	case DUK_TYPE_BOOLEAN:
1578 		return EJ_PROP_BOOL;
1579 	case DUK_TYPE_OBJECT:
1580 		if (duk_is_function(cx, -1))
1581 			return EJ_PROP_FUNCTION;
1582 		if (duk_is_array(cx, -1))
1583 			return EJ_PROP_ARRAY;
1584 		return EJ_PROP_OBJECT;
1585 	case DUK_TYPE_NULL:
1586 		return EJ_PROP_NULL;
1587 	}
1588 	return EJ_PROP_NONE;	/* don't know */
1589 }				/* top_proptype */
1590 
1591 /*********************************************************************
1592 http://theconversation.com/how-to-build-a-moon-base-120259
1593 creates a script object with getter on src, which throws an error if src
1594 is empty or syntactically invalid.
1595 I check for src in prepareScript(), to see if I need to load the page.
1596 It's just a get call, but the getter throws an error,
1597 and the get call is unprotected, and edbrowse aborts. Ouch!
1598 Here is a simple get property call that is  called through
1599 duk_safe_call() and thus protected.
1600 I hope it doesn't introduce too much overhead, because it is almost never
1601 needed, but neither do I want edbrowse to abort!
1602 *********************************************************************/
1603 
protected_get(duk_context * cx,void * udata)1604 static duk_ret_t protected_get(duk_context * cx, void *udata)
1605 {
1606 	const char *name = udata;
1607 	duk_get_prop_string(cx, -1, name);
1608 	return 1;
1609 }
1610 
typeof_property_0(jsobjtype cx0,jsobjtype parent,const char * name)1611 enum ej_proptype typeof_property_0(jsobjtype cx0, jsobjtype parent, const char *name)
1612 {
1613 	duk_context * cx = cx0;
1614 	enum ej_proptype l;
1615 	int rc;
1616 	duk_push_heapptr(cx, parent);
1617 	rc = duk_safe_call(cx, protected_get, (void *)name, 0, 1);
1618 	l = rc ? 0 : top_proptype(cx);
1619 	duk_pop_2(cx);
1620 	return l;
1621 }
1622 
has_property_0(jsobjtype cx0,jsobjtype parent,const char * name)1623 bool has_property_0(jsobjtype cx0, jsobjtype parent, const char *name)
1624 {
1625 	duk_context * cx = cx0;
1626 	bool l;
1627 	duk_push_heapptr(cx, parent);
1628 	l = duk_has_prop_string(cx, -1, name);
1629 	duk_pop(cx);
1630 	return l;
1631 }
1632 
delete_property_0(jsobjtype cx0,jsobjtype parent,const char * name)1633 void delete_property_0(jsobjtype cx0, jsobjtype parent, const char *name)
1634 {
1635 	duk_context * cx = cx0;
1636 	duk_push_heapptr(cx, parent);
1637 	duk_del_prop_string(cx, -1, name);
1638 	duk_pop(cx);
1639 }				/* delete_property_0 */
1640 
get_arraylength_0(jsobjtype cx0,jsobjtype a)1641 int get_arraylength_0(jsobjtype cx0, jsobjtype a)
1642 {
1643 	duk_context * cx = cx0;
1644 	int l;
1645 	duk_push_heapptr(cx, a);
1646 	if (duk_is_array(cx, -1))
1647 		l = duk_get_length(cx, -1);
1648 	else
1649 		l = -1;
1650 	duk_pop(cx);
1651 	return l;
1652 }				/* get_arraylength_0 */
1653 
1654 /* Return a property as a string, if it is
1655  * string compatible. The string is allocated, free it when done. */
get_property_string_0(jsobjtype cx0,jsobjtype parent,const char * name)1656 char *get_property_string_0(jsobjtype cx0, jsobjtype parent, const char *name)
1657 {
1658 	duk_context * cx = cx0;
1659 	const char *s;
1660 	char *s0;
1661 	enum ej_proptype proptype;
1662 	duk_push_heapptr(cx, parent);
1663 	duk_get_prop_string(cx, -1, name);
1664 	proptype = top_proptype(cx);
1665 	if (proptype == EJ_PROP_NONE) {
1666 		duk_pop_2(cx);
1667 		return NULL;
1668 	}
1669 	if (duk_is_object(cx, -1)) {
1670 /* special code here to return the object pointer */
1671 /* That's what edbrowse is going to want. */
1672 		jsobjtype o = duk_get_heapptr(cx, -1);
1673 		s = pointer2string(o);
1674 	} else
1675 		s = duk_safe_to_string(cx, -1);
1676 	if (!s)
1677 		s = emptyString;
1678 	s0 = cloneString(s);
1679 	duk_pop_2(cx);
1680 	return s0;
1681 }				/* get_property_string_0 */
1682 
get_property_object_0(jsobjtype cx0,jsobjtype parent,const char * name)1683 jsobjtype get_property_object_0(jsobjtype cx0, jsobjtype parent, const char *name)
1684 {
1685 	duk_context * cx = cx0;
1686 	jsobjtype o = NULL;
1687 	duk_push_heapptr(cx, parent);
1688 	duk_get_prop_string(cx, -1, name);
1689 	if (duk_is_object(cx, -1))
1690 		o = duk_get_heapptr(cx, -1);
1691 	duk_pop_2(cx);
1692 	return o;
1693 }				/* get_property_object_0 */
1694 
get_property_function_0(jsobjtype cx0,jsobjtype parent,const char * name)1695 jsobjtype get_property_function_0(jsobjtype cx0, jsobjtype parent, const char *name)
1696 {
1697 	duk_context * cx = cx0;
1698 	jsobjtype o = NULL;
1699 	duk_push_heapptr(cx, parent);
1700 	duk_get_prop_string(cx, -1, name);
1701 	if (duk_is_function(cx, -1))
1702 		o = duk_get_heapptr(cx, -1);
1703 	duk_pop_2(cx);
1704 	return o;
1705 }				/* get_property_function_0 */
1706 
get_property_number_0(jsobjtype cx0,jsobjtype parent,const char * name)1707 int get_property_number_0(jsobjtype cx0, jsobjtype parent, const char *name)
1708 {
1709 	duk_context * cx = cx0;
1710 	int n = -1;
1711 	duk_push_heapptr(cx, parent);
1712 	duk_get_prop_string(cx, -1, name);
1713 	if (duk_is_number(cx, -1)) {
1714 		double d = duk_get_number(cx, -1);
1715 		n = d;		// truncate
1716 	}
1717 	duk_pop_2(cx);
1718 	return n;
1719 }				/* get_property_number_0 */
1720 
get_property_float_0(jsobjtype cx0,jsobjtype parent,const char * name)1721 double get_property_float_0(jsobjtype cx0, jsobjtype parent, const char *name)
1722 {
1723 	duk_context * cx = cx0;
1724 	double d = -1;
1725 	duk_push_heapptr(cx, parent);
1726 	duk_get_prop_string(cx, -1, name);
1727 	if (duk_is_number(cx, -1))
1728 		d = duk_get_number(cx, -1);
1729 	duk_pop_2(cx);
1730 	return d;
1731 }				/* get_property_float_0 */
1732 
get_property_bool_0(jsobjtype cx0,jsobjtype parent,const char * name)1733 bool get_property_bool_0(jsobjtype cx0, jsobjtype parent, const char *name)
1734 {
1735 	duk_context * cx = cx0;
1736 	bool b = false;
1737 	duk_push_heapptr(cx, parent);
1738 	duk_get_prop_string(cx, -1, name);
1739 	if (duk_is_number(cx, -1)) {
1740 		if (duk_get_number(cx, -1))
1741 			b = true;
1742 	}
1743 	if (duk_is_boolean(cx, -1)) {
1744 		if (duk_get_boolean(cx, -1))
1745 			b = true;
1746 	}
1747 	duk_pop_2(cx);
1748 	return b;
1749 }				/* get_property_bool_0 */
1750 
set_property_string_0(jsobjtype cx0,jsobjtype parent,const char * name,const char * value)1751 int set_property_string_0(jsobjtype cx0, jsobjtype parent, const char *name,
1752 			    const char *value)
1753 {
1754 	duk_context * cx = cx0;
1755 	bool defset = false;
1756 	duk_c_function setter = NULL;
1757 	duk_c_function getter = NULL;
1758 	const char *altname;
1759 	duk_push_heapptr(cx, parent);
1760 	if (stringEqual(name, "innerHTML"))
1761 		setter = setter_innerHTML, getter = getter_innerHTML,
1762 		    altname = "inner$HTML";
1763 	if (stringEqual(name, "value")) {
1764 // This one is complicated. If option.value had side effects,
1765 // that would only serve to confuse.
1766 		bool valsetter = true;
1767 		duk_get_global_string(cx, "Option");
1768 		if (duk_instanceof(cx, -2, -1))
1769 			valsetter = false;
1770 		duk_pop(cx);
1771 		duk_get_global_string(cx, "Select");
1772 		if (duk_instanceof(cx, -2, -1)) {
1773 			valsetter = false;
1774 			puts("select.value set! This shouldn't happen.");
1775 		}
1776 		duk_pop(cx);
1777 		if (valsetter)
1778 			setter = setter_value,
1779 			    getter = getter_value, altname = "val$ue";
1780 	}
1781 	if (setter) {
1782 		if (!duk_get_prop_string(cx, -1, name))
1783 			defset = true;
1784 		duk_pop(cx);
1785 	}
1786 	if (defset) {
1787 		duk_push_string(cx, name);
1788 		duk_push_c_function(cx, getter, 0);
1789 		duk_push_c_function(cx, setter, 1);
1790 		duk_def_prop(cx, -4,
1791 			     (DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER
1792 			      | DUK_DEFPROP_SET_ENUMERABLE));
1793 	}
1794 	if (!value)
1795 		value = emptyString;
1796 	duk_push_string(cx, value);
1797 	duk_put_prop_string(cx, -2, (setter ? altname : name));
1798 	duk_pop(cx);
1799 	return 0;
1800 }				/* set_property_string_0 */
1801 
set_property_bool_0(jsobjtype cx0,jsobjtype parent,const char * name,bool n)1802 int set_property_bool_0(jsobjtype cx0, jsobjtype parent, const char *name, bool n)
1803 {
1804 	duk_context * cx = cx0;
1805 	duk_push_heapptr(cx, parent);
1806 	duk_push_boolean(cx, n);
1807 	duk_put_prop_string(cx, -2, name);
1808 	duk_pop(cx);
1809 	return 0;
1810 }				/* set_property_bool_0 */
1811 
set_property_number_0(jsobjtype cx0,jsobjtype parent,const char * name,int n)1812 int set_property_number_0(jsobjtype cx0, jsobjtype parent, const char *name, int n)
1813 {
1814 	duk_context * cx = cx0;
1815 	duk_push_heapptr(cx, parent);
1816 	duk_push_int(cx, n);
1817 	duk_put_prop_string(cx, -2, name);
1818 	duk_pop(cx);
1819 	return 0;
1820 }				/* set_property_number_0 */
1821 
set_property_float_0(jsobjtype cx0,jsobjtype parent,const char * name,double n)1822 int set_property_float_0(jsobjtype cx0, jsobjtype parent, const char *name, double n)
1823 {
1824 	duk_context * cx = cx0;
1825 	duk_push_heapptr(cx, parent);
1826 	duk_push_number(cx, n);
1827 	duk_put_prop_string(cx, -2, name);
1828 	duk_pop(cx);
1829 	return 0;
1830 }				/* set_property_float_0 */
1831 
set_property_object_0(jsobjtype cx0,jsobjtype parent,const char * name,jsobjtype child)1832 int set_property_object_0(jsobjtype cx0, jsobjtype parent, const char *name, jsobjtype child)
1833 {
1834 	duk_context * cx = cx0;
1835 	duk_push_heapptr(cx, parent);
1836 
1837 // Special code for frame.contentDocument
1838 	if (stringEqual(name, "contentDocument")) {
1839 		bool rc;
1840 		duk_get_global_string(cx, "Frame");
1841 		rc = duk_instanceof(cx, -2, -1);
1842 		duk_pop(cx);
1843 		if (rc) {
1844 			duk_push_string(cx, name);
1845 			duk_push_c_function(cx, getter_cd, 0);
1846 			duk_push_c_function(cx, setter_cd, 1);
1847 			duk_def_prop(cx, -4,
1848 				     (DUK_DEFPROP_HAVE_SETTER |
1849 				      DUK_DEFPROP_HAVE_GETTER |
1850 				      DUK_DEFPROP_SET_ENUMERABLE));
1851 			name = "content$Document";
1852 		}
1853 	}
1854 
1855 	if (stringEqual(name, "contentWindow")) {
1856 		bool rc;
1857 		duk_get_global_string(cx, "Frame");
1858 		rc = duk_instanceof(cx, -2, -1);
1859 		duk_pop(cx);
1860 		if (rc) {
1861 			duk_push_string(cx, name);
1862 			duk_push_c_function(cx, getter_cw, 0);
1863 			duk_push_c_function(cx, setter_cw, 1);
1864 			duk_def_prop(cx, -4,
1865 				     (DUK_DEFPROP_HAVE_SETTER |
1866 				      DUK_DEFPROP_HAVE_GETTER |
1867 				      DUK_DEFPROP_SET_ENUMERABLE));
1868 			name = "content$Window";
1869 		}
1870 	}
1871 
1872 	duk_push_heapptr(cx, child);
1873 	duk_put_prop_string(cx, -2, name);
1874 	duk_pop(cx);
1875 	return 0;
1876 }				/* set_property_object_0 */
1877 
1878 // handler.toString = function() { return this.body; }
native_fntos(duk_context * cx)1879 static duk_ret_t native_fntos(duk_context * cx)
1880 {
1881 	duk_push_this(cx);
1882 	duk_get_prop_string(cx, -1, "body");
1883 	duk_remove(cx, -2);
1884 	return 1;
1885 }
1886 
set_property_function_0(jsobjtype cx0,jsobjtype parent,const char * name,const char * body)1887 int set_property_function_0(jsobjtype cx0, jsobjtype parent, const char *name,
1888 			      const char *body)
1889 {
1890 	duk_context * cx = cx0;
1891 	char *body2, *s;
1892 	int l;
1893 	if (!body || !*body) {
1894 // null or empty function, function will return null.
1895 		body = "null";
1896 	}
1897 	duk_push_string(cx, body);
1898 	duk_push_string(cx, name);
1899 	if (duk_pcompile(cx, 0)) {
1900 		processError(cx);
1901 		debugPrint(3, "compile error for %p.%s", parent, name);
1902 		duk_push_c_function(cx, native_error_stub_1, 0);
1903 	}
1904 // At this point I have to undo the mashinations performed by handlerSet().
1905 	s = body2 = cloneString(body);
1906 	l = strlen(s);
1907 	if (l > 16 && stringEqual(s + l - 16, " }.bind(this))()")) {
1908 		s[l - 16] = 0;
1909 		if (!strncmp(s, "(function(){", 12))
1910 			s += 12;
1911 	}
1912 	duk_push_string(cx, s);
1913 	nzFree(body2);
1914 	duk_put_prop_string(cx, -2, "body");
1915 	duk_push_c_function(cx, native_fntos, 0);
1916 	duk_put_prop_string(cx, -2, "toString");
1917 	duk_push_heapptr(cx, parent);
1918 	duk_insert(cx, -2);	// switch places
1919 	duk_put_prop_string(cx, -2, name);
1920 	duk_pop(cx);
1921 	return 0;
1922 }
1923 
1924 /*********************************************************************
1925 Error object is at the top of the duktape stack.
1926 Extract the line number, call stack, and error message,
1927 the latter being error.toString().
1928 Leave the result in errorMessage, which is sent to edbrowse in the 2 process
1929 model, or printed right now if JS1 is set.
1930 Pop the error object when done.
1931 *********************************************************************/
1932 
processError(duk_context * cx)1933 static void processError(duk_context * cx)
1934 {
1935 	const char *callstack = emptyString;
1936 	int offset = 0;
1937 	char *cut, *s;
1938 
1939 	if (duk_get_prop_string(cx, -1, "lineNumber"))
1940 		offset = duk_get_int(cx, -1);
1941 	duk_pop(cx);
1942 
1943 	if (duk_get_prop_string(cx, -1, "stack"))
1944 		callstack = duk_to_string(cx, -1);
1945 	nzFree(errorMessage);
1946 	errorMessage = cloneString(duk_to_string(cx, -2));
1947 	if (strstr(errorMessage, "callstack") && strlen(callstack)) {
1948 // this is rare.
1949 		nzFree(errorMessage);
1950 		errorMessage = cloneString(callstack);
1951 	}
1952 	if (offset) {
1953 		jsLineno += (offset - 1);
1954 // symtax error message includes the relative line number, which is confusing
1955 // since edbrowse prints the absolute line number.
1956 		cut = strstr(errorMessage, " (line ");
1957 		if (cut) {
1958 			s = cut + 7;
1959 			while (isdigit(*s))
1960 				++s;
1961 			if (stringEqual(s, ")"))
1962 				*cut = 0;
1963 		}
1964 	}
1965 	duk_pop(cx);
1966 
1967 	if (debugLevel >= 3) {
1968 /* print message, this will be in English, and mostly for our debugging */
1969 		if (jsSourceFile) {
1970 			if (debugFile)
1971 				fprintf(debugFile, "%s line %d: ",
1972 					jsSourceFile, jsLineno);
1973 			else
1974 				printf("%s line %d: ", jsSourceFile, jsLineno);
1975 		}
1976 		debugPrint(3, "%s", errorMessage);
1977 	}
1978 	free(errorMessage);
1979 	errorMessage = 0;
1980 }
1981 
1982 /*********************************************************************
1983 No arguments; returns abool.
1984 This function is typically used for handlers: onclick, onchange, onsubmit, onload, etc.
1985 The return value is sometimes significant.
1986 If a hyperlink has an onclick function, and said function returns false,
1987 the hyperlink is not followed.
1988 If onsubmit returns false the form does not submit.
1989 And yet this opens a can of worms. Here is my default behavior for corner cases.
1990 I generally want the browser to continue, unless the function
1991 explicitly says false.
1992 Edbrowse should do as much as it can for the casual user.
1993 Javascript function returns boolean. Pass this value back.
1994 Function returns number. nonzero is true and zero is false.
1995 Function returns string. "false" is false and everything else is true.
1996 Function returns a bogus type like object. true
1997 Function returns undefined. true
1998 Function doesn't exist. true, unless debugging.
1999 Function encounters an error during execution. true, unless debugging.
2000 *********************************************************************/
2001 
2002 /*********************************************************************
2003 For debugging; please leave the stack the way you found it.
2004 As you climb up the tree, check for parentNode = null.
2005 null is an object so it passes the object test.
2006 This should never happen, but does in http://4x4dorogi.net
2007 Also check for recursion.
2008 If there is an error fetching nodeName or class, e.g. when the node is null,
2009 (if we didn't check for parentNode = null in the above website),
2010 then asking for nodeName causes yet another runtime error.
2011 This invokes our machinery again, including uptrace if debug is on,
2012 and it invokes the duktape machinery again as well.
2013 The resulting core dump has the stack so corrupted, that gdb is hopelessly confused.
2014 *********************************************************************/
2015 
uptrace(duk_context * cx,jsobjtype node)2016 static void uptrace(duk_context * cx, jsobjtype node)
2017 {
2018 	static bool infunction = false;
2019 	int t;
2020 	if (debugLevel < 3)
2021 		return;
2022 	if(infunction) {
2023 		debugPrint(3, "uptrace recursion; this is unrecoverable!");
2024 		exit(1);
2025 	}
2026 	infunction = true;
2027 	duk_push_heapptr(cx, node);
2028 	while (true) {
2029 		const char *nn, *cn;	// node name class name
2030 		char nnbuf[20];
2031 		if (duk_get_prop_string(cx, -1, "nodeName"))
2032 			nn = duk_to_string(cx, -1);
2033 		else
2034 			nn = "?";
2035 		strncpy(nnbuf, nn, 20);
2036 		nnbuf[20 - 1] = 0;
2037 		if (!nnbuf[0])
2038 			strcpy(nnbuf, "?");
2039 		duk_pop(cx);
2040 		if (duk_get_prop_string(cx, -1, "class"))
2041 			cn = duk_to_string(cx, -1);
2042 		else
2043 			cn = "?";
2044 		debugPrint(3, "%s.%s", nnbuf, (cn[0] ? cn : "?"));
2045 		duk_pop(cx);
2046 		if (!duk_get_prop_string(cx, -1, "parentNode")) {
2047 // we're done.
2048 			duk_pop_2(cx);
2049 			break;
2050 		}
2051 		duk_remove(cx, -2);
2052 		t = top_proptype(cx);
2053 		if(t == EJ_PROP_NULL) {
2054 			debugPrint(3, "null");
2055 			duk_pop(cx);
2056 			break;
2057 		}
2058 		if(t != EJ_PROP_OBJECT) {
2059 			debugPrint(3, "parentNode not object, type %d", t);
2060 			duk_pop(cx);
2061 			break;
2062 		}
2063 	}
2064 	debugPrint(3, "end uptrace");
2065 	infunction = false;
2066 }
2067 
run_function_bool_0(jsobjtype cx0,jsobjtype parent,const char * name)2068 bool run_function_bool_0(jsobjtype cx0, jsobjtype parent, const char *name)
2069 {
2070 	duk_context * cx = cx0;
2071 	int dbl = 3;		// debug level
2072 	int seqno = -1;
2073 	duk_push_heapptr(cx, parent);
2074 	if (stringEqual(name, "ontimer")) {
2075 		dbl = 4;
2076 		if (duk_get_prop_string(cx, -1, "tsn"))
2077 			seqno = duk_get_int(cx, -1);
2078 		duk_pop(cx);
2079 	}
2080 	if (!duk_get_prop_string(cx, -1, name) || !duk_is_function(cx, -1)) {
2081 #if 0
2082 		if (!errorMessage)
2083 			asprintf(&errorMessage, "no such function %s", name);
2084 #endif
2085 		duk_pop_2(cx);
2086 		return (debugLevel < 3);
2087 	}
2088 	duk_insert(cx, -2);
2089 	if (seqno > 0)
2090 		debugPrint(dbl, "exec %s timer %d", name, seqno);
2091 	else
2092 		debugPrint(dbl, "exec %s", name);
2093 	if (!duk_pcall_method(cx, 0)) {
2094 		bool rc = true;
2095 		debugPrint(dbl, "exec complete");
2096 		if (duk_is_boolean(cx, -1))
2097 			rc = duk_get_boolean(cx, -1);
2098 		if (duk_is_number(cx, -1))
2099 			rc = (duk_get_number(cx, -1) != 0);
2100 		if (duk_is_string(cx, -1)) {
2101 			const char *b = duk_get_string(cx, -1);
2102 			if (stringEqualCI(b, "false"))
2103 				rc = false;
2104 		}
2105 		duk_pop(cx);
2106 		return rc;
2107 	}
2108 // error in execution
2109 	if (intFlag)
2110 		i_puts(MSG_Interrupted);
2111 	processError(cx);
2112 	debugPrint(3, "failure on %p.%s()", parent, name);
2113 	uptrace(cx, parent);
2114 	debugPrint(3, "exec complete");
2115 	return (debugLevel < 3);
2116 }				/* run_function_bool_0 */
2117 
2118 // The single argument to the function has to be an object.
2119 // Returns -1 if the return is not int or bool
run_function_onearg_0(jsobjtype cx0,jsobjtype parent,const char * name,jsobjtype child)2120 int run_function_onearg_0(jsobjtype cx0, jsobjtype parent, const char *name, jsobjtype child)
2121 {
2122 	duk_context * cx = cx0;
2123 	int rc = -1;
2124 	duk_push_heapptr(cx, parent);
2125 	if (!duk_get_prop_string(cx, -1, name) || !duk_is_function(cx, -1)) {
2126 #if 0
2127 		if (!errorMessage)
2128 			asprintf(&errorMessage, "no such function %s", name);
2129 #endif
2130 		duk_pop_2(cx);
2131 		return rc;
2132 	}
2133 	duk_insert(cx, -2);
2134 	duk_push_heapptr(cx, child);	// child is the only argument
2135 	if (!duk_pcall_method(cx, 1)) {
2136 // See if return is int or bool
2137 		enum ej_proptype t = top_proptype(cx);
2138 		if (t == EJ_PROP_BOOL)
2139 			rc = duk_get_boolean(cx, -1);
2140 		if (t == EJ_PROP_INT)
2141 			rc = duk_get_number(cx, -1);
2142 		duk_pop(cx);
2143 		return rc;
2144 	}
2145 // error in execution
2146 	if (intFlag)
2147 		i_puts(MSG_Interrupted);
2148 	processError(cx);
2149 	debugPrint(3, "failure on %p.%s[]", parent, name);
2150 	uptrace(cx, parent);
2151 	return rc;
2152 }				/* run_function_onearg_0 */
2153 
2154 // The single argument to the function has to be a string.
run_function_onestring_0(jsobjtype cx0,jsobjtype parent,const char * name,const char * s)2155 void run_function_onestring_0(jsobjtype cx0, jsobjtype parent, const char *name,
2156 				const char *s)
2157 {
2158 	duk_context * cx = cx0;
2159 	duk_push_heapptr(cx, parent);
2160 	if (!duk_get_prop_string(cx, -1, name) || !duk_is_function(cx, -1)) {
2161 #if 0
2162 		if (!errorMessage)
2163 			asprintf(&errorMessage, "no such function %s", name);
2164 #endif
2165 		duk_pop_2(cx);
2166 		return;
2167 	}
2168 	duk_insert(cx, -2);
2169 	duk_push_string(cx, s);	// s is the only argument
2170 	if (!duk_pcall_method(cx, 1)) {
2171 		duk_pop(cx);
2172 		return;
2173 	}
2174 // error in execution
2175 	if (intFlag)
2176 		i_puts(MSG_Interrupted);
2177 	processError(cx);
2178 	debugPrint(3, "failure on %p.%s[]", parent, name);
2179 	uptrace(cx, parent);
2180 }				/* run_function_onestring_0 */
2181 
instantiate_array_0(jsobjtype cx0,jsobjtype parent,const char * name)2182 jsobjtype instantiate_array_0(jsobjtype cx0, jsobjtype parent, const char *name)
2183 {
2184 	duk_context * cx = cx0;
2185 	jsobjtype a;
2186 	duk_push_heapptr(cx, parent);
2187 	if (duk_get_prop_string(cx, -1, name) && duk_is_array(cx, -1)) {
2188 		a = duk_get_heapptr(cx, -1);
2189 		duk_pop_2(cx);
2190 		return a;
2191 	}
2192 	duk_pop(cx);
2193 	duk_get_global_string(cx, "Array");
2194 	if (duk_pnew(cx, 0)) {
2195 		processError(cx);
2196 		debugPrint(3, "failure on %p.%s = []", parent, name);
2197 		uptrace(cx, parent);
2198 		duk_pop(cx);
2199 		return 0;
2200 	}
2201 	a = duk_get_heapptr(cx, -1);
2202 	duk_put_prop_string(cx, -2, name);
2203 	duk_pop(cx);
2204 	return a;
2205 }				/* instantiate_array_0 */
2206 
instantiate_0(jsobjtype cx0,jsobjtype parent,const char * name,const char * classname)2207 jsobjtype instantiate_0(jsobjtype cx0, jsobjtype parent, const char *name,
2208 			  const char *classname)
2209 {
2210 	duk_context * cx = cx0;
2211 	jsobjtype a;
2212 	duk_push_heapptr(cx, parent);
2213 	if (duk_get_prop_string(cx, -1, name) && duk_is_object(cx, -1)) {
2214 // I'll assume the object is of the proper class.
2215 		a = duk_get_heapptr(cx, -1);
2216 		duk_pop_2(cx);
2217 		return a;
2218 	}
2219 	duk_pop(cx);
2220 	if (!classname)
2221 		classname = "Object";
2222 	if (!duk_get_global_string(cx, classname)) {
2223 		fprintf(stderr, "unknown class %s, cannot instantiate\n",
2224 			classname);
2225 		exit(8);
2226 	}
2227 	if (duk_pnew(cx, 0)) {
2228 		processError(cx);
2229 		debugPrint(3, "failure on %p.%s = new %s", parent, name,
2230 			   classname);
2231 		uptrace(cx, parent);
2232 		duk_pop(cx);
2233 		return 0;
2234 	}
2235 	a = duk_get_heapptr(cx, -1);
2236 	duk_put_prop_string(cx, -2, name);
2237 	duk_pop(cx);
2238 	return a;
2239 }				/* instantiate_0 */
2240 
instantiate_array_element_0(jsobjtype cx0,jsobjtype parent,int idx,const char * classname)2241 jsobjtype instantiate_array_element_0(jsobjtype cx0, jsobjtype parent, int idx,
2242 					const char *classname)
2243 {
2244 	duk_context * cx = cx0;
2245 	jsobjtype a;
2246 	if (!classname)
2247 		classname = "Object";
2248 	duk_push_heapptr(cx, parent);
2249 	duk_get_global_string(cx, classname);
2250 	if (duk_pnew(cx, 0)) {
2251 		processError(cx);
2252 		debugPrint(3, "failure on %p[%d] = new %s", parent, idx,
2253 			   classname);
2254 		uptrace(cx, parent);
2255 		duk_pop(cx);
2256 		return 0;
2257 	}
2258 	a = duk_get_heapptr(cx, -1);
2259 	duk_put_prop_index(cx, -2, idx);
2260 	duk_pop(cx);
2261 	return a;
2262 }
2263 
set_array_element_object_0(jsobjtype cx0,jsobjtype parent,int idx,jsobjtype child)2264 int set_array_element_object_0(jsobjtype cx0, jsobjtype parent, int idx, jsobjtype child)
2265 {
2266 	duk_context * cx = cx0;
2267 	duk_push_heapptr(cx, parent);
2268 	duk_push_heapptr(cx, child);
2269 	duk_put_prop_index(cx, -2, idx);
2270 	duk_pop(cx);
2271 	return 0;
2272 }
2273 
get_array_element_object_0(jsobjtype cx0,jsobjtype parent,int idx)2274 jsobjtype get_array_element_object_0(jsobjtype cx0, jsobjtype parent, int idx)
2275 {
2276 	duk_context * cx = cx0;
2277 	jsobjtype a = 0;
2278 	duk_push_heapptr(cx, parent);
2279 	duk_get_prop_index(cx, -1, idx);
2280 	if (duk_is_object(cx, -1))
2281 		a = duk_get_heapptr(cx, -1);
2282 	duk_pop_2(cx);
2283 	return a;
2284 }
2285 
run_script_0(jsobjtype cx0,const char * s)2286 char *run_script_0(jsobjtype cx0, const char *s)
2287 {
2288 	duk_context * cx = cx0;
2289 	char *result = 0;
2290 	bool rc;
2291 	const char *gc;
2292 	char *s2 = 0;
2293 
2294 // special debugging code to replace bp@ and trace@ with expanded macros.
2295 // Warning: breakpoints and tracing can change the flow of execution
2296 // prior to duktape commit 67c891d9e075cc49281304ff5955cae24faa1496
2297 	if (strstr(s, "bp@(") || strstr(s, "trace@(")) {
2298 		int l;
2299 		const char *u, *v1, *v2;
2300 		s2 = initString(&l);
2301 		u = s;
2302 		while (true) {
2303 			v1 = strstr(u, "bp@(");
2304 			v2 = strstr(u, "trace@(");
2305 			if (v1 && v2 && v2 < v1)
2306 				v1 = v2;
2307 			if (!v1)
2308 				v1 = v2;
2309 			if (!v1)
2310 				break;
2311 			stringAndBytes(&s2, &l, u, v1 - u);
2312 			stringAndString(&s2, &l, (*v1 == 'b' ?
2313 						  ";(function(arg$,l$ne){if(l$ne) alert('break at line ' + l$ne); while(true){var res = prompt('bp'); if(!res) continue; if(res === '.') break; try { res = eval(res); alert(res); } catch(e) { alert(e.toString()); }}}).call(this,(typeof arguments=='object'?arguments:[]),\""
2314 						  :
2315 						  ";(function(arg$,l$ne){ if(l$ne === step$go||typeof step$exp==='string'&&eval(step$exp)) step$l = 2; if(step$l == 0) return; if(step$l == 1) { alert3(l$ne); return; } if(l$ne) alert('break at line ' + l$ne); while(true){var res = prompt('bp'); if(!res) continue; if(res === '.') break; try { res = eval(res); alert(res); } catch(e) { alert(e.toString()); }}}).call(this,(typeof arguments=='object'?arguments:[]),\""));
2316 			v1 = strchr(v1, '(') + 1;
2317 			v2 = strchr(v1, ')');
2318 			stringAndBytes(&s2, &l, v1, v2 - v1);
2319 			stringAndString(&s2, &l, "\");");
2320 			u = ++v2;
2321 		}
2322 		stringAndString(&s2, &l, u);
2323 	}
2324 
2325 	rc = duk_peval_string(cx, (s2 ? s2 : s));
2326 	nzFree(s2);
2327 	if (intFlag)
2328 		i_puts(MSG_Interrupted);
2329 	if (!rc) {
2330 		s = duk_safe_to_string(cx, -1);
2331 		if (s && !*s)
2332 			s = 0;
2333 		if (s)
2334 			result = cloneString(s);
2335 		duk_pop(cx);
2336 	} else {
2337 		processError(cx);
2338 	}
2339 	gc = getenv("JSGC");
2340 	if (gc && *gc)
2341 		duk_gc(cx, 0);
2342 	return result;
2343 }
2344 
2345 // execute script.text code; more efficient than the above.
run_data_0(jsobjtype cx0,jsobjtype o)2346 void run_data_0(jsobjtype cx0, jsobjtype o)
2347 {
2348 	duk_context * cx = cx0;
2349 	bool rc;
2350 	const char *s, *gc;
2351 	duk_push_heapptr(cx, o);
2352 	if (!duk_get_prop_string(cx, -1, "text")) {
2353 // no data
2354 		duk_pop_2(cx);
2355 		return;
2356 	}
2357 	s = duk_safe_to_string(cx, -1);
2358 	if (!s || !*s)
2359 		return;
2360 // defer to the earlier routine if there are breakpoints
2361 	if (strstr(s, "bp@(") || strstr(s, "trace@(")) {
2362 		run_script_0(cx, s);
2363 		duk_pop_2(cx);
2364 		return;
2365 	}
2366 	rc = duk_peval_string(cx, s);
2367 	if (intFlag)
2368 		i_puts(MSG_Interrupted);
2369 	if (!rc) {
2370 		duk_pop_n(cx, 3);
2371 	} else {
2372 		processError(cx);
2373 		duk_pop_2(cx);
2374 	}
2375 	gc = getenv("JSGC");
2376 	if (gc && *gc)
2377 		duk_gc(cx, 0);
2378 }
2379 
2380