2 * This file is where we decide whether to initialise the modern support browser run-time.
4 * - Beware: This file MUST parse without errors on even the most ancient of browsers!
6 /* eslint-disable no-implicit-globals */
7 /* global $CODE, RLQ:true, NORLQ:true */
10 * See <https://www.mediawiki.org/wiki/Compatibility#Browsers>
12 * Browsers that pass these checks get served our modern run-time. This includes all Grade A
13 * browsers, and some Grade C and Grade X browsers.
15 * The following browsers are known to pass these checks:
21 * - Mobile Safari 11.2+ (iOS 11+)
25 * @return {boolean} User agent is compatible with MediaWiki JS
27 function isCompatible() {
29 // Ensure DOM Level 4 features (including Selectors API).
31 // https://caniuse.com/#feat=queryselector
32 'querySelector' in document &&
34 // Ensure HTML 5 features (including Web Storage API)
36 // https://caniuse.com/#feat=namevalue-storage
37 // https://blog.whatwg.org/this-week-in-html-5-episode-30
38 'localStorage' in window &&
40 // Ensure ES2015 grammar and runtime API (a.k.a. ES6)
42 // In practice, Promise.finally is a good proxy for overall ES6 support and
43 // rejects most unsupporting browsers in one sweep. The feature itself
44 // was specified in ES2018, however.
45 // https://caniuse.com/promise-finally
46 // Chrome 63+, Edge 18+, Opera 50+, Safari 11.1+, Firefox 58+, iOS 11+
48 // eslint-disable-next-line es-x/no-promise, es-x/no-promise-prototype-finally, dot-notation
49 typeof Promise === 'function' && Promise.prototype[ 'finally' ] &&
50 // ES6 Arrow Functions (with default params), this ensures
51 // genuine syntax support for ES6 grammar, not just API coverage.
53 // https://caniuse.com/arrow-functions
54 // Chrome 45+, Safari 10+, Firefox 22+, Opera 32+
56 // Based on Benjamin De Cock's snippet here:
57 // https://gist.github.com/bendc/d7f3dbc83d0f65ca0433caf90378cd95
60 // eslint-disable-next-line no-new, no-new-func
61 new Function( '(a = 0) => a' );
67 // ES6 RegExp.prototype.flags
69 // https://caniuse.com/mdn-javascript_builtins_regexp_flags
70 // Edge 79+ (Chromium-based, rejects MSEdgeHTML-based Edge <= 18)
72 // eslint-disable-next-line es-x/no-regexp-prototype-flags
77 if ( !isCompatible() ) {
78 // Handle basic supported browsers (Grade C).
79 // Undo speculative modern (Grade A) root CSS class `<html class="client-js">`.
80 // See ResourceLoaderClientHtml::getDocumentAttributes().
81 document.documentElement.className = document.documentElement.className
82 .replace( /(^|\s)client-js(\s|$)/, '$1client-nojs$2' );
84 // Process any callbacks for basic support (Grade C).
85 while ( window.NORLQ && NORLQ[ 0 ] ) {
89 push: function ( fn ) {
94 // Clear and disable the modern (Grade A) queue.
99 // Handle modern (Grade A).
101 if ( window.performance && performance.mark ) {
102 performance.mark( 'mwStartup' );
105 // This embeds mediawiki.js, which defines 'mw' and 'mw.loader'.
106 $CODE.defineLoader();
109 * The $CODE placeholder is substituted in ResourceLoaderStartUpModule.php.
115 $CODE.registrations();
117 // First set page-specific config needed by mw.loader (wgUserName)
118 mw.config.set( window.RLCONF || {} );
119 mw.loader.state( window.RLSTATE || {} );
120 mw.loader.load( window.RLPAGEMODULES || [] );
122 // Process RLQ callbacks
124 // The code in these callbacks could've been exposed from load.php and
125 // requested client-side. Instead, they are pushed by the server directly
126 // (from ResourceLoaderClientHtml and other parts of MediaWiki). This
127 // saves the need for additional round trips. It also allows load.php
128 // to remain stateless and sending personal data in the HTML instead.
130 // The HTML inline script lazy-defines the 'RLQ' array. Now that we are
131 // processing it, replace it with an implementation where 'push' actually
132 // considers executing the code directly. This is to ensure any late
133 // arrivals will also be processed. Late arrival can happen because
134 // startup.js is executed asynchronously, concurrently with the streaming
135 // response of the HTML.
136 queue = window.RLQ || [];
137 // Replace RLQ with an empty array, then process the things that were
138 // in RLQ previously. We have to do this to avoid an infinite loop:
139 // non-function items are added back to RLQ by the processing step.
141 RLQ.push = function ( fn ) {
142 if ( typeof fn === 'function' ) {
145 // If the first parameter is not a function, then it is an array
146 // containing a list of required module names and a function.
147 // Do an actual push for now, as this signature is handled
148 // later by mediawiki.base.js.
149 RLQ[ RLQ.length ] = fn;
152 while ( queue[ 0 ] ) {
153 // Process all values gathered so far
154 RLQ.push( queue.shift() );
157 // Clear and disable the basic (Grade C) queue.