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