Bug 1055533 - Implement Element.closest(). r=bz

This commit is contained in:
Giovanni Sferro 2014-09-13 15:08:00 +02:00
parent 0f88833823
commit a00eae3420
5 changed files with 117 additions and 0 deletions

View File

@ -646,6 +646,8 @@ public:
}
bool HasAttributeNS(const nsAString& aNamespaceURI,
const nsAString& aLocalName) const;
Element* Closest(const nsAString& aSelector,
ErrorResult& aResult);
bool Matches(const nsAString& aSelector,
ErrorResult& aError);
already_AddRefed<nsIHTMLCollection>

View File

@ -2747,6 +2747,33 @@ Element::SetTokenList(nsIAtom* aAtom, nsIVariant* aValue)
return rv.ErrorCode();
}
Element*
Element::Closest(const nsAString& aSelector, ErrorResult& aResult)
{
nsCSSSelectorList* selectorList = ParseSelectorList(aSelector, aResult);
if (!selectorList) {
// Either we failed (and aResult already has the exception), or this
// is a pseudo-element-only selector that matches nothing.
return nullptr;
}
OwnerDoc()->FlushPendingLinkUpdates();
TreeMatchContext matchingContext(false,
nsRuleWalker::eRelevantLinkUnvisited,
OwnerDoc(),
TreeMatchContext::eNeverMatchVisited);
matchingContext.SetHasSpecifiedScope();
matchingContext.AddScopeElement(this);
for (nsINode* node = this; node; node = node->GetParentNode()) {
if (node->IsElement() &&
nsCSSRuleProcessor::SelectorListMatches(node->AsElement(),
matchingContext,
selectorList)) {
return node->AsElement();
}
}
return nullptr;
}
bool
Element::Matches(const nsAString& aSelector, ErrorResult& aError)
{

View File

@ -574,6 +574,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183
[test_domparser_null_char.html]
[test_domparsing.html]
[test_elementTraversal.html]
[test_element_closest.html]
[test_encodeToStringWithMaxLength.html]
[test_fileapi.html]
skip-if = e10s

View File

@ -0,0 +1,84 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1055533
-->
<head>
<title>Test for Bug 1055533</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body id="body">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1055533">Mozilla Bug 1055533</a>
<div id="test8" class="div3">
<div id="test7" class="div2">
<div id="test6" class="div1">
<form id="test10" class="form2"></form>
<form id="test5" class="form1" name="form-a">
<input id="test1" class="input1" required>
<fieldset class="fieldset2" id="test2">
<select id="test3" class="select1" required>
<option default id="test4" value="">Test4</option>
<option selected id="test11">Test11</option>
<option id="test12">Test12</option>
<option id="test13">Test13</option>
</select>
<input id="test9" type="text" required>
</fieldset>
</form>
</div>
</div>
</div>
<script class="testbody" type="text/javascript">
test("select" , "test12", "test3");
test("fieldset" , "test13", "test2");
test("div" , "test13", "test6");
test("body" , "test3" , "body");
test("[default]" , "test4" , "test4");
test("[selected]" , "test4" , "");
test("[selected]" , "test11", "test11");
test('[name="form-a"]' , "test12", "test5");
test('form[name="form-a"]' , "test13", "test5");
test("input[required]" , "test9" , "test9");
test("select[required]" , "test9" , "");
test("div:not(.div1)" , "test13", "test7");
test("div.div3" , "test6" , "test8");
test("div#test7" , "test1" , "test7");
test(".div3 > .div2" , "test12", "test7");
test(".div3 > .div1" , "test12", "");
test("form > input[required]" , "test9" , "");
test("fieldset > select[required]", "test12", "test3");
test("input + fieldset" , "test6" , "");
test("form + form" , "test3" , "test5");
test("form + form" , "test5" , "test5");
test(":empty" , "test10", "test10");
test(":last-child" , "test11", "test2");
test(":first-child" , "test12", "test3");
test(":invalid" , "test11", "test2");
test(":scope" , "test4", "test4");
test("select > :scope" , "test4", "test4");
test("div > :scope" , "test4", "");
try {
test(":has(> :scope)" , "test4", "test3");
} catch(e) {
todo(false, ":has(> :scope) [:has is not implemented yet]");
}
function test(aSelector, aElementId, aTargetId) {
var el = document.getElementById(aElementId).closest(aSelector);
if (el === null) {
is("", aTargetId, aSelector);
} else {
is(el.id, aTargetId, aSelector);
}
}
</script>
</body>
</html>

View File

@ -52,6 +52,9 @@ interface Element : Node {
[Pure]
boolean hasAttributeNS(DOMString? namespace, DOMString localName);
[Throws, Pure]
Element? closest(DOMString selector);
[Throws, Pure]
boolean matches(DOMString selector);