Skip to content

Commit

Permalink
DBW: add no Mutation Events audit
Browse files Browse the repository at this point in the history
  • Loading branch information
ebidel committed Oct 16, 2016
1 parent 1598261 commit a285cf2
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 0 deletions.
77 changes: 77 additions & 0 deletions lighthouse-core/audits/dobetterweb/no-mutation-events.js
@@ -0,0 +1,77 @@
/**
* @license
* Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @fileoverview Audit a page to see if it is using Mutation Events (and suggest
* MutationObservers instead).
*/

'use strict';

const url = require('url');
const Audit = require('../audit');
const Formatter = require('../../formatters/formatter');

class NoMutationEventsAudit extends Audit {

/**
* @return {!AuditMeta}
*/
static get meta() {
return {
category: 'JavaScript',
name: 'no-mutation-events',
description: 'Site does not Mutation Events in its own scripts',
helpText: 'Using <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events" target="_blank">Mutation events</a> degrades application performance. They are deprecated in the DOM events spec, replaced by <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" target="_blank">MutationObservers</a>.',
requiredArtifacts: ['URL', 'MutationEventUse']
};
}

/**
* @param {!Artifacts} artifacts
* @return {!AuditResult}
*/
static audit(artifacts) {
if (typeof artifacts.MutationEventUse === 'undefined' ||
artifacts.MutationEventUse === -1) {
return NoMutationEventsAudit.generateAuditResult({
rawValue: -1,
debugString: 'MutationEventUse gatherer did not run'
});
}

const pageHost = url.parse(artifacts.URL.finalUrl).host;
// Filter usage from other hosts.
const results = artifacts.MutationEventUse.usage.filter(err => {
return url.parse(err.url).host === pageHost;
}).map(err => {
return Object.assign({
label: `line: ${err.line}, col: ${err.col}`
}, err);
});

return NoMutationEventsAudit.generateAuditResult({
rawValue: results.length === 0,
extendedInfo: {
formatter: Formatter.SUPPORTED_FORMATS.URLLIST,
value: results
}
});
}
}

module.exports = NoMutationEventsAudit;
5 changes: 5 additions & 0 deletions lighthouse-core/config/dobetterweb.json
Expand Up @@ -9,6 +9,7 @@
"../gather/gatherers/dobetterweb/console-time-usage",
"../gather/gatherers/dobetterweb/datenow",
"../gather/gatherers/dobetterweb/document-write",
"../gather/gatherers/dobetterweb/mutation-events",
"../gather/gatherers/dobetterweb/websql"
]
}],
Expand All @@ -18,6 +19,7 @@
"../audits/dobetterweb/no-console-time",
"../audits/dobetterweb/no-datenow",
"../audits/dobetterweb/no-document-write",
"../audits/dobetterweb/no-mutation-events",
"../audits/dobetterweb/no-old-flexbox",
"../audits/dobetterweb/no-websql",
"../audits/dobetterweb/uses-http2",
Expand Down Expand Up @@ -65,6 +67,9 @@
},
"no-console-time": {
"expectedValue": false
},
"no-mutation-events": {
"expectedValue": false
}
}
}, {
Expand Down
65 changes: 65 additions & 0 deletions lighthouse-core/gather/gatherers/dobetterweb/mutation-events.js
@@ -0,0 +1,65 @@
/**
* @license
* Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @fileoverview Tests whether the page is using Date.now().
*/

'use strict';

const Gatherer = require('../gatherer');

const MUTATION_EVENTS = [
'DOMAttrModified',
'DOMAttributeNameChanged',
'DOMCharacterDataModified',
'DOMElementNameChanged',
'DOMNodeInserted',
'DOMNodeInsertedIntoDocument',
'DOMNodeRemoved',
'DOMNodeRemovedFromDocument',
'DOMSubtreeModified'
];

class MutationEventUse extends Gatherer {

beforePass(options) {
this.collectUsage = options.driver.captureFunctionCallSites('addEventListener');
this.collectUsage2 = options.driver.captureFunctionCallSites('document.addEventListener');
// TODO: document.body appears to not be defined by this time.
// this.collectUsage3 = options.driver.captureFunctionCallSites('document.body.addEventListener');
}

afterPass() {
const promise = this.collectUsage().then(results1 => {
return this.collectUsage2().then(results2 => results1.concat(results2));
});

return promise.then(uses => {
uses = uses.filter(use => {
const eventName = use.args[0];
return MUTATION_EVENTS.indexOf(eventName) !== -1;
});
this.artifact.usage = uses;
}, _ => {
this.artifact = -1;
return;
});
}
}

module.exports = MutationEventUse;
69 changes: 69 additions & 0 deletions lighthouse-core/test/audits/dobetterweb/no-mutation-events-test.js
@@ -0,0 +1,69 @@
/**
* Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';

const NoMutationEventsAudit = require('../../../audits/dobetterweb/no-mutation-events.js');
const assert = require('assert');

const URL = 'https://example.com';

/* eslint-env mocha */

describe('Page does not use mutation events', () => {
it('fails when no input present', () => {
const auditResult = NoMutationEventsAudit.audit({});
assert.equal(auditResult.rawValue, -1);
assert.ok(auditResult.debugString);
});

it('passes when mutation events are not used', () => {
const auditResult = NoMutationEventsAudit.audit({
MutationEventUse: {usage: []},
URL: {finalUrl: URL},
});
assert.equal(auditResult.rawValue, true);
assert.equal(auditResult.extendedInfo.value.length, 0);
});

it('passes when mutation events are used on a different origin', () => {
const auditResult = NoMutationEventsAudit.audit({
MutationEventUse: {
usage: [
{url: 'http://different.com/two', line: 2, col: 2},
{url: 'http://example2.com/two', line: 2, col: 22}
]
},
URL: {finalUrl: URL},
});
assert.equal(auditResult.rawValue, true);
assert.equal(auditResult.extendedInfo.value.length, 0);
});

it('fails when mutation events are used on the origin', () => {
const auditResult = NoMutationEventsAudit.audit({
MutationEventUse: {
usage: [
{url: 'http://example.com/one', line: 1, col: 1},
{url: 'http://example.com/two', line: 10, col: 1},
{url: 'http://example2.com/two', line: 2, col: 22}
]
},
URL: {finalUrl: URL},
});
assert.equal(auditResult.rawValue, false);
assert.equal(auditResult.extendedInfo.value.length, 2);
});
});

0 comments on commit a285cf2

Please sign in to comment.