Skip to content

Instantly share code, notes, and snippets.

@alivedise
Last active August 29, 2015 14:26
Show Gist options
  • Save alivedise/bb744a8b2d4b52fe6773 to your computer and use it in GitHub Desktop.
Save alivedise/bb744a8b2d4b52fe6773 to your computer and use it in GitHub Desktop.
Gecko patch of sendEvent
# HG changeset patch
# User Alive Kuo <alegnadise@gmail.com>
# Date 1438156107 -28800
# Wed Jul 29 15:48:27 2015 +0800
# Node ID e88d55beece4d0aeb4158b072d248209d6c2adc1
# Parent d3228c82badd270863c56fc6b395e1ff452bb99a
[mq]: browserkeyevent
diff --git a/dom/browser-element/BrowserElementChildPreload.js b/dom/browser-element/BrowserElementChildPreload.js
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -18,16 +18,273 @@ XPCOMUtils.defineLazyServiceGetter(this,
"nsIAudioChannelService");
let kLongestReturnedString = 128;
function debug(msg) {
//dump("BrowserElementChildPreload - " + msg + "\n");
}
+let textInputProcessorCallback = {
+ onNotify: function(aTextInputProcessor, aNotification) {
+ try {
+ switch (aNotification.type) {
+ case "request-to-commit":
+ // TODO: Send a notification through asyncMessage to the keyboard here.
+ aTextInputProcessor.commitComposition();
+
+ break;
+ case "request-to-cancel":
+ // TODO: Send a notification through asyncMessage to the keyboard here.
+ aTextInputProcessor.cancelComposition();
+
+ break;
+
+ case "notify-detached":
+ // TODO: Send a notification through asyncMessage to the keyboard here.
+ break;
+
+ // TODO: Manage _focusedElement for text input from here instead.
+ // (except for <select> which will be need to handled elsewhere)
+ case "notify-focus":
+ break;
+
+ case "notify-blur":
+ break;
+ }
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }
+};
+
+function _computeKeyCodeFromChar(aChar)
+{
+ if (aChar.length != 1) {
+ return 0;
+ }
+ const nsIDOMKeyEvent = Ci.nsIDOMKeyEvent;
+ if (aChar >= 'a' && aChar <= 'z') {
+ return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
+ }
+ if (aChar >= 'A' && aChar <= 'Z') {
+ return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
+ }
+ if (aChar >= '0' && aChar <= '9') {
+ return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
+ }
+ // returns US keyboard layout's keycode
+ switch (aChar) {
+ case '~':
+ case '`':
+ return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
+ case '!':
+ return nsIDOMKeyEvent.DOM_VK_1;
+ case '@':
+ return nsIDOMKeyEvent.DOM_VK_2;
+ case '#':
+ return nsIDOMKeyEvent.DOM_VK_3;
+ case '$':
+ return nsIDOMKeyEvent.DOM_VK_4;
+ case '%':
+ return nsIDOMKeyEvent.DOM_VK_5;
+ case '^':
+ return nsIDOMKeyEvent.DOM_VK_6;
+ case '&':
+ return nsIDOMKeyEvent.DOM_VK_7;
+ case '*':
+ return nsIDOMKeyEvent.DOM_VK_8;
+ case '(':
+ return nsIDOMKeyEvent.DOM_VK_9;
+ case ')':
+ return nsIDOMKeyEvent.DOM_VK_0;
+ case '-':
+ case '_':
+ return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
+ case '+':
+ case '=':
+ return nsIDOMKeyEvent.DOM_VK_EQUALS;
+ case '{':
+ case '[':
+ return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
+ case '}':
+ case ']':
+ return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
+ case '|':
+ case '\\':
+ return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
+ case ':':
+ case ';':
+ return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
+ case '\'':
+ case '"':
+ return nsIDOMKeyEvent.DOM_VK_QUOTE;
+ case '<':
+ case ',':
+ return nsIDOMKeyEvent.DOM_VK_COMMA;
+ case '>':
+ case '.':
+ return nsIDOMKeyEvent.DOM_VK_PERIOD;
+ case '?':
+ case '/':
+ return nsIDOMKeyEvent.DOM_VK_SLASH;
+ default:
+ return 0;
+ }
+}
+
+function _guessKeyNameFromKeyCode(aKeyCode)
+{
+ let KeyboardEvent = content.KeyboardEvent;
+ switch (aKeyCode) {
+ case KeyboardEvent.DOM_VK_CANCEL:
+ return "Cancel";
+ case KeyboardEvent.DOM_VK_HELP:
+ return "Help";
+ case KeyboardEvent.DOM_VK_BACK_SPACE:
+ return "Backspace";
+ case KeyboardEvent.DOM_VK_TAB:
+ return "Tab";
+ case KeyboardEvent.DOM_VK_CLEAR:
+ return "Clear";
+ case KeyboardEvent.DOM_VK_RETURN:
+ return "Enter";
+ case KeyboardEvent.DOM_VK_SHIFT:
+ return "Shift";
+ case KeyboardEvent.DOM_VK_CONTROL:
+ return "Control";
+ case KeyboardEvent.DOM_VK_ALT:
+ return "Alt";
+ case KeyboardEvent.DOM_VK_PAUSE:
+ return "Pause";
+ case KeyboardEvent.DOM_VK_EISU:
+ return "Eisu";
+ case KeyboardEvent.DOM_VK_ESCAPE:
+ return "Escape";
+ case KeyboardEvent.DOM_VK_CONVERT:
+ return "Convert";
+ case KeyboardEvent.DOM_VK_NONCONVERT:
+ return "NonConvert";
+ case KeyboardEvent.DOM_VK_ACCEPT:
+ return "Accept";
+ case KeyboardEvent.DOM_VK_MODECHANGE:
+ return "ModeChange";
+ case KeyboardEvent.DOM_VK_PAGE_UP:
+ return "PageUp";
+ case KeyboardEvent.DOM_VK_PAGE_DOWN:
+ return "PageDown";
+ case KeyboardEvent.DOM_VK_END:
+ return "End";
+ case KeyboardEvent.DOM_VK_HOME:
+ return "Home";
+ case KeyboardEvent.DOM_VK_LEFT:
+ return "ArrowLeft";
+ case KeyboardEvent.DOM_VK_UP:
+ return "ArrowUp";
+ case KeyboardEvent.DOM_VK_RIGHT:
+ return "ArrowRight";
+ case KeyboardEvent.DOM_VK_DOWN:
+ return "ArrowDown";
+ case KeyboardEvent.DOM_VK_SELECT:
+ return "Select";
+ case KeyboardEvent.DOM_VK_PRINT:
+ return "Print";
+ case KeyboardEvent.DOM_VK_EXECUTE:
+ return "Execute";
+ case KeyboardEvent.DOM_VK_PRINTSCREEN:
+ return "PrintScreen";
+ case KeyboardEvent.DOM_VK_INSERT:
+ return "Insert";
+ case KeyboardEvent.DOM_VK_DELETE:
+ return "Delete";
+ case KeyboardEvent.DOM_VK_WIN:
+ return "OS";
+ case KeyboardEvent.DOM_VK_CONTEXT_MENU:
+ return "ContextMenu";
+ case KeyboardEvent.DOM_VK_SLEEP:
+ return "Standby";
+ case KeyboardEvent.DOM_VK_F1:
+ return "F1";
+ case KeyboardEvent.DOM_VK_F2:
+ return "F2";
+ case KeyboardEvent.DOM_VK_F3:
+ return "F3";
+ case KeyboardEvent.DOM_VK_F4:
+ return "F4";
+ case KeyboardEvent.DOM_VK_F5:
+ return "F5";
+ case KeyboardEvent.DOM_VK_F6:
+ return "F6";
+ case KeyboardEvent.DOM_VK_F7:
+ return "F7";
+ case KeyboardEvent.DOM_VK_F8:
+ return "F8";
+ case KeyboardEvent.DOM_VK_F9:
+ return "F9";
+ case KeyboardEvent.DOM_VK_F10:
+ return "F10";
+ case KeyboardEvent.DOM_VK_F11:
+ return "F11";
+ case KeyboardEvent.DOM_VK_F12:
+ return "F12";
+ case KeyboardEvent.DOM_VK_F13:
+ return "F13";
+ case KeyboardEvent.DOM_VK_F14:
+ return "F14";
+ case KeyboardEvent.DOM_VK_F15:
+ return "F15";
+ case KeyboardEvent.DOM_VK_F16:
+ return "F16";
+ case KeyboardEvent.DOM_VK_F17:
+ return "F17";
+ case KeyboardEvent.DOM_VK_F18:
+ return "F18";
+ case KeyboardEvent.DOM_VK_F19:
+ return "F19";
+ case KeyboardEvent.DOM_VK_F20:
+ return "F20";
+ case KeyboardEvent.DOM_VK_F21:
+ return "F21";
+ case KeyboardEvent.DOM_VK_F22:
+ return "F22";
+ case KeyboardEvent.DOM_VK_F23:
+ return "F23";
+ case KeyboardEvent.DOM_VK_F24:
+ return "F24";
+ case KeyboardEvent.DOM_VK_NUM_LOCK:
+ return "NumLock";
+ case KeyboardEvent.DOM_VK_SCROLL_LOCK:
+ return "ScrollLock";
+ case KeyboardEvent.DOM_VK_VOLUME_MUTE:
+ return "VolumeMute";
+ case KeyboardEvent.DOM_VK_VOLUME_DOWN:
+ return "VolumeDown";
+ case KeyboardEvent.DOM_VK_VOLUME_UP:
+ return "VolumeUp";
+ case KeyboardEvent.DOM_VK_META:
+ return "Meta";
+ case KeyboardEvent.DOM_VK_ALTGR:
+ return "AltGraph";
+ case KeyboardEvent.DOM_VK_ATTN:
+ return "Attn";
+ case KeyboardEvent.DOM_VK_CRSEL:
+ return "CrSel";
+ case KeyboardEvent.DOM_VK_EXSEL:
+ return "ExSel";
+ case KeyboardEvent.DOM_VK_EREOF:
+ return "EraseEof";
+ case KeyboardEvent.DOM_VK_PLAY:
+ return "Play";
+ default:
+ return "Unidentified";
+ }
+}
+
+
function sendAsyncMsg(msg, data) {
// Ensure that we don't send any messages before BrowserElementChild.js
// finishes loading.
if (!BrowserElementIsReady)
return;
if (!data) {
data = { };
@@ -129,16 +386,20 @@ BrowserElementChild.prototype = {
// A cache of the menuitem dom objects keyed by the id we generate
// and pass to the embedder
this._ctxHandlers = {};
// Counter of contextmenu events fired
this._ctxCounter = 0;
this._shuttingDown = false;
+ this._tip = Cc["@mozilla.org/text-input-processor;1"]
+ .createInstance(Ci.nsITextInputProcessor);
+ this._tip.beginInputTransaction(content, textInputProcessorCallback);
+
addEventListener('DOMTitleChanged',
this._titleChangedHandler.bind(this),
/* useCapture = */ true,
/* wantsUntrusted = */ false);
addEventListener('DOMLinkAdded',
this._linkAddedHandler.bind(this),
/* useCapture = */ true,
@@ -212,16 +473,17 @@ BrowserElementChild.prototype = {
let self = this;
let mmCalls = {
"purge-history": this._recvPurgeHistory,
"get-screenshot": this._recvGetScreenshot,
"get-contentdimensions": this._recvGetContentDimensions,
"set-visible": this._recvSetVisible,
"get-visible": this._recvVisible,
+ "send-key-event": this._recvSendKeyEvent,
"send-mouse-event": this._recvSendMouseEvent,
"send-touch-event": this._recvSendTouchEvent,
"get-can-go-back": this._recvCanGoBack,
"get-can-go-forward": this._recvCanGoForward,
"go-back": this._recvGoBack,
"go-forward": this._recvGoForward,
"reload": this._recvReload,
"stop": this._recvStop,
@@ -1257,16 +1519,64 @@ BrowserElementChild.prototype = {
_updateVisibility: function() {
var visible = this._forcedVisible && this._ownerVisible;
if (docShell && docShell.isActive !== visible) {
docShell.isActive = visible;
sendAsyncMsg('visibilitychange', {visible: visible});
}
},
+ _recvSendKeyEvent: function(data) {
+ let json = data.json;
+ let aKey = json.key;
+ let aEvent = json.options;
+ var keyCode = 0, charCode = 0;
+ if (aKey.indexOf("VK_") == 0) {
+ keyCode = content.KeyEvent["DOM_" + aKey];
+ if (!keyCode) {
+ throw "Unknown key: " + aKey;
+ }
+ } else {
+ charCode = aKey.charCodeAt(0);
+ keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
+ }
+
+ var flags = 0;
+ if (aEvent && aEvent.location != undefined) {
+ switch (aEvent.location) {
+ case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
+ flags |= this._tip.KEY_FLAG_LOCATION_STANDARD;
+ break;
+ case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
+ flags |= this._tip.KEY_FLAG_LOCATION_LEFT;
+ break;
+ case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
+ flags |= this._tip.KEY_FLAG_LOCATION_RIGHT;
+ break;
+ case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
+ flags |= this._tip.KEY_FLAG_LOCATION_NUMPAD;
+ break;
+ }
+ }
+
+ let keyboardEvent = new content.KeyboardEvent('', {
+ key: _guessKeyNameFromKeyCode(keyCode),
+ keyCode: keyCode,
+ code: '',
+ location: aEvent.location,
+ repeat: aEvent.repeat
+ });
+ let keydownDefaultPrevented;
+ let consumedFlags = this._tip.keydown(keyboardEvent, flags);
+ keydownDefaultPrevented =
+ !!(this._tip.KEYDOWN_IS_CONSUMED & consumedFlags);
+ this._tip.keyup(keyboardEvent, flags);
+
+ },
+
_recvSendMouseEvent: function(data) {
let json = data.json;
let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
utils.sendMouseEventToWindow(json.type, json.x, json.y, json.button,
json.clickCount, json.modifiers);
},
diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -584,16 +584,25 @@ BrowserElementParent.prototype = {
let offsetY = {};
tabParent.getChildProcessOffset(offsetX, offsetY);
offset.x = offsetX.value;
offset.y = offsetY.value;
}
return offset;
},
+
+ sendKeyEvent: defineNoReturnMethod(function(key, options) {
+ this._sendAsyncMsg("send-key-event", {
+ "key": key,
+ "options": options
+ });
+ }),
+
+
sendMouseEvent: defineNoReturnMethod(function(type, x, y, button, clickCount, modifiers) {
let offset = this.getChildProcessOffset();
x += offset.x;
y += offset.y;
this._sendAsyncMsg("send-mouse-event", {
"type": type,
"x": x,
diff --git a/dom/browser-element/nsIBrowserElementAPI.idl b/dom/browser-element/nsIBrowserElementAPI.idl
--- a/dom/browser-element/nsIBrowserElementAPI.idl
+++ b/dom/browser-element/nsIBrowserElementAPI.idl
@@ -37,16 +37,19 @@ interface nsIBrowserElementAPI : nsISupp
void setFrameLoader(in nsIFrameLoader frameLoader);
void setVisible(in boolean visible);
nsIDOMDOMRequest getVisible();
void setActive(in boolean active);
boolean getActive();
+
+ void sendKeyEvent(in DOMString key,
+ [optional] in jsval options);
void sendMouseEvent(in DOMString type,
in uint32_t x,
in uint32_t y,
in uint32_t button,
in uint32_t clickCount,
in uint32_t mifiers);
void sendTouchEvent(in DOMString aType,
[const, array, size_is(count)] in uint32_t aIdentifiers,
diff --git a/dom/html/nsBrowserElement.cpp b/dom/html/nsBrowserElement.cpp
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -124,16 +124,43 @@ nsBrowserElement::GetActive(ErrorResult&
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
}
return isActive;
}
void
+nsBrowserElement::SendKeyEvent(const nsAString& aKey,
+ const BrowserElementKeyEventOptions& aOptions,
+ ErrorResult& aRv)
+{
+ NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
+ NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
+
+ nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(mBrowserElementAPI);
+ AutoJSAPI jsapi;
+ jsapi.Init(wrappedObj->GetJSObject());
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JS::Value> options(cx);
+
+ if (!ToJSValue(cx, aOptions, &options)) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+
+ nsresult rv = mBrowserElementAPI->SendKeyEvent(aKey,
+ options);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ }
+}
+
+void
nsBrowserElement::SendMouseEvent(const nsAString& aType,
uint32_t aX,
uint32_t aY,
uint32_t aButton,
uint32_t aClickCount,
uint32_t aModifiers,
ErrorResult& aRv)
{
diff --git a/dom/html/nsBrowserElement.h b/dom/html/nsBrowserElement.h
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -15,16 +15,17 @@
class nsFrameLoader;
namespace mozilla {
namespace dom {
struct BrowserElementDownloadOptions;
struct BrowserElementExecuteScriptOptions;
+struct BrowserElementKeyEventOptions;
class BrowserElementNextPaintEventCallback;
class DOMRequest;
enum class BrowserFindCaseSensitivity: uint32_t;
enum class BrowserFindDirection: uint32_t;
} // namespace dom
class ErrorResult;
@@ -37,16 +38,20 @@ public:
nsBrowserElement() : mOwnerIsWidget(false) {}
virtual ~nsBrowserElement() {}
void SetVisible(bool aVisible, ErrorResult& aRv);
already_AddRefed<dom::DOMRequest> GetVisible(ErrorResult& aRv);
void SetActive(bool aActive, ErrorResult& aRv);
bool GetActive(ErrorResult& aRv);
+
+ void SendKeyEvent(const nsAString& aKey,
+ const dom::BrowserElementKeyEventOptions& options,
+ ErrorResult& aRv);
void SendMouseEvent(const nsAString& aType,
uint32_t aX,
uint32_t aY,
uint32_t aButton,
uint32_t aClickCount,
uint32_t aModifiers,
ErrorResult& aRv);
void SendTouchEvent(const nsAString& aType,
diff --git a/dom/webidl/BrowserElement.webidl b/dom/webidl/BrowserElement.webidl
--- a/dom/webidl/BrowserElement.webidl
+++ b/dom/webidl/BrowserElement.webidl
@@ -14,16 +14,33 @@ dictionary BrowserElementDownloadOptions
DOMString? referrer;
};
dictionary BrowserElementExecuteScriptOptions {
DOMString? url;
DOMString? origin;
};
+dictionary BrowserElementKeyEventOptions {
+ long location;
+ DOMString? type;
+ boolean shiftKey;
+ boolean ctrlKey;
+ boolean altKey;
+ boolean metaKey;
+ boolean accelKey;
+ boolean altGrKey;
+ boolean capsLockKey;
+ boolean fnKey;
+ boolean numLockKey;
+ boolean scrollLockKey;
+ boolean symbolLockKey;
+ boolean osKey;
+};
+
[NoInterfaceObject]
interface BrowserElement {
};
BrowserElement implements BrowserElementCommon;
BrowserElement implements BrowserElementPrivileged;
[NoInterfaceObject]
@@ -59,16 +76,22 @@ interface BrowserElementCommon {
void removeNextPaintListener(BrowserElementNextPaintEventCallback listener);
};
[NoInterfaceObject]
interface BrowserElementPrivileged {
[Throws,
Pref="dom.mozBrowserFramesEnabled",
CheckAnyPermissions="browser"]
+ void sendKeyEvent(DOMString key,
+ optional BrowserElementKeyEventOptions options);
+
+ [Throws,
+ Pref="dom.mozBrowserFramesEnabled",
+ CheckAnyPermissions="browser"]
void sendMouseEvent(DOMString type,
unsigned long x,
unsigned long y,
unsigned long button,
unsigned long clickCount,
unsigned long modifiers);
[Throws,
@alivedise
Copy link
Author

In file included from Unified_cpp_dom_html4.cpp:119:0:
/Users/alive/Projects/mozilla-central/dom/html/nsBrowserElement.cpp: In member function 'void mozilla::nsBrowserElement::SendKeyEvent(const nsAString_internal&, const mozilla::dom::BrowserElementKeyEventOptions&, mozilla::ErrorResult&)':
/Users/alive/Projects/mozilla-central/dom/html/nsBrowserElement.cpp:140:60: error: no matching function for call to 'nsIBrowserElementAPI::SendKeyEvent(const nsAString_internal&, const mozilla::dom::BrowserElementKeyEventOptions&)'
/Users/alive/Projects/mozilla-central/dom/html/nsBrowserElement.cpp:140:60: note: candidate is:
In file included from /Users/alive/Projects/mozilla-central/dom/html/nsBrowserElement.h:14:0,
from /Users/alive/Projects/mozilla-central/dom/html/nsBrowserElement.cpp:7,
from Unified_cpp_dom_html4.cpp:119:
../../dist/include/nsIBrowserElementAPI.h:141:20: note: virtual nsresult nsIBrowserElementAPI::SendKeyEvent(const nsAString_internal&, JS::HandleValue)
../../dist/include/nsIBrowserElementAPI.h:141:20: note: no known conversion for argument 2 from 'const mozilla::dom::BrowserElementKeyEventOptions' to 'JS::HandleValue {aka JS::HandleJS::Value}'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment