Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1590)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/audits2/Audits2Panel.js

Issue 2861053003: DevTools: [lighthouse] Implement performance metrics filmstrip (Closed)
Patch Set: test fixed Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @unrestricted 6 * @unrestricted
7 */ 7 */
8 Audits2.Audits2Panel = class extends UI.PanelWithSidebar { 8 Audits2.Audits2Panel = class extends UI.PanelWithSidebar {
9 constructor() { 9 constructor() {
10 super('audits2'); 10 super('audits2');
(...skipping 27 matching lines...) Expand all
38 38
39 this._dropTarget = new UI.DropTarget( 39 this._dropTarget = new UI.DropTarget(
40 this.contentElement, [UI.DropTarget.Types.Files], Common.UIString('Drop audit file here'), 40 this.contentElement, [UI.DropTarget.Types.Files], Common.UIString('Drop audit file here'),
41 this._handleDrop.bind(this)); 41 this._handleDrop.bind(this));
42 42
43 this._showLandingPage(); 43 this._showLandingPage();
44 } 44 }
45 45
46 _clearAll() { 46 _clearAll() {
47 this._treeOutline.removeChildren(); 47 this._treeOutline.removeChildren();
48 if (!this._treeOutline.rootElement().childCount()) 48 this._showLandingPage();
49 this._showLandingPage();
50 } 49 }
51 50
52 _deleteSelected() { 51 _deleteSelected() {
53 var selection = this._treeOutline.selectedTreeElement; 52 var selection = this._treeOutline.selectedTreeElement;
54 if (selection) 53 if (selection)
55 this._treeOutline.removeChild(selection); 54 selection._deleteItem();
56 if (!this._treeOutline.rootElement().childCount())
57 this._showLandingPage();
58 } 55 }
59 56
60 _showLandingPage() { 57 _showLandingPage() {
58 if (this._treeOutline.rootElement().childCount())
59 return;
60
61 this.mainElement().removeChildren(); 61 this.mainElement().removeChildren();
62 var landingPage = this.mainElement().createChild('div', 'vbox audits2-landin g-page'); 62 var landingPage = this.mainElement().createChild('div', 'vbox audits2-landin g-page');
63 var landingCenter = landingPage.createChild('div', 'vbox audits2-landing-cen ter'); 63 var landingCenter = landingPage.createChild('div', 'vbox audits2-landing-cen ter');
64 landingCenter.createChild('div', 'audits2-logo'); 64 landingCenter.createChild('div', 'audits2-logo');
65 var text = landingCenter.createChild('div', 'audits2-landing-text'); 65 var text = landingCenter.createChild('div', 'audits2-landing-text');
66 text.createChild('span', 'audits2-landing-bold-text').textContent = Common.U IString('Audits'); 66 text.createChild('span', 'audits2-landing-bold-text').textContent = Common.U IString('Audits');
67 text.createChild('span').textContent = Common.UIString( 67 text.createChild('span').textContent = Common.UIString(
68 ' help you identify and fix common problems that affect' + 68 ' help you identify and fix common problems that affect' +
69 ' your site\'s performance, accessibility, and user experience. '); 69 ' your site\'s performance, accessibility, and user experience. ');
70 var link = text.createChild('span', 'link'); 70 var link = text.createChild('span', 'link');
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 if (!this._dialog) 197 if (!this._dialog)
198 return; 198 return;
199 this._statusElement.textContent = statusMessage; 199 this._statusElement.textContent = statusMessage;
200 } 200 }
201 201
202 /** 202 /**
203 * @return {!Promise<undefined>} 203 * @return {!Promise<undefined>}
204 */ 204 */
205 _stop() { 205 _stop() {
206 return this._protocolService.detach().then(_ => { 206 return this._protocolService.detach().then(_ => {
207 Emulation.InspectedPagePlaceholder.instance().update(true);
207 this._auditRunning = false; 208 this._auditRunning = false;
208 this._updateButton(); 209 this._updateButton();
209 var resourceTreeModel = SDK.targetManager.mainTarget().model(SDK.ResourceT reeModel); 210 var resourceTreeModel = SDK.targetManager.mainTarget().model(SDK.ResourceT reeModel);
210 if (resourceTreeModel && this._inspectedURL !== SDK.targetManager.mainTarg et().inspectedURL()) 211 if (resourceTreeModel && this._inspectedURL !== SDK.targetManager.mainTarg et().inspectedURL())
211 resourceTreeModel.navigate(this._inspectedURL).then(() => this._hideDial og()); 212 resourceTreeModel.navigate(this._inspectedURL).then(() => this._hideDial og());
212 else 213 else
213 this._hideDialog(); 214 this._hideDialog();
214 }); 215 });
215 } 216 }
216 217
217 /** 218 /**
218 * @param {!ReportRenderer.ReportJSON} lighthouseResult 219 * @param {!ReportRenderer.ReportJSON} lighthouseResult
219 */ 220 */
220 _finish(lighthouseResult) { 221 _finish(lighthouseResult) {
221 if (lighthouseResult === null) { 222 if (lighthouseResult === null) {
222 this._updateStatus(Common.UIString('Auditing failed.')); 223 this._updateStatus(Common.UIString('Auditing failed.'));
223 return; 224 return;
224 } 225 }
225 var treeElement = new Audits2.Audits2Panel.TreeElement(lighthouseResult, thi s.mainElement()); 226 var treeElement =
227 new Audits2.Audits2Panel.TreeElement(lighthouseResult, this.mainElement( ), this._showLandingPage.bind(this));
226 this._treeOutline.appendChild(treeElement); 228 this._treeOutline.appendChild(treeElement);
227 treeElement._populate(); 229 treeElement._populate();
228 treeElement.select(); 230 treeElement.select();
229 this._hideDialog(); 231 this._hideDialog();
230 } 232 }
231 233
232 /** 234 /**
233 * @param {!Error} err 235 * @param {!Error} err
234 */ 236 */
235 _renderBugReport(err) { 237 _renderBugReport(err) {
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 this._initWorker(); 432 this._initWorker();
431 433
432 return this._backendPromise.then(_ => this._backend.send(method, params)); 434 return this._backendPromise.then(_ => this._backend.send(method, params));
433 } 435 }
434 }; 436 };
435 437
436 Audits2.Audits2Panel.TreeElement = class extends UI.TreeElement { 438 Audits2.Audits2Panel.TreeElement = class extends UI.TreeElement {
437 /** 439 /**
438 * @param {!ReportRenderer.ReportJSON} lighthouseResult 440 * @param {!ReportRenderer.ReportJSON} lighthouseResult
439 * @param {!Element} resultsView 441 * @param {!Element} resultsView
442 * @param {function()} showLandingCallback
440 */ 443 */
441 constructor(lighthouseResult, resultsView) { 444 constructor(lighthouseResult, resultsView, showLandingCallback) {
442 super('', false); 445 super('', false);
443 this._lighthouseResult = lighthouseResult; 446 this._lighthouseResult = lighthouseResult;
444 this._resultsView = resultsView; 447 this._resultsView = resultsView;
448 this._showLandingCallback = showLandingCallback;
445 /** @type {?Element} */ 449 /** @type {?Element} */
446 this._reportContainer = null; 450 this._reportContainer = null;
447 451
448 var url = new Common.ParsedURL(lighthouseResult.url); 452 var url = new Common.ParsedURL(lighthouseResult.url);
449 var timestamp = lighthouseResult.generatedTime; 453 var timestamp = lighthouseResult.generatedTime;
450 var titleElement = this.titleElement(); 454 var titleElement = this.titleElement();
451 titleElement.classList.add('audits2-report-tree-item'); 455 titleElement.classList.add('audits2-report-tree-item');
452 titleElement.createChild('div').textContent = url.domain(); 456 titleElement.createChild('div').textContent = url.domain();
453 titleElement.createChild('span', 'dimmed').textContent = new Date(timestamp) .toLocaleString(); 457 titleElement.createChild('span', 'dimmed').textContent = new Date(timestamp) .toLocaleString();
454 this.listItemElement.addEventListener('contextmenu', this._handleContextMenu Event.bind(this), false); 458 this.listItemElement.addEventListener('contextmenu', this._handleContextMenu Event.bind(this), false);
(...skipping 10 matching lines...) Expand all
465 * @override 469 * @override
466 * @param {boolean=} selectedByUser 470 * @param {boolean=} selectedByUser
467 * @return {boolean} 471 * @return {boolean}
468 */ 472 */
469 onselect(selectedByUser) { 473 onselect(selectedByUser) {
470 this._renderReport(); 474 this._renderReport();
471 return true; 475 return true;
472 } 476 }
473 477
474 /** 478 /**
479 * @override
480 */
481 ondelete() {
482 this._deleteItem();
483 return true;
484 }
485
486 _deleteItem() {
487 this.treeOutline.removeChild(this);
488 this._showLandingCallback();
489 }
490
491 /**
475 * @param {!Event} event 492 * @param {!Event} event
476 */ 493 */
477 _handleContextMenuEvent(event) { 494 _handleContextMenuEvent(event) {
478 var contextMenu = new UI.ContextMenu(event); 495 var contextMenu = new UI.ContextMenu(event);
479 contextMenu.appendItem(Common.UIString('Save as\u2026'), () => { 496 contextMenu.appendItem(Common.UIString('Save as\u2026'), () => {
480 var url = new Common.ParsedURL(this._lighthouseResult.url).domain(); 497 var url = new Common.ParsedURL(this._lighthouseResult.url).domain();
481 var timestamp = this._lighthouseResult.generatedTime; 498 var timestamp = this._lighthouseResult.generatedTime;
482 var fileName = `${url}-${new Date(timestamp).toISO8601Compact()}.json`; 499 var fileName = `${url}-${new Date(timestamp).toISO8601Compact()}.json`;
483 Workspace.fileManager.save(fileName, JSON.stringify(this._lighthouseResult ), true); 500 Workspace.fileManager.save(fileName, JSON.stringify(this._lighthouseResult ), true);
484 }); 501 });
502 contextMenu.appendItem(Common.UIString('Delete'), () => this._deleteItem());
485 contextMenu.show(); 503 contextMenu.show();
486 } 504 }
487 505
488 /** 506 /**
489 * @override 507 * @override
490 */ 508 */
491 onunbind() { 509 onunbind() {
492 if (this._reportContainer && this._reportContainer.parentElement) 510 if (this._reportContainer && this._reportContainer.parentElement)
493 this._reportContainer.remove(); 511 this._reportContainer.remove();
494 } 512 }
(...skipping 12 matching lines...) Expand all
507 var categoryRenderer = new CategoryRenderer(dom, detailsRenderer); 525 var categoryRenderer = new CategoryRenderer(dom, detailsRenderer);
508 var renderer = new Audits2.Audits2Panel.ReportRenderer(dom, categoryRenderer ); 526 var renderer = new Audits2.Audits2Panel.ReportRenderer(dom, categoryRenderer );
509 527
510 var templatesHTML = Runtime.cachedResources['audits2/lighthouse/templates.ht ml']; 528 var templatesHTML = Runtime.cachedResources['audits2/lighthouse/templates.ht ml'];
511 var templatesDOM = new DOMParser().parseFromString(templatesHTML, 'text/html '); 529 var templatesDOM = new DOMParser().parseFromString(templatesHTML, 'text/html ');
512 if (!templatesDOM) 530 if (!templatesDOM)
513 return; 531 return;
514 532
515 renderer.setTemplateContext(templatesDOM); 533 renderer.setTemplateContext(templatesDOM);
516 renderer.renderReport(this._lighthouseResult, this._reportContainer); 534 renderer.renderReport(this._lighthouseResult, this._reportContainer);
535
536 var performanceScoreElement = this._reportContainer.querySelector('.lh-categ ory[id=performance] .lh-score');
537 var artifacts = this._lighthouseResult['artifacts'];
538 if (!performanceScoreElement || !artifacts)
539 return;
540 var tracePass = artifacts['traces'] ? artifacts['traces']['defaultPass'] : n ull;
541 if (!tracePass)
542 return;
543
544 var fmp = this._lighthouseResult['audits']['first-meaningful-paint'];
545 if (!fmp || !fmp['extendedInfo'])
546 return;
547
548 var tti = this._lighthouseResult['audits']['time-to-interactive'];
549 if (!tti || !tti['extendedInfo'])
550 return;
551
552 var navStart = fmp['extendedInfo']['value']['timestamps']['navStart'];
553 var markers = [
554 {
555 title: Common.UIString('First contentful paint'),
556 value: (fmp['extendedInfo']['value']['timestamps']['fCP'] - navStart) / 1000
557 },
558 {
559 title: Common.UIString('First meaningful paint'),
560 value: (fmp['extendedInfo']['value']['timestamps']['fMP'] - navStart) / 1000
561 },
562 {
563 title: Common.UIString('Time to interactive'),
564 value: (tti['extendedInfo']['value']['timestamps']['timeToInteractive'] - navStart) / 1000
565 },
566 {
567 title: Common.UIString('Visually ready'),
568 value: (tti['extendedInfo']['value']['timestamps']['visuallyReady'] - na vStart) / 1000
569 }
570 ];
571
572 var timeSpan = Math.max(...markers.map(marker => marker.value));
573 var screenshots = tracePass.traceEvents.filter(e => e.cat === 'disabled-by-d efault-devtools.screenshot');
574 var timelineElement = createElementWithClass('div', 'audits2-timeline');
575 var filmStripElement = timelineElement.createChild('div', 'audits2-filmstrip ');
576
577 var numberOfFrames = 8;
578 var roundToMs = 100;
579 var timeStep = (Math.ceil(timeSpan / numberOfFrames / roundToMs)) * roundToM s;
580
581 for (var time = 0; time < timeSpan; time += timeStep) {
582 var frameForTime = null;
583 for (var e of screenshots) {
584 if ((e.ts - navStart) / 1000 < time + timeStep)
585 frameForTime = e.args.snapshot;
586 }
587 var frame = filmStripElement.createChild('div', 'frame');
588 frame.createChild('div', 'time').textContent = Number.millisToString(time + timeStep);
589
590 var thumbnail = frame.createChild('div', 'thumbnail');
591 if (frameForTime) {
592 var img = thumbnail.createChild('img');
593 img.src = 'data:image/jpg;base64,' + frameForTime;
594 }
595 }
596
597 for (var marker of markers) {
598 var markerElement = timelineElement.createChild('div', 'audits2-timeline-m arker');
599 markerElement.createChild('div', 'audits2-timeline-bar').style.width =
600 (100 * (marker.value / timeSpan) | 0) + '%';
601 markerElement.createChild('span').textContent = Common.UIString('%s: ', ma rker.title);
602 markerElement.createChild('span', 'audits2-timeline-subtitle').textContent = Number.millisToString(marker.value);
603 }
604
605 performanceScoreElement.parentElement.insertBefore(timelineElement, performa nceScoreElement.nextSibling);
517 } 606 }
518 }; 607 };
519 608
520 Audits2.Audits2Panel.TreeSubElement = class extends UI.TreeElement { 609 Audits2.Audits2Panel.TreeSubElement = class extends UI.TreeElement {
521 /** 610 /**
522 * @param {string} id 611 * @param {string} id
523 * @param {string} name 612 * @param {string} name
524 * @param {number} score 613 * @param {number} score
525 */ 614 */
526 constructor(id, name, score) { 615 constructor(id, name, score) {
(...skipping 12 matching lines...) Expand all
539 onselect() { 628 onselect() {
540 this.parent._renderReport(); 629 this.parent._renderReport();
541 var node = this.parent._resultsView.querySelector('.lh-category[id=' + this. _id + ']'); 630 var node = this.parent._resultsView.querySelector('.lh-category[id=' + this. _id + ']');
542 if (node) { 631 if (node) {
543 node.scrollIntoView(true); 632 node.scrollIntoView(true);
544 return true; 633 return true;
545 } 634 }
546 return false; 635 return false;
547 } 636 }
548 }; 637 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698