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 3 commits
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
261 changes: 137 additions & 124 deletions lighthouse-extension/app/src/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,152 +16,116 @@
*/
'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');

const MAX_ISSUE_ERROR_LENGTH = 60;

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;
}
/**
* 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 MAX_ISSUE_ERROR_LENGTH = 60;

const subpageVisibleClass = 'subpage--visible';

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

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

function buildReportErrorLink(err) {
const reportErrorEl = document.createElement('a');
reportErrorEl.className = 'button button--report-error';
function getLighthouseVersion() {
return chrome.runtime.getManifest().version;
}

let qsBody = '**Lighthouse Version**: ' + getLighthouseVersion() + '\n';
qsBody += '**Chrome Version**: ' + getChromeVersion() + '\n';
function getChromeVersion() {
return /Chrome\/([0-9.]+)/.exec(navigator.userAgent)[1];
}

if (siteURL) {
qsBody += '**URL**: ' + siteURL + '\n';
}
function showRunningSubpage() {
document.querySelector('.status').classList.add(subpageVisibleClass);
}

qsBody += '**Error Message**: ' + err.message + '\n';
qsBody += '**Stack Trace**:\n ```' + err.stack + '```';
function hideRunningSubpage() {
document.querySelector('.status').classList.remove(subpageVisibleClass);
}

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);
function buildReportErrorLink(err) {
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() {
showRunningSubpage();

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 @@ -182,15 +146,58 @@ document.addEventListener('DOMContentLoaded', _ => {
feedbackEl.appendChild(buildReportErrorLink(err));
}

stopSpinner();
hideRunningSubpage();
background.console.error(err);
});
});
}

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

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

return frag;
}
Copy link
Member

Choose a reason for hiding this comment

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

this doesn't use the first list argument in here.

I was thinking you could also just have it fetch the background itself (though it doesn't really matter), and since selectedAggregations is only used in here (not in the parent), you could even absorb that too:

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

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

then below it would be
generateOptionsList().then(frag => optionsList.appendChild(frag));

Copy link
Member

Choose a reason for hiding this comment

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

(could still just pass in background, of course, since the caller already has a reference to it)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

redid this function a bit.


/**
* Initializes the popup's state and UI elements.
* @param {!Window} background Reference to the extension's background page.
*/
function initPopup(background) {
if (background.isRunning()) {
showRunningSubpage();
}

background.listenForStatus(logStatus);

const optionsList = document.querySelector('.options__list');
background.loadSelectedAggregations().then(aggregations => {
const frag = generateOptionsList(optionsList, aggregations, background);
optionsList.appendChild(frag);
});

const generateReportButton = document.getElementById('generate-report');
generateReportButton.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'))
Expand All @@ -206,6 +213,12 @@ document.addEventListener('DOMContentLoaded', _ => {
}

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

// Show the user what URL is going to be tested.
document.querySelector('header h2').textContent = siteURL.origin;
});
}

document.addEventListener('DOMContentLoaded', _ => {
getBackgroundPage.then(initPopup);
});