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

DBW: add audit for Date.now() usage #707

Merged
merged 5 commits into from
Sep 27, 2016
Merged

DBW: add audit for Date.now() usage #707

merged 5 commits into from
Sep 27, 2016

Conversation

ebidel
Copy link
Contributor

@ebidel ebidel commented Sep 26, 2016

R: @paulirish @brendankenny @GoogleChrome/lighthouse

screen shot 2016-09-26 at 5 52 29 pm

@samccone
Copy link
Contributor

TIL

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

Looking good so far

category: 'JavaScript',
name: 'no-datenow',
description: 'Site does not use Date.now() in its own scripts',
helpText: 'Consider using <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/now" target="_blank">performance.now()</a>, which as better precision than <code>Date.now()</code> and always increases at a constant rate, independent of the system clock.',
Copy link
Member

Choose a reason for hiding this comment

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

FWIW, this does seem like one of those tests people are going to be annoyed about if they need Date.now() for an actual timestamp since the epoch or if performance.now() overhead isn't worth it

Choose a reason for hiding this comment

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

@brendankenny not annoyed, but if not Date.now(), what should be use if what you need is not performance? Maybe lighthouse should show recommendations besides just the warning/error?


afterPass(options) {
return options.driver.evaluateAsync(`(${collectDateNowUsage.toString()}())`)
.then(errors => {
Copy link
Member

Choose a reason for hiding this comment

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

maybe something like dateNowUses to differentiate from the function below this handling actual errors?

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

err.url = `${JSON.stringify(err)}`;
prev.push(err);
}
return prev;
Copy link
Member

Choose a reason for hiding this comment

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

do you want to dedupe these so if a function calling Date.now() is itself called many times you don't get a long long list of them?

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

@@ -15,7 +15,10 @@
<summary>URLs</summary>
{{#each this}}
<div class="http-resource">
<span class="http-resource__url">{{this.url}}</span><span class="http-resource__protocol">({{this.protocol}})</span>
<span class="http-resource__url">{{this.url}}</span>
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't overload url here like this. Maybe just add a place for a more general text string that is styled the same?

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

// Filter out Date.now() usage from scripts on other domains.
const results = artifacts.DateNowUse.errors.reduce((prev, err) => {
if (url.parse(err.url).host === pageHost) {
err.url = `${JSON.stringify(err)}`;
Copy link
Member

Choose a reason for hiding this comment

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

you shouldn't modify the artifact itself here (and should pick a more descriptive property name once the formatter is changed)

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

@brendankenny
Copy link
Member

Some idle thoughts while thinking about the next audit you (or whoever) write that uses this technique: it might be good to pull it up into driver, so that you any gatherer could evaluateScriptOnLoad a script that injected something and get events or a single event with lists of the locations where you did something.

It could also maybe be made more robust by throwing and catching errors but using the Debugger domain to turn on breaking on caught exceptions, then filtering out any not generated by Lighthouse, or maybe inserting debugger; statements and actually breaking there. The event/callback/whatever could be as simple as getting the callstack/location/whatever or eventually allowing more scripts to be executed at that point, the same way you can interact with the page in the DevTools console when execution is paused.

} catch (e) {
if (e.stack) {
const split = e.stack.split('\n');
const m = split[split.length - 1].match(/(https?:\/\/.*):(\d+):(\d+)/);
Copy link
Member

Choose a reason for hiding this comment

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

I tried out this date.now monkeypatch in the console, but it led to e.stack as

"Error: __called Date.now()__
    at Error (native)
    at Function.Date.now (<anonymous>:4:13)
    at <anonymous>:1:6"

The match() then returns null. I think you could make the regex testing here a bit more liberal.

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 with the ST API

throw Error('__called Date.now()__');
} catch (e) {
if (e.stack) {
const split = e.stack.split('\n');
Copy link
Member

Choose a reason for hiding this comment

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

HOLD UP WAIT A MINUTE

https://github.com/v8/v8/wiki/Stack-Trace-API#customizing-stack-traces

⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️

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

@ebidel
Copy link
Contributor Author

ebidel commented Sep 27, 2016

Well that looks useful!

On Mon, Sep 26, 2016, 5:23 PM Paul Irish notifications@github.com wrote:

@paulirish commented on this pull request.

In lighthouse-core/gather/gatherers/dobetterweb/datenow.js
#707 (review)
:

+/* global window, __returnResults */
+
+'use strict';
+
+const Gatherer = require('../gatherer');
+
+function patchDateNow() {

  • window.__stackTraceErrors = [];
  • // Override Date.now() so we know when if it's called.
  • const orig = Date.now;
  • Date.now = function() {
  • try {
  •  throw Error('**called Date.now()**');
    
  • } catch (e) {
  •  if (e.stack) {
    
  •    const split = e.stack.split('\n');
    

HOLD UP WAIT A MINUTE

https://github.com/v8/v8/wiki/Stack-Trace-API#customizing-stack-traces

⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️ ⬆️


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#707 (review),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAOigAmLcglYHesjlR5fTGtZtEqN75Biks5quGHzgaJpZM4KHFKz
.

@ebidel
Copy link
Contributor Author

ebidel commented Sep 27, 2016

Moved to the ST API. Works great. PTAL.

@nicklepedde
Copy link

I am one of those annoyed people, who does use and need Date.now(). My App has portions that require action from the customer by a specific date/time. Can we at least add an option to the report to ignore that rule?

@wardpeet
Copy link
Collaborator

you can create a custom configuration and don't include the audit & gatherer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants