Reputation: 12571
What is a generally accepted way to validate preference values in a Firefox extension, specifically when using the prefwindow mechanism in XUL?
I am introducing some new preferences in one of my extensions that I would like to validate before the preferences window is closed. If there's an error, the user should be allowed to correct the issue, and then proceed. I see that the prefwindow
element has two potentially useful functions to help in this regard:
The former seems to have an associated bug (Bug 474527) that prevents the prefwindow
from remaining open when returning false
from that function. This is bad in that it doesn't give the user an opportunity to immediately correct their mistake.
The latter appears to have the problem that the preferences get saved prior to exiting, which leaves the preferences in a bad state internally.
In addition, the prefwindow
mechanism supports the browser.preferences.instantApply
option, in which preference values are written immediately upon updating the associated control. This makes validation extra tricky. Is there a clean way to validate custom preferences in a Firefox extension, allowing the user to correct any potential mistakes?
Upvotes: 2
Views: 255
Reputation: 57651
Normally you would want to validate the preferences when they are changed. That's something that onchange
attribute (and the corresponding change
event) is good for:
<preference name="preference.name" onchange="validate(this);"/>
The event is fired after the preference value changes. There are two drawbacks:
instantApply
the new preference value is already saved, too late to validate and decline.You can solve the first issue by intercepting the change events for the actual input fields. For example, for a text field you would do:
<input preference="preference.name"
oninput="if (!validate(this)) event.stopPropagation();"
onchange="if (!validate(this)) { event.stopPropagation(); this.focus(); }"/>
So changes that don't validate correctly don't bubble up to the <prefpane>
element and don't get saved. The events to listen to are: input
and change
for text fields, command
for buttons and checkboxes, select
for the <colorpicker>
element.
The second issue is tricky. You still want to validate the input when it happens, showing the message immediately would be bad UI however. I think that the best solution is to assume for each input field initially that it is still "in progress". You would only set a flag that the value is complete when you first see a blur
event on the field. That's when you can show a validation message if necessary (ideally red text showing up in your preference page, not a modal prompt).
So to indicate what the final solution might look like (untested code but I used something like that in the past):
<description id="error" hidden="true">Invalid preference value</description>
<input preference="preference.name"
_errorText="error"
onblur="validate(event);"
oninput="validate(event);"
onchange="validate(event);/>
<script>
function validate(event)
{
// Perform actual validation
var field = event.target;
var valid = isValid(field);
// If this is the blur event then the element is no longer "in progress"
if (event.type == "blur")
{
field._inputDone = true;
if (!valid)
field.focus();
}
// Prevent preferences changing to invalid value
if (!valid)
event.stopPropagation();
// Show or hide error text
var errorText = document.getElementById(field.getAttribute("_errorText"));
errorText.hidden = valid || !field._inputDone;
}
</script>
Upvotes: 2
Reputation: 4537
If you want to validate values as soon as the field is changed so you can handle the instantApply
case, you could hook into the change events for the individual fields (e.g. oninput
for a textbox
). Display an error message and force the focus back to the field if the value is invalid. You can either set it back to a valid value automatically or block the user from closing the dialog until it is fixed.
Upvotes: 1