ResourceLoader: Raise MW JavaScript startup requirement to ES6

The UA sniffs that overrode the feature tests are no longer needed.

* MSIE 10: Fine, rejected by feature checks.

* UC Mini "Speed Mode": Redundant, the version that this sniff
  matched is pre-ES6. Current versions of UC Mini don't appear to
  support enabling "Speed Mode" on random websites nor does it offer
  it for Wikipedia specifically.
  Details at https://phabricator.wikimedia.org/T178356#8740573.

* Google Web Light: Redundant, shutdown as of 2022.
  Any references or extensions that still reach the proxy, get
  redirected to our online URLs
  https://googleweblight.com/?lite_url=https://en.m.wikipedia.org/wiki/Banana
  https://phabricator.wikimedia.org/T152602
  https://en.wikipedia.org/wiki/Google_Web_Light

* MeeGo: Redundant, discontinued and presumed rejected.
  Either way, unsupported.

* Opera Mini: Fine, rejected by checks.
  Details at https://phabricator.wikimedia.org/T178356#8740573.

* Ovi Browser: Redundant, discontinued and presumed rejected.
  Either way, unsupported.

* Google Glass: Improve UX (since 2013, T58008).

* NetFront: Redundant. Old versions are presumed rejected.
  Current versions are Chromium-based and presumed fine.
  The exclusion was not UX based, but due to jQuery explicitly not
  supporting it in 2013. This is no longer the case, so we can let
  the feature test lead the way here.

* PlayStation: Redundant, same story as NetFront.
  The version that matched the sniff is presumed rejected.
  Current versions probably fine, but even not, don't match
  our sniff so are already enabled today.

Bug: T178356
Change-Id: Ib6263ce3ffd11af5e501de8857f3e48a248c6210
This commit is contained in:
James D. Forrester 2023-03-24 08:56:01 -04:00
parent 2d706c0aa5
commit 6c7177349b
24 changed files with 82 additions and 458 deletions

View file

@ -14,6 +14,9 @@ See the file UPGRADE for more detailed per-version upgrade instructions from the
oldest supported upgrading version, MediaWiki 1.35.
Some specific notes for MediaWiki 1.41 upgrades are below:
* (T178356) MediaWiki now requires browsers to support ES6 for them to receive
JavaScript, up from ES5. In practice, this primarily means that users of
Internet Explorer 11 (EOL in 2022) will no longer get JavaScript tools.
* …
For notes on 1.40.x and older releases, see HISTORY.

View file

@ -3434,12 +3434,8 @@ config-schema:
List of template files to be loaded for client-side usage via `mw.templates`.
Default: `[]`
- es6 `{boolean}`:
If true, this module will only be executed in browsers that support ES6. You should set
this flag for modules that use ES6 in their JavaScript. Only use this for modules that
provide progressive enhancements that are safe to not load in browsers that are not
modern but still have a substantial user base, like IE11.
Since: MW 1.36
Default: `false`
Since: MW 1.36; ignored since MW 1.41.
Default: `true`
## Examples
**Example: Using an alternate subclass**
```

View file

@ -341,7 +341,7 @@
},
"es6": {
"type": "boolean",
"description": "Whether this module requires an ES6-capable browser. If set to true, loading this module in a non-ES6 browser will cause an error. Using ES6 syntax in modules is not yet supported, but will be in the near future. Default is false."
"description": "Deprecated flag as to whether this module requires an ES6-capable browser; ignored since MediaWiki 1.41. Default is true."
}
}
},

View file

@ -358,7 +358,7 @@
},
"es6": {
"type": "boolean",
"description": "Whether this module requires an ES6-capable browser. If set to true, loading this module in a non-ES6 browser will cause an error. Using ES6 syntax in modules is not yet supported, but will be in the near future. Default is false."
"description": "Deprecated flag as to whether this module requires an ES6-capable browser; ignored since MediaWiki 1.41. Default is true."
}
}
},

View file

@ -5498,14 +5498,9 @@ class MainConfigSchema {
* Default: `[]`
*
* - es6 `{boolean}`:
* If true, this module will only be executed in browsers that support ES6. You should set
* this flag for modules that use ES6 in their JavaScript. Only use this for modules that
* provide progressive enhancements that are safe to not load in browsers that are not
* modern but still have a substantial user base, like IE11.
* Since: MW 1.36; ignored since MW 1.41.
*
* Since: MW 1.36
*
* Default: `false`
* Default: `true`
*
* ## Examples
*

View file

@ -135,9 +135,6 @@ class FileModule extends Module {
/** @var bool Whether CSSJanus flipping should be skipped for this module */
protected $noflip = false;
/** @var bool Whether this module requires the client to support ES6 */
protected $es6 = false;
/**
* @var bool Whether getStyleURLsForDebug should return raw file paths,
* or return load.php urls
@ -236,7 +233,6 @@ class FileModule extends Module {
// Single booleans
case 'debugRaw':
case 'noflip':
case 'es6':
$this->{$member} = (bool)$option;
break;
}
@ -492,7 +488,7 @@ class FileModule extends Module {
}
public function requiresES6() {
return $this->es6;
return true;
}
/**

View file

@ -521,12 +521,12 @@ abstract class Module implements LoggerAwareInterface {
* If the client does not support ES6, attempting to load a module that requires ES6 will
* result in an error.
*
* @stable to override
* @deprecated since 1.41, ignored by ResourceLoader
* @since 1.36
* @return bool
*/
public function requiresES6() {
return false;
return true;
}
/**

View file

@ -245,7 +245,6 @@ class StartUpModule extends Module {
$registryData[$name] = [
'version' => $versionHash,
'dependencies' => $module->getDependencies( $context ),
'es6' => $module->requiresES6(),
'group' => $this->getGroupId( $module->getGroup() ),
'source' => $module->getSource(),
'skip' => $skipFunction,
@ -263,10 +262,7 @@ class StartUpModule extends Module {
// Call mw.loader.register(name, version, dependencies, group, source, skip)
$registrations[] = [
$name,
// HACK: signify ES6 with a ! added at the end of the version
// This avoids having to add another register() parameter, and generating
// a bunch of nulls for ES6-only modules
$data['version'] . ( $data['es6'] ? '!' : '' ),
$data['version'],
$data['dependencies'],
$data['group'],
// Swap default (local) for null
@ -391,14 +387,6 @@ class StartUpModule extends Module {
'$VARS.storeVary' => $context->encodeJson( $this->getStoreVary( $context ) ),
'$VARS.groupUser' => $context->encodeJson( $this->getGroupId( self::GROUP_USER ) ),
'$VARS.groupPrivate' => $context->encodeJson( $this->getGroupId( self::GROUP_PRIVATE ) ),
// Only expose private mw.loader.isES6ForTest in test mode.
'$CODE.test( isES6Supported )' => $conf->get( MainConfigNames::EnableJavaScriptTest ) ?
'(mw.loader.isES6ForTest !== undefined ? mw.loader.isES6ForTest : isES6Supported)' :
'isES6Supported',
// Only expose private mw.redefineFallbacksForTest in test mode.
'$CODE.maybeRedefineFallbacksForTest();' => $conf->get( MainConfigNames::EnableJavaScriptTest ) ?
'mw.redefineFallbacksForTest = defineFallbacks;' :
'',
];
$profilerStubs = [
'$CODE.profileExecuteStart();' => 'mw.loader.profiler.onExecuteStart( module );',

View file

@ -7,7 +7,7 @@
"--builtin-classes": true,
"--processes": "0",
"--warnings-exit-nonzero": true,
"--external": "Blob,File,FileList,HTMLDocument,HTMLElement,HTMLIFrameElement,HTMLInputElement,HTMLBodyElement,HTMLHtmlElement,KeyboardEvent,MouseEvent,Node,Window,XMLDocument",
"--external": "Blob,File,FileList,HTMLDocument,HTMLElement,HTMLIFrameElement,HTMLInputElement,HTMLBodyElement,HTMLHtmlElement,KeyboardEvent,MouseEvent,Node,Window,XMLDocument,Set",
"--output": "docs/js",
"--exclude": [
"resources/src/jquery.tablesorter",

View file

@ -41,8 +41,6 @@
maxQueryLength: 2000
};
window.$CODE = {
test: function () { return true; },
maybeRedefineFallbacksForTest: function () {}
};
// Mock startup.js
window.RLQ = [];

View file

@ -116,6 +116,7 @@ return [
],
],
'es6-polyfills' => [
'deprecated' => 'No longer needed, MediaWiki requires ES6 now, see T178356',
'scripts' => [
'resources/lib/promise-polyfill/promise-polyfill.js',
'resources/src/es6-polyfills/array-find-polyfill.js',
@ -129,15 +130,14 @@ return [
'scripts' => [
'resources/lib/intersection-observer/intersection-observer.js',
'resources/lib/fetch-polyfill/fetch.umd.js',
// The URL polyfill depends on the following in addition to the ES5 baseline
// The URL polyfill depends on the following in addition to the ES6 baseline
// https://github.com/Financial-Times/polyfill-library/blob/v3.110.1/polyfills/URL/config.toml#L10
// - ES6 Array.from (via es6-polyfills)
// - ES6 Symbol.iterator (no fill needed, used conditionally)
'resources/lib/url/URL.js',
'resources/lib/url/URL-toJSON.js',
],
'skipFunction' => 'resources/src/skip-web2017-polyfills.js',
'dependencies' => [ 'es6-polyfills' ]
'dependencies' => []
],
'mediawiki.base' => [
'localBasePath' => "$wgBaseDirectory/resources/src/mediawiki.base",
@ -570,7 +570,6 @@ return [
],
],
'es6' => true,
],
// Alias for 'vue', for backwards compatibility
@ -581,7 +580,6 @@ return [
'dependencies' => [
'vue'
],
'es6' => true,
],
'vuex' => [
@ -611,7 +609,6 @@ return [
'dependencies' => [
'vue',
],
'es6' => true,
],
'wvui' => [
@ -627,7 +624,6 @@ return [
'vue',
'@vue/composition-api',
],
'es6' => true,
],
'wvui-search' => [
@ -642,7 +638,6 @@ return [
'dependencies' => [
'vue'
],
'es6' => true,
],
'@wikimedia/codex' => [
@ -667,7 +662,6 @@ return [
'dependencies' => [
'vue'
],
'es6' => true
],
'@wikimedia/codex-search' => [
@ -692,7 +686,6 @@ return [
'dependencies' => [
'vue'
],
'es6' => true
],
/* MediaWiki */

View file

@ -69,9 +69,7 @@ mw.log.error = Function.prototype.bind.call( console.error, console );
* @return {Function}
*/
mw.log.makeDeprecated = function ( key, msg ) {
// Support IE 11, Safari 5: Use ES6 Set conditionally. Fallback to not logging.
var isFirst = window.Set ? stackSet() : function () {};
var isFirst = stackSet();
return function maybeLog() {
if ( isFirst() ) {
if ( key ) {
@ -101,16 +99,6 @@ mw.log.makeDeprecated = function ( key, msg ) {
* Tracking is disabled by default, except for global variables on `window`.
*/
mw.log.deprecate = function ( obj, key, val, msg, logName ) {
// Support IE 11, ES5: Use ES6 Set conditionally. Fallback to not logging.
//
// Support Safari 5.0: Object.defineProperty throws "not supported on DOM Objects" for
// Node or Element objects (incl. document)
// Safari 4.0 doesn't have this method, and it was fixed in Safari 5.1.
if ( !window.Set ) {
obj[ key ] = val;
return;
}
var maybeLog = mw.log.makeDeprecated(
logName || ( obj === window ? key : null ),
'Use of "' + ( logName || key ) + '" is deprecated.' + ( msg ? ' ' + msg : '' )

View file

@ -151,6 +151,8 @@
@media all and ( max-width: ( @width-breakpoint-tablet - 1 ) ) {
.mw-prefs-search.oo-ui-fieldLayout {
// Fallback is graceful enough.
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
position: sticky;
top: 0;
z-index: 1;

View file

@ -122,6 +122,8 @@
.iw-resultset:nth-child( 1 ) {
.iw-result__header {
// Fallback is graceful enough.
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
border-top: unset;
}
}

View file

@ -1,20 +1,22 @@
{
"root": true,
"extends": [
"wikimedia/client-es5",
"wikimedia/client-es6",
"wikimedia/jsduck"
],
"parserOptions": {
"ecmaVersion": 3
},
"rules": {
"max-len": 0
"max-len": "off",
"no-var": "off"
},
"overrides": [
{
"files": [
"startup.js"
],
"extends": "wikimedia/client-es5",
"parserOptions": {
"ecmaVersion": 3
}

View file

@ -6,40 +6,12 @@
*/
/* global $VARS, $CODE, mw */
/* eslint-disable es-x/no-set, es-x/no-promise-prototype-finally, es-x/no-regexp-prototype-flags */
( function () {
'use strict';
var StringSet,
store,
var store,
hasOwn = Object.hasOwnProperty;
function defineFallbacks() {
// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set>
/**
* @private
* @class StringSet
*/
StringSet = window.Set || function () {
var set = Object.create( null );
return {
add: function ( value ) {
set[ value ] = true;
},
has: function ( value ) {
return value in set;
}
};
};
}
defineFallbacks();
// In test mode, this generates `mw.redefineFallbacksForTest = defineFallbacks;`.
// Otherwise, it produces nothing. See also ResourceLoader\StartUpModule::getScript().
$CODE.maybeRedefineFallbacksForTest();
/**
* Client for ResourceLoader server end point.
*
@ -84,38 +56,6 @@
return hash;
}
// Check whether the browser supports ES6.
// We are feature detecting Promises and Arrow Functions with default params
// (which are good indicators of overall support). An additional test for
// regex behavior filters out Android 4.4.4 and Edge 18 or lower.
// This check doesn't quite guarantee full ES6 support: Safari 11-13 don't
// support non-BMP characters in identifiers, but support all other ES6
// features we care about. To guard against accidentally breaking these
// Safari versions with code they can't parse, we have an eslint rule
// prohibiting non-BMP characters from being used in identifiers.
var isES6Supported =
// Check for Promise support (filters out most non-ES6 browsers)
typeof Promise === 'function' &&
// eslint-disable-next-line dot-notation, no-undef
Promise.prototype[ 'finally' ] &&
// Check for RegExp.prototype.flags (filters out Android 4.4.4 and Edge <= 18)
/./g.flags === 'g' &&
// Test for arrow functions and default arguments, a good proxy for a
// wide range of ES6 support. Borrowed from Benjamin De Cock's snippet here:
// https://gist.github.com/bendc/d7f3dbc83d0f65ca0433caf90378cd95
// This will exclude Safari and Mobile Safari prior to version 10.
( function () {
try {
// eslint-disable-next-line no-new, no-new-func
new Function( '(a = 0) => a' );
return true;
} catch ( e ) {
return false;
}
}() );
/**
* Fired via mw.track on various resource loading errors.
*
@ -538,7 +478,7 @@
* dependencies, such that later modules depend on earlier modules. The array
* contains the module names. If the array contains already some module names,
* this function appends its result to the pre-existing array.
* @param {StringSet} [unresolved] Used to detect loops in the dependency graph.
* @param {Set} [unresolved] Used to detect loops in the dependency graph.
* @throws {Error} If an unknown module or a circular dependency is encountered
*/
function sortDependencies( module, resolved, unresolved ) {
@ -559,7 +499,7 @@
// Create unresolved if not passed in
if ( !unresolved ) {
unresolved = new StringSet();
unresolved = new Set();
}
// Track down dependencies
@ -618,15 +558,14 @@
//
// Most likely due to a cached reference after the module was
// removed, otherwise made redundant, or omitted from the registry
// by the ResourceLoader "target" system or "requiresES6" flag.
// by the ResourceLoader "target" system.
//
// These errors can be common, e.g. queuing an ES6-only module
// unconditionally from the server-side is OK and should fail gracefully
// in ES5 browsers.
// These errors can be common, e.g. queuing an unavailable module
// unconditionally from the server-side is OK and should fail gracefully.
mw.log.warn( 'Skipped unavailable module ' + modules[ i ] );
// Do not track this error as an exception when the module:
// - Is valid, but gracefully filtered out by target system.
// - Is valid, but gracefully filtered out by requiresES6 flag.
// - Was recently valid, but is still referenced in stale cache.
//
// Basically the only reason to track this as exception is when the error
@ -1329,23 +1268,6 @@
throw new Error( 'module already registered: ' + module );
}
version = String( version || '' );
// requiresES6 is encoded as a ! at the end of version
if ( version.slice( -1 ) === '!' ) {
if ( !$CODE.test( isES6Supported ) ) {
// Exclude ES6-only modules from the registry in ES5 browsers.
//
// These must:
// - be gracefully skipped if a top-level page module, in resolveStubbornly().
// - fail hard when otherwise used or depended on, in sortDependencies().
// - be detectable in the public API, per T299677.
return;
}
// Remove the ! at the end to get the real version
version = version.slice( 0, -1 );
}
registry[ module ] = {
// Exposed to execute() for mw.loader.implement() closures.
// Import happens via require().
@ -1354,7 +1276,7 @@
},
// module.export objects for each package file inside this module
packageExports: {},
version: version,
version: String( version || '' ),
dependencies: dependencies || [],
group: typeof group === 'undefined' ? null : group,
source: typeof source === 'string' ? source : 'local',
@ -1411,7 +1333,7 @@
storedImplementations = [],
storedNames = [],
requestNames = [],
batch = new StringSet();
batch = new Set();
// Iterate the list of requested modules, and do one of three things:
// - 1) Nothing (if already loaded or being loaded).
@ -1508,7 +1430,6 @@
* a list of arguments compatible with this method
* @param {string|number} [version] Module version hash (falls backs to empty string)
* Can also be a number (timestamp) for compatibility with MediaWiki 1.25 and earlier.
* A version string that ends with '!' signifies that the module requires ES6 support.
* @param {string[]} [dependencies] Array of module names on which this module depends.
* @param {string} [group=null] Group which the module is in
* @param {string} [source='local'] Name of the source

View file

@ -10,52 +10,38 @@
* See <https://www.mediawiki.org/wiki/Compatibility#Browsers>
*
* Capabilities required for modern run-time:
* - ECMAScript 5
* - ECMAScript 2015 (a.k.a. ES6)
* - DOM Level 4 (including Selectors API)
* - HTML5 (including Web Storage API)
*
* Browsers we support in our modern run-time (Grade A):
* - Chrome 13+
* - IE 11+
* - Firefox 4+
* - Safari 5+
* - Opera 15+
* - Mobile Safari 6.0+ (iOS 6+)
* - Android 4.1+
* - Chrome 21+
* - Edge 79+
* - Opera 38+
* - Firefox 54+
* - Safari 11.1+
* - Mobile Safari 11.2+ (iOS 11+)
* - Android 5.0+
*
* Browsers we support in our no-JavaScript, basic run-time (Grade C):
* - Chrome 1+
* - Opera 15+
* - IE 8+
* - Firefox 3+
* - Safari 3+
* - Opera 15+
* - Mobile Safari 5.0+ (iOS 4+)
* - Android 2.0+
* - WebOS < 1.5
* - PlayStation
* - Symbian-based browsers
* - NetFront-based browser
* - Opera Mini
* - Nokia's Ovi Browser
* - MeeGo's browser
* - Google Glass
* - UC Mini (speed mode on)
* - Opera Mini (Extreme mode)
* - UC Mini (Speed mode)
*
* Other browsers that pass the check are considered unknown (Grade X).
*
* @private
* @param {string} ua User agent string
* @return {boolean} User agent is compatible with MediaWiki JS
*/
function isCompatible( ua ) {
function isCompatible() {
return !!(
// https://caniuse.com/#feat=es5
// https://caniuse.com/#feat=use-strict
( function () {
'use strict';
return !this && Function.prototype.bind;
}() ) &&
// First, check DOM4 and HTML5 features for faster-fail
// https://caniuse.com/#feat=queryselector
'querySelector' in document &&
@ -64,58 +50,38 @@ function isCompatible( ua ) {
// https://blog.whatwg.org/this-week-in-html-5-episode-30
'localStorage' in window &&
// Force certain browsers into Basic mode, even if they pass the check.
// Now, check whether the browser supports ES6.
//
// Some of the below are "remote browsers", where the webpage is actually
// rendered remotely in a capable browser (cloud service) by the vendor,
// with the client app receiving a graphical representation through a
// format that is not HTML/CSS. These get a better user experience if
// we turn JavaScript off, to avoid triggering JavaScript calls, which
// either don't work or require a roundtrip to the server with added
// latency. Note that remote browsers are sometimes referred to as
// "proxy browsers", but that term is also conflated with browsers
// that accelerate or compress web pages through a "proxy", where
// client-side JS would generally be okay.
// ES6 Promise, this rejects most unsupporting browsers.
// https://caniuse.com/promises
//
// Remember:
// ES6 Promise.finally, this rejects Safari 10 and iOS 10.
// https://caniuse.com/promise-finally
// eslint-disable-next-line es-x/no-promise, es-x/no-promise-prototype-finally, dot-notation
typeof Promise === 'function' && Promise.prototype[ 'finally' ] &&
// ES6 Arrow Functions (with default params), this ensures genuine syntax
// support in the engine, not just API coverage.
// https://caniuse.com/arrow-functions
//
// - Add new entries on top, and document why and since when.
// - Please extend the regex instead of adding new ones, for performance.
// - Add a test case to startup.test.js.
//
// Forced into Basic mode:
//
// - MSIE 10: Bugs (since 2018, T187869).
// Low traffic. Reduce support cost by no longer having to workaround
// bugs in its JavaScript APIs.
//
// - UC Mini "Speed Mode": Improve UX, save data (since 2016, T147369).
// Does not have an obvious user agent, other than ending with an
// incomplete `Gecko/` token.
//
// - Google Web Light: Bugs, save data (since 2016, T152602).
// Proxy breaks most JavaScript.
//
// - MeeGo: Bugs (since 2015, T97546).
//
// - Opera Mini: Improve UX, save data. (since 2013, T49572).
// It is a remote browser.
//
// - Ovi Browser: Improve UX, save data (since 2013, T57600).
// It is a remote browser. UA contains "S40OviBrowser".
//
// - Google Glass: Improve UX (since 2013, T58008).
// Run modern browser engine, but limited UI is better served when
// content is expand by default, requiring little interaction.
//
// - NetFront: Unsupported by jQuery (since 2013, commit c46fc74).
// - PlayStation: Unsupported by jQuery (since 2013, commit c46fc74).
//
!ua.match( /MSIE 10|NetFront|Opera Mini|S40OviBrowser|MeeGo|Android.+Glass|^Mozilla\/5\.0 .+ Gecko\/$|googleweblight|PLAYSTATION|PlayStation/ )
// Based on Benjamin De Cock's snippet here:
// https://gist.github.com/bendc/d7f3dbc83d0f65ca0433caf90378cd95
( function () {
try {
// eslint-disable-next-line no-new, no-new-func
new Function( '(a = 0) => a' );
return true;
} catch ( e ) {
return false;
}
}() ) &&
// ES6 RegExp.prototype.flags, this rejects Android 4.4.4 and MSEdge <= 18,
// which was the last Edge Legacy version (MSEdgeHTML-based).
// eslint-disable-next-line es-x/no-regexp-prototype-flags
/./g.flags === 'g'
);
}
if ( !isCompatible( navigator.userAgent ) ) {
if ( !isCompatible() ) {
// Handle basic supported browsers (Grade C).
// Undo speculative modern (Grade A) root CSS class `<html class="client-js">`.
// See ResourceLoaderClientHtml::getDocumentAttributes().

View file

@ -112,7 +112,6 @@ class ResourceLoaderTestModule extends Module {
protected $script = '';
protected $styles = '';
protected $skipFunction = null;
protected $es6 = false;
protected $isRaw = false;
protected $isKnownEmpty = false;
protected $type = Module::LOAD_GENERAL;
@ -169,7 +168,7 @@ class ResourceLoaderTestModule extends Module {
}
public function requiresES6() {
return $this->es6;
return true;
}
public function isRaw() {

View file

@ -951,9 +951,9 @@ class FileModuleTest extends ResourceLoaderTestCase {
public function testRequiresES6() {
$module = new FileModule();
$this->assertFalse( $module->requiresES6(), 'requiresES6 defaults to false' );
$this->assertTrue( $module->requiresES6(), 'requiresES6 defaults to true' );
$module = new FileModule( [ 'es6' => false ] );
$this->assertFalse( $module->requiresES6(), 'requiresES6 is false when set to false' );
$this->assertTrue( $module->requiresES6(), 'requiresES6 is true even when set to false' );
$module = new FileModule( [ 'es6' => true ] );
$this->assertTrue( $module->requiresES6(), 'requiresES6 is true when set to true' );
}

View file

@ -502,7 +502,7 @@ mw.loader.addSource({
mw.loader.register([
[
"test.es6",
"!"
""
]
]);',
] ],
@ -677,7 +677,7 @@ mw.loader.register([
],
[
"test.es6",
"!"
""
]
]);'
] ],

View file

@ -103,8 +103,6 @@ class ResourcesTest extends MediaWikiIntegrationTestCase {
* Example:
* - A depends on B. A has targets: mobile, desktop. B has targets: desktop. Therefore the
* dependency is sometimes unregistered: it's impossible to load module A on mobile.
* - A depends on B. B has requiresES6=true but A does not. In some browsers, B will be
* unregistered at startup and thus impossible to satisfy as dependency.
*/
public function testUnsatisfiableDependencies() {
$data = self::getAllModules();
@ -116,10 +114,6 @@ class ResourcesTest extends MediaWikiIntegrationTestCase {
$depNames = $module->getDependencies( $data['context'] );
$moduleTargets = $module->getTargets();
// Detect incompatible ES6 requirements (T316324)
$requiresES6 = $module->requiresES6();
$incompatibleDepNames = [];
foreach ( $depNames as $depName ) {
$dep = $data['modules'][$depName] ?? null;
if ( !$dep ) {
@ -139,13 +133,7 @@ class ResourcesTest extends MediaWikiIntegrationTestCase {
. "because its dependency '$depName' does not have it\n";
}
}
if ( !$requiresES6 && $dep->requiresES6() ) {
$incompatibleDepNames[] = $depName;
}
}
$this->assertEquals( [], $incompatibleDepNames,
"The module '$moduleName' must not depend on modules with requiresES6=true"
);
}
$this->assertEquals( [], $incompatibleTargetNames, $targetsErrMsg );
}

View file

@ -44,7 +44,6 @@ return [
'test.MediaWiki' => [
'scripts' => [
'tests/qunit/resources/startup/startup.test.js',
'tests/qunit/resources/startup/mediawiki.test.js',
'tests/qunit/resources/startup/mw.Map.test.js',
'tests/qunit/resources/startup/mw.loader.test.js',

View file

@ -1,5 +1,3 @@
/* eslint-disable es-x/no-set */
( function () {
QUnit.module( 'mw.loader', QUnit.newMwEnvironment( {
beforeEach: function ( assert ) {
@ -26,11 +24,6 @@
},
afterEach: function () {
mw.loader.maxQueryLength = 2000;
// Teardown for StringSet shim test
if ( this.nativeSet ) {
window.Set = this.nativeSet;
mw.redefineFallbacksForTest();
}
if ( this.resetStore ) {
mw.loader.store.enabled = null;
mw.loader.store.items = {};
@ -38,7 +31,6 @@
}
// Remove any remaining temporary static state
// exposed for mocking and stubbing.
delete mw.loader.isES6ForTest;
delete mw.loader.testCallback;
delete mw.loader.testFail;
delete mw.getScriptExampleScriptLoaded;
@ -145,8 +137,8 @@
} );
} );
// Covers mw.loader#sortDependencies (with native Set if available)
QUnit.test( '.using() - Error: Circular dependency [StringSet default]', function ( assert ) {
// Covers mw.loader#sortDependencies (with native Set)
QUnit.test( '.using() - Error: Circular dependency [Set]', function ( assert ) {
var done = assert.async();
mw.loader.register( [
@ -165,36 +157,6 @@
.always( done );
} );
// @covers mw.loader#sortDependencies (with fallback shim)
QUnit.test( '.using() - Error: Circular dependency [StringSet shim]', function ( assert ) {
var done = assert.async();
if ( !window.Set ) {
assert.expect( 0 );
done();
return;
}
this.nativeSet = window.Set;
window.Set = undefined;
mw.redefineFallbacksForTest();
mw.loader.register( [
[ 'test.shim.circleA', '0', [ 'test.shim.circleB' ] ],
[ 'test.shim.circleB', '0', [ 'test.shim.circleC' ] ],
[ 'test.shim.circleC', '0', [ 'test.shim.circleA' ] ]
] );
mw.loader.using( 'test.shim.circleC' ).then(
function () {
assert.true( false, 'Unexpected resolution, expected error.' );
},
function ( e ) {
assert.true( /Circular/.test( String( e ) ), 'Detect circular dependency' );
}
)
.always( done );
} );
QUnit.test( '.load() - Error: Circular dependency', function ( assert ) {
var capture = [];
mw.loader.register( [
@ -583,18 +545,11 @@
}, /already registered/, 'duplicate ID from addSource(Object)' );
} );
QUnit.test( '.register() - ES6 support', function ( assert ) {
// Implied: isES6Supported correctly evaluates to true in a modern browsers
QUnit.test( '.register() - ES6 support always true', function ( assert ) {
mw.loader.register( 'test1.regular', 'aaa' );
mw.loader.register( 'test1.es6only', 'bbb!' );
assert.strictEqual( mw.loader.getState( 'test1.regular' ), 'registered' );
assert.strictEqual( mw.loader.getState( 'test1.es6only' ), 'registered' );
mw.loader.isES6ForTest = false;
mw.loader.register( 'test2.regular', 'aaa' );
mw.loader.register( 'test2.es6only', 'bbb!' );
assert.strictEqual( mw.loader.getState( 'test2.regular' ), 'registered' );
assert.strictEqual( mw.loader.getState( 'test2.es6only' ), null );
} );
// This is a regression test because in the past we called getCombinedVersion()

View file

@ -1,167 +0,0 @@
/* global isCompatible: true */
( function () {
var featureTestable, bannedUserAgent;
// Browsers that pass or fail the feature test and get bucketed
// correctly as modern (Grade A) and basic (Grade C) supported. This is tested
// here to make sure they don't get caught in any bad UX-snif regexes.
featureTestable = [
/* Modern support (Grade A) */
// Chrome
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.205 Safari/534.16',
// Firefox 4+
'Mozilla/5.0 (Windows NT 6.1.1; rv:5.0) Gecko/20100101 Firefox/5.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0',
'Mozilla/5.0 (Macintosh; I; Intel Mac OS X 11_7_9; de-LI; rv:1.9b4) Gecko/2012010317 Firefox/10.0a4',
'Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0',
'Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/12.0',
'Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1',
// Kindle Fire
'Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Kindle Fire Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Safari/533.1',
// Safari 5.0+
'Mozilla/5.0 (Macintosh; I; Intel Mac OS X 10_6_7; ru-ru) AppleWebKit/534.31+ (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1',
// Opera 15+ (Chromium-based)
'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36 OPR/15.0.1147.153',
'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36 OPR/16.0.1196.62',
'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36 OPR/23.0.1522.75',
// Internet Explorer 11
'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
// Edge
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246',
// Edge Mobile
'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 640 XL LTE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Edge/12.10166',
// BlackBerry 6+
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9300; en) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.570 Mobile Safari/534.8+',
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+',
'Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.386 Mobile Safari/537.3+',
// Open WebOS 1.4+ (HP Veer 4G)
'Mozilla/5.0 (webOS/2.1.2; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 P160UNA/1.0',
// Firefox Mobile
'Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0',
// iOS
'Mozilla/5.0 (ipod: U;CPU iPhone OS 2_2 like Mac OS X: es_es) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.0 Mobile/3B48b Safari/419.3',
'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3B48b Safari/419.3',
// Android
'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17',
// UC Mini (speed mode off)
'Mozilla/5.0 (Linux; U; Android 6.0.1; en-US; Nexus_5 Build/MMB29S) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1 UCBrowser/10.7.6.805 Mobile',
/* Basic support (Grade C) */
// Internet Explorer < 10
'Mozilla/2.0 (compatible; MSIE 3.03; Windows 3.1)',
'Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)',
'Mozilla/4.0 (compatible; MSIE 5.0; Windows 98;)',
'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1)',
'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; en-US)',
'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)',
'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)',
// Firefox < 4
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2',
'Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.1.1) Gecko/20070311 Firefox/2.0.0.1',
'Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3',
// Opera < 15 (Presto-based)
'Mozilla/5.0 (Windows NT 5.0; U) Opera 7.54 [en]',
'Opera/7.54 (Windows NT 5.0; U) [en]',
'Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.0',
'Opera/8.0 (X11; Linux i686; U; cs)',
'Opera/9.00 (X11; Linux i686; U; de)',
'Opera/9.62 (X11; Linux i686; U; en) Presto/2.1.1',
'Opera/9.80 (Windows NT 6.1; U; en) Presto/2.2.15 Version/10.00',
'Opera/9.80 (Windows NT 6.1; U; ru) Presto/2.8.131 Version/11.10',
'Opera/9.80 (Windows NT 6.1; WOW64; U; pt) Presto/2.10.229 Version/11.62',
'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
'Opera/9.80 (Windows NT 5.1) Presto/2.12.388 Version/12.17',
// BlackBerry < 6
'BlackBerry9300/5.0.0.716 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/133',
'BlackBerry7250/4.0.0 Profile/MIDP-2.0 Configuration/CLDC-1.1',
/* Unknown support (Grade X) */
// Open WebOS < 1.5 (Palm Pre, Palm Pixi)
'Mozilla/5.0 (webOS/1.0; U; en-US) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/1.0 Safari/525.27.1 Pre/1.0',
'Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pixi/1.1 ',
// SymbianOS
'NokiaN95_8GB-3;Mozilla/5.0 SymbianOS/9.2;U;Series60/3.1 NokiaN95_8GB-3/11.2.011 Profile/MIDP-2.0 Configuration/CLDC-1.1 AppleWebKit/413 (KHTML, like Gecko)',
'Nokia7610/2.0 (5.0509.0) SymbianOS/7.0s Series60/2.1 Profile/MIDP-2.0 Configuration/CLDC-1.0 ',
'Mozilla/5.0 (SymbianOS/9.1; U; [en]; SymbianOS/91 Series60/3.0) AppleWebKit/413 (KHTML, like Gecko) Safari/413',
'Mozilla/5.0 (SymbianOS/9.3; Series60/3.2 NokiaE52-2/091.003; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.34 Mobile Safari/533.4',
// Gecko
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.7) Gecko/20060928 (Debian|Debian-1.8.0.7-1) Epiphany/2.14',
'Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.6) Gecko/20070817 IceWeasel/2.0.0.6-g2',
// KHTML
'Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.4 (like Gecko)',
'Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.5 (like Gecko)',
// Text browsers
'Links (2.1pre33; Darwin 8.11.0 Power Macintosh; x)',
'Links (6.9; Unix 6.9-astral sparc; 80x25)',
'Lynx/2.8.6rel.4 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.8g',
'w3m/0.5.1',
// Bots
'Googlebot/2.1 (+http://www.google.com/bot.html)',
'Mozilla/5.0 (compatible; googlebot/2.1; +http://www.google.com/bot.html)',
'Mozilla/5.0 (compatible; YandexBot/3.0)',
// Scripts
'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5',
'Wget/1.9',
'Wget/1.10.1 (Red Hat modified)',
// Unknown
'I\'m an unknown browser',
'I\'m an unknown Glass browser',
// Empty
''
];
// Browsers that pass the feature test but for which we chose to serve
// a basic experience instead. E.g. they have implementation bugs
// for which support would be more expensive than deem worthwhile.
bannedUserAgent = [
/* Basic (Grade C) */
// Internet Explorer 10
'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
// IE Mobile 10
'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)',
// NetFront
'Mozilla/4.0 (compatible; Linux 2.6.10) NetFront/3.3 Kindle/1.0 (screen 600x800)',
'Mozilla/4.0 (compatible; Linux 2.6.22) NetFront/3.4 Kindle/2.0 (screen 824x1200; rotate)',
'Mozilla/4.08 (Windows; Mobile Content Viewer/1.0) NetFront/3.2',
// Opera Mini
'Opera/9.80 (J2ME/MIDP; Opera Mini/3.1.10423/22.387; U; en) Presto/2.5.25 Version/10.54',
'Opera/9.50 (J2ME/MIDP; Opera Mini/4.0.10031/298; U; en)',
'Opera/9.80 (J2ME/MIDP; Opera Mini/6.24093/26.1305; U; en) Presto/2.8.119 Version/10.54',
'Opera/9.80 (Android; Opera Mini/7.29530/27.1407; U; en) Presto/2.8.119 Version/11.10',
// Ovi Browser
'Mozilla/5.0 (Series40; NokiaX3-02/05.60; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/3.2.0.0.6',
'Mozilla/5.0 (Series40; Nokia305/05.92; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/3.7.0.0.11',
// Google Glass
'Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; Glass 1 Build/IMM76L; XE11) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
// MeeGo
'Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13',
// UC Mini (speed mode on)
'Mozilla/5.0 (X11; U; Linux i686; zh-CN; r:1.2.3.4) Gecko/',
// Google Web Light proxy
'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19',
// PlayStation
'Mozilla/5.0 (PLAYSTATION 3; 1.10)',
'Mozilla/5.0 (PLAYSTATION 3; 3.55)',
'Mozilla/5.0 (PLAYSTATION 3 4.21) AppleWebKit/531.22.8 (KHTML, like Gecko)',
'Mozilla/5.0 (PlayStation 4 1.70) AppleWebKit/536.26 (KHTML, like Gecko)'
];
QUnit.module( 'startup', QUnit.newMwEnvironment() );
QUnit.test( 'isCompatible( featureTestable )', function ( assert ) {
featureTestable.forEach( function ( ua ) {
assert.strictEqual( isCompatible( ua ), true, ua );
} );
} );
QUnit.test( 'isCompatible( bannedUserAgent )', function ( assert ) {
bannedUserAgent.forEach( function ( ua ) {
assert.strictEqual( isCompatible( ua ), false, ua );
} );
} );
}() );