1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00

[smjs document replace] fixed property location, replace and writeln functions, tests

This commit is contained in:
nobody@earth.com 2021-02-24 11:23:34 +01:00
parent b538eafbbc
commit f953744189
8 changed files with 495 additions and 18 deletions

View File

@ -210,6 +210,7 @@ struct document {
struct uri_list ecmascript_imports;
/** used by setTimeout */
timer_id_T timeout;
int ecmascript_counter;
#endif
#ifdef CONFIG_CSS
/** @todo FIXME: We should externally maybe using cache_entry store the

View File

@ -400,6 +400,12 @@ spidermonkey_check_for_exception(JSContext *ctx) {
}
//JS_ClearPendingException(ctx);
}
/* This absorbs all following exceptions
* probably not the 100% correct solution
* to the javascript error handling but
* at least there isn't too much click-bait
* on each site with javascript enabled */
JS_ClearPendingException(ctx);
}
}

View File

@ -141,7 +141,33 @@ document_get_property_location(JSContext *ctx, unsigned int argc, JS::Value *vp)
assert(JS_InstanceOf(ctx, parent_win, &window_class, NULL));
if_assert_failed return false;
JS_GetProperty(ctx, parent_win, "location", args.rval());
JSCompartment *comp = js::GetContextCompartment(ctx);
if (!comp) {
return false;
}
struct session *ses;
int index;
struct location *loc;
struct ecmascript_interpreter *interpreter = JS_GetCompartmentPrivate(comp);
struct view_state *vs;
vs = interpreter->vs;
struct document *document;
struct document_view *doc_view = interpreter->vs->doc_view;
doc_view = vs->doc_view;
document = doc_view->document;
char *str = get_uri_string(document->uri, URI_ORIGINAL);
if (str)
{
args.rval().setString(JS_NewStringCopyZ(ctx, str));
mem_free(str);
} else {
args.rval().setUndefined();
}
return true;
}
@ -429,10 +455,12 @@ document_get_property(JSContext *ctx, JS::HandleObject hobj, JS::HandleId hid, J
static bool document_write(JSContext *ctx, unsigned int argc, JS::Value *rval);
static bool document_writeln(JSContext *ctx, unsigned int argc, JS::Value *rval);
static bool document_replace(JSContext *ctx, unsigned int argc, JS::Value *rval);
const spidermonkeyFunctionSpec document_funcs[] = {
{ "write", document_write, 1 },
{ "writeln", document_writeln, 1 },
{ "replace", document_replace, 1 },
{ NULL }
};
@ -450,18 +478,27 @@ document_write_do(JSContext *ctx, unsigned int argc, JS::Value *rval, int newlin
struct string *ret = interpreter->ret;
JS::CallArgs args = JS::CallArgsFromVp(argc, rval);
if (argc >= 1 && ret) {
int i = 0;
char *code;
for (; i < argc; ++i) {
char *code = jsval_to_string(ctx, args[i]);
code = stracpy("");
add_to_string(ret, code);
int numeric_arg;
if (argc >= 1)
{
for (int i=0;i<argc;++i)
{
code = jshandle_value_to_char_string(ctx,&args[i]);
}
if (newline)
add_char_to_string(ret, '\n');
{
add_to_strn(&code, "\n");
}
}
//DBG("%s",code);
/* XXX: I don't know about you, but I have *ENOUGH* of those 'Undefined
* function' errors, I want to see just the useful ones. So just
* lighting a led and going away, no muss, no fuss. --pasky */
@ -469,6 +506,29 @@ document_write_do(JSContext *ctx, unsigned int argc, JS::Value *rval, int newlin
* -> "Show information about the document using some valid,
* nevertheless unsupported methods/properties." --pasky too */
struct document_view *doc_view = interpreter->vs->doc_view;
struct document *document;
document = doc_view->document;
struct cache_entry *cached = doc_view->document->cached;
struct fragment *f = cached ? cached->frag.next : NULL;
cached = doc_view->document->cached;
f = get_cache_fragment(cached);
struct string buffer = INIT_STRING("", 0);
if (f && f->length)
{
//char *code = jsval_to_string(ctx, args[0]);
int code_len=strlen(code);
if (document->ecmascript_counter==0)
{
add_fragment(cached,0,code,code_len);
} else {
add_fragment(cached,f->length,code,code_len);
}
document->ecmascript_counter++;
}
#ifdef CONFIG_LEDS
set_led_value(interpreter->vs->doc_view->session->status.ecmascript_led, 'J');
#endif
@ -492,3 +552,122 @@ document_writeln(JSContext *ctx, unsigned int argc, JS::Value *rval)
{
return document_write_do(ctx, argc, rval, 1);
}
// Helper function for document replace
char *
str_replace(char *orig, char *rep, char *with)
{
char *result; // the return string
char *ins; // the next insert point
char *tmp; // varies
int len_rep; // length of rep (the string to remove)
int len_with; // length of with (the string to replace rep with)
int len_front; // distance between rep and end of last rep
int count; // number of replacements
// sanity checks and initialization
if (!orig || !rep)
{
return NULL;
}
len_rep = strlen(rep);
if (len_rep == 0) {
return NULL; // empty rep causes infinite loop during count
}
if (!with) {
with = "";
}
len_with = strlen(with);
// count the number of replacements needed
ins = orig;
for (count = 0; tmp = strstr(ins, rep); ++count)
{
ins = tmp + len_rep;
}
result = tmp = malloc(strlen(orig) + ( (len_with - len_rep) * count) + 1);
if (!result) {
return NULL;
}
// first time through the loop, all the variable are set correctly
// from here on,
// tmp points to the end of the result string
// ins points to the next occurrence of rep in orig
// orig points to the remainder of orig after "end of rep"
while (count--)
{
ins = strstr(orig, rep);
len_front = ins - orig;
tmp = strncpy(tmp, orig, len_front) + len_front;
tmp = strcpy(tmp, with) + len_with;
orig += len_front + len_rep; // move to next "end of rep"
}
strcpy(tmp, orig);
//sprintf(tmp,'%s',orig);
return(result);
}
/* @document_funcs{"replace"} */
static bool
document_replace(JSContext *ctx, unsigned int argc, JS::Value *vp)
{
JSCompartment *comp = js::GetContextCompartment(ctx);
struct ecmascript_interpreter *interpreter = JS_GetCompartmentPrivate(comp);
struct document_view *doc_view = interpreter->vs->doc_view;
struct session *ses = doc_view->session;
struct terminal *term = ses->tab->term;
struct string *ret = interpreter->ret;
struct document *document;
JS::CallArgs args = CallArgsFromVp(argc, vp);
document = doc_view->document;
if (argc != 2) {
args.rval().setBoolean(false);
return(true);
}
unsigned char *needle;
unsigned char *heystack;
needle = stracpy("");
heystack = stracpy("");
needle = jshandle_value_to_char_string(ctx,&args[0]);
heystack = jshandle_value_to_char_string(ctx,&args[1]);
//DBG("doc replace %s %s\n", needle, heystack);
int nu_len=0;
int fd_len=0;
unsigned char *nu;
struct cache_entry *cached = doc_view->document->cached;
struct fragment *f = cached ? cached->frag.next : NULL;
cached = doc_view->document->cached;
f = get_cache_fragment(cached);
if (f && f->length)
{
fd_len=strlen(f->data);
nu=str_replace(f->data,needle,heystack);
nu_len=strlen(nu);
delete_entry_content(cached);
/* This is very ugly, indeed. And Yes fd_len isn't
* logically correct. But using nu_len will cause
* the document to render improperly.
* TBD: somehow better rerender the document */
int ret = add_fragment(cached,0,nu,fd_len);
normalize_cache_entry(cached,nu_len);
document->ecmascript_counter++;
//DBG("doc replace %s %s\n", needle, heystack);
}
args.rval().setBoolean(true);
return(true);
}

View File

@ -173,6 +173,9 @@ localstorage_getitem(JSContext *ctx, unsigned int argc, JS::Value *vp)
static bool
localstorage_setitem(JSContext *ctx, unsigned int argc, JS::Value *vp)
{
unsigned char *key;
unsigned char *val;
JSCompartment *comp = js::GetContextCompartment(ctx);
if (!comp)
@ -188,8 +191,9 @@ localstorage_setitem(JSContext *ctx, unsigned int argc, JS::Value *vp)
return(true);
}
unsigned char *key = JS_EncodeString(ctx, args[0].toString());
unsigned char *val = JS_EncodeString(ctx, args[1].toString());
key = jshandle_value_to_char_string(ctx,&args[0]);
val = jshandle_value_to_char_string(ctx,&args[1]);
saveToStorage(key,val);
//DBG("%s %s\n", key, val);

View File

@ -9,6 +9,7 @@ static void string_to_jsval(JSContext *ctx, JS::Value *vp, char *string);
static void astring_to_jsval(JSContext *ctx, JS::Value *vp, char *string);
static int jsval_to_boolean(JSContext *ctx, JS::Value *vp);
static unsigned char * jshandle_value_to_char_string(JSContext *ctx, JS::MutableHandleValue *obj);
@ -38,4 +39,32 @@ jsval_to_boolean(JSContext *ctx, JS::Value *vp)
return (int)r_vp.toBoolean();
}
/* Since SpiderMonkey 52 the Mutable Handle Object
* is different for String and Number and must be
* handled accordingly */
unsigned char *
jshandle_value_to_char_string(JSContext *ctx, JS::MutableHandleValue *obj)
{
unsigned char *ret;
ret = stracpy("");
if (obj->isString())
{
ret = JS_EncodeString(ctx, obj->toString());
} else if (obj->isNumber())
{
int tmpinta = obj->toNumber();
char tmpints[256]="";
sprintf(tmpints,"%d",tmpinta);
add_to_strn(&ret,tmpints);
} else if (obj->isBoolean())
{
int tmpinta = obj->toBoolean();
char tmpints[16]="";
sprintf(tmpints,"%d",tmpinta);
add_to_strn(&ret,tmpints);
}
return(ret);
}
#endif

View File

@ -633,6 +633,34 @@ session_is_loading(struct session *ses)
return 0;
}
#ifdef CONFIG_ECMASCRIPT
void
doc_rerender_after_document_update(struct session *ses) {
/** This is really not nice. But that's the way
** how to display the final Javascript render
** taken from toggle_plain_html(ses, ses->doc_view, 0);
** This is toggled */
assert(ses && ses->doc_view && ses->tab && ses->tab->term);
if_assert_failed
{
int dummy=1;
} else {
if (ses->doc_view->document->ecmascript_counter>0)
{
if (ses->doc_view->vs)
{
ses->doc_view->vs->plain = !ses->doc_view->vs->plain;
draw_formatted(ses, 1);
ses->doc_view->vs->plain = !ses->doc_view->vs->plain;
draw_formatted(ses, 1);
//DBG("REDRAWING...");
}
}
}
}
#endif
void
doc_loading_callback(struct download *download, struct session *ses)
{
@ -646,6 +674,14 @@ doc_loading_callback(struct download *download, struct session *ses)
draw_formatted(ses, 1);
#ifdef CONFIG_ECMASCRIPT
/* This is implemented to rerender the document
* in case it was modified by document.replace
* or document write */
doc_rerender_after_document_update(ses);
#endif
if (get_cmd_opt_bool("auto-submit")) {
if (!list_empty(ses->doc_view->document->forms)) {
get_cmd_opt_bool("auto-submit") = 0;

View File

@ -0,0 +1,173 @@
<head>
<title>
ARITHMETICS
</title>
<style>
* {
font-size: 24px;
}
body {
background: black;
}
input {
font-family: monospace;
background: black;
color: gray;
border: 0px;
}
*:focus {
outline: none !important;
border: 0px;
}
</style>
</head>
<body>
<font color="gray">
<form id="core" name="core" action="arithmetics.html">
<pre>
<font color="yellow">#x#</font>
+<font color="yellow">#z#</font>
---------
= <input autocomplete="off" autofocus name="i" value="" size="2">
Control: <font color="#bbbbbb">#f#</font>
Score: <font color="green">#s#</font>
<input name="tm" value="#NOW#" size="3" type="hidden">
<input type="submit" size="2" value="o">
</pre>
</form>
<script>
// CHANGE THE MAX_NUMBER HERE
var MAX_NUMBER=10;
function lpad(number,sz) {
if (sz==undefined) {
sz=4;
}
maxnum=Math.pow(10,sz)-1;
str_shift=Array(sz).join(" ");
if (number<=maxnum) { number = (str_shift+number).slice(-sz); }
///console.log(number+" "+maxnum+" "+sz+"|"+str_shift+"|");
return number;
}
function doc_replace(needle,heystack) {
if (document.replace) {
document.replace(needle,heystack);
} else {
document.getElementsByTagName("html")[0].innerHTML=document.getElementsByTagName("html")[0].innerHTML.replace(needle,heystack);
}
}
function findGetParameter(paramName) {
var res = null;
loc=document.location.toString();
//console.log(loc);
elems=loc.split('?')[1];
if (!elems) { return(res); }
//console.log(elems);
par=elems.split('&');
for (i=0;i<par.length;i++) {
key=par[i].split('=');
if (key[0]==paramName) {
res=key[1];
break;
//console.log("key: "+key[0]+" val: "+key[1]);
}
}
return(res);
}
function proc_var(prnname,lodname,savename,saveval) {
if (lodname!=null) {
x = localStorage.getItem(lodname);
//console.log("Loading "+lodname+" val: "+x);
} else {
x=saveval;
}
if (x==null || !x) { x="@"+prnname+"@"; }
doc_replace('#'+prnname+'#',lpad(x));
if (savename!==undefined) {
localStorage.setItem(prnname,saveval);
//console.log("Saving "+prnname+" val: "+saveval);
}
return(x);
}
function get_rand_var(xname) {
x = Math.round(Math.random()*(MAX_NUMBER));
localStorage.setItem(xname,x);
doc_replace('#'+xname+'#',lpad(x));
return(x);
}
function prepare_task() {
// print and get input begin
// get previous random number
a=proc_var('a','x');
z=proc_var('u','z');
// generate random number
x=get_rand_var('x');
y=get_rand_var('z');
// get previous user input
p=proc_var('p','i');
return [a,z];
}
function get_user_input() {
// get user input
var i = findGetParameter("i");
proc_var('i',null,'i',i);
// print and get input end
return(i);
}
function evaluate_result(a,z,i) {
// feedback loop begin
correctAnswer=0;
//console.log("z: " + z + " a: " + a + " i: " + i);
if (Number(z) + Number(a) == Number(i)) { // user inputs prev number
doc_replace('#f#',' OK');
doc_replace('#bbbbbb','#00ee00');
correctAnswer=1;
} else {
doc_replace('#f#','NOK');
doc_replace('#bbbbbb','#ee0000');
}
// feedback loop end
return(correctAnswer);
}
function updateScore(gain) {
keyname="score";
score=localStorage.getItem(keyname);
if (!score || score==null
|| typeof(score)=="NaN"
|| score=="NaN"
) {
score=0;
}
score=Number(score);
score+=gain;
localStorage.setItem(keyname,score.toString());
return(score);
}
function process_result(cor) {
score=updateScore(cor);
doc_replace('#s#',lpad(score,6));
}
function get_timestamp() {
var now = new Date();
var res = now.toISOString().replace(/\D/g, "");;
return(res);
}
function prevent_form_resubmission() {
ts=get_timestamp();
document.replace('#NOW#',ts);
}
prevent_form_resubmission();
res=prepare_task();
inp=get_user_input();
cor=evaluate_result(res[0],res[1],inp);
process_result(cor);
</script>
</font>
</body>
</html>

View File

@ -1,11 +1,60 @@
<html>
<body>
<a href="history.html">history.html</a><br/>
<head>
<title>
-| 16 colors |-
</title>
<script type="text/javascript">
document.writeln('<a href="ontest.html">Test</a><br/>');
document.writeln('<a href="infinite2.html">infinite2.html</a><br/>');
document.write(1,2,'3', '<br/>');
</script>
<a href="onload.html">onload.html</a><br/>
document.writeln('<html><head><title>-| 16 colors |-</title></head><pre>');
var colors=[
"white",
"maroon",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"grey",
"red",
"lime",
"olive",
"teal",
"purple",
"fuchsia",
"aqua",
];
document.write('<table>');
colors.forEach(printstr);
document.write('</table>');
document.writeln('<br>');
colors.forEach(printstrln);
function lpad_str(str) {
if (str.length<=9) { str=(str+" ").slice(-9); }
return str;
}
function lpad(number) {
if (number<=999) { number = ("00"+number).slice(-3); }
return number;
}
function printstr(item,index) {
if ((index)%4==0) { document.writeln("<tr>"); }
document.write('<td>');
document.write('<font color="'+item+'">'+lpad_str(item) +'</font> ');
document.write('<td>');
if ((index+1)%4==0) { document.writeln("</tr>"); }
}
function printstrln(item,index) {
document.writeln('<font color="'+item+'">'+lpad(index)+' This is '+item+'</font>');
}
document.writeln('');
document.writeln('<font color="yellow">This is true : '+true+'</font>');
document.writeln('<font color="red">and this is false: '+false+'</font>');
document.writeln('<font color="yellow">',"That's it as ... ","1+1=",2,'</font>');
document.writeln('</pre></body></html>');
</script>
</head>
<body>
</body>
</html>