Bug 1137557 - Part 2: Remove sendKeyEvent from forms.js. r=masayuki, r=smaug (to make the webidl hook happy)

--HG--
extra : source : 83af10efcd3ced1f1ffaa202aeea7de03cf096f9
This commit is contained in:
Tim Chien 2015-07-17 04:41:00 -04:00
Родитель 653afd145a
Коммит 877fd2b4fe
3 изменённых файлов: 320 добавлений и 63 удалений

Просмотреть файл

@ -740,13 +740,15 @@ MozInputContext.prototype = {
sendKey: function ic_sendKey(keyCode, charCode, modifiers, repeat) {
let self = this;
// XXX: modifiers are ignored in this API method.
return this._sendPromise(function(resolverId) {
cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SendKey', {
contextId: self._contextId,
requestId: resolverId,
keyCode: keyCode,
charCode: charCode,
modifiers: modifiers,
repeat: repeat
});
});

Просмотреть файл

@ -18,10 +18,50 @@ XPCOMUtils.defineLazyServiceGetter(Services, "fm",
"@mozilla.org/focus-manager;1",
"nsIFocusManager");
XPCOMUtils.defineLazyGetter(this, "domWindowUtils", function () {
return content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
});
/*
* A WeakMap to map window to objects keeping it's TextInputProcessor instance.
*/
let WindowMap = {
// WeakMap of <window, object> pairs.
_map: null,
/*
* Set the object associated to the window and return it.
*/
_getObjForWin: function(win) {
if (!this._map) {
this._map = new WeakMap();
}
if (this._map.has(win)) {
return this._map.get(win);
} else {
let obj = {
tip: null
};
this._map.set(win, obj);
return obj;
}
},
getTextInputProcessor: function(win) {
if (!win) {
return;
}
let obj = this._getObjForWin(win);
let tip = obj.tip
if (!tip) {
tip = obj.tip = Cc["@mozilla.org/text-input-processor;1"]
.createInstance(Ci.nsITextInputProcessor);
}
if (!tip.beginInputTransaction(win, textInputProcessorCallback)) {
tip = obj.tip = null;
}
return tip;
}
};
const RESIZE_SCROLL_DELAY = 20;
// In content editable node, when there are hidden elements such as <br>, it
@ -41,6 +81,151 @@ let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
let HTMLOptGroupElement = Ci.nsIDOMHTMLOptGroupElement;
let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
function guessKeyNameFromKeyCode(KeyboardEvent, aKeyCode) {
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";
}
}
let FormVisibility = {
/**
* Searches upwards in the DOM for an element that has been scrolled.
@ -184,6 +369,41 @@ let FormVisibility = {
}
};
// This object implements nsITextInputProcessorCallback
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;
}
};
let FormAssistant = {
init: function fa_init() {
addEventListener("focus", this, true, false);
@ -502,34 +722,87 @@ let FormAssistant = {
case "Forms:Input:SendKey":
CompositionManager.endComposition('');
let flags = domWindowUtils.KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS;
this._editing = true;
let doKeypress = domWindowUtils.sendKeyEvent('keydown', json.keyCode,
json.charCode, json.modifiers, flags);
if (doKeypress) {
domWindowUtils.sendKeyEvent('keypress', json.keyCode,
json.charCode, json.modifiers, flags);
let win = target.ownerDocument.defaultView;
let tip = WindowMap.getTextInputProcessor(win);
if (!tip) {
if (json.requestId) {
sendAsyncMessage("Forms:SendKey:Result:Error", {
requestId: json.requestId,
error: "Unable to start input transaction."
});
}
break;
}
if(!json.repeat) {
domWindowUtils.sendKeyEvent('keyup', json.keyCode,
json.charCode, json.modifiers, flags);
// The naive way to figure out if the key to dispatch is printable.
let printable = !!json.charCode;
let keyboardEventDict = {
// For printable keys, the value should be the actual character.
// For non-printable keys, it should be a value in the D3E spec.
// Here we make some educated guess for it.
key: printable ?
String.fromCharCode(json.charCode) :
guessKeyNameFromKeyCode(win.KeyboardEvent, json.keyCode),
// We don't have any information to tell the virtual key the
// user have interacted with.
code: "",
// We violate the spec here and ask TextInputProcessor not to infer
// this value from value of key nor code so we could keep the original
// behavior.
keyCode: json.keyCode,
// We do not have the information to infer location of the virtual key
// either (and we would need TextInputProcessor not to compute it).
location: 0,
// This indicates the key is triggered for repeats.
repeat: json.repeat
};
let keyboardEvent = new win.KeyboardEvent("", keyboardEventDict);
let flags = tip.KEY_KEEP_KEY_LOCATION_STANDARD;
if (!printable) {
flags |= tip.KEY_NON_PRINTABLE_KEY;
}
if (!json.keyCode) {
flags |= tip.KEY_KEEP_KEYCODE_ZERO;
}
this._editing = false;
let keydownDefaultPrevented;
try {
let consumedFlags = tip.keydown(keyboardEvent, flags);
keydownDefaultPrevented =
!!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
if (!json.repeat) {
tip.keyup(keyboardEvent, flags);
}
} catch (e) {
dump("forms.js:" + e.toString() + "\n");
if (json.requestId && doKeypress) {
sendAsyncMessage("Forms:SendKey:Result:OK", {
requestId: json.requestId,
selectioninfo: this.getSelectionInfo()
});
if (json.requestId) {
sendAsyncMessage("Forms:SendKey:Result:Error", {
requestId: json.requestId,
error: "Unable to type into destoryed input."
});
}
break;
}
else if (json.requestId && !doKeypress) {
sendAsyncMessage("Forms:SendKey:Result:Error", {
requestId: json.requestId,
error: "Keydown event got canceled"
});
if (json.requestId) {
if (keydownDefaultPrevented) {
sendAsyncMessage("Forms:SendKey:Result:Error", {
requestId: json.requestId,
error: "Key event(s) was cancelled."
});
} else {
sendAsyncMessage("Forms:SendKey:Result:OK", {
requestId: json.requestId,
selectioninfo: this.getSelectionInfo()
});
}
}
break;
case "Forms:Select:Choice":
@ -1170,7 +1443,7 @@ function replaceSurroundingText(element, text, offset, length) {
let CompositionManager = {
_isStarted: false,
_textInputProcessor: null,
_tip: null,
_clauseAttrMap: {
'raw-input':
Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE,
@ -1182,34 +1455,6 @@ let CompositionManager = {
Ci.nsITextInputProcessor.ATTR_SELECTED_CLAUSE
},
_callback: function cm_callback(aTIP, aNotification)
{
try {
switch (aNotification.type) {
case "request-to-commit":
aTIP.commitComposition();
break;
case "request-to-cancel":
aTIP.cancelComposition();
break;
}
} catch (e) {
return false;
}
return true;
},
_prepareTextInputProcessor: function cm_prepareTextInputProcessor(aWindow)
{
if (!this._textInputProcessor) {
this._textInputProcessor =
Cc["@mozilla.org/text-input-processor;1"].
createInstance(Ci.nsITextInputProcessor);
}
return this._textInputProcessor.beginInputTransaction(aWindow,
this._callback);
},
setComposition: function cm_setComposition(element, text, cursor, clauses) {
// Check parameters.
if (!element) {
@ -1248,30 +1493,39 @@ let CompositionManager = {
}
let win = element.ownerDocument.defaultView;
if (!this._prepareTextInputProcessor(win)) {
let tip = WindowMap.getTextInputProcessor(win);
if (!tip) {
return;
}
// Update the composing text.
this._textInputProcessor.setPendingCompositionString(text);
tip.setPendingCompositionString(text);
for (var i = 0; i < clauseLens.length; i++) {
if (!clauseLens[i]) {
continue;
}
this._textInputProcessor.appendClauseToPendingComposition(clauseLens[i],
clauseAttrs[i]);
tip.appendClauseToPendingComposition(clauseLens[i], clauseAttrs[i]);
}
if (cursor >= 0) {
this._textInputProcessor.setCaretInPendingComposition(cursor);
tip.setCaretInPendingComposition(cursor);
}
this._isStarted = tip.flushPendingComposition();
if (this._isStarted) {
this._tip = tip;
}
this._isStarted = this._textInputProcessor.flushPendingComposition();
},
endComposition: function cm_endComposition(text) {
if (!this._isStarted) {
return;
}
this._textInputProcessor.commitCompositionWith(text ? text : "");
let tip = this._tip;
if (!tip) {
return;
}
tip.commitCompositionWith(text ? text : "");
this._isStarted = false;
this._tip = null;
},
// Composition ends due to external actions.
@ -1281,5 +1535,6 @@ let CompositionManager = {
}
this._isStarted = false;
this._tip = null;
}
};

Просмотреть файл

@ -193,7 +193,7 @@ interface MozInputContext: EventTarget {
/*
* send a character with its key events.
* @param modifiers see http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsIDOMWindowUtils.idl#206
* @param modifiers this paramater is no longer honored.
* @param repeat indicates whether a key would be sent repeatedly.
* @return true if succeeds. Otherwise false if the input context becomes void.
* Alternative: sendKey(KeyboardEvent event), but we will likely