PageRenderTime 69ms CodeModel.GetById 22ms app.highlight 42ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 38var EXPORTED_SYMBOLS = ["inspectElement"]
 39
 40var elementslib = {}; Components.utils.import('resource://mozmill/modules/elementslib.js', elementslib);
 41var mozmill = {}; Components.utils.import('resource://mozmill/modules/mozmill.js', mozmill);
 42var utils = {}; Components.utils.import('resource://mozmill/modules/utils.js', utils);
 43
 44var arrays = {}; Components.utils.import('resource://mozmill/stdlib/arrays.js', arrays);
 45var dom = {}; Components.utils.import('resource://mozmill/stdlib/dom.js', dom);
 46var objects = {}; Components.utils.import('resource://mozmill/stdlib/objects.js', objects);
 47var json2 = {}; Components.utils.import('resource://mozmill/stdlib/json2.js', json2);
 48var withs = {}; Components.utils.import('resource://mozmill/stdlib/withs.js', withs);
 49
 50var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
 51           .getService(Components.interfaces.nsIWindowMediator);
 52
 53var isNotAnonymous = function (elem, result) {
 54  if (result == undefined) {
 55    var result = true;
 56  }
 57  if ( elem.parentNode ) {
 58    var p = elem.parentNode;
 59    return isNotAnonymous(p, result == arrays.inArray(p.childNodes, elem) == true);
 60  } else {
 61    return result;
 62  }
 63}
 64
 65var elemIsAnonymous = function (elem) {
 66  if (elem.getAttribute('anonid') || !arrays.inArray(elem.parentNode.childNodes, elem)) {
 67    return true;
 68  }
 69  return false;
 70}
 71
 72var getXPath = function (node, path) {
 73  path = path || [];
 74
 75  if(node.parentNode) {
 76    path = getXPath(node.parentNode, path);
 77  }
 78
 79  if(node.previousSibling) {
 80    var count = 1;
 81    var sibling = node.previousSibling
 82    do {
 83      if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
 84      sibling = sibling.previousSibling;
 85    } while(sibling);
 86    if(count == 1) {count = null;}
 87  } else if(node.nextSibling) {
 88    var sibling = node.nextSibling;
 89    do {
 90      if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
 91        var count = 1;
 92        sibling = null;
 93      } else {
 94        var count = null;
 95        sibling = sibling.previousSibling;
 96      }
 97    } while(sibling);
 98  }
 99
100  if(node.nodeType == 1) {
101 //   if ($('absXpaths').checked){
102      path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
103  //  }
104  //  else{
105  //    path.push(node.nodeName.toLowerCase() + (node.id ? "" : count > 0 ? "["+count+"]" : ''));
106  //  }
107  }
108  return path;
109};
110
111function getXSPath(node){
112  var xpArray = getXPath(node);
113  var stringXpath = xpArray.join('/');
114  stringXpath = '/'+stringXpath;
115  stringXpath = stringXpath.replace('//','/');
116  return stringXpath;
117}
118function getXULXpath (el, xml) {
119	var xpath = '';
120	var pos, tempitem2;
121
122	while(el !== xml.documentElement) {		
123		pos = 0;
124		tempitem2 = el;
125		while(tempitem2) {
126			if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) {
127			  // If it is ELEMENT_NODE of the same name
128				pos += 1;
129			}
130			tempitem2 = tempitem2.previousSibling;
131		}
132
133		xpath = "*[name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;
134
135		el = el.parentNode;
136	}
137	xpath = '/*'+"[name()='"+xml.documentElement.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']"+'/'+xpath;
138	xpath = xpath.replace(/\/$/, '');
139	return xpath;
140}
141
142var getDocument = function (elem) {
143  while (elem.parentNode) {
144    var elem = elem.parentNode;
145  }
146  return elem;
147}
148
149var getTopWindow = function(doc) {
150  return utils.getChromeWindow(doc.defaultView);
151}
152
153var attributeToIgnore = ['focus', 'focused', 'selected', 'select', 'flex', // General Omissions
154                         'linkedpanel', 'last-tab', 'afterselected', // From Tabs UI, thanks Farhad
155                         'style', // Gets set dynamically all the time, also effected by dx display code
156                         ];
157
158var getUniqueAttributesReduction = function (attributes, node) {
159  for (var i in attributes) {
160    if ( node.getAttribute(i) == attributes[i] || arrays.inArray(attributeToIgnore, i) || arrays.inArray(attributeToIgnore, attributes[i]) || i == 'id') {
161      delete attributes[i];
162    }
163  }
164  return attributes;
165}
166
167var getLookupExpression = function (_document, elem) {
168  expArray = [];
169  while ( elem.parentNode ) {
170    var exp = getLookupForElem(_document, elem);
171    expArray.push(exp);
172    var elem = elem.parentNode;
173  }
174  expArray.reverse();
175  return '/' + expArray.join('/');
176}
177
178var getLookupForElem = function (_document, elem) {
179  if ( !elemIsAnonymous(elem) ) {
180    if (elem.id != "" && !withs.startsWith(elem.id, 'panel')) {
181      identifier = {'name':'id', 'value':elem.id};
182    } else if ((elem.name != "") && (typeof(elem.name) != "undefined")) {
183      identifier = {'name':'name', 'value':elem.name};
184    } else {
185      identifier = null;
186    }
187
188    if (identifier) {
189      var result = {'id':elementslib._byID, 'name':elementslib._byName}[identifier.name](_document, elem.parentNode, identifier.value);
190      if ( typeof(result != 'array') ) {
191        return identifier.name+'('+json2.JSON.stringify(identifier.value)+')';
192      }
193    }
194
195    // At this point there is either no identifier or it returns multiple
196    var parse = [n for each (n in elem.parentNode.childNodes) if
197                 (n.getAttribute && n != elem)
198                 ];
199    parse.unshift(dom.getAttributes(elem));
200    var uniqueAttributes = parse.reduce(getUniqueAttributesReduction);
201
202    if (!result) {
203      var result = elementslib._byAttrib(elem.parentNode, uniqueAttributes);
204    }
205
206    if (!identifier && typeof(result) == 'array' ) {
207      return json2.JSON.stringify(uniqueAttributes) + '['+arrays.indexOf(result, elem)+']'
208    } else {
209      var aresult = elementslib._byAttrib(elem.parentNode, uniqueAttributes);
210      if ( typeof(aresult != 'array') ) {
211        if (objects.getLength(uniqueAttributes) == 0) {
212          return '['+arrays.indexOf(elem.parentNode.childNodes, elem)+']'
213        }
214        return json2.JSON.stringify(uniqueAttributes)
215      } else if ( result.length > aresult.length ) {
216        return json2.JSON.stringify(uniqueAttributes) + '['+arrays.indexOf(aresult, elem)+']'
217      } else {
218        return identifier.name+'('+json2.JSON.stringify(identifier.value)+')' + '['+arrays.indexOf(result, elem)+']'
219      }
220    }
221
222  } else {
223    // Handle Anonymous Nodes
224    var parse = [n for each (n in _document.getAnonymousNodes(elem.parentNode)) if
225                 (n.getAttribute && n != elem)
226                 ];
227    parse.unshift(dom.getAttributes(elem));
228    var uniqueAttributes = parse.reduce(getUniqueAttributesReduction);
229    if (uniqueAttributes.anonid && typeof(elementslib._byAnonAttrib(_document,
230        elem.parentNode, {'anonid':uniqueAttributes.anonid})) != 'array') {
231      uniqueAttributes = {'anonid':uniqueAttributes.anonid};
232    }
233
234    if (objects.getLength(uniqueAttributes) == 0) {
235      return 'anon(['+arrays.indexOf(_document.getAnonymousNodes(elem.parentNode), elem)+'])';
236    } else if (arrays.inArray(uniqueAttributes, 'anonid')) {
237      return 'anon({"anonid":"'+uniqueAttributes['anonid']+'"})';
238    } else {
239      return 'anon('+json2.JSON.stringify(uniqueAttributes)+')';
240    }
241
242  }
243  return 'broken '+elemIsAnonymous(elem)
244}
245
246var removeHTMLTags = function(str){
247 	 	str = str.replace(/&(lt|gt);/g, function (strMatch, p1){
248 		 	return (p1 == "lt")? "<" : ">";
249 		});
250 		var strTagStrippedText = str.replace(/<\/?[^>]+(>|$)/g, "");
251 		strTagStrippedText = strTagStrippedText.replace(/&nbsp;/g,"");
252	return strTagStrippedText;
253}
254
255var isMagicAnonymousDiv = function (_document, node) {
256  if (node.getAttribute && node.getAttribute('class') == 'anonymous-div') {
257    if (!arrays.inArray(node.parentNode.childNodes, node) && (_document.getAnonymousNodes(node) == null ||
258        !arrays.inArray(_document.getAnonymousNodes(node), node) ) ) {
259          return true;
260        }
261  }
262  return false;
263}
264
265var copyToClipboard = function(str){
266  const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"] .getService(Components.interfaces.nsIClipboardHelper);
267  gClipboardHelper.copyString(str);
268}
269
270var getControllerAndDocument = function (_document, _window) {
271  var windowtype = _window.document.documentElement.getAttribute('windowtype');
272  var controllerString, documentString, activeTab;
273
274  // TODO replace with object based cases
275  switch(windowtype) {
276    case 'navigator:browser':
277      controllerString = 'mozmill.getBrowserController()';
278      activeTab = mozmill.getBrowserController().tabs.activeTab;
279      break;
280    case 'Browser:Preferences':
281      controllerString = 'mozmill.getPreferencesController()';
282      break;
283    case 'Extension:Manager':
284      controllerString = 'mozmill.getAddonsController()';
285      break;
286    default:
287      if(windowtype)
288        controllerString = 'new mozmill.controller.MozMillController(mozmill.utils.getWindowByType("' + windowtype + '"))';
289      else if(_window.document.title)
290        controllerString = 'new mozmill.controller.MozMillController(mozmill.utils.getWindowByTitle("'+_window.document.title+'"))';
291      else
292        controllerString = 'Cannot find window';
293      break;
294  }
295
296  if(activeTab == _document) {
297    documentString = 'controller.tabs.activeTab';
298  } else if(activeTab == _document.defaultView.top.document) {
299    // if this document is from an iframe in the active tab
300    var stub = getDocumentStub(_document, activeTab.defaultView);
301    documentString = 'controller.tabs.activeTab.defaultView' + stub;
302  } else {
303    var stub = getDocumentStub(_document, _window);
304    if(stub)
305      documentString = 'controller.window' + stub;
306    else
307      documentString = 'Cannot find document';
308  }
309  return {'controllerString':controllerString, 'documentString':documentString}
310}
311
312getDocumentStub = function( _document, _window) {
313  if(_window.document == _document)
314    return '.document';
315  for(var i = 0; i < _window.frames.length; i++) {
316    var stub = getDocumentStub(_document, _window.frames[i]);
317    if (stub)
318      return '.frames['+i+']' + stub;
319  }
320  return '';
321}
322
323var inspectElement = function(e){
324  if (e.originalTarget != undefined) {
325    target = e.originalTarget;
326  } else {
327    target = e.target;
328  }
329
330  //Element highlighting
331  try {
332    if (this.lastEvent)
333      this.lastEvent.target.style.outline = "";
334  } catch(err) {}
335
336  this.lastEvent = e;
337
338  try {
339     e.target.style.outline = "1px solid darkblue";
340  } catch(err){}
341
342  var _document = getDocument(target);
343
344
345  if (isMagicAnonymousDiv(_document, target)) {
346    target = target.parentNode;
347  }
348
349  var windowtype = _document.documentElement.getAttribute('windowtype');
350  var _window = getTopWindow(_document);
351  r = getControllerAndDocument(_document, _window);
352
353  // displayText = "Controller: " + r.controllerString + '\n\n';
354  if ( isNotAnonymous(target) ) {
355    // Logic for which identifier to use is duplicated above
356    if (target.id != "" && !withs.startsWith(target.id, 'panel')) {
357      elemText = "new elementslib.ID("+ r.documentString + ', "' + target.id + '")';
358      var telem = new elementslib.ID(_document, target.id);
359    } else if ((target.name != "") && (typeof(target.name) != "undefined")) {
360      elemText = "new elementslib.Name("+ r.documentString + ', "' + target.name + '")';
361      var telem = new elementslib.Name(_document, target.name);
362    } else if (target.nodeName == "A") {
363      var linkText = removeHTMLTags(target.innerHTML);
364      elemText = "new elementslib.Link("+ r.documentString + ', "' + linkText + '")';
365      var telem = new elementslib.Link(_document, linkText);
366    }
367  }
368  // Fallback on XPath
369  if (telem == undefined || telem.getNode() != target) {
370    if (windowtype == null) {
371      var stringXpath = getXSPath(target);
372    } else {
373      var stringXpath = getXULXpath(target, _document);
374    }
375    var telem = new elementslib.XPath(_document, stringXpath);
376    if ( telem.getNode() == target ) {
377      elemText = "new elementslib.XPath("+ r.documentString + ', "' + stringXpath + '")';
378    }
379  }
380  // Fallback to Lookup
381  if (telem == undefined || telem.getNode() != target) {
382    var exp = getLookupExpression(_document, target);
383    elemText = "new elementslib.Lookup("+ r.documentString + ", '" + exp + "')";
384    var telem = new elementslib.Lookup(_document, exp);
385  }
386
387  return {'validation':( target == telem.getNode() ),
388          'elementText':elemText,
389          'elementType':telem.constructor.name,
390          'controllerText':r.controllerString,
391          'documentString':r.documentString,
392          }
393}
394
395
396