Form Builder - Performance Enhancements

NOTE: This page describes an Orbeon Forms project, not a feature which is currently part of Orbeon Forms.


The Form Builder editor page is the biggest single-page application we have built. Unsurprisingly, there are some performance issues.

Update 2012-04-20


In April 2012, Form Builder uses its new grid editor based on the xxforms:dynamic control, and more client-side code is used e.g. to place edition icons. This means that the situation is a bit different from what is discussed below on this page.

Current situation

  • the DOM can now be smaller because
    • there are less controls per grid cell
    • the grid doesn't need to have all the controls represented in the markup
    • → "Optimize grid cells: switch/case/group relevance in repeats" is no longer important
  • numbers
    • form with 150 controls over 4 sections now takes 500 KB of uncompressed HTML
    • <td><input> takes < 1 KB instead of 12 KB before! (total: 140 KB)
    • that can still be improved
    • Chrome & FF 11 deal fine with that DOM size
  • updates affecting the whole form can sometimes be slower because the entire user interface
  • but most updates are now handled at the section level and can be reasonably fast
  • "live XBL components" is done thanks to full updates to grid

Possible next steps

  • more work on DOM size
    • 60 KB: leading white space
      • check places where whitespace should not be inserted, e.g. annotator, extractor or XBL?
      • what about indentation? why doesn't Saxon fix it?
    • 29 KBL placeholder XBL controls
      • could output just JS for them, no markup
    • 26KB: space saved by using "xf-" instead of "xforms-"
      • 'xforms-" * 6594
    • 12 KB: space saved by using "fb" instead of "fb-dynamic"
      • later: could also look into using ids prefixed by their actual scope instead of their container scope!
    • 4 KB: "Missing or incorrect value"
      • could save this is alert text is only sent when the control is invalid
    • ids are very long within cells
      • fb-dynamic$section-50-section$xf-879$control-96-control$xforms-input-1
  • "Optimize non-relevant dialogs": this is still possible; can that do a difference?

Example of td

As of 2012-04-20:

<td id="fb-dynamic$section-50-section$xf-879$td-94-td" class="xforms-activable fr-grid-td xforms-group">
    <div class="fr-grid-content"
        <span id="fb-dynamic$section-50-section$xf-879$control-96-control"
              class="xforms-control xforms-input">
        <img class="xforms-disabled xforms-help-image"
             src="/orbeon/3.9.42/ops/images/xforms/help.png" title="" alt="">
        <span class="xforms-disabled xforms-help"></span>
        <label class="xforms-label"
        <input id="fb-dynamic$section-50-section$xf-879$control-96-control$xforms-input-1"
        <span class="xforms-alert-inactive xforms-alert">Missing or incorrect value</span>
        <span class="xforms-disabled xforms-hint"></span></span>

This can clearly be improved.

Historical: Issues found when working on large forms

  • General slowness of the UI
  • Lack of kbd shortcuts for cut/paste, inserting controls, opening control properties, etc.

Initial findings

So far we have tracked down the following:
  • DOM in browser can become very large for large forms; this makes Firefox suffer (IE 7 is worse, Safari/Chrome better)
    • main culprit is probably the number of controls within each cell, only one being visible (see solution under "Next Steps")
  • Control bindings updates can easily take 250 ms.
  • Upon insertion of sections, etc., the Ajax responses can be very large, and do not tell the client "insert and shift", but resend everything after the insertion point.
  • Non-visible dialogs and cases add to control updates
  • i18n of all the form, including dialogs, adds to time -> dependencies for i18n stuff

Next steps

Optimize grid cells: switch/case/group relevance in repeats

  • this can save a lot of size in the page and live DOM
  • use a data-driven xf:switch for this, which might be more appropriate and remove the need for many xforms:group. See WG notes on such a feature.
  • templates
    • client-side already has all the necessary templates!
    • no need to dynamically send templates through Ajax
  • server
    • upon initialization
      • server does NOT send markup for hidden cases or non-relevant groups within repeats
      • server markup might have to be changed a little bit
    • consider non-visible case and groups as non-relevant (see bug, + basically relevance optimization)
  • client
    • upon case/group becoming hidden, remove markup from the DOM
    • upon case/group becoming visible, insert markup into the DOM
  • Q:
    • what if entire switch becomes non-relevant? no markup for cases sent?
    • can non-relevant groups benefit?
  • P2
    • also consider switch/case which are not in repeat: could send templates during initialization as well
    • consider templates for XBL: could be sent to client as well? when would it be useful?

Once grid cells are optimized: live XBL components

Currently (2010-10)
  • there might be e.g. 100 components in the library
  • currently, the non-standard ones are not live because they would make grid cells too large
  • make all XBL components within grid cells live!
  • load all components using XSLT/XInclude into form (as we don't yet have dynamic XBL loading)
    • ⇒ this loses the current feature of configuring this per app name
    • ⇒ however section templates are still loaded per app as we interpret them
  • put all components within grid cell, within data-bound switch
  • XPath dependencies should work for that switch!
  • use full updates mode for that switch

Optimize non-relevant dialogs

  • hidden dialogs must not impact performance
    • their controls should not be built/updated/evaluated (like non-relevant case)
    • nested models must be "destroyed" when non-relevant (same lifecycle as if new repeat iteration containing XBL model)

Enable XPath dependencies

[DONE: 2011-02]

Devise new update strategy for inserts

  • Detect insert/shift scenarios
  • Tell client
  • Client to insert (duplicate template) and shift (update ids)

Optimize YUI layout use

  • Check whether YUI Layout does/can operate without moving large center DIV around

Possibly: keep only one section open at a given time

  • init: first section open
  • close all other sections once section open


  • metrics
    • a single <td> in the grid takes 12080 bytes; with 50 controls on the page that's 600 KB of markup
    • tests
      • leaving only xforms:input: down to 6383 bytes
      • +using xf-* classes: 5947 bytes
      • +removing all disabled cases and content of xf-disabled groups: 2871 bytes => total would be 143 KB
      • more metrics on DOM size :

        <td id="xf-1236·1-1-2-2" rowspan="1" class="fb-grid-td">: 13603 bytes
        <div class="fb-control-control">: 7491 bytes
        <span class="fr-inplace-input" id="fb-control-hint-control·1-1-2-2">:1438 bytes
        <span class="fr-inplace-input" id="fb-control-label-control·1-1-2-2">: 1441 bytes
        <div class="fb-grid-cell-icons">: 944 bytes
        <span class="fb-grid-control-icons xforms-group" id="xf-1285·1-1-2-2">: 858 bytes
        went from 13603 to 12080 by removing unneeded labels; example form when from  491009 to 458555

    • will also reduce thanks to reduction of size of in-place editor (xf:case)
  • more metrics: perf of "Application for registration with a Swiss representation"
    • current: 912 KB
    • + removing disabled groups content: 544 KB
    • + removing hidden case content: 428K
    • + removing case separators: 399 KB -> still too much!
    • fb-grid-content: 1712 bytes
    • NOTE: above not right because xforms:group itself must be shown -> use data-bound switch


  • Inserts
    • Don't rebuild entire tree of controls after insert
    • Instead detect impacted repeat node-sets and handle incremental upgrade to tree
    • For first tree build in request, make sure to build tree anyway
  • Dynamic state is large, might take a lot of time to encode (check).
  • When adding a control to a small form, about 13 inserts take place, each taking on average over 60 ms, for over 750 ms of time lost (about the same time is spent somewhere else).