From f953744189ba889621a1648512bc8fe23f57bd4f Mon Sep 17 00:00:00 2001 From: "nobody@earth.com" Date: Wed, 24 Feb 2021 11:23:34 +0100 Subject: [PATCH] [smjs document replace] fixed property location, replace and writeln functions, tests --- src/document/document.h | 1 + src/ecmascript/spidermonkey.c | 6 + src/ecmascript/spidermonkey/document.c | 197 ++++++++++++++++++++- src/ecmascript/spidermonkey/localstorage.c | 8 +- src/ecmascript/spidermonkey/util.h | 29 +++ src/session/session.c | 36 ++++ test/ecmascript/arithmetics.html | 173 ++++++++++++++++++ test/ecmascript/document_write.html | 63 ++++++- 8 files changed, 495 insertions(+), 18 deletions(-) create mode 100644 test/ecmascript/arithmetics.html diff --git a/src/document/document.h b/src/document/document.h index 1585b31ac..f15dd633a 100644 --- a/src/document/document.h +++ b/src/document/document.h @@ -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 diff --git a/src/ecmascript/spidermonkey.c b/src/ecmascript/spidermonkey.c index 2805c68ff..706c347b8 100644 --- a/src/ecmascript/spidermonkey.c +++ b/src/ecmascript/spidermonkey.c @@ -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); } } diff --git a/src/ecmascript/spidermonkey/document.c b/src/ecmascript/spidermonkey/document.c index 0cc304d2d..97416f5c2 100644 --- a/src/ecmascript/spidermonkey/document.c +++ b/src/ecmascript/spidermonkey/document.c @@ -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 "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); +} + diff --git a/src/ecmascript/spidermonkey/localstorage.c b/src/ecmascript/spidermonkey/localstorage.c index c40773d71..d9e932580 100644 --- a/src/ecmascript/spidermonkey/localstorage.c +++ b/src/ecmascript/spidermonkey/localstorage.c @@ -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); diff --git a/src/ecmascript/spidermonkey/util.h b/src/ecmascript/spidermonkey/util.h index 955d9512a..c072c4f7a 100644 --- a/src/ecmascript/spidermonkey/util.h +++ b/src/ecmascript/spidermonkey/util.h @@ -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 diff --git a/src/session/session.c b/src/session/session.c index c667bc4c6..5b060e6be 100644 --- a/src/session/session.c +++ b/src/session/session.c @@ -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; diff --git a/test/ecmascript/arithmetics.html b/test/ecmascript/arithmetics.html new file mode 100644 index 000000000..56a28263b --- /dev/null +++ b/test/ecmascript/arithmetics.html @@ -0,0 +1,173 @@ + + + +ARITHMETICS + + + + + +
+
+                               
+   #x#
+  +#z#
+ ---------
+
+  = 
+
+Control:   #f#
+Score:  #s#
+
+
+
+
+
+ +
+ + diff --git a/test/ecmascript/document_write.html b/test/ecmascript/document_write.html index 02856d605..f20bfcc0b 100644 --- a/test/ecmascript/document_write.html +++ b/test/ecmascript/document_write.html @@ -1,11 +1,60 @@ - -history.html
+ + +-| 16 colors |- + -onload.html
+document.writeln('-| 16 colors |-
');
+  var colors=[
+    "white",
+    "maroon",
+    "green",
+    "yellow",
+    "blue",
+    "magenta",
+    "cyan",
+    "white",
+    "grey",
+    "red",
+    "lime",
+    "olive",
+    "teal",
+    "purple",
+    "fuchsia",
+    "aqua",
+  ];
+
+  document.write('');
+  colors.forEach(printstr);
+  document.write('
'); + document.writeln('
'); + 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(""); } + document.write(''); + document.write(''+lpad_str(item) +' '); + document.write(''); + if ((index+1)%4==0) { document.writeln(""); } + } + function printstrln(item,index) { + document.writeln(''+lpad(index)+' This is '+item+''); + } + document.writeln(''); + document.writeln('This is true : '+true+''); + document.writeln('and this is false: '+false+''); + document.writeln('',"That's it as ... ","1+1=",2,''); + document.writeln('
'); + + +