make site responsive and add feedback link

This commit is contained in:
John Gruen 2017-07-20 23:52:21 +02:00
parent 34f26fc017
commit 463393552b
9 changed files with 290 additions and 134 deletions

View File

@ -1,6 +1,6 @@
extends: stylelint-config-standard extends: stylelint-config-standard
rules: rules:
color-hex-case: upper color-hex-case: lower
declaration-colon-newline-after: null declaration-colon-newline-after: null
selector-list-comma-newline-after: null selector-list-comma-newline-after: null

View File

@ -58,10 +58,12 @@ class FileReceiver extends EventEmitter {
true, true,
['encrypt', 'decrypt'] ['encrypt', 'decrypt']
) )
]).then(([fdata, key]) => { ])
.then(([fdata, key]) => {
this.emit('decrypting', true); this.emit('decrypting', true);
return Promise.all([ return Promise.all([
window.crypto.subtle.decrypt( window.crypto.subtle
.decrypt(
{ {
name: 'AES-GCM', name: 'AES-GCM',
iv: hexToArray(fdata.iv), iv: hexToArray(fdata.iv),
@ -69,30 +71,33 @@ class FileReceiver extends EventEmitter {
}, },
key, key,
fdata.data fdata.data
).then(decrypted => { )
.then(decrypted => {
this.emit('decrypting', false); this.emit('decrypting', false);
return Promise.resolve(decrypted) return Promise.resolve(decrypted);
}), }),
fdata.filename, fdata.filename,
hexToArray(fdata.aad) hexToArray(fdata.aad)
]); ]);
}).then(([decrypted, fname, proposedHash]) => { })
.then(([decrypted, fname, proposedHash]) => {
this.emit('hashing', true); this.emit('hashing', true);
return window.crypto.subtle.digest('SHA-256', decrypted).then(calculatedHash => { return window.crypto.subtle
.digest('SHA-256', decrypted)
.then(calculatedHash => {
this.emit('hashing', false); this.emit('hashing', false);
const integrity = new Uint8Array(calculatedHash).toString() === proposedHash.toString(); const integrity =
new Uint8Array(calculatedHash).toString() ===
proposedHash.toString();
if (!integrity) { if (!integrity) {
this.emit('unsafe', true) this.emit('unsafe', true);
return Promise.reject(); return Promise.reject();
} else { } else {
this.emit('safe', true); this.emit('safe', true);
return Promise.all([ return Promise.all([decrypted, decodeURIComponent(fname)]);
decrypted,
decodeURIComponent(fname)
]);
} }
}) });
}) });
} }
} }

View File

@ -40,7 +40,9 @@ $(document).ready(function() {
//disable button for 3s //disable button for 3s
$copyBtn.attr('disabled', true); $copyBtn.attr('disabled', true);
$('#link').attr('disabled', true); $('#link').attr('disabled', true);
$copyBtn.html('<img src="/resources/check-16.svg" class="icon-check"></img>'); $copyBtn.html(
'<img src="/resources/check-16.svg" class="icon-check"></img>'
);
window.setTimeout(() => { window.setTimeout(() => {
$copyBtn.attr('disabled', false); $copyBtn.attr('disabled', false);
$('#link').attr('disabled', false); $('#link').attr('disabled', false);
@ -71,10 +73,12 @@ $(document).ready(function() {
event.preventDefault(); event.preventDefault();
let file = ''; let file = '';
if (event.type === 'drop') { if (event.type === 'drop') {
if (event.originalEvent.dataTransfer.files.length > 1 || event.originalEvent.dataTransfer.files[0].size === 0){ if (
event.originalEvent.dataTransfer.files.length > 1 ||
event.originalEvent.dataTransfer.files[0].size === 0
) {
$('.upload-window').removeClass('ondrag'); $('.upload-window').removeClass('ondrag');
document.l10n.formatValue('uploadPageMultipleFilesAlert') document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => {
.then(str => {
alert(str); alert(str);
}); });
return; return;
@ -95,8 +99,7 @@ $(document).ready(function() {
$('#cancel-upload').click(() => { $('#cancel-upload').click(() => {
fileSender.cancel(); fileSender.cancel();
location.reload(); location.reload();
document.l10n.formatValue('uploadCancelNotification') document.l10n.formatValue('uploadCancelNotification').then(str => {
.then(str => {
notify(str); notify(str);
}); });
}); });
@ -110,15 +113,21 @@ $(document).ready(function() {
}); });
if (progress[1] < 1000000) { if (progress[1] < 1000000) {
$('.progress-text').text( $('.progress-text').text(
`${file.name} (${(progress[0] / 1000).toFixed(1)}KB of ${(progress[1] / 1000).toFixed(1)}KB)` `${file.name} (${(progress[0] / 1000).toFixed(
1
)}KB of ${(progress[1] / 1000).toFixed(1)}KB)`
); );
} else if (progress[1] < 1000000000) { } else if (progress[1] < 1000000000) {
$('.progress-text').text( $('.progress-text').text(
`${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000).toFixed(1)}MB)` `${file.name} (${(progress[0] / 1000000).toFixed(
1
)}MB of ${(progress[1] / 1000000).toFixed(1)}MB)`
); );
} else { } else {
$('.progress-text').text( $('.progress-text').text(
`${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)` `${file.name} (${(progress[0] / 1000000).toFixed(
1
)}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)`
); );
} }
}); });
@ -163,7 +172,10 @@ $(document).ready(function() {
expiry: expiration expiry: expiration
}; };
localStorage.setItem(info.fileId, JSON.stringify(fileData)); localStorage.setItem(info.fileId, JSON.stringify(fileData));
$('#upload-filename').attr('data-l10n-id', 'uploadSuccessConfirmHeader'); $('#upload-filename').attr(
'data-l10n-id',
'uploadSuccessConfirmHeader'
);
t = window.setTimeout(() => { t = window.setTimeout(() => {
$('#page-one').attr('hidden', true); $('#page-one').attr('hidden', true);
$('#upload-progress').attr('hidden', true); $('#upload-progress').attr('hidden', true);
@ -172,8 +184,7 @@ $(document).ready(function() {
}, 1000); }, 1000);
populateFileList(JSON.stringify(fileData)); populateFileList(JSON.stringify(fileData));
document.l10n.formatValue('notifyUploadDone') document.l10n.formatValue('notifyUploadDone').then(str => {
.then(str => {
notify(str); notify(str);
}); });
}) })
@ -219,10 +230,18 @@ $(document).ready(function() {
const row = document.createElement('tr'); const row = document.createElement('tr');
const name = document.createElement('td'); const name = document.createElement('td');
const link = document.createElement('td'); const link = document.createElement('td');
const $copyIcon = $('<img>', { src: '/resources/copy-16.svg', class: 'icon-copy', 'data-l10n-id': 'copyUrlHover'}); const $copyIcon = $('<img>', {
src: '/resources/copy-16.svg',
class: 'icon-copy',
'data-l10n-id': 'copyUrlHover'
});
const expiry = document.createElement('td'); const expiry = document.createElement('td');
const del = document.createElement('td'); const del = document.createElement('td');
const $delIcon = $('<img>', { src: '/resources/close-16.svg', class: 'icon-delete', 'data-l10n-id': 'deleteButtonHover' }); const $delIcon = $('<img>', {
src: '/resources/close-16.svg',
class: 'icon-delete',
'data-l10n-id': 'deleteButtonHover'
});
const popupDiv = document.createElement('div'); const popupDiv = document.createElement('div');
const $popupText = $('<div>', { class: 'popuptext' }); const $popupText = $('<div>', { class: 'popuptext' });
const cellText = document.createTextNode(file.name); const cellText = document.createTextNode(file.name);
@ -230,14 +249,8 @@ $(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( $('#copy-text').attr('data-l10n-args', '{"filename": "' + file.name + '"}');
'data-l10n-args', $('#copy-text').attr('data-l10n-id', 'copyUrlFormLabelWithName');
'{"filename": "' + file.name + '"}'
);
$('#copy-text').attr(
'data-l10n-id',
'copyUrlFormLabelWithName'
);
$popupText.attr('tabindex', '-1'); $popupText.attr('tabindex', '-1');
name.appendChild(cellText); name.appendChild(cellText);
@ -264,8 +277,7 @@ $(document).ready(function() {
aux.select(); aux.select();
document.execCommand('copy'); document.execCommand('copy');
document.body.removeChild(aux); document.body.removeChild(aux);
document.l10n.formatValue('copiedUrl') document.l10n.formatValue('copiedUrl').then(translated => {
.then(translated => {
link.innerHTML = translated; link.innerHTML = translated;
}); });
window.setTimeout(() => { window.setTimeout(() => {
@ -327,13 +339,7 @@ $(document).ready(function() {
$(popupNvmSpan).addClass('nvm'); $(popupNvmSpan).addClass('nvm');
$(popupNvmSpan).attr('data-l10n-id', 'nevermindButton'); $(popupNvmSpan).attr('data-l10n-id', 'nevermindButton');
$popupText.html([ $popupText.html([popupDelSpan, '&nbsp;', '&nbsp;', popupNvmSpan]);
popupDelSpan,
'&nbsp;',
'&nbsp;',
popupNvmSpan
]);
// add data cells to table row // add data cells to table row
row.appendChild(name); row.appendChild(name);
@ -378,7 +384,6 @@ $(document).ready(function() {
$popupText.removeClass('show'); $popupText.removeClass('show');
}); });
toggleHeader(); toggleHeader();
} }
function toggleHeader() { function toggleHeader() {

View File

@ -1,4 +1,6 @@
title = Firefox Send title = Firefox Send
siteSubtitle = web experiment
siteFeedback = Feedback
uploadPageHeader = Private, Encrypted File Sharing uploadPageHeader = Private, Encrypted File Sharing
uploadPageExplainer = Send files through a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever. uploadPageExplainer = Send files through a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever.

View File

@ -1,7 +1,12 @@
/*** index.html ***/ /*** index.html ***/
html { html {
background: url('resources/send_bg.svg'); background: url('resources/send_bg.svg');
font-family: 'SF Pro Text', sans-serif; font-family: -apple-system,
BlinkMacSystemFont,
'SF Pro Text',
Helvetica,
Arial,
sans-serif;
font-weight: 200; font-weight: 200;
background-size: 100%; background-size: 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -10,24 +15,94 @@ html {
} }
body { body {
min-height: 100%; display: flex;
position: relative; flex-direction: column;
margin: 0; margin: 0;
min-height: 100vh;
position: relative;
}
.header {
align-items: flex-start;
box-sizing: border-box;
display: flex;
justify-content: space-between;
padding: 31px;
width: 100%;
} }
.send-logo { .send-logo {
display: flex;
position: relative; position: relative;
top: 31px; align-items: center;
left: 31px; }
display: inline-block;
.site-title {
font-size: 34px;
font-weight: 500;
margin: 0;
position: relative;
top: -1px;
}
.site-subtitle {
font-size: 12px;
margin: 0 8px;
color: #0c0c0d;
}
.site-subtitle a {
font-weight: bold;
color: #0c0c0d;
transition: color 50ms;
}
.send-logo:hover a {
color: #0297f8;
}
.feedback {
background-color: #0297f8;
background-image: url('resources/feedback.svg');
background-position: 4px 6px;
background-repeat: no-repeat;
background-size: 14px;
border-radius: 3px;
border: 1px solid #0297f8;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
color: #fff;
cursor: pointer;
display: block;
float: right;
font-size: 12px;
line-height: 12px;
opacity: 0.9;
padding: 6px 6px 5px 20px;
}
.feedback:hover,
.feedback:focus {
background-color: #0287e8;
}
.feedback:active {
background-color: #0277d8;
} }
.all { .all {
padding-top: 10%; flex: 1;
padding-bottom: 51px; display: flex;
flex-direction: column;
justify-content: flex-start;
max-width: 630px;
margin: 0 auto;
width: 96%;
} }
input, select, textarea, button { input,
select,
textarea,
button {
font-family: inherit; font-family: inherit;
} }
@ -38,24 +113,26 @@ a {
/** page-one **/ /** page-one **/
.title { .title {
font-size: 33px; font-size: 33px;
line-height: 40px;
margin: 20px auto; margin: 20px auto;
text-align: center; text-align: center;
max-width: 520px;
font-family: 'SF Pro Display', sans-serif; font-family: 'SF Pro Display', sans-serif;
} }
.description { .description {
font-size: 15px; font-size: 15px;
line-height: 23px; line-height: 23px;
width: 630px; max-width: 630px;
text-align: center; text-align: center;
margin: 0 auto 60px; margin: 0 auto 60px;
color: #0C0C0D; color: #0c0c0d;
width: 92%;
} }
.upload-window { .upload-window {
border: 1px dashed rgba(0, 148, 251, 0.5); border: 1px dashed rgba(0, 148, 251, 0.5);
margin: 0 auto; margin: 0 auto;
width: 640px;
height: 255px; height: 255px;
border-radius: 4px; border-radius: 4px;
display: flex; display: flex;
@ -63,14 +140,15 @@ a {
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
transition: transform 150ms;
padding: 15px;
} }
.upload-window.ondrag { .upload-window.ondrag {
border: 3px dashed rgba(0, 148, 251, 0.5); border: 3px dashed rgba(0, 148, 251, 0.5);
margin: 0 auto; margin: 0 auto;
width: 636px;
height: 251px; height: 251px;
transform: scale(1.05); transform: scale(1.04);
border-radius: 4.2px; border-radius: 4.2px;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -80,7 +158,7 @@ a {
} }
.link { .link {
color: #0094FB; color: #0094fb;
text-decoration: none; text-decoration: none;
} }
@ -92,10 +170,10 @@ a {
} }
#browse { #browse {
background: #0297F8; background: #0297f8;
border-radius: 5px; border-radius: 5px;
font-size: 15px; font-size: 15px;
color: #FFF; color: #fff;
width: 240px; width: 240px;
height: 44px; height: 44px;
display: flex; display: flex;
@ -110,6 +188,7 @@ input[type="file"] {
#file-size-msg { #file-size-msg {
font-size: 12px; font-size: 12px;
line-height: 16px;
color: #737373; color: #737373;
margin-bottom: 22px; margin-bottom: 22px;
} }
@ -129,7 +208,7 @@ th {
td { td {
font-size: 15px; font-size: 15px;
vertical-align: top; vertical-align: top;
color: #4A4A4A; color: #4a4a4a;
padding: 17px 19px 0; padding: 17px 19px 0;
line-height: 23px; line-height: 23px;
} }
@ -144,12 +223,13 @@ tbody {
} }
#uploaded-files { #uploaded-files {
width: 640px;
margin: 45.3px auto; margin: 45.3px auto;
table-layout: fixed; table-layout: fixed;
} }
.icon-delete, .icon-copy, .icon-check { .icon-delete,
.icon-copy,
.icon-check {
cursor: pointer; cursor: pointer;
} }
@ -165,7 +245,7 @@ tbody {
visibility: hidden; visibility: hidden;
width: 160px; width: 160px;
background-color: #555; background-color: #555;
color: #FFF; color: #fff;
text-align: center; text-align: center;
border-radius: 6px; border-radius: 6px;
padding: 8px 0; padding: 8px 0;
@ -239,14 +319,13 @@ tbody {
} }
#cancel-upload { #cancel-upload {
color: #D70022; color: #d70022;
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
} }
/** share-link **/ /** share-link **/
#share-window { #share-window {
width: 645px;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -262,53 +341,59 @@ tbody {
#copy { #copy {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
width: 640px;
} }
#copy-text { #copy-text {
align-self: flex-start; align-self: flex-start;
margin-top: 60px; margin-top: 60px;
margin-bottom: 10px; margin-bottom: 10px;
color: #0C0C0D; color: #0c0c0d;
} }
#link { #link {
width: 480px; flex: 1;
height: 56px; height: 56px;
border: 1px solid #0297F8; border: 1px solid #0297f8;
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
font-size: 24px; font-size: 24px;
color: #737373; color: #737373;
font-family: 'SF Pro Display', sans-serif; font-family: 'SF Pro Display', sans-serif;
letter-spacing: 0; letter-spacing: 0;
line-height: 23px; line-height: 23px;
padding-left: 5px;
padding-right: 5px;
} }
#link:disabled { #link:disabled {
border: 1px solid #05A700; border: 1px solid #05a700;
background: #FFF; background: #fff;
} }
#copy-btn { #copy-btn {
width: 165px; flex: 0 1 165px;
height: 60px; background: #0297f8;
background: #0297F8;
border: 1px solid #0297F8;
border-radius: 0 6px 6px 0; border-radius: 0 6px 6px 0;
border: 1px solid #0297f8;
color: white; color: white;
cursor: pointer; cursor: pointer;
font-size: 15px; font-size: 15px;
height: 60px;
padding-left: 10px;
padding-right: 10px;
white-space: nowrap;
} }
#copy-btn:disabled { #copy-btn:disabled {
background: #05A700; background: #05a700;
border: 1px solid #05A700; border: 1px solid #05a700;
cursor: auto; cursor: auto;
} }
#delete-file { #delete-file {
width: 176px; width: 176px;
height: 44px; height: 44px;
background: #FFF; background: #fff;
border: 1px solid rgba(12, 12, 13, 0.3); border: 1px solid rgba(12, 12, 13, 0.3);
border-radius: 5px; border-radius: 5px;
font-size: 15px; font-size: 15px;
@ -322,7 +407,7 @@ tbody {
font-size: 15px; font-size: 15px;
margin: auto; margin: auto;
text-align: center; text-align: center;
color: #0094FB; color: #0094fb;
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
} }
@ -336,7 +421,8 @@ tbody {
text-align: center; text-align: center;
} }
#upload-error[hidden], #unsupported-browser[hidden] { #upload-error[hidden],
#unsupported-browser[hidden] {
display: none; display: none;
} }
@ -356,9 +442,8 @@ tbody {
.unsupported-description { .unsupported-description {
font-size: 13px; font-size: 13px;
line-height: 23px; line-height: 23px;
width: 630px;
text-align: center; text-align: center;
color: #7D7D7D; color: #7d7d7d;
margin: 0 auto 23px; margin: 0 auto 23px;
} }
@ -370,14 +455,14 @@ tbody {
margin-bottom: 181px; margin-bottom: 181px;
width: 260px; width: 260px;
height: 80px; height: 80px;
background: #12BC00; background: #12bc00;
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
border: 0; border: 0;
box-shadow: 0 5px 3px rgb(234, 234, 234); box-shadow: 0 5px 3px rgb(234, 234, 234);
font-family: 'Fira Sans'; font-family: 'Fira Sans';
font-weight: 500; font-weight: 500;
color: #FFF; color: #fff;
font-size: 26px; font-size: 26px;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -406,15 +491,15 @@ tbody {
margin-top: 20px; margin-top: 20px;
margin-bottom: 30px; margin-bottom: 30px;
text-align: center; text-align: center;
background: #0297F8; background: #0297f8;
border: 1px solid #0297F8; border: 1px solid #0297f8;
border-radius: 5px; border-radius: 5px;
font-weight: 300; font-weight: 300;
cursor: pointer; cursor: pointer;
} }
#download-btn:disabled { #download-btn:disabled {
background: #47B04B; background: #47b04b;
cursor: auto; cursor: auto;
} }
@ -434,9 +519,8 @@ tbody {
.expired-description { .expired-description {
font-size: 15px; font-size: 15px;
line-height: 23px; line-height: 23px;
width: 630px;
text-align: center; text-align: center;
color: #7D7D7D; color: #7d7d7d;
margin: 0 auto 23px; margin: 0 auto 23px;
} }
@ -460,14 +544,13 @@ tbody {
/* footer */ /* footer */
.footer { .footer {
position: absolute;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
font-size: 15px; font-size: 15px;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
padding: 10px; padding: 50px 10px 10px;
} }
.mozilla-logo { .mozilla-logo {
@ -495,8 +578,60 @@ tbody {
margin-left: 30px; margin-left: 30px;
} }
.github, .twitter { .github,
.twitter {
width: 32px; width: 32px;
height: 32px; height: 32px;
margin-bottom: -5px; margin-bottom: -5px;
} }
@media (max-width: 768px) {
.description {
margin: 0 auto 25px;
}
#copy {
width: 100%;
}
#link {
font-size: 18px;
}
.mozilla-logo {
margin-left: -7px;
}
.legal-links > * {
display: block;
padding: 10px 0;
}
}
@media (max-width: 520px) {
#copy {
width: 100%;
flex-direction: column;
}
#link {
font-size: 22px;
padding: 15px 10px;
border-radius: 6px 6px 0 0;
}
#copy-btn {
border-radius: 0 0 6px 6px;
flex: 0 1 65px;
}
th {
font-size: 14px;
padding: 0 5px;
}
td {
font-size: 13px;
padding: 17px 5px 0;
}
}

View File

@ -0,0 +1 @@
<svg width="15" height="13" viewBox="0 0 15 13" xmlns="http://www.w3.org/2000/svg"><title>Combined Shape</title><path d="M10.274 9.193a5.957 5.957 0 0 1-2.98.778C4.37 9.97 2 7.963 2 5.485 2 3.008 4.37 1 7.294 1c2.924 0 5.294 2.008 5.294 4.485 0 .843-.274 1.632-.751 2.305l.577 2.21-2.14-.807zm-5.983-2.96a.756.756 0 0 0 .763-.748.756.756 0 0 0-.763-.747.756.756 0 0 0-.764.747c0 .413.342.748.764.748zm3.054 0a.756.756 0 0 0 .764-.748.756.756 0 0 0-.764-.747.756.756 0 0 0-.764.747c0 .413.342.748.764.748zm3.054 0a.756.756 0 0 0 .764-.748.756.756 0 0 0-.764-.747.756.756 0 0 0-.763.747c0 .413.342.748.763.748z" fill="#FFF" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 649 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -34,10 +34,12 @@ app.engine(
app.set('view engine', 'handlebars'); app.set('view engine', 'handlebars');
app.use(helmet()); app.use(helmet());
app.use(helmet.hsts({ app.use(
helmet.hsts({
maxAge: 31536000, maxAge: 31536000,
force: conf.env === 'production' force: conf.env === 'production'
})); })
);
app.use( app.use(
helmet.contentSecurityPolicy({ helmet.contentSecurityPolicy({
directives: { directives: {
@ -241,7 +243,7 @@ app.post('/upload', (req, res, next) => {
.catch(err => { .catch(err => {
log.info('DeleteError:', newId); log.info('DeleteError:', newId);
}); });
}) });
}); });
app.get('/__lbheartbeat__', (req, res) => { app.get('/__lbheartbeat__', (req, res) => {

View File

@ -13,10 +13,17 @@
<script defer src="/l20n/dist/web/l20n.js"></script> <script defer src="/l20n/dist/web/l20n.js"></script>
</head> </head>
<body> <body>
<header class="header">
<div class="send-logo"> <div class="send-logo">
<img src="/resources/send_logo.svg"/> <img src="/resources/send_logo.svg" alt="Send"/>
<img src="/resources/send_logo_type.svg"/> <h1 class="site-title">Send</h1>
<div class="site-subtitle">
<a href="https://testpilot.firefox.com" target="_blank">Firefox Test Pilot</a>
<div data-l10n-id="siteSubtitle">web experiment</div>
</div> </div>
</div>
<a href="https://qsurvey.mozilla.com/s3/txp-firefox-send" rel="noreferrer noopener" class="feedback" target="_blank" data-l10n-id="siteFeedback">Feedback</a>
</header>
<div class="all"> <div class="all">
{{{body}}} {{{body}}}
</div> </div>