xmlet/HtmlFlow

Missing oncontextmenu and disable textarea whitespace

ottolewis8 opened this issue · 6 comments

I have an element that when right click will show a custom menu, using oncontextmenu function which returns false prevents the default browser context menu from showing.
The attribute oncontextmenu is missing, you can use attrOnauxclick or attrOnclick and look at the event to determine if a right click was sent but the browser context menu is shown.
I have a work around to use a custom attribute which works but can you add oncontextmenu?

Also textarea></textarea> should not have any white space between tags as the browser adds this to the form.
image
image
Text area is not empty

Inside the view I found .setIndented(false) which can fix the textarea being not empty (would prefer only on areatext rather than whole document) but with async view you get a stack overflow error.

public class Test {

	public static HtmlViewAsync view = HtmlFlow.viewAsync(Test::template);

	static void template(HtmlPage view) {
		view.setIndented(false).div().__();
	}
}
var b = Test.view.renderAsync("");
try {
	b.get();
} catch (InterruptedException e) {
	e.printStackTrace();
} catch (ExecutionException e) {
	e.printStackTrace();
}

Hi @ottolewis8,

Well, I will try to answer all the different points of this Issue:

  1. setIndent
  2. HtmlViewAsync
  3. Missing oncontextmenu
  4. disable textarea whitespace

1. setIndent

  1. You should not call setIndent in the template chained with the builders (i.e. html(), div(), etc)
  2. setIndent should be used on the view and not in the template function.

So, regarding your last example you should remove the setIndent call of the template function and use it on:

public static HtmlViewAsync view = HtmlFlow.viewAsync(Test::template).setIndent(false);
// or:
public static HtmlView view = HtmlFlow.view(Test::template).setIndent(false);

Henceforward, every time you render you will get an unindented HTML document. Also notice, that calling setIndent has an induced overhead due to an internal preprocessing. Thus, you should call it only once on view initialisation.

2. HtmlViewAsync

Do you really need HtmlViewAsync? You should use this kind of view only when you need to deal with asynchronous data such as CompletableFuture, Publisher, or other.

In most cases an HtmlView is enough or even HtmlDoc that is simpler. Please, check core concepts here https://htmlflow.org/features#core-concepts and choose the most simplest option that fit your needs by the following order of increasing complexity: HtmlDoc, HtmlView, HtmlViewAsync.

3. Missing oncontextmenu

I think we can solve it and include that attribute in a new release. Let me check if @lcduarte agrees. I think we can include that attribute in globalEventAttributes and generate a new release of HtmlApiFaster:
https://github.com/xmlet/HtmlApiFaster/blob/master/src/main/resources/html_5_2.xsd#L1258

4. disable textarea whitespace

That's tricky because according to our indentation processing every end tag will make a newline and decrease indentation. And it is odd to add a conditional behaviour specifically for a text area. I hope that setIndentation would be enough to solve your issue.

Thank you,
Miguel

Thanks for the response.
I can use setIndent(false) on view creation which will fix the unwanted content in textarea and I found a browser extension which can format HTML if I ever need to inspect it.

Does async view support multiple threads using a view at the same time, I have an example view which simulates async behaviour.
If threadCount is set 1 it always works, but with a larger number some threads throw exceptions with arraycopy or never exit.
Unless I misunderstand when HtmlViewAsync should be used.
Changing to non-async view it works every time.

public static HtmlView<Object> view = HtmlFlow.view(Test1::template); // none async works with multi threads
public static HtmlViewAsync<Object> view = HtmlFlow.viewAsync(Test1::template); // fails multi thread
public class Test1 {

	public static HtmlViewAsync<Object> view = HtmlFlow.viewAsync(Test1::template);

	static void template(HtmlPage view) {
		view.div().span().of(span -> {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			span.a().attrHref("link").text("text").__().of(s -> {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				s.a().attrHref("link2").text("text2").__();
			});
		}).__()
		.__();
	}
}
public static void main(String[] args) throws InterruptedException {
		System.out.println("start");
		final int threadCount = 50;
		AtomicInteger left = new AtomicInteger(threadCount);
		Thread thread[] = new Thread[threadCount];
		for (int i = 0; i < threadCount; i++) {
			final int threadNumber = i;
			thread[i] = new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						var b = Test1.view.renderAsync("");
						b.get();
						System.out.println("Thread " + threadNumber + " exited, remaining: " + left.decrementAndGet());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
		}
		for (int i = 0; i < threadCount; i++) {
			thread[i].start();
		}
		for (int i = 0; i < threadCount; i++) {
			thread[i].join();
		}
		System.out.println("end");
	}

This only happens with 4.2, 4.1 does not have this issue.

Hi @ottolewis8,

TLDR In parallel thread scenarios, you must use .threadSafe().

Additionally, I have significant doubts about your necessity of an HtmlAsyncView. I ask you again: are you using a model of type CompletableFuture, Observable, Flux, Publisher, etc? If not, do not use HtmlAsyncView.

Unless I misunderstand when HtmlViewAsync should be used.

Maybe documentation is not clear. Could you please specify which statement misled you regarding the role of HtmlViewAsync?

Regarding your example, it was a matter of luck that one of them works and the other doesn't. Concurrency is not deterministic. On my machine, both fail, and one of them enters an infinite loop.

And, even when your test completes successfully, there is a high likelihood of the resulting HTML being incorrect.

I developed a unit test with your example and you can observe that both views (i.e. HtmlView and HtmlViewAsync) run successfully and produce valid HTML. Notice, both views are built with .threadSafe()

HtmlViewAsync<Object> view = HtmlFlow.viewAsync(TestAsyncViewInConcurInMultpleThreads::template).threadSafe();
...
HtmlView<Object> view = HtmlFlow.view(TestAsyncViewInConcurInMultpleThreads::template).threadSafe();

However, if you remove .threadSafe() they may complete, or not. And, even if they complete there is high chance of failing the assertion that checks the conformity of HTML. Try your self:

https://github.com/xmlet/HtmlFlow/blob/resolve%23105/src/test/java/htmlflow/test/TestAsyncViewInConcurInMultpleThreads.java

You are correct I don't Asyc view as no Future, pulushers etc is used. I was looking at the V4 documentation and there is no mention of thread safety: https://htmlflow.org/features but there is on V3 https://htmlflow.org/features_version3. Making the changes you have suggested.

Fixes on new release 4.3. oncontextmenu included in global events attributes. Also documentation updated about threadSafe() and setIndent(boolean).