diff --git a/docs/browser-support.rst b/docs/browser-support.rst index 3460358eadf..7a2de57b461 100644 --- a/docs/browser-support.rst +++ b/docs/browser-support.rst @@ -8,7 +8,12 @@ Browser Support =============== -We seek to provide usable experiences of our most important web content to all user agents. But newer browsers are far more capable than older browsers, and the capabilities they provide are valuable to developers and site visitors. We **will** take advantage of modern browser capabilities. Older browsers **will** have a different experience of the website than newer browsers. We will strike this balance by generally adhering to the core principles of `Progressive Enhancement `_:: +We seek to provide usable experiences of our most important web content to all user agents. +But newer browsers are far more capable than older browsers, and the capabilities they +provide are valuable to developers and site visitors. We **will** take advantage of modern +browser capabilities. Older browsers **will** have a different experience of the website than +newer browsers. We will strike this balance by generally adhering to the core principles of +`Progressive Enhancement `_: * Basic content should be accessible to all web browsers * Basic functionality should be accessible to all web browsers @@ -17,42 +22,97 @@ We seek to provide usable experiences of our most important web content to all u * Enhanced behavior is provided by unobtrusive, externally linked JavaScript * End-user web browser preferences are respected -Some website experiences may require us to deviate from these principles -- imagine *a marketing campaign page built under timeline pressure to deliver novel functionality to a particular locale for a short while* -- but those will be exceptions and rare. +Some website experiences may require us to deviate from these principles -- imagine *a +marketing campaign page built under timeline pressure to deliver novel functionality to a +particular locale for a short while* -- but those will be exceptions and rare. -Technical details ------------------ +Browser Support Matrix (Updated 2019-08-10) +------------------------------------------- -We deliver enhanced CSS & JS to browsers in our browser support matrix (below). We deliver basic support to all other user agents. - -Basic support consists of no page-specific CSS or JS. Instead, we deliver basic semantic HTML, a universal CSS stylesheet that gets applied to all pages, and a universal JS bundle that only handles downloading Firefox (click a button, get a file), and Google Analytics. - -Browser Support Matrix (Updated 20190409) ------------------------------------------ +We deliver enhanced CSS & JS to browsers in our browser support matrix (below). +We deliver basic support to all other user agents. **The following browsers have enhanced support:** * All evergreen browsers (Firefox, Chrome, Safari, Edge, Opera, etc.) - * IE10 and above. + * IE11 and above. **The following browsers have basic support:** - * All other IE browsers. + * Outdated evergreen browser versions. + * IE10 and below. -Exceptions (Updated 20190409) ------------------------------ +Delivering basic support +------------------------ -Some pages of the website provide critical functionality to older browsers. In particular, the Firefox desktop download funnel enables users on older browsers to get a modern browser. To the extent possible, we try to deliver enhanced experiences to all user agents on these pages. +On IE browsers that support `conditional comments`_ (IE9 and below), basic support +consists of no page-specific CSS or JS. Instead, we deliver well formed semantic HTML, a +universal CSS stylesheet that gets applied to all pages, and a universal JS bundle that +only handles downloading Firefox (click a button, get a file), and Google Analytics. -**The following pages get enhanced support for a longer list of user agents:** +On other legacy browsers, developers should rely on `feature detection`_ where appropriate. - * www.mozilla.org/firefox/new/ - * www.mozilla.org/firefox/download/thanks/ +CSS +~~~ -.. Note:: +For CSS, enhanced experiences can be delivered using `feature queries`_, whilst allowing +older browsers to degrade gracefully using simpler layouts when needed. + +Additionally, there is also a universal CSS class hook available that gets delivered via +a site-wide JS feature detection snippet: + +.. code-block:: css + + .is-modern-browser { + /* Styles will only be applied to browsers that get enhanced support. */ + } + +.. _conditional comments: https://wikipedia.org/wiki/Conditional_comment +.. _feature detection: https://developer.mozilla.org/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection +.. _feature queries: https://developer.mozilla.org/docs/Web/CSS/@supports + +JavaScript +~~~~~~~~~~ + +For JS, enhanced support can be delivered using a helper that leverages the same +feature detection snippet: + +.. code-block:: javascript - An enhanced experience can be defined as a step above basic support. This can be achieved by delivering extra page-specific CSS or JS to legacy browsers. It does not mean continuing to deliver 1st class support. + (function() { + 'use strict'; -Future Support (Updated 20190409) ---------------------------------- + function onLoad() { + // Code that will only be run on browsers that get enhanced support. + } + + window.Mozilla.run(onLoad); + })(); + +The ``site.isModernBrowser`` global property can also be used within conditionals like so: + +.. code-block:: javascript + + if (window.site.isModernBrowser) { + // Code that will only be run on browsers that get enhanced support. + } + +Exceptions (Updated 2019-08-10) +------------------------------- + +Some pages of the website provide critical functionality to older browsers. In particular, +the Firefox desktop download funnel enables users on older browsers to get a modern browser. +To the extent possible, we try to deliver enhanced experiences to all user agents on these +pages. + +**The following pages get enhanced support for a longer list of user agents:** + + * /firefox/ + * /firefox/new/ + * /firefox/download/thanks/ + +.. Note:: -Since IE10 does not support conditional comments (the mechanism currently used to deliver basic support to old IE browsers), in the future bedrock will adopt a universal JS feature detection snippet. This snippet will be used to limit the execution of JS on older browsers, delivering a better degraded experience. + An enhanced experience can be defined as a step above basic support. This can be achieved + by delivering extra page-specific CSS or JS to legacy browsers. It does not mean continuing + to deliver 1st class support. diff --git a/media/css/firefox/all/all-unified.scss b/media/css/firefox/all/all-unified.scss index f1009d75f4a..e6a430ce89b 100644 --- a/media/css/firefox/all/all-unified.scss +++ b/media/css/firefox/all/all-unified.scss @@ -104,12 +104,6 @@ $image-path: '/media/protocol/img'; display: block; } } - - @media #{$mq-lg} { - .is-supported & { - min-height: 700px; - } - } } .c-intro { @@ -130,7 +124,6 @@ $image-path: '/media/protocol/img'; @media #{$mq-md} { @include bidi(((float, left, right),)); - margin-bottom: $layout-xl; width: calc(50% - #{$spacing-lg}); } @@ -182,11 +175,6 @@ $image-path: '/media/protocol/img'; .c-selection-options { display: none; } - - // only show the form if JS is supported. - .is-supported & { - display: block; - } } .c-download { @@ -233,15 +221,6 @@ $image-path: '/media/protocol/img'; } } -.js .c-all-downloads { - display: none; - - // still allow the list to be shown as a fallback. - &.is-fallback { - display: block; - } -} - .c-product-heading { @include text-display-md; background: $color-white; @@ -344,3 +323,25 @@ $image-path: '/media/protocol/img'; @include text-body-sm; } } + +// Modern browsers get the form, legacy browsers the download list. +.is-modern-browser { + .c-all-downloads { + display: none; + + // still allow the list to be shown as a fallback. + &.is-fallback { + display: block; + } + } + + .c-selection-form { + display: block; + } + + .c-product-select-form { + @media #{$mq-lg} { + min-height: 700px; + } + } +} diff --git a/media/js/base/mozilla-run.js b/media/js/base/mozilla-run.js new file mode 100644 index 00000000000..1e92cdce47f --- /dev/null +++ b/media/js/base/mozilla-run.js @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Create namespace +if (typeof window.Mozilla === 'undefined') { + window.Mozilla = {}; +} + +(function() { + 'use strict'; + + window.Mozilla.run = function(callback) { + var isModernBrowser = window.site && window.site.isModernBrowser; + + if (isModernBrowser && typeof callback === 'function') { + callback(); + } + }; +})(window.Mozilla); diff --git a/media/js/base/site.js b/media/js/base/site.js index 47ac9cd95a6..ca2b2812833 100644 --- a/media/js/base/site.js +++ b/media/js/base/site.js @@ -135,6 +135,11 @@ return 32; }, + // Universal feature detect to deliver graded browser support (targets IE 11 and above). + cutsTheMustard: function () { + return 'classList' in document.createElement('div') && 'MutationObserver' in window; + }, + platform: 'other', platformVersion: undefined, archType: 'x64', @@ -192,6 +197,13 @@ h.className += ' is-firefox'; } + // Add class to reflect browsers that get 1st class JS & CSS support. + var isModernBrowser = window.site.isModernBrowser = window.site.cutsTheMustard(); + + if (isModernBrowser) { + h.className += ' is-modern-browser'; + } + // Add class to reflect javascript availability for CSS h.className = h.className.replace(/\bno-js\b/, 'js'); })(); diff --git a/media/js/firefox/all/all-downloads-unified-init.js b/media/js/firefox/all/all-downloads-unified-init.js index 7558fa1c5bb..fa7f6d7137d 100644 --- a/media/js/firefox/all/all-downloads-unified-init.js +++ b/media/js/firefox/all/all-downloads-unified-init.js @@ -5,47 +5,42 @@ (function(Mozilla){ 'use strict'; - var browserHelpContent = document.getElementById('browser-help'); - var browserHelpIcon = document.getElementById('icon-browser-help'); - var downloadList = document.getElementById('all-downloads'); - var form = document.getElementById('product-select-form'); - var installerHelpContent = document.getElementById('installer-help'); - var installerHelpIcon = document.querySelectorAll('.icon-installer-help'); - - function showHelpModal(modalContent, modalTitle, eventLabel) { - Mzp.Modal.createModal(this, modalContent, { - title: modalTitle, - className: 'help-modal' - }); - - window.dataLayer.push({ - 'event': 'in-page-interaction', - 'eAction': 'link click', - 'eLabel': eventLabel - }); - } - - if (!Mozilla.FirefoxDownloader.isSupported()) { - downloadList.style.display = 'block'; - return; - } else { - form.classList.add('is-supported'); - } - - Mozilla.FirefoxDownloader.init(); - - // Browser help modal. - browserHelpIcon.addEventListener('click', function(e) { - e.preventDefault(); - showHelpModal.call(this, browserHelpContent, browserHelpIcon.textContent, 'Get Browser Help'); - }, false); - - // Installer help modal. - for (var i = 0; i < installerHelpIcon.length; i++) { - installerHelpIcon[i].addEventListener('click', function(e) { + function onLoad() { + var browserHelpContent = document.getElementById('browser-help'); + var browserHelpIcon = document.getElementById('icon-browser-help'); + var installerHelpContent = document.getElementById('installer-help'); + var installerHelpIcon = document.querySelectorAll('.icon-installer-help'); + + function showHelpModal(modalContent, modalTitle, eventLabel) { + Mzp.Modal.createModal(this, modalContent, { + title: modalTitle, + className: 'help-modal' + }); + + window.dataLayer.push({ + 'event': 'in-page-interaction', + 'eAction': 'link click', + 'eLabel': eventLabel + }); + } + + Mozilla.FirefoxDownloader.init(); + + // Browser help modal. + browserHelpIcon.addEventListener('click', function(e) { e.preventDefault(); - showHelpModal.call(this, installerHelpContent, e.target.textContent, 'Get Installer Help'); + showHelpModal.call(this, browserHelpContent, browserHelpIcon.textContent, 'Get Browser Help'); }, false); + + // Installer help modal. + for (var i = 0; i < installerHelpIcon.length; i++) { + installerHelpIcon[i].addEventListener('click', function(e) { + e.preventDefault(); + showHelpModal.call(this, installerHelpContent, e.target.textContent, 'Get Installer Help'); + }, false); + } } + Mozilla.run(onLoad); + })(window.Mozilla); diff --git a/media/js/firefox/all/all-downloads-unified.js b/media/js/firefox/all/all-downloads-unified.js index af6fbfc2bde..b45e27eb811 100644 --- a/media/js/firefox/all/all-downloads-unified.js +++ b/media/js/firefox/all/all-downloads-unified.js @@ -447,16 +447,6 @@ } }; - /** - * Basic feature detect for minimum browser support. - */ - FirefoxDownloader.isSupported = function() { - return 'querySelector' in document && - 'querySelectorAll' in document && - 'addEventListener' in window && - 'classList' in document.createElement('div'); - }; - /** * Initialize the form and show the default selection. */ diff --git a/media/static-bundles.json b/media/static-bundles.json index 1e3e05e59fb..a09a1b80770 100644 --- a/media/static-bundles.json +++ b/media/static-bundles.json @@ -1461,6 +1461,7 @@ "js/newsletter/form.js", "js/base/mozilla-client.js", "js/base/class-list-polyfill.js", + "js/base/mozilla-run.js", "protocol/js/protocol-supports.js", "protocol/js/protocol-utils.js", "protocol/js/protocol-menu.js", @@ -1485,6 +1486,7 @@ "js/base/mozilla-utils.js", "js/base/mozilla-client.js", "js/base/class-list-polyfill.js", + "js/base/mozilla-run.js", "protocol/js/protocol-supports.js", "protocol/js/protocol-utils.js", "protocol/js/protocol-menu.js", @@ -1508,6 +1510,7 @@ "js/base/mozilla-utils.js", "js/base/mozilla-client.js", "js/base/class-list-polyfill.js", + "js/base/mozilla-run.js", "protocol/js/protocol-supports.js", "protocol/js/protocol-utils.js", "protocol/js/protocol-menu.js", diff --git a/tests/unit/karma.conf.js b/tests/unit/karma.conf.js index 810bd60e9aa..9f258e62dfa 100644 --- a/tests/unit/karma.conf.js +++ b/tests/unit/karma.conf.js @@ -18,6 +18,7 @@ module.exports = function(config) { 'media/js/base/mozilla-client.js', 'media/js/base/search-params.js', // end common dependencies. + 'media/js/base/mozilla-run.js', 'media/js/base/core-datalayer-page-id.js', 'media/js/base/core-datalayer.js', 'media/js/base/dnt-helper.js', @@ -37,6 +38,7 @@ module.exports = function(config) { 'media/js/firefox/new/yandex/scene1.js', 'media/js/firefox/tracking-protection-tour.js', 'media/js/ie/mozilla-utils-ie.js', + 'tests/unit/spec/base/mozilla-run.js', 'tests/unit/spec/base/core-datalayer-page-id.js', 'tests/unit/spec/base/core-datalayer.js', 'tests/unit/spec/base/dnt-helper.js', diff --git a/tests/unit/spec/base/mozilla-run.js b/tests/unit/spec/base/mozilla-run.js new file mode 100644 index 00000000000..e9730758df4 --- /dev/null +++ b/tests/unit/spec/base/mozilla-run.js @@ -0,0 +1,36 @@ +/* For reference read the Jasmine and Sinon docs + * Jasmine docs: https://jasmine.github.io/2.0/introduction.html + * Sinon docs: http://sinonjs.org/docs/ + */ + +describe('mozilla-run.js', function() { + + 'use strict'; + + describe('run', function() { + + afterEach(function() { + window.site.isModernBrowser = window.site.cutsTheMustard(); + }); + + it('should execute callback for modern browsers', function() { + var obj = { + callback: function() {} // eslint-disable-line no-empty-function + }; + window.site.isModernBrowser = true; + spyOn(obj, 'callback').and.callThrough(); + window.Mozilla.run(obj.callback); + expect(obj.callback).toHaveBeenCalled(); + }); + + it('should not execute callback for legacy browsers', function() { + var obj = { + callback: function() {} // eslint-disable-line no-empty-function + }; + window.site.isModernBrowser = false; + spyOn(obj, 'callback').and.callThrough(); + window.Mozilla.run(obj.callback); + expect(obj.callback).not.toHaveBeenCalled(); + }); + }); +});