/services/sync/tps/extensions/mozmill/resource/modules/inspection.js

http://github.com/zpao/v8monkey · JavaScript · 396 lines · 81 code · 14 blank · 301 comment · 28 complexity · 00da627ef9594cef7ea5964e534abce3 MD5 · raw file

  1. // ***** BEGIN LICENSE BLOCK *****
  2. // Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. //
  4. // The contents of this file are subject to the Mozilla Public License Version
  5. // 1.1 (the "License"); you may not use this file except in compliance with
  6. // the License. You may obtain a copy of the License at
  7. // http://www.mozilla.org/MPL/
  8. //
  9. // Software distributed under the License is distributed on an "AS IS" basis,
  10. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. // for the specific language governing rights and limitations under the
  12. // License.
  13. //
  14. // The Original Code is Mozilla Corporation Code.
  15. //
  16. // The Initial Developer of the Original Code is
  17. // Mikeal Rogers.
  18. // Portions created by the Initial Developer are Copyright (C) 2008
  19. // the Initial Developer. All Rights Reserved.
  20. //
  21. // Contributor(s):
  22. // Mikeal Rogers <mikeal.rogers@gmail.com>
  23. //
  24. // Alternatively, the contents of this file may be used under the terms of
  25. // either the GNU General Public License Version 2 or later (the "GPL"), or
  26. // the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. // in which case the provisions of the GPL or the LGPL are applicable instead
  28. // of those above. If you wish to allow use of your version of this file only
  29. // under the terms of either the GPL or the LGPL, and not to allow others to
  30. // use your version of this file under the terms of the MPL, indicate your
  31. // decision by deleting the provisions above and replace them with the notice
  32. // and other provisions required by the GPL or the LGPL. If you do not delete
  33. // the provisions above, a recipient may use your version of this file under
  34. // the terms of any one of the MPL, the GPL or the LGPL.
  35. //
  36. // ***** END LICENSE BLOCK *****
  37. var EXPORTED_SYMBOLS = ["inspectElement"]
  38. var elementslib = {}; Components.utils.import('resource://mozmill/modules/elementslib.js', elementslib);
  39. var mozmill = {}; Components.utils.import('resource://mozmill/modules/mozmill.js', mozmill);
  40. var utils = {}; Components.utils.import('resource://mozmill/modules/utils.js', utils);
  41. var arrays = {}; Components.utils.import('resource://mozmill/stdlib/arrays.js', arrays);
  42. var dom = {}; Components.utils.import('resource://mozmill/stdlib/dom.js', dom);
  43. var objects = {}; Components.utils.import('resource://mozmill/stdlib/objects.js', objects);
  44. var json2 = {}; Components.utils.import('resource://mozmill/stdlib/json2.js', json2);
  45. var withs = {}; Components.utils.import('resource://mozmill/stdlib/withs.js', withs);
  46. var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  47. .getService(Components.interfaces.nsIWindowMediator);
  48. var isNotAnonymous = function (elem, result) {
  49. if (result == undefined) {
  50. var result = true;
  51. }
  52. if ( elem.parentNode ) {
  53. var p = elem.parentNode;
  54. return isNotAnonymous(p, result == arrays.inArray(p.childNodes, elem) == true);
  55. } else {
  56. return result;
  57. }
  58. }
  59. var elemIsAnonymous = function (elem) {
  60. if (elem.getAttribute('anonid') || !arrays.inArray(elem.parentNode.childNodes, elem)) {
  61. return true;
  62. }
  63. return false;
  64. }
  65. var getXPath = function (node, path) {
  66. path = path || [];
  67. if(node.parentNode) {
  68. path = getXPath(node.parentNode, path);
  69. }
  70. if(node.previousSibling) {
  71. var count = 1;
  72. var sibling = node.previousSibling
  73. do {
  74. if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
  75. sibling = sibling.previousSibling;
  76. } while(sibling);
  77. if(count == 1) {count = null;}
  78. } else if(node.nextSibling) {
  79. var sibling = node.nextSibling;
  80. do {
  81. if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
  82. var count = 1;
  83. sibling = null;
  84. } else {
  85. var count = null;
  86. sibling = sibling.previousSibling;
  87. }
  88. } while(sibling);
  89. }
  90. if(node.nodeType == 1) {
  91. // if ($('absXpaths').checked){
  92. path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
  93. // }
  94. // else{
  95. // path.push(node.nodeName.toLowerCase() + (node.id ? "" : count > 0 ? "["+count+"]" : ''));
  96. // }
  97. }
  98. return path;
  99. };
  100. function getXSPath(node){
  101. var xpArray = getXPath(node);
  102. var stringXpath = xpArray.join('/');
  103. stringXpath = '/'+stringXpath;
  104. stringXpath = stringXpath.replace('//','/');
  105. return stringXpath;
  106. }
  107. function getXULXpath (el, xml) {
  108. var xpath = '';
  109. var pos, tempitem2;
  110. while(el !== xml.documentElement) {
  111. pos = 0;
  112. tempitem2 = el;
  113. while(tempitem2) {
  114. if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) {
  115. // If it is ELEMENT_NODE of the same name
  116. pos += 1;
  117. }
  118. tempitem2 = tempitem2.previousSibling;
  119. }
  120. xpath = "*[name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;
  121. el = el.parentNode;
  122. }
  123. xpath = '/*'+"[name()='"+xml.documentElement.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']"+'/'+xpath;
  124. xpath = xpath.replace(/\/$/, '');
  125. return xpath;
  126. }
  127. var getDocument = function (elem) {
  128. while (elem.parentNode) {
  129. var elem = elem.parentNode;
  130. }
  131. return elem;
  132. }
  133. var getTopWindow = function(doc) {
  134. return utils.getChromeWindow(doc.defaultView);
  135. }
  136. var attributeToIgnore = ['focus', 'focused', 'selected', 'select', 'flex', // General Omissions
  137. 'linkedpanel', 'last-tab', 'afterselected', // From Tabs UI, thanks Farhad
  138. 'style', // Gets set dynamically all the time, also effected by dx display code
  139. ];
  140. var getUniqueAttributesReduction = function (attributes, node) {
  141. for (var i in attributes) {
  142. if ( node.getAttribute(i) == attributes[i] || arrays.inArray(attributeToIgnore, i) || arrays.inArray(attributeToIgnore, attributes[i]) || i == 'id') {
  143. delete attributes[i];
  144. }
  145. }
  146. return attributes;
  147. }
  148. var getLookupExpression = function (_document, elem) {
  149. expArray = [];
  150. while ( elem.parentNode ) {
  151. var exp = getLookupForElem(_document, elem);
  152. expArray.push(exp);
  153. var elem = elem.parentNode;
  154. }
  155. expArray.reverse();
  156. return '/' + expArray.join('/');
  157. }
  158. var getLookupForElem = function (_document, elem) {
  159. if ( !elemIsAnonymous(elem) ) {
  160. if (elem.id != "" && !withs.startsWith(elem.id, 'panel')) {
  161. identifier = {'name':'id', 'value':elem.id};
  162. } else if ((elem.name != "") && (typeof(elem.name) != "undefined")) {
  163. identifier = {'name':'name', 'value':elem.name};
  164. } else {
  165. identifier = null;
  166. }
  167. if (identifier) {
  168. var result = {'id':elementslib._byID, 'name':elementslib._byName}[identifier.name](_document, elem.parentNode, identifier.value);
  169. if ( typeof(result != 'array') ) {
  170. return identifier.name+'('+json2.JSON.stringify(identifier.value)+')';
  171. }
  172. }
  173. // At this point there is either no identifier or it returns multiple
  174. var parse = [n for each (n in elem.parentNode.childNodes) if
  175. (n.getAttribute && n != elem)
  176. ];
  177. parse.unshift(dom.getAttributes(elem));
  178. var uniqueAttributes = parse.reduce(getUniqueAttributesReduction);
  179. if (!result) {
  180. var result = elementslib._byAttrib(elem.parentNode, uniqueAttributes);
  181. }
  182. if (!identifier && typeof(result) == 'array' ) {
  183. return json2.JSON.stringify(uniqueAttributes) + '['+arrays.indexOf(result, elem)+']'
  184. } else {
  185. var aresult = elementslib._byAttrib(elem.parentNode, uniqueAttributes);
  186. if ( typeof(aresult != 'array') ) {
  187. if (objects.getLength(uniqueAttributes) == 0) {
  188. return '['+arrays.indexOf(elem.parentNode.childNodes, elem)+']'
  189. }
  190. return json2.JSON.stringify(uniqueAttributes)
  191. } else if ( result.length > aresult.length ) {
  192. return json2.JSON.stringify(uniqueAttributes) + '['+arrays.indexOf(aresult, elem)+']'
  193. } else {
  194. return identifier.name+'('+json2.JSON.stringify(identifier.value)+')' + '['+arrays.indexOf(result, elem)+']'
  195. }
  196. }
  197. } else {
  198. // Handle Anonymous Nodes
  199. var parse = [n for each (n in _document.getAnonymousNodes(elem.parentNode)) if
  200. (n.getAttribute && n != elem)
  201. ];
  202. parse.unshift(dom.getAttributes(elem));
  203. var uniqueAttributes = parse.reduce(getUniqueAttributesReduction);
  204. if (uniqueAttributes.anonid && typeof(elementslib._byAnonAttrib(_document,
  205. elem.parentNode, {'anonid':uniqueAttributes.anonid})) != 'array') {
  206. uniqueAttributes = {'anonid':uniqueAttributes.anonid};
  207. }
  208. if (objects.getLength(uniqueAttributes) == 0) {
  209. return 'anon(['+arrays.indexOf(_document.getAnonymousNodes(elem.parentNode), elem)+'])';
  210. } else if (arrays.inArray(uniqueAttributes, 'anonid')) {
  211. return 'anon({"anonid":"'+uniqueAttributes['anonid']+'"})';
  212. } else {
  213. return 'anon('+json2.JSON.stringify(uniqueAttributes)+')';
  214. }
  215. }
  216. return 'broken '+elemIsAnonymous(elem)
  217. }
  218. var removeHTMLTags = function(str){
  219. str = str.replace(/&(lt|gt);/g, function (strMatch, p1){
  220. return (p1 == "lt")? "<" : ">";
  221. });
  222. var strTagStrippedText = str.replace(/<\/?[^>]+(>|$)/g, "");
  223. strTagStrippedText = strTagStrippedText.replace(/&nbsp;/g,"");
  224. return strTagStrippedText;
  225. }
  226. var isMagicAnonymousDiv = function (_document, node) {
  227. if (node.getAttribute && node.getAttribute('class') == 'anonymous-div') {
  228. if (!arrays.inArray(node.parentNode.childNodes, node) && (_document.getAnonymousNodes(node) == null ||
  229. !arrays.inArray(_document.getAnonymousNodes(node), node) ) ) {
  230. return true;
  231. }
  232. }
  233. return false;
  234. }
  235. var copyToClipboard = function(str){
  236. const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"] .getService(Components.interfaces.nsIClipboardHelper);
  237. gClipboardHelper.copyString(str);
  238. }
  239. var getControllerAndDocument = function (_document, _window) {
  240. var windowtype = _window.document.documentElement.getAttribute('windowtype');
  241. var controllerString, documentString, activeTab;
  242. // TODO replace with object based cases
  243. switch(windowtype) {
  244. case 'navigator:browser':
  245. controllerString = 'mozmill.getBrowserController()';
  246. activeTab = mozmill.getBrowserController().tabs.activeTab;
  247. break;
  248. case 'Browser:Preferences':
  249. controllerString = 'mozmill.getPreferencesController()';
  250. break;
  251. case 'Extension:Manager':
  252. controllerString = 'mozmill.getAddonsController()';
  253. break;
  254. default:
  255. if(windowtype)
  256. controllerString = 'new mozmill.controller.MozMillController(mozmill.utils.getWindowByType("' + windowtype + '"))';
  257. else if(_window.document.title)
  258. controllerString = 'new mozmill.controller.MozMillController(mozmill.utils.getWindowByTitle("'+_window.document.title+'"))';
  259. else
  260. controllerString = 'Cannot find window';
  261. break;
  262. }
  263. if(activeTab == _document) {
  264. documentString = 'controller.tabs.activeTab';
  265. } else if(activeTab == _document.defaultView.top.document) {
  266. // if this document is from an iframe in the active tab
  267. var stub = getDocumentStub(_document, activeTab.defaultView);
  268. documentString = 'controller.tabs.activeTab.defaultView' + stub;
  269. } else {
  270. var stub = getDocumentStub(_document, _window);
  271. if(stub)
  272. documentString = 'controller.window' + stub;
  273. else
  274. documentString = 'Cannot find document';
  275. }
  276. return {'controllerString':controllerString, 'documentString':documentString}
  277. }
  278. getDocumentStub = function( _document, _window) {
  279. if(_window.document == _document)
  280. return '.document';
  281. for(var i = 0; i < _window.frames.length; i++) {
  282. var stub = getDocumentStub(_document, _window.frames[i]);
  283. if (stub)
  284. return '.frames['+i+']' + stub;
  285. }
  286. return '';
  287. }
  288. var inspectElement = function(e){
  289. if (e.originalTarget != undefined) {
  290. target = e.originalTarget;
  291. } else {
  292. target = e.target;
  293. }
  294. //Element highlighting
  295. try {
  296. if (this.lastEvent)
  297. this.lastEvent.target.style.outline = "";
  298. } catch(err) {}
  299. this.lastEvent = e;
  300. try {
  301. e.target.style.outline = "1px solid darkblue";
  302. } catch(err){}
  303. var _document = getDocument(target);
  304. if (isMagicAnonymousDiv(_document, target)) {
  305. target = target.parentNode;
  306. }
  307. var windowtype = _document.documentElement.getAttribute('windowtype');
  308. var _window = getTopWindow(_document);
  309. r = getControllerAndDocument(_document, _window);
  310. // displayText = "Controller: " + r.controllerString + '\n\n';
  311. if ( isNotAnonymous(target) ) {
  312. // Logic for which identifier to use is duplicated above
  313. if (target.id != "" && !withs.startsWith(target.id, 'panel')) {
  314. elemText = "new elementslib.ID("+ r.documentString + ', "' + target.id + '")';
  315. var telem = new elementslib.ID(_document, target.id);
  316. } else if ((target.name != "") && (typeof(target.name) != "undefined")) {
  317. elemText = "new elementslib.Name("+ r.documentString + ', "' + target.name + '")';
  318. var telem = new elementslib.Name(_document, target.name);
  319. } else if (target.nodeName == "A") {
  320. var linkText = removeHTMLTags(target.innerHTML);
  321. elemText = "new elementslib.Link("+ r.documentString + ', "' + linkText + '")';
  322. var telem = new elementslib.Link(_document, linkText);
  323. }
  324. }
  325. // Fallback on XPath
  326. if (telem == undefined || telem.getNode() != target) {
  327. if (windowtype == null) {
  328. var stringXpath = getXSPath(target);
  329. } else {
  330. var stringXpath = getXULXpath(target, _document);
  331. }
  332. var telem = new elementslib.XPath(_document, stringXpath);
  333. if ( telem.getNode() == target ) {
  334. elemText = "new elementslib.XPath("+ r.documentString + ', "' + stringXpath + '")';
  335. }
  336. }
  337. // Fallback to Lookup
  338. if (telem == undefined || telem.getNode() != target) {
  339. var exp = getLookupExpression(_document, target);
  340. elemText = "new elementslib.Lookup("+ r.documentString + ", '" + exp + "')";
  341. var telem = new elementslib.Lookup(_document, exp);
  342. }
  343. return {'validation':( target == telem.getNode() ),
  344. 'elementText':elemText,
  345. 'elementType':telem.constructor.name,
  346. 'controllerText':r.controllerString,
  347. 'documentString':r.documentString,
  348. }
  349. }