This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 268087 - Html Editor hangs for 3-4 minutes on 'incorrect' HTML (Netbeans completely freezes)
Summary: Html Editor hangs for 3-4 minutes on 'incorrect' HTML (Netbeans completely fr...
Status: NEW
Alias: None
Product: web
Classification: Unclassified
Component: HTML Editor (show other bugs)
Version: Dev
Hardware: PC Windows 7
: P3 normal (vote)
Assignee: Milutin Kristofic
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-09-18 22:15 UTC by NukemBy
Modified: 2016-09-19 08:50 UTC (History)
0 users

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments
JMH perf test for type.isAssignableFrom() (3.65 KB, application/x-zip-compressed)
2016-09-18 22:15 UTC, NukemBy
Details

Note You need to log in before you can comment on or make changes to this bug.
Description NukemBy 2016-09-18 22:15:03 UTC
Created attachment 162102 [details]
JMH perf test for type.isAssignableFrom()

I'm opening in HTML editor a file, which is not a real HTML, but rather an HTML template with some special syntax like this:

    {#rgn "region-name" hidden=true}
        <div class="className">
            <div class="className">${title}<div class="className">
                <i class="className" onclick=${@item_onClick}/>
                </div>
            </div>
            ...
        </div>
    {/rgn}

Note: 'onclick' attribute is not enclosed into quotes - this is intended because it should be evaluated as a function reference at run-time, not a string. HTML editor goes crazy with many of such elements on the page - it looks like it thinks that 'i' tag is not closed - and therefore consecutive elements are treated as children rather than siblings. On one of such pages Netbeans completely freezes for around 3-4 minutes and becomes completely unresponsive - here is lengthy call stack for this

    "Editor Parsing Loop"
	at org.netbeans.modules.html.parser.ElementsFactory$VirtualOpenTag.children(ElementsFactory.java:793)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:183)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:187)
	at org.netbeans.modules.html.editor.lib.api.elements.ElementUtils.findBySemanticRange(ElementUtils.java:193)
	at org.netbeans.modules.html.editor.api.gsf.HtmlParserResult.findBySemanticRange(HtmlParserResult.java:215)
	at org.netbeans.modules.css.visual.HtmlEditorSourceTask.setActiveElement(HtmlEditorSourceTask.java:152)
	at org.netbeans.modules.css.visual.HtmlEditorSourceTask.run(HtmlEditorSourceTask.java:121)
	at org.netbeans.modules.css.visual.HtmlEditorSourceTask.run(HtmlEditorSourceTask.java:72)
	at org.netbeans.modules.parsing.impl.TaskProcessor.callParserResultTask(TaskProcessor.java:584)
	at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.run(TaskProcessor.java:809)
	at org.openide.util.lookup.Lookups.executeWith(Lookups.java:304)
	at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.execute(TaskProcessor.java:725)
	at org.netbeans.modules.parsing.impl.TaskProcessor$CompilationJob.run(TaskProcessor.java:686)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1443)
	at org.netbeans.modules.openide.util.GlobalLookup.execute(GlobalLookup.java:68)
	at org.openide.util.lookup.Lookups.executeWith(Lookups.java:303)
	at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2058)

Few things with this issue:

1. Is it possible to make Netbeans understand non-quoted attribute values? I think that will fix the root cause.

      <i class="className" onclick=${@item_onClick}/>

2. I tried digging into findBySemanticRange(), but it seems to be beyond my understanding. 

Probably it has sense to limit recursion to something around 10 levels - valid HTML typically does not have larger level of 'indentation', or add some self-guarding against overall time of the gsf.HtmlParserResult.findBySemanticRange()


3. Self profiler says that in this whole lookup 99% of time is spent in 

package org.netbeans.modules.html.parser;
public class ElementsFactory {

    static class VirtualOpenTag implements OpenTag, ModifiableOpenTag {

        @Override
        public <T extends Element> Collection<T> children(Class<T> type)

Nevertheless that particular problem cannot be fixed in that only method, but I see few things which can significantly improve performance (let's say 10 times) of findBySemanticRange() in general.


3.1) Class.isAssignableFrom() is rather slow and better be replaced by functionally equivalent for this use-case Class.isInstance(), which is roughly 20 times faster (JVM specific feature?) 

package org.netbeans.modules.html.parser;
public class ElementsFactory {

    static class VirtualOpenTag implements OpenTag, ModifiableOpenTag {

        @Override
        public <T extends Element> Collection<T> children(Class<T> type) {
            Collection<T> filtered = new ArrayList<T>();
            for (Element child : children()) {
                if (type.isInstance(child)) {
 --> //         if (type.isAssignableFrom(child.getClass())) { <--
                    filtered.add(type.cast(child));
                }
            }
            return filtered;
        }

I've attached a JMH benchmark as a prooflink, the summary is (Per 1 mln of iterations)

    Benchmark                      Mode  Cnt  Score    Error  Units
    Casting.cast_isAssignableFrom  avgt    5  0.044 ?  0.002   s/op
    Casting.cast_isInstance        avgt    5  0.002 ?  0.001   s/op


2. empty instance of array list is created per each iteration, even when there is no matching results. Assuming that method in that particular 'bad' environment is invoked many thousands of times - many thousands of unneeded empty ArrayLists are created and put extra pressure on GC. It is better to rewrite it as following:

        @Override
        public <T extends Element> Collection<T> children(Class<T> type) {
            Collection<T> filtered = null;
            for (Element child : children()) {
                if (type.isInstance(child)) {
                    if( filtered == null )
                        filtered = new ArrayList<T>();
                    filtered.add(type.cast(child));
                }
            }
            return (filtered != null) ? filtered : Collections.<T>emptyList();
        }
Comment 1 NukemBy 2016-09-19 08:50:36 UTC
It looks like dirty safe-guard hack like following fixes the issue:

    public static Node findBySemanticRange(Node node, int offset, boolean forward) {
        int depth = 0;
        
        for( Node parent = node.parent(); parent != null; parent = parent.parent(), depth++ ) {
            if( depth > 10 )
                return node;
        }
        
        for (OpenTag child : node.children(OpenTag.class)) {


However root cause is wrong custom file mapping configured in my environment - it should be xxx->XHTML (which supports and requires self-closing tags), not just HTML. Unquoted attributes like 'onclick=${@item_onClick}/>' do not cause problems.

After applying both changes I do not experience performance problems.