Details
-
Improvement
-
Resolution: Unresolved
-
Major
-
None
-
17.10.0-rc-1
-
Unknown
-
Description
Context
Measured on a plain page view (Sandbox.WebHome, no images, no comments, no macros, 18.8.0-SNAPSHOT), the server sends a 93,113-byte HTML document of which about 63% is not useful at first paint: 33.5 KB of markup for dialogs and menus nobody opened, and 24.9 KB of inline <script>. The overhead is a near-constant 51-57 KB per page view whatever the page contains, and the document is not compressed, so those are real bytes on the wire every time.
Reproduction steps
- Visit http://localhost:8080/xwiki/bin/view/Sandbox/
- Look at the HTML source of the page, or inspect #tmMoreActions in the browser debugger
- Do not click on "More Actions"
Expected
The contents of the menu are not in the document until the user opens the menu.
Actual
The whole dropdown is rendered server-side on every page view and merely hidden with CSS: 6,453 bytes, 6.9% of the document, for the 14 entries Administer Page, Copy, Move / Rename, Delete, Export, Annotate, Print Preview, Share by Email, Source, Children, Comments, Attachments (1), History, Information.
Opening the menu currently fires zero network requests, which confirms the content is entirely pre-rendered.
It is also server-side work, not only markup
Per page view, whether or not the menu is ever opened:
- three counts are computed even though only non-zero ones are displayed — $doc.getObjects('XWiki.XWikiComments').size() (shortcuts.vm:42), $services.annotations.getAnnotations($doc).size() (:49) and $doc.getAttachmentList().size() (:54), the last being the "Attachments (1)" above;
- the right checks in menus_content.vm:23-45, including getObjectNumbers for users and groups — plus a redundant second $xwiki.hasAccessLevel('view') at :457 when $canView from :23 already holds it;
- roughly 14 $services.localization.render(...) and 14 $services.icon.renderHTML(...) calls;
- three UI extension points resolved through #displaySecureUIX (:198-241), each doing a per-UIX hasAccess('admin', $uix.authorReference, ...) (:211-212) and a full $services.uix.render($uix, 'html/5.0') (:219).
Where it comes from
All under xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/:
- menus_content.vm:426-484 — #macro(displayOptionsMenu), invoked at :483
- menus_content.vm:349-377 — #macro(displayAdminActions) (Copy, Move/Rename, Delete)
- menus_content.vm:382-421 — #macro(displayMoreActions) (Export, Print Preview, Share by Email)
- shortcuts.vm:25-98 — the "Viewers" entries
- menus_macros.vm:137-150 — #macro(submenuitem)
- menus_content.vm:159-185 — #macro(displayMenu), which wraps it in the <dl class="dropdown-menu">
Suggested fix
Fetch the menu body when the menu is opened (show.bs.dropdown), using the established pattern: an xpage= partial that declares its own skin extensions with #initRequiredSkinExtensions() / #sendRequiredSkinExtensions() (templates/display_macros.vm:205-213), which sets the X-XWIKI-HTML-HEAD header that xwiki.js:1692-1755 already consumes on every XHR. A complete 12-line example of such a partial is templates/security/requiredrights/getRequiredRightsInformation.vm, fetched by requiredRightsInformationUpdater.js:26-51. Any lazily inserted markup must fire xwiki:dom:updated afterwards.
Trade-off to accept knowingly: this removes 6.45 KB from every page view but adds one request the first time a user opens the menu, so the menu gains a short latency on first open. For a menu opened on a small fraction of page views that is a good trade, but it is a real one.
Three things the implementation has to handle:
- menus_content.vm:413-414 pulls $xwiki.jsx.use('XWiki.SharePage') / ssx.use from inside the menu build — those calls have to move into the partial.
- The keyboard shortcut handlers at menus_content.vm:490-521 hard-reference DOM ids that only exist because the menu was rendered ($('tmViewSource').href, $('tmActionDelete').href, $('tmEditWiki').href, ...) and must be guarded or moved.
- #displayMenu (:173) only emits the <dl> wrapper if the rendered content is non-blank, i.e. the server currently renders the whole menu just to decide whether to show it. A lazy design has to either always emit the wrapper and hide it when the fetched body comes back empty, or introduce a cheap "would this menu have content?" predicate. This is the one part that needs actual design work.
Note that setting async_enabled = 1 on the UIX instead would not work today: async.js:103 activates on ".xwiki-async:visible" and its MutationObserver only watches addedNodes, so a placeholder inside a hidden dropdown is never fetched. That is worth a separate issue.
Attachments
Issue Links
- relates to
-
XWIKI-24844 Skin extensions using "always" cannot restrict themselves to a set of actions
-
- Open
-
-
XWIKI-24848 The navigation drawer is fully rendered on every page view although it is only shown on click
-
- Open
-