fix(logo): Use the new Firefox logo in Fx Desktop/Android >= 57, iOS >= 10. (#5643) r=@vbudhram, @ryanfeeley

Do some UA sniffing on startup to add the "fx-57" class
to the HTML element. If "fx-57" exists, display the
new logo.

fixes #5588
This commit is contained in:
Shane Tomlinson 2017-10-25 12:49:47 +01:00 коммит произвёл GitHub
Родитель 1a520c2136
Коммит 46921fbaee
14 изменённых файлов: 329 добавлений и 1 удалений

Двоичные данные
app/favicon-pre57.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 15 KiB

Двоичные данные
app/favicon.ico

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 15 KiB

После

Ширина:  |  Высота:  |  Размер: 66 KiB

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Просмотреть файл

@ -83,6 +83,7 @@
this.addSearchParamStyles();
this.addFxiOSSyncStyles();
this.addGetUserMediaStyles();
this.addFx57Styles();
},
addJSStyle: function () {
@ -142,6 +143,15 @@
} else {
this._addClass('no-getusermedia');
}
},
/**
* Add the `fx-57` class to the body if in Fx >= 57.
*/
addFx57Styles: function () {
if (this.environment.isFx57OrAbove() || this.environment.isFxiOS10OrAbove()) {
this._addClass('fx-57');
}
}
};

Просмотреть файл

@ -108,6 +108,40 @@
return /FxiOS/.test(this.window.navigator.userAgent);
},
/**
* Is the user in Firefox for iOS 10 or above?
*
* @param {String} [userAgent=navigator.userAgent] UA string
* @returns {Boolean}
*/
isFxiOS10OrAbove: function (userAgent) {
var fxRegExp = /FxiOS\/(\d{2,}\.\d{1,})/;
var matches = fxRegExp.exec(userAgent || this.window.navigator.userAgent);
if (matches && matches[1]) {
return parseFloat(matches[1]) >= 10;
}
return false;
},
/**
* Is the user in Firefox (Desktop or Android) 57 or above?
*
* @param {String} [userAgent=navigator.userAgent] UA string
* @returns {Boolean}
*/
isFx57OrAbove: function (userAgent) {
var fxRegExp = /Firefox\/(\d{2,}\.\d{1,})$/;
var matches = fxRegExp.exec(userAgent || this.window.navigator.userAgent);
if (matches && matches[1]) {
return parseFloat(matches[1]) >= 57;
}
return false;
},
hasSendBeacon: function () {
return typeof this.window.navigator.sendBeacon === 'function';
}

Просмотреть файл

@ -26,6 +26,14 @@
display: none;
}
html.fx-57 & {
background-image: image-url('firefox-logo.svg');
background-position-y: -5px;
@include respond-to('small') {
background-position-y: -4px;
}
}
.static & {
opacity: 1;
}

Просмотреть файл

@ -40,6 +40,14 @@ body.settings #main-content.card {
background-repeat: no-repeat;
margin: 0;
html.fx-57 & {
background-image: image-url('firefox-logo.svg');
background-position-y: -5px;
@include respond-to('small') {
background-position-y: -4px;
}
}
html[dir='ltr'] & {
float: left;
}
@ -78,7 +86,6 @@ body.settings #main-content.card {
background-position: right 8px;
padding-right: 36px;
}
}
}

Просмотреть файл

@ -178,6 +178,40 @@ define(function (require, exports, module) {
});
});
describe('addFx57Styles', () => {
it('adds `fx-57` if UA is Fx >= 57', () => {
sinon.stub(environment, 'isFx57OrAbove').callsFake(() => true);
sinon.stub(environment, 'isFxiOS10OrAbove').callsFake(() => false);
startupStyles.addFx57Styles();
assert.isTrue(/fx-57/.test(startupStyles.getClassName()));
});
it('does not add `fx-57` if UA is Fx <= 56', () => {
sinon.stub(environment, 'isFx57OrAbove').callsFake(() => false);
sinon.stub(environment, 'isFxiOS10OrAbove').callsFake(() => false);
startupStyles.addFx57Styles();
assert.isFalse(/fx-57/.test(startupStyles.getClassName()));
});
it('adds `fx-57` if UA is FxiOS >= 10', () => {
sinon.stub(environment, 'isFx57OrAbove').callsFake(() => false);
sinon.stub(environment, 'isFxiOS10OrAbove').callsFake(() => true);
startupStyles.addFx57Styles();
assert.isTrue(/fx-57/.test(startupStyles.getClassName()));
});
it('does not add `fx-57` if UA is FxiOS <= 9', () => {
sinon.stub(environment, 'isFx57OrAbove').callsFake(() => false);
sinon.stub(environment, 'isFxiOS10OrAbove').callsFake(() => false);
startupStyles.addFx57Styles();
assert.isFalse(/fx-57/.test(startupStyles.getClassName()));
});
});
describe('initialize', function () {
it('runs all the tests', function () {

Просмотреть файл

@ -159,6 +159,40 @@ define(function (require, exports, module) {
});
});
describe('isFxiOS10OrAbove', function () {
it('returns `false` if on Fx 10 or above, false otw', function () {
assert.isFalse(environment.isFxiOS10OrAbove('FxiOS/9.0'));
assert.isTrue(environment.isFxiOS10OrAbove('FxiOS/10.0'));
assert.isTrue(environment.isFxiOS10OrAbove('FxiOS/11.0'));
assert.isFalse(environment.isFxiOS('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:40.0) Gecko/20100101 Firefox/40.0'));
});
});
describe('isFx57OrAbove', () => {
it('returns `true` if Fx Desktop 57 or above', () => {
assert.isTrue(environment.isFx57OrAbove('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:57.0) Gecko/20100101 Firefox/57.0'));
assert.isTrue(environment.isFx57OrAbove('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:58.0) Gecko/20100101 Firefox/58.0'));
assert.isTrue(environment.isFx57OrAbove('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:101.0) Gecko/20100101 Firefox/101.0'));
assert.isTrue(environment.isFx57OrAbove('Mozilla/5.0 (Windows NT x.y; WOW64; rv:101.0) Gecko/20100101 Firefox/101.0'));
});
it('returns `true` if Fx for Android 57 or above', () => {
assert.isTrue(environment.isFx57OrAbove('Mozilla/5.0 (Android 4.4; Mobile; rv:57.0) Gecko/57.0 Firefox/57.0'));
assert.isTrue(environment.isFx57OrAbove('Mozilla/5.0 (Android 4.4; Mobile; rv:57.0) Gecko/58.0 Firefox/58.0'));
assert.isTrue(environment.isFx57OrAbove('Mozilla/5.0 (Android 4.4; Mobile; rv:57.0) Gecko/999.0 Firefox/999.0'));
});
it('returns `false` otw', () => {
assert.isFalse(environment.isFx57OrAbove('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:40.0) Gecko/20100101 Firefox/40.0'));
assert.isFalse(environment.isFx57OrAbove('Mozilla/5.0 (Windows NT x.y; WOW64; rv:10.0) Gecko/20100101 Firefox/10.0'));
assert.isFalse(environment.isFx57OrAbove('Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0'));
assert.isFalse(environment.isFx57OrAbove('Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0'));
assert.isFalse(environment.isFx57OrAbove('Mozilla/5.0 (Android 4.4; Mobile; rv:56.0) Gecko/56.0 Firefox/56.0'));
assert.isFalse(environment.isFx57OrAbove(
'Mozilla/5.0 (iPod touch; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4'));
});
});
describe('hasSendBeacon', function () {
it('returns `true` if sendBeacon function exists', function () {
windowMock.navigator.sendBeacon = function () {};

Просмотреть файл

@ -32,6 +32,7 @@ module.exports = function (config, i18n) {
// Disable server verification for now due to issues with customs
//require('./routes/get-verify-email')(),
require('./routes/get-apple-app-site-association')(),
require('./routes/get-favicon')(),
require('./routes/get-frontend')(),
require('./routes/get-terms-privacy')(i18n),
require('./routes/get-index')(config),

Просмотреть файл

@ -0,0 +1,30 @@
/* 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/. */
/**
* Serve up the favicon. A temporary route to serve the Fx 57 logo to
* only Fx >= 57 until the logo is publicly released.
*/
const uaParser = require('node-uap');
function shouldSee57Icon(uaHeader) {
const agent = uaParser.parse(uaHeader);
return (agent.ua.family === 'Firefox' && parseInt(agent.ua.major, 10) >= 57) ||
(agent.ua.family === 'Firefox Mobile' && parseInt(agent.ua.major, 10) >= 57) ||
(agent.ua.family === 'Firefox iOS' && parseInt(agent.ua.major, 10) >= 10);
}
module.exports = function () {
return {
method: 'get',
path: '/favicon.ico',
process: (req, res, next) => {
if (! shouldSee57Icon(req.headers['user-agent'] || '')) {
req.url = 'favicon-pre57.ico';
}
next();
}
};
};

Просмотреть файл

@ -7,6 +7,8 @@ define([], function () {
return {
'android_chrome': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19',
'android_firefox': 'Mozilla/5.0 (Android 4.4; Mobile; rv:43.0) Gecko/41.0 Firefox/43.0',
'android_firefox_56': 'Mozilla/5.0 (Android 4.4; Mobile; rv:56.0) Gecko/56.0 Firefox/56.0',
'android_firefox_57': 'Mozilla/5.0 (Android 4.4; Mobile; rv:57.0) Gecko/57.0 Firefox/57.0',
'desktop_chrome': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.59 Safari/537.36',
'desktop_firefox': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:50.0) Gecko/20100101 Firefox/50.0',
'desktop_firefox_55': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0',
@ -16,6 +18,8 @@ define([], function () {
'ios_firefox': 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4',
'ios_firefox_6_0': 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/6.0 Mobile/12F69 Safari/600.1.4',
'ios_firefox_6_1': 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/6.1 Mobile/12F69 Safari/600.1.4',
'ios_firefox_9': 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/9.0 Mobile/12F69 Safari/600.1.4',
'ios_firefox_10': 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/10.0 Mobile/12F69 Safari/600.1.4', // eslint-disable-line
'ios_safari': 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3'
};
/*eslint-enable max-len*/

Просмотреть файл

@ -33,6 +33,7 @@ define([
'tests/server/statsd-collector',
'tests/server/raven',
'tests/server/routes/get-apple-app-site-association',
'tests/server/routes/get-favicon',
'tests/server/routes/get-config',
'tests/server/routes/get-verify-email',
'tests/server/routes/get-fxa-client-configuration',

Просмотреть файл

@ -0,0 +1,164 @@
/* 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/. */
define([
'intern!object',
'intern/chai!assert',
'intern/dojo/node!../../../server/lib/routes/get-favicon',
'../../functional/lib/ua-strings',
], function (registerSuite, assert, route, uaStrings) {
let instance, request, response;
/*eslint-disable sorting/sort-object-props*/
registerSuite({
name: 'routes/get-favicon',
'route interface is correct': function () {
assert.isFunction(route);
assert.lengthOf(route, 0);
},
'initialise route': {
setup: function () {
instance = route();
},
'instance interface is correct': function () {
assert.isObject(instance);
assert.lengthOf(Object.keys(instance), 3);
assert.equal(instance.method, 'get');
assert.equal(instance.path, '/favicon.ico');
assert.isFunction(instance.process);
assert.lengthOf(instance.process, 3);
},
'route.process': {
'no user-agent header': {
setup: function () {
request = {
headers: {},
url: 'favicon.ico'
};
return new Promise((resolve) => {
instance.process(request, response, resolve);
});
},
'should see old icon': function () {
assert.equal(request.url, 'favicon-pre57.ico');
}
},
'Firefox desktop 56': {
setup: function () {
request = {
headers: {
'user-agent': uaStrings.desktop_firefox_56
},
url: 'favicon.ico'
};
return new Promise((resolve) => {
instance.process(request, response, resolve);
});
},
'should see old icon': function () {
assert.equal(request.url, 'favicon-pre57.ico');
}
},
'Firefox desktop 57': {
setup: function () {
request = {
headers: {
'user-agent': uaStrings.desktop_firefox_57
},
url: 'favicon.ico'
};
return new Promise((resolve) => {
instance.process(request, response, resolve);
});
},
'should see new icon': function () {
assert.equal(request.url, 'favicon.ico');
}
},
'Firefox android 56': {
setup: function () {
request = {
headers: {
'user-agent': uaStrings.android_firefox_56
},
url: 'favicon.ico'
};
return new Promise((resolve) => {
instance.process(request, response, resolve);
});
},
'should see old icon': function () {
assert.equal(request.url, 'favicon-pre57.ico');
}
},
'Firefox android 57': {
setup: function () {
request = {
headers: {
'user-agent': uaStrings.android_firefox_57
},
url: 'favicon.ico'
};
return new Promise((resolve) => {
instance.process(request, response, resolve);
});
},
'should see new icon': function () {
assert.equal(request.url, 'favicon.ico');
}
},
'Firefox iOS 9': {
setup: function () {
request = {
headers: {
'user-agent': uaStrings.ios_firefox_9
},
url: 'favicon.ico'
};
return new Promise((resolve) => {
instance.process(request, response, resolve);
});
},
'should see old icon': function () {
assert.equal(request.url, 'favicon-pre57.ico');
}
},
'Firefox iOS 10': {
setup: function () {
request = {
headers: {
'user-agent': uaStrings.ios_firefox_10
},
url: 'favicon.ico'
};
return new Promise((resolve) => {
instance.process(request, response, resolve);
});
},
'should see new icon': function () {
assert.equal(request.url, 'favicon.ico');
}
},
}
}
});
/*eslint-enable sorting/sort-object-props*/
});