Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRX: persistent background page -> event page #1487

Merged
merged 4 commits into from
Jan 19, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lighthouse-extension/app/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"scripts": [
"scripts/chromereload.js",
"scripts/lighthouse-background.js"
]
],
"persistent": false
},
"permissions": [
"activeTab",
Expand Down
282 changes: 145 additions & 137 deletions lighthouse-extension/app/src/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,152 +16,117 @@
*/
'use strict';

document.addEventListener('DOMContentLoaded', _ => {
const background = chrome.extension.getBackgroundPage();
const defaultAggregations = background.getDefaultAggregations();

const siteNameEl = window.document.querySelector('header h2');
const generateReportEl = document.getElementById('generate-report');
const subpageVisibleClass = 'subpage--visible';

const statusEl = document.body.querySelector('.status');
const statusMessageEl = document.body.querySelector('.status__msg');
const statusDetailsMessageEl = document.body.querySelector('.status__detailsmsg');
const feedbackEl = document.body.querySelector('.feedback');

const generateOptionsEl = document.getElementById('configure-options');
const optionsEl = document.body.querySelector('.options');
const optionsList = document.body.querySelector('.options__list');
const okButton = document.getElementById('ok');
/**
* Error strings that indicate a problem in how Lighthouse was run, not in
* Lighthouse itself, mapped to more useful strings to report to the user.
*/
const NON_BUG_ERROR_MESSAGES = {
'Another debugger': 'You probably have DevTools open. Close DevTools to use Lighthouse',
'multiple tabs': 'You probably have multiple tabs open to the same origin. ' +
'Close the other tabs to use Lighthouse.',
// The extension debugger API is forbidden from attaching to the web store.
// @see https://chromium.googlesource.com/chromium/src/+/5d1f214db0f7996f3c17cd87093d439ce4c7f8f1/chrome/common/extensions/chrome_extensions_client.cc#232
'The extensions gallery cannot be scripted': 'The Lighthouse extension cannot audit the ' +
'Chrome Web Store. If necessary, use the Lighthouse CLI to do so.',
// The user tries to review an error page or has network issues
'Unable to load the page': 'Unable to load the page. Please verify the url you ' +
'are trying to review.'
};

const subpageVisibleClass = 'subpage--visible';

const getBackgroundPage = new Promise((resolve, reject) => {
chrome.runtime.getBackgroundPage(resolve);
});

const MAX_ISSUE_ERROR_LENGTH = 60;
let siteURL = null;
let statusEl = null;

let siteURL = null;

/**
* Error strings that indicate a problem in how Lighthouse was run, not in
* Lighthouse itself, mapped to more useful strings to report to the user.
*/
const NON_BUG_ERROR_MESSAGES = {
'Another debugger': 'You probably have DevTools open. Close DevTools to use Lighthouse',
'multiple tabs': 'You probably have multiple tabs open to the same origin. ' +
'Close the other tabs to use Lighthouse.',
// The extension debugger API is forbidden from attaching to the web store.
// @see https://chromium.googlesource.com/chromium/src/+/5d1f214db0f7996f3c17cd87093d439ce4c7f8f1/chrome/common/extensions/chrome_extensions_client.cc#232
'The extensions gallery cannot be scripted': 'The Lighthouse extension cannot audit the ' +
'Chrome Web Store. If necessary, use the Lighthouse CLI to do so.',
// The user tries to review an error page or has network issues
'Unable to load the page': 'Unable to load the page. Please verify the url you ' +
'are trying to review.'
};

function getLighthouseVersion() {
return chrome.runtime.getManifest().version;
}
function getLighthouseVersion() {
return chrome.runtime.getManifest().version;
}

function getChromeVersion() {
return /Chrome\/([0-9.]+)/.exec(navigator.userAgent)[1];
}
function getChromeVersion() {
return /Chrome\/([0-9.]+)/.exec(navigator.userAgent)[1];
}

function buildReportErrorLink(err) {
const reportErrorEl = document.createElement('a');
reportErrorEl.className = 'button button--report-error';
function startSpinner() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not just a spinner...showStatusSubpage/hideStatusSubpage?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

statusEl.classList.add(subpageVisibleClass);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe do document.querySelector('.status') rather than keeping global around?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

let qsBody = '**Lighthouse Version**: ' + getLighthouseVersion() + '\n';
qsBody += '**Chrome Version**: ' + getChromeVersion() + '\n';
function stopSpinner() {
statusEl.classList.remove(subpageVisibleClass);
}

if (siteURL) {
qsBody += '**URL**: ' + siteURL + '\n';
}

qsBody += '**Error Message**: ' + err.message + '\n';
qsBody += '**Stack Trace**:\n ```' + err.stack + '```';
function buildReportErrorLink(err) {
const MAX_ISSUE_ERROR_LENGTH = 60;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

top level const is normal for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


const base = 'https://github.com/googlechrome/lighthouse/issues/new?';
let titleError = err.message;
if (titleError.length > MAX_ISSUE_ERROR_LENGTH) {
titleError = `${titleError.substring(0, MAX_ISSUE_ERROR_LENGTH - 3)}...`;
}
const title = encodeURI('title=Extension Error: ' + titleError);
const body = '&body=' + encodeURI(qsBody);
let qsBody = '**Lighthouse Version**: ' + getLighthouseVersion() + '\n';
qsBody += '**Chrome Version**: ' + getChromeVersion() + '\n';

reportErrorEl.href = base + title + body;
reportErrorEl.textContent = 'Report Error';
reportErrorEl.target = '_blank';
return reportErrorEl;
if (siteURL) {
qsBody += '**URL**: ' + siteURL + '\n';
}

function startSpinner() {
statusEl.classList.add(subpageVisibleClass);
}
qsBody += '**Error Message**: ' + err.message + '\n';
qsBody += '**Stack Trace**:\n ```' + err.stack + '```';

function stopSpinner() {
statusEl.classList.remove(subpageVisibleClass);
}
const base = 'https://github.com/googlechrome/lighthouse/issues/new?';
let titleError = err.message;

function logstatus([, message, details]) {
statusMessageEl.textContent = message;
statusDetailsMessageEl.textContent = details;
if (titleError.length > MAX_ISSUE_ERROR_LENGTH) {
titleError = `${titleError.substring(0, MAX_ISSUE_ERROR_LENGTH - 3)}...`;
}

function createOptionItem(text, isChecked) {
const input = document.createElement('input');
input.setAttribute('type', 'checkbox');
input.setAttribute('value', text);
if (isChecked) {
input.setAttribute('checked', 'checked');
}

const label = document.createElement('label');
label.appendChild(input);
label.appendChild(document.createTextNode(text));
const listItem = document.createElement('li');
listItem.appendChild(label);

return listItem;
const title = encodeURI('title=Extension Error: ' + titleError);
const body = '&body=' + encodeURI(qsBody);

const reportErrorEl = document.createElement('a');
reportErrorEl.className = 'button button--report-error';
reportErrorEl.href = base + title + body;
reportErrorEl.textContent = 'Report Error';
reportErrorEl.target = '_blank';

return reportErrorEl;
}

function logstatus([, message, details]) {
document.querySelector('.status__msg').textContent = message;
const statusDetailsMessageEl = document.querySelector('.status__detailsmsg');
statusDetailsMessageEl.textContent = details;
}

function createOptionItem(text, isChecked) {
const input = document.createElement('input');
input.setAttribute('type', 'checkbox');
input.setAttribute('value', text);
if (isChecked) {
input.setAttribute('checked', 'checked');
}

/**
* Generates a document fragment containing a list of checkboxes and labels
* for the aggregation categories.
* @param {!Object<boolean>} selectedAggregations
* @return {!DocumentFragment}
*/
function generateOptionsList(list, selectedAggregations) {
const frag = document.createDocumentFragment();

defaultAggregations.forEach(aggregation => {
const isChecked = selectedAggregations[aggregation.name];
frag.appendChild(createOptionItem(aggregation.name, isChecked));
});

return frag;
}
const label = document.createElement('label');
label.appendChild(input);
label.appendChild(document.createTextNode(text));
const listItem = document.createElement('li');
listItem.appendChild(label);

if (background.isRunning()) {
startSpinner();
}
return listItem;
}

background.listenForStatus(logstatus);
background.loadSelectedAggregations().then(aggregations => {
const frag = generateOptionsList(optionsList, aggregations);
optionsList.appendChild(frag);
});
function onGenerateReportButtonClick() {
startSpinner();

generateReportEl.addEventListener('click', () => {
startSpinner();
feedbackEl.textContent = '';
const feedbackEl = document.querySelector('.feedback');
feedbackEl.textContent = '';

background.loadSelectedAggregations()
.then(selectedAggregations => {
getBackgroundPage.then(background => {
background.loadSelectedAggregations().then(selectedAggregations => {
return background.runLighthouseInExtension({
flags: {
disableCpuThrottling: true
},
restoreCleanState: true
}, selectedAggregations);
})
.catch(err => {
}).catch(err => {
let message = err.message;
let includeReportLink = true;

Expand All @@ -186,26 +151,69 @@ document.addEventListener('DOMContentLoaded', _ => {
background.console.error(err);
});
});
}

generateOptionsEl.addEventListener('click', () => {
optionsEl.classList.add(subpageVisibleClass);
});
document.addEventListener('DOMContentLoaded', _ => {
getBackgroundPage.then(background => {
statusEl = document.querySelector('.status');

/**
* Generates a document fragment containing a list of checkboxes and labels
* for the aggregation categories.
* @param {!Object<boolean>} selectedAggregations
* @return {!DocumentFragment}
*/
function generateOptionsList(list, selectedAggregations) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this outside of the DOMContentLoaded block? It's already used asynchronously, so fine if it's asyc as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. but needed to add a background page param to make that work.

const frag = document.createDocumentFragment();

const defaultAggregations = background.getDefaultAggregations();
defaultAggregations.forEach(aggregation => {
const isChecked = selectedAggregations[aggregation.name];
frag.appendChild(createOptionItem(aggregation.name, isChecked));
});

return frag;
}

okButton.addEventListener('click', () => {
// Save selected aggregation categories on options page close.
const checkedAggregations = Array.from(optionsEl.querySelectorAll(':checked'))
.map(input => input.value);
background.saveSelectedAggregations(checkedAggregations);
if (background.isRunning()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move all the rest of this to a named setup function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

startSpinner();
}

optionsEl.classList.remove(subpageVisibleClass);
});
const optionsList = document.querySelector('.options__list');

chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) {
if (tabs.length === 0) {
return;
}
background.listenForStatus(logstatus);
background.loadSelectedAggregations().then(aggregations => {
const frag = generateOptionsList(optionsList, aggregations);
optionsList.appendChild(frag);
});

siteURL = new URL(tabs[0].url);
siteNameEl.textContent = siteURL.origin;
const generateReportEl = document.getElementById('generate-report');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generateReportButton?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

generateReportEl.addEventListener('click', onGenerateReportButtonClick);

const generateOptionsEl = document.getElementById('configure-options');
const optionsEl = document.querySelector('.options');
generateOptionsEl.addEventListener('click', () => {
optionsEl.classList.add(subpageVisibleClass);
});

const okButton = document.getElementById('ok');
okButton.addEventListener('click', () => {
// Save selected aggregation categories on options page close.
const checkedAggregations = Array.from(optionsEl.querySelectorAll(':checked'))
.map(input => input.value);
background.saveSelectedAggregations(checkedAggregations);

optionsEl.classList.remove(subpageVisibleClass);
});

chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) {
if (tabs.length === 0) {
return;
}

siteURL = new URL(tabs[0].url);

document.querySelector('header h2').textContent = siteURL.origin;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment here? or intermediate originEl (or whatever) variable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

});
});
});