diff --git a/CHANGELOG b/CHANGELOG
index e67b998..95fb238 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,8 @@
+* Fix issue where Opera 9.x returns incorrect results on certain Selector queries with descendant combinators. [#395 state:resolved] (Arpan, fearphage, kangax, Andrew Dupont)
+
* Null out references to elements in cache on page unload. Need this in addition to the Event#stopObserving calls to clean up memory leaks. [#425 state:resolved] (ykphuah, mr_justin, Andrew Dupont)
-* Ensure `toString` and `valueOf` properties are copied to a subclass only when necessary in IE6. [@382 state:resolved] (Samuel Lebeau)
+* Ensure `toString` and `valueOf` properties are copied to a subclass only when necessary in IE6. [#382 state:resolved] (Samuel Lebeau)
* Make sure `getAttribute` is used without flag when accessing the "type" attribute of an iframe (IE throws error otherwise). [#118 state:resolved] (Zekid, kangax)
diff --git a/src/dom/selector.js b/src/dom/selector.js
index b5a8ea4..0e3e486 100644
--- a/src/dom/selector.js
+++ b/src/dom/selector.js
@@ -18,23 +18,50 @@ var Selector = Class.create({
},
- shouldUseXPath: function() {
- if (!Prototype.BrowserFeatures.XPath) return false;
+ shouldUseXPath: (function() {
- var e = this.expression;
-
- // Safari 3 chokes on :*-of-type and :empty
- if (Prototype.Browser.WebKit &&
- (e.include("-of-type") || e.include(":empty")))
- return false;
+ // Some versions of Opera 9.x produce incorrect results when using XPath
+ // with descendant combinators.
+ // see: http://opera.remcol.ath.cx/bugs/index.php?action=bug&id=652
+ var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
+ var isBuggy = false;
+ if (document.evaluate && window.XPathResult) {
+ var el = document.createElement('div');
+ el.innerHTML = '
';
+
+ var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
+ "//*[local-name()='li' or local-name()='LI']";
+
+ var result = document.evaluate(xpath, el, null,
+ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+ isBuggy = (result.snapshotLength !== 2);
+ el = null;
+ }
+ return isBuggy;
+ })();
- // XPath can't do namespaced attributes, nor can it read
- // the "checked" property from DOM nodes
- if ((/(\[[\w-]*?:|:checked)/).test(e))
- return false;
+ return function() {
+ if (!Prototype.BrowserFeatures.XPath) return false;
- return true;
- },
+ var e = this.expression;
+
+ // Safari 3 chokes on :*-of-type and :empty
+ if (Prototype.Browser.WebKit &&
+ (e.include("-of-type") || e.include(":empty")))
+ return false;
+
+ // XPath can't do namespaced attributes, nor can it read
+ // the "checked" property from DOM nodes
+ if ((/(\[[\w-]*?:|:checked)/).test(e))
+ return false;
+
+ if (IS_DESCENDANT_SELECTOR_BUGGY) return false;
+
+ return true;
+ }
+
+ })(),
shouldUseSelectorsAPI: function() {
if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
diff --git a/test/unit/selector_test.js b/test/unit/selector_test.js
index 4fb7bf2..5501eed 100644
--- a/test/unit/selector_test.js
+++ b/test/unit/selector_test.js
@@ -88,7 +88,7 @@ new Test.Unit.Runner({
},
testSelectorWithAttributeContainingDash: function() {
- this.assertEnumEqual([$('attr_with_dash')], $$('[foo-bar]'));
+ this.assertEnumEqual([$('attr_with_dash')], $$('[foo-bar]'), "attribute with hyphen");
},
testSelectorWithUniversalAndHyphenTokenizedAttributeValue: function() {
@@ -392,5 +392,13 @@ new Test.Unit.Runner({
var b = $('dupContainer').down('#dupL4');
this.assertEqual(a, b);
+ },
+
+ testDescendantSelectorBuggy: function() {
+ var el = document.createElement('div');
+ el.innerHTML = '';
+ document.body.appendChild(el);
+ this.assertEqual(2, $(el).select('ul li').length);
+ document.body.removeChild(el);
}
});
\ No newline at end of file