Bug 757496 - Support mozKeyboard.setSelectedOption[s]. r=fabrice

--HG--
extra : rebase_source : 4f62f03c060524aa62eb388554b1c94d674505b6
This commit is contained in:
Vivien Nicolas 2012-06-06 14:19:33 +02:00
parent 955543dd18
commit a52844af7a
5 changed files with 267 additions and 30 deletions

157
b2g/chrome/content/forms.js Normal file
View File

@ -0,0 +1,157 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* 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/. */
let Ci = Components.interfaces;
let Cc = Components.classes;
let Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
dump("###################################### forms.js loaded\n");
let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
let HTMLOptGroupElement = Ci.nsIDOMHTMLOptGroupElement;
let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
let FormAssistant = {
init: function fa_init() {
addEventListener("focus", this, true, false);
addEventListener("click", this, true, false);
addEventListener("blur", this, true, false);
addMessageListener("Forms:Select:Choice", this);
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
currentTarget: null,
handleEvent: function fa_handleEvent(evt) {
switch (evt.type) {
case "click":
case "focus": {
content.setTimeout(function(self) {
let target = evt.target;
if (target instanceof HTMLSelectElement) {
sendAsyncMessage("Forms:Input", self._getJSON(target));
self.currentTarget = target;
} else if (target instanceof HTMLOptionElement &&
target.parentNode instanceof HTMLSelectElement) {
target = target.parentNode;
sendAsyncMessage("Forms:Input", self._getJSON(target));
self.currentTarget = target;
}
}, 0, this);
break;
}
case "blur": {
let target = this.currentTarget;
if (!target)
return;
this.currentTarget = null;
sendAsyncMessage("Forms:Input", { "type": "blur" });
let e = target.ownerDocument.createEvent("Events");
e.initEvent("change", true, true, target.ownerDocument.defaultView,
0, false, false, false, false, null);
content.setTimeout(function() {
target.dispatchEvent(evt);
}, 0);
break;
}
}
},
receiveMessage: function fa_receiveMessage(msg) {
let json = msg.json;
switch (msg.name) {
case "Forms:Select:Choice":
if (!this.currentTarget)
return;
let options = this.currentTarget.options;
if ("index" in json) {
options.item(json.index).selected = true;
} else if ("indexes" in json) {
for (let i = 0; i < options.length; i++) {
options.item(i).selected = (json.indexes.indexOf(i) != -1);
}
}
break;
}
},
observe: function fa_observe(subject, topic, data) {
Services.obs.removeObserver(this, "xpcom-shutdown");
removeMessageListener("Forms:Select:Choice", this);
},
_getJSON: function fa_getJSON(element) {
let choices = getListForElement(element);
return {
type: element.type.toLowerCase(),
choices: choices
};
}
};
FormAssistant.init();
function getListForElement(element) {
if (!(element instanceof HTMLSelectElement))
return null;
let optionIndex = 0;
let result = {
"multiple": element.multiple,
"choices": []
};
// Build up a flat JSON array of the choices.
// In HTML, it's possible for select element choices to be under a
// group header (but not recursively). We distinguish between headers
// and entries using the boolean "list.group".
let children = element.children;
for (let i = 0; i < children.length; i++) {
let child = children[i];
if (child instanceof HTMLOptGroupElement) {
result.choices.push({
"group": true,
"text": child.label || child.firstChild.data,
"disabled": child.disabled
});
let subchildren = child.children;
for (let j = 0; j < subchildren.length; j++) {
let subchild = subchildren[j];
result.choices.push({
"group": false,
"inGroup": true,
"text": subchild.text,
"disabled": child.disabled || subchild.disabled,
"selected": subchild.selected,
"optionIndex": optionIndex++
});
}
} else if (child instanceof HTMLOptionElement) {
result.choices.push({
"group": false,
"inGroup": false,
"text": child.text,
"disabled": child.disabled,
"selected": child.selected,
"optionIndex": optionIndex++
});
}
}
return result;
};

View File

@ -123,11 +123,11 @@ var shell = {
addPermissions(domains.split(","));
// Load webapi.js as a frame script
let frameScriptUrl = 'chrome://browser/content/webapi.js';
let webapiUrl = 'chrome://browser/content/webapi.js';
try {
messageManager.loadFrameScript(frameScriptUrl, true);
messageManager.loadFrameScript(webapiUrl, true);
} catch (e) {
dump('Error loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n');
dump('Error loading ' + webapiUrl + ' as a frame script: ' + e + '\n');
}
CustomEventManager.init();
@ -406,17 +406,20 @@ var CustomEventManager = {
handleEvent: function custevt_handleEvent(evt) {
let detail = evt.detail;
dump("XXX FIXME : Got a mozContentEvent: " + detail.type);
dump('XXX FIXME : Got a mozContentEvent: ' + detail.type);
switch(detail.type) {
case "desktop-notification-click":
case "desktop-notification-close":
case 'desktop-notification-click':
case 'desktop-notification-close':
AlertsHelper.handleEvent(detail);
break;
case "webapps-install-granted":
case "webapps-install-denied":
case 'webapps-install-granted':
case 'webapps-install-denied':
WebappsHelper.handleEvent(detail);
break;
case 'select-choicechange':
FormsHelper.handleEvent(detail);
break;
}
}
}

View File

@ -9,6 +9,7 @@ chrome.jar:
% content browser %content/
content/dbg-browser-actors.js (content/dbg-browser-actors.js)
content/forms.js (content/forms.js)
content/settings.js (content/settings.js)
* content/shell.xul (content/shell.xul)
* content/shell.js (content/shell.js)

View File

@ -2,14 +2,19 @@
* 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/. */
'use strict';
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIChromeFrameMessageManager);
// -----------------------------------------------------------------------
// MozKeyboard
// -----------------------------------------------------------------------
@ -18,35 +23,90 @@ function MozKeyboard() { }
MozKeyboard.prototype = {
classID: Components.ID("{397a7fdf-2254-47be-b74e-76625a1a66d5}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIB2GKeyboard, Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIObserver]),
classInfo: XPCOMUtils.generateCI({classID: Components.ID("{397a7fdf-2254-47be-b74e-76625a1a66d5}"),
contractID: "@mozilla.org/b2g-keyboard;1",
interfaces: [Ci.nsIB2GKeyboard],
flags: Ci.nsIClassInfo.DOM_OBJECT,
classDescription: "B2G Virtual Keyboard"}),
init: function(aWindow) {
this._utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIB2GKeyboard, Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIObserver
]),
classInfo: XPCOMUtils.generateCI({
"classID": Components.ID("{397a7fdf-2254-47be-b74e-76625a1a66d5}"),
"contractID": "@mozilla.org/b2g-keyboard;1",
"interfaces": [Ci.nsIB2GKeyboard],
"flags": Ci.nsIClassInfo.DOM_OBJECT,
"classDescription": "B2G Virtual Keyboard"
}),
init: function mozKeyboardInit(win) {
messageManager.loadFrameScript("chrome://browser/content/forms.js", true);
messageManager.addMessageListener("Forms:Input", this);
Services.obs.addObserver(this, "inner-window-destroyed", false);
this._window = win;
this._utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
this.innerWindowID = this._utils.currentInnerWindowID;
this._focusHandler = null;
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "inner-window-destroyed") {
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (wId == this.innerWindowID) {
Services.obs.removeObserver(this, "inner-window-destroyed");
this._utils = null;
}
}
uninit: function mozKeyboardUninit() {
Services.obs.removeObserver(this, "inner-window-destroyed");
messageManager.removeMessageListener("Forms:Input", this);
this._window = null;
this._utils = null;
this._focusHandler = null;
},
sendKey: function mozKeyboardSendKey(keyCode, charCode) {
charCode = (charCode == undefined) ? keyCode : charCode;
['keydown', 'keypress', 'keyup'].forEach((function sendKey(type) {
["keydown", "keypress", "keyup"].forEach((function sendKey(type) {
this._utils.sendKeyEvent(type, keyCode, charCode, null);
}).bind(this));
},
setSelectedOption: function mozKeyboardSetSelectedOption(index) {
messageManager.sendAsyncMessage("Forms:Select:Choice", {
"index": index
});
},
setSelectedOptions: function mozKeyboardSetSelectedOptions(indexes) {
messageManager.sendAsyncMessage("Forms:Select:Choice", {
"indexes": indexes || []
});
},
set onfocuschange(val) {
this._focusHandler = val;
},
get onfocuschange() {
return this._focusHandler;
},
receiveMessage: function mozKeyboardReceiveMessage(msg) {
let handler = this._focusHandler;
if (!handler || !(handler instanceof Ci.nsIDOMEventListener))
return;
let detail = {
"detail": msg.json
};
let evt = new this._window.CustomEvent("focuschanged", detail);
handler.handleEvent(evt);
},
observe: function mozKeyboardObserve(subject, topic, data) {
if (topic == "inner-window-destroyed") {
let wId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (wId == this.innerWindowID) {
this.uninit();
}
}
}
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([MozKeyboard]);

View File

@ -7,12 +7,28 @@
[scriptable, uuid(3615a616-571d-4194-bf54-ccf546067b14)]
interface nsIB2GCameraContent : nsISupports
{
/* temporary solution, waiting for getUserMedia */
DOMString getCameraURI([optional] in jsval options);
/* temporary solution, waiting for getUserMedia */
DOMString getCameraURI([optional] in jsval options);
};
[scriptable, uuid(80ad05f8-e5f6-4a36-b25d-5d5a969b365d)]
[scriptable, uuid(53990d7a-ab2a-11e1-8543-7767e4cbcbff)]
interface nsIB2GKeyboard : nsISupports
{
void sendKey(in long aKeyCode, in long aCharCode);
void sendKey(in long keyCode, in long charCode);
// Select the <select> option specified by index.
// If this method is called on a <select> that support multiple
// selection, then the option specified by index will be added to
// the selection.
// If this method is called for a select that does not support multiple
// selection the previous element will be unselected.
void setSelectedOption(in jsval index);
// Select the <select> options specified by indexes. All other options
// will be deselected.
// If this method is called for a <select> that does not support multiple
// selection, then the last index specified in indexes will be selected.
void setSelectedOptions(in jsval indexes);
attribute nsIDOMEventListener onfocuschange;
};