Page MenuHomePhabricator

Appended mw.loader.state() creates invalid response on load.php?only=scripts if content ends without semi-colon
Closed, ResolvedPublic

Description

A user of one of my tools reported a JavaScript error, preventing her to authorise the tool via OAuth on www.mediawiki.org.

I can confirm the issue. Error message:

load.php?debug=false&lang=en&modules=ext.globalCssJs.user&only=scripts&skin=vector&user=Magnus+Mans…:1 Uncaught SyntaxError: Unexpected identifier

Problematic JavaScript code:

mw.loader.load("//en.wikipedia.org/w/index.php?title=User:Magnus Manske/autodesc.js&action=raw&ctype=text/javascript");$(document).ready(function(){if(typeof mw.config.values.wgWikibaseItemId=='undefined'&&mw.config.values.wgAction=='view'&&mw.config.values.wgNamespaceNumber==0&&mw.config.values.wgDBname!='wikidatawiki'){var portletLink=mw.util.addPortletLink('p-tb','#','Create Wikidata item','t-wd_create');$(portletLink).click(function(){var url="https://tools.wmflabs.org/wikidata-todo/duplicity.php?wiki="+mw.config.values.wgDBname+"&page="+encodeURIComponent(mw.config.values.wgTitle);window.location.href=url;return false;});}})mw.loader.state({"ext.globalCssJs.user":"ready"});

Note the missing ";" before the final "mw.loader.state". Overzealous JS optimiser?

Event Timeline

Krinkle renamed this task from OAuth JS error on mediawiki.org to Appended mw.loader.state() creates invalid response on load.php?only=scripts if content ends without semi-colon.May 26 2017, 2:17 PM
Krinkle triaged this task as High priority.
Krinkle edited projects, added Performance-Team; removed MediaWiki-extensions-OAuth.
Krinkle moved this task from Inbox to Confirmed Problem on the MediaWiki-ResourceLoader board.

mw.loader.state() is added for only=scripts requests, by ResourceLoader::makeModuleResponse().

It does to by calling ResourceLoader::makeLoaderStateScript(), minifying the result, and appending it to $out.

Variable $out at this point is the result of calling ResourceLoaderModule::getModuleContent(), taking the 'scripts' index, and minifying the result.

  • getModuleContent() is the public (process-cached) wrapper around ResourceLoaderModule::buildContent which explicitly ensures that scripts ends with a semi-colon and a line-break.
  • The minifier we use (JavaScriptMinifier.php), as far as I can tell does not strip semi-colons in any case. I've verified this also with our unit tests.

I found the culprit.

Enabling debug mode on https://meta.wikimedia.org/w/load.php?debug=true&lang=en&modules=ext.globalCssJs.user&only=scripts&skin=vector&user=Magnus+Manske shows the following:

/*
User:Magnus_Manske/global.js
*/
/* .. */

$(document).ready ( function () {
	if ( typeof mw.config.values.wgWikibaseItemId == 'undefined' && mw.config.values.wgAction == 'view' && mw.config.values.wgNamespaceNumber == 0 && mw.config.values.wgDBname != 'wikidatawiki' ) {
		var portletLink = mw.util.addPortletLink( 'p-tb', '#', 'Create Wikidata item','t-wd_create');
		$(portletLink).click ( function () {
			var url = "https://tools.wmflabs.org/wikidata-todo/duplicity.php?wiki=" + mw.config.values.wgDBname + "&page=" + encodeURIComponent(mw.config.values.wgTitle) ;
			window.location.href = url ;
			return false ;
		} ) ;
	}
})

// Wiki-Labels [[File:User:EpochFail/WikiLabels.js]]
//mw.loader.load( '//labels.wmflabs.org/gadget/loader.js' );
mw.loader.state( {
    "ext.globalCssJs.user": "ready"
} );

Note how the "scripts" portion (before the final statement containing mw.loader.state) doesn't end with a real semi-colon token, but with two inline comments.

As such, the logic that is ensuring a semi-colon at the end in ResourceLoaderModule::buildContent(), is fooled. The logic applies PHP trim() and then asserts whether last character is a semi-colon. And indeed, it is, except it's part of the comment.

So no extra separation semi-colon is needed it seems, then it gets minified (which removes the comment), and then we append mw.loader.state().

Change 361622 had a related patch set uploaded (by Krinkle; owner: Krinkle):
[mediawiki/core@master] resourceloader: Add basic tests for getScript() and buildContent()

https://gerrit.wikimedia.org/r/361622

Change 361622 merged by jenkins-bot:
[mediawiki/core@master] resourceloader: Add basic tests for getScript() and buildContent()

https://gerrit.wikimedia.org/r/361622

Change 361806 had a related patch set uploaded (by Krinkle; owner: Krinkle):
[mediawiki/core@master] resourceloader: Add more concat test cases for makeModuleResponse

https://gerrit.wikimedia.org/r/361806

Change 361810 had a related patch set uploaded (by Krinkle; owner: Krinkle):
[mediawiki/core@master] resourceloader: Use "\n" instead of ";" as separator for scripts

https://gerrit.wikimedia.org/r/361810

Change 361806 merged by jenkins-bot:
[mediawiki/core@master] resourceloader: Add more concat test cases for makeModuleResponse

https://gerrit.wikimedia.org/r/361806

Change 361810 merged by jenkins-bot:
[mediawiki/core@master] resourceloader: Use "\n" instead of ";" as separator for scripts

https://gerrit.wikimedia.org/r/361810