1
0
mirror of https://gitlab.com/timvisee/send.git synced 2024-11-10 21:22:35 +01:00

Merge pull request #416 from mozilla/shim-webcrypto

WIP: use webcrypto-liner to support Safari 10
This commit is contained in:
Danny Coates 2017-08-05 10:09:09 -07:00 committed by GitHub
commit 2dfbee090a
11 changed files with 856 additions and 719 deletions

View File

@ -1,35 +1,61 @@
window.Raven = require('raven-js'); const testPilotGA = require('testpilot-ga');
const Raven = require('raven-js');
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) { if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
window.Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install(); Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
} }
const testPilotGA = require('testpilot-ga'); const analytics = new testPilotGA({
const { gcmCompliant, sendEvent } = require('./utils');
window.analytics = new testPilotGA({
an: 'Firefox Send', an: 'Firefox Send',
ds: 'web', ds: 'web',
tid: window.GOOGLE_ANALYTICS_ID tid: window.GOOGLE_ANALYTICS_ID
}); });
const isSender = !location.pathname.includes('/download'); function sendEvent() {
const ec = isSender ? 'sender' : 'recipient'; return analytics.sendEvent.apply(analytics, arguments).catch(() => 0);
}
function findMetric(href) {
switch (href) {
case 'https://www.mozilla.org/':
return 'mozilla';
case 'https://www.mozilla.org/about/legal':
return 'legal';
case 'https://testpilot.firefox.com/about':
return 'about';
case 'https://testpilot.firefox.com/privacy':
return 'privacy';
case 'https://testpilot.firefox.com/terms':
return 'terms';
case 'https://www.mozilla.org/privacy/websites/#cookies':
return 'cookies';
case 'https://github.com/mozilla/send':
return 'github';
case 'https://twitter.com/FxTestPilot':
return 'twitter';
case 'https://www.mozilla.org/firefox/new/?scene=2':
return 'download-firefox';
default:
return 'other';
}
}
const ua = navigator.userAgent.toLowerCase(); const ua = navigator.userAgent.toLowerCase();
gcmCompliant().catch(err => {
sendEvent(ec, 'unsupported', {
cd6: err
}).then(() => {
location.replace('/unsupported/gcm');
});
});
if ( if (
ua.indexOf('firefox') > -1 && ua.indexOf('firefox') > -1 &&
parseInt(ua.match(/firefox\/*([^\n\r]*)\./)[1], 10) <= 49 parseInt(ua.match(/firefox\/*([^\n\r]*)\./)[1], 10) <= 49
) { ) {
const isSender = !location.pathname.includes('/download');
const ec = isSender ? 'sender' : 'recipient';
sendEvent(ec, 'unsupported', { sendEvent(ec, 'unsupported', {
cd6: new Error('Firefox is outdated.') cd6: new Error('Firefox is outdated.')
}).then(() => { }).then(() => {
location.replace('/unsupported/outdated'); location.replace('/unsupported/outdated');
}); });
} }
module.exports = {
Raven,
sendEvent,
findMetric
};

View File

@ -1,19 +1,17 @@
require('./common'); const { Raven, findMetric, sendEvent } = require('./common');
const FileReceiver = require('./fileReceiver'); const FileReceiver = require('./fileReceiver');
const { notify, findMetric, sendEvent } = require('./utils'); const { notify, gcmCompliant } = require('./utils');
const bytes = require('bytes'); const bytes = require('bytes');
const Storage = require('./storage'); const Storage = require('./storage');
const storage = new Storage(localStorage); const storage = new Storage(localStorage);
const links = require('./links');
const $ = require('jquery'); const $ = require('jquery');
require('jquery-circle-progress'); require('jquery-circle-progress');
const Raven = window.Raven;
$(document).ready(function() { $(document).ready(function() {
//link back to homepage gcmCompliant()
$('.send-new').attr('href', window.location.origin); .then(function() {
$('.send-new').click(function() { $('.send-new').click(function() {
sendEvent('recipient', 'restarted', { sendEvent('recipient', 'restarted', {
cd2: 'completed' cd2: 'completed'
@ -44,6 +42,7 @@ $(document).ready(function() {
function download() { function download() {
// Disable the download button to avoid accidental double clicks. // Disable the download button to avoid accidental double clicks.
$('#download-btn').attr('disabled', 'disabled'); $('#download-btn').attr('disabled', 'disabled');
links.setOpenInNewTab(true);
storage.totalDownloads += 1; storage.totalDownloads += 1;
@ -179,6 +178,15 @@ $(document).ready(function() {
.catch(err => { .catch(err => {
Raven.captureException(err); Raven.captureException(err);
return Promise.reject(err); return Promise.reject(err);
}); })
.then(() => links.setOpenInNewTab(false));
} }
})
.catch(err => {
sendEvent('sender', 'unsupported', {
cd6: err
}).then(() => {
location.replace('/unsupported/gcm');
});
});
}); });

23
frontend/src/links.js Normal file
View File

@ -0,0 +1,23 @@
let links = [];
document.addEventListener('DOMContentLoaded', function() {
links = document.querySelectorAll('a:not([target])');
});
function setOpenInNewTab(bool) {
if (bool === false) {
links.forEach(l => {
l.removeAttribute('target');
l.removeAttribute('rel');
});
} else {
links.forEach(l => {
l.setAttribute('target', '_blank');
l.setAttribute('rel', 'noopener noreferrer');
});
}
}
module.exports = {
setOpenInNewTab
};

View File

@ -1,7 +1,12 @@
/* global MAXFILESIZE EXPIRE_SECONDS */ /* global MAXFILESIZE EXPIRE_SECONDS */
require('./common'); const { Raven, findMetric, sendEvent } = require('./common');
const FileSender = require('./fileSender'); const FileSender = require('./fileSender');
const { notify, findMetric, sendEvent, ONE_DAY_IN_MS } = require('./utils'); const {
copyToClipboard,
notify,
gcmCompliant,
ONE_DAY_IN_MS
} = require('./utils');
const bytes = require('bytes'); const bytes = require('bytes');
const Storage = require('./storage'); const Storage = require('./storage');
const storage = new Storage(localStorage); const storage = new Storage(localStorage);
@ -9,8 +14,6 @@ const storage = new Storage(localStorage);
const $ = require('jquery'); const $ = require('jquery');
require('jquery-circle-progress'); require('jquery-circle-progress');
const Raven = window.Raven;
if (storage.has('referrer')) { if (storage.has('referrer')) {
window.referrer = storage.referrer; window.referrer = storage.referrer;
storage.remove('referrer'); storage.remove('referrer');
@ -21,19 +24,11 @@ if (storage.has('referrer')) {
const allowedCopy = () => { const allowedCopy = () => {
const support = !!document.queryCommandSupported; const support = !!document.queryCommandSupported;
return support ? document.queryCommandSupported('copy') : false; return support ? document.queryCommandSupported('copy') : false;
} };
const copyToClipboard = (value) => {
const aux = document.createElement('input');
aux.setAttribute('value', value);
document.body.appendChild(aux);
aux.select();
const result = document.execCommand('copy');
document.body.removeChild(aux);
return result;
}
$(document).ready(function() { $(document).ready(function() {
gcmCompliant()
.then(function() {
$('#page-one').removeAttr('hidden'); $('#page-one').removeAttr('hidden');
$('#file-upload').change(onUpload); $('#file-upload').change(onUpload);
@ -64,7 +59,7 @@ $(document).ready(function() {
$('body').on('dragover', allowDrop).on('drop', onUpload); $('body').on('dragover', allowDrop).on('drop', onUpload);
// reset copy button // reset copy button
const $copyBtn = $('#copy-btn'); const $copyBtn = $('#copy-btn');
$copyBtn.attr('disabled', !allowedCopy()); $copyBtn.attr('disabled', false);
$('#link').attr('disabled', false); $('#link').attr('disabled', false);
$copyBtn.attr('data-l10n-id', 'copyUrlFormButton'); $copyBtn.attr('data-l10n-id', 'copyUrlFormButton');
@ -87,7 +82,6 @@ $(document).ready(function() {
sendEvent('sender', 'copied', { sendEvent('sender', 'copied', {
cd4: 'success-screen' cd4: 'success-screen'
}); });
//disable button for 3s //disable button for 3s
$copyBtn.attr('disabled', true); $copyBtn.attr('disabled', true);
$('#link').attr('disabled', true); $('#link').attr('disabled', true);
@ -102,14 +96,12 @@ $(document).ready(function() {
} }
}); });
const $uploadWindow = $('.upload-window'); $('.upload-window').on('dragover', () => {
$uploadWindow.on('dragover', () => { $('.upload-window').addClass('ondrag');
$uploadWindow.addClass('ondrag'); });
}) $('.upload-window').on('dragleave', () => {
.on('dragleave', () => { $('.upload-window').removeClass('ondrag');
$uploadWindow.removeClass('ondrag');
}); });
//initiate progress bar //initiate progress bar
$('#ul-progress').circleProgress({ $('#ul-progress').circleProgress({
value: 0.0, value: 0.0,
@ -136,15 +128,17 @@ $(document).ready(function() {
let file = ''; let file = '';
if (event.type === 'drop') { if (event.type === 'drop') {
if (!event.originalEvent.dataTransfer.files[0]) { if (!event.originalEvent.dataTransfer.files[0]) {
$uploadWindow.removeClass('ondrag'); $('.upload-window').removeClass('ondrag');
return; return;
} }
if ( if (
event.originalEvent.dataTransfer.files.length > 1 || event.originalEvent.dataTransfer.files.length > 1 ||
event.originalEvent.dataTransfer.files[0].size === 0 event.originalEvent.dataTransfer.files[0].size === 0
) { ) {
$uploadWindow.removeClass('ondrag'); $('.upload-window').removeClass('ondrag');
document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => { document.l10n
.formatValue('uploadPageMultipleFilesAlert')
.then(str => {
alert(str); alert(str);
}); });
return; return;
@ -190,7 +184,9 @@ $(document).ready(function() {
const percent = progress[0] / progress[1]; const percent = progress[0] / progress[1];
// update progress bar // update progress bar
$('#ul-progress').circleProgress('value', percent); $('#ul-progress').circleProgress('value', percent);
$('#ul-progress').circleProgress().on('circle-animation-end', function() { $('#ul-progress')
.circleProgress()
.on('circle-animation-end', function() {
$('.percent-number').text(`${Math.floor(percent * 100)}`); $('.percent-number').text(`${Math.floor(percent * 100)}`);
}); });
$('.progress-text').text( $('.progress-text').text(
@ -352,7 +348,6 @@ $(document).ready(function() {
'data-l10n-id': 'copyUrlHover', 'data-l10n-id': 'copyUrlHover',
disabled: !allowedCopy() disabled: !allowedCopy()
}); });
const expiry = document.createElement('td'); const expiry = document.createElement('td');
const del = document.createElement('td'); const del = document.createElement('td');
const $delIcon = $('<img>', { const $delIcon = $('<img>', {
@ -367,7 +362,10 @@ $(document).ready(function() {
const url = file.url.trim() + `#${file.secretKey}`.trim(); const url = file.url.trim() + `#${file.secretKey}`.trim();
$('#link').attr('value', url); $('#link').attr('value', url);
$('#copy-text').attr('data-l10n-args', '{"filename": "' + file.name + '"}'); $('#copy-text').attr(
'data-l10n-args',
'{"filename": "' + file.name + '"}'
);
$('#copy-text').attr('data-l10n-id', 'copyUrlFormLabelWithName'); $('#copy-text').attr('data-l10n-id', 'copyUrlFormLabelWithName');
$popupText.attr('tabindex', '-1'); $popupText.attr('tabindex', '-1');
@ -388,7 +386,7 @@ $(document).ready(function() {
link.style.color = '#0A8DFF'; link.style.color = '#0A8DFF';
//copy link to clipboard when icon clicked //copy link to clipboard when icon clicked
$copyIcon.on('click', function() { $copyIcon.click(function() {
// record copied event from upload list // record copied event from upload list
sendEvent('sender', 'copied', { sendEvent('sender', 'copied', {
cd4: 'upload-list' cd4: 'upload-list'
@ -545,4 +543,12 @@ $(document).ready(function() {
$('#file-list').removeAttr('hidden'); $('#file-list').removeAttr('hidden');
} }
} }
})
.catch(err => {
sendEvent('sender', 'unsupported', {
cd6: err
}).then(() => {
location.replace('/unsupported/gcm');
});
});
}); });

View File

@ -60,41 +60,22 @@ function gcmCompliant() {
) )
.then(() => { .then(() => {
return Promise.resolve(); return Promise.resolve();
})
.catch(err => {
return Promise.reject();
}); });
}) })
.catch(err => { .catch(err => {
return Promise.reject(); return loadShim();
}); });
} catch (err) { } catch (err) {
return Promise.reject(); return loadShim();
} }
} function loadShim() {
return new Promise((resolve, reject) => {
function findMetric(href) { const shim = document.createElement('script');
switch (href) { shim.src = '/cryptofill.js';
case 'https://www.mozilla.org/': shim.addEventListener('load', resolve);
return 'mozilla'; shim.addEventListener('error', reject);
case 'https://www.mozilla.org/about/legal': document.head.appendChild(shim);
return 'legal'; });
case 'https://testpilot.firefox.com/about':
return 'about';
case 'https://testpilot.firefox.com/privacy':
return 'privacy';
case 'https://testpilot.firefox.com/terms':
return 'terms';
case 'https://www.mozilla.org/privacy/websites/#cookies':
return 'cookies';
case 'https://github.com/mozilla/send':
return 'github';
case 'https://twitter.com/FxTestPilot':
return 'twitter';
case 'https://www.mozilla.org/firefox/new/?scene=2':
return 'download-firefox';
default:
return 'other';
} }
} }
@ -102,21 +83,35 @@ function isFile(id) {
return /^[0-9a-fA-F]{10}$/.test(id); return /^[0-9a-fA-F]{10}$/.test(id);
} }
function sendEvent() { function copyToClipboard(str) {
return window.analytics.sendEvent const aux = document.createElement('input');
.apply(window.analytics, arguments) aux.setAttribute('value', str);
.catch(() => 0); aux.contentEditable = true;
aux.readOnly = true;
document.body.appendChild(aux);
if (navigator.userAgent.match(/iphone|ipad|ipod/i)) {
const range = document.createRange();
range.selectNodeContents(aux);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
aux.setSelectionRange(0, str.length);
} else {
aux.select();
}
const result = document.execCommand('copy');
document.body.removeChild(aux);
return result;
} }
const ONE_DAY_IN_MS = 86400000; const ONE_DAY_IN_MS = 86400000;
module.exports = { module.exports = {
copyToClipboard,
arrayToHex, arrayToHex,
hexToArray, hexToArray,
notify, notify,
gcmCompliant, gcmCompliant,
findMetric,
isFile, isFile,
sendEvent,
ONE_DAY_IN_MS ONE_DAY_IN_MS
}; };

55
package-lock.json generated
View File

@ -4,6 +4,12 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@types/node": {
"version": "7.0.39",
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.39.tgz",
"integrity": "sha512-KQHAZeVsk4UIT9XaR6cn4WpHZzimK6UBD1UomQKfQQFmTlUHaNBzeuov+TM4+kigLO0IJt4I5OOsshcCyA9gSA==",
"dev": true
},
"accepts": { "accepts": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
@ -181,6 +187,12 @@
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
}, },
"asmcrypto.js": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-0.0.11.tgz",
"integrity": "sha1-dLshuq/Z3OFoTcBvFShoOzUMHKA=",
"dev": true
},
"asn1.js": { "asn1.js": {
"version": "4.9.1", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz",
@ -5254,6 +5266,12 @@
"integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=",
"dev": true "dev": true
}, },
"tslib": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz",
"integrity": "sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw=",
"dev": true
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
@ -5290,6 +5308,12 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true "dev": true
}, },
"typescript": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz",
"integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=",
"dev": true
},
"uglify-js": { "uglify-js": {
"version": "2.8.29", "version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
@ -5440,6 +5464,37 @@
"indexof": "0.0.1" "indexof": "0.0.1"
} }
}, },
"webcrypto-core": {
"version": "0.1.16",
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-0.1.16.tgz",
"integrity": "sha1-3kkUtZFI23Moe8T45hz0+y9W8CA=",
"dev": true,
"requires": {
"@types/node": "6.0.85",
"tslib": "1.7.1"
},
"dependencies": {
"@types/node": {
"version": "6.0.85",
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.85.tgz",
"integrity": "sha512-6qLZpfQFO/g5Ns2e7RsW6brk0Q6Xzwiw7kVVU/XiQNOiJXSojhX76GP457PBYIsNMH2WfcGgcnZB4awFDHrwpA==",
"dev": true
}
}
},
"webcrypto-liner": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/webcrypto-liner/-/webcrypto-liner-0.1.25.tgz",
"integrity": "sha512-p/wtjIvs10nZvruEJlglTf5Qjb9W/sDvfEFuw5RjM+axGaXj8mBGF7tFJb5sjVbSTqv1gsxktYWzG/PZfn2A8Q==",
"dev": true,
"requires": {
"@types/node": "7.0.39",
"asmcrypto.js": "0.0.11",
"elliptic": "6.4.0",
"typescript": "2.3.4",
"webcrypto-core": "0.1.16"
}
},
"which": { "which": {
"version": "1.2.14", "version": "1.2.14",
"resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",

View File

@ -17,6 +17,7 @@
"redis": "^2.7.1" "redis": "^2.7.1"
}, },
"devDependencies": { "devDependencies": {
"asmcrypto.js": "0.0.11",
"babel-polyfill": "^6.23.0", "babel-polyfill": "^6.23.0",
"browserify": "^14.4.0", "browserify": "^14.4.0",
"eslint": "^4.3.0", "eslint": "^4.3.0",
@ -38,7 +39,8 @@
"stylelint-config-standard": "^17.0.0", "stylelint-config-standard": "^17.0.0",
"supertest": "^3.0.0", "supertest": "^3.0.0",
"testpilot-ga": "^0.3.0", "testpilot-ga": "^0.3.0",
"uglifyify": "^4.0.3" "uglifyify": "^4.0.3",
"webcrypto-liner": "^0.1.25"
}, },
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"

22
public/cryptofill.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -35,5 +35,5 @@
</div> </div>
</div> </div>
<a class="send-new" data-l10n-id="sendYourFilesLink" target="_blank"></a> <a class="send-new" data-l10n-id="sendYourFilesLink" href="/"></a>
</div> </div>

View File

@ -28,7 +28,7 @@
<img src="/resources/send_logo.svg" alt="Send"/><h1 class="site-title">Send</h1> <img src="/resources/send_logo.svg" alt="Send"/><h1 class="site-title">Send</h1>
</a> </a>
<div class="site-subtitle"> <div class="site-subtitle">
<a href="https://testpilot.firefox.com" target="_blank" rel="noopener noreferrer">Firefox Test Pilot</a> <a href="https://testpilot.firefox.com">Firefox Test Pilot</a>
<div data-l10n-id="siteSubtitle">web experiment</div> <div data-l10n-id="siteSubtitle">web experiment</div>
</div> </div>
</div> </div>
@ -37,23 +37,23 @@
<div class="all"> <div class="all">
<noscript> <noscript>
<h2>Firefox Send requires JavaScript</h2> <h2>Firefox Send requires JavaScript</h2>
<p><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-does-firefox-send-require-javascript" target="_blank" rel="noreferrer noopener">Why does Firefox Send require JavaScript?</a></p> <p><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-does-firefox-send-require-javascript">Why does Firefox Send require JavaScript?</a></p>
<p>Please enable JavaScript and try again.</p> <p>Please enable JavaScript and try again.</p>
</noscript> </noscript>
{{{body}}} {{{body}}}
</div> </div>
<div class="footer"> <div class="footer">
<div class="legal-links"> <div class="legal-links">
<a href="https://www.mozilla.org" target="_blank" rel="noopener noreferrer"><img class="mozilla-logo" src="/resources/mozilla-logo.svg"/></a> <a href="https://www.mozilla.org"><img class="mozilla-logo" src="/resources/mozilla-logo.svg"/></a>
<a href="https://www.mozilla.org/about/legal" data-l10n-id="footerLinkLegal" target="_blank" rel="noopener noreferrer">Legal</a> <a href="https://www.mozilla.org/about/legal" data-l10n-id="footerLinkLegal">Legal</a>
<a href="https://testpilot.firefox.com/about" data-l10n-id="footerLinkAbout" target="_blank" rel="noopener noreferrer">About Test Pilot</a> <a href="https://testpilot.firefox.com/about" data-l10n-id="footerLinkAbout">About Test Pilot</a>
<a href="/legal" data-l10n-id="footerLinkPrivacy" target="_blank">Privacy</a> <a href="/legal" data-l10n-id="footerLinkPrivacy">Privacy</a>
<a href="/legal" data-l10n-id="footerLinkTerms" target="_blank">Terms</a> <a href="/legal" data-l10n-id="footerLinkTerms">Terms</a>
<a href="https://www.mozilla.org/privacy/websites/#cookies" data-l10n-id="footerLinkCookies" target="_blank" rel="noopener noreferrer">Cookies</a> <a href="https://www.mozilla.org/privacy/websites/#cookies" data-l10n-id="footerLinkCookies">Cookies</a>
</div> </div>
<div class="social-links"> <div class="social-links">
<a href="https://github.com/mozilla/send" target="_blank" rel="noreferrer noopener"><img class="github" src="/resources/github-icon.svg"/></a> <a href="https://github.com/mozilla/send"><img class="github" src="/resources/github-icon.svg"/></a>
<a href="https://twitter.com/FxTestPilot" target="_blank" rel="noreferrer noopener"><img class="twitter" src="/resources/twitter-icon.svg"/></a> <a href="https://twitter.com/FxTestPilot"><img class="twitter" src="/resources/twitter-icon.svg"/></a>
</div> </div>
</div> </div>
</body> </body>

View File

@ -8,8 +8,8 @@
</a> </a>
{{else}} {{else}}
<div class="description" data-l10n-id="notSupportedDetail">Unfortunately this browser does not support the web technology that powers Firefox Send. Youll need to try another browser. We recommend Firefox!</div> <div class="description" data-l10n-id="notSupportedDetail">Unfortunately this browser does not support the web technology that powers Firefox Send. Youll need to try another browser. We recommend Firefox!</div>
<div class="description"><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-is-my-browser-not-supported" data-l10n-id="notSupportedLink" target="_blank" rel="noopener noreferrer">Why is my browser not supported?</a></div> <div class="description"><a href="https://github.com/mozilla/send/blob/master/docs/faq.md#why-is-my-browser-not-supported" data-l10n-id="notSupportedLink">Why is my browser not supported?</a></div>
<a id="dl-firefox" href="https://www.mozilla.org/firefox/new/?scene=2" target="_blank" rel="noopener noreferrer"> <a id="dl-firefox" href="https://www.mozilla.org/firefox/new/?scene=2">
<img src="/resources/firefox_logo-only.svg" class="firefox-logo" alt="Firefox"/> <img src="/resources/firefox_logo-only.svg" class="firefox-logo" alt="Firefox"/>
<div class="unsupported-button-text">Firefox<br> <div class="unsupported-button-text">Firefox<br>
<span data-l10n-id="downloadFirefoxButtonSub">Free Download</span> <span data-l10n-id="downloadFirefoxButtonSub">Free Download</span>