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 107565 ef62a9a2ee425de0ae494159190d4e51eaf8f09a
parent 107564 fa77c8c2a3464037199d9333fa051d89857a6c52
child 107566 6beb523ebcb572d9f09b3416c2bf39dec2507540
push id214
push userakeybl@mozilla.com
push dateWed, 14 Nov 2012 20:38:59 +0000
treeherdermozilla-release@c8b08ec8e1aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs751637
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
+};
+