From 865f3fa0d096cb2823c922b859a713ef9ac8d6b1 Mon Sep 17 00:00:00 2001
From: Witold Filipczyk <witekfl@poczta.onet.pl>
Date: Sat, 19 Nov 2022 19:24:15 +0100
Subject: [PATCH] [ecmascript] Rewritten document.write

spidermonkey segfaults on test/ecmascript/document_write.html
quickjs and mujs do not.
---
 src/ecmascript/ecmascript.cpp            | 40 ++++++++++++++++++++++++
 src/ecmascript/ecmascript.h              |  3 ++
 src/ecmascript/mujs/document.cpp         | 39 ++---------------------
 src/ecmascript/quickjs/document.cpp      | 39 ++---------------------
 src/ecmascript/spidermonkey/document.cpp | 38 ++--------------------
 test/ecmascript/document_write.html      |  8 +++--
 6 files changed, 58 insertions(+), 109 deletions(-)

diff --git a/src/ecmascript/ecmascript.cpp b/src/ecmascript/ecmascript.cpp
index 22d9d1565..102b640eb 100644
--- a/src/ecmascript/ecmascript.cpp
+++ b/src/ecmascript/ecmascript.cpp
@@ -302,6 +302,7 @@ ecmascript_get_interpreter(struct view_state *vs)
 	}
 
 	(void)init_string(&interpreter->code);
+	(void)init_string(&interpreter->writecode);
 	return interpreter;
 }
 
@@ -322,6 +323,7 @@ ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter)
 #endif
 	free_ecmascript_string_list(&interpreter->onload_snippets);
 	done_string(&interpreter->code);
+	done_string(&interpreter->writecode);
 	/* Is it superfluous? */
 	if (interpreter->vs->doc_view) {
 		struct ecmascript_timeout *t;
@@ -369,6 +371,44 @@ check_for_rerender(struct ecmascript_interpreter *interpreter, const char* text)
 		struct session *ses = doc_view->session;
 		struct cache_entry *cached = document->cached;
 
+		if (!strcmp(text, "eval")) {
+			if (interpreter->element_offset) {
+				if (interpreter->writecode.length) {
+					std::map<int, xmlpp::Element *> *mapa = (std::map<int, xmlpp::Element *> *)document->element_map;
+
+					if (mapa) {
+						auto element = (*mapa).find(interpreter->element_offset);
+
+						if (element != (*mapa).end()) {
+							xmlpp::Element *el = element->second;
+							xmlpp::ustring text = "<root>";
+							text += interpreter->writecode.source;
+							text += "</root>";
+
+							xmlDoc* doc = htmlReadDoc((xmlChar*)text.c_str(), NULL, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
+							// Encapsulate raw libxml document in a libxml++ wrapper
+							xmlpp::Document doc1(doc);
+
+							auto root = doc1.get_root_node();
+							auto root1 = root->find("//root")[0];
+							auto children2 = root1->get_children();
+							auto it2 = children2.begin();
+							auto end2 = children2.end();
+							for (; it2 != end2; ++it2) {
+								xmlAddPrevSibling(el->cobj(), (*it2)->cobj());
+							}
+							xmlpp::Node::remove_node(el);
+						}
+					}
+				}
+			} else {
+				if (interpreter->writecode.length) {
+					add_fragment(cached, 0, interpreter->writecode.source, interpreter->writecode.length);
+					document->ecmascript_counter++;
+				}
+			}
+		}
+
 		//fprintf(stderr, "%s\n", text);
 
 		if (document->dom) {
diff --git a/src/ecmascript/ecmascript.h b/src/ecmascript/ecmascript.h
index 02f2337c6..2c4d06fd5 100644
--- a/src/ecmascript/ecmascript.h
+++ b/src/ecmascript/ecmascript.h
@@ -66,6 +66,9 @@ struct ecmascript_interpreter {
 	/* The code evaluated by setTimeout() */
 	struct string code;
 
+	/* document.write buffer */
+	struct string writecode;
+
 	struct heartbeat *heartbeat;
 
 	/* This is a cross-rerenderings accumulator of
diff --git a/src/ecmascript/mujs/document.cpp b/src/ecmascript/mujs/document.cpp
index b5995cab4..0274e8ab5 100644
--- a/src/ecmascript/mujs/document.cpp
+++ b/src/ecmascript/mujs/document.cpp
@@ -858,12 +858,6 @@ mjs_document_write_do(js_State *J, int newline)
 	fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__);
 #endif
 	struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J);
-	struct string code;
-
-	if (!init_string(&code)) {
-		js_pushnull(J);
-		return;
-	}
 	int argc = 1;
 
 	if (argc >= 1)
@@ -873,47 +867,20 @@ mjs_document_write_do(js_State *J, int newline)
 			const char *str = js_tostring(J, i+1);
 
 			if (str) {
-				add_to_string(&code, str);
+				add_to_string(&interpreter->writecode, str);
 			}
 		}
 
 		if (newline) 
 		{
-			add_to_string(&code, "\n");
+			add_to_string(&interpreter->writecode, "\n");
 		}
 	}
-	//DBG("%s",code.source);
-
-	/* 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 */
-	/* TODO: Perhaps we can introduce ecmascript.error_report_unsupported
-	 * -> "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);
-
-	if (f && f->length)
-	{
-		if (false && 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++;
-	}
+	interpreter->changed = true;
 
 #ifdef CONFIG_LEDS
 	set_led_value(interpreter->vs->doc_view->session->status.ecmascript_led, 'J');
 #endif
-
-	done_string(&code);
 	js_pushundefined(J);
 }
 
diff --git a/src/ecmascript/quickjs/document.cpp b/src/ecmascript/quickjs/document.cpp
index d89a639ff..20172ca27 100644
--- a/src/ecmascript/quickjs/document.cpp
+++ b/src/ecmascript/quickjs/document.cpp
@@ -903,11 +903,6 @@ js_document_write_do(JSContext *ctx, JSValueConst this_val, int argc, JSValueCon
 	fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__);
 #endif
 	struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx);
-	struct string code;
-
-	if (!init_string(&code)) {
-		return JS_EXCEPTION;
-	}
 
 	if (argc >= 1)
 	{
@@ -921,48 +916,20 @@ js_document_write_do(JSContext *ctx, JSValueConst this_val, int argc, JSValueCon
 			if (!str) {
 				return JS_EXCEPTION;
 			}
-			add_bytes_to_string(&code, str, len);
+			add_bytes_to_string(&interpreter->writecode, str, len);
 			JS_FreeCString(ctx, str);
 		}
 
 		if (newline) 
 		{
-			add_to_string(&code, "\n");
+			add_to_string(&interpreter->writecode, "\n");
 		}
 	}
-	//DBG("%s",code.source);
-
-	/* 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 */
-	/* TODO: Perhaps we can introduce ecmascript.error_report_unsupported
-	 * -> "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);
-
-	if (f && f->length)
-	{
-		if (false && 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++;
-	}
+	interpreter->changed = true;
 
 #ifdef CONFIG_LEDS
 	set_led_value(interpreter->vs->doc_view->session->status.ecmascript_led, 'J');
 #endif
-
-	done_string(&code);
-
 	return JS_FALSE;
 }
 
diff --git a/src/ecmascript/spidermonkey/document.cpp b/src/ecmascript/spidermonkey/document.cpp
index ec25a7856..9e8c79cc9 100644
--- a/src/ecmascript/spidermonkey/document.cpp
+++ b/src/ecmascript/spidermonkey/document.cpp
@@ -1305,56 +1305,24 @@ document_write_do(JSContext *ctx, unsigned int argc, JS::Value *rval, int newlin
 	struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS::GetRealmPrivate(comp);
 	JS::CallArgs args = JS::CallArgsFromVp(argc, rval);
 
-	struct string code;
-
-	if (!init_string(&code)) {
-		return false;
-	}
-
 	if (argc >= 1)
 	{
 		for (unsigned int i = 0; i < argc; ++i)
 		{
-			jshandle_value_to_char_string(&code, ctx, args[i]);
+			jshandle_value_to_char_string(&interpreter->writecode, ctx, args[i]);
 		}
 
 		if (newline) 
 		{
-			add_to_string(&code, "\n");
+			add_to_string(&interpreter->writecode, "\n");
 		}
 	}
-	//DBG("%s",code.source);
-
-	/* 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 */
-	/* TODO: Perhaps we can introduce ecmascript.error_report_unsupported
-	 * -> "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);
-
-	if (f && f->length)
-	{
-		if (false && 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++;
-	}
+	interpreter->changed = true;
 
 #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;
diff --git a/test/ecmascript/document_write.html b/test/ecmascript/document_write.html
index f20bfcc0b..5b7fb3b5f 100644
--- a/test/ecmascript/document_write.html
+++ b/test/ecmascript/document_write.html
@@ -4,6 +4,9 @@
 -| 16 colors |-
 </title>
 <script type="text/javascript">
+function onl()
+{
+
 document.writeln('<html><head><title>-| 16 colors |-</title></head><pre>');
   var colors=[
     "white",
@@ -53,8 +56,9 @@ document.writeln('<html><head><title>-| 16 colors |-</title></head><pre>');
   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>
+}
+</script>
 </head>
-<body>
+<body onload="onl()">
 </body>
 </html>