Keydown Simulation in Chrome Fires Normally But Not the Correct Key

Keydown Simulation in Chrome fires normally but not the correct key

So very very close...

You just needed to override the 'which' property. Here's some sample code:

Podium = {};
Podium.keydown = function(k) {
var oEvent = document.createEvent('KeyboardEvent');

// Chromium Hack
Object.defineProperty(oEvent, 'keyCode', {
get : function() {
return this.keyCodeVal;
}
});
Object.defineProperty(oEvent, 'which', {
get : function() {
return this.keyCodeVal;
}
});

if (oEvent.initKeyboardEvent) {
oEvent.initKeyboardEvent("keydown", true, true, document.defaultView, false, false, false, false, k, k);
} else {
oEvent.initKeyEvent("keydown", true, true, document.defaultView, false, false, false, false, k, 0);
}

oEvent.keyCodeVal = k;

if (oEvent.keyCode !== k) {
alert("keyCode mismatch " + oEvent.keyCode + "(" + oEvent.which + ")");
}

document.dispatchEvent(oEvent);
}

Sample usage:

Podium.keydown(65);

Note: this code is not designed to work in IE, Safari, or other browsers. Well, maybe with Firefox. YMMV.

Firing a keyboard event on Chrome

I just want to throw this basic snippet out there. It works in Chrome and is based on the hack mentioned by Paul Irish.

It uses charCode instead of keyCode (which can be useful in certain situation), but adapt to keyCode if you so please.

var keyboardEvent = new KeyboardEvent('keypress', {bubbles:true}); 
Object.defineProperty(keyboardEvent, 'charCode', {get:function(){return this.charCodeVal;}});
keyboardEvent.charCodeVal = [your char code];
document.body.dispatchEvent(keyboardEvent);

How to simulate key presses or a click with JavaScript?

Simulating a mouse click

My guess is that the webpage is listening to mousedown rather than click (which is bad for accessibility because when a user uses the keyboard, only focus and click are fired, not mousedown). So you should simulate mousedown, click, and mouseup (which, by the way, is what the iPhone, iPod Touch, and iPad do on tap events).

To simulate the mouse events, you can use this snippet for browsers that support DOM 2 Events. For a more foolproof simulation, fill in the mouse position using initMouseEvent instead.

// DOM 2 Events
var dispatchMouseEvent = function(target, var_args) {
var e = document.createEvent("MouseEvents");
// If you need clientX, clientY, etc., you can call
// initMouseEvent instead of initEvent
e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
};
dispatchMouseEvent(element, 'mouseover', true, true);
dispatchMouseEvent(element, 'mousedown', true, true);
dispatchMouseEvent(element, 'click', true, true);
dispatchMouseEvent(element, 'mouseup', true, true);

When you fire a simulated click event, the browser will actually fire the default action (e.g. navigate to the link's href, or submit a form).

In IE, the equivalent snippet is this (unverified since I don't have IE). I don't think you can give the event handler mouse positions.

// IE 5.5+
element.fireEvent("onmouseover");
element.fireEvent("onmousedown");
element.fireEvent("onclick"); // or element.click()
element.fireEvent("onmouseup");

Simulating keydown and keypress

You can simulate keydown and keypress events, but unfortunately in Chrome they only fire the event handlers and don't perform any of the default actions. I think this is because the DOM 3 Events working draft describes this funky order of key events:

  1. keydown (often has default action such as fire click, submit, or textInput events)
  2. keypress (if the key isn't just a modifier key like Shift or Ctrl)
  3. (keydown, keypress) with repeat=true if the user holds down the button
  4. default actions of keydown!!
  5. keyup

This means that you have to (while combing the HTML5 and DOM 3 Events drafts) simulate a large amount of what the browser would otherwise do. I hate it when I have to do that. For example, this is roughly how to simulate a key press on an input or textarea.

// DOM 3 Events
var dispatchKeyboardEvent = function(target, initKeyboradEvent_args) {
var e = document.createEvent("KeyboardEvents");
e.initKeyboardEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
};
var dispatchTextEvent = function(target, initTextEvent_args) {
var e = document.createEvent("TextEvent");
e.initTextEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
};
var dispatchSimpleEvent = function(target, type, canBubble, cancelable) {
var e = document.createEvent("Event");
e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
};

var canceled = !dispatchKeyboardEvent(element,
'keydown', true, true, // type, bubbles, cancelable
null, // window
'h', // key
0, // location: 0=standard, 1=left, 2=right, 3=numpad, 4=mobile, 5=joystick
''); // space-sparated Shift, Control, Alt, etc.
dispatchKeyboardEvent(
element, 'keypress', true, true, null, 'h', 0, '');
if (!canceled) {
if (dispatchTextEvent(element, 'textInput', true, true, null, 'h', 0)) {
element.value += 'h';
dispatchSimpleEvent(element, 'input', false, false);
// not supported in Chrome yet
// if (element.form) element.form.dispatchFormInput();
dispatchSimpleEvent(element, 'change', false, false);
// not supported in Chrome yet
// if (element.form) element.form.dispatchFormChange();
}
}
dispatchKeyboardEvent(
element, 'keyup', true, true, null, 'h', 0, '');

I don't think it is possible to simulate key events in IE.

Fire Keydown event in Google Chrome extension

Gmail is composed of frames.

By default, chrome.tabs.executeScript only injects code in the main (top-window) frame. To also inject the code in the subframes, add allFrames: true to your details.

Also, do NOT use 'send("' + USER_INPUT_HERE + '");' to trigger your function. This makes your application vulnerable to script injections. Use JSON.stringify(USER_INPUT_HERE) to correctly escape the string.

chrome.tabs.executeScript(null, {
file: 'send.js',
allFrames: true
}, function () {
chrome.tabs.executeScript(null, {
code: 'send(' + JSON.stringify(message.value) + ');',
allFrames: true
});
message.value = '';
});

Example of the vulnerability:

User input: " + (function(){while(1)chrome.tabs.create({url:"http://evil.com"})}()) + "


Objects created by the extension are never passed to a page. Properties and property descriptors added to an event are lost when passed to the page. Instead of a content script, I advise to use an injected script What is the difference between an injected script, Content script and extension code? to get the desired effect:

// Script to be injected
var code = '(' + function(message) {
/* .. "function send"'s body from send.js .. */
} + ')(' + JSON.stringify(message.value) + ');';
// Content script which *injects* the script
chrome.tabs.executeScript(null, {
code: 'var s = document.createElement("script");' +
's.textContent = ' + JSON.stringify(code) + ';' +
'(document.head||document.documentElement).appendChild(s);' +
's.parentNode.removeChild(s);' /*<--Clean-up*/
});

For clarification, the previous snippet results in the following code to be injected in the page:

(function(message) { /* logic of send */ })("user input");

The Event object is constructed and passed within Gmail's page, so that all properties are preserved, as if you used the console to execute the script. It does not run in the context of your extension any more.



Related Topics



Leave a reply



Submit