1 /* ebjs.c: edbrowse javascript engine interface.
2 *
3 * Launch the js engine process and communicate with it to build
4 * js objects and run js code.
5 * Also provide some wrapper functions like get_property_string,
6 * so that edbrowse can call functions to manipulate js objects,
7 * thus hiding the details of sending messages to the js process
8 * and receiving replies from same. */
9
10 #include "eb.h"
11
12 #include <stdarg.h>
13
14 /* If connection is lost, mark all js sessions as dead. */
markAllDead(void)15 static void markAllDead(void)
16 {
17 int cx; /* edbrowse context */
18 struct ebWindow *w;
19 Frame *f;
20 bool killed = false;
21
22 for (cx = 1; cx <= maxSession; ++cx) {
23 w = sessionList[cx].lw;
24 while (w) {
25 for (f = &w->f0; f; f = f->next) {
26 if (!f->winobj)
27 continue;
28 f->winobj = 0;
29 f->docobj = 0;
30 f->cx = 0;
31 killed = true;
32 }
33 w = w->prev;
34 }
35 }
36
37 if (killed)
38 i_puts(MSG_JSCloseSessions);
39 } /* markAllDead */
40
41 static int js_pid;
42
43 /* Start the js process. */
js_start(void)44 static void js_start(void)
45 {
46 debugPrint(5, "setting of communication channels for javascript");
47 if (js_main()) {
48 i_puts(MSG_JSEngineRun);
49 markAllDead();
50 } else {
51 js_pid = 1;
52 }
53 } /* js_start */
54
55 /* Javascript has changed an input field */
56 static void javaSetsInner(jsobjtype v, const char *newtext);
javaSetsTagVar(jsobjtype v,const char * newtext)57 void javaSetsTagVar(jsobjtype v, const char *newtext)
58 {
59 Tag *t = tagFromJavaVar(v);
60 if (!t)
61 return;
62 if (t->itype == INP_HIDDEN || t->itype == INP_RADIO
63 || t->itype == INP_FILE)
64 return;
65 if (t->itype == INP_TA) {
66 javaSetsInner(v, newtext);
67 return;
68 }
69 nzFree(t->value);
70 t->value = cloneString(newtext);
71 } /* javaSetsTagVar */
72
javaSetsInner(jsobjtype v,const char * newtext)73 static void javaSetsInner(jsobjtype v, const char *newtext)
74 {
75 int side;
76 Tag *t = tagFromJavaVar(v);
77 if (!t)
78 return;
79 /* the tag should always be a textarea tag. */
80 if (t->action != TAGACT_INPUT || t->itype != INP_TA) {
81 debugPrint(3,
82 "innerText is applied to tag %d that is not a textarea.",
83 t->seqno);
84 return;
85 }
86 side = t->lic;
87 if (side <= 0 || side >= MAXSESSION || side == context)
88 return;
89 if (sessionList[side].lw == NULL)
90 return;
91 if (cw->browseMode)
92 i_printf(MSG_BufferUpdated, side);
93 sideBuffer(side, newtext, -1, 0);
94 } /* javaSetsInner */
95
96 /* start a document.write */
dwStart(void)97 void dwStart(void)
98 {
99 if (cf->dw)
100 return;
101 cf->dw = initString(&cf->dw_l);
102 stringAndString(&cf->dw, &cf->dw_l, "<!DOCTYPE public><body>");
103 } /* dwStart */
104
debugString(const char * v)105 static const char *debugString(const char *v)
106 {
107 if (!v)
108 return emptyString;
109 if (strlen(v) > 100)
110 return "long";
111 return v;
112 } /* debugString */
113
114 // Create a js context for the current frame.
115 // The corresponding js context will be stored in cf->cx.
116 // We don't pass frame as a parameter, cf (current frame) is assumed.
createJavaContext(void)117 void createJavaContext(void)
118 {
119 if (!allowJS)
120 return;
121
122 if (!js_pid)
123 js_start();
124
125 debugPrint(5, "> create context for session %d", context);
126 createJavaContext_0(cf);
127 if (cf->cx) {
128 debugPrint(5, "< ok");
129 setupJavaDom();
130 } else {
131 debugPrint(5, "< error");
132 i_puts(MSG_JavaContextError);
133 }
134 } /* createJavaContext */
135
136 /*********************************************************************
137 This is unique among all the wrappered calls in that it can be made for
138 a window that is not the current window.
139 You can free another window, or a whole stack of windows, by typeing
140 q2 while in session 1.
141 *********************************************************************/
142
freeJavaContext(Frame * f)143 void freeJavaContext(Frame *f)
144 {
145 if (!f->cx)
146 return;
147 debugPrint(5, "> free frame %p", f);
148 freeJavaContext_0(f->cx);
149 f->cx = f->winobj = f->docobj = 0;
150 debugPrint(5, "< ok");
151 cssFree(f);
152 } /* freeJavaContext */
153
154 /* Run some javascript code under the current window */
155 /* Pass the return value of the script back as a string. */
jsRunScriptResult(const Frame * f,jsobjtype obj,const char * str,const char * filename,int lineno)156 char *jsRunScriptResult(const Frame *f, jsobjtype obj, const char *str,
157 const char *filename, int lineno)
158 {
159 char *result;
160
161 // this never runs from the j process.
162 if (whichproc != 'e') {
163 debugPrint(1, "jsRunScript run from the js process");
164 return NULL;
165 }
166
167 if (!allowJS || !f->winobj)
168 return NULL;
169 if (!str || !str[0])
170 return NULL;
171
172 debugPrint(5, "> script:");
173 jsSourceFile = filename;
174 jsLineno = lineno;
175 whichproc = 'j';
176 result = run_script_0(f->cx, str);
177 whichproc = 'e';
178 jsSourceFile = NULL;
179 debugPrint(5, "< ok");
180 return result;
181 } /* jsRunScriptResult */
182
183 /* like the above but throw away the result */
jsRunScript(const Frame * f,jsobjtype obj,const char * str,const char * filename,int lineno)184 void jsRunScript(const Frame *f, jsobjtype obj, const char *str,
185 const char *filename, int lineno)
186 {
187 char *s = jsRunScriptResult(f, obj, str, filename, lineno);
188 nzFree(s);
189 } /* jsRunScript */
190
jsRunData(const Frame * f,jsobjtype obj,const char * filename,int lineno)191 void jsRunData(const Frame *f, jsobjtype obj,
192 const char *filename, int lineno)
193 {
194 // this never runs from the j process.
195 if (whichproc != 'e') {
196 debugPrint(1, "jsRunData run from the js process");
197 return;
198 }
199 if (!allowJS || !f->winobj || !obj)
200 return;
201 debugPrint(5, "> script:");
202 jsSourceFile = filename;
203 jsLineno = lineno;
204 whichproc = 'j';
205 run_data_0(f->cx, obj);
206 whichproc = 'e';
207 jsSourceFile = NULL;
208 debugPrint(5, "< ok");
209 }
210
211 /* does the member exist in the object or its prototype? */
has_property(const Frame * f,jsobjtype obj,const char * name)212 bool has_property(const Frame *f, jsobjtype obj, const char *name)
213 {
214 bool p = false;
215 if (!obj) {
216 debugPrint(3, "has_property(0, %s)", name);
217 return false;
218 }
219 if (whichproc == 'j')
220 return has_property_0(f->cx, obj, name);
221 if (!allowJS || !f->winobj)
222 return false;
223 debugPrint(5, "> has %s", name);
224 p = has_property_0(f->cx, obj, name);
225 debugPrint(5, "< %s", (p ? "true" : "false"));
226 return p;
227 } /* has_property */
228
229 /* return the type of the member */
typeof_property(const Frame * f,jsobjtype obj,const char * name)230 enum ej_proptype typeof_property(const Frame *f, jsobjtype obj, const char *name)
231 {
232 enum ej_proptype p;
233 if (!obj) {
234 debugPrint(3, "typeof_property(0, %s)", name);
235 return EJ_PROP_NONE;
236 }
237 if (whichproc == 'j')
238 return typeof_property_0(f->cx, obj, name);
239 if (!allowJS || !f->winobj)
240 return EJ_PROP_NONE;
241 debugPrint(5, "> has %s", name);
242 p = typeof_property_0(f->cx, obj, name);
243 debugPrint(5, "< %d", p);
244 return p;
245 } /* typeof_property */
246
delete_property(const Frame * f,jsobjtype obj,const char * name)247 void delete_property(const Frame *f, jsobjtype obj, const char *name)
248 {
249 if (!obj) {
250 debugPrint(3, "delete_property(0, %s)", name);
251 return;
252 }
253 if (whichproc == 'j') {
254 delete_property_0(f->cx, obj, name);
255 return;
256 }
257 if (!allowJS || !f->winobj)
258 return;
259 debugPrint(5, "> delete %s", name);
260 delete_property_0(f->cx, obj, name);
261 debugPrint(5, "< ok");
262 } /* delete_property */
263
264 // allocated; the caller must free it
get_property_string(const Frame * f,jsobjtype obj,const char * name)265 char *get_property_string(const Frame *f, jsobjtype obj, const char *name)
266 {
267 char *s;
268 if (!allowJS || !f->winobj)
269 return 0;
270 if (!obj) {
271 debugPrint(3, "get_property_string(0, %s)", name);
272 return 0;
273 }
274 if (whichproc == 'j')
275 return get_property_string_0(f->cx, obj, name);
276 debugPrint(5, "> get %s", name);
277 s = get_property_string_0(f->cx, obj, name);
278 debugPrint(5, "< %s", debugString(s));
279 return s;
280 } /* get_property_string */
281
get_property_number(const Frame * f,jsobjtype obj,const char * name)282 int get_property_number(const Frame *f, jsobjtype obj, const char *name)
283 {
284 int n = -1;
285 if (!obj) {
286 debugPrint(3, "get_property_number(0, %s)", name);
287 return -1;
288 }
289 if (whichproc == 'j')
290 return get_property_number_0(f->cx, obj, name);
291 if (!allowJS || !f->winobj)
292 return -1;
293 debugPrint(5, "> get %s", name);
294 n = get_property_number_0(f->cx, obj, name);
295 debugPrint(5, "< %d", n);
296 return n;
297 } /* get_property_number */
298
get_property_float(const Frame * f,jsobjtype obj,const char * name)299 double get_property_float(const Frame *f, jsobjtype obj, const char *name)
300 {
301 double n = 0.0;
302 if (!obj) {
303 debugPrint(3, "get_property_float(0, %s)", name);
304 return n;
305 }
306 if (whichproc == 'j')
307 return get_property_float_0(f->cx, obj, name);
308 if (!allowJS || !f->winobj)
309 return n;
310 debugPrint(5, "> get %s", name);
311 n = get_property_float_0(f->cx, obj, name);
312 debugPrint(5, "< %lf", n);
313 return n;
314 } /* get_property_float */
315
get_property_bool(const Frame * f,jsobjtype obj,const char * name)316 bool get_property_bool(const Frame *f, jsobjtype obj, const char *name)
317 {
318 bool n = false;
319 if (!obj) {
320 debugPrint(3, "get_property_bool(0, %s)", name);
321 return n;
322 }
323 if (whichproc == 'j')
324 return get_property_bool_0(f->cx, obj, name);
325 if (!allowJS || !f->winobj)
326 return n;
327 debugPrint(5, "> get %s", name);
328 n = get_property_bool_0(f->cx, obj, name);
329 debugPrint(5, "< %s", (n ? "treu" : "false"));
330 return n;
331 } /* get_property_bool */
332
333 /* get a js object as a member of another object */
get_property_object(const Frame * f,jsobjtype parent,const char * name)334 jsobjtype get_property_object(const Frame *f, jsobjtype parent, const char *name)
335 {
336 jsobjtype child = 0;
337 if (!parent) {
338 debugPrint(3, "get_property_object(0, %s)", name);
339 return child;
340 }
341 if (whichproc == 'j')
342 return get_property_object_0(f->cx, parent, name);
343 if (!allowJS || !f->winobj)
344 return child;
345 debugPrint(5, "> get %s", name);
346 child = get_property_object_0(f->cx, parent, name);
347 debugPrint(5, "< %p", child);
348 return child;
349 } /* get_property_object */
350
get_property_function(const Frame * f,jsobjtype parent,const char * name)351 jsobjtype get_property_function(const Frame *f, jsobjtype parent, const char *name)
352 {
353 jsobjtype child = 0;
354 if (!parent) {
355 debugPrint(3, "get_property_function(0, %s)", name);
356 return child;
357 }
358 if (whichproc == 'j')
359 return get_property_function_0(f->cx, parent, name);
360 if (!allowJS || !f->winobj)
361 return child;
362 debugPrint(5, "> get %s", name);
363 child = get_property_function_0(f->cx, parent, name);
364 debugPrint(5, "< %p", child);
365 return child;
366 } /* get_property_function */
367
get_array_element_object(const Frame * f,jsobjtype obj,int idx)368 jsobjtype get_array_element_object(const Frame *f, jsobjtype obj, int idx)
369 {
370 jsobjtype p = 0;
371 if (!allowJS || !f->winobj)
372 return p;
373 if (!obj) {
374 debugPrint(3, "get_array_element_object(0, %d)", idx);
375 return p;
376 }
377 if (whichproc == 'j')
378 return get_array_element_object_0(f->cx, obj, idx);
379 debugPrint(5, "> get [%d]", idx);
380 p = get_array_element_object_0(f->cx, obj, idx);
381 debugPrint(5, "< %p", p);
382 return p;
383 } /* get_array_element_object */
384
set_property_string(const Frame * f,jsobjtype obj,const char * name,const char * value)385 int set_property_string(const Frame *f, jsobjtype obj, const char *name, const char *value)
386 {
387 int rc;
388 if (!allowJS || !f->winobj)
389 return -1;
390 if (!obj) {
391 debugPrint(3, "set_property_string(0, %s, %s)", name, value);
392 return -1;
393 }
394 if (whichproc == 'j')
395 return set_property_string_0(f->cx, obj, name, value);
396 if (value == NULL)
397 value = emptyString;
398 debugPrint(5, "> set %s=%s", name, debugString(value));
399 rc = set_property_string_0(f->cx, obj, name, value);
400 debugPrint(5, "< ok");
401 return rc;
402 } /* set_property_string */
403
set_property_number(const Frame * f,jsobjtype obj,const char * name,int n)404 int set_property_number(const Frame *f, jsobjtype obj, const char *name, int n)
405 {
406 int rc;
407 if (!allowJS || !f->winobj)
408 return -1;
409 if (!obj) {
410 debugPrint(3, "set_property_number(0, %s, %d)", name, n);
411 return -1;
412 }
413 if (whichproc == 'j')
414 return set_property_number_0(f->cx, obj, name, n);
415 debugPrint(5, "> set %s=%d", name, n);
416 rc = set_property_number_0(f->cx, obj, name, n);
417 debugPrint(5, "< ok");
418 return rc;
419 } /* set_property_number */
420
set_property_float(const Frame * f,jsobjtype obj,const char * name,double n)421 int set_property_float(const Frame *f, jsobjtype obj, const char *name, double n)
422 {
423 int rc;
424 if (!allowJS || !f->winobj)
425 return -1;
426 if (!obj) {
427 debugPrint(3, "set_property(0, %s, %lf)", name, n);
428 return -1;
429 }
430 if (whichproc == 'j')
431 return set_property_float_0(f->cx, obj, name, n);
432 debugPrint(5, "> set %s=%lf", name, n);
433 rc = set_property_float_0(f->cx, obj, name, n);
434 debugPrint(5, "< ok");
435 return rc;
436 } /* set_property_float */
437
set_property_bool(const Frame * f,jsobjtype obj,const char * name,bool n)438 int set_property_bool(const Frame *f, jsobjtype obj, const char *name, bool n)
439 {
440 int rc;
441 if (!allowJS || !f->winobj)
442 return -1;
443 if (!obj) {
444 debugPrint(3, "set_property(0, %s, %d)", name, n);
445 return -1;
446 }
447 if (whichproc == 'j')
448 return set_property_bool_0(f->cx, obj, name, n);
449 debugPrint(5, "> set %s=%s", name, (n ? "true" : "false"));
450 rc = set_property_bool_0(f->cx, obj, name, n);
451 debugPrint(5, "< ok");
452 return rc;
453 } /* set_property_bool */
454
set_property_object(const Frame * f,jsobjtype parent,const char * name,jsobjtype child)455 int set_property_object(const Frame *f, jsobjtype parent, const char *name, jsobjtype child)
456 {
457 int rc;
458 if (!allowJS || !f->winobj)
459 return -1;
460 if (!parent) {
461 debugPrint(3, "set_property_object(0, %s, %p)", name, child);
462 return -1;
463 }
464 if (whichproc == 'j')
465 return set_property_object_0(f->cx, parent, name, child);
466 debugPrint(5, "> set %s=%p", name, child);
467 rc = set_property_object_0(f->cx, parent, name, child);
468 debugPrint(5, "< ok");
469 return rc;
470 } /* set_property_object */
471
instantiate_array(const Frame * f,jsobjtype parent,const char * name)472 jsobjtype instantiate_array(const Frame *f, jsobjtype parent, const char *name)
473 {
474 jsobjtype p = 0;
475 if (!allowJS || !f->winobj)
476 return p;
477 if (!parent) {
478 debugPrint(3, "instantiate_array(0, %s)", name);
479 return p;
480 }
481 if (whichproc == 'j')
482 return instantiate_array_0(f->cx, parent, name);
483 debugPrint(5, "> new array %s", name);
484 p = instantiate_array_0(f->cx, parent, name);
485 debugPrint(5, "< ok");
486 return p;
487 } /* instantiate_array */
488
set_array_element_object(const Frame * f,jsobjtype array,int idx,jsobjtype child)489 int set_array_element_object(const Frame *f, jsobjtype array, int idx, jsobjtype child)
490 {
491 int rc;
492 if (!allowJS || !f->winobj)
493 return -1;
494 if (!array) {
495 debugPrint(3, "set_array_element_object(0, %d)", idx);
496 return -1;
497 }
498 if (whichproc == 'j')
499 return set_array_element_object_0(f->cx, array, idx, child);
500 debugPrint(5, "> set [%d]=%p", idx, child);
501 rc = set_array_element_object_0(f->cx, array, idx, child);
502 debugPrint(5, "< ok");
503 return rc;
504 } /* set_array_element_object */
505
instantiate_array_element(const Frame * f,jsobjtype array,int idx,const char * classname)506 jsobjtype instantiate_array_element(const Frame *f, jsobjtype array, int idx,
507 const char *classname)
508 {
509 jsobjtype p = 0;
510 if (!allowJS || !f->winobj)
511 return p;
512 if (!array) {
513 debugPrint(3, "instantiate_array_element(0, %d, %s)", idx,
514 classname);
515 return p;
516 }
517 if (whichproc == 'j')
518 return instantiate_array_element_0(f->cx, array, idx, classname);
519 debugPrint(5, "> set [%d]=%s", idx, classname);
520 p = instantiate_array_element_0(f->cx, array, idx, classname);
521 debugPrint(5, "< ok");
522 return p;
523 } /* instantiate_array_element */
524
525 /* Instantiate a new object from a given class.
526 * Return is NULL if there is a js disaster.
527 * Set classname = NULL for a generic object. */
instantiate(const Frame * f,jsobjtype parent,const char * name,const char * classname)528 jsobjtype instantiate(const Frame *f, jsobjtype parent, const char *name, const char *classname)
529 {
530 jsobjtype p = 0;
531 if (!allowJS || !f->winobj)
532 return p;
533 if (!parent) {
534 debugPrint(3, "instantiate(0, %s, %s)", name, classname);
535 return p;
536 }
537 if (whichproc == 'j')
538 return instantiate_0(f->cx, parent, name, classname);
539 debugPrint(5, "> instantiate %s %s", name,
540 (classname ? classname : "object"));
541 p = instantiate_0(f->cx, parent, name, classname);
542 debugPrint(5, "< ok");
543 return p;
544 } /* instantiate */
545
set_property_function(const Frame * f,jsobjtype parent,const char * name,const char * body)546 int set_property_function(const Frame *f, jsobjtype parent, const char *name, const char *body)
547 {
548 int rc;
549 if (!allowJS || !f->winobj)
550 return -1;
551 if (!parent) {
552 debugPrint(3, "set_property_function(0, %s)", name);
553 return -1;
554 }
555 if (whichproc == 'j')
556 return set_property_function_0(f->cx, parent, name, body);
557 if (!body)
558 body = emptyString;
559 debugPrint(5, "> set %s=%s", name, debugString(body));
560 rc = set_property_function_0(f->cx, parent, name, body);
561 debugPrint(5, "< ok");
562 return rc;
563 } /* set_property_function */
564
get_arraylength(const Frame * f,jsobjtype a)565 int get_arraylength(const Frame *f, jsobjtype a)
566 {
567 int l;
568 if (!allowJS || !f->winobj)
569 return -1;
570 if (!a) {
571 debugPrint(3, "get_arraylength(0)");
572 return -1;
573 }
574 if (whichproc == 'j')
575 return get_arraylength_0(f->cx, a);
576 debugPrint(5, "> get length");
577 l = get_arraylength_0(f->cx, a);
578 debugPrint(5, "< ok");
579 return l;
580 } /* get_arraylength */
581
582 /* run a function with no args that returns a boolean */
run_function_bool(const Frame * f,jsobjtype obj,const char * name)583 bool run_function_bool(const Frame *f, jsobjtype obj, const char *name)
584 {
585 bool rc;
586 if (!allowJS || !f->winobj)
587 return false;
588 if (!obj) {
589 debugPrint(3, "run_function_bool(0, %s", name);
590 return false;
591 }
592 if (intFlag)
593 return false;
594 if (whichproc == 'j')
595 return run_function_bool_0(f->cx, obj, name);
596 debugPrint(5, "> function %s", name);
597 whichproc = 'j';
598 rc = run_function_bool_0(f->cx, obj, name);
599 whichproc = 'e';
600 debugPrint(5, "< %s", (rc ? "true" : "false"));
601 return rc;
602 } /* run_function_bool */
603
create_event(const Frame * f,jsobjtype parent,const char * evname)604 jsobjtype create_event(const Frame *f, jsobjtype parent, const char *evname)
605 {
606 jsobjtype e;
607 const char *evname1 = evname;
608 if (evname[0] == 'o' && evname[1] == 'n')
609 evname1 += 2;
610 // gc$event protects from garbage collection
611 e = instantiate(f, parent, "gc$event", "Event");
612 set_property_string(f, e, "type", evname1);
613 return e;
614 }
615
unlink_event(const Frame * f,jsobjtype parent)616 void unlink_event(const Frame *f, jsobjtype parent)
617 {
618 delete_property(f, parent, "gc$event");
619 }
620
run_event_bool(const Frame * f,jsobjtype obj,const char * pname,const char * evname)621 bool run_event_bool(const Frame *f, jsobjtype obj, const char *pname, const char *evname)
622 {
623 int rc;
624 jsobjtype eo; // created event object
625 if (!handlerPresent(f, obj, evname))
626 return true;
627 if (debugLevel >= 3) {
628 bool evdebug = get_property_bool(f, f->winobj, "eventDebug");
629 if (evdebug) {
630 int seqno = get_property_number(f, obj, "eb$seqno");
631 debugPrint(3, "trigger %s tag %d %s", pname, seqno, evname);
632 }
633 }
634 eo = create_event(f, obj, evname);
635 set_property_object(f, eo, "target", obj);
636 set_property_object(f, eo, "currentTarget", obj);
637 set_property_number(f, eo, "eventPhase", 2);
638 rc = run_function_onearg(f, obj, evname, eo);
639 unlink_event(f, obj);
640 // no return or some other return is treated as true in this case
641 if (rc < 0)
642 rc = true;
643 return rc;
644 }
645
run_function_onearg(const Frame * f,jsobjtype obj,const char * name,jsobjtype a)646 int run_function_onearg(const Frame *f, jsobjtype obj, const char *name, jsobjtype a)
647 {
648 int rc;
649 if (!allowJS || !f->winobj)
650 return 0;
651 if (!obj) {
652 debugPrint(3, "run_function_onearg(0, %s", name);
653 return 0;
654 }
655 if (whichproc == 'j')
656 return run_function_onearg_0(f->cx, obj, name, a);
657 debugPrint(5, "> function %s", name);
658 whichproc = 'j';
659 rc = run_function_onearg_0(f->cx, obj, name, a);
660 whichproc = 'e';
661 debugPrint(5, "< ok");
662 return rc;
663 }
664
run_function_onestring(const Frame * f,jsobjtype obj,const char * name,const char * s)665 void run_function_onestring(const Frame *f, jsobjtype obj, const char *name, const char *s)
666 {
667 if (!allowJS || !f->winobj)
668 return;
669 if (!obj) {
670 debugPrint(3, "run_function_onestring(0, %s", name);
671 return;
672 }
673 if (whichproc == 'j') {
674 run_function_onestring_0(f->cx, obj, name, s);
675 return;
676 }
677 debugPrint(5, "> function %s", name);
678 whichproc = 'j';
679 run_function_onestring_0(f->cx, obj, name, s);
680 whichproc = 'e';
681 debugPrint(5, "< ok");
682 }
683
684 /*********************************************************************
685 Everything beyond this point is, perhaps, part of a DOM support layer
686 above what has come before.
687 Still, these are library-like routines that are used repeatedly
688 by other files, particularly html.c and decorate.c.
689 *********************************************************************/
690
691 /* pass, to the js process, the filename,
692 * or the <base href=url>, for relative url resolution on innerHTML.
693 * This has to be retained per edbrowse buffer. */
set_basehref(const char * h)694 void set_basehref(const char *h)
695 {
696 if (!h)
697 h = emptyString;
698 set_property_string(cf, cf->winobj, "eb$base", h);
699 // This is special code for snapshot simulations.
700 // If the file jslocal is present, push base over to window.location etc,
701 // as though you were running that page.
702 if (!access("jslocal", 4) && h[0]) {
703 run_function_bool(cf, cf->winobj, "eb$base$snapshot");
704 nzFree(cf->fileName);
705 cf->fileName = cloneString(h);
706 }
707 } /* set_basehref */
708
709 #ifdef DOSLIKE // port of uname(p), and struct utsname
710 struct utsname {
711 char sysname[32];
712 char machine[32];
713 };
714
uname(struct utsname * pun)715 int uname(struct utsname *pun)
716 {
717 memset(pun, 0, sizeof(struct utsname));
718 // TODO: WIN32: maybe fill in sysname, and machine...
719 return 0;
720 }
721
722 #else // !DOSLIKE - // port of uname(p), and struct utsname
723 #include <sys/utsname.h>
724 #endif // DOSLIKE y/n // port of uname(p), and struct utsname
725
726 /* After createJavaContext, set up the document object and other variables
727 * and methods that are base for client side DOM. */
setupJavaDom(void)728 void setupJavaDom(void)
729 {
730 jsobjtype w = cf->winobj; // window object
731 jsobjtype d = cf->docobj; // document object
732 jsobjtype cx = cf->cx; // current context
733 jsobjtype nav; // navigator object
734 jsobjtype navpi; // navigator plugins
735 jsobjtype navmt; // navigator mime types
736 jsobjtype hist; // history object
737 struct MIMETYPE *mt;
738 struct utsname ubuf;
739 int i;
740 char save_c;
741 static const char *const languages[] = { 0,
742 "english", "french", "portuguese", "polish",
743 "german", "russian", "italian",
744 };
745 extern const char startWindowJS[];
746 extern const char thirdJS[];
747
748 set_property_object_0(cx, w, "window", w);
749
750 /* the js window/document setup script.
751 * These are all the things that do not depend on the platform,
752 * OS, configurations, etc. */
753 jsRunScript(cf, w, startWindowJS, "StartWindow", 1);
754 jsRunScript(cf, w, thirdJS, "Third", 1);
755
756 nav = get_property_object_0(cx, w, "navigator");
757 if (nav == NULL)
758 return;
759 /* some of the navigator is in startwindow.js; the runtime properties are here. */
760 set_property_string_0(cx, nav, "userLanguage", languages[eb_lang]);
761 set_property_string_0(cx, nav, "language", languages[eb_lang]);
762 set_property_string_0(cx, nav, "appVersion", version);
763 set_property_string_0(cx, nav, "vendorSub", version);
764 set_property_string_0(cx, nav, "userAgent", currentAgent);
765 uname(&ubuf);
766 set_property_string_0(cx, nav, "oscpu", ubuf.sysname);
767 set_property_string_0(cx, nav, "platform", ubuf.machine);
768
769 /* Build the array of mime types and plugins,
770 * according to the entries in the config file. */
771 navpi = get_property_object_0(cx, nav, "plugins");
772 navmt = get_property_object_0(cx, nav, "mimeTypes");
773 if (navpi == NULL || navmt == NULL)
774 return;
775 mt = mimetypes;
776 for (i = 0; i < maxMime; ++i, ++mt) {
777 int len;
778 /* po is the plugin object and mo is the mime object */
779 jsobjtype po = instantiate_array_element_0(cx, navpi, i, 0);
780 jsobjtype mo = instantiate_array_element_0(cx, navmt, i, 0);
781 if (po == NULL || mo == NULL)
782 return;
783 set_property_object_0(cx, mo, "enabledPlugin", po);
784 set_property_string_0(cx, mo, "type", mt->type);
785 set_property_object_0(cx, navmt, mt->type, mo);
786 set_property_string_0(cx, mo, "description", mt->desc);
787 set_property_string_0(cx, mo, "suffixes", mt->suffix);
788 /* I don't really have enough information from the config file to fill
789 * in the attributes of the plugin object.
790 * I'm just going to fake it.
791 * Description will be the same as that of the mime type,
792 * and the filename will be the program to run.
793 * No idea if this is right or not. */
794 set_property_string_0(cx, po, "description", mt->desc);
795 set_property_string_0(cx, po, "filename", mt->program);
796 /* For the name, how about the program without its options? */
797 len = strcspn(mt->program, " \t");
798 save_c = mt->program[len];
799 mt->program[len] = 0;
800 set_property_string_0(cx, po, "name", mt->program);
801 mt->program[len] = save_c;
802 }
803
804 hist = get_property_object_0(cx, w, "history");
805 if (hist == NULL)
806 return;
807 set_property_string_0(cx, hist, "current", cf->fileName);
808
809 set_property_string_0(cx, d, "referrer", cw->referrer);
810 set_property_string_0(cx, d, "URL", cf->fileName);
811 set_property_string_0(cx, d, "location", cf->fileName);
812 set_property_string_0(cx, w, "location", cf->fileName);
813 jsRunScript(cf, w,
814 "window.location.replace = document.location.replace = function(s) { this.href = s; };Object.defineProperty(window.location,'replace',{enumerable:false});Object.defineProperty(document.location,'replace',{enumerable:false});",
815 "locreplace", 1);
816 set_property_string_0(cx, d, "domain", getHostURL(cf->fileName));
817 if (debugClone)
818 set_property_bool_0(cx, w, "cloneDebug", true);
819 if (debugEvent)
820 set_property_bool_0(cx, w, "eventDebug", true);
821 if (debugThrow)
822 set_property_bool_0(cx, w, "throwDebug", true);
823 } /* setupJavaDom */
824
825 /* Get the url from a url object, special wrapper.
826 * Owner object is passed, look for obj.href, obj.src, or obj.action.
827 * Return that if it's a string, or its member href if it is a url.
828 * The result, coming from get_property_string, is allocated. */
get_property_url(const Frame * f,jsobjtype owner,bool action)829 char *get_property_url(const Frame *f, jsobjtype owner, bool action)
830 {
831 enum ej_proptype mtype; /* member type */
832 jsobjtype uo = 0; /* url object */
833 if (action) {
834 mtype = typeof_property(f, owner, "action");
835 if (mtype == EJ_PROP_STRING)
836 return get_property_string(f, owner, "action");
837 if (mtype != EJ_PROP_OBJECT)
838 return 0;
839 uo = get_property_object(f, owner, "action");
840 } else {
841 mtype = typeof_property(f, owner, "href");
842 if (mtype == EJ_PROP_STRING)
843 return get_property_string(f, owner, "href");
844 if (mtype == EJ_PROP_OBJECT)
845 uo = get_property_object(f, owner, "href");
846 else if (mtype)
847 return 0;
848 if (!uo) {
849 mtype = typeof_property(f, owner, "src");
850 if (mtype == EJ_PROP_STRING)
851 return get_property_string(f, owner, "src");
852 if (mtype == EJ_PROP_OBJECT)
853 uo = get_property_object(f, owner, "src");
854 }
855 }
856
857 if (uo == NULL)
858 return 0;
859 /* should this be href$val? */
860 return get_property_string(f, uo, "href");
861 } /* get_property_url */
862
863 /*********************************************************************
864 See if a tag object is still rooted on the js side.
865 If not, it could be garbage collected away. We could be accessing a bad pointer.
866 Worse, it could be reallocated to a new object,
867 so we're not even accessing the object we think we are.
868 Imagine a timer fires and js rearranges the entire tree, but we haven't
869 rerendered yet. You type g on a link.
870 That line isn't even there, the tag is obsolete, its pointer is obsolete.
871 Check for that here in the only way I think is safe, from the top.
872 However, if objects are rooted in some way,
873 so that they can't go away without being released,
874 then there is an easier way. Start with the given node and climb up
875 through parentNode, looking for a global object.
876 *********************************************************************/
877
tagIsRooted(Tag * t)878 bool tagIsRooted(Tag *t)
879 {
880 Tag *u, *v = 0, *w;
881
882 for(u = t; u; v = u, u = u->parent) {
883 u->lic = -1;
884 if(!v)
885 continue;
886 for(w = u->firstchild; w; w = w->sibling) {
887 ++u->lic;
888 if(w == v)
889 break;
890 }
891 if(!w) // this should never happen!
892 goto fail;
893 // lic is the count of the child in the chain
894 }
895 u = v;
896
897 /*********************************************************************
898 We're at the top. Should be html.
899 There's no other <html> tag, even if the page has subframes,
900 so this should be a rock solid test.
901 *********************************************************************/
902
903 if(u->action != TAGACT_HTML)
904 goto fail;
905
906 // Now climb down the chain from u to t.
907 // I don't know why we would ever click on or even examine a tag under <head>,
908 // but I guess I'll allow for the possibility.
909 if(u->lic == 0) // head
910 u = u->firstchild;
911 else if(u->lic == 1) // body
912 u = u->firstchild->sibling;
913 else // should never happen
914 goto fail;
915
916 while(true) {
917 int i, len;
918 jsobjtype cn; // child nodes
919 // Imagine removing an object from the tree, allocating a new one, and by sheer
920 // bad luck, the new object gets the same pointer. Then put it back in the
921 // same place in the tree. I've seen it happen.
922 // Use our sseqno to defend against this.
923 if(get_property_number_0(u->f0->cx, u->jv, "eb$seqno") != u->seqno)
924 goto fail;
925 if(u == t)
926 break;
927 i = 0;
928 v = u->firstchild;
929 while(++i <= u->lic)
930 v = v->sibling;
931 if(!v->jv)
932 goto fail;
933 // find v->jv in the children of u.
934 if(!(cn = get_property_object_0(u->f0->cx, u->jv, "childNodes")))
935 goto fail;
936 len = get_arraylength_0(u->f0->cx, cn);
937 for(i = 0; i < len; ++i)
938 if(get_array_element_object_0(u->f0->cx, cn, i) == v->jv) // found it
939 break;
940 if(i == len)
941 goto fail; // not found
942 u = v;
943 }
944
945 debugPrint(4, "%s %d is rooted", t->info->name, t->seqno);
946 return true; // properly rooted
947
948 fail:
949 debugPrint(3, "%s %d is not rooted", t->info->name, t->seqno);
950 return false;
951 }
952
953 /*********************************************************************
954 Javascript sometimes builds or rebuilds a submenu, based upon your selection
955 in a primary menu. These new options must map back to html tags,
956 and then to the dropdown list as you interact with the form.
957 This is tested in jsrt - select a state,
958 whereupon the colors below, that you have to choose from, can change.
959 This does not easily fold into rerender(),
960 since the line <> in the buffer looks exactly the same,
961 so this tells you the options underneath have changed.
962 *********************************************************************/
963
rebuildSelector(Tag * sel,jsobjtype oa,int len2)964 static void rebuildSelector(Tag *sel, jsobjtype oa, int len2)
965 {
966 int i2 = 0;
967 bool check2;
968 char *s;
969 const char *selname;
970 bool changed = false;
971 Tag *t, *t0 = 0;
972 jsobjtype oo; /* option object */
973 jsobjtype cx = sel->f0->cx;
974
975 selname = sel->name;
976 if (!selname)
977 selname = "?";
978 debugPrint(4, "testing selector %s %d", selname, len2);
979 sel->lic = (sel->multiple ? 0 : -1);
980 t = cw->optlist;
981
982 while (t && i2 < len2) {
983 t0 = t;
984 /* there is more to both lists */
985 if (t->controller != sel) {
986 t = t->same;
987 continue;
988 }
989
990 /* find the corresponding option object */
991 if ((oo = get_array_element_object_0(cx, oa, i2)) == NULL) {
992 /* Wow this shouldn't happen. */
993 /* Guess I'll just pretend the array stops here. */
994 len2 = i2;
995 break;
996 }
997
998 if (t->jv != oo) {
999 debugPrint(5, "oo switch");
1000 /*********************************************************************
1001 Ok, we freed up the old options, and garbage collection
1002 could well kill the tags that went with these options,
1003 i.e. the tags we're looking at now.
1004 I'm bringing the tags back to life.
1005 *********************************************************************/
1006 t->dead = false;
1007 disconnectTagObject(t);
1008 connectTagObject(t, oo);
1009 }
1010
1011 t->rchecked = get_property_bool_0(cx, oo, "defaultSelected");
1012 check2 = get_property_bool_0(cx, oo, "selected");
1013 if (check2) {
1014 if (sel->multiple)
1015 ++sel->lic;
1016 else
1017 sel->lic = i2;
1018 }
1019 ++i2;
1020 if (t->checked != check2)
1021 changed = true;
1022 t->checked = check2;
1023 s = get_property_string_0(cx, oo, "text");
1024 if ((s && !t->textval) || !stringEqual(t->textval, s)) {
1025 nzFree(t->textval);
1026 t->textval = s;
1027 changed = true;
1028 } else
1029 nzFree(s);
1030 s = get_property_string_0(cx, oo, "value");
1031 if ((s && !t->value) || !stringEqual(t->value, s)) {
1032 nzFree(t->value);
1033 t->value = s;
1034 } else
1035 nzFree(s);
1036 t = t->same;
1037 }
1038
1039 /* one list or the other or both has run to the end */
1040 if (i2 == len2) {
1041 for (; t; t = t->same) {
1042 if (t->controller != sel) {
1043 t0 = t;
1044 continue;
1045 }
1046 /* option is gone in js, disconnect this option tag from its select */
1047 disconnectTagObject(t);
1048 t->controller = 0;
1049 t->action = TAGACT_NOP;
1050 if (t0)
1051 t0->same = t->same;
1052 else
1053 cw->optlist = t->same;
1054 changed = true;
1055 }
1056 } else if (!t) {
1057 for (; i2 < len2; ++i2) {
1058 if ((oo = get_array_element_object_0(cx, oa, i2)) == NULL)
1059 break;
1060 t = newTag(sel->f0, "option");
1061 t->lic = i2;
1062 t->controller = sel;
1063 connectTagObject(t, oo);
1064 t->step = 2; // already decorated
1065 t->textval = get_property_string_0(cx, oo, "text");
1066 t->value = get_property_string_0(cx, oo, "value");
1067 t->checked = get_property_bool_0(cx, oo, "selected");
1068 if (t->checked) {
1069 if (sel->multiple)
1070 ++sel->lic;
1071 else
1072 sel->lic = i2;
1073 }
1074 t->rchecked = get_property_bool_0(cx, oo, "defaultSelected");
1075 changed = true;
1076 }
1077 }
1078
1079 if (!changed)
1080 return;
1081 debugPrint(4, "selector %s has changed", selname);
1082
1083 s = displayOptions(sel);
1084 if (!s)
1085 s = emptyString;
1086 javaSetsTagVar(sel->jv, s);
1087 nzFree(s);
1088
1089 if (!sel->multiple)
1090 set_property_number_0(cx, sel->jv, "selectedIndex", sel->lic);
1091 } /* rebuildSelector */
1092
rebuildSelectors(void)1093 void rebuildSelectors(void)
1094 {
1095 int i1;
1096 Tag *t;
1097 jsobjtype oa; /* option array */
1098 int len; /* length of option array */
1099
1100 if (!isJSAlive)
1101 return;
1102
1103 for (i1 = 0; i1 < cw->numTags; ++i1) {
1104 t = tagList[i1];
1105 if (!t->jv)
1106 continue;
1107 if (t->action != TAGACT_INPUT)
1108 continue;
1109 if (t->itype != INP_SELECT)
1110 continue;
1111 if(!tagIsRooted(t))
1112 continue;
1113
1114 /* there should always be an options array, if not then move on */
1115 if ((oa = get_property_object_0(t->f0->cx, t->jv, "options")) == NULL)
1116 continue;
1117 if ((len = get_arraylength_0(t->f0->cx, oa)) < 0)
1118 continue;
1119 rebuildSelector(t, oa, len);
1120 }
1121
1122 } /* rebuildSelectors */
1123