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..6a6ec1375 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,25 @@ 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; + struct string code; - for (; i < argc; ++i) { - char *code = jsval_to_string(ctx, args[i]); + init_string(&code); - add_to_string(ret, code); + 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; + cached = doc_view->document->cached; + struct fragment *f = get_cache_fragment(cached); + struct string buffer = INIT_STRING("", 0); + if (f && f->length) + { + int code_len=code.length; + if (document->ecmascript_counter==0) + { + add_fragment(cached,0,code.source,code.length); + } else { + add_fragment(cached,f->length,code.source,code.length); + } + document->ecmascript_counter++; + } + + + #ifdef CONFIG_LEDS set_led_value(interpreter->vs->doc_view->session->status.ecmascript_led, 'J'); #endif + done_string(&code); args.rval().setBoolean(false); return true; @@ -492,3 +549,129 @@ document_writeln(JSContext *ctx, unsigned int argc, JS::Value *rval) { return document_write_do(ctx, argc, rval, 1); } + +void +string_replace(struct string *res, struct string *inp, struct string *what, struct string *repl) +{ + struct string tmp; + struct string tmp2; + char *head; + char *found; + char *ins; + char *tmp_cnt; + + init_string(&tmp); + init_string(&tmp2); + + add_to_string(&tmp,inp->source); + + head = tmp.source; + int count = 0; + ins = head; + if (what->length==0) + { + add_to_string(res,inp->source); + return; + } + + // count occurence of string in input + for (count = 0; tmp_cnt = strstr(ins, what->source); ++count) + { + ins = tmp_cnt + what->length; + } + + for (int i=0;isource); + // count chars before and after occurence + int bf_len=found-tmp.source; + int af_len=tmp.length-bf_len-what->length; + // move head by what + found+=what->length; + // join the before, needle and after to res + add_bytes_to_string(&tmp2,tmp.source,bf_len); + add_bytes_to_string(&tmp2,repl->source,repl->length); + add_bytes_to_string(&tmp2,found,af_len); + // clear tmp string and tmp2 string + init_string(&tmp); + add_to_string(&tmp,tmp2.source); + init_string(&tmp2); + //printf("TMP: %s |\n",tmp.source); + head = tmp.source; + } + add_to_string(res,tmp.source); + + done_string(&tmp); + done_string(&tmp2); + +} + +/* @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); + } + + struct string needle; + struct string heystack; + + init_string(&needle); + init_string(&heystack); + + jshandle_value_to_char_string(&needle, ctx, &args[0]); + jshandle_value_to_char_string(&heystack, ctx, &args[1]); + + //DBG("doc replace %s %s\n", needle.source, heystack.source); + + int nu_len=0; + int fd_len=0; + unsigned char *nu; + struct cache_entry *cached = doc_view->document->cached; + cached = doc_view->document->cached; + struct fragment *f = get_cache_fragment(cached); + if (f && f->length) + { + fd_len=f->length; + + struct string f_data; + init_string(&f_data); + add_to_string(&f_data,f->data); + + struct string nu_str; + init_string(&nu_str); + string_replace(&nu_str,&f_data,&needle,&heystack); + nu_len=nu_str.length; + 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 + * now it's places on the session level in doc_loading_callback */ + int ret = add_fragment(cached,0,nu_str.source,fd_len); + normalize_cache_entry(cached,nu_len); + document->ecmascript_counter++; + //DBG("doc replace %s %s\n", needle.source, heystack.source); + } + + done_string(&needle); + done_string(&heystack); + + args.rval().setBoolean(true); + + return(true); +} + diff --git a/src/ecmascript/spidermonkey/localstorage.c b/src/ecmascript/spidermonkey/localstorage.c index c40773d71..0615795ff 100644 --- a/src/ecmascript/spidermonkey/localstorage.c +++ b/src/ecmascript/spidermonkey/localstorage.c @@ -173,6 +173,12 @@ localstorage_getitem(JSContext *ctx, unsigned int argc, JS::Value *vp) static bool localstorage_setitem(JSContext *ctx, unsigned int argc, JS::Value *vp) { + struct string key; + struct string val; + + init_string(&key); + init_string(&val); + JSCompartment *comp = js::GetContextCompartment(ctx); if (!comp) @@ -188,9 +194,11 @@ 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()); - saveToStorage(key,val); + jshandle_value_to_char_string(&key,ctx,&args[0]); + jshandle_value_to_char_string(&val,ctx,&args[1]); + + saveToStorage(key.source,val.source); + //DBG("%s %s\n", key, val); @@ -199,5 +207,8 @@ localstorage_setitem(JSContext *ctx, unsigned int argc, JS::Value *vp) #endif args.rval().setBoolean(true); + done_string(&key); + done_string(&val); + return(true); } diff --git a/src/ecmascript/spidermonkey/util.h b/src/ecmascript/spidermonkey/util.h index 955d9512a..94aaa761b 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 void jshandle_value_to_char_string(struct string *string, JSContext *ctx, JS::MutableHandleValue *obj); @@ -38,4 +39,27 @@ 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 */ +void +jshandle_value_to_char_string(struct string *string,JSContext *ctx, JS::MutableHandleValue *obj) +{ + init_string(string); + + if (obj->isString()) + { + add_to_string(string,JS_EncodeString(ctx, obj->toString())); + } else if (obj->isNumber()) + { + int tmpinta = obj->toNumber(); + add_format_to_string(string, "%d", tmpinta); + } else if (obj->isBoolean()) + { + int tmpinta = obj->toNumber(); + add_format_to_string(string, "%d", tmpinta); + } +} + #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_replace.html b/test/ecmascript/document_replace.html new file mode 100644 index 000000000..8734d3c7d --- /dev/null +++ b/test/ecmascript/document_replace.html @@ -0,0 +1,8 @@ + + + +
+#HERE#
+
+ + 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('
'); + + +