2
* Javascript support, the URL class and the location object.
3
* The cookie string, and other things with get/set side effects.
4
* Copyright (c) Karl Dahlke, 2008
5
* This file is part of the edbrowse project, released under GPL.
11
/* jsprf.h is not publically visible on some systems,
12
so I can't #include it here.
13
Instead, I'll declare the needed prototype myself, and hope it is consistent
14
with whatever smjs you are using. */
17
JS_smprintf(const char *fmt, ...);
19
#define PROP_FIXED (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
22
/*********************************************************************
23
The URL class, and the associated window.location object, is tricky,
24
with lots of interacting properties and setter functions.
25
*********************************************************************/
27
static const char *emptyParms[] = { 0 };
28
static jsval emptyArgs[] = { 0 };
31
url_initialize(const char *url, bool readonly, bool exclude_href);
39
if(JSVAL_IS_STRING(v)) {
40
return JS_GetStringBytes(JSVAL_TO_STRING(v));
44
sprintf(buf, "%d", n);
47
if(JSVAL_IS_DOUBLE(v)) {
48
d = *JSVAL_TO_DOUBLE(v);
51
sprintf(buf, "%d", n);
53
sprintf(buf, "%lf", d);
56
/* Sorry, I don't look for object.toString() */
57
return 0; /* failed */
60
static JSClass url_class = {
63
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
64
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
67
/* To builld path names, host names, etc. */
68
static char urlbuffer[512];
69
static JSObject *uo; /* the url object */
71
static bool setter_suspend;
73
/* Are we modifying window.location? */
74
/*Return false if we are, because that will put a stop to javascript. */
78
if(uo != jwloc && uo != jdloc) {
83
/* This call frees t, or takes it over, so you should not free it here. */
84
gotoLocation(uo_href, 0, true);
89
/* Converting to a string just pulls out the href property */
91
loc_toString(JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
94
JS_GetProperty(jcx, obj, "href", rval);
99
loc_reload(JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
102
const char *s = firstURL();
104
gotoLocation(cloneString(s), 0, true);
106
JS_ReportError(jcx, "location.reload() cannot find a url to refresh");
111
loc_replace(JSContext * cx, JSObject * obj, uintN argc, jsval * argv,
116
if(argc > 0 && JSVAL_IS_STRING(argv[0])) {
117
s = stringize(argv[0]);
118
/* I have to copy the string, just so I can run unpercent */
121
t = resolveURL(cw->fileName, ss);
123
/* This call frees t, or takes it over, so you should not free it here. */
124
gotoLocation(t, 0, true);
128
"argument to location.replace() does not look like a url");
132
/* Put a url together from its pieces, after something has changed. */
134
build_url(int exception, const char *e)
137
const char *prot, *slashes, *host, *pathname, *pathslash, *search, *hash;
139
static const char *const noslashes[] = {
140
"mailto", "telnet", "javascript", 0
142
setter_suspend = true;
143
/* I'm a little worried about the first one being freed while I'm
144
* getting the next one.
145
* I just don't know that much about the js heap. */
149
JS_GetProperty(jcx, uo, "protocol", &v);
152
slashes = EMPTYSTRING;
153
if(stringInListCI(noslashes, prot) < 0)
158
JS_GetProperty(jcx, uo, "host", &v);
164
JS_GetProperty(jcx, uo, "pathname", &v);
165
pathname = stringize(v);
167
pathslash = EMPTYSTRING;
168
if(pathname[0] != '/')
173
JS_GetProperty(jcx, uo, "search", &v);
174
search = stringize(v);
179
JS_GetProperty(jcx, uo, "hash", &v);
183
JS_smprintf("%s%s%s%s%s%s%s", prot, slashes, host, pathslash, pathname,
185
v = STRING_TO_JSVAL(JS_NewStringCopyZ(jcx, new_url));
186
JS_SetProperty(jcx, uo, "href", &v);
187
/* I want control over this string */
188
uo_href = cloneString(new_url);
189
JS_smprintf_free(new_url);
190
setter_suspend = false;
193
/* Rebuild host, because hostname or port changed. */
195
build_host(int exception, const char *hostname, int port)
199
setter_suspend = true;
201
JS_GetProperty(jcx, uo, "port", &v);
202
port = JSVAL_TO_INT(v);
204
JS_GetProperty(jcx, uo, "hostname", &v);
205
hostname = stringize(v);
207
JS_GetProperty(jcx, uo, "host", &v);
208
oldhost = stringize(v);
209
if(exception == 2 || strchr(oldhost, ':'))
210
sprintf(urlbuffer, "%s:%d", hostname, port);
212
strcpy(urlbuffer, hostname);
213
if(strlen(urlbuffer) >= sizeof (urlbuffer))
214
i_printfExit(MSG_PortTooLong);
215
v = STRING_TO_JSVAL(JS_NewStringCopyZ(jcx, urlbuffer));
216
JS_SetProperty(jcx, uo, "host", &v);
217
setter_suspend = false;
220
/* define or set a local property */
222
loc_def_set(const char *name, const char *s,
223
JSBool(*setter) (JSContext *, JSObject *, jsval, jsval *), jsuint attr)
228
vv = STRING_TO_JSVAL(JS_NewStringCopyZ(jcx, s));
230
vv = JS_GetEmptyStringValue(jcx);
231
JS_HasProperty(jcx, uo, name, &found);
233
JS_SetProperty(jcx, uo, name, &vv);
235
JS_DefineProperty(jcx, uo, name, vv, NULL, setter, attr);
238
/* Like the above, but using an integer, this is for port only. */
240
loc_def_set_n(const char *name, int port,
241
JSBool(*setter) (JSContext *, JSObject *, jsval, jsval *), jsuint attr)
244
jsval vv = INT_TO_JSVAL(port);
245
JS_HasProperty(jcx, uo, name, &found);
247
JS_SetProperty(jcx, uo, name, &vv);
249
JS_DefineProperty(jcx, uo, name, vv, NULL, setter, attr);
250
} /* loc_def_set_n */
253
loc_def_set_part(const char *name, const char *s, int n,
254
JSBool(*setter) (JSContext *, JSObject *, jsval, jsval *), jsuint attr)
259
vv = STRING_TO_JSVAL(JS_NewStringCopyN(jcx, s, n));
261
vv = JS_GetEmptyStringValue(jcx);
262
JS_HasProperty(jcx, uo, name, &found);
264
JS_SetProperty(jcx, uo, name, &vv);
266
JS_DefineProperty(jcx, uo, name, vv, NULL, setter, attr);
267
} /* loc_def_set_part */
270
setter_loc(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
272
const char *s = stringize(*vp);
275
"window.location is assigned something that I don't understand");
278
/* I have to copy the string, just so I can run unpercent */
279
char *ss = cloneString(s);
281
t = resolveURL(cw->fileName, ss);
283
/* This call frees t, or takes it over, so you should not free it here. */
284
gotoLocation(t, 0, true);
286
/* Return false to stop javascript. */
287
/* After all, we're trying to move to a new web page. */
292
setter_loc_href(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
297
url = stringize(*vp);
301
url_initialize(url, false, true);
302
uo_href = cloneString(url);
303
if(uo == jwloc || uo == jdloc) {
305
unpercentURL(uo_href);
306
t = resolveURL(cw->fileName, uo_href);
311
} /* setter_loc_href */
314
setter_loc_hash(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
323
} /* setter_loc_hash */
326
setter_loc_search(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
335
} /* setter_loc_search */
338
setter_loc_prot(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
347
} /* setter_loc_prot */
350
setter_loc_pathname(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
359
} /* setter_loc_pathname */
362
setter_loc_hostname(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
372
} /* setter_loc_hostname */
375
setter_loc_port(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
380
port = JSVAL_TO_INT(*vp);
382
build_host(2, 0, port);
385
} /* setter_loc_port */
388
setter_loc_host(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
398
/* and we have to update hostname and port */
399
setter_suspend = true;
405
v = STRING_TO_JSVAL(JS_NewStringCopyN(jcx, e, n));
406
JS_SetProperty(jcx, uo, "hostname", &v);
408
v = INT_TO_JSVAL(atoi(s + 1));
409
JS_SetProperty(jcx, uo, "port", &v);
411
setter_suspend = false;
413
} /* setter_loc_pathname */
416
url_initialize(const char *url, bool readonly, bool exclude_href)
422
jsuint attr = JSPROP_ENUMERATE | JSPROP_PERMANENT;
424
attr |= JSPROP_READONLY;
426
setter_suspend = true;
428
/* Store the url in location.href */
430
loc_def_set("href", url, setter_loc_href, attr);
433
/* Now make a property for each piece of the url. */
434
if(s = getProtURL(url)) {
435
sprintf(urlbuffer, "%s:", s);
436
if(strlen(urlbuffer) >= sizeof (urlbuffer))
437
i_printfExit(MSG_ProtTooLong);
440
loc_def_set("protocol", s, setter_loc_prot, attr);
442
data = getDataURL(url);
445
s = strchr(data, '#');
446
loc_def_set("hash", s, setter_loc_hash, attr);
451
loc_def_set("hostname", s, setter_loc_hostname, attr);
453
getPortLocURL(url, &pl, &port);
454
loc_def_set_n("port", port, setter_loc_port, attr);
456
if(s) { /* this was hostname */
457
strcpy(urlbuffer, s);
459
sprintf(urlbuffer + strlen(urlbuffer), ":%d", port);
460
if(strlen(urlbuffer) >= sizeof (urlbuffer))
461
i_printfExit(MSG_PortTooLong);
464
loc_def_set("host", s, setter_loc_host, attr);
468
getDirURL(url, &s, &pl);
470
pl = strpbrk(s, "?\1#");
471
n = pl ? pl - s : strlen(s);
475
loc_def_set_part("pathname", s, n, setter_loc_pathname, attr);
478
if(data && (s = strpbrk(data, "?\1")) &&
479
(!(pl = strchr(data, '#')) || pl > s)) {
485
loc_def_set_part("search", s, n, setter_loc_search, attr);
487
setter_suspend = false;
488
} /* url_initialize */
491
url_ctor(JSContext * cx, JSObject * obj, uintN argc, jsval * argv, jsval * rval)
495
if(argc && JSVAL_IS_STRING(*argv)) {
496
s = stringize(argv[0]);
499
} /* string argument */
501
url_initialize(url, false, false);
505
static JSFunctionSpec url_methods[] = {
506
{"toString", loc_toString, 0, 0, 0},
511
initLocationClass(void)
513
JS_InitClass(jcx, jwin, 0, &url_class, url_ctor, 1,
514
NULL, url_methods, NULL, NULL);
515
} /* initLocationClass */
518
/*********************************************************************
519
If js changes the value of an input field in a form,
520
this fact has to make it back to the text you are reading, in edbrowse,
522
That requires a special setter function to pass the new value back to the text.
523
*********************************************************************/
526
setter_value(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
531
val = stringize(*vp);
534
"input.value is assigned something other than a string; this can cause problems when you submit the form.");
536
javaSetsTagVar(obj, val);
542
setter_checked(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
547
b = JSVAL_TO_BOOLEAN(*vp);
549
} /* setter_checked */
552
setter_selected(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
557
b = JSVAL_TO_BOOLEAN(*vp);
559
} /* setter_selected */
562
setter_selidx(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
567
n = JSVAL_TO_INT(*vp);
569
} /* setter_selidx */
572
getter_cookie(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
575
char *cook = initString(&cook_l);
576
const char *url = cw->fileName;
582
proto = getProtURL(url);
583
if(proto && stringEqualCI(proto, "https"))
585
sendCookies(&cook, &cook_l, url, secure);
586
if(memEqualCI(cook, "cookie: ", 8)) { /* should often happen */
587
strcpy(cook, cook + 8);
589
if(s = strstr(cook, "\r\n"))
593
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(jcx, cook));
596
} /* getter_cookie */
599
setter_cookie(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
601
const char *host = getHostURL(cw->fileName);
603
JS_ReportError(jcx, "cannot set cookie, ill-defined domain");
605
const char *s = stringize(*vp);
606
if(!receiveCookie(cw->fileName, s))
607
JS_ReportError(jcx, "unable to set cookie %s", s);
610
} /* setter_cookie */
613
setter_domain(JSContext * cx, JSObject * obj, jsval id, jsval * vp)
615
const char *hostname = getHostURL(cw->fileName);
618
goto out; /* local file, don't care */
619
dom = stringize(*vp);
620
if(dom && strlen(dom) && domainSecurityCheck(hostname, dom))
625
"document.domain is being set to an insecure string <%s>", dom);
628
} /* setter_domain */
631
/*********************************************************************
632
Convenient set property routines that can be invoked from edbrowse,
633
requiring no knowledge of smjs.
634
*********************************************************************/
636
static JSBool(*my_getter) (JSContext *, JSObject *, jsval, jsval *);
637
static JSBool(*my_setter) (JSContext *, JSObject *, jsval, jsval *);
640
establish_property_string(void *jv, const char *name, const char *value,
643
jsuint attr = JSPROP_ENUMERATE | JSPROP_PERMANENT;
645
attr |= JSPROP_READONLY;
647
my_getter = my_setter = 0;
648
if(stringEqual(name, "value"))
649
my_setter = setter_value;
650
if(stringEqual(name, "domain"))
651
my_setter = setter_domain;
652
if(stringEqual(name, "cookie")) {
653
my_getter = getter_cookie;
654
my_setter = setter_cookie;
656
JS_DefineProperty(jcx, obj, name,
657
((value && *value) ? STRING_TO_JSVAL(JS_NewStringCopyZ(jcx, value))
658
: JS_GetEmptyStringValue(jcx)), my_getter, my_setter, attr);
659
} /* establish_property_string */
662
establish_property_number(void *jv, const char *name, int value, bool readonly)
664
jsuint attr = JSPROP_ENUMERATE | JSPROP_PERMANENT;
666
attr |= JSPROP_READONLY;
669
if(stringEqual(name, "selectedIndex"))
670
my_setter = setter_selidx;
671
JS_DefineProperty(jcx, obj, name,
672
INT_TO_JSVAL(value), NULL, my_setter, attr);
673
} /* establish_property_number */
676
establish_property_bool(void *jv, const char *name, bool value, bool readonly)
678
jsuint attr = JSPROP_ENUMERATE | JSPROP_PERMANENT;
680
attr |= JSPROP_READONLY;
683
if(stringEqual(name, "checked"))
684
my_setter = setter_checked;
685
if(stringEqual(name, "selected"))
686
my_setter = setter_selected;
687
JS_DefineProperty(jcx, obj, name,
688
(value ? JSVAL_TRUE : JSVAL_FALSE), NULL, my_setter, attr);
689
} /* establish_property_bool */
692
establish_property_array(void *jv, const char *name)
695
JSObject *a = JS_NewArrayObject(jcx, 0, NULL);
696
establish_property_object(obj, name, a);
698
} /* establish_property_array */
701
establish_property_object(void *parent, const char *name, void *child)
703
JS_DefineProperty(jcx, parent, name,
704
OBJECT_TO_JSVAL(((JSObject *) child)), 0, 0, PROP_FIXED);
705
} /* establish_property_object */
708
establish_property_url(void *jv, const char *name,
709
const char *url, bool readonly)
712
jsuint attr = JSPROP_ENUMERATE | JSPROP_PERMANENT;
714
attr |= JSPROP_READONLY;
716
/* window.location, and document.location, has a special setter */
718
if(stringEqual(name, "location"))
719
my_setter = setter_loc;
720
uo = JS_NewObject(jcx, &url_class, NULL, obj);
721
JS_DefineProperty(jcx, obj, name,
722
OBJECT_TO_JSVAL(uo), NULL, my_setter, attr);
725
url_initialize(url, readonly, false);
726
if(my_setter == setter_loc) {
731
JS_DefineFunction(jcx, uo, "reload", loc_reload, 0, PROP_FIXED);
732
JS_DefineFunction(jcx, uo, "replace", loc_replace, 1, PROP_FIXED);
733
} /* location object */
734
} /* establish_property_url */
737
set_property_string(void *jv, const char *name, const char *value)
741
setter_suspend = true;
742
vv = ((value && *value) ? STRING_TO_JSVAL(JS_NewStringCopyZ(jcx, value))
743
: JS_GetEmptyStringValue(jcx));
744
JS_SetProperty(jcx, obj, name, &vv);
745
setter_suspend = false;
746
} /* set_property_string */
749
set_property_number(void *jv, const char *name, int value)
753
setter_suspend = true;
754
vv = INT_TO_JSVAL(value);
755
JS_SetProperty(jcx, obj, name, &vv);
756
setter_suspend = false;
757
} /* set_property_number */
760
set_property_bool(void *jv, const char *name, int value)
764
setter_suspend = true;
765
vv = (value ? JSVAL_TRUE : JSVAL_FALSE);
766
JS_SetProperty(jcx, obj, name, &vv);
767
setter_suspend = false;
768
} /* set_property_bool */
770
/* These get routines assume the property exists, and of the right type. */
772
get_property_url(void *jv, bool doaction)
775
JSObject *lo; /* location object */
778
JSBool found = false;
782
JS_HasProperty(jcx, obj, "href", &found);
784
JS_GetProperty(jcx, obj, "href", &v);
786
JS_HasProperty(jcx, obj, "src", &found);
788
JS_GetProperty(jcx, obj, "src", &v);
791
JS_HasProperty(jcx, obj, "action", &found);
793
JS_GetProperty(jcx, obj, "action", &v);
797
if(!JSVAL_IS_STRING(v)) {
798
if(!JSVAL_IS_OBJECT(v)) {
801
"url object is assigned something that I don't understand; I may not be able to fetch the next web page.");
804
lo = JSVAL_TO_OBJECT(v);
805
JS_HasProperty(jcx, lo, "actioncrash", &found);
808
if(!JS_InstanceOf(jcx, lo, &url_class, emptyArgs))
810
JS_GetProperty(jcx, lo, "href", &v);
814
} /* get_property_url */
817
get_property_string(void *jv, const char *name)
823
JS_GetProperty(jcx, obj, name, &v);
825
} /* get_property_string */
828
get_property_bool(void *jv, const char *name)
834
JS_GetProperty(jcx, obj, name, &v);
835
return JSVAL_TO_BOOLEAN(v);
836
} /* get_property_bool */
839
get_property_option(void *jv)
843
JSObject *oa; /* option array */
844
JSObject *oo; /* option object */
849
JS_GetProperty(jcx, obj, "selectedIndex", &v);
853
JS_GetProperty(jcx, obj, "options", &v);
854
oa = JSVAL_TO_OBJECT(v);
855
JS_GetElement(jcx, oa, n, &v);
856
oo = JSVAL_TO_OBJECT(v);
857
return get_property_string(oo, "value");
858
} /* get_property_option */
861
/*********************************************************************
862
Manage the array of options under an html select.
863
This will explode into a lot of code, if we ever implement
864
dynamic option lists under js control.
865
*********************************************************************/
867
static JSClass option_class = {
870
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
871
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
874
establish_js_option(void *ev, int idx)
876
JSObject *so = ev; /* select object */
878
JSObject *oa; /* option array */
879
JSObject *oo; /* option object */
880
JS_GetProperty(jcx, so, "options", &vv);
881
oa = JSVAL_TO_OBJECT(vv);
882
oo = JS_NewObject(jcx, &option_class, NULL, so);
883
vv = OBJECT_TO_JSVAL(oo);
884
JS_DefineElement(jcx, oa, idx, vv, NULL, NULL, JSPROP_ENUMERATE);
885
/* option.form = select.form */
886
JS_GetProperty(jcx, so, "form", &vv);
887
JS_SetProperty(jcx, oo, "form", &vv);
889
} /* establish_js_option */
892
/*********************************************************************
893
Compile and call event handlers.
894
*********************************************************************/
897
handlerGo(void *obj, const char *name)
901
JSObject *fo; /* function object */
904
JS_HasProperty(jcx, obj, name, &found);
907
JS_GetProperty(jcx, obj, name, &v);
908
if(!JSVAL_IS_OBJECT(v))
910
fo = JSVAL_TO_OBJECT(v);
911
if(!JS_ObjectIsFunction(jcx, fo))
913
f = JS_ValueToFunction(jcx, v);
914
JS_CallFunction(jcx, obj, f, 0, emptyArgs, &rval);
916
if(JSVAL_IS_BOOLEAN(rval))
917
rc = JSVAL_TO_BOOLEAN(rval);
923
handlerSet(void *ev, const char *name, const char *code)
930
newcode = allocMem(strlen(code) + 60);
931
strcpy(newcode, "with(document) { ");
932
JS_HasProperty(jcx, obj, "form", &found);
934
strcat(newcode, "with(this.form) { ");
935
strcat(newcode, code);
937
strcat(newcode, " }");
938
strcat(newcode, " }");
939
JS_CompileFunction(jcx, obj, name, 0, emptyParms, /* no named parameters */
940
newcode, strlen(newcode), name, 1);
945
link_onunload_onclick(void *jv)
949
JS_GetProperty(jcx, obj, "onunload", &v);
950
JS_DefineProperty(jcx, obj, "onclick", v, 0, 0, PROP_FIXED);
951
} /* link_onunload_onclick */
954
handlerPresent(void *ev, const char *name)
957
JSBool found = JS_FALSE;
960
JS_HasProperty(jcx, obj, name, &found);
962
} /* handlerPresent */