mediawiki.page.watch.ajax: Fail early if updateWatchLink is called wrong
authorThiemo Mättig <thiemo.maettig@wikimedia.de>
Thu, 6 Feb 2014 14:33:01 +0000 (15:33 +0100)
committerKrinkle <krinklemail@gmail.com>
Thu, 20 Mar 2014 18:13:32 +0000 (18:13 +0000)
If the exposed function is called from a gadget or user script but
no watch/unwatch link was found the function fails even if the
first parameter is a valid jQuery object as required by the
documentation. $link.attr(...) returns null and null.match(...)
fails.

A very simple example why this can happen is as follows:
mw.page.watch.updateWatchLink( $( '#ca-watch a' ), 'watch', 'loading' );
mw.page.watch.updateWatchLink( $( '#ca-watch a.loading' ), 'unwatch' );
(starts the spinning loading animation and tries to stop it
afterwards but may fail if something went wrong, e.g. the user
clicked the star).

The action parameter needs to be checked because it is used to
build an ID and a message key. Bad values turn the page in an
unrecoverable state (the watch link gets an empty action=, the
label turns into something <undefined> and no script can recover
that broken state since the ID turned into whatever). While such a
check is not necesarry in most cases it is here, because the
function is exposed.

Change-Id: I6ee9a7ee6b7c0fc7a5444674afd1ed6f8cacc858

resources/mediawiki.page/mediawiki.page.watch.ajax.js

index 998a8c2..1041e7f 100644 (file)
        function updateWatchLink( $link, action, state ) {
                var accesskeyTip, msgKey, $li, otherAction;
 
+               // A valid but empty jQuery object shouldn't throw a TypeError
+               if ( !$link.length ) {
+                       return;
+               }
+
+               // Invalid actions shouldn't silently turn the page in an unrecoverable state
+               if ( action !== 'watch' && action !== 'unwatch' ) {
+                       throw new Error( 'Invalid action' );
+               }
+
                // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
                msgKey = state === 'loading' ? action + 'ing' : action;
                otherAction = action === 'watch' ? 'unwatch' : 'watch';
@@ -69,8 +79,6 @@
        function mwUriGetAction( url ) {
                var action, actionPaths, key, i, m, parts;
 
-               actionPaths = mw.config.get( 'wgActionPaths' );
-
                // TODO: Does MediaWiki give action path or query param
                // precedence ? If the former, move this to the bottom
                action = mw.util.getParamValue( 'action', url );
@@ -78,6 +86,7 @@
                        return action;
                }
 
+               actionPaths = mw.config.get( 'wgActionPaths' );
                for ( key in actionPaths ) {
                        if ( actionPaths.hasOwnProperty( key ) ) {
                                parts = actionPaths[key].split( '$1' );