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