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