in src/js/webview/CSSSelectorResolver.js [191:268]
static generateCandidatesRecursive(
element: Element,
currentLevel: number,
lastLevel: number
): string[] {
// Ends recursion when reaching the root or the lastLevel
if (currentLevel > lastLevel || element === document.body) {
return [];
}
let classes = element.classList;
let tagName = element.tagName.toLowerCase();
let id = element.getAttribute('id');
let candidates = new Set();
let selector = '';
// .class candidates
for (let className of classes) {
if (!className.startsWith('facebook-instant-articles-builder')) {
selector = '.' + className;
candidates.add(selector);
}
}
// tagName.class candidates
for (let className of classes) {
if (!className.startsWith('facebook-instant-articles-builder')) {
selector = tagName + '.' + className;
candidates.add(selector);
}
}
// #id candidate
if (id) {
selector = '#' + id;
candidates.add(selector);
}
// tagName candidate
selector = tagName;
candidates.add(selector);
// tagName#id candidate
if (id) {
selector = tagName + '.' + id;
candidates.add(selector);
}
// empty selector candidate (creating selectors with ancestors
// that omit the intermediate elements)
if (currentLevel > 0 && currentLevel < lastLevel) {
candidates.add('');
}
// Recursively find the candidates for the parentNode (1 level less deeply)
let parent = element.parentElement;
let parentCandidates = [];
if (parent != null) {
parentCandidates = this.generateCandidatesRecursive(
parent,
currentLevel + 1,
lastLevel
);
}
// candidates = candidates ∪ (parentCandidates × candidates)
let candidatesClone = [...candidates];
for (let parentCandidate of parentCandidates) {
for (let candidate of candidatesClone) {
let newCandidate = parentCandidate.trim() + ' ' + candidate.trim();
candidates.add(newCandidate.trim());
}
}
// return as array
return Array.from(candidates);
}