Index: test/browser/elemHideEmulation.js |
=================================================================== |
--- a/test/browser/elemHideEmulation.js |
+++ b/test/browser/elemHideEmulation.js |
@@ -12,25 +12,33 @@ |
* GNU General Public License for more details. |
* |
* You should have received a copy of the GNU General Public License |
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
*/ |
"use strict"; |
-/* globals ElemHideEmulation */ |
+/* globals ElemHideEmulation, splitSelector, |
+ parseSelectorContent, |
+ parseSelector, positionInParent, makeSelector, |
+ PlainSelector, HasSelector, PropsSelector */ |
let myUrl = document.currentScript.src; |
exports.tearDown = function(callback) |
{ |
let styleElements = document.head.getElementsByTagName("style"); |
while (styleElements.length) |
styleElements[0].parentNode.removeChild(styleElements[0]); |
+ |
+ let child; |
+ while (child = document.body.firstChild) |
+ document.body.removeChild(child); |
+ |
callback(); |
}; |
function unexpectedError(error) |
{ |
console.error(error); |
this.ok(false, "Unexpected error: " + error); |
} |
@@ -64,72 +72,333 @@ |
else |
{ |
styleElement = document.createElement("style"); |
document.head.appendChild(styleElement); |
} |
styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length); |
} |
-function createElementWithStyle(styleBlock) |
+// insert a <div> with a unique id and and empty CSS rule |
+// for the the selector matching the id. |
+function createElementWithStyle(styleBlock, parent) |
{ |
let element = document.createElement("div"); |
element.id = findUniqueId(); |
- document.body.appendChild(element); |
+ if (!parent) |
+ document.body.appendChild(element); |
+ else |
+ parent.appendChild(element); |
insertStyleRule("#" + element.id + " " + styleBlock); |
return element; |
} |
-function applyElemHideEmulation(selectors) |
+// Will ensure the class ElemHideEmulation is loaded |
+// and then will call the callback. |
+// NOTE: if it never loads, this will probably hang in an infinite |
+// loop |
+function loadElemHideEmulation() |
{ |
if (typeof ElemHideEmulation == "undefined") |
{ |
return loadScript(myUrl + "/../../../lib/common.js").then(() => |
{ |
return loadScript(myUrl + "/../../../chrome/content/elemHideEmulation.js"); |
}).then(() => |
{ |
- return applyElemHideEmulation(selectors); |
+ return loadElemHideEmulation(); |
}); |
} |
- let elemHideEmulation = new ElemHideEmulation( |
- window, |
- callback => |
- { |
- let patterns = []; |
- selectors.forEach(selector => |
- { |
- patterns.push({selector}); |
- }); |
- callback(patterns); |
- }, newSelectors => |
- { |
- if (!newSelectors.length) |
- return; |
- let selector = newSelectors.join(", "); |
- insertStyleRule(selector + "{display: none !important;}"); |
- } |
- ); |
- |
- elemHideEmulation.apply(); |
return Promise.resolve(); |
} |
+// instantiate a ElemHideEmulation with @selectors. |
+function applyElemHideEmulation(selectors) |
+{ |
+ return loadElemHideEmulation().then(() => |
+ { |
+ let elemHideEmulation = new ElemHideEmulation( |
+ window, |
+ callback => |
+ { |
+ let patterns = []; |
+ selectors.forEach(selector => |
+ { |
+ patterns.push({selector}); |
+ }); |
+ callback(patterns); |
+ }, |
+ newSelectors => |
+ { |
+ if (!newSelectors.length) |
+ return; |
+ let selector = newSelectors.join(", "); |
+ insertStyleRule(selector + "{display: none !important;}"); |
+ }, |
+ elems => |
+ { |
+ if (!elems.length) |
+ return; |
+ for (let elem of elems) |
+ elem.style.display = "none"; |
+ } |
+ ); |
+ |
+ elemHideEmulation.apply(); |
+ return Promise.resolve(); |
+ }); |
+} |
+ |
+exports.testParseSelectorContent = function(test) |
+{ |
+ loadElemHideEmulation().then(() => |
+ { |
+ let parsed = parseSelectorContent("> div) > div", 0); |
+ test.equal(parsed.text, "> div"); |
+ test.equal(parsed.end, 5); |
+ |
+ parsed = parseSelectorContent("\"> div\") > div", 0); |
+ test.equal(parsed.text, "\"> div\""); |
+ test.equal(parsed.end, 7); |
+ parsed = parseSelectorContent(" \"> div\" ) > div", 0); |
+ test.equal(parsed.text, " \"> div\" "); |
+ test.equal(parsed.end, 9); |
+ |
+ // parens not closed. |
+ parsed = parseSelectorContent("> div > div", 0); |
+ test.equal(parsed, null); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testParseSelector = function(test) |
+{ |
+ loadElemHideEmulation().then(() => |
+ { |
+ let {selectors, hide} = parseSelector(""); |
+ test.equal(selectors.length, 0); |
+ test.ok(!hide); |
+ |
+ let selector = "div > :-abp-properties('background-color: rgb(0, 0, 0)')"; |
+ let parsed = parseSelector(selector); |
+ test.equal(parsed.selectors.length, 2); |
+ test.ok(!parsed.hide); |
+ test.ok(parsed.selectors[0] instanceof PlainSelector); |
+ test.ok(parsed.selectors[1] instanceof PropsSelector); |
+ |
+ selector = "div > :-abp-has(> div.inside) > div"; |
+ parsed = parseSelector(selector); |
+ test.equal(parsed.selectors.length, 3); |
+ test.ok(parsed.hide); |
+ test.ok(parsed.selectors[0] instanceof PlainSelector); |
+ test.ok(parsed.selectors[1] instanceof HasSelector); |
+ test.ok(parsed.selectors[2] instanceof PlainSelector); |
+ |
+ selector = "div > div:-abp-has(> div.inside) > div"; |
+ parsed = parseSelector(selector); |
+ |
+ test.equal(parsed.selectors.length, 3); |
+ test.ok(parsed.hide); |
+ test.ok(parsed.selectors[0] instanceof PlainSelector); |
+ test.ok(parsed.selectors[1] instanceof HasSelector); |
+ test.ok(parsed.selectors[2] instanceof PlainSelector); |
+ |
+ selector = "div > :-abp-has(> div.inside) > :-abp-properties(background-color: rgb(0, 0, 0))"; |
+ parsed = parseSelector(selector); |
+ |
+ test.equal(parsed.selectors.length, 4); |
+ test.ok(parsed.hide); |
+ test.ok(parsed.selectors[0] instanceof PlainSelector); |
+ test.ok(parsed.selectors[1] instanceof HasSelector); |
+ test.ok(parsed.selectors[2] instanceof PlainSelector); |
+ test.ok(parsed.selectors[3] instanceof PropsSelector); |
+ |
+ selector = "div > :-abp-has(> div.inside > :-abp-properties(background-color: rgb(0, 0, 0))"; |
+ parsed = parseSelector(selector); |
+ test.equal(parsed.selectors, null); |
+ |
+ // -abp-has-unsupported() is unknown. Ensure we fail parsing. |
+ selector = 'div[arial-label="Story"]:-abp-has(> div > div > span > span:-abp-unsupported("Suggested Post"))'; |
+ parsed = parseSelector(selector); |
+ test.equal(parsed.selectors, null); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+function buildDom(doc) |
+{ |
+ doc.body.innerHTML = `<div id="parent"> |
+ <div id="middle"> |
+ <div id="middle1"><div id="inside" class="inside"></div></div> |
+ </div> |
+ <div id="sibling"> |
+ <div id="tohide">to hide</div> |
+ </div> |
+ <div id="sibling2"> |
+ <div id="sibling21"><div id="sibling211" class="inside"></div></div> |
+ </div> |
+ </div>`; |
+ let parent = document.getElementById("parent"); |
+ let middle = document.getElementById("middle"); |
+ let middle1 = document.getElementById("middle1"); |
+ let inside = document.getElementById("inside"); |
+ let sibling = document.getElementById("sibling"); |
+ let sibling2 = document.getElementById("sibling2"); |
+ let toHide = document.getElementById("tohide"); |
+ return {parent, middle, middle1, inside, sibling, sibling2, toHide}; |
+} |
+ |
+exports.testPositionInParent = function(test) |
+{ |
+ let nodes = buildDom(document); |
+ |
+ loadElemHideEmulation().then(() => |
+ { |
+ test.equal(positionInParent(nodes.middle1), 1); |
+ test.equal(positionInParent(nodes.inside), 1); |
+ test.equal(positionInParent(nodes.sibling2), 3); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testMakeSelector = function(test) |
+{ |
+ let nodes = buildDom(document); |
+ |
+ loadElemHideEmulation().then(() => |
+ { |
+ test.equal(makeSelector(nodes.middle, ""), |
+ ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(1)"); |
+ test.equal(makeSelector(nodes.toHide, ""), |
+ ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(2) > DIV:nth-child(1)"); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testPlainSelector = function(test) |
+{ |
+ buildDom(document); |
+ |
+ loadElemHideEmulation().then(() => |
+ { |
+ let selector = new PlainSelector("div > div"); |
+ |
+ let iter = selector.getSelectors("foo > "); |
+ let value = iter.next(); |
+ test.equal(value.value[0], "foo > div > div"); |
+ test.ok(iter.next().done); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testHasSelector = function(test) |
+{ |
+ buildDom(document); |
+ |
+ loadElemHideEmulation().then(() => |
+ { |
+ let selector = new HasSelector("> div.inside"); |
+ |
+ let iter = selector.getSelectors("", document, document.sheet); |
+ let value = iter.next(); |
+ test.ok(!value.done); |
+ test.equal(value.value[0], |
+ ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(1) > DIV:nth-child(1)"); |
+ |
+ iter = selector.getElements("", document, document.sheet); |
+ value = iter.next(); |
+ test.ok(!value.done); |
+ test.equal(value.value.id, "middle1"); |
+ value = iter.next(); |
+ test.ok(!value.done); |
+ test.equal(value.value.id, "sibling21"); |
+ value = iter.next(); |
+ test.ok(value.done); |
+ |
+ selector = new HasSelector(":-abp-has(> div.inside)"); |
+ |
+ test.ok(selector._innerSelectors); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testSplitStyleRule = function(test) |
+{ |
+ loadElemHideEmulation().then(() => |
+ { |
+ let selectors = splitSelector("div:-abp-has(div) > [-abp-properties='background-color: rgb(0, 0, 0)'] > span"); |
+ test.ok(selectors); |
+ test.equal(selectors.length, 1, "There is only one selector"); |
+ |
+ selectors = splitSelector("div:-abp-has(div), [-abp-properties='background-color: rgb(0, 0, 0)']"); |
+ test.ok(selectors); |
+ test.equal(selectors.length, 2, "There are two selectors"); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
exports.testVerbatimPropertySelector = function(test) |
{ |
let toHide = createElementWithStyle("{background-color: #000}"); |
applyElemHideEmulation( |
[":-abp-properties(background-color: rgb(0, 0, 0))"] |
).then(() => |
{ |
expectHidden(test, toHide); |
}).catch(unexpectedError.bind(test)).then(() => test.done()); |
}; |
+exports.testVerbatimPropertySelectorWithPrefix = function(test) |
+{ |
+ let parent = createElementWithStyle("{background-color: #000}"); |
+ let toHide = createElementWithStyle("{background-color: #000}", parent); |
+ applyElemHideEmulation( |
+ ["div > :-abp-properties(background-color: rgb(0, 0, 0))"] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectHidden(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testVerbatimPropertySelectorWithPrefixNoMatch = function(test) |
+{ |
+ let parent = createElementWithStyle("{background-color: #000}"); |
+ let toHide = createElementWithStyle("{background-color: #fff}", parent); |
+ applyElemHideEmulation( |
+ ["div > :-abp-properties(background-color: rgb(0, 0, 0))"] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectVisible(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testVerbatimPropertySelectorWithSuffix = function(test) |
+{ |
+ let parent = createElementWithStyle("{background-color: #000}"); |
+ let toHide = createElementWithStyle("{background-color: #000}", parent); |
+ applyElemHideEmulation( |
+ [":-abp-properties(background-color: rgb(0, 0, 0)) > div"] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectHidden(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testVerbatimPropertyPseudoSelectorWithPrefixAndSuffix = function(test) |
+{ |
+ let parent = createElementWithStyle("{background-color: #000}"); |
+ let middle = createElementWithStyle("{background-color: #000}", parent); |
+ let toHide = createElementWithStyle("{background-color: #000}", middle); |
+ applyElemHideEmulation( |
+ ["div > :-abp-properties(background-color: rgb(0, 0, 0)) > div"] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectVisible(test, middle); |
+ expectHidden(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
exports.testPropertySelectorWithWildcard = function(test) |
{ |
let toHide = createElementWithStyle("{background-color: #000}"); |
applyElemHideEmulation( |
[":-abp-properties(*color: rgb(0, 0, 0))"] |
).then(() => |
{ |
expectHidden(test, toHide); |
@@ -183,8 +452,143 @@ |
window.setTimeout(() => |
{ |
expectHidden(test, toHide); |
resolve(); |
}, 0); |
}); |
}).catch(unexpectedError.bind(test)).then(() => test.done()); |
}; |
+ |
+exports.testPseudoClassHasSelector = function(test) |
+{ |
+ let toHide = createElementWithStyle("{}"); |
+ applyElemHideEmulation( |
+ ["div:-abp-has(div)"] |
+ ).then(() => |
+ { |
+ expectVisible(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testPseudoClassHasSelectorWithPrefix = function(test) |
+{ |
+ let parent = createElementWithStyle("{}"); |
+ let child = createElementWithStyle("{}", parent); |
+ applyElemHideEmulation( |
+ ["div:-abp-has(div)"] |
+ ).then(() => |
+ { |
+ expectHidden(test, parent); |
+ expectVisible(test, child); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testPseudoClassHasSelectorWithSuffix = function(test) |
+{ |
+ let parent = createElementWithStyle("{}"); |
+ let middle = createElementWithStyle("{}", parent); |
+ let child = createElementWithStyle("{}", middle); |
+ applyElemHideEmulation( |
+ ["div:-abp-has(div) > div"] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectHidden(test, middle); |
+ expectHidden(test, child); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testPseudoClassHasSelectorWithSuffixSibling = function(test) |
+{ |
+ let parent = createElementWithStyle("{}"); |
+ let middle = createElementWithStyle("{}", parent); |
+ let toHide = createElementWithStyle("{}"); |
+ applyElemHideEmulation( |
+ ["div:-abp-has(div) + div"] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectVisible(test, middle); |
+ expectHidden(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+exports.testPseudoClassHasSelectorWithSuffixSiblingChild = function(test) |
+{ |
+ // <div> |
+ // <div></div> |
+ // <div> |
+ // <div>to hide</div> |
+ // </div> |
+ // </div> |
+ let parent = createElementWithStyle("{}"); |
+ let middle = createElementWithStyle("{}", parent); |
+ let sibling = createElementWithStyle("{}"); |
+ let toHide = createElementWithStyle("{}", sibling); |
+ applyElemHideEmulation( |
+ ["div:-abp-has(div) + div > div"] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectVisible(test, middle); |
+ expectVisible(test, sibling); |
+ expectHidden(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |
+ |
+function runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, selector) |
+{ |
+ document.body.innerHTML = `<div id="parent"> |
+ <div id="middle"> |
+ <div id="middle1"><div id="inside" class="inside"></div></div> |
+ </div> |
+ <div id="sibling"> |
+ <div id="tohide">to hide</div> |
+ </div> |
+ <div id="sibling2"> |
+ <div id="sibling21"><div id="sibling211" class="inside"></div></div> |
+ </div> |
+ </div>`; |
+ let parent = document.getElementById("parent"); |
+ let middle = document.getElementById("middle"); |
+ let inside = document.getElementById("inside"); |
+ let sibling = document.getElementById("sibling"); |
+ let sibling2 = document.getElementById("sibling2"); |
+ let toHide = document.getElementById("tohide"); |
+ |
+ insertStyleRule(".inside {}"); |
+ |
+ applyElemHideEmulation( |
+ [selector] |
+ ).then(() => |
+ { |
+ expectVisible(test, parent); |
+ expectVisible(test, middle); |
+ expectVisible(test, inside); |
+ expectVisible(test, sibling); |
+ expectVisible(test, sibling2); |
+ expectHidden(test, toHide); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+} |
+ |
+exports.testPseudoClassHasSelectorWithHasAndWithSuffixSibling = function(test) |
+{ |
+ runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, "div:-abp-has(:-abp-has(div.inside)) + div > div"); |
+}; |
+ |
+exports.testPseudoClassHasSelectorWithHasAndWithSuffixSibling2 = function(test) |
+{ |
+ runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, "div:-abp-has(:-abp-has(> div.inside)) + div > div"); |
+}; |
+ |
+exports.testPseudoClassHasSelectorWithPropSelector = function(test) |
+{ |
+ let parent = createElementWithStyle("{}"); |
+ let child = createElementWithStyle("{background-color: #000}", parent); |
+ applyElemHideEmulation( |
+ ["div:-abp-has(:-abp-properties(background-color: rgb(0, 0, 0)))"] |
+ ).then(() => |
+ { |
+ expectVisible(test, child); |
+ expectHidden(test, parent); |
+ }).catch(unexpectedError.bind(test)).then(() => test.done()); |
+}; |