mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
d4eb7d5782
--HG-- extra : rebase_source : da55a4937383eda2baf7c9a362501da8ee664146
445 lines
12 KiB
JavaScript
445 lines
12 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
var EXPORTED_SYMBOLS = ["Elem", "ID", "Link", "XPath", "Selector", "Name", "Anon", "AnonXPath",
|
|
"Lookup", "_byID", "_byName", "_byAttrib", "_byAnonAttrib",
|
|
];
|
|
|
|
var utils = {}; Components.utils.import('resource://mozmill/modules/utils.js', utils);
|
|
var strings = {}; Components.utils.import('resource://mozmill/stdlib/strings.js', strings);
|
|
var arrays = {}; Components.utils.import('resource://mozmill/stdlib/arrays.js', arrays);
|
|
var json2 = {}; Components.utils.import('resource://mozmill/stdlib/json2.js', json2);
|
|
var withs = {}; Components.utils.import('resource://mozmill/stdlib/withs.js', withs);
|
|
var dom = {}; Components.utils.import('resource://mozmill/stdlib/dom.js', dom);
|
|
var objects = {}; Components.utils.import('resource://mozmill/stdlib/objects.js', objects);
|
|
|
|
var countQuotes = function(str){
|
|
var count = 0;
|
|
var i = 0;
|
|
while(i < str.length) {
|
|
i = str.indexOf('"', i);
|
|
if (i != -1) {
|
|
count++;
|
|
i++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
};
|
|
|
|
/**
|
|
* smartSplit()
|
|
*
|
|
* Takes a lookup string as input and returns
|
|
* a list of each node in the string
|
|
*/
|
|
var smartSplit = function (str) {
|
|
// Ensure we have an even number of quotes
|
|
if (countQuotes(str) % 2 != 0) {
|
|
throw new Error ("Invalid Lookup Expression");
|
|
}
|
|
|
|
/**
|
|
* This regex matches a single "node" in a lookup string.
|
|
* In otherwords, it matches the part between the two '/'s
|
|
*
|
|
* Regex Explanation:
|
|
* \/ - start matching at the first forward slash
|
|
* ([^\/"]*"[^"]*")* - match as many pairs of quotes as possible until we hit a slash (ignore slashes inside quotes)
|
|
* [^\/]* - match the remainder of text outside of last quote but before next slash
|
|
*/
|
|
var re = /\/([^\/"]*"[^"]*")*[^\/]*/g
|
|
var ret = []
|
|
var match = re.exec(str);
|
|
while (match != null) {
|
|
ret.push(match[0].replace(/^\//, ""));
|
|
match = re.exec(str);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
/**
|
|
* defaultDocuments()
|
|
*
|
|
* Returns a list of default documents in which to search for elements
|
|
* if no document is provided
|
|
*/
|
|
function defaultDocuments() {
|
|
var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator);
|
|
win = windowManager.getMostRecentWindow("navigator:browser");
|
|
return [win.gBrowser.selectedBrowser.contentDocument, win.document];
|
|
};
|
|
|
|
/**
|
|
* nodeSearch()
|
|
*
|
|
* Takes an optional document, callback and locator string
|
|
* Returns a handle to the located element or null
|
|
*/
|
|
function nodeSearch(doc, func, string) {
|
|
if (doc != undefined) {
|
|
var documents = [doc];
|
|
} else {
|
|
var documents = defaultDocuments();
|
|
}
|
|
var e = null;
|
|
var element = null;
|
|
//inline function to recursively find the element in the DOM, cross frame.
|
|
var search = function(win, func, string) {
|
|
if (win == null)
|
|
return;
|
|
|
|
//do the lookup in the current window
|
|
element = func.call(win, string);
|
|
|
|
if (!element || (element.length == 0)) {
|
|
var frames = win.frames;
|
|
for (var i=0; i < frames.length; i++) {
|
|
search(frames[i], func, string);
|
|
}
|
|
}
|
|
else { e = element; }
|
|
};
|
|
|
|
for (var i = 0; i < documents.length; ++i) {
|
|
var win = documents[i].defaultView;
|
|
search(win, func, string);
|
|
if (e) break;
|
|
}
|
|
return e;
|
|
};
|
|
|
|
/**
|
|
* Selector()
|
|
*
|
|
* Finds an element by selector string
|
|
*/
|
|
function Selector(_document, selector, index) {
|
|
if (selector == undefined) {
|
|
throw new Error('Selector constructor did not recieve enough arguments.');
|
|
}
|
|
this.selector = selector;
|
|
this.getNodeForDocument = function (s) {
|
|
return this.document.querySelectorAll(s);
|
|
};
|
|
var nodes = nodeSearch(_document, this.getNodeForDocument, this.selector);
|
|
return nodes ? nodes[index || 0] : null;
|
|
};
|
|
|
|
/**
|
|
* ID()
|
|
*
|
|
* Finds an element by ID
|
|
*/
|
|
function ID(_document, nodeID) {
|
|
if (nodeID == undefined) {
|
|
throw new Error('ID constructor did not recieve enough arguments.');
|
|
}
|
|
this.getNodeForDocument = function (nodeID) {
|
|
return this.document.getElementById(nodeID);
|
|
};
|
|
return nodeSearch(_document, this.getNodeForDocument, nodeID);
|
|
};
|
|
|
|
/**
|
|
* Link()
|
|
*
|
|
* Finds a link by innerHTML
|
|
*/
|
|
function Link(_document, linkName) {
|
|
if (linkName == undefined) {
|
|
throw new Error('Link constructor did not recieve enough arguments.');
|
|
}
|
|
|
|
this.getNodeForDocument = function (linkName) {
|
|
var getText = function(el){
|
|
var text = "";
|
|
if (el.nodeType == 3){ //textNode
|
|
if (el.data != undefined){
|
|
text = el.data;
|
|
} else {
|
|
text = el.innerHTML;
|
|
}
|
|
text = text.replace(/n|r|t/g, " ");
|
|
}
|
|
if (el.nodeType == 1){ //elementNode
|
|
for (var i = 0; i < el.childNodes.length; i++) {
|
|
var child = el.childNodes.item(i);
|
|
text += getText(child);
|
|
}
|
|
if (el.tagName == "P" || el.tagName == "BR" || el.tagName == "HR" || el.tagName == "DIV") {
|
|
text += "n";
|
|
}
|
|
}
|
|
return text;
|
|
};
|
|
|
|
//sometimes the windows won't have this function
|
|
try {
|
|
var links = this.document.getElementsByTagName('a'); }
|
|
catch(err){ // ADD LOG LINE mresults.write('Error: '+ err, 'lightred');
|
|
}
|
|
for (var i = 0; i < links.length; i++) {
|
|
var el = links[i];
|
|
//if (getText(el).indexOf(this.linkName) != -1) {
|
|
if (el.innerHTML.indexOf(linkName) != -1){
|
|
return el;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
return nodeSearch(_document, this.getNodeForDocument, linkName);
|
|
};
|
|
|
|
/**
|
|
* XPath()
|
|
*
|
|
* Finds an element by XPath
|
|
*/
|
|
function XPath(_document, expr) {
|
|
if (expr == undefined) {
|
|
throw new Error('XPath constructor did not recieve enough arguments.');
|
|
}
|
|
|
|
this.getNodeForDocument = function (s) {
|
|
var aNode = this.document;
|
|
var aExpr = s;
|
|
var xpe = null;
|
|
|
|
if (this.document.defaultView == null) {
|
|
xpe = new getMethodInWindows('XPathEvaluator')();
|
|
} else {
|
|
xpe = new this.document.defaultView.XPathEvaluator();
|
|
}
|
|
var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ? aNode.documentElement : aNode.ownerDocument.documentElement);
|
|
var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
|
|
var found = [];
|
|
var res;
|
|
while (res = result.iterateNext())
|
|
found.push(res);
|
|
return found[0];
|
|
};
|
|
return nodeSearch(_document, this.getNodeForDocument, expr);
|
|
};
|
|
|
|
/**
|
|
* Name()
|
|
*
|
|
* Finds an element by Name
|
|
*/
|
|
function Name(_document, nName) {
|
|
if (nName == undefined) {
|
|
throw new Error('Name constructor did not recieve enough arguments.');
|
|
}
|
|
this.getNodeForDocument = function (s) {
|
|
try{
|
|
var els = this.document.getElementsByName(s);
|
|
if (els.length > 0) { return els[0]; }
|
|
}
|
|
catch(err){};
|
|
return null;
|
|
};
|
|
return nodeSearch(_document, this.getNodeForDocument, nName);
|
|
};
|
|
|
|
|
|
var _returnResult = function (results) {
|
|
if (results.length == 0) {
|
|
return null
|
|
} else if (results.length == 1) {
|
|
return results[0];
|
|
} else {
|
|
return results;
|
|
}
|
|
}
|
|
var _forChildren = function (element, name, value) {
|
|
var results = [];
|
|
var nodes = [e for each (e in element.childNodes) if (e)]
|
|
for (var i in nodes) {
|
|
var n = nodes[i];
|
|
if (n[name] == value) {
|
|
results.push(n);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
var _forAnonChildren = function (_document, element, name, value) {
|
|
var results = [];
|
|
var nodes = [e for each (e in _document.getAnoymousNodes(element)) if (e)];
|
|
for (var i in nodes ) {
|
|
var n = nodes[i];
|
|
if (n[name] == value) {
|
|
results.push(n);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
var _byID = function (_document, parent, value) {
|
|
return _returnResult(_forChildren(parent, 'id', value));
|
|
}
|
|
var _byName = function (_document, parent, value) {
|
|
return _returnResult(_forChildren(parent, 'tagName', value));
|
|
}
|
|
var _byAttrib = function (parent, attributes) {
|
|
var results = [];
|
|
|
|
var nodes = parent.childNodes;
|
|
for (var i in nodes) {
|
|
var n = nodes[i];
|
|
requirementPass = 0;
|
|
requirementLength = 0;
|
|
for (var a in attributes) {
|
|
requirementLength++;
|
|
try {
|
|
if (n.getAttribute(a) == attributes[a]) {
|
|
requirementPass++;
|
|
}
|
|
} catch (err) {
|
|
// Workaround any bugs in custom attribute crap in XUL elements
|
|
}
|
|
}
|
|
if (requirementPass == requirementLength) {
|
|
results.push(n);
|
|
}
|
|
}
|
|
return _returnResult(results)
|
|
}
|
|
var _byAnonAttrib = function (_document, parent, attributes) {
|
|
var results = [];
|
|
|
|
if (objects.getLength(attributes) == 1) {
|
|
for (var i in attributes) {var k = i; var v = attributes[i]; }
|
|
var result = _document.getAnonymousElementByAttribute(parent, k, v)
|
|
if (result) {
|
|
return result;
|
|
|
|
}
|
|
}
|
|
var nodes = [n for each (n in _document.getAnonymousNodes(parent)) if (n.getAttribute)];
|
|
function resultsForNodes (nodes) {
|
|
for (var i in nodes) {
|
|
var n = nodes[i];
|
|
requirementPass = 0;
|
|
requirementLength = 0;
|
|
for (var a in attributes) {
|
|
requirementLength++;
|
|
if (n.getAttribute(a) == attributes[a]) {
|
|
requirementPass++;
|
|
}
|
|
}
|
|
if (requirementPass == requirementLength) {
|
|
results.push(n);
|
|
}
|
|
}
|
|
}
|
|
resultsForNodes(nodes)
|
|
if (results.length == 0) {
|
|
resultsForNodes([n for each (n in parent.childNodes) if (n != undefined && n.getAttribute)])
|
|
}
|
|
return _returnResult(results)
|
|
}
|
|
var _byIndex = function (_document, parent, i) {
|
|
if (parent instanceof Array) {
|
|
return parent[i];
|
|
}
|
|
return parent.childNodes[i];
|
|
}
|
|
var _anonByName = function (_document, parent, value) {
|
|
return _returnResult(_forAnonChildren(_document, parent, 'tagName', value));
|
|
}
|
|
var _anonByAttrib = function (_document, parent, value) {
|
|
return _byAnonAttrib(_document, parent, value);
|
|
}
|
|
var _anonByIndex = function (_document, parent, i) {
|
|
return _document.getAnonymousNodes(parent)[i];
|
|
}
|
|
|
|
/**
|
|
* Lookup()
|
|
*
|
|
* Finds an element by Lookup expression
|
|
*/
|
|
function Lookup (_document, expression) {
|
|
if (expression == undefined) {
|
|
throw new Error('Lookup constructor did not recieve enough arguments.');
|
|
}
|
|
|
|
var expSplit = [e for each (e in smartSplit(expression) ) if (e != '')];
|
|
expSplit.unshift(_document)
|
|
var nCases = {'id':_byID, 'name':_byName, 'attrib':_byAttrib, 'index':_byIndex};
|
|
var aCases = {'name':_anonByName, 'attrib':_anonByAttrib, 'index':_anonByIndex};
|
|
|
|
|
|
var reduceLookup = function (parent, exp) {
|
|
// Handle case where only index is provided
|
|
var cases = nCases;
|
|
|
|
// Handle ending index before any of the expression gets mangled
|
|
if (withs.endsWith(exp, ']')) {
|
|
var expIndex = json2.JSON.parse(strings.vslice(exp, '[', ']'));
|
|
}
|
|
// Handle anon
|
|
if (withs.startsWith(exp, 'anon')) {
|
|
var exp = strings.vslice(exp, '(', ')');
|
|
var cases = aCases;
|
|
}
|
|
if (withs.startsWith(exp, '[')) {
|
|
try {
|
|
var obj = json2.JSON.parse(strings.vslice(exp, '[', ']'));
|
|
} catch (err) {
|
|
throw new Error(err+'. String to be parsed was || '+strings.vslice(exp, '[', ']')+' ||');
|
|
}
|
|
var r = cases['index'](_document, parent, obj);
|
|
if (r == null) {
|
|
throw new Error('Expression "'+exp+'" returned null. Anonymous == '+(cases == aCases));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
for (var c in cases) {
|
|
if (withs.startsWith(exp, c)) {
|
|
try {
|
|
var obj = json2.JSON.parse(strings.vslice(exp, '(', ')'))
|
|
} catch(err) {
|
|
throw new Error(err+'. String to be parsed was || '+strings.vslice(exp, '(', ')')+' ||');
|
|
}
|
|
var result = cases[c](_document, parent, obj);
|
|
}
|
|
}
|
|
|
|
if (!result) {
|
|
if ( withs.startsWith(exp, '{') ) {
|
|
try {
|
|
var obj = json2.JSON.parse(exp)
|
|
} catch(err) {
|
|
throw new Error(err+'. String to be parsed was || '+exp+' ||');
|
|
}
|
|
|
|
if (cases == aCases) {
|
|
var result = _anonByAttrib(_document, parent, obj)
|
|
} else {
|
|
var result = _byAttrib(parent, obj)
|
|
}
|
|
}
|
|
if (!result) {
|
|
throw new Error('Expression "'+exp+'" returned null. Anonymous == '+(cases == aCases));
|
|
}
|
|
}
|
|
|
|
// Final return
|
|
if (expIndex) {
|
|
// TODO: Check length and raise error
|
|
return result[expIndex];
|
|
} else {
|
|
// TODO: Check length and raise error
|
|
return result;
|
|
}
|
|
// Maybe we should cause an exception here
|
|
return false;
|
|
};
|
|
return expSplit.reduce(reduceLookup);
|
|
};
|