Jonah Bishop
Jonah Bishop

Reputation: 12571

How can I validate my Firefox extension's preferences?

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

Answers (2)

Wladimir Palant
Wladimir Palant

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:

  • In case of instantApply the new preference value is already saved, too late to validate and decline.
  • For text fields the preferences are saved every time a new character is typed. This becomes ugly if you report validation failure while the user is still typing.

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

Matthew Gertner
Matthew Gertner

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

Related Questions