Bug 751637: Added keys helper to Marionette and removing sendKeysToElement atom; r=jgriffin
authorDavid Burns <dburns@mozilla.com>
Sun, 05 Aug 2012 12:49:42 -0700
changeset 101571 ef62a9a2ee425de0ae494159190d4e51eaf8f09a
parent 101570 fa77c8c2a3464037199d9333fa051d89857a6c52
child 101572 6beb523ebcb572d9f09b3416c2bf39dec2507540
push idunknown
push userunknown
push dateunknown
reviewersjgriffin
bugs751637
milestone17.0a1
Bug 751637: Added keys helper to Marionette and removing sendKeysToElement atom; r=jgriffin
testing/marionette/atoms/atoms.js
testing/marionette/client/marionette/keys.py
testing/marionette/client/marionette/marionette.py
testing/marionette/client/marionette/tests/unit/test_typing.py
testing/marionette/client/marionette/www/javascriptPage.html
testing/marionette/jar.mn
testing/marionette/marionette-actors.js
testing/marionette/marionette-listener.js
testing/marionette/marionette-sendkeys.js
--- a/testing/marionette/atoms/atoms.js
+++ b/testing/marionette/atoms/atoms.js
@@ -220,87 +220,8 @@ var m={7:"NoSuchElementError",8:"NoSuchF
 l.prototype.toString=function(){return"["+this.name+"] "+this.message};function n(a){this.stack=Error().stack||"";a&&(this.message=""+a)}f(n,Error);n.prototype.name="CustomError";function o(a,c){c.unshift(a);n.call(this,g.apply(null,c));c.shift()}f(o,n);o.prototype.name="AssertionError";function p(a,c){var b;a:if("string"==typeof a)b="string"!=typeof c||1!=c.length?-1:a.indexOf(c,0);else{for(b=0;b<a.length;b++)if(b in a&&a[b]===c)break a;b=-1}return 0<=b};if(!k["1.9.1"]){for(var q=0,r=(""+h).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),s="1.9.1".replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),t=Math.max(r.length,s.length),u=0;0==q&&u<t;u++){var v=r[u]||"",w=s[u]||"",x=RegExp("(\\d*)(\\D*)","g"),z=RegExp("(\\d*)(\\D*)","g");do{var A=x.exec(v)||["","",""],B=z.exec(w)||["","",""];if(0==A[0].length&&0==B[0].length)break;q=((0==A[1].length?0:parseInt(A[1],10))<(0==B[1].length?0:parseInt(B[1],10))?-1:(0==A[1].length?0:parseInt(A[1],10))>(0==B[1].length?
 0:parseInt(B[1],10))?1:0)||((0==A[2].length)<(0==B[2].length)?-1:(0==A[2].length)>(0==B[2].length)?1:0)||(A[2]<B[2]?-1:A[2]>B[2]?1:0)}while(0==q)}k["1.9.1"]=0<=q};(function(){var a=e.Components;if(!a)return!1;try{if(!a.classes)return!1}catch(c){return!1}var b=a.classes,a=a.interfaces;b["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);b["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();function C(a,c,b,d,y){this.b=!!c;if(a&&(this.a=a))this.c="number"==typeof d?d:1!=this.a.nodeType?0:this.b?-1:1;this.depth=void 0!=y?y:this.c||0;this.b&&(this.depth*=-1)}f(C,function(){});C.prototype.a=null;C.prototype.c=0;f(function(a,c,b,d){C.call(this,a,c,0,null,d)},C);var D={"class":"className",readonly:"readOnly"},E=["checked","disabled","draggable","hidden"],F="BUTTON,INPUT,OPTGROUP,OPTION,SELECT,TEXTAREA".split(",");function G(a){var c=a.tagName.toUpperCase();if(p(F,c)){var b;b=D.disabled||"disabled";var d=a[b];b=void 0===d&&p(E,b)?!1:d;a=b?!1:a.parentNode&&1==a.parentNode.nodeType&&"OPTGROUP"==c||"OPTION"==c?G(a.parentNode):!0}else a=!0;return a};var H=G,I=["_"],J=e;!(I[0]in J)&&J.execScript&&J.execScript("var "+I[0]);for(var K;I.length&&(K=I.shift());)!I.length&&void 0!==H?J[K]=H:J=J[K]?J[K]:J[K]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
 //isElementSelected
 var isElementSelected = function(){return function(){var f=!1,g=this;function h(a,b){function c(){}c.prototype=b.prototype;a.d=b.prototype;a.prototype=new c};function i(a,b){for(var c=1;c<arguments.length;c++)var d=(""+arguments[c]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a};var k,l="",m=/rv\:([^\);]+)(\)|;)/.exec(g.navigator?g.navigator.userAgent:null);k=l=m?m[1]:"";var n={};function o(a,b){this.code=a;this.message=b||"";this.name=p[a]||p[13];var c=Error(this.message);c.name=this.name;this.stack=c.stack||""}h(o,Error);
 var p={7:"NoSuchElementError",8:"NoSuchFrameError",9:"UnknownCommandError",10:"StaleElementReferenceError",11:"ElementNotVisibleError",12:"InvalidElementStateError",13:"UnknownError",15:"ElementNotSelectableError",19:"XPathLookupError",23:"NoSuchWindowError",24:"InvalidCookieDomainError",25:"UnableToSetCookieError",26:"ModalDialogOpenedError",27:"NoModalDialogOpenError",28:"ScriptTimeoutError",32:"InvalidSelectorError",33:"SqlDatabaseError",34:"MoveTargetOutOfBoundsError"};
 o.prototype.toString=function(){return"["+this.name+"] "+this.message};function q(a){this.stack=Error().stack||"";a&&(this.message=""+a)}h(q,Error);q.prototype.name="CustomError";function r(a,b){b.unshift(a);q.call(this,i.apply(null,b));b.shift()}h(r,q);r.prototype.name="AssertionError";if(!n["1.9.1"]){for(var s=0,t=(""+k).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),u="1.9.1".replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),v=Math.max(t.length,u.length),w=0;0==s&&w<v;w++){var x=t[w]||"",y=u[w]||"",z=RegExp("(\\d*)(\\D*)","g"),A=RegExp("(\\d*)(\\D*)","g");do{var B=z.exec(x)||["","",""],C=A.exec(y)||["","",""];if(0==B[0].length&&0==C[0].length)break;s=((0==B[1].length?0:parseInt(B[1],10))<(0==C[1].length?0:parseInt(C[1],10))?-1:(0==B[1].length?0:parseInt(B[1],10))>(0==C[1].length?
 0:parseInt(C[1],10))?1:0)||((0==B[2].length)<(0==C[2].length)?-1:(0==B[2].length)>(0==C[2].length)?1:0)||(B[2]<C[2]?-1:B[2]>C[2]?1:0)}while(0==s)}n["1.9.1"]=0<=s};var D={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},E={IMG:" ",BR:"\n"};function F(a,b,c){if(!(a.nodeName in D))if(3==a.nodeType)c?b.push((""+a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in E)b.push(E[a.nodeName]);else for(a=a.firstChild;a;)F(a,b,c),a=a.nextSibling};(function(){var a=g.Components;if(!a)return f;try{if(!a.classes)return f}catch(b){return f}var c=a.classes,a=a.interfaces;c["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);c["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();function G(a,b,c,d,e){this.b=!!b;if(a&&(this.a=a))this.c="number"==typeof d?d:1!=this.a.nodeType?0:this.b?-1:1;this.depth=void 0!=e?e:this.c||0;this.b&&(this.depth*=-1)}h(G,function(){});G.prototype.a=null;G.prototype.c=0;h(function(a,b,c,d){G.call(this,a,b,0,null,d)},G);function H(a,b){return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}function I(a){return H(a,"OPTION")?!0:H(a,"INPUT")?(a=a.type.toLowerCase(),"checkbox"==a||"radio"==a):f}var J={"class":"className",readonly:"readOnly"},K=["checked","disabled","draggable","hidden"];function L(a){if(I(a)){if(!I(a))throw new o(15,"Element is not selectable");var b="selected",c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";var c=b,d=J[c]||c,b=a[d],e;if(e=void 0===b){b:if("string"==typeof K)d="string"!=typeof d||1!=d.length?-1:K.indexOf(d,0);else{for(e=0;e<K.length;e++)if(e in K&&K[e]===d){d=e;break b}d=-1}e=0<=d}if(e)a=f;else{if(d="value"==c)if(d=H(a,"OPTION")){var j;c=c.toLowerCase();if(a.hasAttribute)j=a.hasAttribute(c);else try{j=a.attributes[c].specified}catch(P){j=
 f}d=!j}d&&(j=[],F(a,j,f),b=j.join(""));a=b}a=!!a}else a=f;return a}var M=["_"],N=g;!(M[0]in N)&&N.execScript&&N.execScript("var "+M[0]);for(var O;M.length&&(O=M.shift());)!M.length&&void 0!==L?N[O]=L:N=N[O]?N[O]:N[O]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
-//sendKeysToElement/type
-var sendKeysToElement = function(){return function(){function g(a){throw a;}var h=void 0,i=!0,k=null,l=!1;function n(a){return function(){return this[a]}}function o(a){return function(){return a}}var p,q=this;
-function aa(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
-else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function s(a){return a!==h}function ba(a){var b=aa(a);return"array"==b||"object"==b&&"number"==typeof a.length}function t(a){return"string"==typeof a}function u(a){return"function"==aa(a)}function ca(a){a=aa(a);return"object"==a||"array"==a||"function"==a}var da="closure_uid_"+Math.floor(2147483648*Math.random()).toString(36),ea=0,fa=Date.now||function(){return+new Date};
-function x(a,b){function c(){}c.prototype=b.prototype;a.aa=b.prototype;a.prototype=new c};function ga(a,b){for(var c=1;c<arguments.length;c++)var d=(""+arguments[c]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a}function ha(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function ia(a){if(!ja.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ka,"&amp;"));-1!=a.indexOf("<")&&(a=a.replace(la,"&lt;"));-1!=a.indexOf(">")&&(a=a.replace(ma,"&gt;"));-1!=a.indexOf('"')&&(a=a.replace(na,"&quot;"));return a}var ka=/&/g,la=/</g,ma=/>/g,na=/\"/g,ja=/[&<>\"]/;
-function oa(a,b){for(var c=0,d=ha(""+a).split("."),e=ha(""+b).split("."),f=Math.max(d.length,e.length),j=0;0==c&&j<f;j++){var m=d[j]||"",r=e[j]||"",O=RegExp("(\\d*)(\\D*)","g"),G=RegExp("(\\d*)(\\D*)","g");do{var v=O.exec(m)||["","",""],w=G.exec(r)||["","",""];if(0==v[0].length&&0==w[0].length)break;c=((0==v[1].length?0:parseInt(v[1],10))<(0==w[1].length?0:parseInt(w[1],10))?-1:(0==v[1].length?0:parseInt(v[1],10))>(0==w[1].length?0:parseInt(w[1],10))?1:0)||((0==v[2].length)<(0==w[2].length)?-1:(0==
-v[2].length)>(0==w[2].length)?1:0)||(v[2]<w[2]?-1:v[2]>w[2]?1:0)}while(0==c)}return c}var pa=2147483648*Math.random()|0,qa={};function ra(a){return qa[a]||(qa[a]=(""+a).replace(/\-([a-z])/g,function(a,c){return c.toUpperCase()}))};var sa,ta;function ua(){return q.navigator?q.navigator.userAgent:k}var va,wa=q.navigator;va=wa&&wa.platform||"";sa=-1!=va.indexOf("Mac");ta=-1!=va.indexOf("Win");var xa=-1!=va.indexOf("Linux"),ya,za="",Aa=/rv\:([^\);]+)(\)|;)/.exec(ua());ya=za=Aa?Aa[1]:"";var Ba={};var Ca=window;function Da(a,b){for(var c in a)b.call(h,a[c],c,a)}function Ea(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b};function y(a,b){this.code=a;this.message=b||"";this.name=Fa[a]||Fa[13];var c=Error(this.message);c.name=this.name;this.stack=c.stack||""}x(y,Error);
-var Fa={7:"NoSuchElementError",8:"NoSuchFrameError",9:"UnknownCommandError",10:"StaleElementReferenceError",11:"ElementNotVisibleError",12:"InvalidElementStateError",13:"UnknownError",15:"ElementNotSelectableError",19:"XPathLookupError",23:"NoSuchWindowError",24:"InvalidCookieDomainError",25:"UnableToSetCookieError",26:"ModalDialogOpenedError",27:"NoModalDialogOpenError",28:"ScriptTimeoutError",32:"InvalidSelectorError",33:"SqlDatabaseError",34:"MoveTargetOutOfBoundsError"};
-y.prototype.toString=function(){return"["+this.name+"] "+this.message};function Ga(a){this.stack=Error().stack||"";a&&(this.message=""+a)}x(Ga,Error);Ga.prototype.name="CustomError";function Ha(a,b){b.unshift(a);Ga.call(this,ga.apply(k,b));b.shift()}x(Ha,Ga);Ha.prototype.name="AssertionError";function Ia(a,b,c){if(!a){var d=Array.prototype.slice.call(arguments,2),e="Assertion failed";if(b)var e=e+(": "+b),f=d;g(new Ha(""+e,f||[]))}}function Ja(a,b){g(new Ha("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1)))};function z(a){return a[a.length-1]}var Ka=Array.prototype;function A(a,b){if(t(a))return!t(b)||1!=b.length?-1:a.indexOf(b,0);for(var c=0;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1}function La(a,b){for(var c=a.length,d=t(a)?a.split(""):a,e=0;e<c;e++)e in d&&b.call(h,d[e],e,a)}function Ma(a,b){for(var c=a.length,d=Array(c),e=t(a)?a.split(""):a,f=0;f<c;f++)f in e&&(d[f]=b.call(h,e[f],f,a));return d}
-function Na(a,b,c){for(var d=a.length,e=t(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return i;return l}function Oa(a,b,c){for(var d=a.length,e=t(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return l;return i}function Pa(a,b){var c;a:{c=a.length;for(var d=t(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(h,d[e],e,a)){c=e;break a}c=-1}return 0>c?k:t(a)?a.charAt(c):a[c]}function Qa(a){return Ka.concat.apply(Ka,arguments)}
-function Ra(a){if("array"==aa(a))return Qa(a);for(var b=[],c=0,d=a.length;c<d;c++)b[c]=a[c];return b}function Sa(a,b,c){Ia(a.length!=k);return 2>=arguments.length?Ka.slice.call(a,b):Ka.slice.call(a,b,c)};var Ta;Ba["1.9.1"]||(Ba["1.9.1"]=0<=oa(ya,"1.9.1"));function Ua(a,b){var c;c=(c=a.className)&&"function"==typeof c.split?c.split(/\s+/):[];var d=Sa(arguments,1),e;e=c;for(var f=0,j=0;j<d.length;j++)0<=A(e,d[j])||(e.push(d[j]),f++);e=f==d.length;a.className=c.join(" ");return e};function B(a,b){this.x=s(a)?a:0;this.y=s(b)?b:0}B.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function Va(a,b){this.width=a;this.height=b}Va.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};Va.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};Va.prototype.scale=function(a){this.width*=a;this.height*=a;return this};var C=3;function Wa(a){return a?new Xa(D(a)):Ta||(Ta=new Xa)}function Ya(a,b){Da(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in Za?a.setAttribute(Za[d],b):0==d.lastIndexOf("aria-",0)?a.setAttribute(d,b):a[d]=b})}var Za={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",rowspan:"rowSpan",valign:"vAlign",height:"height",width:"width",usemap:"useMap",frameborder:"frameBorder",maxlength:"maxLength",type:"type"};
-function E(a){return a?a.parentWindow||a.defaultView:window}function $a(a,b,c){function d(c){c&&b.appendChild(t(c)?a.createTextNode(c):c)}for(var e=2;e<c.length;e++){var f=c[e];ba(f)&&!(ca(f)&&0<f.nodeType)?La(ab(f)?Ra(f):f,d):d(f)}}function bb(a){return a&&a.parentNode?a.parentNode.removeChild(a):k}
-function F(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}
-function cb(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if("sourceIndex"in a||a.parentNode&&"sourceIndex"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?db(a,b):!c&&F(e,b)?-1*eb(a,b):!d&&F(f,a)?eb(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=D(a);c=d.createRange();c.selectNode(a);c.collapse(i);d=d.createRange();d.selectNode(b);d.collapse(i);
-return c.compareBoundaryPoints(q.Range.START_TO_END,d)}function eb(a,b){var c=a.parentNode;if(c==b)return-1;for(var d=b;d.parentNode!=c;)d=d.parentNode;return db(d,a)}function db(a,b){for(var c=b;c=c.previousSibling;)if(c==a)return-1;return 1}
-function fb(a){var b,c=arguments.length;if(c){if(1==c)return arguments[0]}else return k;var d=[],e=Infinity;for(b=0;b<c;b++){for(var f=[],j=arguments[b];j;)f.unshift(j),j=j.parentNode;d.push(f);e=Math.min(e,f.length)}f=k;for(b=0;b<e;b++){for(var j=d[0][b],m=1;m<c;m++)if(j!=d[m][b])return f;f=j}return f}function D(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function gb(a,b){var c=[];return hb(a,b,c,i)?c[0]:h}
-function hb(a,b,c,d){if(a!=k)for(a=a.firstChild;a;){if(b(a)&&(c.push(a),d)||hb(a,b,c,d))return i;a=a.nextSibling}return l}var ib={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},jb={IMG:" ",BR:"\n"};function kb(a,b,c){if(!(a.nodeName in ib))if(a.nodeType==C)c?b.push((""+a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in jb)b.push(jb[a.nodeName]);else for(a=a.firstChild;a;)kb(a,b,c),a=a.nextSibling}
-function ab(a){if(a&&"number"==typeof a.length){if(ca(a))return"function"==typeof a.item||"string"==typeof a.item;if(u(a))return"function"==typeof a.item}return l}function lb(a,b){for(var a=a.parentNode,c=0;a;){if(b(a))return a;a=a.parentNode;c++}return k}function Xa(a){this.A=a||q.document||document}p=Xa.prototype;p.ha=n("A");p.q=function(a){return t(a)?this.A.getElementById(a):a};
-p.fa=function(a,b,c){var d=this.A,e=arguments,f=e[1],j=d.createElement(e[0]);f&&(t(f)?j.className=f:"array"==aa(f)?Ua.apply(k,[j].concat(f)):Ya(j,f));2<e.length&&$a(d,j,e);return j};p.createElement=function(a){return this.A.createElement(a)};p.createTextNode=function(a){return this.A.createTextNode(a)};p.ua=function(){return this.A.parentWindow||this.A.defaultView};
-function mb(a){var b=a.A,a="CSS1Compat"==b.compatMode?b.documentElement:b.body,b=b.parentWindow||b.defaultView;return new B(b.pageXOffset||a.scrollLeft,b.pageYOffset||a.scrollTop)}p.appendChild=function(a,b){a.appendChild(b)};p.removeNode=bb;p.contains=F;var H={};H.Ba=function(){var a={Ra:"http://www.w3.org/2000/svg"};return function(b){return a[b]||k}}();H.qa=function(a,b,c){var d=D(a);if(!d.implementation.hasFeature("XPath","3.0"))return k;try{var e=d.createNSResolver?d.createNSResolver(d.documentElement):H.Ba;return d.evaluate(b,a,e,c,k)}catch(f){"NS_ERROR_ILLEGAL_VALUE"!=f.name&&g(new y(32,"Unable to locate an element with the xpath expression "+b+" because of the following error:\n"+f))}};
-H.oa=function(a,b){(!a||1!=a.nodeType)&&g(new y(32,'The result of the xpath expression "'+b+'" is: '+a+". It should be an element."))};H.Ka=function(a,b){var c=function(){var c=H.qa(b,a,9);return c?c.singleNodeValue||k:b.selectSingleNode?(c=D(b),c.setProperty&&c.setProperty("SelectionLanguage","XPath"),b.selectSingleNode(a)):k}();c===k||H.oa(c,a);return c};
-H.Qa=function(a,b){var c=function(){var c=H.qa(b,a,7);if(c){for(var e=c.snapshotLength,f=[],j=0;j<e;++j)f.push(c.snapshotItem(j));return f}return b.selectNodes?(c=D(b),c.setProperty&&c.setProperty("SelectionLanguage","XPath"),b.selectNodes(a)):[]}();La(c,function(b){H.oa(b,a)});return c};var nb,ob="",pb=/Firefox\/([0-9.]+)/.exec(ua());nb=ob=pb?pb[2]||pb[1]:"";var qb=k,rb=function(){var a=q.Components;if(!a)return l;try{if(!a.classes)return l}catch(b){return l}var c=a.classes,a=a.interfaces,d=c["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator),e=c["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo).version;qb=function(a){return 0<=d.Ma(e,""+a)};return i}();var I="StopIteration"in q?q.StopIteration:Error("StopIteration");function J(){}J.prototype.next=function(){g(I)};J.prototype.t=function(){return this};function sb(a){if(a instanceof J)return a;if("function"==typeof a.t)return a.t(l);if(ba(a)){var b=0,c=new J;c.next=function(){for(;;){b>=a.length&&g(I);if(b in a)return a[b++];b++}};return c}g(Error("Not implemented"))};function K(a,b,c,d,e){this.p=!!b;a&&L(this,a,d);this.depth=e!=h?e:this.s||0;this.p&&(this.depth*=-1);this.Ca=!c}x(K,J);p=K.prototype;p.r=k;p.s=0;p.ma=l;function L(a,b,c,d){if(a.r=b)a.s="number"==typeof c?c:1!=a.r.nodeType?0:a.p?-1:1;"number"==typeof d&&(a.depth=d)}
-p.next=function(){var a;if(this.ma){(!this.r||this.Ca&&0==this.depth)&&g(I);a=this.r;var b=this.p?-1:1;if(this.s==b){var c=this.p?a.lastChild:a.firstChild;c?L(this,c):L(this,a,-1*b)}else(c=this.p?a.previousSibling:a.nextSibling)?L(this,c):L(this,a.parentNode,-1*b);this.depth+=this.s*(this.p?-1:1)}else this.ma=i;(a=this.r)||g(I);return a};
-p.splice=function(a){var b=this.r,c=this.p?1:-1;this.s==c&&(this.s=-1*c,this.depth+=this.s*(this.p?-1:1));this.p=!this.p;K.prototype.next.call(this);this.p=!this.p;for(var c=ba(arguments[0])?arguments[0]:arguments,d=c.length-1;0<=d;d--)b.parentNode&&b.parentNode.insertBefore(c[d],b.nextSibling);bb(b)};function tb(a,b,c,d){K.call(this,a,b,c,k,d)}x(tb,K);tb.prototype.next=function(){do tb.aa.next.call(this);while(-1==this.s);return this.r};function ub(a,b){var c=D(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,k))?c[b]||c.getPropertyValue(b):""}function vb(a,b){return ub(a,b)||(a.currentStyle?a.currentStyle[b]:k)||a.style&&a.style[b]}
-function wb(a){for(var b=D(a),c=vb(a,"position"),d="fixed"==c||"absolute"==c,a=a.parentNode;a&&a!=b;a=a.parentNode)if(c=vb(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return k}
-function xb(a){var b=new B;if(1==a.nodeType)if(a.getBoundingClientRect)a=a.getBoundingClientRect(),b.x=a.left,b.y=a.top;else{var c=mb(Wa(a));var d,e=D(a),f=vb(a,"position"),j=e.getBoxObjectFor&&!a.getBoundingClientRect&&"absolute"==f&&(d=e.getBoxObjectFor(a))&&(0>d.screenX||0>d.screenY),f=new B(0,0),m=(e?9==e.nodeType?e:D(e):document).documentElement;if(a!=m)if(a.getBoundingClientRect)d=a.getBoundingClientRect(),a=mb(Wa(e)),f.x=d.left+a.x,f.y=d.top+a.y;else if(e.getBoxObjectFor&&!j)d=e.getBoxObjectFor(a),
-a=e.getBoxObjectFor(m),f.x=d.screenX-a.screenX,f.y=d.screenY-a.screenY;else{d=a;do f.x+=d.offsetLeft,f.y+=d.offsetTop,d!=a&&(f.x+=d.clientLeft||0,f.y+=d.clientTop||0),d=d.offsetParent;while(d&&d!=a);for(d=a;(d=wb(d))&&d!=e.body&&d!=m;)f.x-=d.scrollLeft,f.y-=d.scrollTop}b.x=f.x-c.x;b.y=f.y-c.y}else c=u(a.ta),d=a,a.targetTouches?d=a.targetTouches[0]:c&&a.ta().targetTouches&&(d=a.ta().targetTouches[0]),b.x=d.clientX,b.y=d.clientY;return b}
-function yb(a){var b=a.offsetWidth,c=a.offsetHeight;return!s(b)&&a.getBoundingClientRect?(a=a.getBoundingClientRect(),new Va(a.right-a.left,a.bottom-a.top)):new Va(b,c)};function M(a,b){return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}var zb={"class":"className",readonly:"readOnly"},Ab=["checked","disabled","draggable","hidden"];function Bb(a,b){var c=zb[b]||b,d=a[c];if(!s(d)&&0<=A(Ab,c))return l;if(c="value"==b)if(c=M(a,"OPTION")){var e;c=b.toLowerCase();if(a.hasAttribute)e=a.hasAttribute(c);else try{e=a.attributes[c].specified}catch(f){e=l}c=!e}c&&(d=[],kb(a,d,l),d=d.join(""));return d}
-var Cb="async,autofocus,autoplay,checked,compact,complete,controls,declare,defaultchecked,defaultselected,defer,disabled,draggable,ended,formnovalidate,hidden,indeterminate,iscontenteditable,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,paused,pubdate,readonly,required,reversed,scoped,seamless,seeking,selected,spellcheck,truespeed,willvalidate".split(","),Db="BUTTON,INPUT,OPTGROUP,OPTION,SELECT,TEXTAREA".split(",");
-function Eb(a){var b=a.tagName.toUpperCase();return!(0<=A(Db,b))?i:Bb(a,"disabled")?l:a.parentNode&&1==a.parentNode.nodeType&&"OPTGROUP"==b||"OPTION"==b?Eb(a.parentNode):i}var Fb="text,search,tel,url,email,password,number".split(",");function Gb(a){return M(a,"TEXTAREA")?i:M(a,"INPUT")?0<=A(Fb,a.type.toLowerCase()):Hb(a)?i:l}
-function Hb(a){function b(a){return"inherit"==a.contentEditable?(a=Ib(a))?b(a):l:"true"==a.contentEditable}return!s(a.contentEditable)?l:s(a.isContentEditable)?a.isContentEditable:b(a)}function Ib(a){for(a=a.parentNode;a&&1!=a.nodeType&&9!=a.nodeType&&11!=a.nodeType;)a=a.parentNode;return M(a)?a:k}function Jb(a,b){b=ra(b);return ub(a,b)||Kb(a,b)}
-function Kb(a,b){var c=a.currentStyle||a.style,d=c[b];!s(d)&&u(c.getPropertyValue)&&(d=c.getPropertyValue(b));return"inherit"!=d?s(d)?d:k:(c=Ib(a))?Kb(c,b):k}function Lb(a){if(u(a.getBBox))try{var b=a.getBBox();if(b)return b}catch(c){}if("none"!=vb(a,"display"))a=yb(a);else{var b=a.style,d=b.display,e=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";a=yb(a);b.display=d;b.position=f;b.visibility=e}return a}
-function N(a,b){function c(a){if("none"==Jb(a,"display"))return l;a=Ib(a);return!a||c(a)}function d(a){var b=Lb(a);return 0<b.height&&0<b.width?i:Na(a.childNodes,function(a){return a.nodeType==C||M(a)&&d(a)})}function e(a){var b=Ib(a);if(b&&"hidden"==Jb(b,"overflow")){var c=Lb(b),d=xb(b),a=xb(a);return d.x+c.width<a.x||d.y+c.height<a.y?l:e(b)}return i}M(a)||g(Error("Argument to isShown must be of type Element"));if(M(a,"OPTION")||M(a,"OPTGROUP")){var f=lb(a,function(a){return M(a,"SELECT")});return!!f&&
-N(f,i)}if(M(a,"MAP")){if(!a.name)return l;f=D(a);f=f.evaluate?H.Ka('/descendant::*[@usemap = "#'+a.name+'"]',f):gb(f,function(b){var c;if(c=M(b))8==b.nodeType?b=k:(c="usemap","style"==c?(b=ha(b.style.cssText).toLowerCase(),b=";"==b.charAt(b.length-1)?b:b+";"):(b=b.getAttributeNode(c),b=!b?k:0<=A(Cb,c)?"true":b.specified?b.value:k)),c=b=="#"+a.name;return c});return!!f&&N(f,b)}return M(a,"AREA")?(f=lb(a,function(a){return M(a,"MAP")}),!!f&&N(f,b)):M(a,"INPUT")&&"hidden"==a.type.toLowerCase()||M(a,
-"NOSCRIPT")||"hidden"==Jb(a,"visibility")||!c(a)||!b&&0==Mb(a)||!d(a)||!e(a)?l:i}function Mb(a){var b=1,c=Jb(a,"opacity");c&&(b=Number(c));(a=Ib(a))&&(b*=Mb(a));return b};function P(){this.v=Ca.document.documentElement;this.xa=k;var a=D(this.v).activeElement;a&&Nb(this,a)}P.prototype.q=n("v");function Nb(a,b){a.v=b;a.xa=M(b,"OPTION")?lb(b,function(a){return M(a,"SELECT")}):k}
-function Ob(a,b,c,d,e,f){function j(a,c){var d={identifier:a,screenX:c.x,screenY:c.y,clientX:c.x,clientY:c.y,pageX:c.x,pageY:c.y};m.changedTouches.push(d);if(b==Pb||b==Qb)m.touches.push(d),m.targetTouches.push(d)}var m={touches:[],targetTouches:[],changedTouches:[],altKey:l,ctrlKey:l,shiftKey:l,metaKey:l,relatedTarget:k,scale:0,rotation:0};j(c,d);s(e)&&j(e,f);Rb(a.v,b,m)}rb&&rb&&(rb?qb(4):oa(nb,4));rb&&(rb?qb(4):oa(nb,4));function Q(a,b,c){this.L=a;this.U=b;this.V=c}Q.prototype.create=function(a){a=D(a).createEvent("HTMLEvents");a.initEvent(this.L,this.U,this.V);return a};Q.prototype.toString=n("L");function R(a,b,c){Q.call(this,a,b,c)}x(R,Q);
-R.prototype.create=function(a,b){var c=D(a),d=E(c),c=c.createEvent("MouseEvents"),e=1;this==Sb&&(e=b.wheelDelta/-40);this==Tb&&(e=b.wheelDelta);c.initMouseEvent(this.L,this.U,this.V,d,e,0,0,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget);return c};function Ub(a,b,c){Q.call(this,a,b,c)}x(Ub,Q);
-Ub.prototype.create=function(a,b){var c=D(a),d=E(c),e=b.charCode?0:b.keyCode,c=c.createEvent("KeyboardEvent");c.initKeyEvent(this.L,this.U,this.V,d,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,e,b.charCode);this.L==Vb&&b.preventDefault&&c.preventDefault();return c};function Wb(a,b,c){Q.call(this,a,b,c)}x(Wb,Q);
-Wb.prototype.create=function(a,b){function c(b){var c=Ma(b,function(b){return{identifier:b.identifier,screenX:b.screenX,screenY:b.screenY,clientX:b.clientX,clientY:b.clientY,pageX:b.pageX,pageY:b.pageY,target:a}});c.item=function(a){return c[a]};return c}var d=D(a),e=E(d),f=c(b.changedTouches),j=b.touches==b.changedTouches?f:c(b.touches),m=b.targetTouches==b.changedTouches?f:c(b.targetTouches),d=d.createEvent("MouseEvents");d.initMouseEvent(this.L,this.U,this.V,e,1,0,0,b.clientX,b.clientY,b.ctrlKey,
-b.altKey,b.shiftKey,b.metaKey,0,b.relatedTarget);d.touches=j;d.targetTouches=m;d.changedTouches=f;d.scale=b.scale;d.rotation=b.rotation;return d};
-var Xb=new R("click",i,i),Yb=new R("contextmenu",i,i),Zb=new R("dblclick",i,i),$b=new R("mousedown",i,i),ac=new R("mousemove",i,l),bc=new R("mouseout",i,i),cc=new R("mouseover",i,i),dc=new R("mouseup",i,i),Sb=new R("DOMMouseScroll",i,i),Tb=new R("MozMousePixelScroll",i,i),ec=new Ub("keydown",i,i),Vb=new Ub("keypress",i,i),fc=new Ub("keyup",i,i),Qb=new Wb("touchmove",i,i),Pb=new Wb("touchstart",i,i);function Rb(a,b,c){b=b.create(a,c);"isTrusted"in b||(b.Oa=l);return a.dispatchEvent(b)};function gc(a,b){hc(a)&&(a.selectionStart=b,a.selectionEnd=b)}function hc(a){try{return"number"==typeof a.selectionStart}catch(b){return l}};function ic(a){if("function"==typeof a.N)return a.N();if(t(a))return a.split("");if(ba(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return Ea(a)};function jc(a,b){this.l={};this.ja={};var c=arguments.length;if(1<c){c%2&&g(Error("Uneven number of arguments"));for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.ca(a)}p=jc.prototype;p.ba=0;p.N=function(){var a=[],b;for(b in this.l)":"==b.charAt(0)&&a.push(this.l[b]);return a};function kc(a){var b=[],c;for(c in a.l)if(":"==c.charAt(0)){var d=c.substring(1);b.push(a.ja[c]?Number(d):d)}return b}
-p.remove=function(a){var a=":"+a,b=this.l,c;(c=a in b)&&delete b[a];return c?(delete this.ja[a],this.ba++,i):l};p.set=function(a,b){var c=":"+a;c in this.l||(this.ba++,"number"==typeof a&&(this.ja[c]=i));this.l[c]=b};p.ca=function(a){var b;if(a instanceof jc)b=kc(a),a=a.N();else{b=[];var c=0,d;for(d in a)b[c++]=d;a=Ea(a)}for(c=0;c<b.length;c++)this.set(b[c],a[c])};
-p.t=function(a){var b=0,c=kc(this),d=this.l,e=this.ba,f=this,j=new J;j.next=function(){for(;;){e!=f.ba&&g(Error("The map has changed since the iterator was created"));b>=c.length&&g(I);var j=c[b++];return a?j:d[":"+j]}};return j};function lc(a){this.l=new jc;a&&this.ca(a)}function mc(a){var b=typeof a;return"object"==b&&a||"function"==b?"o"+(a[da]||(a[da]=++ea)):b.substr(0,1)+a}p=lc.prototype;p.add=function(a){this.l.set(mc(a),a)};p.ca=function(a){for(var a=ic(a),b=a.length,c=0;c<b;c++)this.add(a[c])};p.remove=function(a){return this.l.remove(mc(a))};p.contains=function(a){return":"+mc(a)in this.l.l};p.N=function(){return this.l.N()};p.t=function(){return this.l.t(l)};function nc(){P.call(this);this.ga=Gb(this.q())&&!Bb(this.q(),"readOnly");this.ka=new lc}x(nc,P);var oc={};function S(a,b,c){ca(a)&&(a=a.c);a=new pc(a,b,c);if(b&&(!(b in oc)||c))oc[b]={key:a,shift:l},c&&(oc[c]={key:a,shift:i});return a}function pc(a,b,c){this.code=a;this.C=b||k;this.Ja=c||this.C}S(8);S(9);var qc=S(13),T=S(16),rc=S(17),sc=S(18);S(19);S(20);S(27);S(32," ");S(33);S(34);S(35);S(36);var tc=S(37);S(38);var uc=S(39);S(40);S(44);S(45);S(46);S(48,"0",")");S(49,"1","!");S(50,"2","@");
-S(51,"3","#");S(52,"4","$");S(53,"5","%");S(54,"6","^");S(55,"7","&");S(56,"8","*");S(57,"9","(");S(65,"a","A");S(66,"b","B");S(67,"c","C");S(68,"d","D");S(69,"e","E");S(70,"f","F");S(71,"g","G");S(72,"h","H");S(73,"i","I");S(74,"j","J");S(75,"k","K");S(76,"l","L");S(77,"m","M");S(78,"n","N");S(79,"o","O");S(80,"p","P");S(81,"q","Q");S(82,"r","R");S(83,"s","S");S(84,"t","T");S(85,"u","U");S(86,"v","V");S(87,"w","W");S(88,"x","X");S(89,"y","Y");S(90,"z","Z");
-var vc=S(ta?{c:91,e:91,opera:219}:sa?{c:224,e:91,opera:17}:{c:0,e:91,opera:k}),wc=S(ta?{c:92,e:92,opera:220}:sa?{c:224,e:93,opera:17}:{c:0,e:92,opera:k}),xc=S(ta?{c:93,e:93,opera:0}:sa?{c:0,e:0,opera:16}:{c:93,e:k,opera:0});S({c:96,e:96,opera:48},"0");S({c:97,e:97,opera:49},"1");S({c:98,e:98,opera:50},"2");S({c:99,e:99,opera:51},"3");S({c:100,e:100,opera:52},"4");S({c:101,e:101,opera:53},"5");S({c:102,e:102,opera:54},"6");S({c:103,e:103,opera:55},"7");S({c:104,e:104,opera:56},"8");
-S({c:105,e:105,opera:57},"9");S({c:106,e:106,opera:xa?56:42},"*");S({c:107,e:107,opera:xa?61:43},"+");S({c:109,e:109,opera:xa?109:45},"-");S({c:110,e:110,opera:xa?190:78},".");S({c:111,e:111,opera:xa?191:47},"/");S(144);S(112);S(113);S(114);S(115);S(116);S(117);S(118);S(119);S(120);S(121);S(122);S(123);S({c:107,e:187,opera:61},"=","+");S({c:109,e:189,opera:109},"-","_");S(188,",","<");S(190,".",">");S(191,"/","?");S(192,"`","~");S(219,"[","{");S(220,"\\","|");S(221,"]","}");
-S({c:59,e:186,opera:59},";",":");S(222,"'",'"');var yc=[sc,rc,vc,T];nc.prototype.m=function(a){return this.ka.contains(a)};function zc(a,b){a.m(b)&&0<=A(yc,b)&&g(new y(13,"Cannot press a modifier key that is already pressed."));var c=b.code!==k&&Ac(a,ec,b);if((!Bc(b)||Ac(a,Vb,b,!c))&&c&&a.ga&&!b.C)switch(b){case tc:case uc:var c=a.q(),d=0;hc(c)&&(d=c.selectionStart);c=d;b==tc?gc(a.q(),c-1):gc(a.q(),c+1)}a.ka.add(b)}
-function Bc(a){if(a.C||a==qc)return i;switch(a){case T:case rc:case sc:return l;case vc:case wc:case xc:return i;default:return i}}function Cc(a,b){a.m(b)||g(new y(13,"Cannot release a key that is not pressed."));b.code===k||Ac(a,fc,b);a.ka.remove(b)}
-function Ac(a,b,c,d){c.code===k&&g(new y(13,"Key must have a keycode to be fired."));var e=a.m(sc),f=a.m(rc),j=a.m(vc),m=a.m(T),r=c.code;c.C&&b==Vb?(c.C||g(new y(13,"not a character key")),c=(a.m(T)?c.Ja:c.C).charCodeAt(0)):c=0;return Rb(a.v,b,{altKey:e,ctrlKey:f,metaKey:j,shiftKey:m,keyCode:r,charCode:c,preventDefault:!!d})}
-function Dc(a,b){Nb(a,b);a.ga=Gb(b)&&!Bb(b,"readOnly");var c;c=a.xa||a.v;var d=D(c).activeElement;if(c==d)c=l;else{if(d&&u(d.blur))try{d.blur()}catch(e){g(e)}u(c.focus)?(c.focus(),c=i):c=l}a.ga&&c&&gc(b,b.value.length)};function Ec(a){return Fc(a||arguments.callee.caller,[])}
-function Fc(a,b){var c=[];if(0<=A(b,a))c.push("[...circular reference...]");else if(a&&50>b.length){c.push(Gc(a)+"(");for(var d=a.arguments,e=0;e<d.length;e++){0<e&&c.push(", ");var f;f=d[e];switch(typeof f){case "object":f=f?"object":"null";break;case "string":break;case "number":f=""+f;break;case "boolean":f=f?"true":"false";break;case "function":f=(f=Gc(f))?f:"[fn]";break;default:f=typeof f}40<f.length&&(f=f.substr(0,40)+"...");c.push(f)}b.push(a);c.push(")\n");try{c.push(Fc(a.caller,b))}catch(j){c.push("[exception trying to get caller]\n")}}else a?
-c.push("[...long stack...]"):c.push("[end]");return c.join("")}function Gc(a){if(Hc[a])return Hc[a];a=""+a;if(!Hc[a]){var b=/function ([^\(]+)/.exec(a);Hc[a]=b?b[1]:"[Anonymous]"}return Hc[a]}var Hc={};function Ic(a,b,c,d,e){this.reset(a,b,c,d,e)}Ic.prototype.sa=k;Ic.prototype.ra=k;var Jc=0;Ic.prototype.reset=function(a,b,c,d,e){"number"==typeof e||Jc++;d||fa();this.P=a;this.Ha=b;delete this.sa;delete this.ra};Ic.prototype.ya=function(a){this.P=a};function U(a){this.Ia=a}U.prototype.Z=k;U.prototype.P=k;U.prototype.da=k;U.prototype.va=k;function Kc(a,b){this.name=a;this.value=b}Kc.prototype.toString=n("name");var Lc=new Kc("WARNING",900),Mc=new Kc("CONFIG",700);U.prototype.getParent=n("Z");U.prototype.ya=function(a){this.P=a};function Nc(a){if(a.P)return a.P;if(a.Z)return Nc(a.Z);Ja("Root logger has no level set.");return k}
-U.prototype.log=function(a,b,c){if(a.value>=Nc(this).value){a=this.Ea(a,b,c);b="log:"+a.Ha;q.console&&(q.console.timeStamp?q.console.timeStamp(b):q.console.markTimeline&&q.console.markTimeline(b));q.msWriteProfilerMark&&q.msWriteProfilerMark(b);for(b=this;b;){var c=b,d=a;if(c.va)for(var e=0,f=h;f=c.va[e];e++)f(d);b=b.getParent()}}};
-U.prototype.Ea=function(a,b,c){var d=new Ic(a,""+b,this.Ia);if(c){d.sa=c;var e;var f=arguments.callee.caller;try{var j;var m;c:{for(var r=["window","location","href"],O=q,G;G=r.shift();)if(O[G]!=k)O=O[G];else{m=k;break c}m=O}if(t(c))j={message:c,name:"Unknown error",lineNumber:"Not available",fileName:m,stack:"Not available"};else{var v,w,r=l;try{v=c.lineNumber||c.Pa||"Not available"}catch(Ed){v="Not available",r=i}try{w=c.fileName||c.filename||c.sourceURL||m}catch(Fd){w="Not available",r=i}j=r||
-!c.lineNumber||!c.fileName||!c.stack?{message:c.message,name:c.name,lineNumber:v,fileName:w,stack:c.stack||"Not available"}:c}e="Message: "+ia(j.message)+'\nUrl: <a href="view-source:'+j.fileName+'" target="_new">'+j.fileName+"</a>\nLine: "+j.lineNumber+"\n\nBrowser stack:\n"+ia(j.stack+"-> ")+"[end]\n\nJS stack traversal:\n"+ia(Ec(f)+"-> ")}catch(Cd){e="Exception trying to expose exception! You win, we lose. "+Cd}d.ra=e}return d};var Oc={},Pc=k;
-function Qc(a){Pc||(Pc=new U(""),Oc[""]=Pc,Pc.ya(Mc));var b;if(!(b=Oc[a])){b=new U(a);var c=a.lastIndexOf("."),d=a.substr(c+1),c=Qc(a.substr(0,c));c.da||(c.da={});c.da[d]=b;b.Z=c;Oc[a]=b}return b};function Rc(){}x(Rc,function(){});Qc("goog.dom.SavedRange");x(function(a){this.La="goog_"+pa++;this.Da="goog_"+pa++;this.pa=Wa(a.ha());a.T(this.pa.fa("SPAN",{id:this.La}),this.pa.fa("SPAN",{id:this.Da}))},Rc);function V(){}function Sc(a){if(a.getSelection)return a.getSelection();var a=a.document,b=a.selection;if(b){try{var c=b.createRange();if(c.parentElement){if(c.parentElement().document!=a)return k}else if(!c.length||c.item(0).document!=a)return k}catch(d){return k}return b}return k}function Tc(a){for(var b=[],c=0,d=a.G();c<d;c++)b.push(a.B(c));return b}V.prototype.H=o(l);V.prototype.ha=function(){return D(this.b())};V.prototype.ua=function(){return E(this.ha())};
-V.prototype.containsNode=function(a,b){return this.z(Uc(Vc(a),h),b)};function W(a,b){K.call(this,a,b,i)}x(W,K);function Wc(){}x(Wc,V);Wc.prototype.z=function(a,b){var c=Tc(this),d=Tc(a);return(b?Na:Oa)(d,function(a){return Na(c,function(c){return c.z(a,b)})})};Wc.prototype.insertNode=function(a,b){if(b){var c=this.b();c.parentNode&&c.parentNode.insertBefore(a,c)}else c=this.g(),c.parentNode&&c.parentNode.insertBefore(a,c.nextSibling);return a};Wc.prototype.T=function(a,b){this.insertNode(a,i);this.insertNode(b,l)};function Xc(a,b,c,d,e){var f;if(a&&(this.f=a,this.i=b,this.d=c,this.h=d,1==a.nodeType&&"BR"!=a.tagName&&(a=a.childNodes,(b=a[b])?(this.f=b,this.i=0):(a.length&&(this.f=z(a)),f=i)),1==c.nodeType))(this.d=c.childNodes[d])?this.h=0:this.d=c;W.call(this,e?this.d:this.f,e);if(f)try{this.next()}catch(j){j!=I&&g(j)}}x(Xc,W);p=Xc.prototype;p.f=k;p.d=k;p.i=0;p.h=0;p.b=n("f");p.g=n("d");p.O=function(){return this.ma&&this.r==this.d&&(!this.h||1!=this.s)};p.next=function(){this.O()&&g(I);return Xc.aa.next.call(this)};"ScriptEngine"in q&&"JScript"==q.ScriptEngine()&&(q.ScriptEngineMajorVersion(),q.ScriptEngineMinorVersion(),q.ScriptEngineBuildVersion());function Yc(){}Yc.prototype.z=function(a,b){var c=b&&!a.isCollapsed(),d=a.a;try{return c?0<=this.n(d,0,1)&&0>=this.n(d,1,0):0<=this.n(d,0,0)&&0>=this.n(d,1,1)}catch(e){g(e)}};Yc.prototype.containsNode=function(a,b){return this.z(Vc(a),b)};Yc.prototype.t=function(){return new Xc(this.b(),this.j(),this.g(),this.k())};function Zc(a){this.a=a}x(Zc,Yc);p=Zc.prototype;p.F=function(){return this.a.commonAncestorContainer};p.b=function(){return this.a.startContainer};p.j=function(){return this.a.startOffset};p.g=function(){return this.a.endContainer};p.k=function(){return this.a.endOffset};p.n=function(a,b,c){return this.a.compareBoundaryPoints(1==c?1==b?q.Range.START_TO_START:q.Range.START_TO_END:1==b?q.Range.END_TO_START:q.Range.END_TO_END,a)};p.isCollapsed=function(){return this.a.collapsed};
-p.select=function(a){this.$(E(D(this.b())).getSelection(),a)};p.$=function(a){a.removeAllRanges();a.addRange(this.a)};p.insertNode=function(a,b){var c=this.a.cloneRange();c.collapse(b);c.insertNode(a);c.detach();return a};
-p.T=function(a,b){var c=E(D(this.b()));if(c=(c=Sc(c||window))&&$c(c))var d=c.b(),e=c.g(),f=c.j(),j=c.k();var m=this.a.cloneRange(),r=this.a.cloneRange();m.collapse(l);r.collapse(i);m.insertNode(b);r.insertNode(a);m.detach();r.detach();if(c){if(d.nodeType==C)for(;f>d.length;){f-=d.length;do d=d.nextSibling;while(d==a||d==b)}if(e.nodeType==C)for(;j>e.length;){j-=e.length;do e=e.nextSibling;while(e==a||e==b)}c=new ad;c.I=bd(d,f,e,j);"BR"==d.tagName&&(m=d.parentNode,f=A(m.childNodes,d),d=m);"BR"==e.tagName&&
-(m=e.parentNode,j=A(m.childNodes,e),e=m);c.I?(c.f=e,c.i=j,c.d=d,c.h=f):(c.f=d,c.i=f,c.d=e,c.h=j);c.select()}};p.collapse=function(a){this.a.collapse(a)};function cd(a){this.a=a}x(cd,Zc);function Vc(a){var b=D(a).createRange();if(a.nodeType==C)b.setStart(a,0),b.setEnd(a,a.length);else if(X(a)){for(var c,d=a;(c=d.firstChild)&&X(c);)d=c;b.setStart(d,0);for(d=a;(c=d.lastChild)&&X(c);)d=c;b.setEnd(d,1==d.nodeType?d.childNodes.length:d.length)}else c=a.parentNode,a=A(c.childNodes,a),b.setStart(c,a),b.setEnd(c,a+1);return new cd(b)}
-cd.prototype.$=function(a,b){var c=b?this.g():this.b(),d=b?this.k():this.j(),e=b?this.b():this.g(),f=b?this.j():this.k();a.collapse(c,d);(c!=e||d!=f)&&a.extend(e,f)};function dd(a){this.a=a}x(dd,Yc);Qc("goog.dom.browserrange.IeRange");function ed(a){var b=D(a).body.createTextRange();if(1==a.nodeType)b.moveToElementText(a),X(a)&&!a.childNodes.length&&b.collapse(l);else{for(var c=0,d=a;d=d.previousSibling;){var e=d.nodeType;if(e==C)c+=d.length;else if(1==e){b.moveToElementText(d);break}}d||b.moveToElementText(a.parentNode);b.collapse(!d);c&&b.move("character",c);b.moveEnd("character",a.length)}return b}p=dd.prototype;p.Q=k;p.f=k;p.d=k;p.i=-1;p.h=-1;
-p.u=function(){this.Q=this.f=this.d=k;this.i=this.h=-1};
-p.F=function(){if(!this.Q){var a=this.a.text,b=this.a.duplicate(),c=a.replace(/ +$/,"");(c=a.length-c.length)&&b.moveEnd("character",-c);c=b.parentElement();b=b.htmlText.replace(/(\r\n|\r|\n)+/g," ").length;if(this.isCollapsed()&&0<b)return this.Q=c;for(;b>c.outerHTML.replace(/(\r\n|\r|\n)+/g," ").length;)c=c.parentNode;for(;1==c.childNodes.length&&c.innerText==(c.firstChild.nodeType==C?c.firstChild.nodeValue:c.firstChild.innerText)&&X(c.firstChild);)c=c.firstChild;0==a.length&&(c=fd(this,c));this.Q=
-c}return this.Q};function fd(a,b){for(var c=b.childNodes,d=0,e=c.length;d<e;d++){var f=c[d];if(X(f)){var j=ed(f),m=j.htmlText!=f.outerHTML;if(a.isCollapsed()&&m?0<=a.n(j,1,1)&&0>=a.n(j,1,0):a.a.inRange(j))return fd(a,f)}}return b}p.b=function(){this.f||(this.f=gd(this,1),this.isCollapsed()&&(this.d=this.f));return this.f};p.j=function(){0>this.i&&(this.i=hd(this,1),this.isCollapsed()&&(this.h=this.i));return this.i};
-p.g=function(){if(this.isCollapsed())return this.b();this.d||(this.d=gd(this,0));return this.d};p.k=function(){if(this.isCollapsed())return this.j();0>this.h&&(this.h=hd(this,0),this.isCollapsed()&&(this.i=this.h));return this.h};p.n=function(a,b,c){return this.a.compareEndPoints((1==b?"Start":"End")+"To"+(1==c?"Start":"End"),a)};
-function gd(a,b,c){c=c||a.F();if(!c||!c.firstChild)return c;for(var d=1==b,e=0,f=c.childNodes.length;e<f;e++){var j=d?e:f-e-1,m=c.childNodes[j],r;try{r=Vc(m)}catch(O){continue}var G=r.a;if(a.isCollapsed())if(X(m)){if(r.z(a))return gd(a,b,m)}else{if(0==a.n(G,1,1)){a.i=a.h=j;break}}else{if(a.z(r)){if(!X(m)){d?a.i=j:a.h=j+1;break}return gd(a,b,m)}if(0>a.n(G,1,0)&&0<a.n(G,0,1))return gd(a,b,m)}}return c}
-function hd(a,b){var c=1==b,d=c?a.b():a.g();if(1==d.nodeType){for(var d=d.childNodes,e=d.length,f=c?1:-1,j=c?0:e-1;0<=j&&j<e;j+=f){var m=d[j];if(!X(m)&&0==a.a.compareEndPoints((1==b?"Start":"End")+"To"+(1==b?"Start":"End"),Vc(m).a))return c?j:j+1}return-1==j?0:j}e=a.a.duplicate();f=ed(d);e.setEndPoint(c?"EndToEnd":"StartToStart",f);e=e.text.length;return c?d.length-e:e}p.isCollapsed=function(){return 0==this.a.compareEndPoints("StartToEnd",this.a)};p.select=function(){this.a.select()};
-function id(a,b,c){var d;d=d||Wa(a.parentElement());var e;1!=b.nodeType&&(e=i,b=d.fa("DIV",k,b));a.collapse(c);d=d||Wa(a.parentElement());var f=c=b.id;c||(c=b.id="goog_"+pa++);a.pasteHTML(b.outerHTML);(b=d.q(c))&&(f||b.removeAttribute("id"));if(e){a=b.firstChild;e=b;if((d=e.parentNode)&&11!=d.nodeType)if(e.removeNode)e.removeNode(l);else{for(;b=e.firstChild;)d.insertBefore(b,e);bb(e)}b=a}return b}p.insertNode=function(a,b){var c=id(this.a.duplicate(),a,b);this.u();return c};
-p.T=function(a,b){var c=this.a.duplicate(),d=this.a.duplicate();id(c,a,i);id(d,b,l);this.u()};p.collapse=function(a){this.a.collapse(a);a?(this.d=this.f,this.h=this.i):(this.f=this.d,this.i=this.h)};function jd(a){this.a=a}x(jd,Zc);jd.prototype.$=function(a){a.collapse(this.b(),this.j());(this.g()!=this.b()||this.k()!=this.j())&&a.extend(this.g(),this.k());0==a.rangeCount&&a.addRange(this.a)};function kd(a){this.a=a}x(kd,Zc);kd.prototype.n=function(a,b,c){return Ba["528"]||(Ba["528"]=0<=oa(ya,"528"))?kd.aa.n.call(this,a,b,c):this.a.compareBoundaryPoints(1==c?1==b?q.Range.START_TO_START:q.Range.END_TO_START:1==b?q.Range.START_TO_END:q.Range.END_TO_END,a)};kd.prototype.$=function(a,b){a.removeAllRanges();b?a.setBaseAndExtent(this.g(),this.k(),this.b(),this.j()):a.setBaseAndExtent(this.b(),this.j(),this.g(),this.k())};function X(a){var b;a:if(1!=a.nodeType)b=l;else{switch(a.tagName){case "APPLET":case "AREA":case "BASE":case "BR":case "COL":case "FRAME":case "HR":case "IMG":case "INPUT":case "IFRAME":case "ISINDEX":case "LINK":case "NOFRAMES":case "NOSCRIPT":case "META":case "OBJECT":case "PARAM":case "SCRIPT":case "STYLE":b=l;break a}b=i}return b||a.nodeType==C};function ad(){}x(ad,V);function Uc(a,b){var c=new ad;c.M=a;c.I=!!b;return c}p=ad.prototype;p.M=k;p.f=k;p.i=k;p.d=k;p.h=k;p.I=l;p.ia=o("text");p.Y=function(){return Y(this).a};p.u=function(){this.f=this.i=this.d=this.h=k};p.G=o(1);p.B=function(){return this};function Y(a){var b;if(!(b=a.M)){b=a.b();var c=a.j(),d=a.g(),e=a.k(),f=D(b).createRange();f.setStart(b,c);f.setEnd(d,e);b=a.M=new cd(f)}return b}p.F=function(){return Y(this).F()};p.b=function(){return this.f||(this.f=Y(this).b())};
-p.j=function(){return this.i!=k?this.i:this.i=Y(this).j()};p.g=function(){return this.d||(this.d=Y(this).g())};p.k=function(){return this.h!=k?this.h:this.h=Y(this).k()};p.H=n("I");p.z=function(a,b){var c=a.ia();return"text"==c?Y(this).z(Y(a),b):"control"==c?(c=ld(a),(b?Na:Oa)(c,function(a){return this.containsNode(a,b)},this)):l};p.isCollapsed=function(){return Y(this).isCollapsed()};p.t=function(){return new Xc(this.b(),this.j(),this.g(),this.k())};p.select=function(){Y(this).select(this.I)};
-p.insertNode=function(a,b){var c=Y(this).insertNode(a,b);this.u();return c};p.T=function(a,b){Y(this).T(a,b);this.u()};p.la=function(){return new md(this)};p.collapse=function(a){a=this.H()?!a:a;this.M&&this.M.collapse(a);a?(this.d=this.f,this.h=this.i):(this.f=this.d,this.i=this.h);this.I=l};function md(a){a.H()?a.g():a.b();a.H()?a.k():a.j();a.H()?a.b():a.g();a.H()?a.j():a.k()}x(md,Rc);function nd(){}x(nd,Wc);p=nd.prototype;p.a=k;p.o=k;p.S=k;p.u=function(){this.S=this.o=k};p.ia=o("control");p.Y=function(){return this.a||document.body.createControlRange()};p.G=function(){return this.a?this.a.length:0};p.B=function(a){a=this.a.item(a);return Uc(Vc(a),h)};p.F=function(){return fb.apply(k,ld(this))};p.b=function(){return od(this)[0]};p.j=o(0);p.g=function(){var a=od(this),b=z(a);return Pa(a,function(a){return F(a,b)})};p.k=function(){return this.g().childNodes.length};
-function ld(a){if(!a.o&&(a.o=[],a.a))for(var b=0;b<a.a.length;b++)a.o.push(a.a.item(b));return a.o}function od(a){a.S||(a.S=ld(a).concat(),a.S.sort(function(a,c){return a.sourceIndex-c.sourceIndex}));return a.S}p.isCollapsed=function(){return!this.a||!this.a.length};p.t=function(){return new pd(this)};p.select=function(){this.a&&this.a.select()};p.la=function(){return new qd(this)};p.collapse=function(){this.a=k;this.u()};function qd(a){this.o=ld(a)}x(qd,Rc);
-function pd(a){a&&(this.o=od(a),this.f=this.o.shift(),this.d=z(this.o)||this.f);W.call(this,this.f,l)}x(pd,W);p=pd.prototype;p.f=k;p.d=k;p.o=k;p.b=n("f");p.g=n("d");p.O=function(){return!this.depth&&!this.o.length};p.next=function(){this.O()&&g(I);if(!this.depth){var a=this.o.shift();L(this,a,1,1);return a}return pd.aa.next.call(this)};function rd(){this.w=[];this.R=[];this.W=this.K=k}x(rd,Wc);p=rd.prototype;p.Ga=Qc("goog.dom.MultiRange");p.u=function(){this.R=[];this.W=this.K=k};p.ia=o("mutli");p.Y=function(){1<this.w.length&&this.Ga.log(Lc,"getBrowserRangeObject called on MultiRange with more than 1 range",h);return this.w[0]};p.G=function(){return this.w.length};p.B=function(a){this.R[a]||(this.R[a]=Uc(new cd(this.w[a]),h));return this.R[a]};
-p.F=function(){if(!this.W){for(var a=[],b=0,c=this.G();b<c;b++)a.push(this.B(b).F());this.W=fb.apply(k,a)}return this.W};function sd(a){a.K||(a.K=Tc(a),a.K.sort(function(a,c){var d=a.b(),e=a.j(),f=c.b(),j=c.j();return d==f&&e==j?0:bd(d,e,f,j)?1:-1}));return a.K}p.b=function(){return sd(this)[0].b()};p.j=function(){return sd(this)[0].j()};p.g=function(){return z(sd(this)).g()};p.k=function(){return z(sd(this)).k()};p.isCollapsed=function(){return 0==this.w.length||1==this.w.length&&this.B(0).isCollapsed()};
-p.t=function(){return new td(this)};p.select=function(){var a=Sc(this.ua());a.removeAllRanges();for(var b=0,c=this.G();b<c;b++)a.addRange(this.B(b).Y())};p.la=function(){return new ud(this)};p.collapse=function(a){if(!this.isCollapsed()){var b=a?this.B(0):this.B(this.G()-1);this.u();b.collapse(a);this.R=[b];this.K=[b];this.w=[b.Y()]}};function ud(a){Ma(Tc(a),function(a){return a.la()})}x(ud,Rc);function td(a){a&&(this.J=Ma(sd(a),function(a){return sb(a)}));W.call(this,a?this.b():k,l)}x(td,W);p=td.prototype;
-p.J=k;p.X=0;p.b=function(){return this.J[0].b()};p.g=function(){return z(this.J).g()};p.O=function(){return this.J[this.X].O()};p.next=function(){try{var a=this.J[this.X],b=a.next();L(this,a.r,a.s,a.depth);return b}catch(c){return(c!==I||this.J.length-1==this.X)&&g(c),this.X++,this.next()}};function $c(a){var b,c=l;if(a.createRange)try{b=a.createRange()}catch(d){return k}else if(a.rangeCount){if(1<a.rangeCount){b=new rd;for(var c=0,e=a.rangeCount;c<e;c++)b.w.push(a.getRangeAt(c));return b}b=a.getRangeAt(0);c=bd(a.anchorNode,a.anchorOffset,a.focusNode,a.focusOffset)}else return k;b&&b.addElement?(a=new nd,a.a=b):a=Uc(new cd(b),c);return a}
-function bd(a,b,c,d){if(a==c)return d<b;var e;if(1==a.nodeType&&b)if(e=a.childNodes[b])a=e,b=0;else if(F(a,c))return i;if(1==c.nodeType&&d)if(e=c.childNodes[d])c=e,d=0;else if(F(c,a))return l;return 0<(cb(a,c)||b-d)};function vd(){P.call(this);this.na=k;this.D=new B(0,0);this.wa=l}x(vd,P);var Z={};Z[Xb]=[0,1,2,k];Z[Yb]=[k,k,2,k];Z[dc]=[0,1,2,k];Z[bc]=[0,0,0,0];Z[ac]=[0,0,0,0];Z[Zb]=Z[Xb];Z[$b]=Z[dc];Z[cc]=Z[bc];vd.prototype.move=function(a,b){var c=xb(a);this.D.x=b.x+c.x;this.D.y=b.y+c.y;a!=this.q()&&(c=this.q()===Ca.document.documentElement||this.q()===Ca.document.body,c=!this.wa&&c?k:this.q(),wd(this,bc,a),Nb(this,a),wd(this,cc,c));wd(this,ac)};
-function wd(a,b,c){a.wa=i;var d=a.D,e;b in Z?(e=Z[b][a.na===k?3:a.na],e===k&&g(new y(13,"Event does not permit the specified mouse button."))):e=0;N(a.v,i)&&Eb(a.v)&&(c&&!(cc==b||bc==b)&&g(new y(12,"Event type does not allow related target: "+b)),c={clientX:d.x,clientY:d.y,button:e,altKey:l,ctrlKey:l,shiftKey:l,metaKey:l,wheelDelta:0,relatedTarget:c||k},(a=a.v)&&Rb(a,b,c))};function xd(){P.call(this);this.D=new B(0,0);this.ea=new B(0,0)}x(xd,P);xd.prototype.Aa=0;xd.prototype.za=0;xd.prototype.move=function(a,b,c){this.m()||Nb(this,a);a=xb(a);this.D.x=b.x+a.x;this.D.y=b.y+a.y;s(c)&&(this.ea.x=c.x+a.x,this.ea.y=c.y+a.y);if(this.m()){b=Qb;this.m()||g(new y(13,"Should never fire event when touchscreen is not pressed."));var d,e;this.za&&(d=this.za,e=this.ea);Ob(this,b,this.Aa,this.D,d,e)}};xd.prototype.m=function(){return!!this.Aa};function yd(a,b){this.x=a;this.y=b}x(yd,B);yd.prototype.scale=function(a){this.x*=a;this.y*=a;return this};yd.prototype.add=function(a){this.x+=a.x;this.y+=a.y;return this};function zd(){P.call(this)}x(zd,P);(function(a){a.Na=function(){return a.Fa||(a.Fa=new a)}})(zd);function Ad(a,b){N(a,i)||g(new y(11,"Element is not currently visible and may not be manipulated"));(!N(a,i)||!Eb(a))&&g(new y(12,"Element is not currently interactable and may not be manipulated"));var c=new nc;Dc(c,a);var d=Sa(arguments,1);La(d,function(a){t(a)?La(a.split(""),function(a){1!=a.length&&g(new y(13,"Argument not a single character: "+a));var b=oc[a];b||(b=a.toUpperCase(),b=S(b.charCodeAt(0),a.toLowerCase(),b),b={key:b,shift:a!=b.C});a=b;a.shift&&zc(c,T);zc(c,a.key);Cc(c,a.key);a.shift&&
-Cc(c,T)}):0<=A(yc,a)?c.m(a)?Cc(c,a):zc(c,a):(zc(c,a),Cc(c,a))});La(yc,function(a){c.m(a)&&Cc(c,a)})}var Bd=["_"],$=q;!(Bd[0]in $)&&$.execScript&&$.execScript("var "+Bd[0]);for(var Dd;Bd.length&&(Dd=Bd.shift());)!Bd.length&&s(Ad)?$[Dd]=Ad:$=$[Dd]?$[Dd]:$[Dd]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/keys.py
@@ -0,0 +1,84 @@
+# copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License Version 2.0 = uthe "License")
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http //www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class Keys(object):
+
+    NULL         = u'\ue000'
+    CANCEL       = u'\ue001' #  ^break
+    HELP         = u'\ue002'
+    BACK_SPACE   = u'\ue003'
+    TAB          = u'\ue004'
+    CLEAR        = u'\ue005'
+    RETURN       = u'\ue006'
+    ENTER        = u'\ue007'
+    SHIFT        = u'\ue008'
+    LEFT_SHIFT   = u'\ue008' #  alias
+    CONTROL      = u'\ue009'
+    LEFT_CONTROL = u'\ue009' #  alias
+    ALT          = u'\ue00a'
+    LEFT_ALT     = u'\ue00a' #  alias
+    PAUSE        = u'\ue00b'
+    ESCAPE       = u'\ue00c'
+    SPACE        = u'\ue00d'
+    PAGE_UP      = u'\ue00e'
+    PAGE_DOWN    = u'\ue00f'
+    END          = u'\ue010'
+    HOME         = u'\ue011'
+    LEFT         = u'\ue012'
+    ARROW_LEFT   = u'\ue012' # alias
+    UP           = u'\ue013'
+    ARROW_UP     = u'\ue013' # alias
+    RIGHT        = u'\ue014'
+    ARROW_RIGHT  = u'\ue014' #  alias
+    DOWN         = u'\ue015'
+    ARROW_DOWN   = u'\ue015' #  alias
+    INSERT       = u'\ue016'
+    DELETE       = u'\ue017'
+    SEMICOLON    = u'\ue018'
+    EQUALS       = u'\ue019'
+
+    NUMPAD0      = u'\ue01a' #  numbe pad  keys
+    NUMPAD1      = u'\ue01b'
+    NUMPAD2      = u'\ue01c'
+    NUMPAD3      = u'\ue01d'
+    NUMPAD4      = u'\ue01e'
+    NUMPAD5      = u'\ue01f'
+    NUMPAD6      = u'\ue020'
+    NUMPAD7      = u'\ue021'
+    NUMPAD8      = u'\ue022'
+    NUMPAD9      = u'\ue023'
+    MULTIPLY     = u'\ue024'
+    ADD          = u'\ue025'
+    SEPARATOR    = u'\ue026'
+    SUBTRACT     = u'\ue027'
+    DECIMAL      = u'\ue028'
+    DIVIDE       = u'\ue029'
+
+    F1           = u'\ue031' #  function  keys
+    F2           = u'\ue032'
+    F3           = u'\ue033'
+    F4           = u'\ue034'
+    F5           = u'\ue035'
+    F6           = u'\ue036'
+    F7           = u'\ue037'
+    F8           = u'\ue038'
+    F9           = u'\ue039'
+    F10          = u'\ue03a'
+    F11          = u'\ue03b'
+    F12          = u'\ue03c'
+
+    META         = u'\ue03d'
+    COMMAND      = u'\ue03d'
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -1,15 +1,16 @@
 # 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/.
 
 import socket
 
 from client import MarionetteClient
+from keys import Keys
 from errors import *
 from emulator import Emulator
 from geckoinstance import GeckoInstance
 
 class HTMLElement(object):
 
     CLASS = "class name"
     SELECTOR = "css selector"
@@ -42,18 +43,29 @@ class HTMLElement(object):
 
     def click(self):
         return self.marionette._send_message('clickElement', 'ok', element=self.id)
 
     @property
     def text(self):
         return self.marionette._send_message('getElementText', 'value', element=self.id)
 
-    def send_keys(self, string):
-        return self.marionette._send_message('sendKeysToElement', 'ok', element=self.id, value=string)
+    def send_keys(self, *string):
+        typing = []
+        for val in string:
+            if isinstance(val, Keys):
+                typing.append(val)
+            elif isinstance(val, int):
+                val = str(val)
+                for i in range(len(val)):
+                    typing.append(val[i])
+            else:
+                for i in range(len(val)):
+                    typing.append(val[i])
+        return self.marionette._send_message('sendKeysToElement', 'ok', element=self.id, value=typing)
 
     def value(self):
         return self.marionette._send_message('getElementValue', 'value', element=self.id)
 
     def clear(self):
         return self.marionette._send_message('clearElement', 'ok', element=self.id)
 
     def selected(self):
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_typing.py
@@ -0,0 +1,228 @@
+# 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/.
+
+from marionette_test import MarionetteTestCase
+from keys import Keys
+
+
+class TestTyping(MarionetteTestCase):
+
+    def testShouldFireKeyPressEvents(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("a")
+        result = self.marionette.find_element("id", "result")
+        self.assertTrue("press:" in result.text)
+
+    def testShouldFireKeyDownEvents(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("I")
+        result = self.marionette.find_element("id", "result")
+        self.assertTrue("down" in result.text)
+
+    def testShouldFireKeyUpEvents(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("a")
+        result = self.marionette.find_element("id", "result")
+        self.assertTrue("up:" in result.text)
+
+    def testShouldTypeLowerCaseLetters(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("abc def")
+        self.assertEqual(keyReporter.get_attribute("value"), "abc def")
+    
+    def testShouldBeAbleToTypeCapitalLetters(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("ABC DEF")
+        self.assertEqual(keyReporter.get_attribute("value"), "ABC DEF")
+    
+    def testShouldBeAbleToTypeQuoteMarks(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("\"")
+        self.assertEqual(keyReporter.get_attribute("value"), "\"")
+    
+    def testShouldBeAbleToTypeTheAtCharacter(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("@")
+        self.assertEqual(keyReporter.get_attribute("value"), "@")
+    
+    def testShouldBeAbleToMixUpperAndLowerCaseLetters(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys("me@eXample.com")
+        self.assertEqual(keyReporter.get_attribute("value"), "me@eXample.com")
+
+    def testArrowKeysShouldNotBePrintable(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        keyReporter = self.marionette.find_element("id", "keyReporter")
+        keyReporter.send_keys(Keys.ARROW_LEFT)
+        self.assertEqual(keyReporter.get_attribute("value"), "")
+
+    def testWillSimulateAKeyUpWhenEnteringTextIntoInputElements(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyUp")
+        element.send_keys("I like cheese")
+        result = self.marionette.find_element("id", "result")
+        self.assertEqual(result.text, "I like cheese")
+
+    def testWillSimulateAKeyDownWhenEnteringTextIntoInputElements(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyDown")
+        element.send_keys("I like cheese")
+        result = self.marionette.find_element("id", "result")
+        #  Because the key down gets the result before the input element is
+        #  filled, we're a letter short here
+        self.assertEqual(result.text, "I like chees")
+
+    def testWillSimulateAKeyPressWhenEnteringTextIntoInputElements(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyPress")
+        element.send_keys("I like cheese")
+        result = self.marionette.find_element("id", "result")
+        #  Because the key down gets the result before the input element is
+        #  filled, we're a letter short here
+        self.assertEqual(result.text, "I like chees")
+
+    def testWillSimulateAKeyUpWhenEnteringTextIntoTextAreas(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyUpArea")
+        element.send_keys("I like cheese")
+        result = self.marionette.find_element("id", "result")
+        self.assertEqual(result.text, "I like cheese")
+
+    def testWillSimulateAKeyDownWhenEnteringTextIntoTextAreas(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyDownArea")
+        element.send_keys("I like cheese")
+        result = self.marionette.find_element("id", "result")
+        #  Because the key down gets the result before the input element is
+        #  filled, we're a letter short here
+        self.assertEqual(result.text, "I like chees")
+
+    def testWillSimulateAKeyPressWhenEnteringTextIntoTextAreas(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyPressArea")
+        element.send_keys("I like cheese")
+        result = self.marionette.find_element("id", "result")
+        #  Because the key down gets the result before the input element is
+        #  filled, we're a letter short here
+        self.assertEqual(result.text, "I like chees")
+
+    def testShouldReportKeyCodeOfArrowKeysUpDownEvents(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        result = self.marionette.find_element("id", "result")
+        element = self.marionette.find_element("id", "keyReporter")
+        element.send_keys(Keys.ARROW_DOWN)
+        self.assertTrue("down: 40" in result.text.strip())
+        self.assertTrue("up: 40" in result.text.strip())
+
+        element.send_keys(Keys.ARROW_UP)
+        self.assertTrue("down: 38" in  result.text.strip())
+        self.assertTrue("up: 38" in result.text.strip())
+
+        element.send_keys(Keys.ARROW_LEFT)
+        self.assertTrue("down: 37" in result.text.strip())
+        self.assertTrue("up: 37" in result.text.strip())
+
+        element.send_keys(Keys.ARROW_RIGHT)
+        self.assertTrue("down: 39" in result.text.strip())
+        self.assertTrue("up: 39" in result.text.strip())
+
+        #  And leave no rubbish/printable keys in the "keyReporter"
+        self.assertEqual(element.get_attribute("value"), "")
+  
+    def testNumericShiftKeys(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        result = self.marionette.find_element("id", "result")
+        element = self.marionette.find_element("id", "keyReporter")
+        numericShiftsEtc = "~!@#$%^&*()_+{}:i\"<>?|END~"
+        element.send_keys(numericShiftsEtc)
+        self.assertEqual(element.get_attribute("value"), numericShiftsEtc)
+        self.assertTrue(" up: 16" in result.text.strip())
+
+    def testLowerCaseAlphaKeys(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyReporter")
+        lowerAlphas = "abcdefghijklmnopqrstuvwxyz"
+        element.send_keys(lowerAlphas)
+        self.assertEqual(element.get_attribute("value"), lowerAlphas)
+
+    def testUppercaseAlphaKeys(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        result = self.marionette.find_element("id", "result")
+        element = self.marionette.find_element("id", "keyReporter")
+        upperAlphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        element.send_keys(upperAlphas)
+        self.assertEqual(element.get_attribute("value"), upperAlphas)
+        self.assertTrue(" up: 16" in result.text.strip())
+
+    def testAllPrintableKeys(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        result = self.marionette.find_element("id", "result")
+        element = self.marionette.find_element("id", "keyReporter")
+        allPrintable = "!\"#$%&'()*+,-./0123456789:<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+        element.send_keys(allPrintable)
+
+        self.assertTrue(element.get_attribute("value"), allPrintable)
+        self.assertTrue(" up: 16" in result.text.strip())
+
+    def testSpecialSpaceKeys(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyReporter")
+        element.send_keys("abcd" + Keys.SPACE + "fgh" + Keys.SPACE + "ij")
+        self.assertEqual(element.get_attribute("value"), "abcd fgh ij")
+
+    def testShouldTypeAnInteger(self):
+        test_html = self.marionette.absolute_url("javascriptPage.html")
+        self.marionette.navigate(test_html)
+        
+        element = self.marionette.find_element("id", "keyReporter")
+        element.send_keys(1234)
+        self.assertEqual(element.get_attribute("value"), "1234")    
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/javascriptPage.html
@@ -0,0 +1,273 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+  <!-- 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/. -->
+<head>
+    <title>Testing Javascript</title>
+    <script type="text/javascript">
+        var seen = {};
+
+        function updateContent(input) {
+            document.getElementById('result').innerHTML = "<p>" + input.value + "</p>";
+        }
+
+        function displayMessage(message) {
+            document.getElementById('result').innerHTML = "<p>" + message + "</p>";
+        }
+
+        function appendMessage(message) {
+            document.getElementById('result').innerHTML += message + " ";
+        }
+
+        function register(message) {
+          if (!seen[message]) {
+            appendMessage(message);
+            seen[message] = true;
+          }
+        }
+
+        function delayedShowHide(delay, show) {
+          var blackBox = document.getElementById('clickToHide');
+          window.setTimeout(function() {
+            blackBox.style.display = show ? '' : 'none';
+          }, delay);
+        }
+    </script>
+    <script type="text/javascript">
+        var startList = function() {
+            // Ugh. Let's hope no-one is faking their user agent when running the tests
+            if (navigator.userAgent.indexOf("MSIE") != -1) {
+                var navRoot = document.getElementById("nav");
+                for (var i = 0; i < navRoot.childNodes.length; i++) {
+                    var node = navRoot.childNodes[i];
+                    if (node.nodeName == "LI") {
+                        node.onmouseover = function() {
+                            this.className += " over";
+                        };
+                        node.onmouseout = function() {
+                            this.className = this.className.replace(" over", "");
+                        };
+                    }
+                }
+            }
+        };
+        window.onload=startList;
+    </script>
+    <style type="text/css">
+        #nav {
+            padding: 0; margin: 0; list-style: none;
+        }
+        #nav li {
+            float: left; position: relative; width: 10em;
+        }
+        #nav li ul {
+            display: none; position: absolute; top: 1em; left: 0;
+        }
+        #nav li > ul { top: auto; left: auto; }
+        #nav li:hover ul, #nav li.over ul{ display: block; background: white; }
+    </style>
+</head>
+<body>
+<h1>Type Stuff</h1>
+
+<div>
+    <ul id="nav">
+        <li id="menu1">Menu 1
+            <ul>
+                <li id="item1" onclick="displayMessage('item 1');">Item 1</li>
+                <li>Item 2</li>
+            </ul>
+        </li>
+    </ul>
+</div>
+
+<div id="resultContainer" height="30">&nbsp;
+  <div id="result" style="width:300;height:60">
+    <p>&nbsp;</p>
+  </div>
+
+</div>
+                                                                                         
+<div id="formageddon">
+    <form action="#">
+        Key Up: <input type="text" id="keyUp" onkeyup="javascript:updateContent(this)"/><br/>
+        Key Down: <input type="text" id="keyDown" onkeydown="javascript:updateContent(this)"/><br/>
+        Key Press: <input type="text" id="keyPress" onkeypress="javascript:updateContent(this)"/><br/>
+        Change: <input type="text" id="change" onkeypress="javascript:displayMessage('change')"/><br/>
+        <textarea id="keyDownArea" onkeydown="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+        <textarea id="keyPressArea" onkeypress="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+        <textarea id="keyUpArea" onkeyup="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+        <select id="selector" onchange="javascript:updateContent(this)">
+            <option value="foo">Foo</option>
+            <option value="bar">Bar</option>
+        </select>
+        <input type="checkbox" id="checkbox" value="checkbox thing" onchange="javascript:updateContent(this)"/>
+        <input id="clickField" type="text" onclick="document.getElementById('clickField').value='Clicked';" value="Hello"/>
+        <input id="doubleClickField" type="text" onclick="document.getElementById('doubleClickField').value='Clicked';" ondblclick="document.getElementById('doubleClickField').value='DoubleClicked';" oncontextmenu="document.getElementById('doubleClickField').value='ContextClicked'; return false;" value="DoubleHello"/>
+        <input id="clearMe" value="Something" onchange="displayMessage('Cleared')"/>
+    </form>
+</div>
+
+<div>
+    <p><a href="#" onclick="javascript:document.title='Changed'">Change the page title!</a></p>
+
+    <p><a onclick="javascript:document.title='Changed'" id="nohref">No href</a></p>
+
+    <p><a id="updatediv" href="#" onclick="javascript:document.getElementById('dynamo').innerHTML = 'Fish and chips!';">Update a
+        div</a></p>
+</div>
+
+<div id="dynamo">What's for dinner?</div>
+
+<div id="mousedown" onmousedown="javascript:displayMessage('mouse down');">
+    <p>Click for the mouse down event</p>
+    <span><p id="child">Here's some text</p></span>
+</div>
+
+<div id="mouseup" onmouseup="javascript:displayMessage('mouse up');">
+    <p>Click for the mouse up event</p>
+</div>
+
+<div id="mouseclick" onclick="javascript:displayMessage('mouse click');">
+    <p>Click for the mouse click event</p>
+</div>
+
+<div id="error" onclick="document.getElementById('doesnotexist').innerHTML = 'cheese';">
+    Clicking this causes a JS exception in the click handler
+</div>
+
+<div>
+  <form action="resultPage.html" id="on-form">
+    <input id="theworks"
+           onfocus="appendMessage('focus')"
+           onkeydown="appendMessage('keydown')"
+           onkeypress="appendMessage('keypress')"
+           onkeyup="appendMessage('keyup')"
+           onblur="appendMessage('blur')"
+           onchange="appendMessage('change')"
+           />
+
+    <input id="changeable" name="changeable" onfocus="appendMessage('focus')" onchange="appendMessage('change')" onblur="appendMessage('blur')"/>
+           
+    <button type="button" id="plainButton" 
+    		onfocus="appendMessage('focus')"
+           	onkeydown="appendMessage('keydown')"
+           	onkeypress="appendMessage('keypress')"
+           	onkeyup="appendMessage('keyup')"
+           	onblur="appendMessage('blur')"
+           	onclick="appendMessage('click')"
+           	onmousedown="appendMessage('mousedown ')" 
+           	onmouseup="appendMessage('mouseup ')"
+            onmouseover="register('mouseover ')"
+            onmousemove="register('mousemove ')"
+    >
+        <b>Go somewhere</b>
+    </button>
+    <button type="submit" id="submittingButton"><emph>submit</emph></button>
+    <button type="button" id="jsSubmitButton" onclick="javascript:document.getElementById('on-form').submit();">Submitomatic</button>
+
+    <button type="button" id="switchFocus" onclick="document.getElementById('theworks').focus();">Switch focus</button>
+    <button type="button" onclick="var element = document.getElementById('switchFocus'); var clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent('click', true, true, null, 0, 0, 0, 0, 0,false, false, false, false, 0, element);element.dispatchEvent(clickEvent);">Do magic</button><br/>
+    <label id="labelForCheckbox" for="labeledCheckbox" onclick="appendMessage('labelclick')">Toggle checkbox</label><input type="checkbox" id="labeledCheckbox" onclick="appendMessage('chboxclick')"/>
+  </form>
+  
+  <form action="javascriptPage.html" id="submitListeningForm" onsubmit="appendMessage('form-onsubmit '); return false;">
+    <p>
+      <input id="submitListeningForm-text" type="text" onsubmit="appendMessage('text-onsubmit ')" onclick="appendMessage('text-onclick ');" />
+      <input id="submitListeningForm-submit" type="submit" onsubmit="appendMessage('submit-onsubmit ')" onclick="appendMessage('submit-onclick ');" />
+    </p>
+  </form>
+</div>
+
+<p id="suppressedParagraph" style="display: none">A paragraph suppressed using CSS display=none</p>
+
+<div>
+    <p id="displayed">Displayed</p>
+
+    <form action="#"><input type="hidden" name="hidden" /> </form>
+
+    <p id="none" style="display: none;">Display set to none</p>
+
+    <p id="hidden" style="visibility: hidden;">Hidden</p>
+
+    <div id="hiddenparent" style="height: 2em; display: none;">
+      <div id="hiddenchild">
+        <a href="#" id="hiddenlink">ok</a>
+      </div>
+    </div>
+    
+    <div style="visibility: hidden;">
+      <span>
+        <input id="unclickable" />
+        <input type="checkbox" id="untogglable" checked="checked" />Check box you can't see
+      </span>
+    </div>
+
+    <p id="outer" style="visibility: hidden">A <b id="visibleSubElement" style="visibility: visible">sub-element that is explicitly visible</b> using CSS visibility=visible</p>
+</div>
+
+<div>
+    <form>
+        <input type="text" id="keyReporter" size="80"
+               onkeyup="appendMessage('up: ' + event.keyCode)"
+               onkeypress="appendMessage('press: ' + event.keyCode)"
+               onkeydown="displayMessage(''); appendMessage('down: ' + event.keyCode)" />
+        <input name="suppress" onkeydown="if (event.preventDefault) event.preventDefault(); event.returnValue = false; return false;" onkeypress="appendMessage('press');"/>
+    </form>
+</div>
+
+<!-- Used for testing styles -->
+<div style="background-color: green;" id="green-parent">
+  <p id="style1">This should be greenish</p>
+    <ul>
+      <li id="green-item">So should this</li>
+      <li id="red-item" style="background-color: red;">But this is red</li>
+    </ul>
+</div>
+
+<a href="#" id="close" onclick="window.close();">Close window</a>
+
+<div id="delete" onclick="var d = document.getElementById('deleted'); this.removeChild(d);">
+    <p id="deleted">I should be deleted when you click my containing div</p>
+    <p>Whereas, I should not</p>
+</div>
+
+<div>
+    <span id="hideMe" onclick="this.style.display = 'none';">Click to hide me.</span>
+</div>
+
+<div style="margin-top: 10px;">
+  Click actions delayed by 3000ms:
+  <div id="clickToShow" onclick="delayedShowHide(3000, true);"
+       style="float: left;width: 100px;height:100px;border: 1px solid black;">
+    Click to show black box
+  </div>
+  <div id="clickToHide" onclick="delayedShowHide(3000, false);"
+       style="float: left;width: 100px;height:100px;border: 1px solid black;
+              background-color: black; color: white; display: none;">
+    Click to hide black box
+  </div>
+  <div style="clear: both"></div>
+</div>
+
+<a id="new_window" onmouseup="window.open('closeable_window.html', 'close_me')" href="#">Click me to open a new window</a>
+
+<a id="throwing-mouseover" onmouseover="throw new Error()" href="#throwing-mouseover">Mouse over me will throw a JS error</a>
+
+<div id="parent">
+    <span id="movable" onmouseover="var p = document.getElementById('movable'); displayMessage('parent matches? ' + (p != event.relatedTarget));">
+        Click on me to show the related target
+    </span>
+</div>
+
+<div id="zero" style="width:0;height:0">
+  <div>
+    <img src="map.png">
+  </div>
+</div>
+
+</body>
+</html>
+
+
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -2,16 +2,17 @@
 # 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/.
 
 marionette.jar:
 % content marionette %content/
   content/marionette-actors.js      (marionette-actors.js)
   content/marionette-listener.js    (marionette-listener.js)
   content/marionette-elements.js    (marionette-elements.js)
+  content/marionette-sendkeys.js    (marionette-sendkeys.js)
   content/marionette-log-obj.js     (marionette-log-obj.js)
   content/marionette-simpletest.js  (marionette-simpletest.js)
   content/marionette-perf.js  (marionette-perf.js)
   content/EventUtils.js  (EventUtils.js)
   content/ChromeUtils.js  (ChromeUtils.js)
 #ifdef ENABLE_TESTS
   content/test.xul  (client/marionette/chrome/test.xul)
   content/test2.xul  (client/marionette/chrome/test2.xul)
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -1145,17 +1145,17 @@ MarionetteDriverActor.prototype = {
    *        the element that will be checked
    *        'value' member holds the value to send to the element
    */
   sendKeysToElement: function MDA_sendKeysToElement(aRequest) {
     if (this.context == "chrome") {
       try {
         let el = this.curBrowser.elementManager.getKnownElement(aRequest.element, this.getCurrentWindow());
         el.focus();
-        utils.sendString(aRequest.value, utils.window);
+        utils.sendString(aRequest.value.join(""), utils.window);
         this.sendOk();
       }
       catch (e) {
         this.sendError(e.message, e.code, e.stack);
       }
     }
     else {
       this.sendAsync("sendKeysToElement", {element:aRequest.element, value: aRequest.value});
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -18,16 +18,17 @@ Cu.import("chrome://marionette/content/m
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");  
 let utils = {};
 utils.window = content;
 // Load Event/ChromeUtils for use with JS scripts:
 loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
 loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils);
 loader.loadSubScript("chrome://marionette/content/atoms.js", utils);
+loader.loadSubScript("chrome://marionette/content/marionette-sendkeys.js", utils);
 
 loader.loadSubScript("chrome://specialpowers/content/specialpowersAPI.js");
 loader.loadSubScript("chrome://specialpowers/content/specialpowers.js");
 
 let marionetteLogObj = new MarionetteLogObj();
 let marionettePerf = new MarionettePerfData();
 
 let isB2G = false;
@@ -654,17 +655,17 @@ function isElementSelected(msg) {
 }
 
 /**
  * Send keys to element
  */
 function sendKeysToElement(msg) {
   try {
     let el = elementManager.getKnownElement(msg.json.element, curWindow);
-    utils.sendKeysToElement(el, msg.json.value);
+    utils.type(curWindow.document, el, msg.json.value.join(""), true);
     sendOk();
   }
   catch (e) {
     sendError(e.message, e.code, e.stack);
   }
 }
 
 /**
new file mode 100644
--- /dev/null
+++ b/testing/marionette/marionette-sendkeys.js
@@ -0,0 +1,383 @@
+/*
+ *  Copyright 2007-2009 WebDriver committers
+ *  Copyright 2007-2009 Google Inc.
+ *  Portions copyright 2012 Software Freedom Conservancy
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *  
+ */
+
+
+var type = function(doc, element, text, releaseModifiers,
+    opt_keysState) {
+
+  var currentTextLength = element.value.length;
+  element.selectionStart = currentTextLength;
+  element.selectionEnd = currentTextLength;
+
+  // For consistency between native and synthesized events, convert common
+  // escape sequences to their Key enum aliases.
+  text = text.replace(new RegExp('\b', 'g'), '\uE003').   // DOM_VK_BACK_SPACE
+      replace(/\t/g, '\uE004').                           // DOM_VK_TAB
+      replace(/(\r\n|\n|\r)/g, '\uE006');                 // DOM_VK_RETURN
+
+  var controlKey = false;
+  var shiftKey = false;
+  var altKey = false;
+  var metaKey = false;
+  if (opt_keysState) {
+    controlKey = opt_keysState.control;
+    shiftKey = opt_keysState.shiftKey;
+    altKey = opt_keysState.alt;
+    metaKey = opt_keysState.meta;
+  }
+
+  shiftCount = 0;
+
+  var upper = text.toUpperCase();
+
+  for (var i = 0; i < text.length; i++) {
+    var c = text.charAt(i);
+
+    // NULL key: reset modifier key states, and continue
+
+    if (c == '\uE000') {
+      if (controlKey) {
+        var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
+        keyEvent(doc, element, "keyup", kCode, 0,
+            controlKey = false, shiftKey, altKey, metaKey, false);
+      }
+
+      if (shiftKey) {
+        var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
+        keyEvent(doc, element, "keyup", kCode, 0,
+            controlKey, shiftKey = false, altKey, metaKey, false);
+      }
+
+      if (altKey) {
+        var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
+        keyEvent(doc, element, "keyup", kCode, 0,
+            controlKey, shiftKey, altKey = false, metaKey, false);
+      }
+
+      if (metaKey) {
+        var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
+        keyEvent(doc, element, "keyup", kCode, 0,
+            controlKey, shiftKey, altKey, metaKey = false, false);
+      }
+
+      continue;
+    }
+
+    // otherwise decode keyCode, charCode, modifiers ...
+
+    var modifierEvent = "";
+    var charCode = 0;
+    var keyCode = 0;
+
+    if (c == '\uE001') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CANCEL;
+    } else if (c == '\uE002') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_HELP;
+    } else if (c == '\uE003') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_SPACE;
+    } else if (c == '\uE004') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_TAB;
+    } else if (c == '\uE005') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CLEAR;
+    } else if (c == '\uE006') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RETURN;
+    } else if (c == '\uE007') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ENTER;
+    } else if (c == '\uE008') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
+      shiftKey = !shiftKey;
+      modifierEvent = shiftKey ? "keydown" : "keyup";
+    } else if (c == '\uE009') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
+      controlKey = !controlKey;
+      modifierEvent = controlKey ? "keydown" : "keyup";
+    } else if (c == '\uE00A') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
+      altKey = !altKey;
+      modifierEvent = altKey ? "keydown" : "keyup";
+    } else if (c == '\uE03D') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
+      metaKey = !metaKey;
+      modifierEvent = metaKey ? "keydown" : "keyup";
+    } else if (c == '\uE00B') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAUSE;
+    } else if (c == '\uE00C') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ESCAPE;
+    } else if (c == '\uE00D') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SPACE;
+      keyCode = charCode = ' '.charCodeAt(0);  // printable
+    } else if (c == '\uE00E') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_UP;
+    } else if (c == '\uE00F') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN;
+    } else if (c == '\uE010') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_END;
+    } else if (c == '\uE011') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_HOME;
+    } else if (c == '\uE012') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_LEFT;
+    } else if (c == '\uE013') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_UP;
+    } else if (c == '\uE014') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RIGHT;
+    } else if (c == '\uE015') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DOWN;
+    } else if (c == '\uE016') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_INSERT;
+    } else if (c == '\uE017') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DELETE;
+    } else if (c == '\uE018') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEMICOLON;
+      charCode = ';'.charCodeAt(0);
+    } else if (c == '\uE019') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_EQUALS;
+      charCode = '='.charCodeAt(0);
+    } else if (c == '\uE01A') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD0;
+      charCode = '0'.charCodeAt(0);
+    } else if (c == '\uE01B') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD1;
+      charCode = '1'.charCodeAt(0);
+    } else if (c == '\uE01C') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD2;
+      charCode = '2'.charCodeAt(0);
+    } else if (c == '\uE01D') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD3;
+      charCode = '3'.charCodeAt(0);
+    } else if (c == '\uE01E') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD4;
+      charCode = '4'.charCodeAt(0);
+    } else if (c == '\uE01F') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD5;
+      charCode = '5'.charCodeAt(0);
+    } else if (c == '\uE020') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD6;
+      charCode = '6'.charCodeAt(0);
+    } else if (c == '\uE021') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD7;
+      charCode = '7'.charCodeAt(0);
+    } else if (c == '\uE022') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD8;
+      charCode = '8'.charCodeAt(0);
+    } else if (c == '\uE023') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD9;
+      charCode = '9'.charCodeAt(0);
+    } else if (c == '\uE024') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_MULTIPLY;
+      charCode = '*'.charCodeAt(0);
+    } else if (c == '\uE025') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ADD;
+      charCode = '+'.charCodeAt(0);
+    } else if (c == '\uE026') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEPARATOR;
+      charCode = ','.charCodeAt(0);
+    } else if (c == '\uE027') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SUBTRACT;
+      charCode = '-'.charCodeAt(0);
+    } else if (c == '\uE028') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DECIMAL;
+      charCode = '.'.charCodeAt(0);
+    } else if (c == '\uE029') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DIVIDE;
+      charCode = '/'.charCodeAt(0);
+    } else if (c == '\uE031') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F1;
+    } else if (c == '\uE032') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F2;
+    } else if (c == '\uE033') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F3;
+    } else if (c == '\uE034') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F4;
+    } else if (c == '\uE035') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F5;
+    } else if (c == '\uE036') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F6;
+    } else if (c == '\uE037') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F7;
+    } else if (c == '\uE038') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F8;
+    } else if (c == '\uE039') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F9;
+    } else if (c == '\uE03A') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F10;
+    } else if (c == '\uE03B') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F11;
+    } else if (c == '\uE03C') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F12;
+    } else if (c == ',' || c == '<') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_COMMA;
+      charCode = c.charCodeAt(0);
+    } else if (c == '.' || c == '>') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PERIOD;
+      charCode = c.charCodeAt(0);
+    } else if (c == '/' || c == '?') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SLASH;
+      charCode = text.charCodeAt(i);
+    } else if (c == '`' || c == '~') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
+      charCode = c.charCodeAt(0);
+    } else if (c == '{' || c == '[') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
+      charCode = c.charCodeAt(0);
+    } else if (c == '\\' || c == '|') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
+      charCode = c.charCodeAt(0);
+    } else if (c == '}' || c == ']') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
+      charCode = c.charCodeAt(0);
+    } else if (c == '\'' || c == '"') {
+      keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_QUOTE;
+      charCode = c.charCodeAt(0);
+    } else {
+      keyCode = upper.charCodeAt(i);
+      charCode = text.charCodeAt(i);
+    }
+
+    // generate modifier key event if needed, and continue
+
+    if (modifierEvent) {
+      keyEvent(doc, element, modifierEvent, keyCode, 0,
+          controlKey, shiftKey, altKey, metaKey, false);
+      continue;
+    }
+
+    // otherwise, shift down if needed
+
+    var needsShift = false;
+    if (charCode) {
+      needsShift = /[A-Z\!\$\^\*\(\)\+\{\}\:\?\|~@#%&_"<>]/.test(c);
+    }
+
+    if (needsShift && !shiftKey) {
+      var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
+      keyEvent(doc, element, "keydown", kCode, 0,
+          controlKey, true, altKey, metaKey, false);
+      shiftCount += 1;
+    }
+
+    // generate key[down/press/up] for key
+
+    var pressCode = keyCode;
+    if (charCode >= 32 && charCode < 127) {
+      pressCode = 0;
+      if (!needsShift && shiftKey && charCode > 32) {
+        // If typing a lowercase character key and the shiftKey is down, the
+        // charCode should be mapped to the shifted key value. This assumes
+        // a default 104 international keyboard layout.
+        if (charCode >= 97 && charCode <= 122) {
+          charCode = charCode + 65 - 97;  // [a-z] -> [A-Z]
+        } else {
+          var mapFrom = '`1234567890-=[]\\;\',./';
+          var mapTo = '~!@#$%^&*()_+{}|:"<>?';
+
+          var value = String.fromCharCode(charCode).
+              replace(/([\[\\\.])/g, '\\$1');
+          var index = mapFrom.search(value);
+          if (index >= 0) {
+            charCode = mapTo.charCodeAt(index);
+          }
+        }
+      }
+    }
+
+    var accepted =
+        keyEvent(doc, element, "keydown", keyCode, 0,
+            controlKey, needsShift || shiftKey, altKey, metaKey, false);
+
+    keyEvent(doc, element, "keypress", pressCode, charCode,
+        controlKey, needsShift || shiftKey, altKey, metaKey, !accepted);
+
+    keyEvent(doc, element, "keyup", keyCode, 0,
+        controlKey, needsShift || shiftKey, altKey, metaKey, false);
+
+    // shift up if needed
+
+    if (needsShift && !shiftKey) {
+      var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
+      keyEvent(doc, element, "keyup", kCode, 0,
+          controlKey, false, altKey, metaKey, false);
+    }
+  }
+
+  // exit cleanup: keyup active modifier keys
+
+  if (controlKey && releaseModifiers) {
+    var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
+    keyEvent(doc, element, "keyup", kCode, 0,
+        controlKey = false, shiftKey, altKey, metaKey, false);
+  }
+
+  if (shiftKey && releaseModifiers) {
+    var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
+    keyEvent(doc, element, "keyup", kCode, 0,
+        controlKey, shiftKey = false, altKey, metaKey, false);
+  }
+
+  if (altKey && releaseModifiers) {
+    var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
+    keyEvent(doc, element, "keyup", kCode, 0,
+        controlKey, shiftKey, altKey = false, metaKey, false);
+  }
+
+  if (metaKey && releaseModifiers) {
+    var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
+    keyEvent(doc, element, "keyup", kCode, 0,
+        controlKey, shiftKey, altKey, metaKey = false, false);
+  }
+
+  return {
+    shiftKey: shiftKey,
+    alt: altKey,
+    meta: metaKey,
+    control: controlKey
+  };
+};
+
+
+var keyEvent = function(doc, element, type, keyCode, charCode,
+                          controlState, shiftState, altState, metaState,
+                          shouldPreventDefault) {
+  var preventDefault = shouldPreventDefault == undefined ? false
+      : shouldPreventDefault;
+
+  var keyboardEvent = doc.createEvent("KeyEvents");
+  var currentView = doc.defaultView;
+
+  keyboardEvent.initKeyEvent(
+      type, //  in DOMString typeArg,
+      true, //  in boolean canBubbleArg
+      true, //  in boolean cancelableArg
+      currentView, //  in nsIDOMAbstractView viewArg
+      controlState, //  in boolean ctrlKeyArg
+      altState, //  in boolean altKeyArg
+      shiftState, //  in boolean shiftKeyArg
+      metaState, //  in boolean metaKeyArg
+      keyCode, //  in unsigned long keyCodeArg
+      charCode);    //  in unsigned long charCodeArg
+
+  if (preventDefault) {
+    keyboardEvent.preventDefault();
+  }
+
+  var win = doc.defaultView;
+  var domUtil = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                .getInterface(Components.interfaces.nsIDOMWindowUtils);
+  return domUtil.dispatchDOMEventViaPresShell(element, keyboardEvent, true);
+};
+