Bug 704879 - (5/6) Add form validation messages. r=lucasr

This commit is contained in:
Margaret Leibovic 2012-03-06 11:56:44 -08:00
parent d15e231c7e
commit 775ec3156f
2 changed files with 129 additions and 16 deletions

View File

@ -74,6 +74,11 @@ public class FormAssistPopup extends ListView implements GeckoEventListener {
private static final int POPUP_MIN_WIDTH_IN_DPI = 200;
private static final int POPUP_ROW_HEIGHT_IN_DPI = 32;
private static enum PopupType { NONE, AUTOCOMPLETE, VALIDATION };
// Keep track of the type of popup we're currently showing
private PopupType mTypeShowing = PopupType.NONE;
public FormAssistPopup(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
@ -85,13 +90,17 @@ public class FormAssistPopup extends ListView implements GeckoEventListener {
setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parentView, View view, int position, long id) {
String value = ((TextView) view).getText().toString();
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value));
hide();
if (mTypeShowing.equals(PopupType.AUTOCOMPLETE)) {
TextView textView = (TextView) view;
String value = textView.getText().toString();
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value));
hide();
}
}
});
GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", this);
GeckoAppShell.registerGeckoEventListener("FormAssist:ValidationMessage", this);
GeckoAppShell.registerGeckoEventListener("FormAssist:Hide", this);
}
@ -99,6 +108,8 @@ public class FormAssistPopup extends ListView implements GeckoEventListener {
try {
if (event.equals("FormAssist:AutoComplete")) {
handleAutoCompleteMessage(message);
} else if (event.equals("FormAssist:ValidationMessage")) {
handleValidationMessage(message);
} else if (event.equals("FormAssist:Hide")) {
handleHideMessage(message);
}
@ -113,15 +124,22 @@ public class FormAssistPopup extends ListView implements GeckoEventListener {
final double zoom = message.getDouble("zoom");
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() {
// Don't show autocomplete popup when using fullscreen VKB
InputMethodManager imm =
(InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (!imm.isFullscreenMode())
show(suggestions, rect, zoom);
showAutoCompleteSuggestions(suggestions, rect, zoom);
}
});
}
private void handleValidationMessage(JSONObject message) throws JSONException {
final String validationMessage = message.getString("validationMessage");
final JSONArray rect = message.getJSONArray("rect");
final double zoom = message.getDouble("zoom");
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() {
showValidationMessage(validationMessage, rect, zoom);
}
});
}
private void handleHideMessage(JSONObject message) {
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() {
@ -130,18 +148,38 @@ public class FormAssistPopup extends ListView implements GeckoEventListener {
});
}
public void show(JSONArray suggestions, JSONArray rect, double zoom) {
private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, R.layout.autocomplete_list_item);
for (int i = 0; i < suggestions.length(); i++) {
try {
try {
for (int i = 0; i < suggestions.length(); i++)
adapter.add(suggestions.get(i).toString());
} catch (JSONException e) {
Log.i(LOGTAG, "JSONException: " + e);
}
} catch (JSONException e) {
Log.e(LOGTAG, "JSONException: " + e);
}
setAdapter(adapter);
if (positionAndShowPopup(rect, zoom))
mTypeShowing = PopupType.AUTOCOMPLETE;
}
// TODO: style the validation message popup differently (bug 731654)
private void showValidationMessage(String validationMessage, JSONArray rect, double zoom) {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, R.layout.autocomplete_list_item);
adapter.add(validationMessage);
setAdapter(adapter);
if (positionAndShowPopup(rect, zoom))
mTypeShowing = PopupType.VALIDATION;
}
// Returns true if the popup is successfully shown, false otherwise
public boolean positionAndShowPopup(JSONArray rect, double zoom) {
// Don't show the form assist popup when using fullscreen VKB
InputMethodManager imm =
(InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isFullscreenMode())
return false;
if (!isShown()) {
setVisibility(View.VISIBLE);
startAnimation(mAnimation);
@ -193,7 +231,7 @@ public class FormAssistPopup extends ListView implements GeckoEventListener {
listLeft = (int) (viewport.width - listWidth);
}
listHeight = sRowHeight * adapter.getCount();
listHeight = sRowHeight * getAdapter().getCount();
// The text box doesnt fit below
if ((listTop + listHeight) > viewport.height) {
@ -217,11 +255,14 @@ public class FormAssistPopup extends ListView implements GeckoEventListener {
mLayout.setMargins(listLeft, listTop, 0, 0);
setLayoutParams(mLayout);
requestLayout();
return true;
}
public void hide() {
if (isShown()) {
setVisibility(View.GONE);
mTypeShowing = PopupType.NONE;
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null));
}
}

View File

@ -2832,20 +2832,31 @@ var ErrorPageEventHandler = {
};
var FormAssistant = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
// Used to keep track of the element that corresponds to the current
// autocomplete suggestions
_currentInputElement: null,
// Keep track of whether or not an invalid form has been submitted
_invalidSubmit: false,
init: function() {
Services.obs.addObserver(this, "FormAssist:AutoComplete", false);
Services.obs.addObserver(this, "FormAssist:Hidden", false);
Services.obs.addObserver(this, "invalidformsubmit", false);
BrowserApp.deck.addEventListener("input", this, false);
BrowserApp.deck.addEventListener("pageshow", this, false);
},
uninit: function() {
Services.obs.removeObserver(this, "FormAssist:AutoComplete");
Services.obs.removeObserver(this, "FormAssist:Hidden");
Services.obs.removeObserver(this, "invalidformsubmit");
BrowserApp.deck.removeEventListener("input", this);
BrowserApp.deck.removeEventListener("pageshow", this);
},
observe: function(aSubject, aTopic, aData) {
@ -2865,15 +2876,43 @@ var FormAssistant = {
}
},
notifyInvalidSubmit: function notifyInvalidSubmit(aFormElement, aInvalidElements) {
if (!aInvalidElements.length)
return;
// Ignore this notificaiton if the current tab doesn't contain the invalid form
if (BrowserApp.selectedBrowser.contentDocument !=
aFormElement.ownerDocument.defaultView.top.document)
return;
this._invalidSubmit = true;
let currentElement = aInvalidElements.queryElementAt(0, Ci.nsISupports);
if (this._showValidationMessage(currentElement))
currentElement.focus();
},
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "input":
let currentElement = aEvent.target;
// Since we can only show one popup at a time, prioritze autocomplete
// suggestions over a form validation message
if (this._showAutoCompleteSuggestions(currentElement))
break;
if (this._showValidationMessage(currentElement))
break;
// If we're not showing autocomplete suggestions, hide the form assist popup
this._hideFormAssistPopup();
break;
// Reset invalid submit state on each pageshow
case "pageshow":
let target = aEvent.originalTarget;
if (target == content.document || target.ownerDocument == content.document)
this._invalidSubmit = false;
}
},
@ -2953,6 +2992,39 @@ var FormAssistant = {
return true;
},
// Only show a validation message if the user submitted an invalid form,
// there's a non-empty message string, and the element is the correct type
_isValidateable: function _isValidateable(aElement) {
if (!this._invalidSubmit ||
!aElement.validationMessage ||
!(aElement instanceof HTMLInputElement ||
aElement instanceof HTMLTextAreaElement ||
aElement instanceof HTMLSelectElement ||
aElement instanceof HTMLButtonElement))
return false;
return true;
},
// Sends a validation message and position data for an element to the Java UI.
// Returns true if there's a validation message to show, false otherwise.
_showValidationMessage: function _sendValidationMessage(aElement) {
if (!this._isValidateable(aElement))
return false;
let positionData = this._getElementPositionData(aElement);
sendMessageToJava({
gecko: {
type: "FormAssist:ValidationMessage",
validationMessage: aElement.validationMessage,
rect: positionData.rect,
zoom: positionData.zoom
}
});
return true;
},
_hideFormAssistPopup: function _hideFormAssistPopup() {
sendMessageToJava({
gecko: { type: "FormAssist:Hide" }