-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,188 +16,203 @@ | |
*/ | ||
'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)}...`; | ||
} | ||
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'); | ||
} | ||
|
||
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); | ||
|
||
const label = document.createElement('label'); | ||
label.appendChild(input); | ||
label.appendChild(document.createTextNode(text)); | ||
const listItem = document.createElement('li'); | ||
listItem.appendChild(label); | ||
return listItem; | ||
} | ||
|
||
return listItem; | ||
} | ||
/** | ||
* Click event handler for Generate Report button. | ||
* @param {!Window} background Reference to the extension's background page. | ||
* @param {!Object<boolean>} selectedAggregations | ||
*/ | ||
function onGenerateReportButtonClick(background, selectedAggregations) { | ||
showRunningSubpage(); | ||
|
||
const feedbackEl = document.querySelector('.feedback'); | ||
feedbackEl.textContent = ''; | ||
|
||
background.runLighthouseInExtension({ | ||
flags: { | ||
disableCpuThrottling: true | ||
}, | ||
restoreCleanState: true | ||
}, selectedAggregations).catch(err => { | ||
let message = err.message; | ||
let includeReportLink = true; | ||
|
||
// Check for errors in how the user ran Lighthouse and replace with a more | ||
// helpful message (and remove 'Report Error' link). | ||
for (const [test, replacement] of Object.entries(NON_BUG_ERROR_MESSAGES)) { | ||
if (message.includes(test)) { | ||
message = replacement; | ||
includeReportLink = false; | ||
break; | ||
} | ||
} | ||
|
||
/** | ||
* 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)); | ||
}); | ||
feedbackEl.textContent = message; | ||
|
||
return frag; | ||
} | ||
if (includeReportLink) { | ||
feedbackEl.className = 'feedback-error'; | ||
feedbackEl.appendChild(buildReportErrorLink(err)); | ||
} | ||
|
||
if (background.isRunning()) { | ||
startSpinner(); | ||
} | ||
hideRunningSubpage(); | ||
background.console.error(err); | ||
}); | ||
} | ||
|
||
/** | ||
* Generates a document fragment containing a list of checkboxes and labels | ||
* for the aggregation categories. | ||
* @param {!Window} background Reference to the extension's background page. | ||
* @param {!Object<boolean>} selectedAggregations | ||
*/ | ||
function generateOptionsList(background, selectedAggregations) { | ||
const frag = document.createDocumentFragment(); | ||
|
||
background.listenForStatus(logstatus); | ||
background.loadSelectedAggregations().then(aggregations => { | ||
const frag = generateOptionsList(optionsList, aggregations); | ||
optionsList.appendChild(frag); | ||
background.getDefaultAggregations().forEach(aggregation => { | ||
const isChecked = selectedAggregations[aggregation.name]; | ||
frag.appendChild(createOptionItem(aggregation.name, isChecked)); | ||
}); | ||
|
||
generateReportEl.addEventListener('click', () => { | ||
startSpinner(); | ||
feedbackEl.textContent = ''; | ||
|
||
background.loadSelectedAggregations() | ||
.then(selectedAggregations => { | ||
return background.runLighthouseInExtension({ | ||
flags: { | ||
disableCpuThrottling: true | ||
}, | ||
restoreCleanState: true | ||
}, selectedAggregations); | ||
}) | ||
.catch(err => { | ||
let message = err.message; | ||
let includeReportLink = true; | ||
|
||
// Check for errors in how the user ran Lighthouse and replace with a more | ||
// helpful message (and remove 'Report Error' link). | ||
for (const [test, replacement] of Object.entries(NON_BUG_ERROR_MESSAGES)) { | ||
if (message.includes(test)) { | ||
message = replacement; | ||
includeReportLink = false; | ||
break; | ||
} | ||
} | ||
const optionsList = document.querySelector('.options__list'); | ||
optionsList.appendChild(frag); | ||
} | ||
|
||
/** | ||
* Initializes the popup's state and UI elements. | ||
* @param {!Window} background Reference to the extension's background page. | ||
*/ | ||
function initPopup() { | ||
getBackgroundPage.then(background => { | ||
// To prevent visual hiccups when opening the popup, we default the subpage | ||
// to the "running" view and switch to the default view once we're sure | ||
// Lighthouse is not already auditing the page. This change was necessary | ||
// now that fetching the background event page is async. | ||
if (background.isRunning()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move all the rest of this to a named setup function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
showRunningSubpage(); | ||
} else { | ||
hideRunningSubpage(); | ||
} | ||
|
||
feedbackEl.textContent = message; | ||
background.listenForStatus(logStatus); | ||
|
||
if (includeReportLink) { | ||
feedbackEl.className = 'feedback-error'; | ||
feedbackEl.appendChild(buildReportErrorLink(err)); | ||
} | ||
background.loadSelectedAggregations().then(selectedAggregations => { | ||
generateOptionsList(background, selectedAggregations); | ||
|
||
stopSpinner(); | ||
background.console.error(err); | ||
const generateReportButton = document.getElementById('generate-report'); | ||
generateReportButton.addEventListener('click', () => { | ||
onGenerateReportButtonClick(background, selectedAggregations); | ||
}); | ||
}); | ||
}); | ||
|
||
generateOptionsEl.addEventListener('click', () => { | ||
optionsEl.classList.add(subpageVisibleClass); | ||
}); | ||
const generateOptionsEl = document.getElementById('configure-options'); | ||
const optionsEl = document.querySelector('.options'); | ||
generateOptionsEl.addEventListener('click', () => { | ||
optionsEl.classList.add(subpageVisibleClass); | ||
}); | ||
|
||
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); | ||
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); | ||
optionsEl.classList.remove(subpageVisibleClass); | ||
}); | ||
}); | ||
|
||
chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) { | ||
|
@@ -206,6 +221,10 @@ 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; | ||
}); | ||
}); | ||
} | ||
|
||
initPopup(); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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:then below it would be
generateOptionsList().then(frag => optionsList.appendChild(frag));
There was a problem hiding this comment.
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)There was a problem hiding this comment.
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.