Skip to content

Commit 5e39884

Browse files
committed
fix parameter processing for document.evaluate()
1 parent 5ec9084 commit 5e39884

4 files changed

Lines changed: 193 additions & 2 deletions

File tree

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
<body>
1010
<release version="3.4.0" date="June xx, 2023" description="Chrome/Edge 114, Firefox 114, Bugfixes">
11+
<action type="fix" dev="rbri">
12+
Fix parameter processing for document.evaluate().
13+
</action>
1114
<action type="fix" dev="rbri">
1215
core-js: Negative integer index values must be handles as strings and not as integer.
1316
</action>

src/main/java/org/htmlunit/BrowserVersionFeatures.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,10 @@ public enum BrowserVersionFeatures {
793793
@BrowserFeature(IE)
794794
JS_DOCUMENT_DESIGN_MODE_INHERIT,
795795

796+
/** Javascript document.evaluate creates a new result object even if provided as param. */
797+
@BrowserFeature({CHROME, EDGE})
798+
JS_DOCUMENT_EVALUATE_RECREATES_RESULT,
799+
796800
/** Javascript document.forms(...) supported. */
797801
@BrowserFeature(IE)
798802
JS_DOCUMENT_FORMS_FUNCTION_SUPPORTED,

src/main/java/org/htmlunit/javascript/host/dom/Document.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import static org.htmlunit.BrowserVersionFeatures.HTML_COLOR_EXPAND_ZERO;
3030
import static org.htmlunit.BrowserVersionFeatures.JS_ANCHOR_REQUIRES_NAME_OR_ID;
3131
import static org.htmlunit.BrowserVersionFeatures.JS_DOCUMENT_DESIGN_MODE_INHERIT;
32+
import static org.htmlunit.BrowserVersionFeatures.JS_DOCUMENT_EVALUATE_RECREATES_RESULT;
3233
import static org.htmlunit.BrowserVersionFeatures.JS_DOCUMENT_FORMS_FUNCTION_SUPPORTED;
3334
import static org.htmlunit.BrowserVersionFeatures.JS_DOCUMENT_SELECTION_RANGE_COUNT;
3435
import static org.htmlunit.BrowserVersionFeatures.JS_DOCUMENT_SETTING_DOMAIN_THROWS_FOR_ABOUT_BLANK;
@@ -77,6 +78,7 @@
7778
import org.htmlunit.corejs.javascript.ScriptRuntime;
7879
import org.htmlunit.corejs.javascript.Scriptable;
7980
import org.htmlunit.corejs.javascript.ScriptableObject;
81+
import org.htmlunit.corejs.javascript.Undefined;
8082
import org.htmlunit.cssparser.parser.CSSException;
8183
import org.htmlunit.html.DomComment;
8284
import org.htmlunit.html.DomDocumentFragment;
@@ -637,12 +639,26 @@ public Object createComment(final String comment) {
637639
public XPathResult evaluate(final String expression, final Node contextNode,
638640
final Object resolver, final int type, final Object result) {
639641
try {
640-
XPathResult xPathResult = (XPathResult) result;
641-
if (xPathResult == null) {
642+
XPathResult xPathResult = null;
643+
if (result instanceof XPathResult) {
644+
xPathResult = (XPathResult) result;
645+
646+
if (getBrowserVersion().hasFeature(JS_DOCUMENT_EVALUATE_RECREATES_RESULT)) {
647+
xPathResult = new XPathResult();
648+
xPathResult.setParentScope(getParentScope());
649+
xPathResult.setPrototype(getPrototype(xPathResult.getClass()));
650+
}
651+
}
652+
else if (result == null
653+
|| Undefined.isUndefined(result)
654+
|| result instanceof ScriptableObject) {
642655
xPathResult = new XPathResult();
643656
xPathResult.setParentScope(getParentScope());
644657
xPathResult.setPrototype(getPrototype(xPathResult.getClass()));
645658
}
659+
else {
660+
throw ScriptRuntime.typeError("Argument 5 of Document.evaluate has to be an XPathResult or null.");
661+
}
646662

647663
PrefixResolver prefixResolver = null;
648664
if (resolver instanceof NativeFunction) {

src/test/java/org/htmlunit/html/xpath/HtmlUnitXPath2Test.java

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,4 +423,172 @@ private void compareBooleanValue(final String xpath) throws Exception {
423423

424424
loadPageVerifyTitle2(content);
425425
}
426+
427+
/**
428+
* @throws Exception if the test fails
429+
*/
430+
@Test
431+
@Alerts(DEFAULT = "mySpan",
432+
IE = "error")
433+
public void minimalParameters() throws Exception {
434+
final String content = "<html>\n"
435+
+ "<head>\n"
436+
+ "<script>\n"
437+
+ LOG_TITLE_FUNCTION
438+
+ "function test() {\n"
439+
+ " try {\n"
440+
+ " var res = '';\n"
441+
+ " var result = document.evaluate('//span', document.documentElement);\n"
442+
+ " while (node = result.iterateNext()) {\n"
443+
+ " res += node.id;\n"
444+
+ " }\n"
445+
+ " log(res);\n"
446+
+ " } catch (e) {log('error')}\n"
447+
+ "}\n"
448+
+ "</script></head>\n"
449+
+ "<body onload='test()'>\n"
450+
+ " <div id='myDiv' attr='false'>false</span>\n"
451+
+ " <span id='mySpan' attr='true'>true</span>\n"
452+
+ "</body></html>";
453+
454+
loadPageVerifyTitle2(content);
455+
}
456+
457+
/**
458+
* @throws Exception if the test fails
459+
*/
460+
@Test
461+
@Alerts(DEFAULT = "mySpan",
462+
IE = "error")
463+
public void undefinedResult() throws Exception {
464+
final String content = "<html>\n"
465+
+ "<head>\n"
466+
+ "<script>\n"
467+
+ LOG_TITLE_FUNCTION
468+
+ "function test() {\n"
469+
+ " try {\n"
470+
+ " var res = '';\n"
471+
+ " var result = document.evaluate('//span', "
472+
+ "document.documentElement, null, XPathResult.ANY_TYPE, undefined);\n"
473+
+ " while (node = result.iterateNext()) {\n"
474+
+ " res += node.id;\n"
475+
+ " }\n"
476+
+ " log(res);\n"
477+
+ " } catch (e) {log('error')}\n"
478+
+ "}\n"
479+
+ "</script></head>\n"
480+
+ "<body onload='test()'>\n"
481+
+ " <div id='myDiv' attr='false'>false</span>\n"
482+
+ " <span id='mySpan' attr='true'>true</span>\n"
483+
+ "</body></html>";
484+
485+
loadPageVerifyTitle2(content);
486+
}
487+
488+
/**
489+
* @throws Exception if the test fails
490+
*/
491+
@Test
492+
@Alerts("error")
493+
public void stringResult() throws Exception {
494+
final String content = "<html>\n"
495+
+ "<head>\n"
496+
+ "<script>\n"
497+
+ LOG_TITLE_FUNCTION
498+
+ "function test() {\n"
499+
+ " try {\n"
500+
+ " var res = '';\n"
501+
+ " var result = document.evaluate('//span', "
502+
+ "document.documentElement, null, XPathResult.ANY_TYPE, 'abcd');\n"
503+
+ " while (node = result.iterateNext()) {\n"
504+
+ " res += node.id;\n"
505+
+ " }\n"
506+
+ " log(res);\n"
507+
+ " } catch (e) {log('error')}\n"
508+
+ "}\n"
509+
+ "</script></head>\n"
510+
+ "<body onload='test()'>\n"
511+
+ " <div id='myDiv' attr='false'>false</span>\n"
512+
+ " <span id='mySpan' attr='true'>true</span>\n"
513+
+ "</body></html>";
514+
515+
loadPageVerifyTitle2(content);
516+
}
517+
518+
/**
519+
* @throws Exception if the test fails
520+
*/
521+
@Test
522+
@Alerts(DEFAULT = "mySpan",
523+
IE = "error")
524+
public void objectResult() throws Exception {
525+
final String content = "<html>\n"
526+
+ "<head>\n"
527+
+ "<script>\n"
528+
+ LOG_TITLE_FUNCTION
529+
+ "function test() {\n"
530+
+ " try {\n"
531+
+ " var res = '';\n"
532+
+ " var result = document.evaluate('//span', "
533+
+ "document.documentElement, null, XPathResult.ANY_TYPE, {});\n"
534+
+ " while (node = result.iterateNext()) {\n"
535+
+ " res += node.id;\n"
536+
+ " }\n"
537+
+ " log(res);\n"
538+
+ " } catch (e) {log('error')}\n"
539+
+ "}\n"
540+
+ "</script></head>\n"
541+
+ "<body onload='test()'>\n"
542+
+ " <div id='myDiv' attr='false'>false</span>\n"
543+
+ " <span id='mySpan' attr='true'>true</span>\n"
544+
+ "</body></html>";
545+
546+
loadPageVerifyTitle2(content);
547+
}
548+
549+
/**
550+
* @throws Exception if the test fails
551+
*/
552+
@Test
553+
@Alerts(DEFAULT = "mySpan - - myDiv",
554+
FF = "mySpan - myDiv - ",
555+
FF_ESR = "mySpan - myDiv - ",
556+
IE = "error")
557+
public void reuseResult() throws Exception {
558+
final String content = "<html>\n"
559+
+ "<head>\n"
560+
+ "<script>\n"
561+
+ LOG_TITLE_FUNCTION
562+
+ "function test() {\n"
563+
+ " try {\n"
564+
+ " var res = '';\n"
565+
+ " var result = document.evaluate('//span', "
566+
+ "document.documentElement, null, XPathResult.ANY_TYPE);\n"
567+
+ " while (node = result.iterateNext()) {\n"
568+
+ " res += node.id;\n"
569+
+ " }\n"
570+
+ " res += ' - ';\n"
571+
572+
+ " var result2 = document.evaluate('//div', "
573+
+ "document.documentElement, null, XPathResult.ANY_TYPE, result);\n"
574+
+ " while (node = result.iterateNext()) {\n"
575+
+ " res += node.id;\n"
576+
+ " }\n"
577+
+ " res += ' - ';\n"
578+
579+
+ " while (node = result2.iterateNext()) {\n"
580+
+ " res += node.id;\n"
581+
+ " }\n"
582+
583+
+ " log(res);\n"
584+
+ " } catch (e) {log('error')}\n"
585+
+ "}\n"
586+
+ "</script></head>\n"
587+
+ "<body onload='test()'>\n"
588+
+ " <div id='myDiv' attr='false'>false</span>\n"
589+
+ " <span id='mySpan' attr='true'>true</span>\n"
590+
+ "</body></html>";
591+
592+
loadPageVerifyTitle2(content);
593+
}
426594
}

0 commit comments

Comments
 (0)