Page MenuHomePhabricator

Allow lazy-loading of enabled gadgets that represent VisualEditor plugins
Open, MediumPublic

Description

The Gadgets extension recently added support for gadgets to conditionally load based on page action ('view', 'edit', etc).[1] However, gadgets configured to load on action=edit don't currently load when VE or 2017WE is used, because VE doesn't reload the full page and builds the editing interface entirely via AJAX, so the server-side onBeforePageDisplay hook through which Gadgets adds the modules is never invoked.

Solution:

[1]: https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Gadgets/+/747112, T204201

Event Timeline

Note that VisualEditor has a plugin interface, which is based on loading RL modules that are listed in mw.libs.ve.targetLoader.addPlugin, or wgVisualEditorPluginModules, or queued via mw.hook( 've.loadModules' ). If a gadget is using this today, then presumably it is already working as intended.

VisualEditor waits for these modules before initialising the document, allowing scripts to register menu options, toolbars, and other workflows. In order to use it from a gadget, I think one currently needs to have two gadgets. One init module that is based on the gadget preference, and a hidden one that contains the main code.

With the new action option deployed this month, we could probably optimise this a bit, e.g. with a single gadget containing the real code as represented by a gadget preference. And then prevent it from loading normally by using something like actions=never. One would then place the init code in something like Common.js instead. This is slightly more efficient, and wasn't possible previously because hidden gadgets are (intentionally) excluded from gadget preferences.

We could make this nicer by having the VisualEditor extension for MediaWiki integrate directly with Gadgets. For example, an option like visualeditorplugin (or maybe a fake entry like actions=visualeditorplugin, but that might be confusing long-term and difficult to unify with other conditions per T63007). This option would only work if both extensions are installed, and then the server-side adds the gadget module name to VisualEditorPluginModules when it is enabled (instead of loading it normally). That way, VisualEditor will take care of loading it when needed. This is important because if we just load it ad-hoc, it might load too early or too late. For VE, plugins must load at a particular time, which by design is done declaratively so that VE can manage this for you.

Note that VisualEditor has a plugin interface, which is based on loading RL modules that are listed in mw.libs.ve.targetLoader.addPlugin, or wgVisualEditorPluginModules, or queued via mw.hook( 've.loadModules' ). If a gadget is using this today, then presumably it is already working as intended.

Thanks for sharing this. There was a race condition in the code previously suggested by the docs (or at least this ended up in RuWP's Common.js):

mw.loader.using( 'ext.visualEditor.desktopArticleTarget.init' ).then( function () {
  mw.libs.ve.addPlugin( function () {
    return mw.loader.using( 'ext.gadget.gadgetName' );
  } );
} );

– this had resulted in tools sometimes being loaded, sometimes not when the user opened &action=edit or &veaction=editsource directly instead of clicking "Edit source" on the article page. With the updated code, all gadgets and even user scripts I checked get loaded in time to add their button.

In order to use it from a gadget, I think one currently needs to have two gadgets. One init module that is based on the gadget preference, and a hidden one that contains the main code.

Are you talking about considerations of reducing traffic (which are more relevant for default gadgets)? Because technically I don't see any limitations for fitting all code in one gadget. And in this case – while default gadgets can be loaded for VE from Common.js the way you mentioned – opt-in gadgets as well as user scripts can be perfectly fine being loaded as a single entity. (Well, two in RuWP as we have a separate gadget for adding tools to all possible toolbars that is used as a dependency.)

And what the issue in the task came down to in RuWP is that – we can't specify |actions=edit (or some variation of it like |actions=edit,visualeditorplugin) for non-default gadgets adding a tool both to WikiEditor and VE (2017WE in our case), because this will disable the tool in 2017WE. This, together with the fact that most WikiEditor tools are also 2017WE tools, slightly reduces the gain from the page action conditioning.

This comment was removed by Kipod.

Yes. It is not working. Impossible to set gadget loading only on editing if we want to add also to VE/NWE.

When you load an article like /wiki/Example, this is a page view that implicitly carries action=view. When clicking "Edit" to load VisualEditor, there is no URL navigation. The editing code kicks on over top of a page view. There is JavaScript code that virtually modifies the address bar to insert veaction=edit. This is purely for navigational aid. If we ignore Edit clicks and focus only on veaction=edit, it would still be the same. They are action=view pageviews where JS code inititates the rest.

As such, from the platform perspective, there is no such thing as the "VisualEditor action". It is as much a part of a page view as, for example, the code for tablesorter and mw-collapsible. To load code that interacts with VisualEditor, it must start from action=view. This is not something the Gadgets extension can change or accomodate, it is how the platform works, and that's the result of a set of compromises that generally make things more good than bad.

[…] In order to use it from a gadget, I think one currently needs to have two gadgets. One init module that is based on the gadget preference, and a hidden one that contains the main code.

[…] I don't see any limitations for fitting all code in one gadget.

If you want to register the code as a single gadget module, then this module must not use the action filter optimisation, or permit at least view,action in the action. If you consider your VE plugin is too large to load on pageviews before clicking "Edit", then you must split it into two gadgets in the way I described. The first one would contain only the logic to call VE addPlugin, pointing to the second (hidden) gadget that VE will load for you if/when you click Edit.

Krinkle triaged this task as Medium priority.

I'll keep this open as a feature request for an entirely new mechanism that would integrate end-to-end within VisualEditor.

Krinkle renamed this task from VisualEditor should load gadgets configured for action=edit to Allow lazy-loading of enabled gadgets that represent VisualEditor plugins.Feb 16 2023, 5:58 PM
Krinkle moved this task from Limbo to Perf recommendation on the Performance-Team (Radar) board.